PlaneImpactResponse.cxx
Go to the documentation of this file.
4 #include "WireCellUtil/Testing.h"
7 
10 
11 
12 using namespace std;
13 using namespace WireCell;
14 
15 
17  if (m_spectrum.size()!=0){
18  return m_spectrum;
19  }else{
20  m_spectrum = Waveform::dft(m_waveform);
21  return m_spectrum;
22  }
23 }
24 
25 const Waveform::compseq_t& Gen::ImpactResponse::long_aux_spectrum(){
26  if (m_long_spectrum.size()!=0){
27  return m_long_spectrum;
28  }else{
30  return m_long_spectrum;
31  }
32 }
33 
35  : m_frname("FieldResponse")
36  , m_plane_ident(plane_ident)
37  , m_nbins(nbins)
38  , m_tick(tick)
39  , l(Log::logger("geom"))
40 {
41 }
42 
43 
45 {
47  // IFieldResponse component
48  cfg["field_response"] = m_frname;
49  // plane id to use to index into field response .plane()
50  cfg["plane"] = 0;
51  // names of IWaveforms interpreted as subsequent response
52  // functions.
53  cfg["short_responses"] = Json::arrayValue;
54  cfg["overall_short_padding"] = 100*units::us;
55  cfg["long_responses"] = Json::arrayValue;
56  cfg["long_padding"] = 1.5*units::ms;
57  // number of bins in impact response spectra
58  cfg["nticks"] = 10000;
59  // sample period of response waveforms
60  cfg["tick"] = 0.5*units::us;
61  return cfg;
62 }
63 
65 {
66  m_frname = get(cfg, "field_response", m_frname);
67  m_plane_ident = get(cfg, "plane", m_plane_ident);
68 
69  m_short.clear();
70  auto jfilts = cfg["short_responses"];
71  if (!jfilts.isNull() and !jfilts.empty()) {
72  for (auto jfn: jfilts) {
73  auto tn = jfn.asString();
74  m_short.push_back(tn);
75  }
76  }
77 
78  m_long.clear();
79  auto jfilts1 = cfg["long_responses"];
80  if (!jfilts1.isNull() and !jfilts1.empty()) {
81  for (auto jfn: jfilts1) {
82  auto tn = jfn.asString();
83  m_long.push_back(tn);
84  }
85  }
86 
87  m_overall_short_padding = get(cfg, "overall_short_padding", m_overall_short_padding);
88  m_long_padding = get(cfg, "long_padding", m_long_padding);
89 
90  m_nbins = (size_t) get(cfg, "nticks", (int)m_nbins);
91  m_tick = get(cfg, "tick", m_tick);
92 
94 }
95 
96 
98 {
99  auto ifr = Factory::find_tn<IFieldResponse>(m_frname);
100 
101  const size_t n_short_length = fft_best_length(m_overall_short_padding/m_tick);
102 
103  // build "short" response spectra
104  WireCell::Waveform::compseq_t short_spec(n_short_length, Waveform::complex_t(1.0, 0.0));
105  const size_t nshort = m_short.size();
106  for (size_t ind=0; ind<nshort; ++ind) {
107  const auto& name = m_short[ind];
108  auto iw = Factory::find_tn<IWaveform>(name);
109  if (std::abs(iw->waveform_period() - m_tick) > 1*units::ns) {
110  l->critical("from {} got {} us sample period expected {} us",
111  name, iw->waveform_period()/units::us, m_tick/units::us);
112  THROW(ValueError() << errmsg{"Tick mismatch in " + name});
113  }
114  auto wave = iw->waveform_samples(); // copy
115  if (wave.size() != n_short_length) {
116  l->debug("PIR: short response {} has different number of samples ({}) than expected ({})", name, wave.size(), n_short_length);
117  wave.resize(n_short_length, 0);
118  }
119  // note: we are ignoring waveform_start which will introduce
120  // an arbitrary phase shift....
121  auto spec = Waveform::dft(wave);
122  for (size_t ibin=0; ibin < n_short_length; ++ibin) {
123  short_spec[ibin] *= spec[ibin];
124  }
125  }
126 
127 
128  // build "long" response spectrum in time domain ...
129  size_t n_long_length = fft_best_length(m_nbins);
130  WireCell::Waveform::compseq_t long_spec(n_long_length, Waveform::complex_t(1.0, 0.0));
131  const size_t nlong = m_long.size();
132  for (size_t ind=0; ind<nlong; ++ind) {
133  const auto& name = m_long[ind];
134  auto iw = Factory::find_tn<IWaveform>(name);
135  if (std::abs(iw->waveform_period() - m_tick) > 1*units::ns) {
136  l->critical("from {} got {} us sample period expected {} us",
137  name, iw->waveform_period()/units::us, m_tick/units::us);
138  THROW(ValueError() << errmsg{"Tick mismatch in " + name});
139  }
140  auto wave = iw->waveform_samples(); // copy
141  if (wave.size() != n_long_length) {
142  l->debug("PIR: long response {} has different number of samples ({}) than expected ({})", name, wave.size(), n_long_length);
143  wave.resize(n_long_length, 0);
144  }
145  // note: we are ignoring waveform_start which will introduce
146  // an arbitrary phase shift....
147  auto spec = Waveform::dft(wave);
148  for (size_t ibin=0; ibin < n_long_length; ++ibin) {
149  long_spec[ibin] *= spec[ibin];
150  }
151 
152  }
154  if (nlong >0)
155  long_wf = Waveform::idft(long_spec);
156 
157 
158  const auto& fr = ifr->field_response();
159  const auto& pr = *fr.plane(m_plane_ident);
160  const int npaths = pr.paths.size();
161 
162  // FIXME HUGE ASSUMPTIONS ABOUT ORGANIZATION OF UNDERLYING
163  // FIELD RESPONSE DATA!!!
164  //
165  // Paths must be in increasing pitch with one impact position at
166  // nearest wire and 5 more impact positions equally spaced and at
167  // smaller pitch distances than the associated wire. The final
168  // impact position should be no further from the wire than 1/2
169  // pitch.
170 
171  const int n_per = 6; // fixme: assumption
172  const int n_wires = npaths/n_per;
173  const int n_wires_half = n_wires / 2; // integer div
174  //const int center_index = n_wires_half * n_per;
175 
176  /// FIXME: this assumes impact positions are on uniform grid!
177  m_impact = std::abs(pr.paths[1].pitchpos - pr.paths[0].pitchpos);
178  /// FIXME: this assumes paths are ordered by pitch
179  m_half_extent = std::max(std::abs(pr.paths.front().pitchpos),
180  std::abs(pr.paths.back().pitchpos));
181  /// FIXME: this assumes detailed ordering of paths w/in one wire
182  m_pitch = 2.0*std::abs(pr.paths[n_per-1].pitchpos - pr.paths[0].pitchpos);
183 
184 
185  // native response time binning
186  const int rawresp_size = pr.paths[0].current.size();
187  const double rawresp_min = fr.tstart;
188  const double rawresp_tick = fr.period;
189  const double rawresp_max = rawresp_min + rawresp_size*rawresp_tick;
190  Binning rawresp_bins(rawresp_size, rawresp_min, rawresp_max);
191 
192  // collect paths and index by wire and impact position.
193  std::map<int, region_indices_t> wire_to_ind;
194  for (int ipath = 0; ipath < npaths; ++ipath) {
195  const Response::Schema::PathResponse& path = pr.paths[ipath];
196  const int wirenum = int(ceil(path.pitchpos/pr.pitch)); // signed
197  wire_to_ind[wirenum].push_back(ipath);
198 
199  // match response sampling to digi and zero-pad
200  WireCell::Waveform::realseq_t wave(n_short_length, 0.0);
201  for (int rind=0; rind<rawresp_size; ++rind) { // sample at fine bins of response function
202  const double time = rawresp_bins.center(rind);
203 
204  // fixme: assumes field response appropriately centered
205  const size_t bin = time/m_tick;
206 
207  if (bin>= n_short_length) {
208  l->warn("PIR: out of bounds field response bin={}, ntbins={}, time={} us, tick={} us", bin, n_short_length, time/units::us, m_tick/units::us);
209  }
210 
211 
212  // Here we have sampled, instantaneous induced *current*
213  // (in WCT system-of-units for current) due to a single
214  // drifting electron from the field response function.
215  const double induced_current = path.current[rind];
216 
217  // Integrate across the fine time bin to get the element
218  // of induced *charge* over this bin.
219  const double induced_charge = induced_current*rawresp_tick;
220 
221  // sum up over coarse ticks.
222  wave[bin] += induced_charge;
223  }
225 
226  // Convolve with short responses
227  if (nshort) {
228  for (size_t find=0; find < n_short_length; ++find) {
229  spec[find] *= short_spec[find];
230  }
231  }
233  wf.resize(m_nbins,0);
234 
235  IImpactResponse::pointer ir = std::make_shared<Gen::ImpactResponse>(ipath, wf, m_overall_short_padding/m_tick, long_wf, m_long_padding/m_tick);
236  m_ir.push_back(ir);
237  }
238 
239  // apply symmetry.
240  for (int irelwire=-n_wires_half; irelwire <= n_wires_half; ++irelwire) {
241  auto direct = wire_to_ind[irelwire];
242  auto other = wire_to_ind[-irelwire];
243 
244  std::vector<int> indices(direct.begin(), direct.end());
245  for (auto it = other.rbegin()+1; it != other.rend(); ++it) {
246  indices.push_back(*it);
247  }
248  m_bywire.push_back(indices);
249  }
250 
251 }
252 
254 {
255 }
256 
257 // const Response::Schema::PlaneResponse& PlaneImpactResponse::plane_response() const
258 // {
259 // return *m_fr.plane(m_plane_ident);
260 // }
261 
262 std::pair<int,int> Gen::PlaneImpactResponse::closest_wire_impact(double relpitch) const
263 {
264  const int center_wire = nwires()/2;
265 
266  const int relwire = int(round(relpitch/m_pitch));
267  const int wire_index = center_wire + relwire;
268 
269  const double remainder_pitch = relpitch - relwire*m_pitch;
270  const int impact_index = int(round(remainder_pitch / m_impact)) + nimp_per_wire()/2;
271 
272  return std::make_pair(wire_index, impact_index);
273 }
274 
276 {
277  if (relpitch < -m_half_extent || relpitch > m_half_extent) {
278  return nullptr;
279  }
280  std::pair<int,int> wi = closest_wire_impact(relpitch);
281  if (wi.first < 0 || wi.first >= (int)m_bywire.size()) {
282  l->debug("PIR: closest relative pitch:{} outside of wire range: {}",
283  relpitch, wi.first);
284  return nullptr;
285  }
286  const std::vector<int>& region = m_bywire[wi.first];
287  if (wi.second < 0 || wi.second >= (int)region.size()) {
288  l->debug("PIR: relative pitch:{} outside of impact range: {}",
289  relpitch, wi.second);
290  return nullptr;
291  }
292  int irind = region[wi.second];
293  if (irind < 0 || irind > (int)m_ir.size()) {
294  l->debug("PIR: relative pitch:{} no impact response for region: {}",
295  relpitch, irind);
296  return nullptr;
297  }
298 
299  return m_ir[irind];
300 }
301 
303 {
304  if (relpitch < -m_half_extent || relpitch > m_half_extent) {
305  return TwoImpactResponses(nullptr, nullptr);
306  }
307 
308  std::pair<int,int> wi = closest_wire_impact(relpitch);
309 
310  auto region = m_bywire[wi.first];
311  if (wi.second == 0) {
312  return std::make_pair(m_ir[region[0]], m_ir[region[1]]);
313  }
314  if (wi.second == (int)region.size()-1) {
315  return std::make_pair(m_ir[region[wi.second-1]], m_ir[region[wi.second]]);
316  }
317 
318  const double absimpact = m_half_extent + relpitch - wi.first*m_pitch;
319  const double sign = absimpact - wi.second*m_impact;
320 
321  if (sign > 0) {
322  return TwoImpactResponses(m_ir[region[wi.second]], m_ir[region[wi.second+1]]);
323  }
324  return TwoImpactResponses(m_ir[region[wi.second-1]], m_ir[region[wi.second]]);
325 }
326 
327 
328 
329 
static QCString name
Definition: declinfo.cpp:673
int nwires() const
The number of wires that span the pitch range.
virtual WireCell::Configuration default_configuration() const
Optional, override to return a hard-coded default configuration.
Sequence< real_t > realseq_t
Definition: Waveform.h:31
double center(int ind) const
Definition: Binning.h:86
std::vector< T > Waveform
Definition: IWaveformTool.h:21
Waveform::compseq_t m_long_spectrum
boost::error_info< struct tag_errmsg, std::string > errmsg
Definition: Exceptions.h:54
std::size_t fft_best_length(size_t nsamples, bool keep_odd_even=false)
PlaneImpactResponse(int plane_ident=0, size_t nbins=10000, double tick=0.5 *units::us)
STL namespace.
virtual IImpactResponse::pointer closest(double relpitch) const
cfg
Definition: dbjson.py:29
uint size() const
Definition: qcstring.h:201
static QStrList * l
Definition: config.cpp:1044
const double tick
static const double ms
Definition: Units.h:104
T abs(T value)
compseq_t dft(realseq_t seq)
Definition: Waveform.cxx:141
Configuration find(Configuration &lst, const std::string &dotpath, const T &val)
Return dictionary in given list if it value at dotpath matches.
std::pair< IImpactResponse::pointer, IImpactResponse::pointer > TwoImpactResponses
std::pair< int, int > closest_wire_impact(double relpitch) const
double pitchpos
The position in the pitch direction to the starting point of the path.
Definition: Response.h:36
constexpr std::array< std::size_t, geo::vect::dimension< Vector >)> indices()
Returns a sequence of indices valid for a vector of the specified type.
std::shared_ptr< Interface > pointer
Definition: Interface.h:16
#define THROW(e)
Definition: Exceptions.h:25
static int max(int a, int b)
static const double ns
Definition: Units.h:102
logptr_t logger(std::string name)
Definition: Logging.cxx:71
Thrown when a wrong value has been encountered.
Definition: Exceptions.h:37
Waveform::realseq_t m_long_waveform
std::vector< std::string > m_short
Definition: Main.h:22
virtual TwoImpactResponses bounded(double relpitch) const
WireCell::Waveform::realseq_t current
An array holding the induced current for the path on the wire-of-interest.
Definition: Response.h:33
int sign(double val)
Definition: UtilFunc.cxx:104
std::vector< std::string > m_long
std::vector< IImpactResponse::pointer > m_ir
realseq_t idft(compseq_t spec)
Definition: Waveform.cxx:149
int nimp_per_wire() const
not in the interface
WIRECELL_FACTORY(PlaneImpactResponse, WireCell::Gen::PlaneImpactResponse, WireCell::IPlaneImpactResponse, WireCell::IConfigurable) using namespace std
Json::Value Configuration
Definition: Configuration.h:50
QTextStream & bin(QTextStream &s)
static const double us
Definition: Units.h:105
array_xxc dft(const array_xxf &arr)
Definition: Array.cxx:15
std::complex< float > complex_t
The type for the spectrum in each bin.
Definition: Waveform.h:21
Sequence< complex_t > compseq_t
A complex-valued sequence, eg for discrete spectrum powers.
Definition: Waveform.h:34
virtual void configure(const WireCell::Configuration &cfg)
Accept a configuration.