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