Response.cxx
Go to the documentation of this file.
1 #include "WireCellUtil/Persist.h"
2 #include "WireCellUtil/ExecMon.h"
4 #include "WireCellUtil/Logging.h"
5 #include <cmath>
6 #include <set>
7 
8 using spdlog::error;
9 
10 using namespace WireCell;
11 
12 
14 {
15 }
16 
18 {
19 }
20 
22 {
23 }
24 
25 /**
26  frdict['FieldResponse'].keys()
27  ['origin', 'axis', 'period', 'tstart', 'planes']
28 
29  frdict['FieldResponse']['planes'][0]['PlaneResponse'].keys()
30  ['paths', 'planeid', 'pitch']
31 
32  frdict['FieldResponse']['planes'][0]['PlaneResponse']['paths'][0]['PathResponse'].keys()
33  ['current', 'wirepos', 'pitchpos']
34 
35  frdict['FieldResponse']['planes'][0]['PlaneResponse']['paths'][0]['PathResponse']['current']['array'].keys()
36  ['shape', 'elements']
37 
38  */
40 {
41  if (!filename) {
42  error("Response::Schema::load(): empty field response file name");
43  return FieldResponse();
44  }
46  if (top.isNull()) {
47  error("Response::Schema::load(): failed to load {}", filename);
48  return FieldResponse();
49  }
50  Json::Value fr = top["FieldResponse"];
51 
52  using namespace WireCell::Response::Schema;
53 
54  std::vector<PlaneResponse> planes;
55  for (auto plane : fr["planes"]) {
56 
57  auto plr = plane["PlaneResponse"];
58 
59  std::vector<PathResponse> paths;
60  for (auto path : plr["paths"]) {
61  auto par = path["PathResponse"];
63  for (auto c : par["current"]["array"]["elements"]) {
64  current.push_back(c.asDouble());
65  }
66  auto wcpath = PathResponse(current, par["pitchpos"].asDouble(), par["wirepos"].asDouble());
67  paths.push_back(wcpath);
68  }
69 
70  PlaneResponse wcplr(paths,
71  plr["planeid"].asInt(),
72  plr["location"].asDouble(),
73  plr["pitch"].asDouble());
74 
75  planes.push_back(wcplr);
76  }
77 
78  auto adir = fr["axis"];
79  auto axis = WireCell::Vector(adir[0].asDouble(),adir[1].asDouble(),adir[2].asDouble());
80  auto ret = FieldResponse(planes, axis,
81  fr["origin"].asDouble(),
82  fr["tstart"].asDouble(),
83  fr["period"].asDouble(),
84  fr["speed"].asDouble());
85  return ret;
86 }
87 
88 
89 
91 
92 {
93 }
94 
95 
96 /// Warning! this function is NOT GENERAL. It is actually specific
97 /// to Garfield 1D line of paths with half the impact positions
98 /// represented!
100 {
101  using namespace WireCell::Waveform;
102  using namespace WireCell::Response::Schema;
103 
104  std::vector<PlaneResponse> newplanes;
105  for (auto plane : fr.planes) {
106  std::vector<PathResponse> newpaths;
107 
108  double pitch = plane.pitch;
109 
110  std::map<int, realseq_t> avgs;
111  //std::map<int, int> nums;
112 
113 
114  std::map<int,realseq_t> fresp_map;
115  std::map<int,std::pair<double,double>> pitch_pos_range_map;
116 
117  // figure out the range of each response ...
118 
119  int nsamples=0;
120 
121  for (auto path : plane.paths) {
122  int eff_num = path.pitchpos/(0.01 * pitch);
123  if (fresp_map.find(eff_num) == fresp_map.end()){
124  fresp_map[eff_num] = path.current;
125  }else{
126  nsamples = path.current.size();
127  for (int k=0;k!=nsamples;k++){
128  fresp_map[eff_num].at(k) = (fresp_map[eff_num].at(k) + path.current.at(k))/2.;
129  }
130  }
131  if (fresp_map.find(-eff_num) == fresp_map.end()){
132  fresp_map[-eff_num] = path.current;
133  }else{
134  int nsamples = path.current.size();
135  for (int k=0;k!=nsamples;k++){
136  fresp_map[-eff_num].at(k) = (fresp_map[-eff_num].at(k) + path.current.at(k))/2.;
137  }
138  }
139  }
140 
141 
142  std::vector<int> pitch_pos;
143  for (auto it = fresp_map.begin(); it!= fresp_map.end(); it++){
144  pitch_pos.push_back((*it).first);
145  }
146 
147  double min = -1e9;
148  double max = 1e9;
149  for (size_t i=0;i!=pitch_pos.size();i++){
150  int eff_num = pitch_pos.at(i);
151  if (i==0){
152  pitch_pos_range_map[eff_num] = std::make_pair(min,(pitch_pos.at(i) + pitch_pos.at(i+1))/2.*0.01*pitch);
153  }else if (i==pitch_pos.size()-1){
154  pitch_pos_range_map[eff_num] = std::make_pair((pitch_pos.at(i) + pitch_pos.at(i-1))/2.*0.01*pitch,max);
155  }else{
156  pitch_pos_range_map[eff_num] = std::make_pair((pitch_pos.at(i) + pitch_pos.at(i-1))/2.*0.01*pitch,(pitch_pos.at(i) + pitch_pos.at(i+1))/2.*0.01*pitch);
157  }
158  }
159 
160 
161  // figure out how many wires ...
162  std::set<int> wire_regions;
163  for (size_t i=0;i!=pitch_pos.size();i++){
164  if (pitch_pos.at(i)>0){
165  wire_regions.insert( round((pitch_pos.at(i)*0.01*pitch-0.001*pitch)/pitch));
166  }else{
167  wire_regions.insert( round((pitch_pos.at(i)*0.01*pitch+0.001*pitch)/pitch));
168  }
169  }
170 
171 
172  // do the average ...
173  for(auto it = wire_regions.begin(); it!=wire_regions.end(); it++){
174  int wire_no = *it;
175  if (avgs.find(wire_no) == avgs.end()) {
176  avgs[wire_no] = realseq_t(nsamples);
177  }
178  for (auto it1 = fresp_map.begin(); it1!= fresp_map.end(); it1++){
179  int resp_num = (*it1).first;
180  realseq_t& response = (*it1).second;
181  double low_limit = pitch_pos_range_map[resp_num].first;
182  double high_limit = pitch_pos_range_map[resp_num].second;
183  if (low_limit < (wire_no - 0.5)*pitch ){
184  low_limit = (wire_no - 0.5)*pitch;
185  }
186  if (high_limit > (wire_no+0.5)*pitch ){
187  high_limit = (wire_no+0.5)*pitch;
188  }
189 
190  //
191 
192  if (high_limit > low_limit){
193  for (int k=0;k!=nsamples;k++){
194  avgs[wire_no].at(k) += response.at(k) * (high_limit - low_limit) / pitch;
195  }
196  }
197  }
198  }
199 
200 
201  // do average.
202  for (auto it : avgs) {
203  int region = it.first;
204  realseq_t& response = it.second;
205 
206  double sum = 0;
207  for (int k=0;k!=nsamples;k++){
208  sum += response.at(k);
209  }
210 
211  // pack up everything for return.
212  newpaths.push_back(PathResponse(response, region*pitch, 0.0));
213  }
214  newplanes.push_back(PlaneResponse(newpaths,
215  plane.planeid,
216  plane.location,
217  plane.pitch));
218  }
219  return FieldResponse(newplanes, fr.axis, fr.origin, fr.tstart, fr.period, fr.speed);
220 }
221 
222 
223 
225 {
226  using namespace WireCell::Waveform;
227  using namespace WireCell::Response::Schema;
228 
230 
231  std::vector<PlaneResponse> newplanes;
232  for (auto plane : fr_wire_avg.planes) {
233  std::vector<PathResponse> newpaths;
234 
235  int nsamples = Response::as_array(plane).cols();
236 
237  realseq_t ave_response(nsamples,0);
238 
239  for (auto path : plane.paths) {
240  for (int k=0;k!=nsamples;k++){
241  ave_response.at(k) += path.current.at(k);
242  }
243  }
244 
245  newpaths.push_back(PathResponse(ave_response,0.0,0.0));
246 
247  newplanes.push_back(PlaneResponse(newpaths,
248  plane.planeid,
249  plane.location,
250  plane.pitch));
251  }
252  return FieldResponse(newplanes, fr.axis, fr.origin, fr.tstart, fr.period, fr.speed);
253 }
254 
255 
256 Array::array_xxf Response::as_array(const Schema::PlaneResponse& pr, int set_nrows, int set_ncols)
257 {
258  int nrows = pr.paths.size();
259  int ncols = pr.paths[0].current.size();
260  Array::array_xxf ret= Array::array_xxf::Zero(set_nrows, set_ncols); // warning, uninitialized
261 
262  if (set_nrows< nrows || set_ncols < ncols){
263  error("Response: array dimension not correct! ");
264  return ret;
265  }
266 
267  for (int irow = 0; irow < nrows; ++irow) {
268  if (irow < set_nrows){
269  auto& path = pr.paths[irow];
270  for (int icol = 0; icol < ncols; ++icol) {
271  if (icol < set_ncols)
272  ret(irow,icol) = path.current[icol]; // maybe there is a fast way to do this copy?
273  }
274  }
275  }
276  return ret;
277 }
278 
280 {
281  int nrows = pr.paths.size();
282  int ncols = pr.paths[0].current.size();
283  Array::array_xxf ret(nrows, ncols); // warning, uninitialized
284 
285  for (int irow = 0; irow < nrows; ++irow) {
286  auto& path = pr.paths[irow];
287  for (int icol = 0; icol < ncols; ++icol) {
288  ret(irow,icol) = path.current[icol]; // maybe there is a fast way to do this copy?
289  }
290  }
291  return ret;
292 }
293 
294 
295 
297 {
298 
299 
300 }
301 
302 // FIXME: eradicate Domain in favor of Binning
304 {
305  WireCell::Waveform::realseq_t ret(nsamples);
306  const double tick = (domain.second-domain.first)/nsamples;
307  for (int ind=0; ind < nsamples; ++ind) {
308  double t = domain.first + ind*tick;
309  ret[ind] = (*this)(t);
310  }
311  return ret;
312 }
314 {
315  const int nsamples = tbins.nbins();
316  WireCell::Waveform::realseq_t ret(nsamples, 0.0);
317  for (int ind=0; ind<nsamples; ++ind) {
318  const double time = tbins.center(ind);
319  ret[ind] = (*this)(time);
320  }
321  return ret;
322 }
323 
324 
325 
326 
327 /*
328  Cold Electronics response function.
329 
330  How was this function derived?
331 
332  1. Hucheng provided a transfer function of our electronics, which
333  is the Laplace transformation of the shaping function.
334 
335  2. We did a anti-Laplace inverse of the shaping function
336 
337  3. The one you saw in the code is basically the result of the inversion
338 
339  - time is time in system of units
340 
341  - gain in units of volts/charge gives peak value of the response to
342  a delta function of current integrating to unit charge.
343 
344  - shaping is the shaping time in system of units
345 
346  - the hard-coded numbers are the result of the inverting the
347  Lapalace transformation in Mathematica.
348 
349  */
350 double Response::coldelec(double time, double gain, double shaping)
351 {
352  if (time <=0 || time >= 10 * units::microsecond) { // range of validity
353  return 0.0;
354  }
355 
356  const double reltime = time/shaping;
357 
358  // a scaling is needed to make the anti-Lapalace peak match the expected gain
359  // fixme: this scaling is slightly dependent on shaping time. See response.py
360  gain *= 10*1.012;
361 
362  return 4.31054*exp(-2.94809*reltime)*gain
363  -2.6202*exp(-2.82833*reltime)*cos(1.19361*reltime)*gain
364  -2.6202*exp(-2.82833*reltime)*cos(1.19361*reltime)*cos(2.38722*reltime)*gain
365  +0.464924*exp(-2.40318*reltime)*cos(2.5928*reltime)*gain
366  +0.464924*exp(-2.40318*reltime)*cos(2.5928*reltime)*cos(5.18561*reltime)*gain
367  +0.762456*exp(-2.82833*reltime)*sin(1.19361*reltime)*gain
368  -0.762456*exp(-2.82833*reltime)*cos(2.38722*reltime)*sin(1.19361*reltime)*gain
369  +0.762456*exp(-2.82833*reltime)*cos(1.19361*reltime)*sin(2.38722*reltime)*gain
370  -2.620200*exp(-2.82833*reltime)*sin(1.19361*reltime)*sin(2.38722*reltime)*gain
371  -0.327684*exp(-2.40318*reltime)*sin(2.5928*reltime)*gain +
372  +0.327684*exp(-2.40318*reltime)*cos(5.18561*reltime)*sin(2.5928*reltime)*gain
373  -0.327684*exp(-2.40318*reltime)*cos(2.5928*reltime)*sin(5.18561*reltime)*gain
374  +0.464924*exp(-2.40318*reltime)*sin(2.5928*reltime)*sin(5.18561*reltime)*gain;
375 }
376 
377 double Response::hf_filter(double freq, double sigma, double power, bool flag){
378  if (flag){
379  if (freq==0) return 0;
380  }
381 
382  return exp(-0.5*pow(freq/sigma,power));
383 
384 }
385 
386 double Response::lf_filter(double freq, double tau){
387  return 1-exp(-pow(freq/tau,2));
388 }
389 
390 
391 
392 Response::ColdElec::ColdElec(double gain, double shaping)
393  : _g(gain)
394  , _s(shaping)
395 {
396 }
398 {
399 }
400 
402 {
403  return coldelec(time, _g, _s);
404 }
405 
406 
407 Response::SimpleRC::SimpleRC(double width, double tick, double offset)
408  : _width(width), _offset(offset), _tick(tick)
409 
410 {
411 }
413 {
414 }
416 {
417  double ret = 0;
418  if (_width > 0){
419  ret += -_tick/_width * exp(-(time-_offset)/_width); // _tick here is to make this RC response integrated in each bin
420  }
421  if (time < _offset + _tick) { // just the first bin
422  ret += 1.0; // delta function
423  }
424  return ret;
425 }
426 
427 
428 // Vary field response to study systematics
429 // Currently a Gaussian function
430 Response::SysResp::SysResp(double tick, double magnitude, double smear, double offset)
431  : _tick(tick), _mag(magnitude), _smear(smear), _offset(offset)
432 {
433 }
435 {
436 }
438 {
439  double ret = 0;
440  if(_smear > 0){
441  ret = _tick*exp(-0.5*pow((time-_offset)/_smear, 2))/_smear*0.3989422804;
442  }
443  else if(time < _tick+_offset && time >=_offset){
444  ret +=1.0;
445  }
446  else{
447  ret = 0;
448  }
449  return ret*_mag;
450 }
451 
452 
454  : _tau(tau)
455 {
456 }
457 
459 }
460 
461 double Response::LfFilter::operator()(double freq) const
462 {
463  return lf_filter(freq,_tau);
464 }
465 
466 
467 Response::HfFilter::HfFilter(double sigma, double power, bool flag)
468  : _sigma(sigma)
469  , _power(power)
470  , _flag(flag)
471 {
472 }
473 
475 }
476 
477 double Response::HfFilter::operator()(double freq) const
478 {
479  return hf_filter(freq,_sigma,_power,_flag);
480 }
481 
482 
double pitch
The pitch distance between neighboring wires.
Definition: Response.h:65
double tstart
Time at which drift paths begin.
Definition: Response.h:89
Sequence< real_t > realseq_t
Definition: Waveform.h:31
double center(int ind) const
Definition: Binning.h:86
virtual double operator()(double freq) const
Definition: Response.cxx:477
SimpleRC(double width=1.0 *units::ms, double tick=0.5 *units::us, double offset=0.0)
Definition: Response.cxx:407
constexpr T pow(T x)
Definition: pow.h:72
Schema::FieldResponse wire_region_average(const Schema::FieldResponse &fr)
Definition: Response.cxx:99
PlaneResponse * plane(int ident)
Definition: Response.h:97
error
Definition: include.cc:26
static const double microsecond
Definition: Units.h:94
double period
The sampling period of the response function.
Definition: Response.h:92
string filename
Definition: train.py:213
HfFilter(double sigma, double power, bool flag)
Definition: Response.cxx:467
const double tick
void dump(const char *filename, const FieldResponse &fr)
Definition: Response.cxx:90
virtual double operator()(double time) const
Definition: Response.cxx:415
Binning tbins(nticks, t0, t0+readout_time)
const double width
Array::array_xxf as_array(const Schema::PlaneResponse &pr)
Definition: Response.cxx:279
Schema::FieldResponse average_1D(const Schema::FieldResponse &fr)
Definition: Response.cxx:224
std::pair< double, double > Domain
Definition: Waveform.h:75
double coldelec(double time, double gain=7.8, double shaping=1.0 *units::us)
The cold electronics response function.
Definition: Response.cxx:350
virtual double operator()(double freq) const
Definition: Response.cxx:461
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
Definition: document.h:2106
WireCell::Waveform::realseq_t generate(const WireCell::Waveform::Domain &domain, int nsamples)
FIXME: eradicate Domain in favor of Binning.
Definition: Response.cxx:303
virtual double operator()(double time) const
Definition: Response.cxx:437
Json::Value load(const std::string &filename, const externalvars_t &extvar=externalvars_t(), const externalvars_t &extcode=externalvars_t())
Definition: Persist.cxx:121
static Entry * current
std::vector< PlaneResponse > planes
List of PlaneResponse objects.
Definition: Response.h:78
ColdElec(double gain=14 *units::mV/units::fC, double shaping=1.0 *units::us)
Definition: Response.cxx:392
static int max(int a, int b)
Point Vector
An alias for Point.
Definition: Point.h:18
IFrame::pointer sum(std::vector< IFrame::pointer > frames, int ident)
Definition: FrameUtil.cxx:15
FieldResponse load(const char *filename)
Definition: Response.cxx:39
Definition: Main.h:22
double hf_filter(double freq, double sigma=1, double power=2, bool zero_freq_removal=true)
Definition: Response.cxx:377
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
realseq_t magnitude(const compseq_t &seq)
Return the magnitude or absolute value of the sequence.
Definition: Waveform.cxx:65
virtual double operator()(double time) const
Definition: Response.cxx:401
unsigned nrows(sqlite3 *db, std::string const &tablename)
Definition: helpers.cc:84
std::vector< PathResponse > paths
List of PathResponse objects.
Definition: Response.h:54
int nbins() const
Definition: Binning.h:42
Hold info about multiple plane responses in the detector.
Definition: Response.h:75
int planeid
A numerical identifier for the plane.
Definition: Response.h:57
SysResp(double tick=0.5 *units::us, double magnitude=1.0, double smear=0.0 *units::us, double offset=0.0 *units::us)
Definition: Response.cxx:430
Eigen::ArrayXXf array_xxf
A real, 2D array.
Definition: Array.h:54
double lf_filter(double freq, double tau=0.02)
Definition: Response.cxx:386
double speed
The nominal drift speed.
Definition: Response.h:95
void error(const char *fmt, const Args &...args)
Definition: spdlog.h:201