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