Drifter.cxx
Go to the documentation of this file.
1 #include "WireCellGen/Drifter.h"
3 #include "WireCellUtil/Units.h"
4 #include "WireCellUtil/String.h"
5 
10 
11 #include <boost/range.hpp>
12 
13 #include <sstream>
14 
17 
18 using namespace std;
19 using namespace WireCell;
20 
21 bool Gen::Drifter::DepoTimeCompare::operator()(const IDepo::pointer& lhs, const IDepo::pointer& rhs) const
22 {
23  if (lhs->time() == rhs->time()) {
24  if (lhs->pos().x() == lhs->pos().x()) {
25  return lhs.get() < rhs.get(); // break tie by pointer
26  }
27  return lhs->pos().x() < lhs->pos().x();
28  }
29  return lhs->time() < rhs->time();
30 }
31 
32 
33 // Xregion helper
34 
35 Gen::Drifter::Xregion::Xregion(Configuration cfg)
36  : anode(0.0)
37  , response(0.0)
38  , cathode(0.0)
39 {
40  auto ja = cfg["anode"];
41  auto jr = cfg["response"];
42  auto jc = cfg["cathode"];
43  if (ja.isNull()) { ja = jr; }
44  if (jr.isNull()) { jr = ja; }
45  anode = ja.asDouble();
46  response = jr.asDouble();
47  cathode = jc.asDouble();
48 }
50 {
51  return (anode < x and x < response) or (response < x and x < anode);
52 }
54 {
55  return (response < x and x < cathode) or (cathode < x and x < response);
56 }
57 
58 
60  : m_rng(nullptr)
61  , m_rng_tn("Random")
62  , m_DL(7.2 * units::centimeter2/units::second) // from arXiv:1508.07059v2
63  , m_DT(12.0 * units::centimeter2/units::second) // ditto
64  , m_lifetime(8*units::ms) // read off RHS of figure 6 in MICROBOONE-NOTE-1003-PUB
65  , m_fluctuate(true)
66  , m_speed(1.6*units::mm/units::us)
67  , m_toffset(0.0)
68  , n_dropped(0)
69  , n_drifted(0)
70  , l(Log::logger("sim"))
71 {
72 }
73 
75 {
76 }
77 
79 {
81  cfg["DL"] = m_DL;
82  cfg["DT"] = m_DT;
83  cfg["lifetime"] = m_lifetime;
84  cfg["fluctuate"] = m_fluctuate;
85  cfg["drift_speed"] = m_speed;
86  cfg["time_offset"] = m_toffset;
87 
88  // see comments in .h file
89  cfg["xregions"] = Json::arrayValue;
90  return cfg;
91 }
92 
94 {
95  reset();
96 
97  m_rng_tn = get(cfg, "rng", m_rng_tn);
98  m_rng = Factory::find_tn<IRandom>(m_rng_tn);
99 
100  m_DL = get<double>(cfg, "DL", m_DL);
101  m_DT = get<double>(cfg, "DT", m_DT);
102  m_lifetime = get<double>(cfg, "lifetime", m_lifetime);
103  m_fluctuate = get<bool>(cfg, "fluctuate", m_fluctuate);
104  m_speed = get<double>(cfg, "drift_speed", m_speed);
105  m_toffset = get<double>(cfg, "time_offset", m_toffset);
106 
107  auto jxregions = cfg["xregions"];
108  if (jxregions.empty()) {
109  l->critical("no xregions given so I can do nothing");
110  THROW(ValueError() << errmsg{"no xregions given"});
111  }
112  for (auto jone : jxregions) {
113  m_xregions.push_back(Xregion(jone));
114  }
115  l->debug("Drifter: time offset: {} ms, drift speed: {} mm/us",
116  m_toffset/units::ms, m_speed/(units::mm/units::us));
117 }
118 
120 {
121  m_xregions.clear();
122 }
123 
124 
126 {
127  // electrical charge to drift. Electrons should be negative
128  const double Qi = depo->charge();
129  if (Qi == 0.0) {
130  // Yes, some silly depo sources ask us to drift nothing....
131  return false;
132  }
133 
134  // Find which X region to add, or reject. Maybe there is a faster
135  // way to do this than a loop. For example, the regions could be
136  // examined in order to find some regular binning of the X axis
137  // and then at worse only explicitly check extent partial bins
138  // near to their edges.
139 
140 
141 
142  double respx = 0, direction = 0.0;
143 
144  auto xrit = std::find_if(m_xregions.begin(), m_xregions.end(),
146  if (xrit != m_xregions.end()) {
147  // Back up in space and time. This is a best effort fudge. See:
148  // https://github.com/WireCell/wire-cell-gen/issues/22
149 
150  respx = xrit->response;
151  direction = -1.0;
152  }
153  else {
154  xrit = std::find_if(m_xregions.begin(), m_xregions.end(),
156  if (xrit != m_xregions.end()) { // in bulk
157  respx = xrit->response;
158  direction = 1.0;
159  }
160  }
161  if (xrit == m_xregions.end()) {
162  return false; // outside both regions
163  }
164 
165  Point pos = depo->pos();
166  const double dt = std::abs((respx - pos.x())/m_speed);
167  pos.x(respx);
168 
169 
170  double dL = depo->extent_long();
171  double dT = depo->extent_tran();
172 
173  double Qf = Qi;
174  if (direction > 0) {
175  // final number of electrons after drift if no fluctuation.
176  const double absorbprob = 1 - exp(-dt/m_lifetime);
177 
178  double dQ = Qi * absorbprob;
179 
180  // How many electrons remain, with fluctuation.
181  // Note: fano/recomb fluctuation should be done before the depo was first made.
182  if (m_fluctuate) {
183  double sign = 1.0;
184  if (Qi < 0) {
185  sign = -1.0;
186  }
187  dQ = sign*m_rng->binomial((int)std::abs(Qi), absorbprob);
188  }
189  Qf = Qi - dQ;
190 
191  dL = sqrt(2.0*m_DL*dt + dL*dL);
192  dT = sqrt(2.0*m_DT*dt + dT*dT);
193  }
194 
195  auto newdepo = make_shared<SimpleDepo>(depo->time() + direction*dt + m_toffset, pos, Qf, depo, dL, dT);
196  xrit->depos.insert(newdepo);
197  return true;
198 }
199 
200 
201 bool by_time(const IDepo::pointer& lhs, const IDepo::pointer& rhs) {
202  return lhs->time() < rhs->time();
203 }
204 
205 // save all cached depos to the output queue sorted in time order
207 {
208  for (auto& xr : m_xregions) {
209  l->debug("xregion: anode: {} mm, response: {} mm, cathode: {} mm, flushing {}",
210  xr.anode/units::mm, xr.response/units::mm,
211  xr.cathode/units::mm, xr.depos.size());
212  outq.insert(outq.end(), xr.depos.begin(), xr.depos.end());
213  xr.depos.clear();
214  }
215  std::sort(outq.begin(), outq.end(), by_time);
216  outq.push_back(nullptr);
217 }
218 
220 {
221  // It might be faster to use a sorted set which would avoid an
222  // exhaustive iteration of each depos stash. Or not.
223  for (auto& xr : m_xregions) {
224  if (xr.depos.empty()) {
225  continue;
226  }
227 
228  Xregion::ordered_depos_t::const_iterator depo_beg=xr.depos.begin(), depo_end=xr.depos.end();
229  Xregion::ordered_depos_t::iterator depoit = depo_beg;
230  while (depoit != depo_end) {
231  if ((*depoit)->time() < now) {
232  ++depoit;
233  continue;
234  }
235  break;
236  }
237  if (depoit == depo_beg) {
238  continue;
239  }
240 
241  outq.insert(outq.end(), depo_beg, depoit);
242  xr.depos.erase(depo_beg, depoit);
243  }
244  if (outq.empty()) {
245  return;
246  }
247  std::sort(outq.begin(), outq.end(), by_time);
248 }
249 
250 
251 // always returns true because by hook or crook we consume the input.
253 {
254  if (m_speed <= 0.0) {
255  l->error("illegal drift speed: {}", m_speed);
256  return false;
257  }
258 
259  if (!depo) { // no more inputs expected, EOS flush
260 
261  flush(outq);
262 
263  if (n_dropped) {
264  l->debug("at EOS, dropped {} / {} depos from stream, outside of all {} drift regions", n_dropped, n_dropped+n_drifted, m_xregions.size());
265  }
266  n_drifted = n_dropped = 0;
267  return true;
268  }
269 
270  bool ok = insert(depo);
271  if (!ok) {
272  ++n_dropped; // depo is outside our regions but
273  return true; // nothing changed, so just bail
274  }
275  ++n_drifted;
276 
277  // At this point, time has advanced and maybe some drifted repos
278  // are ripe for removal.
279 
280  flush_ripe(outq, depo->time());
281 
282  return true;
283 }
284 
intermediate_table::iterator iterator
std::shared_ptr< const IDepo > pointer
Definition: IData.h:19
bool by_time(const IDepo::pointer &lhs, const IDepo::pointer &rhs)
Definition: Drifter.cxx:201
bool inside_bulk(double x) const
Definition: Drifter.cxx:53
virtual void reset()
Definition: Drifter.cxx:119
boost::error_info< struct tag_errmsg, std::string > errmsg
Definition: Exceptions.h:54
STL namespace.
std::vector< Xregion > m_xregions
Definition: Drifter.h:170
intermediate_table::const_iterator const_iterator
cfg
Definition: dbjson.py:29
IRandom::pointer m_rng
Definition: Drifter.h:131
static const double ms
Definition: Units.h:104
std::deque< output_pointer > output_queue
T abs(T value)
void flush_ripe(output_queue &outq, double now)
Definition: Drifter.cxx:219
std::string m_rng_tn
Definition: Drifter.h:136
bool insert(const input_pointer &depo)
Definition: Drifter.cxx:125
Log::logptr_t l
Definition: Drifter.h:183
#define THROW(e)
Definition: Exceptions.h:25
bool inside_response(double x) const
Definition: Drifter.cxx:49
void flush(output_queue &outq)
Definition: Drifter.cxx:206
logptr_t logger(std::string name)
Definition: Logging.cxx:71
Thrown when a wrong value has been encountered.
Definition: Exceptions.h:37
static const double mm
Definition: Units.h:55
static const double mm
Definition: Units.h:73
T x(const T &val)
Definition: D3Vector.h:55
Definition: Main.h:22
virtual bool operator()(const input_pointer &depo, output_queue &outq)
The calling signature:
Definition: Drifter.cxx:252
int sign(double val)
Definition: UtilFunc.cxx:104
static const double us
Definition: Units.h:101
static const double ms
Definition: Units.h:100
std::shared_ptr< IDrifter > pointer
Definition: IDrifter.h:21
Json::Value Configuration
Definition: Configuration.h:50
static const double us
Definition: Units.h:105
std::shared_ptr< const IDepo > input_pointer
list x
Definition: train.py:276
virtual WireCell::Configuration default_configuration() const
Optional, override to return a hard-coded default configuration.
Definition: Drifter.cxx:78
WIRECELL_FACTORY(Drifter, WireCell::Gen::Drifter, WireCell::IDrifter, WireCell::IConfigurable) using namespace std
second_as<> second
Type of time stored in seconds, in double precision.
Definition: spacetime.h:80
virtual void configure(const WireCell::Configuration &config)
WireCell::IConfigurable interface.
Definition: Drifter.cxx:93
static const double centimeter2
Definition: Units.h:53