Garfield++ v2r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
DriftLineRKF.cc
Go to the documentation of this file.
1#include <iostream>
2#include <iomanip>
3#include <cmath>
4
5#include "DriftLineRKF.hh"
8
9namespace Garfield {
10
12 : m_sensor(NULL),
13 m_medium(NULL),
14 m_maxStepSize(1.e8),
15 m_accuracy(1.e-8),
16 m_maxSteps(1000),
17 m_maxStepsToWire(1000),
18 m_rejectKinks(true),
19 m_useStepSizeLimit(false),
20 m_usePlotting(false),
21 m_view(NULL),
22 m_status(0),
23 m_nPoints(0),
24 m_scaleElectronSignal(1.),
25 m_scaleHoleSignal(1.),
26 m_scaleIonSignal(1.),
27 m_debug(false),
28 m_verbose(false) {
29
30 const unsigned int nMaxPoints = m_maxSteps + m_maxStepsToWire + 10;
31 m_path.resize(nMaxPoints);
32 m_className = "DriftLineRKF";
33}
34
36
37 if (!s) {
38 std::cerr << m_className << "::SetSensor:\n Null pointer.\n";
39 return;
40 }
41 m_sensor = s;
42}
43
45
46 if (a > 0.) {
47 m_accuracy = a;
48 } else {
49 std::cerr << m_className << "::SetIntegrationAccuracy:\n"
50 << " Accuracy must be greater than zero.\n";
51 }
52}
53
54void DriftLineRKF::SetMaximumStepSize(const double ms) {
55
56 if (ms > 0.) {
57 m_maxStepSize = ms;
58 if (!m_useStepSizeLimit) {
59 std::cout << m_className << "::SetMaximumStepSize:\n"
60 << " Don't forget to call EnableStepSizeLimit.\n";
61 }
62 } else {
63 std::cerr << m_className << "::SetMaximumStepSize:\n"
64 << " Step size must be greater than zero.\n";
65 }
66}
67
69
70 if (!view) {
71 std::cerr << m_className << "::EnablePlotting:\n Null pointer.\n";
72 return;
73 }
74 m_usePlotting = true;
75 m_view = view;
76}
77
79
80 m_view = NULL;
81 m_usePlotting = false;
82}
83
84bool DriftLineRKF::DriftElectron(const double x0, const double y0,
85 const double z0, const double t0) {
86
87 m_particleType = ParticleTypeElectron;
88 if (!DriftLine(x0, y0, z0, t0)) return false;
89 GetGain();
90 ComputeSignal(-1., m_scaleElectronSignal);
91 return true;
92}
93
94bool DriftLineRKF::DriftHole(const double x0, const double y0,
95 const double z0, const double t0) {
96
97 m_particleType = ParticleTypeHole;
98 if (!DriftLine(x0, y0, z0, t0)) return false;
99 ComputeSignal(1., m_scaleHoleSignal);
100 return true;
101}
102
103bool DriftLineRKF::DriftIon(const double x0, const double y0,
104 const double z0, const double t0) {
105
106 m_particleType = ParticleTypeIon;
107 if (!DriftLine(x0, y0, z0, t0)) return false;
108 ComputeSignal(1., m_scaleIonSignal);
109 return true;
110}
111
112bool DriftLineRKF::DriftLine(const double x0, const double y0,
113 const double z0, const double t0) {
114
115 // Increase the size of the drift line vector if needed.
116 const unsigned int nMaxPoints = m_maxSteps + m_maxStepsToWire + 10;
117 if (nMaxPoints > m_path.size()) m_path.resize(nMaxPoints);
118
119 // Reset the number of points on the drift line.
120 m_nPoints = 0;
121 // Reset the status flag.
122 m_status = StatusAlive;
123
124 // Check if the sensor is defined.
125 if (!m_sensor) {
126 std::cerr << m_className << "::DriftLine:\n Sensor is not defined.\n";
127 m_status = StatusCalculationAbandoned;
128 return false;
129 }
130
131 // Get the sensor's bounding box.
132 double xmin = 0., xmax = 0.;
133 double ymin = 0., ymax = 0.;
134 double zmin = 0., zmax = 0.;
135 bool bbox = m_sensor->GetArea(xmin, ymin, zmin, xmax, ymax, zmax);
136
137 // Get the electric and magnetic field at the initial position.
138 double ex = 0., ey = 0., ez = 0.;
139 double bx = 0., by = 0., bz = 0.;
140 int status = 0;
141 m_sensor->MagneticField(x0, y0, z0, bx, by, bz, status);
142 m_sensor->ElectricField(x0, y0, z0, ex, ey, ez, m_medium, status);
143 // Make sure the initial position is at a valid location.
144 if (status != 0) {
145 std::cerr << m_className << "::DriftLine:\n"
146 << " No valid field at initial position.\n";
147 m_status = StatusLeftDriftMedium;
148 return false;
149 }
150
151 // Start plotting a new line if requested.
152 int iLine = 0;
153 if (m_usePlotting) {
154 if (m_particleType == ParticleTypeIon) {
155 m_view->NewIonDriftLine(1, iLine, x0, y0, z0);
156 } else if (m_particleType == ParticleTypeElectron) {
157 m_view->NewElectronDriftLine(1, iLine, x0, y0, z0);
158 } else if (m_particleType == ParticleTypeHole) {
159 m_view->NewHoleDriftLine(1, iLine, x0, y0, z0);
160 }
161 }
162
163 // Set the numerical constants for the RKF integration.
164 const double c10 = 214. / 891.;
165 const double c11 = 1. / 33.;
166 const double c12 = 650. / 891.;
167 const double c20 = 533. / 2106.;
168 const double c22 = 800. / 1053.;
169 const double c23 = -1. / 78.;
170
171 const double b10 = 1. / 4.;
172 const double b20 = -189. / 800.;
173 const double b21 = 729. / 800.;
174 const double b30 = 214. / 891.;
175 const double b31 = 1. / 33.;
176 const double b32 = 650. / 891.;
177
178 // Set the charge of the drifting particle.
179 const double charge = m_particleType == ParticleTypeElectron ? -1 : 1;
180 // Initialise the current position.
181 double x = x0;
182 double y = y0;
183 double z = z0;
184 // Initialise the particle velocity.
185 double v0[3] = {0., 0., 0.};
186 if (!GetVelocity(ex, ey, ez, bx, by, bz, v0[0], v0[1], v0[2])) {
187 std::cerr << m_className << "::DriftLine:\n"
188 << " Failed to retrieve drift velocity.\n";
189 return false;
190 }
191 const double vTot = sqrt(v0[0] * v0[0] + v0[1] * v0[1] + v0[2] * v0[2]);
192 if (vTot < Small) {
193 std::cerr << m_className << "::DriftLine:\n"
194 << " Zero velocity at initial position.\n";
195 return false;
196 }
197
198 // Initialise time step and previous time step.
199 double dt = m_accuracy / vTot;
200 double pdt = dt;
201
202 // Set the initial point.
203 m_nPoints = 1;
204 m_path[0].x = x;
205 m_path[0].y = y;
206 m_path[0].z = z;
207 m_path[0].t = t0;
208 int initCycle = 3;
209 while (m_nPoints <= m_maxSteps) {
210 // Get first estimate of the new drift velocity.
211 const double x1 = x + dt * b10 * v0[0];
212 const double y1 = y + dt * b10 * v0[1];
213 const double z1 = z + dt * b10 * v0[2];
214 double v1[3] = {0., 0., 0.};
215 if (!GetVelocity(x1, y1, z1, v1[0], v1[1], v1[2], status)) {
216 m_status = StatusCalculationAbandoned;
217 break;
218 }
219 if (status != 0) {
220 if (status > 0) {
221 if (m_debug) {
222 std::cout << m_className << "::DriftLine: Inside wire, halve.\n";
223 }
224 dt *= 0.5;
225 continue;
226 }
227 if (!EndDriftLine()) m_status = StatusCalculationAbandoned;
228 break;
229 }
230 // Get second estimate of the new drift velocity.
231 const double x2 = x + dt * (b20 * v0[0] + b21 * v1[0]);
232 const double y2 = y + dt * (b20 * v0[1] + b21 * v1[1]);
233 const double z2 = z + dt * (b20 * v0[2] + b21 * v1[2]);
234 double v2[3] = {0., 0., 0.};
235 if (!GetVelocity(x2, y2, z2, v2[0], v2[1], v2[2], status)) {
236 m_status = StatusCalculationAbandoned;
237 break;
238 }
239 if (status != 0) {
240 if (status > 0) {
241 if (m_debug) {
242 std::cout << m_className << "::DriftLine: Inside wire, halve.\n";
243 }
244 dt *= 0.5;
245 continue;
246 }
247 if (!EndDriftLine()) m_status = StatusCalculationAbandoned;
248 break;
249 }
250 // Get third estimate of the new drift velocity.
251 double v3[3] = {0., 0., 0.};
252 const double x3 = x + dt * (b30 * v0[0] + b31 * v1[0] + b32 * v2[0]);
253 const double y3 = y + dt * (b30 * v0[1] + b31 * v1[1] + b32 * v2[1]);
254 const double z3 = z + dt * (b30 * v0[2] + b31 * v1[2] + b32 * v2[2]);
255 if (!GetVelocity(x3, y3, z3, v3[0], v3[1], v3[2], status)) {
256 m_status = StatusCalculationAbandoned;
257 break;
258 }
259 if (status != 0) {
260 if (status > 0) {
261 if (m_debug) {
262 std::cout << m_className << "::DriftLine: Inside wire, halve.\n";
263 }
264 dt *= 0.5;
265 continue;
266 }
267 if (!EndDriftLine()) m_status = StatusCalculationAbandoned;
268 break;
269 }
270
271 // Check if we crossed a wire.
272 double xw = 0., yw = 0., zw = 0.;
273 if (m_sensor->IsWireCrossed(x, y, z, x1, y1, z1, xw, yw, zw) ||
274 m_sensor->IsWireCrossed(x, y, z, x2, y2, z2, xw, yw, zw) ||
275 m_sensor->IsWireCrossed(x, y, z, x3, y3, z3, xw, yw, zw)) {
276 if (m_debug) {
277 std::cout << m_className << "::DriftLine:\n"
278 << " Drift line crossed wire. Reduce step size.\n";
279 }
280 if (dt < Small) {
281 std::cerr << m_className << "::DriftLine: Step size too small. Stop.\n";
282 m_status = StatusCalculationAbandoned;
283 break;
284 }
285 dt *= 0.5;
286 continue;
287 }
288 // Check if we are inside the trap radius of a wire.
289 double rw = 0.;
290 if (m_sensor->IsInTrapRadius(charge, x1, y1, z1, xw, yw, rw) &&
291 m_particleType != ParticleTypeIon) {
292 if (!DriftToWire(xw, yw, rw)) m_status = StatusCalculationAbandoned;
293 break;
294 }
295 if (m_sensor->IsInTrapRadius(charge, x2, y2, z2, xw, yw, rw) &&
296 m_particleType != ParticleTypeIon) {
297 if (!DriftToWire(xw, yw, rw)) m_status = StatusCalculationAbandoned;
298 break;
299 }
300 if (m_sensor->IsInTrapRadius(charge, x3, y3, z3, xw, yw, rw) &&
301 m_particleType != ParticleTypeIon) {
302 if (!DriftToWire(xw, yw, rw)) m_status = StatusCalculationAbandoned;
303 break;
304 }
305 // Calculate the correction terms.
306 double phi1[3] = {0., 0., 0.};
307 double phi2[3] = {0., 0., 0.};
308 for (unsigned int i = 0; i < 3; ++i) {
309 phi1[i] = c10 * v0[i] + c11 * v1[i] + c12 * v2[i];
310 phi2[i] = c20 * v0[i] + c22 * v2[i] + c23 * v3[i];
311 }
312 // Check if the step length is valid.
313 const double phi1Tot =
314 sqrt(phi1[0] * phi1[0] + phi1[1] * phi1[1] + phi1[2] * phi1[2]);
315 if (phi1Tot < Small) {
316 std::cerr << m_className << "::DriftLine:\n"
317 << " Step has zero length. Abandoning drift.\n";
318 m_status = StatusCalculationAbandoned;
319 break;
320 } else if (m_useStepSizeLimit && dt * phi1Tot > m_maxStepSize) {
321 if (m_debug) {
322 std::cout << m_className << "::DriftLine: Step too long, reduce.\n";
323 }
324 dt = 0.5 * m_maxStepSize / phi1Tot;
325 continue;
326 } else if (bbox) {
327 // Don't allow dt to become too large in view of the time resolution.
328 if (dt * fabs(phi1[0]) > 0.1 * fabs(xmax - xmin) ||
329 dt * fabs(phi1[1]) > 0.1 * fabs(ymax - ymin)) {
330 dt *= 0.5;
331 if (m_debug) {
332 std::cout << m_className << "::DriftLine: Step too long, halve.\n";
333 }
334 continue;
335 }
336 } else if (m_rejectKinks && m_nPoints > 1) {
337 if (phi1[0] * (m_path[m_nPoints - 1].x - m_path[m_nPoints - 2].x) +
338 phi1[1] * (m_path[m_nPoints - 1].y - m_path[m_nPoints - 2].y) +
339 phi1[2] * (m_path[m_nPoints - 1].z - m_path[m_nPoints - 2].z) < 0.) {
340 std::cerr << m_className << "::DriftLine: Bending angle > 90 degree.\n";
341 m_status = StatusSharpKink;
342 break;
343 }
344 }
345 if (m_debug) std::cout << m_className << "::DriftLine: Step size ok.\n";
346 // Update the position.
347 x += dt * phi1[0];
348 y += dt * phi1[1];
349 z += dt * phi1[2];
350 // Add a new point to the drift line.
351 ++m_nPoints;
352 m_path[m_nPoints - 1].x = x;
353 m_path[m_nPoints - 1].y = y;
354 m_path[m_nPoints - 1].z = z;
355 m_path[m_nPoints - 1].t = m_path[m_nPoints - 2].t + dt;
356 // Check the new position.
357 m_sensor->ElectricField(x, y, z, ex, ey, ez, m_medium, status);
358 if (status != 0) {
359 // The new position is not inside a valid drift medium.
360 // Go back one step and terminate the drift line.
361 --m_nPoints;
362 if (m_debug) {
363 std::cout << m_className << "::DriftLine:\n"
364 << " New point is outside. Terminate.\n";
365 }
366 if (!EndDriftLine()) m_status = StatusCalculationAbandoned;
367 break;
368 }
369 // Adjust the step size depending on the accuracy of the two estimates.
370 pdt = dt;
371 const double dphi0 = fabs(phi1[0] - phi2[0]);
372 const double dphi1 = fabs(phi1[1] - phi2[1]);
373 const double dphi2 = fabs(phi1[2] - phi2[2]);
374 if (dphi0 > Small || dphi1 > Small || dphi2 > Small) {
375 dt = sqrt(dt * m_accuracy / (dphi0 + dphi1 + dphi2));
376 if (m_debug) {
377 std::cout << m_className << "::DriftLine: Adapting step size to "
378 << dt << " ns (from " << pdt << ").\n";
379 }
380 } else {
381 dt *= 2.;
382 if (m_debug) {
383 std::cout << m_className << "::DriftLine: Double step size.\n";
384 }
385 }
386 // Make sure that dt is different from zero;
387 // this should always be ok.
388 if (dt < Small) {
389 std::cerr << m_className << "::DriftLine:\n"
390 << " Step size is zero (program bug).\n"
391 << " The calculation is abandoned.\n";
392 m_status = StatusCalculationAbandoned;
393 break;
394 }
395 // Check the initial step size.
396 if (initCycle > 0 && dt < pdt / 5.) {
397 if (m_debug) {
398 std::cout << m_className << "::DriftLine: Reinitialise step size.\n";
399 }
400 --initCycle;
401 x = m_path[0].x;
402 y = m_path[0].y;
403 z = m_path[0].z;
404 m_nPoints = 1;
405 continue;
406 }
407 initCycle = 0;
408 // Prevent the step size from growing too fast.
409 if (dt > 10. * pdt) {
410 dt = 10. * pdt;
411 if (m_debug) {
412 std::cout << m_className << "::DriftLine: Limit step size to " << dt << "\n";
413 }
414 }
415 // Stop in case dt tends to become too small.
416 if (dt * (fabs(phi1[0]) + fabs(phi1[1]) + fabs(phi1[2])) < m_accuracy) {
417 std::cerr << m_className << "::DriftLine:\n"
418 << " Step size has become smaller than int. accuracy.\n"
419 << " The calculation is abandoned.\n";
420 m_status = StatusCalculationAbandoned;
421 break;
422 }
423 // Update the velocity.
424 v0[0] = v3[0];
425 v0[1] = v3[1];
426 v0[2] = v3[2];
427
428 if (m_nPoints > m_maxSteps) {
429 m_status = StatusTooManySteps;
430 break;
431 }
432 }
433 if (m_verbose) {
434 // If requested, print step history.
435 std::cout << m_className << "::DriftLine:\n";
436 std::cout << " Drift line status: " << m_status << "\n";
437 std::cout << " Step time time step "
438 << " x y z\n";
439 for (unsigned int i = 0; i < m_nPoints; ++i) {
440 std::cout << std::setw(8) << i << " "
441 << std::fixed << std::setprecision(7)
442 << std::setw(15) << m_path[i].t << " ";
443 if (i > 0) {
444 std::cout << std::setw(15) << m_path[i].t - m_path[i - 1].t << " ";
445 } else {
446 std::cout << std::string(17, ' ');
447 }
448 std::cout << std::setw(15) << m_path[i].x << " "
449 << std::setw(15) << m_path[i].y << " "
450 << std::setw(15) << m_path[i].z << "\n";
451 }
452 }
453 if (m_usePlotting) {
454 for (unsigned int i = 0; i < m_nPoints; ++i) {
455 m_view->AddDriftLinePoint(iLine, m_path[i].x, m_path[i].y, m_path[i].z);
456 }
457 }
458 if (status == StatusCalculationAbandoned) return false;
459 return true;
460}
461
463
464 if (m_nPoints < 2) return 0.;
465 double sum = 0.;
466 for (unsigned int i = 0; i < m_nPoints - 1; ++i) {
467 const unsigned int j = i + 1;
468 sum += IntegrateDiffusion(m_path[i].x, m_path[i].y, m_path[i].z,
469 m_path[j].x, m_path[j].y, m_path[j].z);
470 }
471 return sqrt(sum);
472}
473
475
476 if (m_nPoints < 2) return 0.;
477 if (m_status == StatusCalculationAbandoned) return 0.;
478 // First get a rough estimate of the result.
479 double crude = 0.;
480 double alphaPrev = 0.;
481 for (unsigned int i = 0; i < m_nPoints; ++i) {
482 // Get the Townsend coefficient at this step.
483 const double x = m_path[i].x;
484 const double y = m_path[i].y;
485 const double z = m_path[i].z;
486 double ex, ey, ez;
487 double bx, by, bz;
488 int status;
489 m_sensor->MagneticField(x, y, z, bx, by, bz, status);
490 m_sensor->ElectricField(x, y, z, ex, ey, ez, m_medium, status);
491 if (status != 0) {
492 std::cerr << m_className << "::GetGain:\n"
493 << " Invalid drift line point " << i << ".\n";
494 return 0.;
495 }
496 double alpha = 0.;
497 if (!GetTownsend(ex, ey, ez, bx, by, bz, alpha)) {
498 std::cerr << m_className << "::GetGain:\n"
499 << " Unable to retrieve Townsend coefficient.\n";
500 return 0.;
501 }
502 if (i == 0) {
503 alphaPrev = alpha;
504 continue;
505 }
506 const double dx = x - m_path[i - 1].x;
507 const double dy = y - m_path[i - 1].y;
508 const double dz = z - m_path[i - 1].z;
509 const double d = sqrt(dx * dx + dy * dy + dz * dz);
510 crude += 0.5 * d * (alpha + alphaPrev);
511 alphaPrev = alpha;
512 }
513 // Stop if the rough estimate is negligibly small.
514 if (crude < Small) return 1.;
515
516 // Calculate the integration tolerance based on the rough estimate.
517 const double tol = 1.e-4 * crude;
518 double sum = 0.;
519 m_path[0].alphaint = 0.;
520 for (unsigned int i = 0; i < m_nPoints - 1; ++i) {
521 const unsigned int j = i + 1;
522 sum += IntegrateTownsend(m_path[i].x, m_path[i].y, m_path[i].z,
523 m_path[j].x, m_path[j].y, m_path[j].z, tol);
524 m_path[j].alphaint = sum;
525 }
526 return exp(sum);
527}
528
529bool DriftLineRKF::GetVelocity(const double x, const double y, const double z,
530 double& vx, double& vy, double& vz,
531 int& status) {
532
533 double ex = 0., ey = 0., ez = 0.;
534 double bx = 0., by = 0., bz = 0.;
535 m_sensor->MagneticField(x, y, z, bx, by, bz, status);
536 m_sensor->ElectricField(x, y, z, ex, ey, ez, m_medium, status);
537 // Stop if we are outside a valid drift medium.
538 if (status != 0) return true;
539 if (m_particleType == ParticleTypeElectron) {
540 return m_medium->ElectronVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
541 } else if (m_particleType == ParticleTypeIon) {
542 return m_medium->IonVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
543 } else if (m_particleType == ParticleTypeHole) {
544 return m_medium->HoleVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
545 }
546 std::cerr << m_className << "::GetVelocity:\n"
547 << " Cannot retrieve drift velocity at ("
548 << x << ", " << y << ", " << z << ").\n";
549 return false;
550
551}
552
553bool DriftLineRKF::GetVelocity(const double ex, const double ey,
554 const double ez, const double bx,
555 const double by, const double bz, double& vx,
556 double& vy, double& vz) const {
557
558 if (m_particleType == ParticleTypeElectron) {
559 return m_medium->ElectronVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
560 } else if (m_particleType == ParticleTypeIon) {
561 return m_medium->IonVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
562 } else if (m_particleType == ParticleTypeHole) {
563 return m_medium->HoleVelocity(ex, ey, ez, bx, by, bz, vx, vy, vz);
564 }
565 return false;
566}
567
568bool DriftLineRKF::GetDiffusion(const double ex, const double ey,
569 const double ez, const double bx,
570 const double by, const double bz, double& dl,
571 double& dt) const {
572
573 if (m_particleType == ParticleTypeElectron) {
574 return m_medium->ElectronDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
575 } else if (m_particleType == ParticleTypeIon) {
576 return m_medium->IonDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
577 } else if (m_particleType == ParticleTypeHole) {
578 return m_medium->HoleDiffusion(ex, ey, ez, bx, by, bz, dl, dt);
579 }
580 return false;
581}
582
583bool DriftLineRKF::GetTownsend(const double ex, const double ey,
584 const double ez, const double bx,
585 const double by, const double bz,
586 double& alpha) const {
587
588 if (m_particleType == ParticleTypeElectron) {
589 return m_medium->ElectronTownsend(ex, ey, ez, bx, by, bz, alpha);
590 } else if (m_particleType == ParticleTypeHole) {
591 return m_medium->HoleTownsend(ex, ey, ez, bx, by, bz, alpha);
592 }
593 return false;
594}
595
596bool DriftLineRKF::EndDriftLine() {
597
598 // Start at the last valid point of the drift line.
599 double x0 = m_path[m_nPoints - 1].x;
600 double y0 = m_path[m_nPoints - 1].y;
601 double z0 = m_path[m_nPoints - 1].z;
602 // Get the initial drift velocity.
603 double vx = 0., vy = 0., vz = 0.;
604 int status = 0;
605 if (!GetVelocity(x0, y0, z0, vx, vy, vz, status)) {
606 std::cerr << m_className << "::EndDriftLine:\n";
607 std::cerr << " Failed to retrieve initial drift velocity.\n";
608 return false;
609 } else if (status != 0) {
610 std::cerr << m_className << "::EndDriftLine:\n";
611 std::cerr << " No valid field at initial point. Program bug!\n";
612 return false;
613 }
614 const double speed = sqrt(vx * vx + vy * vy + vz * vz);
615 if (speed < Small) {
616 std::cerr << m_className << "::EndDriftLine:\n";
617 std::cerr << " Zero velocity at initial position.\n";
618 return false;
619 }
620 // Calculate the initial step size.
621 if (m_nPoints > 1) {
622 const double dx = x0 - m_path[m_nPoints - 2].x;
623 const double dy = y0 - m_path[m_nPoints - 2].y;
624 const double dz = z0 - m_path[m_nPoints - 2].z;
625 const double scale = sqrt(dx * dx + dy * dy + dz * dz) / speed;
626 vx *= scale;
627 vy *= scale;
628 vz *= scale;
629 } else {
630 // This is the very first step. Start with a small step size.
631 vx *= m_accuracy / speed;
632 vy *= m_accuracy / speed;
633 vz *= m_accuracy / speed;
634 }
635 double x1 = x0;
636 double y1 = y0;
637 double z1 = z0;
638 // Step along the initial direction until we leave the volume.
639 bool inside = true;
640 while (inside) {
641 x1 += vx;
642 y1 += vy;
643 z1 += vz;
644 double ex = 0., ey = 0., ez = 0.;
645 m_sensor->ElectricField(x1, y1, z1, ex, ey, ez, m_medium, status);
646 if (status != 0) inside = false;
647 }
648 const unsigned int nBisections = 100;
649 const double tol = BoundaryDistance * BoundaryDistance;
650 for (unsigned int i = 0; i < nBisections; ++i) {
651 const double x = x0 + 0.5 * (x1 - x0);
652 const double y = y0 + 0.5 * (y1 - y0);
653 const double z = z0 + 0.5 * (z1 - z0);
654 double ex = 0., ey = 0., ez = 0.;
655 m_sensor->ElectricField(x, y, z, ex, ey, ez, m_medium, status);
656 if (status == 0) {
657 x0 = x;
658 y0 = y;
659 z0 = z;
660 } else {
661 x1 = x;
662 y1 = y;
663 z1 = z;
664 }
665 const double dx = x1 - x0;
666 const double dy = y1 - y0;
667 const double dz = z1 - z0;
668 const double d = dx * dx + dy * dy + dz * dz;
669 if (d < tol) break;
670 }
671 // Calculate the time step.
672 const double dx = x0 - m_path[m_nPoints - 1].x;
673 const double dy = y0 - m_path[m_nPoints - 1].y;
674 const double dz = z0 - m_path[m_nPoints - 1].z;
675 const double dt = sqrt(dx * dx + dy * dy + dz * dz) / speed;
676 // Add the last point, just inside the drift area.
677 ++m_nPoints;
678 m_path[m_nPoints - 1].x = x0;
679 m_path[m_nPoints - 1].y = y0;
680 m_path[m_nPoints - 1].z = z0;
681 m_path[m_nPoints - 1].t = m_path[m_nPoints - 2].t + dt;
682 m_status = StatusLeftDriftMedium;
683 return true;
684}
685
686bool DriftLineRKF::DriftToWire(const double xw, const double yw,
687 const double rw) {
688
689 // Get the starting point.
690 double x0 = m_path[m_nPoints - 1].x;
691 double y0 = m_path[m_nPoints - 1].y;
692 double z0 = m_path[m_nPoints - 1].z;
693 double t0 = m_path[m_nPoints - 1].t - m_path[0].t;
694 if (m_debug) {
695 std::cout << m_className << "::DriftToWire:\n";
696 std::cout << " Drifting particle at (" << x0 << ", " << y0 << ")\n";
697 std::cout << " to wire at (" << xw << ", " << yw
698 << ") with physical radius " << rw << " cm.\n";
699 }
700
701 // Get the initial drift velocity.
702 double vx0 = 0., vy0 = 0., vz0 = 0.;
703 int status = 0;
704 if (!GetVelocity(x0, y0, z0, vx0, vy0, vz0, status)) {
705 std::cerr << m_className << "::DriftToWire:\n";
706 std::cerr << " Failed to retrieve initial drift velocity.\n";
707 return false;
708 } else if (status != 0) {
709 std::cerr << m_className << "::DriftToWire:\n";
710 std::cerr << " No valid field at initial point. Program bug!\n";
711 return false;
712 }
713
714 // Get a coarse estimate of the drift time
715 // assuming a straight-line trajectory and constant velocity.
716 double dx0 = xw - x0;
717 double dy0 = yw - y0;
718 double dt = (sqrt(dx0 * dx0 + dy0 * dy0) - rw) /
719 sqrt(vx0 * vx0 + vy0 * vy0);
720 // If the time needed to reach the wire is very small, stop here.
721 if (dt < 1.e-6 * t0) {
722 m_status = StatusLeftDriftMedium;
723 return true;
724 }
725
726 const double c1 = 1. / 3.;
727 const double r2 = rw * rw;
728 const unsigned int nMaxSteps = m_maxStepsToWire;
729 for (unsigned int i = 0; i < nMaxSteps; ++i) {
730 // Check the time step.
731 bool smallTimeStep = false;
732 /*
733 if (dt < 1.e-6) {
734 // Estimated time step is very small. Stop.
735 if (m_debug) {
736 std::cout << m_className << "::DriftToWire:\n";
737 std::cout << " Estimated time step: " << dt << " ns. Stop.\n";
738 }
739 smallTimeStep = true;
740 }
741 */
742 // Calculate the estimated end-point.
743 double x1 = x0 + dt * vx0;
744 double y1 = y0 + dt * vy0;
745 double z1 = z0 + dt * vz0;
746 if (m_debug) {
747 std::cout << m_className << "::DriftToWire:\n";
748 std::cout << " Step " << i << " from (" << x0 << ", " << y0
749 << ") to (" << x1 << ", " << y1 << ").\n";
750 }
751 // Make sure we are not moving away from the wire.
752 const double dxw0 = xw - x0;
753 const double dyw0 = yw - y0;
754 const double xinp0 = (x1 - x0) * dxw0 + (y1 - y0) * dyw0;
755 if (xinp0 < 0.) {
756 std::cerr << m_className << "::DriftToWire:\n";
757 std::cerr << " Particle moves away from the wire. Abandoning.\n";
758 return false;
759 }
760 // Check if the end-point is inside the wire or the wire was crossed.
761 bool onwire = false;
762 const double dxw1 = xw - x1;
763 const double dyw1 = yw - y1;
764 const double xinp1 = (x0 - x1) * dxw1 + (y0 - y1) * dyw1;
765 if (xinp1 < 0.) {
766 const double d2 = dxw1 * dxw1 + dyw1 * dyw1;
767 if (d2 <= r2) {
768 onwire = true;
769 if (m_debug) std::cout << m_className << "::DriftToWire: Inside.\n";
770 }
771 } else {
772 if (m_debug) std::cout << m_className << "::DriftToWire: Wire crossed.\n";
773 onwire = true;
774 }
775 if (onwire) {
776 const double dw0 = sqrt(dxw0 * dxw0 + dyw0 * dyw0);
777 x1 = xw - (rw + BoundaryDistance) * dxw0 / dw0;
778 y1 = yw - (rw + BoundaryDistance) * dyw0 / dw0;
779 const double dx10 = x1 - x0;
780 const double dy10 = y1 - y0;
781 dt = sqrt((dx10 * dx10 + dy10 * dy10) / (vx0 * vx0 + vy0 * vy0));
782 z1 = z0 + dt * vz0;
783 }
784 // Calculate the drift velocity at the end point.
785 double vx1 = 0., vy1 = 0., vz1 = 0.;
786 if (!GetVelocity(x1, y1, z1, vx1, vy1, vz1, status)) {
787 std::cerr << m_className << "::DriftToWire:\n";
788 std::cerr << " Cannot retrieve drift velocity at end point. Quit.\n";
789 return false;
790 } else if (status != 0) {
791 std::cerr << m_className << "::DriftToWire:\n";
792 std::cerr << " End point is not in a valid drift medium. Quit.\n";
793 return false;
794 }
795 // Get a point halfway between.
796 const double xm = 0.5 * (x0 + x1);
797 const double ym = 0.5 * (y0 + y1);
798 const double zm = 0.5 * (z0 + z1);
799 // Calculate the drift velocity at the mid point.
800 double vxm = 0., vym = 0., vzm = 0.;
801 if (!GetVelocity(xm, ym, zm, vxm, vym, vzm, status)) {
802 std::cerr << m_className << "::DriftToWire:\n";
803 std::cerr << " Cannot retrieve drift velocity at mid point. Quit.\n";
804 return false;
805 } else if (status != 0) {
806 std::cerr << m_className << "::DriftToWire:\n";
807 std::cerr << " Mid point is not in a valid drift medium. Quit.\n";
808 return false;
809 }
810 const double v0 = sqrt(vx0 * vx0 + vy0 * vy0);
811 const double v1 = sqrt(vx1 * vx1 + vy1 * vy1);
812 const double vm = sqrt(vxm * vxm + vym * vym);
813 // Make sure the velocities are non-zero.
814 if (v0 < Small || v1 < Small || vm < Small) {
815 std::cerr << m_className << "::DriftToWire:\n";
816 std::cerr << " Zero velocity. Abandoning.\n";
817 return false;
818 }
819 // Compare first and second order estimates.
820 const double dx01 = x0 - x1;
821 const double dy01 = y0 - y1;
822 const double d10 = sqrt(dx01 * dx01 + dy01 * dy01);
823 if (d10 * c1 * fabs(1. / v0 - 2. / vm + 1. / v1) > 1.e-4 * (1. + fabs(t0)) &&
824 i < nMaxSteps - 1) {
825 // Accuracy was not good enough so halve the step time.
826 if (m_debug) {
827 std::cout << m_className << "::DriftToWire: Reducing step size.\n";
828 }
829 dt *= 0.5;
830 continue;
831 }
832 const double t1 = t0 + d10 * (1. / v0 + 4. / vm + 1. / v1) / 6.;
833 // Add a new point to the drift line.
834 ++m_nPoints;
835 m_path[m_nPoints - 1].x = x1;
836 m_path[m_nPoints - 1].y = y1;
837 m_path[m_nPoints - 1].z = z1;
838 m_path[m_nPoints - 1].t = m_path[0].t + t1;
839 if (onwire) break;
840 // JAMES MOTT HACK (5th Oct 2015)
841 if(smallTimeStep) break;
842 // Proceed to the next step.
843 x0 = x1;
844 y0 = y1;
845 z0 = z1;
846 t0 = t1;
847 vx0 = vx1;
848 vy0 = vy1;
849 vz0 = vz1;
850 }
851 m_status = StatusLeftDriftMedium;
852 return true;
853}
854
855void DriftLineRKF::GetEndPoint(double& x, double& y, double& z, double& t,
856 int& stat) const {
857
858 if (m_nPoints == 0) {
859 x = y = z = t = 0.;
860 stat = m_status;
861 return;
862 }
863 x = m_path[m_nPoints - 1].x;
864 y = m_path[m_nPoints - 1].y;
865 z = m_path[m_nPoints - 1].z;
866 t = m_path[m_nPoints - 1].t;
867 stat = m_status;
868}
869
870void DriftLineRKF::GetDriftLinePoint(const unsigned int i, double& x, double& y,
871 double& z, double& t) const {
872
873 if (i >= m_nPoints) {
874 std::cerr << m_className << "::GetDriftLinePoint:\n";
875 std::cerr << " Index is outside the range.\n";
876 return;
877 }
878
879 // Return midpoint of drift line stage
880 x = m_path[i].x;
881 y = m_path[i].y;
882 z = m_path[i].z;
883 t = m_path[i].t;
884}
885
886double DriftLineRKF::IntegrateDiffusion(const double x, const double y,
887 const double z, const double xe,
888 const double ye, const double ze) {
889
890 if (m_debug) {
891 std::cout << m_className << "::IntegrateDiffusion:\n Integrating from "
892 << x << ", " << y << ", " << z << " to " << xe << ", " << ye
893 << ", " << ze << "\n";
894 }
895 // Make sure initial position is valid.
896 double ex, ey, ez;
897 double bx, by, bz;
898 int status;
899 m_sensor->MagneticField(x, y, z, bx, by, bz, status);
900 m_sensor->ElectricField(x, y, z, ex, ey, ez, m_medium, status);
901 if (status != 0) {
902 std::cerr << m_className << "::IntegrateDiffusion:\n"
903 << " Initial position not valid.\n";
904 return 0.;
905 }
906 // Determine drift velocity at initial position.
907 double vx0 = 0., vy0 = 0., vz0 = 0.;
908 if (!GetVelocity(ex, ey, ez, bx, by, bz, vx0, vy0, vz0)) {
909 std::cerr << m_className << "::IntegrateDiffusion:\n"
910 << " Cannot retrieve drift velocity.\n";
911 return 0.;
912 }
913 double speed0 = sqrt(vx0 * vx0 + vy0 * vy0 + vz0 * vz0);
914 if (speed0 < Small) {
915 std::cerr << m_className << "::IntegrateDiffusion:\n"
916 << " Zero velocity at initial position.\n";
917 return 0.;
918 }
919 // Determine diffusion coefficients at initial position.
920 double dL0 = 0.;
921 double dT0 = 0.;
922 if (!GetDiffusion(ex, ey, ez, bx, by, bz, dL0, dT0)) {
923 std::cerr << m_className << "::IntegrateDiffusion:\n"
924 << " Cannot retrieve diffusion coefficients.\n";
925 return 0.;
926 }
927
928 // Start and end point coordinates of initial step.
929 double x0 = x;
930 double y0 = y;
931 double z0 = z;
932 double x1 = xe;
933 double y1 = ye;
934 double z1 = ze;
935
936 double integral = 0.;
937 bool keepGoing = true;
938 while (keepGoing) {
939 const double dx = x1 - x0;
940 const double dy = y1 - y0;
941 const double dz = z1 - z0;
942 const double d = sqrt(dx * dx + dy * dy + dz * dz);
943 if (d < 1.e-6) {
944 // Step length has become very small.
945 if (m_debug) {
946 std::cout << m_className << "::IntegrateDiffusion: Small step.\n";
947 }
948 const double s = dL0 / speed0;
949 integral += s * s * d;
950 // Check if we are close to the end point.
951 const double dxe = xe - x1;
952 const double dye = ye - y1;
953 const double dze = ze - z1;
954 if (sqrt(dxe * dxe + dye * dye + dze * dze) < 1.e-6) break;
955 // Proceed with the next step.
956 x0 = x1;
957 y0 = y1;
958 z0 = z1;
959 x1 = xe;
960 y1 = ye;
961 z1 = ze;
962 continue;
963 }
964 // Determine drift velocity and diffusion at the end point of the step.
965 m_sensor->MagneticField(x1, y1, z1, bx, by, bz, status);
966 m_sensor->ElectricField(x1, y1, z1, ex, ey, ez, m_medium, status);
967 if (status != 0) {
968 std::cerr << m_className << "::IntegrateDiffusion: Invalid end point.\n";
969 break;
970 }
971 double vx1 = 0.;
972 double vy1 = 0.;
973 double vz1 = 0.;
974 if (!GetVelocity(ex, ey, ez, bx, by, bz, vx1, vy1, vz1)) {
975 std::cerr << m_className << "::IntegrateDiffusion:\n"
976 << " Cannot retrieve drift velocity.\n";
977 break;
978 }
979 double speed1 = sqrt(vx1 * vx1 + vy1 * vy1 + vz1 * vz1);
980 double dL1 = 0.;
981 double dT1 = 0.;
982 if (!GetDiffusion(ex, ey, ez, bx, by, bz, dL1, dT1)) {
983 std::cerr << m_className << "::IntegrateDiffusion:\n"
984 << " Cannot retrieve diffusion.\n";
985 break;
986 }
987 // Determine drift velocity and diffusion at the mid point of the step.
988 const double xm = 0.5 * (x0 + x1);
989 const double ym = 0.5 * (y0 + y1);
990 const double zm = 0.5 * (z0 + z1);
991 m_sensor->MagneticField(xm, ym, zm, bx, by, bz, status);
992 m_sensor->ElectricField(xm, ym, zm, ex, ey, ez, m_medium, status);
993 if (status != 0) {
994 std::cerr << m_className << "::IntegrateDiffusion: Invalid mid point.\n";
995 break;
996 }
997 double vxm = 0.;
998 double vym = 0.;
999 double vzm = 0.;
1000 if (!GetVelocity(ex, ey, ez, bx, by, bz, vxm, vym, vzm)) {
1001 std::cerr << m_className << "::IntegrateDiffusion:\n"
1002 << " Cannot retrieve drift velocity.\n";
1003 break;
1004 }
1005 double speedm = sqrt(vxm * vxm + vym * vym + vzm * vzm);
1006 double dLm = 0.;
1007 double dTm = 0.;
1008 if (!GetDiffusion(ex, ey, ez, bx, by, bz, dLm, dTm)) {
1009 std::cerr << m_className << "::IntegrateDiffusion:\n"
1010 << " Cannot retrieve diffusion.\n";
1011 break;
1012 }
1013 const double tolerance = 1.e-3;
1014 const double s0 = pow(dL0 / speed0, 2);
1015 const double s1 = pow(dL1 / speed1, 2);
1016 const double sm = pow(dLm / speedm, 2);
1017 // Calculate integral using Simpsons rule.
1018 const double simpson = d * (s0 + 4. * sm + s1) / 6.;
1019 // Calculate integral using trapezoidal rule.
1020 const double trapez = 0.5 * d * (s0 + s1);
1021 // Compare the two estimates.
1022 if (fabs(trapez - simpson) * sqrt(2. * d / (s0 + s1)) / 6. < tolerance) {
1023 // Accuracy is good enough.
1024 integral += simpson;
1025 // Proceed to the next step.
1026 x0 = x1;
1027 y0 = y1;
1028 z0 = z1;
1029 dL0 = dL1;
1030 dT0 = dT1;
1031 speed0 = speed1;
1032 x1 = xe;
1033 y1 = ye;
1034 z1 = ze;
1035 } else {
1036 // Accuracy is not good enough, so halve the step.
1037 x1 = xm;
1038 y1 = ym;
1039 z1 = zm;
1040 dL1 = dLm;
1041 dT1 = dTm;
1042 }
1043 }
1044 return integral;
1045}
1046
1047double DriftLineRKF::IntegrateTownsend(const double xi, const double yi,
1048 const double zi,
1049 const double xe, const double ye,
1050 const double ze,
1051 const double tol) {
1052
1053 // Make sure the initial position is valid.
1054 double ex = 0., ey = 0., ez = 0.;
1055 double bx = 0., by = 0., bz = 0.;
1056 int status = 0;
1057 m_sensor->MagneticField(xi, yi, zi, bx, by, bz, status);
1058 m_sensor->ElectricField(xi, yi, zi, ex, ey, ez, m_medium, status);
1059 if (status != 0) {
1060 std::cerr << m_className << "::IntegrateTownsend:\n Initial position ("
1061 << xi << ", " << yi << ", " << zi << ") not valid.\n";
1062 return 0.;
1063 }
1064 // Determine the Townsend coefficient at the initial point.
1065 double alpha0 = 0.;
1066 if (!GetTownsend(ex, ey, ez, bx, by, bz, alpha0)) {
1067 std::cerr << m_className << "::IntegrateTownsend:\n"
1068 << " Cannot retrieve Townsend coefficient at initial point.\n";
1069 return 0.;
1070 }
1071 // Make sure the end position is valid.
1072 m_sensor->MagneticField(xe, ye, ze, bx, by, bz, status);
1073 m_sensor->ElectricField(xe, ye, ze, ex, ey, ez, m_medium, status);
1074 if (status != 0) {
1075 std::cerr << m_className << "::IntegrateTownsend:\n End position ("
1076 << xi << ", " << yi << ", " << zi << ") not valid.\n";
1077 return 0.;
1078 }
1079 // Determine Townsend coefficient at end point.
1080 double alpha1 = 0.;
1081 if (!GetTownsend(ex, ey, ez, bx, by, bz, alpha1)) {
1082 std::cerr << m_className << "::IntegrateTownsend:\n"
1083 << " Cannot retrieve Townsend coefficient at end point.\n";
1084 return 0.;
1085 }
1086 // Start and end point coordinates of initial step.
1087 double x0 = xi;
1088 double y0 = yi;
1089 double z0 = zi;
1090 double x1 = xe;
1091 double y1 = ye;
1092 double z1 = ze;
1093 double dx = x1 - x0;
1094 double dy = y1 - y0;
1095 double dz = z1 - z0;
1096 double d = sqrt(dx * dx + dy * dy + dz * dz);
1097 // Calculate the convergence criterium.
1098 const double eps = tol / d;
1099 unsigned int stepCounter = 0;
1100 double integral = 0.;
1101 bool keepGoing = true;
1102 while (keepGoing) {
1103 dx = x1 - x0;
1104 dy = y1 - y0;
1105 dz = z1 - z0;
1106 d = sqrt(dx * dx + dy * dy + dz * dz);
1107 const double told = 1.e-6;
1108 if (d < told) {
1109 // Step length has become very small.
1110 if (m_debug) {
1111 std::cout << m_className << "::IntegrateTownsend: Small step.\n";
1112 }
1113 integral += alpha0 * d;
1114 // Check if we are close to the end point.
1115 const double dxe = xe - x1;
1116 const double dye = ye - y1;
1117 const double dze = ze - z1;
1118 const double tol2 = told * told;
1119 if (dxe * dxe + dye * dye + dze * dze < tol2) break;
1120 // Proceed with the next step.
1121 x0 = x1;
1122 y0 = y1;
1123 z0 = z1;
1124 x1 = xe;
1125 y1 = ye;
1126 z1 = ze;
1127 continue;
1128 }
1129 ++stepCounter;
1130 // Calculate the Townsend coefficient at the end point of the step.
1131 m_sensor->MagneticField(x1, y1, z1, bx, by, bz, status);
1132 m_sensor->ElectricField(x1, y1, z1, ex, ey, ez, m_medium, status);
1133 if (status != 0) {
1134 std::cerr << m_className << "::IntegrateTownsend: Invalid end point.\n";
1135 break;
1136 }
1137 if (!GetTownsend(ex, ey, ez, bx, by, bz, alpha1)) {
1138 std::cerr << m_className << "::IntegrateTownsend:\n"
1139 << " Cannot retrieve Townsend coefficient at end point.\n";
1140 break;
1141 }
1142 // Calculate the Townsend coefficient at the mid point of the step.
1143 const double xm = 0.5 * (x0 + x1);
1144 const double ym = 0.5 * (y0 + y1);
1145 const double zm = 0.5 * (z0 + z1);
1146 m_sensor->MagneticField(xm, ym, zm, bx, by, bz, status);
1147 m_sensor->ElectricField(xm, ym, zm, ex, ey, ez, m_medium, status);
1148 if (status != 0) {
1149 std::cerr << m_className << "::IntegrateTownsend: Invalid mid point.\n";
1150 break;
1151 }
1152 double alpham = 0.;
1153 if (!GetTownsend(ex, ey, ez, bx, by, bz, alpham)) {
1154 std::cerr << m_className << "::IntegrateTownsend:\n"
1155 << " Cannot retrieve Townsend coefficient at mid point.\n";
1156 break;
1157 }
1158 // Check the accuracy of the result.
1159 if (fabs(alpha0 - 2. * alpham + alpha1) / 3. < eps) {
1160 // Accuracy is good enough.
1161 integral += d * (alpha0 + 4. * alpham + alpha1) / 6.;
1162 // Proceed to the next step.
1163 x0 = x1;
1164 y0 = y1;
1165 z0 = z1;
1166 alpha0 = alpha1;
1167 if (fabs(x0 - xe) < BoundaryDistance &&
1168 fabs(y0 - ye) < BoundaryDistance &&
1169 fabs(z0 - ze) < BoundaryDistance) {
1170 keepGoing = false;
1171 break;
1172 }
1173 x1 += dx;
1174 y1 += dy;
1175 z1 += dz;
1176 } else {
1177 // Accuracy is not good enough, so halve the step.
1178 x1 = xm;
1179 y1 = ym;
1180 z1 = zm;
1181 alpha1 = alpham;
1182 }
1183 }
1184 return integral;
1185}
1186
1187void DriftLineRKF::ComputeSignal(const double q, const double scale) const {
1188
1189 if (m_nPoints < 2) return;
1190 double g = 1.;
1191 for (unsigned int i = 0; i < m_nPoints - 1; ++i) {
1192 // Calculate step length.
1193 const double dt = m_path[i + 1].t - m_path[i].t;
1194 const double dx = m_path[i + 1].x - m_path[i].x;
1195 const double dy = m_path[i + 1].y - m_path[i].y;
1196 const double dz = m_path[i + 1].z - m_path[i].z;
1197 // Calculate average velocity.
1198 const double vx = dx / dt;
1199 const double vy = dy / dt;
1200 const double vz = dz / dt;
1201 // Calculate midpoint.
1202 const double xm = m_path[i].x + 0.5 * dx;
1203 const double ym = m_path[i].y + 0.5 * dy;
1204 const double zm = m_path[i].z + 0.5 * dz;
1205 if (q < 0.) {
1206 // Electron signal is weighted by avalanche size at this step.
1207 g = exp(m_path[i].alphaint);
1208 }
1209 m_sensor->AddSignal(q * g * scale, m_path[i].t, dt, xm, ym, zm, vx, vy, vz);
1210 }
1211}
1212}
void SetIntegrationAccuracy(const double a)
Definition: DriftLineRKF.cc:44
bool DriftHole(const double x0, const double y0, const double z0, const double t0)
Definition: DriftLineRKF.cc:94
bool DriftIon(const double x0, const double y0, const double z0, const double t0)
void SetSensor(Sensor *s)
Definition: DriftLineRKF.cc:35
bool DriftElectron(const double x0, const double y0, const double z0, const double t0)
Definition: DriftLineRKF.cc:84
void GetDriftLinePoint(const unsigned int i, double &x, double &y, double &z, double &t) const
void EnablePlotting(ViewDrift *view)
Definition: DriftLineRKF.cc:68
void GetEndPoint(double &x, double &y, double &z, double &t, int &st) const
void SetMaximumStepSize(const double ms)
Definition: DriftLineRKF.cc:54
virtual bool HoleTownsend(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &alpha)
Definition: Medium.cc:971
virtual bool HoleVelocity(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &vx, double &vy, double &vz)
Definition: Medium.cc:704
virtual bool ElectronVelocity(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &vx, double &vy, double &vz)
Definition: Medium.cc:204
virtual bool IonVelocity(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &vx, double &vy, double &vz)
Definition: Medium.cc:1077
virtual bool ElectronDiffusion(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &dl, double &dt)
Definition: Medium.cc:362
virtual bool ElectronTownsend(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &alpha)
Definition: Medium.cc:490
virtual bool HoleDiffusion(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &dl, double &dt)
Definition: Medium.cc:846
virtual bool IonDiffusion(const double ex, const double ey, const double ez, const double bx, const double by, const double bz, double &dl, double &dt)
Definition: Medium.cc:1122
void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
Get the magnetic field at (x, y, z).
Definition: Sensor.cc:101
bool IsInTrapRadius(const double q0, const double x0, const double y0, const double z0, double &xw, double &yw, double &rw)
Definition: Sensor.cc:301
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&medium, int &status)
Get the drift field and potential at (x, y, z).
Definition: Sensor.cc:48
void AddSignal(const double q, const double t, const double dt, const double x, const double y, const double z, const double vx, const double vy, const double vz)
Definition: Sensor.cc:417
bool IsWireCrossed(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc)
Definition: Sensor.cc:288
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
Definition: Sensor.cc:237
Visualize drift lines and tracks.
Definition: ViewDrift.hh:15
void NewElectronDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
Definition: ViewDrift.cc:127
void AddDriftLinePoint(const unsigned int iL, const double x, const double y, const double z)
Definition: ViewDrift.cc:243
void NewHoleDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
Definition: ViewDrift.cc:156
void NewIonDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
Definition: ViewDrift.cc:180
DoubleAc pow(const DoubleAc &f, double p)
Definition: DoubleAc.cpp:337
DoubleAc exp(const DoubleAc &f)
Definition: DoubleAc.cpp:377
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615
DoubleAc sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:314