Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ViewField.cc
Go to the documentation of this file.
1#include <stdio.h>
2#include <string.h>
3#include <cmath>
4#include <iostream>
5#include <algorithm>
6
7#include <TAxis.h>
8#include <TROOT.h>
9#include <TF1.h>
10#include <TF2.h>
11#include <TH1F.h>
12
13#include "Garfield/Component.hh"
14#include "Garfield/Plotting.hh"
15#include "Garfield/Random.hh"
16#include "Garfield/Sensor.hh"
18#include "Garfield/ViewField.hh"
19
20namespace {
21
22void SampleRange(const double xmin, const double ymin, const double xmax,
23 const double ymax, TF2* f, double& zmin, double& zmax) {
24 constexpr unsigned int n = 1000;
25 const double dx = xmax - xmin;
26 const double dy = ymax - ymin;
27 zmin = std::numeric_limits<double>::max();
28 zmax = -zmin;
29 for (unsigned int i = 0; i < n; ++i) {
30 const double z = f->Eval(xmin + Garfield::RndmUniform() * dx,
31 ymin + Garfield::RndmUniform() * dy);
32 if (z < zmin) zmin = z;
33 if (z > zmax) zmax = z;
34 }
35}
36
37void SampleRange(TF1* f, double& ymin, double& ymax) {
38 constexpr unsigned int n = 1000;
39 ymin = std::numeric_limits<double>::max();
40 ymax = -ymin;
41 double xmin = 0.;
42 double xmax = 1.;
43 f->GetRange(xmin, xmax);
44 const double dx = xmax - xmin;
45 for (unsigned int i = 0; i < n; ++i) {
46 const double y = f->Eval(xmin + dx * Garfield::RndmUniform());
47 if (y < ymin) ymin = y;
48 if (y > ymax) ymax = y;
49 }
50}
51
52double Interpolate(const std::array<double, 1000>& y,
53 const std::array<double, 1000>& x, const double xx) {
54
55 const double tol = 1.e-6 * fabs(x.back() - x.front());
56 if (xx < x[0]) return y[0];
57 const auto it1 = std::upper_bound(x.cbegin(), x.cend(), xx);
58 if (it1 == x.cend()) return y.back();
59 const auto it0 = std::prev(it1);
60 const double dx = (*it1 - *it0);
61 if (dx < tol) return y[it0 - x.cbegin()];
62 const double f = (xx - *it0) / dx;
63 return y[it0 - x.cbegin()] * (1. - f) + f * y[it1 - x.cbegin()];
64}
65
66}
67
68namespace Garfield {
69
70ViewField::ViewField() : ViewBase("ViewField") { }
71
73 if (!s) {
74 std::cerr << m_className << "::SetSensor: Null pointer.\n";
75 return;
76 }
77 m_sensor = s;
78 m_component = nullptr;
79}
80
82 if (!c) {
83 std::cerr << m_className << "::SetComponent: Null pointer.\n";
84 return;
85 }
86 m_component = c;
87 m_sensor = nullptr;
88}
89
90void ViewField::SetVoltageRange(const double vmin, const double vmax) {
91 m_vmin = std::min(vmin, vmax);
92 m_vmax = std::max(vmin, vmax);
93 m_useAutoRange = false;
94}
95
96void ViewField::SetElectricFieldRange(const double emin, const double emax) {
97 m_emin = std::min(emin, emax);
98 m_emax = std::max(emin, emax);
99 m_useAutoRange = false;
100}
101
102void ViewField::SetWeightingFieldRange(const double wmin, const double wmax) {
103 m_wmin = std::min(wmin, wmax);
104 m_wmax = std::max(wmin, wmax);
105 m_useAutoRange = false;
106}
107
108void ViewField::SetNumberOfContours(const unsigned int n) {
109 if (n > 0) m_nContours = n;
110}
111
112void ViewField::SetNumberOfSamples1d(const unsigned int n) {
113 m_nSamples1d = std::max(4u, n);
114}
115
116void ViewField::SetNumberOfSamples2d(const unsigned int nx,
117 const unsigned int ny) {
118 m_nSamples2dX = std::max(4u, nx);
119 m_nSamples2dY = std::max(4u, ny);
120}
121
122void ViewField::PlotContour(const std::string& option) {
123 Draw2d(option, true, false, "", "CONT1Z");
124}
125
126void ViewField::Plot(const std::string& option, const std::string& drawopt) {
127 std::string opt1;
128 std::transform(drawopt.begin(), drawopt.end(),
129 std::back_inserter(opt1), toupper);
130 if (opt1.find("CONT") != std::string::npos) {
131 Draw2d(option, true, false, "", drawopt);
132 } else {
133 Draw2d(option, false, false, "", drawopt);
134 }
135}
136
137void ViewField::PlotProfile(const double x0, const double y0, const double z0,
138 const double x1, const double y1, const double z1,
139 const std::string& option, const bool normalised) {
140 DrawProfile(x0, y0, z0, x1, y1, z1, option, false, "", normalised);
141}
142
143void ViewField::PlotWeightingField(const std::string& label,
144 const std::string& option,
145 const std::string& drawopt) {
146 Draw2d(option, false, true, label, drawopt);
147}
148
149void ViewField::PlotContourWeightingField(const std::string& label,
150 const std::string& option) {
151 Draw2d(option, true, true, label, "CONT1Z");
152}
153
154void ViewField::PlotProfileWeightingField(const std::string& label,
155 const double x0, const double y0,
156 const double z0, const double x1,
157 const double y1, const double z1,
158 const std::string& option,
159 const bool normalised) {
160 DrawProfile(x0, y0, z0, x1, y1, z1, option, true, label, normalised);
161}
162
163
164ViewField::Parameter ViewField::GetPar(const std::string& option,
165 std::string& title) const {
166
167 std::string opt;
168 std::transform(option.begin(), option.end(),
169 std::back_inserter(opt), toupper);
170 if (opt == "V" || opt == "P" || opt == "PHI" ||
171 opt.find("VOLT") != std::string::npos ||
172 opt.find("POT") != std::string::npos) {
173 title = "potential";
174 return Parameter::Potential;
175 } else if (opt == "E" || opt == "FIELD" || opt == "NORM" ||
176 opt.find("MAG") != std::string::npos) {
177 title = "field";
178 return Parameter::Magnitude;
179 } else if (opt.find("X") != std::string::npos) {
180 title = "field (x-component)";
181 return Parameter::Ex;
182 } else if (opt.find("Y") != std::string::npos) {
183 title = "field (y-component)";
184 return Parameter::Ey;
185 } else if (opt.find("Z") != std::string::npos) {
186 title = "field (z-component)";
187 return Parameter::Ez;
188 }
189 std::cerr << m_className << "::GetPar: Unknown option (" << option << ").\n";
190 title = "potential";
191 return Parameter::Potential;
192}
193
194void ViewField::Draw2d(const std::string& option, const bool contour,
195 const bool wfield, const std::string& electrode,
196 const std::string& drawopt) {
197 if (!m_sensor && !m_component) {
198 std::cerr << m_className << "::Draw2d:\n"
199 << " Neither sensor nor component are defined.\n";
200 return;
201 }
202
203 // Determine the x-y range.
204 if (!SetPlotLimits()) return;
205
206 // Determine the quantity to be plotted.
207 std::string title;
208 const Parameter par = GetPar(option, title);
209
210 auto eval = [this, par, wfield, electrode](double* u, double* /*p*/) {
211 // Transform to global coordinates.
212 const double x = m_proj[0][0] * u[0] + m_proj[1][0] * u[1] + m_proj[2][0];
213 const double y = m_proj[0][1] * u[0] + m_proj[1][1] * u[1] + m_proj[2][1];
214 const double z = m_proj[0][2] * u[0] + m_proj[1][2] * u[1] + m_proj[2][2];
215 return wfield ? Wfield(x, y, z, par, electrode) : Field(x, y, z, par);
216 };
217 const std::string fname = FindUnusedFunctionName("f2D");
218 TF2 f2(fname.c_str(), eval, m_xMinPlot, m_xMaxPlot, m_yMinPlot, m_yMaxPlot, 0);
219
220 // Set the x-y range.
222
223 // Set the z-range.
224 double zmin = m_vmin;
225 double zmax = m_vmax;
226 if (wfield) {
227 if (contour) {
228 title = "Contours of the weighting " + title;
229 } else {
230 title = "Weighting " + title;
231 }
232 if (m_useAutoRange) {
233 SampleRange(m_xMinPlot, m_yMinPlot, m_xMaxPlot, m_yMaxPlot, &f2,
234 zmin, zmax);
235 } else if (par == Parameter::Potential) {
236 zmin = 0.;
237 zmax = 1.;
238 } else {
239 zmin = m_wmin;
240 zmax = m_wmax;
241 }
242 } else {
243 if (contour) {
244 title = "Contours of the electric " + title;
245 } else {
246 title = "Electric " + title;
247 }
248 if (par == Parameter::Potential) {
249 if (m_useAutoRange) {
250 if (m_component) {
251 if (m_samplePotential || !m_component->GetVoltageRange(zmin, zmax)) {
253 &f2, zmin, zmax);
254 }
255 } else if (m_sensor) {
256 if (m_samplePotential || !m_sensor->GetVoltageRange(zmin, zmax)) {
258 &f2, zmin, zmax);
259 }
260 }
261 } else {
262 zmin = m_vmin;
263 zmax = m_vmax;
264 }
265 } else {
266 if (m_useAutoRange) {
268 &f2, zmin, zmax);
269 } else {
270 zmin = m_emin;
271 zmax = m_emax;
272 }
273 }
274 }
275 f2.SetMinimum(zmin);
276 f2.SetMaximum(zmax);
277
278 // Set the contours if requested.
279 if (contour) {
280 std::vector<double> level(m_nContours, 0.);
281 if (m_nContours > 1) {
282 const double step = (zmax - zmin) / (m_nContours - 1.);
283 for (unsigned int i = 0; i < m_nContours; ++i) {
284 level[i] = zmin + i * step;
285 }
286 } else {
287 level[0] = 0.5 * (zmax + zmin);
288 }
289 if (m_debug) {
290 std::cout << m_className << "::Draw2d:\n"
291 << " Number of contours: " << m_nContours << "\n";
292 for (unsigned int i = 0; i < m_nContours; ++i) {
293 std::cout << " Level " << i << " = " << level[i] << "\n";
294 }
295 }
296 f2.SetContour(m_nContours, level.data());
297 }
298
299 // Set the resolution.
300 f2.SetNpx(m_nSamples2dX);
301 f2.SetNpy(m_nSamples2dY);
302
303 // Set the labels.
304 std::string labels = ";" + LabelX() + ";" + LabelY();
305 f2.SetTitle(labels.c_str());
306
307 auto pad = GetCanvas();
308 pad->cd();
309 pad->SetTitle(title.c_str());
310 f2.DrawCopy(drawopt.c_str());
311 gPad->SetRightMargin(0.15);
312 gPad->Update();
313}
314
315void ViewField::DrawProfile(const double x0, const double y0, const double z0,
316 const double x1, const double y1, const double z1,
317 const std::string& option,
318 const bool wfield, const std::string& electrode,
319 const bool normalised) {
320 if (!m_sensor && !m_component) {
321 std::cerr << m_className << "::DrawProfile:\n"
322 << " Neither sensor nor component are defined.\n";
323 return;
324 }
325
326 // Check the distance between the two points.
327 double dx = x1 - x0;
328 double dy = y1 - y0;
329 double dz = z1 - z0;
330 if (dx * dx + dy * dy + dz * dz <= 0.) {
331 std::cerr << m_className << "::DrawProfile:\n"
332 << " Start and end points coincide.\n";
333 return;
334 }
335
336 // Determine the quantity to be plotted.
337 std::string title;
338 const Parameter par = GetPar(option, title);
339
340 double t0 = 0.;
341 double t1 = 1.;
342 unsigned int dir = 3;
343 if (fabs(dy) + fabs(dz) < 1.e-6 * fabs(dx)) {
344 t0 = x0;
345 t1 = x1;
346 dir = 0;
347 } else if (fabs(dx) + fabs(dz) < 1.e-6 * fabs(dy)) {
348 t0 = y0;
349 t1 = y1;
350 dir = 1;
351 } else if (fabs(dx) + fabs(dy) < 1.e-6 * fabs(dz)) {
352 t0 = z0;
353 t1 = z1;
354 dir = 2;
355 } else if (!normalised) {
356 t1 = sqrt(dx * dx + dy * dy + dz * dz);
357 dx /= t1;
358 dy /= t1;
359 dz /= t1;
360 }
361
362 auto eval = [this, par, wfield, electrode, dir,
363 x0, y0, z0, dx, dy, dz](double* u, double* /*p*/) {
364 // Get the position.
365 const double t = u[0];
366 double x = dir == 0 ? t : x0;
367 double y = dir == 1 ? t : y0;
368 double z = dir == 2 ? t : z0;
369 if (dir > 2) {
370 x += t * dx;
371 y += t * dy;
372 z += t * dz;
373 }
374 return wfield ? Wfield(x, y, z, par, electrode) : Field(x, y, z, par);
375 };
376
377 const std::string fname = FindUnusedFunctionName("fProfile");
378 TF1 f1(fname.c_str(), eval, t0, t1, 0);
379
380 double fmin = m_vmin;
381 double fmax = m_vmax;
382 if (wfield) {
383 title = "weighting " + title;
384 if (par == Parameter::Potential) {
385 if (m_useAutoRange && m_samplePotential) {
386 SampleRange(&f1, fmin, fmax);
387 } else {
388 fmin = 0.;
389 fmax = 1.;
390 }
391 } else {
392 if (m_useAutoRange) {
393 SampleRange(&f1, fmin, fmax);
394 } else {
395 fmin = m_wmin;
396 fmax = m_wmax;
397 }
398 }
399 } else {
400 title = "electric " + title;
401 if (par == Parameter::Potential) {
402 if (m_useAutoRange) {
403 if (m_component) {
404 if (m_samplePotential || !m_component->GetVoltageRange(fmin, fmax)) {
405 SampleRange(&f1, fmin, fmax);
406 }
407 } else if (m_sensor) {
408 if (m_samplePotential || !m_sensor->GetVoltageRange(fmin, fmax)) {
409 SampleRange(&f1, fmin, fmax);
410 }
411 }
412 } else {
413 fmin = m_vmin;
414 fmax = m_vmax;
415 }
416 } else {
417 if (m_useAutoRange) {
418 SampleRange(&f1, fmin, fmax);
419 } else {
420 fmin = m_emin;
421 fmax = m_emax;
422 }
423 }
424 }
425 f1.SetMinimum(fmin);
426 f1.SetMaximum(fmax);
427
428 std::string labels = ";normalised distance;";
429 if (dir == 0) {
430 labels = ";#it{x} [cm];";
431 } else if (dir == 1) {
432 labels = ";#it{y} [cm];";
433 } else if (dir == 2) {
434 labels = ";#it{z} [cm];";
435 } else if (!normalised) {
436 labels = ";distance [cm];";
437 }
438 if (par == Parameter::Potential) {
439 labels += "#phi";
440 if (wfield) {
441 labels += "_w";
442 } else {
443 labels += " [V]";
444 }
445 } else {
446 labels += "#it{E}";
447 if (wfield) {
448 labels += "_{w";
449 if (par != Parameter::Magnitude) labels += ",";
450 } else if (par != Parameter::Magnitude) {
451 labels += "_{";
452 }
453 if (par == Parameter::Ex) {
454 labels += "x";
455 } else if (par == Parameter::Ey) {
456 labels += "y";
457 } else if (par == Parameter::Ez) {
458 labels += "z";
459 }
460 if (wfield || par != Parameter::Magnitude) labels += "}";
461 if (wfield) {
462 labels += " [1/cm]";
463 } else {
464 labels += " [V/cm]";
465 }
466 }
467 f1.SetTitle(labels.c_str());
468 f1.SetNpx(m_nSamples1d);
469
470 auto pad = GetCanvas();
471 pad->cd();
472 title = "Profile plot of the " + title;
473 pad->SetTitle(title.c_str());
474 f1.DrawCopy();
475 gPad->Update();
476}
477
478bool ViewField::SetPlotLimits() {
479
480 if (m_userPlotLimits) return true;
481 double xmin = 0., ymin = 0., xmax = 0., ymax = 0.;
482 if (m_userBox) {
483 if (PlotLimitsFromUserBox(xmin, ymin, xmax, ymax)) {
484 m_xMinPlot = xmin;
485 m_xMaxPlot = xmax;
486 m_yMinPlot = ymin;
487 m_yMaxPlot = ymax;
488 return true;
489 }
490 }
491 // Try to get the area/bounding box from the sensor/component.
492 bool ok = false;
493
494 if (m_sensor) {
495 ok = PlotLimits(m_sensor, xmin, ymin, xmax, ymax);
496 } else {
497 ok = PlotLimits(m_component, xmin, ymin, xmax, ymax);
498 }
499 if (ok) {
500 m_xMinPlot = xmin;
501 m_xMaxPlot = xmax;
502 m_yMinPlot = ymin;
503 m_yMaxPlot = ymax;
504 }
505 return ok;
506}
507
508double ViewField::Field(const double x, const double y, const double z,
509 const Parameter par) const {
510
511 // Compute the field.
512 double ex = 0., ey = 0., ez = 0., volt = 0.;
513 int status = 0;
514 Medium* medium = nullptr;
515 if (!m_sensor) {
516 m_component->ElectricField(x, y, z, ex, ey, ez, volt, medium, status);
517 } else {
518 m_sensor->ElectricField(x, y, z, ex, ey, ez, volt, medium, status);
519 }
520 if (m_useStatus && status != 0) return m_vBkg;
521 switch (par) {
522 case Parameter::Potential:
523 return volt;
524 case Parameter::Magnitude:
525 return sqrt(ex * ex + ey * ey + ez * ez);
526 case Parameter::Ex:
527 return ex;
528 case Parameter::Ey:
529 return ey;
530 case Parameter::Ez:
531 return ez;
532 default:
533 break;
534 }
535 return volt;
536}
537
538double ViewField::Wfield(const double x, const double y, const double z,
539 const Parameter par,
540 const std::string& electrode) const {
541
542 if (par == Parameter::Potential) {
543 if (m_sensor) {
544 return m_sensor->WeightingPotential(x, y, z, electrode);
545 } else {
546 return m_component->WeightingPotential(x, y, z, electrode);
547 }
548 }
549
550 double ex = 0., ey = 0., ez = 0.;
551 if (!m_sensor) {
552 m_component->WeightingField(x, y, z, ex, ey, ez, electrode);
553 } else {
554 m_sensor->WeightingField(x, y, z, ex, ey, ez, electrode);
555 }
556
557 switch (par) {
558 case Parameter::Magnitude:
559 return sqrt(ex * ex + ey * ey + ez * ez);
560 case Parameter::Ex:
561 return ex;
562 case Parameter::Ey:
563 return ey;
564 case Parameter::Ez:
565 return ez;
566 default:
567 break;
568 }
569 return 0.;
570}
571
572void ViewField::PlotFieldLines(const std::vector<double>& x0,
573 const std::vector<double>& y0,
574 const std::vector<double>& z0,
575 const bool electron, const bool axis,
576 const short col) {
577
578 if (x0.empty() || y0.empty() || z0.empty()) return;
579 const size_t nLines = x0.size();
580 if (y0.size() != nLines || x0.size() != nLines) {
581 std::cerr << m_className << "::PlotLines:\n"
582 << " Mismatch in number of x/y/z coordinates.\n";
583 return;
584 }
585
586 if (!m_sensor && !m_component) {
587 std::cerr << m_className << "::PlotFieldLines:\n"
588 << " Neither sensor nor component are defined.\n";
589 return;
590 }
591
592 auto pad = GetCanvas();
593 pad->cd();
594 pad->SetTitle("Field lines");
595 // Determine the x-y range.
596 const bool rangeSet = RangeSet(pad);
597 if (axis || !rangeSet) {
598 // Determine the plot limits.
599 if (!SetPlotLimits()) {
600 std::cerr << m_className << "::PlotFieldLines:\n"
601 << " Could not determine the plot limits.\n";
602 return;
603 }
604 }
605 if (axis) {
606 auto frame = pad->DrawFrame(m_xMinPlot, m_yMinPlot,
608 frame->GetXaxis()->SetTitle(LabelX().c_str());
609 frame->GetYaxis()->SetTitle(LabelY().c_str());
610 pad->Update();
611 } else if (!rangeSet) {
613 }
614
615 DriftLineRKF drift;
616 Sensor sensor;
617 if (m_sensor) {
618 drift.SetSensor(m_sensor);
619 } else {
620 double xmin = 0., ymin = 0., zmin = 0.;
621 double xmax = 0., ymax = 0., zmax = 0.;
622 if (!m_component->GetBoundingBox(xmin, ymin, zmin, xmax, ymax, zmax)) {
623 if (m_userBox) {
626 }
627 }
628 sensor.AddComponent(m_component);
629 drift.SetSensor(&sensor);
630 }
631 const double lx = 0.1 * fabs(m_xMaxPlot - m_xMinPlot);
632 const double ly = 0.1 * fabs(m_yMaxPlot - m_yMinPlot);
633 drift.SetMaximumStepSize(std::min(lx, ly));
634 for (size_t i = 0; i < nLines; ++i) {
635 std::vector<std::array<float, 3> > xl;
636 if (!drift.FieldLine(x0[i], y0[i], z0[i], xl, electron)) continue;
637 DrawLine(xl, col, 1);
638 }
639 pad->Update();
640}
641
643 const double x0, const double y0, const double z0,
644 const double x1, const double y1, const double z1,
645 std::vector<double>& xf, std::vector<double>& yf,
646 std::vector<double>& zf,
647 const unsigned int nPoints) const {
648
649 if (nPoints < 2) {
650 std::cerr << m_className << "::EqualFluxIntervals:\n"
651 << " Number of flux lines must be > 1.\n";
652 return false;
653 }
654
655 // Set integration intervals.
656 constexpr unsigned int nV = 5;
657 // Compute the inplane vector normal to the track.
658 const double xp = (y1 - y0) * m_plane[2] - (z1 - z0) * m_plane[1];
659 const double yp = (z1 - z0) * m_plane[0] - (x1 - x0) * m_plane[2];
660 const double zp = (x1 - x0) * m_plane[1] - (y1 - y0) * m_plane[0];
661 // Compute the total flux, accepting positive and negative parts.
662 double q = 0.;
663 if (m_component) {
664 q = m_component->IntegrateFluxLine(x0, y0, z0, x1, y1, z1,
665 xp, yp, zp, 20 * nV, 0);
666 } else {
667 q = m_sensor->IntegrateFluxLine(x0, y0, z0, x1, y1, z1,
668 xp, yp, zp, 20 * nV, 0);
669 }
670 const int isign = q > 0 ? +1 : -1;
671 if (m_debug) {
672 std::cout << m_className << "::EqualFluxIntervals:\n";
673 std::printf(" Total flux: %15.e8\n", q);
674 }
675 // Compute the 1-sided flux in a number of steps.
676 double fsum = 0.;
677 unsigned int nOtherSign = 0;
678 double s0 = -1.;
679 double s1 = -1.;
680 constexpr size_t nSteps = 1000;
681 std::array<double, nSteps> sTab;
682 std::array<double, nSteps> fTab;
683 constexpr double ds = 1. / nSteps;
684 const double dx = (x1 - x0) * ds;
685 const double dy = (y1 - y0) * ds;
686 const double dz = (z1 - z0) * ds;
687 for (size_t i = 0; i < nSteps; ++i) {
688 const double x = x0 + i * dx;
689 const double y = y0 + i * dy;
690 const double z = z0 + i * dz;
691 if (m_component) {
692 q = m_component->IntegrateFluxLine(x, y, z, x + dx, y + dy, z + dz,
693 xp, yp, zp, nV, isign);
694 } else {
695 q = m_sensor->IntegrateFluxLine(x, y, z, x + dx, y + dy, z + dz,
696 xp, yp, zp, nV, isign);
697 }
698 sTab[i] = (i + 1) * ds;
699 if (q > 0) {
700 fsum += q;
701 if (s0 < -0.5) s0 = i * ds;
702 s1 = (i + 1) * ds;
703 }
704 if (q < 0) ++nOtherSign;
705 fTab[i] = fsum;
706 }
707 if (m_debug) {
708 std::printf(" Used flux: %15.8e V. Start: %10.3f End: %10.3f\n",
709 fsum, s0, s1);
710 }
711 // Make sure that the sum is positive.
712 if (fsum <= 0) {
713 std::cerr << m_className << "::EqualFluxIntervals:\n"
714 << " 1-Sided flux integral is not > 0.\n";
715 return false;
716 } else if (s0 < -0.5 || s1 < -0.5 || s1 <= s0) {
717 std::cerr << m_className << "::EqualFluxIntervals:\n"
718 << " No flux interval without sign change found.\n";
719 return false;
720 } else if (nOtherSign > 0) {
721 std::cerr << m_className << "::EqualFluxIntervals:\n"
722 << " The flux changes sign over the line.\n";
723 }
724 // Normalise the flux.
725 const double scale = (nPoints - 1) / fsum;
726 for (size_t i = 0; i < nSteps; ++i) fTab[i] *= scale;
727
728 // Compute new cluster position.
729 for (size_t i = 0; i < nPoints; ++i) {
730 double s = std::min(s1, std::max(s0, Interpolate(sTab, fTab, i)));
731 xf.push_back(x0 + s * (x1 - x0));
732 yf.push_back(y0 + s * (y1 - y0));
733 zf.push_back(z0 + s * (z1 - z0));
734 }
735 return true;
736}
737
739 const double x0, const double y0, const double z0,
740 const double x1, const double y1, const double z1,
741 std::vector<double>& xf, std::vector<double>& yf,
742 std::vector<double>& zf, const double interval) const {
743
744 if (interval <= 0.) {
745 std::cerr << m_className << "::FixedFluxIntervals:\n"
746 << " Flux interval must be > 0.\n";
747 return false;
748 }
749
750 // Set integration intervals.
751 constexpr unsigned int nV = 5;
752 // Compute the inplane vector normal to the track.
753 const double xp = (y1 - y0) * m_plane[2] - (z1 - z0) * m_plane[1];
754 const double yp = (z1 - z0) * m_plane[0] - (x1 - x0) * m_plane[2];
755 const double zp = (x1 - x0) * m_plane[1] - (y1 - y0) * m_plane[0];
756 // Compute the total flux, accepting positive and negative parts.
757 double q = 0.;
758 if (m_component) {
759 q = m_component->IntegrateFluxLine(x0, y0, z0, x1, y1, z1,
760 xp, yp, zp, 20 * nV, 0);
761 } else {
762 q = m_sensor->IntegrateFluxLine(x0, y0, z0, x1, y1, z1,
763 xp, yp, zp, 20 * nV, 0);
764 }
765 const int isign = q > 0 ? +1 : -1;
766 if (m_debug) {
767 std::cout << m_className << "::FixedFluxIntervals:\n";
768 std::printf(" Total flux: %15.8e V\n", q);
769 }
770 // Compute the 1-sided flux in a number of steps.
771 double fsum = 0.;
772 unsigned int nOtherSign = 0;
773 double s0 = -1.;
774 constexpr size_t nSteps = 1000;
775 std::array<double, nSteps> sTab;
776 std::array<double, nSteps> fTab;
777 constexpr double ds = 1. / nSteps;
778 const double dx = (x1 - x0) * ds;
779 const double dy = (y1 - y0) * ds;
780 const double dz = (z1 - z0) * ds;
781 for (size_t i = 0; i < nSteps; ++i) {
782 const double x = x0 + i * dx;
783 const double y = y0 + i * dy;
784 const double z = z0 + i * dz;
785 if (m_component) {
786 q = m_component->IntegrateFluxLine(x, y, z, x + dx, y + dy, z + dz,
787 xp, yp, zp, nV, isign);
788 } else {
789 q = m_sensor->IntegrateFluxLine(x, y, z, x + dx, y + dy, z + dz,
790 xp, yp, zp, nV, isign);
791 }
792 sTab[i] = (i + 1) * ds;
793 if (q > 0) {
794 fsum += q;
795 if (s0 < -0.5) s0 = i * ds;
796 }
797 if (q < 0) ++nOtherSign;
798 fTab[i] = fsum;
799 }
800 // Make sure that the sum is positive.
801 if (m_debug) {
802 std::printf(" Used flux: %15.8e V. Start offset: %10.3f\n", fsum, s0);
803 }
804 if (fsum <= 0) {
805 std::cerr << m_className << "::FixedFluxIntervals:\n"
806 << " 1-Sided flux integral is not > 0.\n";
807 return false;
808 } else if (s0 < -0.5) {
809 std::cerr << m_className << "::FixedFluxIntervals:\n"
810 << " No flux interval without sign change found.\n";
811 return false;
812 } else if (nOtherSign > 0) {
813 std::cerr << m_className << "::FixedFluxIntervals:\n"
814 << " Warning: The flux changes sign over the line.\n";
815 }
816
817 double f = 0.;
818 while (f < fsum) {
819 const double s = Interpolate(sTab, fTab, f);
820 f += interval;
821 xf.push_back(x0 + s * (x1 - x0));
822 yf.push_back(y0 + s * (y1 - y0));
823 zf.push_back(z0 + s * (z1 - z0));
824 }
825 return true;
826}
827}
Abstract base class for components.
Definition: Component.hh:13
virtual double WeightingPotential(const double x, const double y, const double z, const std::string &label)
Definition: Component.cc:59
virtual bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Get the bounding box coordinates.
Definition: Component.cc:99
virtual void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status)=0
virtual void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
Definition: Component.cc:40
virtual bool GetVoltageRange(double &vmin, double &vmax)=0
Calculate the voltage range [V].
double IntegrateFluxLine(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const double xp, const double yp, const double zp, const unsigned int nI, const int isign=0)
Definition: Component.cc:320
void SetSensor(Sensor *s)
Set the sensor.
Definition: DriftLineRKF.cc:55
bool FieldLine(const double xi, const double yi, const double zi, std::vector< std::array< float, 3 > > &xl, const bool electron=true)
void SetMaximumStepSize(const double ms)
Set the maximum step size that is allowed. By default, there is no limit.
Definition: DriftLineRKF.cc:72
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
Definition: Sensor.cc:396
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
Get the weighting field at (x, y, z).
Definition: Sensor.cc:131
void AddComponent(Component *comp)
Add a component.
Definition: Sensor.cc:316
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:65
bool SetArea()
Set the user area to the default.
Definition: Sensor.cc:183
double IntegrateFluxLine(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const double xp, const double yp, const double zp, const unsigned int nI, const int isign=0)
Definition: Sensor.cc:301
double WeightingPotential(const double x, const double y, const double z, const std::string &label)
Get the weighting potential at (x, y, z).
Definition: Sensor.cc:147
Base class for visualization classes.
Definition: ViewBase.hh:18
std::array< double, 4 > m_plane
Definition: ViewBase.hh:99
std::string LabelY()
Definition: ViewBase.cc:483
std::string LabelX()
Definition: ViewBase.cc:420
void DrawLine(const std::vector< std::array< float, 3 > > &xl, const short col, const short lw)
Definition: ViewBase.cc:371
std::string m_className
Definition: ViewBase.hh:78
std::array< std::array< double, 3 >, 3 > m_proj
Definition: ViewBase.hh:96
bool RangeSet(TPad *)
Definition: ViewBase.cc:83
TPad * GetCanvas()
Retrieve the canvas.
Definition: ViewBase.cc:74
void SetRange(TPad *pad, const double x0, const double y0, const double x1, const double y1)
Definition: ViewBase.cc:93
bool PlotLimits(Sensor *sensor, double &xmin, double &ymin, double &xmax, double &ymax) const
Definition: ViewBase.cc:608
bool PlotLimitsFromUserBox(double &xmin, double &ymin, double &xmax, double &ymax) const
Definition: ViewBase.cc:663
static std::string FindUnusedFunctionName(const std::string &s)
Find an unused function name.
Definition: ViewBase.cc:290
ViewField()
Constructor.
Definition: ViewField.cc:70
void PlotProfileWeightingField(const std::string &label, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const std::string &option="v", const bool normalised=true)
Definition: ViewField.cc:154
void SetElectricFieldRange(const double emin, const double emax)
Set the plot limits for the electric field.
Definition: ViewField.cc:96
void PlotWeightingField(const std::string &label, const std::string &option, const std::string &drawopt)
Definition: ViewField.cc:143
void SetNumberOfSamples2d(const unsigned int nx, const unsigned int ny)
Set the number of points used for drawing 2D functions.
Definition: ViewField.cc:116
bool FixedFluxIntervals(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, std::vector< double > &xf, std::vector< double > &yf, std::vector< double > &zf, const double interval=10.) const
Generate points along a line, spaced by a given flux interval.
Definition: ViewField.cc:738
void SetComponent(Component *c)
Set the component for which to plot the field.
Definition: ViewField.cc:81
void SetNumberOfSamples1d(const unsigned int n)
Set the number of points used for drawing 1D functions.
Definition: ViewField.cc:112
void PlotFieldLines(const std::vector< double > &x0, const std::vector< double > &y0, const std::vector< double > &z0, const bool electron=true, const bool axis=true, const short col=kOrange - 3)
Draw electric field lines from a set of starting points.
Definition: ViewField.cc:572
void SetVoltageRange(const double vmin, const double vmax)
Set the plot limits for the potential.
Definition: ViewField.cc:90
void PlotProfile(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const std::string &option="v", const bool normalised=true)
Definition: ViewField.cc:137
bool EqualFluxIntervals(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, std::vector< double > &xf, std::vector< double > &yf, std::vector< double > &zf, const unsigned int nPoints=20) const
Generates point along a line, spaced by equal flux intervals.
Definition: ViewField.cc:642
void PlotContour(const std::string &option="v")
Definition: ViewField.cc:122
void Plot(const std::string &option="v", const std::string &drawopt="arr")
Definition: ViewField.cc:126
void SetNumberOfContours(const unsigned int n)
Set the number of contour levels.
Definition: ViewField.cc:108
void SetWeightingFieldRange(const double wmin, const double wmax)
Set the plot limits for the weighting field.
Definition: ViewField.cc:102
void SetSensor(Sensor *s)
Set the sensor for which to plot the field.
Definition: ViewField.cc:72
void PlotContourWeightingField(const std::string &label, const std::string &option)
Definition: ViewField.cc:149
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
Definition: Random.hh:14
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615
DoubleAc sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:314