GaussianDiffusion.cxx
Go to the documentation of this file.
2 
3 #include <iostream> // debugging
4 
5 using namespace WireCell;
6 using namespace std;
7 
8 std::vector<double> Gen::GausDesc::sample(double start, double step, int nsamples) const
9 {
10  std::vector<double> ret;
11 
12  if(!sigma){
13  ret.resize(1, 0);
14  ret[0] = 1;
15  if(nsamples != 1){
16  cerr<<"NOT one sample for true point source: "<<nsamples<<"\n";
17  }
18 
19  }
20  else{
21  ret.resize(nsamples, 0.0);
22  for (int ind=0; ind<nsamples; ++ind) {
23  const double rel = (start + ind*step - center)/sigma;
24  ret[ind] = exp(-0.5*rel*rel);
25  }
26  }
27 
28  return ret;
29 }
30 
31 std::vector<double> Gen::GausDesc::binint(double start, double step, int nbins) const
32 {
33  std::vector<double> bins;
34 
35  if(!sigma){
36  bins.resize(1, 0);
37  bins[0] = 1;
38  if(nbins != 1){
39  cerr<<"NOT one bin for true point source: "<<nbins<<"\n";
40  }
41  }
42  else{
43  bins.resize(nbins, 0.0);
44  std::vector<double> erfs(nbins+1, 0.0);
45  const double sqrt2 = sqrt(2.0);
46  for (int ind=0; ind <= nbins; ++ind) {
47  double x = (start + step * ind - center)/(sqrt2*sigma);
48  erfs[ind] = 0.5*std::erf(x);
49  }
50 
51  double tot = 0.0;
52  for (int ibin=0; ibin < nbins; ++ibin) {
53  const double val = erfs[ibin+1] - erfs[ibin];
54  tot += val;
55  bins[ibin] = val;
56  }
57  }
58  return bins;
59 }
60 
61 // integral Normal distribution with weighting function
62 // a linear weighting for the charge in each pbin
63 // Integral of charge spectrum <pvec> done by GausDesc::binint (do not do it again using Erf())
64 std::vector<double> Gen::GausDesc::weight(double start, double step, int nbins, std::vector<double> pvec) const
65 {
66  std::vector<double> wt;
67  if(!sigma){
68  wt.resize(1, 0);
69  wt[0] = (start+step - center)/step;
70  }
71  else{
72  wt.resize(nbins, 0.0);
73  const double pi = 4.0*atan(1);
74  double x2 = start;
75  double x1 = 0;
76  double gaus2 = exp(-0.5*(start-center)/sigma*(start-center)/sigma);
77  double gaus1 = 0;
78  for (int ind=0; ind<nbins; ind++)
79  {
80  x1 = x2;
81  x2 = x1 + step;
82  double rel = (x2-center)/sigma;
83  gaus1 = gaus2;
84  gaus2 = exp(-0.5*rel*rel);
85 
86  // weighting
87  wt[ind] = -1.0*sigma/(x1-x2)*(gaus2-gaus1)/sqrt(2.0*pi)/pvec[ind] + (center-x2)/(x1-x2);
88  /* std::cerr<<"Gaus: "<<"1 and 2 "<<gaus1<<", "<<gaus2<<std::endl; */
89  /* std::cerr<<"center, x1, x2: "<<center<<", "<<x1<<", "<<x2<<std::endl; */
90  /* std::cerr<<"Total charge: "<<pvec[ind]<<std::endl; */
91  /* std::cerr<<"weight: "<<ind<<" "<<wt[ind]<<std::endl; */
92  }
93  }
94  return wt;
95 }
96 
97 
98 // std::pair<int,int> Gen::GausDesc::subsample_range(int nsamples, double xmin, double xmax, double nsigma) const
99 // {
100 // const double sample_size = (xmax-xmin)/(nsamples-1);
101 // // find closest sample indices
102 // int imin = int(round((center - nsigma*sigma - xmin)/sample_size));
103 // int imax = int(round((center + nsigma*sigma - xmin)/sample_size));
104 
105 // return std::make_pair(std::max(imin, 0), std::min(imax+1, nsamples));
106 // }
107 
108 
109 
110 /// GaussianDiffusion
111 
112 
114  const GausDesc& time_desc,
115  const GausDesc& pitch_desc)
116  : m_deposition(depo)
117  , m_time_desc(time_desc)
118  , m_pitch_desc(pitch_desc)
119  , m_toffset_bin(-1)
120  , m_poffset_bin(-1)
121 {
122 }
123 
124 void Gen::GaussianDiffusion::set_sampling(const Binning& tbin, // overall time tick binning
125  const Binning& pbin, // overall impact position binning
126  double nsigma,
127  IRandom::pointer fluctuate,
128  unsigned int weightstrat)
129 {
130  if (m_patch.size() > 0) {
131  return;
132  }
133 
134  /// Sample time dimension
135  auto tval_range = m_time_desc.sigma_range(nsigma);
136  auto tbin_range = tbin.sample_bin_range(tval_range.first, tval_range.second);
137  const size_t ntss = tbin_range.second - tbin_range.first;
138  m_toffset_bin = tbin_range.first;
139  //auto tvec = m_time_desc.sample(tbin.center(m_toffset_bin), tbin.binsize(), ntss);
140  auto tvec = m_time_desc.binint(tbin.edge(m_toffset_bin), tbin.binsize(), ntss);
141 
142  if (!ntss) {
143  cerr << "Gen::GaussianDiffusion: no time bins for [" << tval_range.first/units::us << "," << tval_range.second/units::us << "] us\n";
144  return;
145  }
146 
147  /// Sample pitch dimension.
148  auto pval_range = m_pitch_desc.sigma_range(nsigma);
149  auto pbin_range = pbin.sample_bin_range(pval_range.first, pval_range.second);
150  const size_t npss = pbin_range.second - pbin_range.first;
151  m_poffset_bin = pbin_range.first;
152  //auto pvec = m_pitch_desc.sample(pbin.center(m_poffset_bin), pbin.binsize(), npss);
153  auto pvec = m_pitch_desc.binint(pbin.edge(m_poffset_bin), pbin.binsize(), npss);
154 
155 
156  if (!npss) {
157  cerr << "No impact bins [" << pval_range.first/units::mm << "," << pval_range.second/units::mm << "] mm\n";
158  return;
159  }
160 
161  // weightstrat = 1;
162 
163  // make charge weights for later interpolation.
164  /// fixme: for hanyu.
165  if(weightstrat == 2){
166  auto wvec = m_pitch_desc.weight(pbin.edge(m_poffset_bin), pbin.binsize(), npss, pvec);
167  m_qweights = wvec;
168  }
169  if(weightstrat == 1){
170  m_qweights.resize(npss, 0.5);
171  }
172 
173  // start making the time vs impact patch of charge.
174  patch_t ret = patch_t::Zero(npss, ntss);
175  double raw_sum=0.0;
176 
177  // Convolve the two independent Gaussians
178  for (size_t ip = 0; ip < npss; ++ip) {
179  for (size_t it = 0; it < ntss; ++it) {
180  const double val = pvec[ip]*tvec[it];
181  raw_sum += val;
182  ret(ip,it) = (float)val;
183  }
184  }
185 
186  // normalize to total charge
187  ret *= m_deposition->charge() / raw_sum;
188 
189  const double charge_sign = m_deposition->charge() < 0 ? -1 : 1;
190 
191  double fluc_sum = 0;
192  if (fluctuate) {
193  double unfluc_sum = 0;
194 
195  for (size_t ip = 0; ip < npss; ++ip) {
196  for (size_t it = 0; it < ntss; ++it) {
197  const float oldval = ret(ip,it);
198  unfluc_sum += oldval;
199  // should be a multinomial distribution, n_i follows binomial distribution
200  // but n_i, n_j has covariance -n_tot * p_i * p_j
201  // normalize later to approximate this multinomial distribution (how precise?)
202  // how precise? better than poisson and 10000 total charge corresponds to a <1% level error.
203  float number = fluctuate->binomial((int)(std::abs(m_deposition->charge())), oldval/m_deposition->charge());
204  //the charge should be netagive -- ionization electrons
205  fluc_sum += charge_sign*number;
206  ret(ip,it) = charge_sign*number;
207  }
208  }
209  if (fluc_sum == 0) {
210  // cerr << "No net charge after fluctuation. Total unfluctuated = "
211  // << unfluc_sum
212  // << " Qdepo = " << m_deposition->charge()
213  // << endl;
214  return;
215  }
216  else {
217  ret *= m_deposition->charge() / fluc_sum;
218  }
219  }
220 
221 
222  { // debugging
223  //double retsum=0.0;
224  //for (size_t ip = 0; ip < npss; ++ip) {
225  // for (size_t it = 0; it < ntss; ++it) {
226  // retsum += ret(ip,it);
227  // }
228  //}
229  // cerr << "GaussianDiffusion: Q in electrons: depo=" << m_deposition->charge()/units::eplus
230  // << " rawsum=" << raw_sum/units::eplus << " flucsum=" << fluc_sum/units::eplus
231  // << " returned=" << retsum/units::eplus << endl;
232  }
233 
234 
235  m_patch = ret;
236 }
237 
239  m_patch.resize(0,0);
240  m_qweights.clear();
241  m_qweights.shrink_to_fit();
242 }
243 
244 // patch = nimpacts rows X nticks columns
245 // patch(row,col)
247 {
248  return m_patch;
249 }
250 
251 const std::vector<double> Gen::GaussianDiffusion::weights() const
252 {
253  return m_qweights;
254 }
255 
256 
std::shared_ptr< const IDepo > pointer
Definition: IData.h:19
const std::vector< double > weights() const
STL namespace.
std::vector< double > binint(double start, double step, int nbins) const
void set_sampling(const Binning &tbin, const Binning &pbin, double nsigma=3.0, IRandom::pointer fluctuate=nullptr, unsigned int weightstrat=1)
double binsize() const
Definition: Binning.h:73
std::vector< double > weight(double start, double step, int nbins, std::vector< double > pvec) const
T abs(T value)
std::pair< double, double > sigma_range(double nsigma=3.0)
std::shared_ptr< Interface > pointer
Definition: Interface.h:16
GaussianDiffusion(const IDepo::pointer &depo, const GausDesc &time_desc, const GausDesc &pitch_desc)
GaussianDiffusion.
std::pair< int, int > tbin_range(const ITrace::vector &traces)
Definition: FrameTools.cxx:143
static const double mm
Definition: Units.h:55
std::vector< double > sample(double start, double step, int nsamples) const
Definition: Main.h:22
def center(depos, point)
Definition: depos.py:117
static const double us
Definition: Units.h:105
list x
Definition: train.py:276
double edge(int ind) const
Definition: Binning.h:99
std::pair< int, int > sample_bin_range(double minval, double maxval) const
Definition: Binning.h:116
float pi
Definition: units.py:11