Garfield++ 3.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 <limits>
6
7#include <TAxis.h>
8#include <TROOT.h>
9
11#include "Garfield/Plotting.hh"
12#include "Garfield/Random.hh"
13#include "Garfield/Sensor.hh"
14#include "Garfield/ViewField.hh"
15
16namespace {
17
18void SampleRange(const double xmin, const double ymin, const double xmax,
19 const double ymax, TF2* f, double& zmin, double& zmax) {
20 const unsigned int n = 1000;
21 const double dx = xmax - xmin;
22 const double dy = ymax - ymin;
23 zmin = std::numeric_limits<double>::max();
24 zmax = -zmin;
25 for (unsigned int i = 0; i < n; ++i) {
26 const double z = f->Eval(xmin + Garfield::RndmUniform() * dx,
27 ymin + Garfield::RndmUniform() * dy);
28 if (z < zmin) zmin = z;
29 if (z > zmax) zmax = z;
30 }
31}
32
33void SampleRange(TF1* f, double& ymin, double& ymax) {
34 const unsigned int n = 1000;
35 ymin = std::numeric_limits<double>::max();
36 ymax = -ymin;
37 for (unsigned int i = 0; i < n; ++i) {
38 const double y = f->Eval(Garfield::RndmUniform());
39 if (y < ymin) ymin = y;
40 if (y > ymax) ymax = y;
41 }
42}
43}
44
45namespace Garfield {
46
49}
50
52 if (m_f2d) delete m_f2d;
53 if (m_f2dW) delete m_f2dW;
54 if (m_fProfile) delete m_fProfile;
55 if (m_fProfileW) delete m_fProfileW;
56}
57
59 if (!s) {
60 std::cerr << m_className << "::SetSensor: Null pointer.\n";
61 return;
62 }
63
64 m_sensor = s;
65 m_component = nullptr;
66}
67
69 if (!c) {
70 std::cerr << m_className << "::SetComponent: Null pointer.\n";
71 return;
72 }
73
74 m_component = c;
75 m_sensor = nullptr;
76}
77
78void ViewField::SetArea(const double xmin, const double ymin, const double xmax,
79 const double ymax) {
80 // Check range, assign if non-null.
81 if (xmin == xmax || ymin == ymax) {
82 std::cerr << m_className << "::SetArea: Null area range is not permitted.\n"
83 << " " << xmin << " < x < " << xmax << "\n"
84 << " " << ymin << " < y < " << ymax << "\n";
85 return;
86 }
87 m_xmin = std::min(xmin, xmax);
88 m_ymin = std::min(ymin, ymax);
89 m_xmax = std::max(xmin, xmax);
90 m_ymax = std::max(ymin, ymax);
91 m_hasUserArea = true;
92}
93
94void ViewField::SetVoltageRange(const double vmin, const double vmax) {
95 m_vmin = std::min(vmin, vmax);
96 m_vmax = std::max(vmin, vmax);
97 m_useAutoRange = false;
98}
99
100void ViewField::SetElectricFieldRange(const double emin, const double emax) {
101 m_emin = std::min(emin, emax);
102 m_emax = std::max(emin, emax);
103 m_useAutoRange = false;
104}
105
106void ViewField::SetWeightingFieldRange(const double wmin, const double wmax) {
107 m_wmin = std::min(wmin, wmax);
108 m_wmax = std::max(wmin, wmax);
109 m_useAutoRange = false;
110}
111
112void ViewField::SetNumberOfContours(const unsigned int n) {
113 if (n <= m_nMaxContours) {
114 m_nContours = n;
115 } else {
116 std::cerr << m_className << "::SetNumberOfContours:\n"
117 << " Max. number of contours is " << m_nMaxContours << ".\n";
118 }
119}
120
121void ViewField::SetNumberOfSamples1d(const unsigned int n) {
122 constexpr unsigned int nmin = 10;
123 constexpr unsigned int nmax = 100000;
124 if (n < nmin || n > nmax) {
125 std::cerr << m_className << "::SetNumberOfSamples1d:\n"
126 << " Number of points (" << n << ") out of range.\n"
127 << " " << nmin << " <= n <= " << nmax << "\n";
128 return;
129 }
130
131 m_nSamples1d = n;
132}
133
134void ViewField::SetNumberOfSamples2d(const unsigned int nx,
135 const unsigned int ny) {
136 constexpr unsigned int nmin = 10;
137 constexpr unsigned int nmax = 10000;
138 if (nx < nmin || nx > nmax) {
139 std::cerr << m_className << "::SetNumberOfSamples2d:\n"
140 << " Number of x-points (" << nx << ") out of range.\n"
141 << " " << nmin << " <= nx <= " << nmax << "\n";
142 } else {
143 m_nSamples2dX = nx;
144 }
145
146 if (ny < nmin || ny > nmax) {
147 std::cerr << m_className << "::SetNumberOfSamples2d:\n"
148 << " Number of y-points (" << ny << ") out of range.\n"
149 << " " << nmin << " <= ny <= " << nmax << "\n";
150 } else {
151 m_nSamples2dY = ny;
152 }
153}
154
155void ViewField::PlotContour(const std::string& option) {
156 SetupCanvas();
157 if (!SetupFunction(option, m_f2d, true, false)) return;
158 m_f2d->Draw("CONT4Z");
159 gPad->SetRightMargin(0.15);
160 gPad->Update();
161}
162
163void ViewField::Plot(const std::string& option, const std::string& drawopt) {
164 SetupCanvas();
165 if (!SetupFunction(option, m_f2d, false, false)) return;
166 m_f2d->Draw(drawopt.c_str());
167 gPad->SetRightMargin(0.15);
168 gPad->Update();
169}
170
171void ViewField::PlotProfile(const double x0, const double y0, const double z0,
172 const double x1, const double y1, const double z1,
173 const std::string& option) {
174 SetupCanvas();
175 if (!SetupProfile(x0, y0, z0, x1, y1, z1, option, m_fProfile, false)) return;
176 m_fProfile->Draw();
177 gPad->Update();
178}
179
180void ViewField::PlotWeightingField(const std::string& label,
181 const std::string& option,
182 const std::string& drawopt) {
183 m_electrode = label;
184 SetupCanvas();
185 if (!SetupFunction(option, m_f2dW, false, true)) return;
186 m_f2dW->Draw(drawopt.c_str());
187 gPad->SetRightMargin(0.15);
188 gPad->Update();
189}
190
191void ViewField::PlotContourWeightingField(const std::string& label,
192 const std::string& option) {
193 m_electrode = label;
194 SetupCanvas();
195 if (!SetupFunction(option, m_f2dW, true, true)) return;
196 m_f2dW->Draw("CONT4Z");
197 gPad->SetRightMargin(0.15);
198 gPad->Update();
199}
200
201void ViewField::PlotProfileWeightingField(const std::string& label,
202 const double x0, const double y0,
203 const double z0, const double x1,
204 const double y1, const double z1,
205 const std::string& option) {
206 m_electrode = label;
207 SetupCanvas();
208 if (!SetupProfile(x0, y0, z0, x1, y1, z1, option, m_fProfileW, true)) return;
209 m_fProfileW->Draw();
210 gPad->Update();
211}
212
213
215 // Default projection: x-y at z=0
216 m_project[0][0] = 1;
217 m_project[1][0] = 0;
218 m_project[2][0] = 0;
219 m_project[0][1] = 0;
220 m_project[1][1] = 1;
221 m_project[2][1] = 0;
222 m_project[0][2] = 0;
223 m_project[1][2] = 0;
224 m_project[2][2] = 0;
225
226 // Plane description
227 m_plane[0] = 0;
228 m_plane[1] = 0;
229 m_plane[2] = 1;
230 m_plane[3] = 0;
231
232 // Prepare axis labels.
233 Labels();
234}
235
236double ViewField::Evaluate2D(double* pos, double* par) {
237 if (!m_sensor && !m_component) return 0.;
238
239 // Transform to global coordinates.
240 const double u = pos[0];
241 const double v = pos[1];
242 const double x = m_project[0][0] * u + m_project[1][0] * v + m_project[2][0];
243 const double y = m_project[0][1] * u + m_project[1][1] * v + m_project[2][1];
244 const double z = m_project[0][2] * u + m_project[1][2] * v + m_project[2][2];
245
246 // Determine which quantity to plot.
247 const PlotType plotType = static_cast<PlotType>(int(fabs(par[0]) / 10.));
248
249 // Compute the field.
250 double ex = 0., ey = 0., ez = 0., volt = 0.;
251 int status = 0;
252 if (par[0] > 0.) {
253 // "Drift" electric field.
254 Medium* medium = nullptr;
255 if (!m_sensor) {
256 m_component->ElectricField(x, y, z, ex, ey, ez, volt, medium, status);
257 } else {
258 m_sensor->ElectricField(x, y, z, ex, ey, ez, volt, medium, status);
259 }
260 } else {
261 // Weighting field.
262 if (!m_sensor) {
263 if (plotType == Potential) {
264 volt = m_component->WeightingPotential(x, y, z, m_electrode);
265 } else {
266 m_component->WeightingField(x, y, z, ex, ey, ez, m_electrode);
267 }
268 } else {
269 if (plotType == Potential) {
270 volt = m_sensor->WeightingPotential(x, y, z, m_electrode);
271 } else {
272 m_sensor->WeightingField(x, y, z, ex, ey, ez, m_electrode);
273 }
274 }
275 }
276
277 if (m_debug) {
278 std::cout << m_className << "::Evaluate2D:\n"
279 << " At (u, v) = (" << u << ", " << v << "), "
280 << " (x,y,z) = (" << x << "," << y << "," << z << ")\n"
281 << " E = " << ex << ", " << ey << ", " << ez
282 << "), V = " << volt << ", status = " << status << "\n";
283 }
284 if (m_useStatus && status != 0) return m_vBkg;
285
286 switch (plotType) {
287 case Potential:
288 return volt;
289 break;
290 case Magnitude:
291 return sqrt(ex * ex + ey * ey + ez * ez);
292 break;
293 case Ex:
294 return ex;
295 break;
296 case Ey:
297 return ey;
298 break;
299 case Ez:
300 return ez;
301 break;
302 default:
303 break;
304 }
305 return volt;
306}
307
308double ViewField::EvaluateProfile(double* pos, double* par) {
309 if (!m_sensor && !m_component) return 0.;
310
311 // Get the start and end position.
312 const double x0 = par[0];
313 const double y0 = par[1];
314 const double z0 = par[2];
315 const double x1 = par[3];
316 const double y1 = par[4];
317 const double z1 = par[5];
318 // Compute the direction.
319 const double dx = x1 - x0;
320 const double dy = y1 - y0;
321 const double dz = z1 - z0;
322 // Get the position.
323 const double t = pos[0];
324 const double x = x0 + t * dx;
325 const double y = y0 + t * dy;
326 const double z = z0 + t * dz;
327 // Determine which quantity to plot.
328 const PlotType plotType = static_cast<PlotType>(int(fabs(par[6]) / 10.));
329
330 // Compute the field.
331 double ex = 0., ey = 0., ez = 0., volt = 0.;
332 int status = 0;
333 if (par[6] > 0.) {
334 Medium* medium = nullptr;
335 // "Drift" electric field.
336 if (!m_sensor) {
337 m_component->ElectricField(x, y, z, ex, ey, ez, volt, medium, status);
338 } else {
339 m_sensor->ElectricField(x, y, z, ex, ey, ez, volt, medium, status);
340 }
341 } else {
342 // Weighting field.
343 if (!m_sensor) {
344 if (plotType == Potential) {
345 volt = m_component->WeightingPotential(x, y, z, m_electrode);
346 } else {
347 m_component->WeightingField(x, y, z, ex, ey, ez, m_electrode);
348 }
349 } else {
350 if (plotType == Potential) {
351 volt = m_sensor->WeightingPotential(x, y, z, m_electrode);
352 } else {
353 m_sensor->WeightingField(x, y, z, ex, ey, ez, m_electrode);
354 }
355 }
356 }
357 if (m_useStatus && status != 0) volt = m_vBkg;
358
359 switch (plotType) {
360 case Potential:
361 return volt;
362 break;
363 case Magnitude:
364 return sqrt(ex * ex + ey * ey + ez * ez);
365 break;
366 case Ex:
367 return ex;
368 break;
369 case Ey:
370 return ey;
371 break;
372 case Ez:
373 return ez;
374 break;
375 default:
376 break;
377 }
378 return volt;
379}
380
381void ViewField::Labels() {
382 // Initialisation of the x-axis label
383 strcpy(m_xLabel, "\0");
384 char buf[100];
385
386 const double tol = 1.e-4;
387 // x portion
388 if (fabs(m_project[0][0] - 1) < tol) {
389 strcat(m_xLabel, "x");
390 } else if (fabs(m_project[0][0] + 1) < tol) {
391 strcat(m_xLabel, "-x");
392 } else if (m_project[0][0] > tol) {
393 sprintf(buf, "%g x", m_project[0][0]);
394 strcat(m_xLabel, buf);
395 } else if (m_project[0][0] < -tol) {
396 sprintf(buf, "%g x", m_project[0][0]);
397 strcat(m_xLabel, buf);
398 }
399
400 // y portion
401 if (strlen(m_xLabel) > 0) {
402 if (m_project[0][1] < -tol) {
403 strcat(m_xLabel, " - ");
404 } else if (m_project[0][1] > tol) {
405 strcat(m_xLabel, " + ");
406 }
407 if (fabs(m_project[0][1] - 1) < tol || fabs(m_project[0][1] + 1) < tol) {
408 strcat(m_xLabel, "y");
409 } else if (fabs(m_project[0][1]) > tol) {
410 sprintf(buf, "%g y", fabs(m_project[0][1]));
411 strcat(m_xLabel, buf);
412 }
413 } else {
414 if (fabs(m_project[0][1] - 1) < tol) {
415 strcat(m_xLabel, "y");
416 } else if (fabs(m_project[0][1] + 1) < tol) {
417 strcat(m_xLabel, "-y");
418 } else if (m_project[0][1] > tol) {
419 sprintf(buf, "%g y", m_project[0][1]);
420 strcat(m_xLabel, buf);
421 } else if (m_project[0][1] < -tol) {
422 sprintf(buf, "%g y", m_project[0][1]);
423 strcat(m_xLabel, buf);
424 }
425 }
426
427 // z portion
428 if (strlen(m_xLabel) > 0) {
429 if (m_project[0][2] < -tol) {
430 strcat(m_xLabel, " - ");
431 } else if (m_project[0][2] > tol) {
432 strcat(m_xLabel, " + ");
433 }
434 if (fabs(m_project[0][2] - 1) < tol || fabs(m_project[0][2] + 1) < tol) {
435 strcat(m_xLabel, "z");
436 } else if (fabs(m_project[0][2]) > tol) {
437 sprintf(buf, "%g z", fabs(m_project[0][2]));
438 strcat(m_xLabel, buf);
439 }
440 } else {
441 if (fabs(m_project[0][2] - 1) < tol) {
442 strcat(m_xLabel, "z");
443 } else if (fabs(m_project[0][2] + 1) < tol) {
444 strcat(m_xLabel, "-z");
445 } else if (m_project[0][2] > tol) {
446 sprintf(buf, "%g z", m_project[0][2]);
447 strcat(m_xLabel, buf);
448 } else if (m_project[0][2] < -tol) {
449 sprintf(buf, "%g z", m_project[0][2]);
450 strcat(m_xLabel, buf);
451 }
452 }
453
454 // Unit
455 strcat(m_xLabel, " [cm]");
456
457 // Initialisation of the y-axis label
458 strcpy(m_yLabel, "\0");
459
460 // x portion
461 if (fabs(m_project[1][0] - 1) < tol) {
462 strcat(m_yLabel, "x");
463 } else if (fabs(m_project[1][0] + 1) < tol) {
464 strcat(m_yLabel, "-x");
465 } else if (m_project[1][0] > tol) {
466 sprintf(buf, "%g x", m_project[1][0]);
467 strcat(m_yLabel, buf);
468 } else if (m_project[1][0] < -tol) {
469 sprintf(buf, "%g x", m_project[1][0]);
470 strcat(m_yLabel, buf);
471 }
472
473 // y portion
474 if (strlen(m_yLabel) > 0) {
475 if (m_project[1][1] < -tol) {
476 strcat(m_yLabel, " - ");
477 } else if (m_project[1][1] > tol) {
478 strcat(m_yLabel, " + ");
479 }
480 if (fabs(m_project[1][1] - 1) < tol || fabs(m_project[1][1] + 1) < tol) {
481 strcat(m_yLabel, "y");
482 } else if (fabs(m_project[1][1]) > tol) {
483 sprintf(buf, "%g y", fabs(m_project[1][1]));
484 strcat(m_yLabel, buf);
485 }
486 } else {
487 if (fabs(m_project[1][1] - 1) < tol) {
488 strcat(m_yLabel, "y");
489 } else if (fabs(m_project[1][1] + 1) < tol) {
490 strcat(m_yLabel, "-y");
491 } else if (m_project[1][1] > tol) {
492 sprintf(buf, "%g y", m_project[1][1]);
493 strcat(m_yLabel, buf);
494 } else if (m_project[1][1] < -tol) {
495 sprintf(buf, "%g y", m_project[1][1]);
496 strcat(m_yLabel, buf);
497 }
498 }
499
500 // z portion
501 if (strlen(m_yLabel) > 0) {
502 if (m_project[1][2] < -tol) {
503 strcat(m_yLabel, " - ");
504 } else if (m_project[1][2] > tol) {
505 strcat(m_yLabel, " + ");
506 }
507 if (fabs(m_project[1][2] - 1) < tol || fabs(m_project[1][2] + 1) < tol) {
508 strcat(m_yLabel, "z");
509 } else if (fabs(m_project[1][2]) > tol) {
510 sprintf(buf, "%g z", fabs(m_project[1][2]));
511 strcat(m_yLabel, buf);
512 }
513 } else {
514 if (fabs(m_project[1][2] - 1) < tol) {
515 strcat(m_yLabel, "z");
516 } else if (fabs(m_project[1][2] + 1) < tol) {
517 strcat(m_yLabel, "-z");
518 } else if (m_project[1][2] > tol) {
519 sprintf(buf, "%g z", m_project[1][2]);
520 strcat(m_yLabel, buf);
521 } else if (m_project[1][2] < -tol) {
522 sprintf(buf, "%g z", m_project[1][2]);
523 strcat(m_yLabel, buf);
524 }
525 }
526
527 // Unit
528 strcat(m_yLabel, " [cm]");
529
530 // Initialisation of the plane label
531 strcpy(m_description, "\0");
532
533 // x portion
534 if (fabs(m_plane[0] - 1) < tol) {
535 strcat(m_description, "x");
536 } else if (fabs(m_plane[0] + 1) < tol) {
537 strcat(m_description, "-x");
538 } else if (m_plane[0] > tol) {
539 sprintf(buf, "%g x", m_plane[0]);
540 strcat(m_description, buf);
541 } else if (m_plane[0] < -tol) {
542 sprintf(buf, "%g x", m_plane[0]);
543 strcat(m_description, buf);
544 }
545
546 // y portion
547 if (strlen(m_description) > 0) {
548 if (m_plane[1] < -tol) {
549 strcat(m_description, " - ");
550 } else if (m_plane[1] > tol) {
551 strcat(m_description, " + ");
552 }
553 if (fabs(m_plane[1] - 1) < tol || fabs(m_plane[1] + 1) < tol) {
554 strcat(m_description, "y");
555 } else if (fabs(m_plane[1]) > tol) {
556 sprintf(buf, "%g y", fabs(m_plane[1]));
557 strcat(m_description, buf);
558 }
559 } else {
560 if (fabs(m_plane[1] - 1) < tol) {
561 strcat(m_description, "y");
562 } else if (fabs(m_plane[1] + 1) < tol) {
563 strcat(m_description, "-y");
564 } else if (m_plane[1] > tol) {
565 sprintf(buf, "%g y", m_plane[1]);
566 strcat(m_description, buf);
567 } else if (m_plane[1] < -tol) {
568 sprintf(buf, "%g y", m_plane[1]);
569 strcat(m_description, buf);
570 }
571 }
572
573 // z portion
574 if (strlen(m_description) > 0) {
575 if (m_plane[2] < -tol) {
576 strcat(m_description, " - ");
577 } else if (m_plane[2] > tol) {
578 strcat(m_description, " + ");
579 }
580 if (fabs(m_plane[2] - 1) < tol || fabs(m_plane[2] + 1) < tol) {
581 strcat(m_description, "z");
582 } else if (fabs(m_plane[2]) > tol) {
583 sprintf(buf, "%g z", fabs(m_plane[2]));
584 strcat(m_description, buf);
585 }
586 } else {
587 if (fabs(m_plane[2] - 1) < tol) {
588 strcat(m_description, "z");
589 } else if (fabs(m_plane[2] + 1) < tol) {
590 strcat(m_description, "-z");
591 } else if (m_plane[2] > tol) {
592 sprintf(buf, "%g z", m_plane[2]);
593 strcat(m_description, buf);
594 } else if (m_plane[2] < -tol) {
595 sprintf(buf, "%g z", m_plane[2]);
596 strcat(m_description, buf);
597 }
598 }
599
600 // Constant
601 sprintf(buf, " = %g", m_plane[3]);
602 strcat(m_description, buf);
603
604 if (m_debug) {
605 std::cout << m_className << "::Labels:\n"
606 << " x label: |" << m_xLabel << "|\n"
607 << " y label: |" << m_yLabel << "|\n"
608 << " plane: |" << m_description << "|\n";
609 }
610}
611
612void ViewField::SetPlane(const double fx, const double fy, const double fz,
613 const double x0, const double y0, const double z0) {
614 // Calculate two in-plane vectors for the normal vector
615 const double fnorm = sqrt(fx * fx + fy * fy + fz * fz);
616 if (fnorm > 0 && fx * fx + fz * fz > 0) {
617 const double fxz = sqrt(fx * fx + fz * fz);
618 m_project[0][0] = fz / fxz;
619 m_project[0][1] = 0;
620 m_project[0][2] = -fx / fxz;
621 m_project[1][0] = -fx * fy / (fxz * fnorm);
622 m_project[1][1] = (fx * fx + fz * fz) / (fxz * fnorm);
623 m_project[1][2] = -fy * fz / (fxz * fnorm);
624 m_project[2][0] = x0;
625 m_project[2][1] = y0;
626 m_project[2][2] = z0;
627 } else if (fnorm > 0 && fy * fy + fz * fz > 0) {
628 const double fyz = sqrt(fy * fy + fz * fz);
629 m_project[0][0] = (fy * fy + fz * fz) / (fyz * fnorm);
630 m_project[0][1] = -fx * fz / (fyz * fnorm);
631 m_project[0][2] = -fy * fz / (fyz * fnorm);
632 m_project[1][0] = 0;
633 m_project[1][1] = fz / fyz;
634 m_project[1][2] = -fy / fyz;
635 m_project[2][0] = x0;
636 m_project[2][1] = y0;
637 m_project[2][2] = z0;
638 } else {
639 std::cout << m_className << "::SetPlane:\n"
640 << " Normal vector has zero norm. No new projection set.\n";
641 }
642
643 // Store the plane description
644 m_plane[0] = fx;
645 m_plane[1] = fy;
646 m_plane[2] = fz;
647 m_plane[3] = fx * x0 + fy * y0 + fz * z0;
648
649 // Make labels to be placed along the axes
650 Labels();
651}
652
653void ViewField::Rotate(const double theta) {
654 // Rotate the axes
655 double auxu[3], auxv[3];
656 const double ctheta = cos(theta);
657 const double stheta = sin(theta);
658 for (int i = 0; i < 3; ++i) {
659 auxu[i] = ctheta * m_project[0][i] - stheta * m_project[1][i];
660 auxv[i] = stheta * m_project[0][i] + ctheta * m_project[1][i];
661 }
662 for (int i = 0; i < 3; ++i) {
663 m_project[0][i] = auxu[i];
664 m_project[1][i] = auxv[i];
665 }
666
667 // Make labels to be placed along the axes
668 Labels();
669}
670
671void ViewField::SetupCanvas() {
672 if (!m_canvas) {
673 m_canvas = new TCanvas();
674 m_canvas->SetTitle("Field View");
675 m_hasExternalCanvas = false;
676 }
677 m_canvas->cd();
678}
679
680ViewField::PlotType ViewField::GetPlotType(const std::string& option,
681 std::string& title) const {
682 if (option == "v" || option == "p" || option == "phi" || option == "volt" ||
683 option == "voltage" || option == "pot" || option == "potential") {
684 title = "potential";
685 return Potential;
686 } else if (option == "e" || option == "field") {
687 title = "field";
688 return Magnitude;
689 } else if (option == "ex") {
690 title = "field (x-component)";
691 return Ex;
692 } else if (option == "ey") {
693 title = "field (y-component)";
694 return Ey;
695 } else if (option == "ez") {
696 title = "field (z-component)";
697 return Ez;
698 }
699 std::cerr << m_className << "::GetPlotType:\n Unknown option (" << option
700 << ").\n";
701 title = "potential";
702 return Potential;
703}
704
705bool ViewField::SetupFunction(const std::string& option, TF2*& f,
706 const bool contour, const bool wfield) {
707 if (!m_sensor && !m_component) {
708 std::cerr << m_className << "::SetupFunction:\n"
709 << " Neither sensor nor component are defined.\n";
710 return false;
711 }
712
713 // Determine the x-y range (unless specified explicitly by the user).
714 if (!m_hasUserArea) {
715 // Try to get the area/bounding box from the sensor/component.
716 double bbmin[3];
717 double bbmax[3];
718 if (m_sensor) {
719 if (!m_sensor->GetArea(bbmin[0], bbmin[1], bbmin[2], bbmax[0], bbmax[1],
720 bbmax[2])) {
721 std::cerr << m_className << "::SetupFunction:\n"
722 << " Sensor area is not defined.\n"
723 << " Please set the plot range explicitly (SetArea).\n";
724 return false;
725 }
726 } else {
727 if (!m_component->GetBoundingBox(bbmin[0], bbmin[1], bbmin[2], bbmax[0],
728 bbmax[1], bbmax[2])) {
729 std::cerr << m_className << "::SetupFunction:\n"
730 << " Bounding box of the component is not defined.\n"
731 << " Please set the plot range explicitly (SetArea).\n";
732 return false;
733 }
734 }
735 const double tol = 1.e-4;
736 double umin[2] = {-std::numeric_limits<double>::max(),
737 -std::numeric_limits<double>::max()};
738 double umax[2] = {std::numeric_limits<double>::max(),
739 std::numeric_limits<double>::max()};
740 for (unsigned int i = 0; i < 3; ++i) {
741 bbmin[i] -= m_project[2][i];
742 bbmax[i] -= m_project[2][i];
743 for (unsigned int j = 0; j < 2; ++j) {
744 if (fabs(m_project[j][i]) < tol) continue;
745 const double t1 = bbmin[i] / m_project[j][i];
746 const double t2 = bbmax[i] / m_project[j][i];
747 const double tmin = std::min(t1, t2);
748 const double tmax = std::max(t1, t2);
749 if (tmin > umin[j] && tmin < umax[j]) umin[j] = tmin;
750 if (tmax < umax[j] && tmax > umin[j]) umax[j] = tmax;
751 }
752 }
753 m_xmin = umin[0];
754 m_xmax = umax[0];
755 m_ymin = umin[1];
756 m_ymax = umax[1];
757 std::cout << m_className << "::SetupFunction:\n Setting plot range to "
758 << m_xmin << " < x < " << m_xmax << ", " << m_ymin << " < y < "
759 << m_ymax << ".\n";
760 }
761 if (!f) {
762 const std::string fname = FindUnusedFunctionName("f2D");
763 f = new TF2(fname.c_str(), this, &ViewField::Evaluate2D, m_xmin, m_xmax,
764 m_ymin, m_ymax, 1, "ViewField", "Evaluate2D");
765 }
766 // Set the x-y range.
767 f->SetRange(m_xmin, m_ymin, m_xmax, m_ymax);
768
769 // Determine the quantity to be plotted.
770 std::string title;
771 const PlotType plotType = GetPlotType(option, title);
772 const int parPlotType = 10 * int(plotType) + 1;
773
774 // Set the z-range.
775 double zmin = m_vmin;
776 double zmax = m_vmax;
777 if (wfield) {
778 title = "weighting " + title;
779 f->SetParameter(0, -parPlotType);
780 if (m_useAutoRange) {
781 SampleRange(m_xmin, m_ymin, m_xmax, m_ymax, f, zmin, zmax);
782 } else if (plotType == Potential) {
783 zmin = 0.;
784 zmax = 1.;
785 } else {
786 zmin = m_wmin;
787 zmax = m_wmax;
788 }
789 } else {
790 f->SetParameter(0, parPlotType);
791 if (plotType == Potential) {
792 if (m_useAutoRange) {
793 if (m_component) {
794 if (!m_component->GetVoltageRange(zmin, zmax)) {
795 SampleRange(m_xmin, m_ymin, m_xmax, m_ymax, f, zmin, zmax);
796 }
797 } else if (m_sensor) {
798 if (!m_sensor->GetVoltageRange(zmin, zmax)) {
799 SampleRange(m_xmin, m_ymin, m_xmax, m_ymax, f, zmin, zmax);
800 }
801 }
802 } else {
803 zmin = m_vmin;
804 zmax = m_vmax;
805 }
806 } else {
807 title = "electric " + title;
808 if (m_useAutoRange) {
809 SampleRange(m_xmin, m_ymin, m_xmax, m_ymax, f, zmin, zmax);
810 } else {
811 zmin = m_emin;
812 zmax = m_emax;
813 }
814 }
815 }
816 f->SetMinimum(zmin);
817 f->SetMaximum(zmax);
818
819 // Set the contours if requested.
820 if (contour) {
821 title = "Contours of the " + title;
822 double level[m_nMaxContours];
823 if (m_nContours > 1) {
824 const double step = (zmax - zmin) / (m_nContours - 1.);
825 for (unsigned int i = 0; i < m_nContours; ++i) {
826 level[i] = zmin + i * step;
827 }
828 } else {
829 level[0] = 0.5 * (zmax + zmin);
830 }
831 if (m_debug) {
832 std::cout << m_className << "::SetupFunction:\n"
833 << " Number of contours: " << m_nContours << "\n";
834 for (unsigned int i = 0; i < m_nContours; ++i) {
835 std::cout << " Level " << i << " = " << level[i] << "\n";
836 }
837 }
838 f->SetContour(m_nContours, level);
839 }
840
841 // Set the resolution.
842 f->SetNpx(m_nSamples2dX);
843 f->SetNpy(m_nSamples2dY);
844
845 // Set the labels.
846 f->GetXaxis()->SetTitle(m_xLabel);
847 f->GetYaxis()->SetTitle(m_yLabel);
848 f->SetTitle(title.c_str());
849
850 return true;
851}
852
853bool ViewField::SetupProfile(const double x0, const double y0, const double z0,
854 const double x1, const double y1, const double z1,
855 const std::string& option, TF1*& f,
856 const bool wfield) {
857 if (!m_sensor && !m_component) {
858 std::cerr << m_className << "::SetupProfile:\n"
859 << " Neither sensor nor component are defined.\n";
860 return false;
861 }
862
863 // Check the distance between the two points.
864 const double dx = x1 - x0;
865 const double dy = y1 - y0;
866 const double dz = z1 - z0;
867 if (dx * dx + dy * dy + dz * dz <= 0.) {
868 std::cerr << m_className << "::SetupProfile:\n"
869 << " Start and end points coincide.\n";
870 return false;
871 }
872
873 if (!f) {
874 const std::string fname = FindUnusedFunctionName("fProfile");
875 f = new TF1(fname.c_str(), this, &ViewField::EvaluateProfile, 0., 1., 7,
876 "ViewField", "EvaluateProfile");
877 }
878 f->SetParameter(0, x0);
879 f->SetParameter(1, y0);
880 f->SetParameter(2, z0);
881 f->SetParameter(3, x1);
882 f->SetParameter(4, y1);
883 f->SetParameter(5, z1);
884
885 // Determine the quantity to be plotted.
886 std::string title;
887 const PlotType plotType = GetPlotType(option, title);
888 const int parPlotType = 10 * int(plotType) + 1;
889
890 double fmin = m_vmin;
891 double fmax = m_vmax;
892 if (wfield) {
893 f->SetParameter(6, -parPlotType);
894 title = "weighting " + title;
895 if (plotType == Potential) {
896 fmin = 0.;
897 fmax = 1.;
898 } else {
899 if (m_useAutoRange) {
900 SampleRange(f, fmin, fmax);
901 } else {
902 fmin = m_wmin;
903 fmax = m_wmax;
904 }
905 }
906 } else {
907 f->SetParameter(6, parPlotType);
908 if (plotType == Potential) {
909 if (m_useAutoRange) {
910 if (m_component) {
911 if (!m_component->GetVoltageRange(fmin, fmax)) {
912 SampleRange(f, fmin, fmax);
913 }
914 } else if (m_sensor) {
915 if (!m_sensor->GetVoltageRange(fmin, fmax)) {
916 SampleRange(f, fmin, fmax);
917 }
918 }
919 } else {
920 fmin = m_vmin;
921 fmax = m_vmax;
922 }
923 } else {
924 title = "electric " + title;
925 if (m_useAutoRange) {
926 SampleRange(f, fmin, fmax);
927 } else {
928 fmin = m_emin;
929 fmax = m_emax;
930 }
931 }
932 }
933 f->SetMinimum(fmin);
934 f->SetMaximum(fmax);
935 if (m_debug) {
936 std::cout << m_className << "::SetupProfile:\n"
937 << " Plotting " << title << " along\n ("
938 << f->GetParameter(0) << ", " << f->GetParameter(1) << ", "
939 << f->GetParameter(2) << ") - (" << f->GetParameter(3) << ", "
940 << f->GetParameter(4) << ", " << f->GetParameter(5) << ")\n";
941 }
942
943 title = "Profile plot of the " + title;
944 f->SetTitle(title.c_str());
945 f->GetXaxis()->SetTitle("normalised distance");
946 if (plotType == Potential) {
947 f->GetYaxis()->SetTitle("potential [V]");
948 } else {
949 f->GetYaxis()->SetTitle("field [V/cm]");
950 }
951 f->SetNpx(m_nSamples1d);
952 return true;
953}
954}
Abstract base class for components.
virtual void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status)=0
virtual double WeightingPotential(const double x, const double y, const double z, const std::string &label)
virtual bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Get the bounding box coordinates.
virtual void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
virtual bool GetVoltageRange(double &vmin, double &vmax)=0
Calculate the voltage range [V].
Abstract base class for media.
Definition: Medium.hh:13
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
Definition: Sensor.cc:355
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:138
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:75
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:154
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
Definition: Sensor.cc:232
Base class for visualization classes.
Definition: ViewBase.hh:10
std::string m_className
Definition: ViewBase.hh:28
std::string FindUnusedFunctionName(const std::string &s) const
Definition: ViewBase.cc:30
bool m_hasExternalCanvas
Definition: ViewBase.hh:35
TCanvas * m_canvas
Definition: ViewBase.hh:34
~ViewField()
Destructor.
Definition: ViewField.cc:51
ViewField()
Constructor.
Definition: ViewField.cc:47
void SetPlane(const double fx, const double fy, const double fz, const double x0, const double y0, const double z0)
Definition: ViewField.cc:612
void SetElectricFieldRange(const double emin, const double emax)
Set the plot limits for the electric field.
Definition: ViewField.cc:100
double Evaluate2D(double *pos, double *par)
Definition: ViewField.cc:236
void PlotWeightingField(const std::string &label, const std::string &option, const std::string &drawopt)
Definition: ViewField.cc:180
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")
Definition: ViewField.cc:201
friend class TF2
Definition: ViewField.hh:134
void SetNumberOfSamples2d(const unsigned int nx, const unsigned int ny)
Set the number of points used for drawing 2D functions.
Definition: ViewField.cc:134
void Rotate(const double angle)
Rotate the viewing plane (angle in radian).
Definition: ViewField.cc:653
friend class TF1
Definition: ViewField.hh:133
void SetArea()
Set the viewing area based on the bounding box of the sensor/component.
Definition: ViewField.hh:39
void SetComponent(ComponentBase *c)
Set the component from which to retrieve the field.
Definition: ViewField.cc:68
void SetNumberOfSamples1d(const unsigned int n)
Set the number of points used for drawing 1D functions.
Definition: ViewField.cc:121
void SetVoltageRange(const double vmin, const double vmax)
Set the plot limits for the potential.
Definition: ViewField.cc:94
void PlotContour(const std::string &option="v")
Definition: ViewField.cc:155
void Plot(const std::string &option="v", const std::string &drawopt="arr")
Definition: ViewField.cc:163
void SetNumberOfContours(const unsigned int n)
Set the number of contour levels (at most 50).
Definition: ViewField.cc:112
void SetWeightingFieldRange(const double wmin, const double wmax)
Set the plot limits for the weighting field.
Definition: ViewField.cc:106
void SetSensor(Sensor *s)
Set the sensor from which to retrieve the field.
Definition: ViewField.cc:58
void SetDefaultProjection()
Set the default viewing plane ( - at ).
Definition: ViewField.cc:214
double EvaluateProfile(double *pos, double *par)
Definition: ViewField.cc:308
void PlotContourWeightingField(const std::string &label, const std::string &option)
Definition: ViewField.cc:191
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")
Definition: ViewField.cc:171
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