Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ViewMedium.cc
Go to the documentation of this file.
1#include <cmath>
2#include <iostream>
3#include <fstream>
4#include <string>
5#include <algorithm>
6#include <limits>
7
8#include <TH1F.h>
9#include <TAxis.h>
10#include <TGraph.h>
11#include <TLatex.h>
12
14#include "Garfield/Medium.hh"
15#include "Garfield/Plotting.hh"
17
18namespace {
19
20int FindIndex(const std::vector<double>& fields, const double field,
21 const double eps) {
22
23 if (fields.empty()) return -1;
24 const int n = fields.size();
25 for (int i = 0; i < n; ++i) {
26 const double sum = fabs(fields[i]) + fabs(field);
27 const double tol = std::max(eps * sum, Garfield::Small);
28 if (fabs(fields[i] - field) < tol) return i;
29 }
30 return -1;
31}
32
33bool NonZero(const std::vector<double>& v) {
34
35 constexpr double tol = 1.e-10;
36 return std::any_of(v.cbegin(), v.cend(), [](double x){ return fabs(x) > tol; });
37}
38
39}
40
41namespace Garfield {
42
43ViewMedium::ViewMedium() : ViewBase("ViewMedium") {}
44
46 if (!m) {
47 std::cerr << m_className << "::SetMedium: Null pointer.\n";
48 return;
49 }
50
51 m_medium = m;
52}
53
54void ViewMedium::SetRangeE(const double emin, const double emax,
55 const bool logscale) {
56 if (emin >= emax || emin < 0.) {
57 std::cerr << m_className << "::SetRangeE: Incorrect range.\n";
58 return;
59 }
60
61 m_eMin = emin;
62 m_eMax = emax;
63 m_logE = logscale;
64}
65
66void ViewMedium::SetRangeB(const double bmin, const double bmax, const bool logscale) {
67 if (bmin >= bmax || bmin < 0.) {
68 std::cerr << m_className << "::SetRangeB: Incorrect range.\n";
69 return;
70 }
71
72 m_bMin = bmin;
73 m_bMax = bmax;
74 m_logB = logscale;
75}
76
77void ViewMedium::SetRangeA(const double amin, const double amax, const bool logscale) {
78 if (amin >= amax || amin < 0.) {
79 std::cerr << m_className << "::SetRangeA: Incorrect range.\n";
80 return;
81 }
82
83 m_aMin = amin;
84 m_aMax = amax;
85 m_logA = logscale;
86}
87
88void ViewMedium::SetRangeY(const double ymin, const double ymax,
89 const bool logscale) {
90 if (ymin >= ymax || ymin < 0.) {
91 std::cerr << m_className << "::SetRangeY: Incorrect range.\n";
92 return;
93 }
94
95 m_yMin = ymin;
96 m_yMax = ymax;
97 m_logY = logscale;
98}
99
100void ViewMedium::Draw() {
101
102 if (m_yPlot.empty()) return;
103 auto canvas = GetCanvas();
104 canvas->cd();
105
106 // Find the x and y ranges.
107 const double xmin = m_xPlot.front();
108 const double xmax = m_xPlot.back();
109 double ymin = m_yMin;
110 double ymax = m_yMax;
111 if (m_autoRangeY) {
112 ymin = std::numeric_limits<double>::max();
113 ymax = -std::numeric_limits<double>::max();
114 for (const auto& plot : m_yPlot) {
115 ymin = std::min(ymin, *std::min_element(plot.cbegin(), plot.cend()));
116 ymax = std::max(ymax, *std::max_element(plot.cbegin(), plot.cend()));
117 }
118 const double dy = 0.05 * fabs(ymax - ymin);
119 ymin = std::max(0., ymin - dy);
120 ymax += dy;
121 }
122 auto frame = canvas->DrawFrame(xmin, ymin, xmax, ymax);
123 // Set the axis labels.
124 if (m_xaxis == Axis::E) {
125 frame->GetXaxis()->SetTitle("electric field [V/cm]");
126 } else if (m_xaxis == Axis::B) {
127 frame->GetXaxis()->SetTitle("magnetic field [T]");
128 } else if (m_xaxis == Axis::Angle) {
129 frame->GetXaxis()->SetTitle("angle between #bf{E} and #bf{B} [rad]");
130 } else {
131 std::cerr << m_className << "::Draw: Invalid x axis.\n";
132 return;
133 }
134 auto yaxis = frame->GetYaxis();
135 switch (m_par[0]) {
136 case Parameter::VelocityE:
137 case Parameter::VelocityB:
138 case Parameter::VelocityExB:
139 yaxis->SetTitle("drift velocity [cm/ns]");
140 canvas->SetTitle("Drift velocity");
141 break;
142 case Parameter::LongitudinalDiffusion:
143 case Parameter::TransverseDiffusion:
144 yaxis->SetTitle("diffusion coefficient [#kern[-0.1]{#sqrt{cm}}#kern[0.1]{]}");
145 canvas->SetTitle("Diffusion");
146 break;
147 case Parameter::Townsend:
148 case Parameter::Attachment:
149 if (std::all_of(m_par.cbegin(), m_par.cend(), [](Parameter p) {
150 return p == Parameter::Townsend; })) {
151 yaxis->SetTitle("#it{#alpha} [1/cm]");
152 canvas->SetTitle("Multiplication");
153 } else if (std::all_of(m_par.cbegin(), m_par.cend(), [](Parameter p) {
154 return p == Parameter::Attachment; })) {
155 yaxis->SetTitle("#it{#eta} [1/cm]");
156 canvas->SetTitle("Attachment");
157 } else {
158 yaxis->SetTitle("#it{#alpha}, #it{#eta} [1/cm]");
159 canvas->SetTitle("Multiplication and attachment");
160 }
161 break;
162 case Parameter::LorentzAngle:
163 yaxis->SetTitle("Angle between #bf{v} and #bf{E} [rad]");
164 canvas->SetTitle("Lorentz angle");
165 break;
166 default:
167 break;
168 }
169 yaxis->SetTitleOffset(0);
170 gPad->SetLeftMargin(0.15);
171 canvas->Update();
172
173 const size_t nPlots = m_yPlot.size();
174 // Set colours.
175 std::vector<short> cols(nPlots, 0);
176 if (m_colours.empty()) {
177 auto it = std::find(m_q.cbegin(), m_q.cend(), Charge::Electron);
178 if (it != m_q.cend()) {
179 cols[std::distance(m_q.cbegin(), it)] = kOrange - 3;
180 }
181 it = std::find(std::next(it), m_q.cend(), Charge::Electron);
182 if (it != m_q.cend()) {
183 cols[std::distance(m_q.cbegin(), it)] = kGreen + 3;
184 }
185 it = std::find(m_q.cbegin(), m_q.cend(), Charge::Hole);
186 if (it != m_q.cend()) {
187 cols[std::distance(m_q.cbegin(), it)] = kRed + 1;
188 }
189 it = std::find(m_q.cbegin(), m_q.cend(), Charge::Ion);
190 if (it != m_q.cend()) {
191 cols[std::distance(m_q.cbegin(), it)] = kRed + 1;
192 }
193 } else {
194 for (size_t i = 0; i < nPlots; ++i) {
195 cols[i] = m_colours[i % m_colours.size()];
196 }
197 }
198 // Set legend.
199 std::vector<std::string> labels = m_labels;
200 labels.resize(nPlots, "");
201 if (nPlots > 1 && m_labels.empty()) {
202 bool allEqual = std::equal(m_q.begin() + 1, m_q.end(), m_q.begin());
203 if (!allEqual) {
204 for (size_t i = 0; i < nPlots; ++i) {
205 if (m_q[i] == Charge::Electron) {
206 labels[i] = "electrons";
207 } else if (m_q[i] == Charge::Hole) {
208 labels[i] = "holes";
209 } else {
210 labels[i] = "ions";
211 }
212 }
213 }
214 allEqual = std::equal(m_par.begin() + 1, m_par.end(), m_par.begin());
215 if (!allEqual) {
216 for (size_t i = 0; i < nPlots; ++i) {
217 if (!labels[i].empty()) labels[i] += ", ";
218 switch (m_par[i]) {
219 case Parameter::VelocityE:
220 labels[i] += "#it{v}_{#it{E}}";
221 break;
222 case Parameter::VelocityB:
223 labels[i] += "#it{v}_{#it{B}#kern[0.1]{t}}";
224 break;
225 case Parameter::VelocityExB:
226 labels[i] += "#it{v}_{#it{E}#kern[0.05]{#times}#it{B}}";
227 break;
228 case Parameter::LongitudinalDiffusion:
229 labels[i] += "#it{D}_{L}";
230 break;
231 case Parameter::TransverseDiffusion:
232 labels[i] += "#it{D}_{T}";
233 break;
234 case Parameter::Townsend:
235 labels[i] += "#alpha";
236 break;
237 case Parameter::Attachment:
238 labels[i] += "#eta";
239 break;
240 case Parameter::LorentzAngle:
241 labels[i] += "Lorentz angle";
242 }
243 }
244 }
245 }
246 TGraph graph;
247 const double colrange = gStyle->GetNumberOfColors() / double(nPlots);
248 for (size_t i = 0; i < nPlots; ++i) {
249 int col = cols[i] > 0 ? cols[i] : gStyle->GetColorPalette(i * colrange);
250 graph.SetLineColor(col);
251 graph.SetMarkerColor(col);
252 graph.DrawGraph(m_xPlot.size(),
253 m_xPlot.data(), m_yPlot[i].data(), "L");
254 graph.SetMarkerStyle(20 + i);
255 if (!m_xGraph[i].empty()) {
256 graph.DrawGraph(m_xGraph[i].size(),
257 m_xGraph[i].data(), m_yGraph[i].data(), "P");
258
259 }
260 }
261 if (m_logX && xmin > 0.) {
262 canvas->SetLogx(1);
263 } else {
264 canvas->SetLogx(0);
265 }
266 if (m_logY && ymin > 0. && !m_autoRangeY) {
267 canvas->SetLogy(1);
268 } else {
269 canvas->SetLogy(0);
270 }
271 gPad->Update();
272
273 TLatex latex;
274 double xSize = 0., ySize = 0.;
275 size_t nLabels = 0;
276 for (const auto& label : labels) {
277 latex.SetText(0, 0, label.c_str());
278 xSize = std::max(xSize, latex.GetXsize());
279 ySize = std::max(ySize, latex.GetYsize());
280 if (!label.empty()) ++nLabels;
281 }
282 // Convert to NDC.
283 const double xSizeNDC = xSize / (gPad->GetX2() - gPad->GetX1());
284 const double ySizeNDC = ySize / (gPad->GetY2() - gPad->GetY1());
285
286 double xLabel = 0., yLabel = 1.;
287 constexpr bool autoPlace = false;
288 if (!autoPlace ||
289 !gPad->PlaceBox(&latex, xSizeNDC, nLabels * ySizeNDC, xLabel, yLabel)) {
290 // Auto-placement failed.
291 const double lm = gPad->GetLeftMargin();
292 const double rm = 1. - gPad->GetRightMargin();
293 const double tm = 1. - gPad->GetTopMargin();
294 const double bm = gPad->GetBottomMargin();
295 if (m_par[0] == Parameter::LongitudinalDiffusion ||
296 m_par[0] == Parameter::TransverseDiffusion) {
297 xLabel = rm - 0.1 * (rm - lm) - xSizeNDC;
298 } else {
299 xLabel = lm + 0.1 * (rm - lm);
300 }
301 yLabel = tm - 0.1 * (tm - bm);
302 }
303
304 for (size_t i = 0; i < nPlots; ++i) {
305 int col = cols[i] > 0 ? cols[i] : gStyle->GetColorPalette(i * colrange);
306 if (labels[i].empty()) continue;
307 latex.SetTextColor(col);
308 latex.DrawLatexNDC(xLabel, yLabel, labels[i].c_str());
309 yLabel -= 1.5 * ySizeNDC;
310 }
311
312 gPad->Update();
313 if (!m_outfile.empty()) Export();
314}
315
316void ViewMedium::Export() {
317
318 if (m_yPlot.empty()) return;
319 const size_t nPlots = m_yPlot.size();
320 std::vector<std::string> ylabel = m_labels;
321 ylabel.resize(nPlots, "");
322 for (size_t i = 0; i < nPlots; ++i) {
323 if (!ylabel[i].empty()) continue;
324 switch (m_q[i]) {
325 case Charge::Electron:
326 ylabel[i] = "electron ";
327 break;
328 case Charge::Hole:
329 ylabel[i] = "hole ";
330 break;
331 case Charge::Ion:
332 ylabel[i] = "ion ";
333 break;
334 default:
335 break;
336 }
337 switch (m_par[i]) {
338 case Parameter::VelocityE:
339 ylabel[i] += "drift velocity along E [cm/ns]";
340 break;
341 case Parameter::VelocityB:
342 ylabel[i] += "drift velocity along Bt [cm/ns]";
343 break;
344 case Parameter::VelocityExB:
345 ylabel[i] += "drift velocity along ExB [cm/ns]";
346 break;
347 case Parameter::LongitudinalDiffusion:
348 ylabel[i] += "longitudinal diffusion [cm1/2]";
349 break;
350 case Parameter::TransverseDiffusion:
351 ylabel[i] += "transverse diffusion [cm1/2]";
352 break;
353 case Parameter::Townsend:
354 ylabel[i] += "Townsend coefficient [1/cm]";
355 break;
356 case Parameter::Attachment:
357 ylabel[i] += "attachment coefficient [1/cm]";
358 break;
359 case Parameter::LorentzAngle:
360 ylabel[i] += "Lorentz angle [rad]";
361 }
362 }
363
364 const std::string sep = " ";
365 std::ofstream outfile(m_outfile, std::ios_base::app);
366 if (!outfile) return;
367 outfile << "# x-axis: ";
368 if (m_xaxis == Axis::E) {
369 outfile << "E [V/cm]";
370 } else if (m_xaxis == Axis::B) {
371 outfile << "B [T]";
372 } else if (m_xaxis == Axis::Angle) {
373 outfile << "Theta [rad]";
374 }
375 outfile << "\n";
376 outfile << "# " << nPlots << " plots:\n";
377 for (size_t i = 0; i < nPlots; ++i) {
378 outfile << "# " << ylabel[i] << "\n";
379 }
380 const size_t nX = m_xPlot.size();
381 for (size_t i = 0; i < nX; ++i) {
382 outfile << m_xPlot[i];
383 for (size_t j = 0; j < nPlots; ++j) {
384 outfile << sep << m_yPlot[j][i];
385 }
386 outfile << "\n";
387 }
388 for (size_t i = 0; i < nPlots; ++i) {
389 if (m_xGraph[i].empty()) continue;
390 outfile << "# " << ylabel[i] << "\n";
391 for (size_t j = 0; j < m_xGraph[i].size(); ++j) {
392 outfile << m_xGraph[i][j] << sep << m_yGraph[i][j] << "\n";
393 }
394 }
395 outfile.close();
396}
397
398void ViewMedium::ResetY() {
399 m_yPlot.clear();
400 m_par.clear();
401 m_q.clear();
402 m_xGraph.clear();
403 m_yGraph.clear();
404}
405
406void ViewMedium::ResetX(const Axis xaxis) {
407
408 double xmin = 0., xmax = 0.;
409 bool logx = false;
410 if (xaxis == Axis::E) {
411 xmin = m_eMin;
412 xmax = m_eMax;
413 logx = m_logE;
414 } else if (xaxis == Axis::B) {
415 xmin = m_bMin;
416 xmax = m_bMax;
417 logx = m_logB;
418 } else if (xaxis == Axis::Angle) {
419 xmin = m_aMin;
420 xmax = m_aMax;
421 logx = m_logA;
422 } else {
423 m_xaxis = Axis::None;
424 return;
425 }
426
427 if (m_autoRangeX) {
428 // Get the field grid.
429 std::vector<double> efields;
430 std::vector<double> bfields;
431 std::vector<double> bangles;
432 m_medium->GetFieldGrid(efields, bfields, bangles);
433 if (xaxis == Axis::E && !efields.empty()) {
434 if (efields.front() > 0. && efields.back() > 10. * efields.front()) {
435 logx = true;
436 xmin = efields.front() / 1.5;
437 xmax = efields.back() * 1.5;
438 } else {
439 logx = false;
440 const double dx = 0.05 * fabs(efields.back() - efields.front());
441 xmin = std::max(0., efields.front() - dx);
442 xmax = efields.back() + dx;
443 }
444 } else if (xaxis == Axis::B && !bfields.empty()) {
445 logx = false;
446 const double dx = 0.05 * fabs(bfields.back() - bfields.front());
447 xmin = std::max(0., bfields.front() - dx);
448 xmax = bfields.back() + dx;
449 } else if (xaxis == Axis::Angle && !bangles.empty()) {
450 logx = false;
451 const double dx = 0.05 * fabs(bangles.back() - bangles.front());
452 xmin = std::max(0., bangles.front() - dx);
453 xmax = std::min(TwoPi, bangles.back() + dx);
454 }
455 }
456
457 constexpr size_t nX = 1000;
458 m_xPlot.assign(nX, 0.);
459 if (logx) {
460 m_xPlot[0] = xmin;
461 const double r = pow(xmax / xmin, 1. / (nX - 1));
462 for (size_t i = 1; i < nX; ++i) {
463 m_xPlot[i] = m_xPlot[i - 1] * r;
464 }
465 } else {
466 const double dx = (xmax - xmin) / (nX - 1);
467 for (size_t i = 0; i < nX; ++i) {
468 m_xPlot[i] = xmin + i * dx;
469 }
470 }
471 m_logX = logx;
472 m_xaxis = xaxis;
473}
474
475void ViewMedium::PlotDiffusion(const Axis xaxis, const Charge charge,
476 const bool same) {
477
478 if (!m_medium) {
479 std::cerr << m_className << "::PlotDiffusion: Medium is not defined.\n";
480 return;
481 }
482 if (xaxis != m_xaxis) {
483 ResetX(xaxis);
484 ResetY();
485 } else if (!same) {
486 ResetY();
487 } else if (!m_par.empty()) {
488 if (m_par[0] != Parameter::TransverseDiffusion &&
489 m_par[0] != Parameter::LongitudinalDiffusion) {
490 ResetY();
491 }
492 }
493 const size_t nX = m_xPlot.size();
494 std::array<std::vector<double>, 2> ypl;
495 for (size_t i = 0; i < 2; ++i) ypl[i].assign(nX, 0.);
496
497 double ex = m_efield;
498 double ctheta = cos(m_angle);
499 double stheta = sin(m_angle);
500 double bx = m_bfield * ctheta;
501 double by = m_bfield * stheta;
502 for (size_t i = 0; i < nX; ++i) {
503 if (xaxis == Axis::E) {
504 ex = m_xPlot[i];
505 } else if (xaxis == Axis::B) {
506 bx = m_xPlot[i] * ctheta;
507 by = m_xPlot[i] * stheta;
508 } else {
509 bx = m_bfield * cos(m_xPlot[i]);
510 by = m_bfield * sin(m_xPlot[i]);
511 }
512 double dl = 0., dt = 0.;
513 if (charge == Charge::Electron) {
514 if (!m_medium->ElectronDiffusion(ex, 0, 0, bx, by, 0, dl, dt)) continue;
515 } else if (charge == Charge::Hole) {
516 if (!m_medium->HoleDiffusion(ex, 0, 0, bx, by, 0, dl, dt)) continue;
517 } else {
518 if (!m_medium->IonDiffusion(ex, 0, 0, bx, by, 0, dl, dt)) continue;
519 }
520 ypl[0][i] = dl;
521 ypl[1][i] = dt;
522 }
523
524 std::array<std::vector<double>, 2> xgr;
525 std::array<std::vector<double>, 2> ygr;
526
527 std::array<std::vector<double>, 3> grid;
528 int ie = 0, ib = 0, ia = 0;
529 if (GetGrid(grid, ie, ib, ia, xaxis)) {
530 const auto nPoints = xaxis == Axis::E ? grid[0].size() :
531 xaxis == Axis::B ? grid[1].size() : grid[2].size();
532 for (size_t j = 0; j < nPoints; ++j) {
533 double x = 0., y = 0.;
534 if (xaxis == Axis::E) {
535 ie = j;
536 x = m_medium->UnScaleElectricField(grid[0][j]);
537 } else if (xaxis == Axis::B) {
538 ib = j;
539 x = grid[1][j];
540 } else if (xaxis == Axis::Angle) {
541 ia = j;
542 x = grid[2][j];
543 }
544 if (charge == Charge::Electron) {
545 if (m_medium->GetElectronTransverseDiffusion(ie, ib, ia, y)) {
546 xgr[1].push_back(x);
547 ygr[1].push_back(m_medium->ScaleDiffusion(y));
548 }
549 if (m_medium->GetElectronLongitudinalDiffusion(ie, ib, ia, y)) {
550 xgr[0].push_back(x);
551 ygr[0].push_back(m_medium->ScaleDiffusion(y));
552 }
553 } else if (charge == Charge::Hole) {
554 if (m_medium->GetHoleTransverseDiffusion(ie, ib, ia, y)) {
555 xgr[1].push_back(x);
556 ygr[1].push_back(m_medium->ScaleDiffusion(y));
557 }
558 if (m_medium->GetHoleLongitudinalDiffusion(ie, ib, ia, y)) {
559 xgr[0].push_back(x);
560 ygr[0].push_back(m_medium->ScaleDiffusion(y));
561 }
562 } else {
563 if (m_medium->GetIonTransverseDiffusion(ie, ib, ia, y)) {
564 xgr[1].push_back(x);
565 ygr[1].push_back(m_medium->ScaleDiffusion(y));
566 }
567 if (m_medium->GetIonLongitudinalDiffusion(ie, ib, ia, y)) {
568 xgr[0].push_back(x);
569 ygr[0].push_back(m_medium->ScaleDiffusion(y));
570 }
571 }
572 }
573 }
574
575 const std::array<Parameter, 2> pars = {Parameter::LongitudinalDiffusion,
576 Parameter::TransverseDiffusion};
577 for (size_t i = 0; i < 2; ++i) {
578 if (!NonZero(ypl[i])) continue;
579 m_yPlot.push_back(std::move(ypl[i]));
580 m_par.push_back(pars[i]);
581 m_q.push_back(charge);
582 m_xGraph.push_back(std::move(xgr[i]));
583 m_yGraph.push_back(std::move(ygr[i]));
584 }
585 Draw();
586}
587
588void ViewMedium::PlotVelocity(const std::string& opt, const char xaxis) {
589
590 const auto ax = GetAxis(xaxis);
591 std::vector<Charge> carriers;
592 if (opt.find('e') != std::string::npos) carriers.push_back(Charge::Electron);
593 if (opt.find('h') != std::string::npos) carriers.push_back(Charge::Hole);
594 if (opt.find('i') != std::string::npos) carriers.push_back(Charge::Ion);
595 const size_t nC = carriers.size();
596 for (size_t i = 0; i < nC; ++i) {
597 const bool same = i > 0 ? true : false;
598 PlotVelocity(ax, carriers[i], same);
599 }
600}
601
602void ViewMedium::PlotDiffusion(const std::string& opt, const char xaxis) {
603
604 const auto ax = GetAxis(xaxis);
605 std::vector<Charge> carriers;
606 if (opt.find('e') != std::string::npos) carriers.push_back(Charge::Electron);
607 if (opt.find('h') != std::string::npos) carriers.push_back(Charge::Hole);
608 if (opt.find('i') != std::string::npos) carriers.push_back(Charge::Ion);
609 const size_t nC = carriers.size();
610 for (size_t i = 0; i < nC; ++i) {
611 const bool same = i > 0 ? true : false;
612 PlotDiffusion(ax, carriers[i], same);
613 }
614}
615
616void ViewMedium::PlotTownsend(const std::string& opt, const char xaxis) {
617
618 const auto ax = GetAxis(xaxis);
619 std::vector<Charge> carriers;
620 if (opt.find('e') != std::string::npos) carriers.push_back(Charge::Electron);
621 if (opt.find('h') != std::string::npos) carriers.push_back(Charge::Hole);
622 if (opt.find('i') != std::string::npos) carriers.push_back(Charge::Ion);
623 const size_t nC = carriers.size();
624 for (size_t i = 0; i < nC; ++i) {
625 const bool same = i > 0 ? true : false;
626 Plot(ax, carriers[i], Parameter::Townsend, same);
627 }
628}
629
630void ViewMedium::PlotAttachment(const std::string& opt, const char xaxis) {
631
632 const auto ax = GetAxis(xaxis);
633 std::vector<Charge> carriers;
634 if (opt.find('e') != std::string::npos) carriers.push_back(Charge::Electron);
635 if (opt.find('h') != std::string::npos) carriers.push_back(Charge::Hole);
636 if (opt.find('i') != std::string::npos) carriers.push_back(Charge::Ion);
637 const size_t nC = carriers.size();
638 for (size_t i = 0; i < nC; ++i) {
639 const bool same = i > 0 ? true : false;
640 Plot(ax, carriers[i], Parameter::Attachment, same);
641 }
642}
643
644void ViewMedium::PlotAlphaEta(const std::string& opt, const char xaxis) {
645
646 const auto ax = GetAxis(xaxis);
647 std::vector<Charge> carriers;
648 if (opt.find('e') != std::string::npos) carriers.push_back(Charge::Electron);
649 if (opt.find('h') != std::string::npos) carriers.push_back(Charge::Hole);
650 if (opt.find('i') != std::string::npos) carriers.push_back(Charge::Ion);
651 bool same = false;
652 for (const auto c : carriers) {
653 Plot(ax, c, Parameter::Townsend, same);
654 Plot(ax, c, Parameter::Attachment, true);
655 same = true;
656 }
657}
658
659void ViewMedium::PlotVelocity(const Axis xaxis, const Charge charge,
660 const bool same) {
661
662 if (!m_medium) {
663 std::cerr << m_className << "::PlotVelocity: Medium is not defined.\n";
664 return;
665 }
666 if (xaxis != m_xaxis) {
667 ResetX(xaxis);
668 ResetY();
669 } else if (!same) {
670 ResetY();
671 } else if (!m_par.empty()) {
672 if (m_par[0] != Parameter::VelocityE &&
673 m_par[0] != Parameter::VelocityB &&
674 m_par[0] != Parameter::VelocityExB) {
675 ResetY();
676 }
677 }
678 const size_t nX = m_xPlot.size();
679 std::array<std::vector<double>, 3> ypl;
680 for (size_t i = 0; i < 3; ++i) ypl[i].assign(nX, 0.);
681
682 double e0 = m_efield;
683 double b0 = m_bfield;
684 double ctheta = cos(m_angle);
685 double stheta = sin(m_angle);
686
687 for (size_t i = 0; i < nX; ++i) {
688 if (xaxis == Axis::E) {
689 e0 = m_xPlot[i];
690 } else if (xaxis == Axis::B) {
691 b0 = m_xPlot[i];
692 } else {
693 ctheta = cos(m_xPlot[i]);
694 stheta = sin(m_xPlot[i]);
695 }
696 double vx = 0., vy = 0., vz = 0.;
697 if (charge == Charge::Electron) {
698 if (m_medium->ElectronVelocity(e0, 0, 0, b0 * ctheta, b0 * stheta, 0,
699 vx, vy, vz)) {
700 ypl[0][i] = fabs(vx);
701 ypl[1][i] = fabs(vy);
702 ypl[2][i] = fabs(vz);
703 }
704 } else if (charge == Charge::Hole) {
705 if (m_medium->HoleVelocity(e0, 0, 0, b0 * ctheta, b0 * stheta, 0,
706 vx, vy, vz)) {
707 ypl[0][i] = fabs(vx);
708 ypl[1][i] = fabs(vy);
709 ypl[2][i] = fabs(vz);
710 }
711 } else {
712 if (m_medium->IonVelocity(e0, 0, 0, b0 * ctheta, b0 * stheta, 0,
713 vx, vy, vz)) {
714 ypl[0][i] = fabs(vx);
715 }
716 }
717 }
718 std::array<std::vector<double>, 3> xgr;
719 std::array<std::vector<double>, 3> ygr;
720
721 std::array<std::vector<double>, 3> grid;
722 int ie = 0, ib = 0, ia = 0;
723 if (GetGrid(grid, ie, ib, ia, xaxis)) {
724 const auto nPoints = xaxis == Axis::E ? grid[0].size() :
725 xaxis == Axis::B ? grid[1].size() : grid[2].size();
726 for (size_t j = 0; j < nPoints; ++j) {
727 double x = 0., y = 0.;
728 if (xaxis == Axis::E) {
729 ie = j;
730 x = m_medium->UnScaleElectricField(grid[0][j]);
731 } else if (xaxis == Axis::B) {
732 ib = j;
733 x = grid[1][j];
734 } else if (xaxis == Axis::Angle) {
735 ia = j;
736 x = grid[2][j];
737 }
738 if (charge == Charge::Electron) {
739 if (m_medium->GetElectronVelocityE(ie, ib, ia, y)) {
740 xgr[0].push_back(x);
741 ygr[0].push_back(m_medium->ScaleVelocity(y));
742 }
743 if (m_medium->GetElectronVelocityB(ie, ib, ia, y)) {
744 xgr[1].push_back(x);
745 ygr[1].push_back(fabs(m_medium->ScaleVelocity(y)));
746 }
747 if (m_medium->GetElectronVelocityExB(ie, ib, ia, y)) {
748 xgr[2].push_back(x);
749 ygr[2].push_back(fabs(m_medium->ScaleVelocity(y)));
750 }
751 } else if (charge == Charge::Hole) {
752 if (m_medium->GetHoleVelocityE(ie, ib, ia, y)) {
753 xgr[0].push_back(x);
754 ygr[0].push_back(m_medium->ScaleVelocity(y));
755 }
756 if (m_medium->GetHoleVelocityB(ie, ib, ia, y)) {
757 xgr[1].push_back(x);
758 ygr[1].push_back(fabs(m_medium->ScaleVelocity(y)));
759 }
760 if (m_medium->GetHoleVelocityExB(ie, ib, ia, y)) {
761 xgr[2].push_back(x);
762 ygr[2].push_back(fabs(m_medium->ScaleVelocity(y)));
763 }
764 } else {
765 if (m_medium->GetIonMobility(ie, ib, ia, y)) {
766 xgr[0].push_back(x);
767 ygr[0].push_back(y * m_medium->UnScaleElectricField(grid[0][ie]));
768 }
769 }
770 }
771 }
772
773 const std::array<Parameter, 3> pars = {Parameter::VelocityE,
774 Parameter::VelocityB,
775 Parameter::VelocityExB};
776 for (size_t i = 0; i < 3; ++i) {
777 if (!NonZero(ypl[i])) continue;
778 m_yPlot.push_back(std::move(ypl[i]));
779 m_par.push_back(pars[i]);
780 m_q.push_back(charge);
781 m_xGraph.push_back(std::move(xgr[i]));
782 m_yGraph.push_back(std::move(ygr[i]));
783 }
784 Draw();
785}
786
787void ViewMedium::Plot(const Axis xaxis, const Charge charge,
788 const Parameter par, const bool same) {
789
790 // Make sure the medium is set.
791 if (!m_medium) {
792 std::cerr << m_className << "::Plot: Medium is not defined.\n";
793 return;
794 }
795 if (xaxis != m_xaxis) {
796 ResetX(xaxis);
797 ResetY();
798 } else if (!same) {
799 ResetY();
800 } else if (!m_par.empty()) {
801 if (m_par[0] != Parameter::Townsend &&
802 m_par[0] != Parameter::Attachment) {
803 ResetY();
804 }
805 }
806
807 const size_t nX = m_xPlot.size();
808 std::vector<double> ypl(nX, 0.);
809 double ex = m_efield;
810 double ctheta = cos(m_angle);
811 double stheta = sin(m_angle);
812 double bx = m_bfield * ctheta;
813 double by = m_bfield * stheta;
814 for (size_t i = 0; i < nX; ++i) {
815 if (xaxis == Axis::E) {
816 ex = m_xPlot[i];
817 } else if (xaxis == Axis::B) {
818 bx = m_xPlot[i] * ctheta;
819 by = m_xPlot[i] * stheta;
820 } else {
821 bx = m_bfield * cos(m_xPlot[i]);
822 by = m_bfield * sin(m_xPlot[i]);
823 }
824 double y = 0.;
825 if (charge == Charge::Electron) {
826 if (par == Parameter::Townsend) {
827 if (!m_medium->ElectronTownsend(ex, 0, 0, bx, by, 0, y)) continue;
828 } else {
829 if (!m_medium->ElectronAttachment(ex, 0, 0, bx, by, 0, y)) continue;
830 y = std::abs(y);
831 }
832 } else {
833 if (par == Parameter::Townsend) {
834 if (!m_medium->HoleTownsend(ex, 0, 0, bx, by, 0, y)) continue;
835 } else {
836 if (!m_medium->HoleAttachment(ex, 0, 0, bx, by, 0, y)) continue;
837 y = std::abs(y);
838 }
839 }
840 ypl[i] = y;
841 }
842
843 std::vector<double> xgr;
844 std::vector<double> ygr;
845
846 std::array<std::vector<double>, 3> grid;
847 int ie = 0, ib = 0, ia = 0;
848 if (GetGrid(grid, ie, ib, ia, xaxis)) {
849 const auto nPoints = xaxis == Axis::E ? grid[0].size() :
850 xaxis == Axis::B ? grid[1].size() : grid[2].size();
851 for (size_t j = 0; j < nPoints; ++j) {
852 double x = 0., y = 0.;
853 if (xaxis == Axis::E) {
854 ie = j;
855 x = m_medium->UnScaleElectricField(grid[0][j]);
856 } else if (xaxis == Axis::B) {
857 ib = j;
858 x = grid[1][j];
859 } else if (xaxis == Axis::Angle) {
860 ia = j;
861 x = grid[2][j];
862 }
863 if (charge == Charge::Electron) {
864 if (par == Parameter::Townsend) {
865 if (m_medium->GetElectronTownsend(ie, ib, ia, y)) {
866 xgr.push_back(x);
867 ygr.push_back(m_medium->ScaleTownsend(exp(y)));
868 }
869 } else {
870 if (m_medium->GetElectronAttachment(ie, ib, ia, y)) {
871 xgr.push_back(x);
872 ygr.push_back(m_medium->ScaleAttachment(exp(y)));
873 }
874 }
875 } else if (charge == Charge::Hole) {
876 if (par == Parameter::Townsend) {
877 if (m_medium->GetHoleTownsend(ie, ib, ia, y)) {
878 xgr.push_back(x);
879 ygr.push_back(m_medium->ScaleTownsend(exp(y)));
880 }
881 } else {
882 if (m_medium->GetHoleAttachment(ie, ib, ia, y)) {
883 xgr.push_back(x);
884 ygr.push_back(m_medium->ScaleAttachment(exp(y)));
885 }
886 }
887 }
888 }
889 }
890
891 if (!NonZero(ypl)) return;
892 m_yPlot.push_back(std::move(ypl));
893 m_par.push_back(par);
894 m_q.push_back(charge);
895 m_xGraph.push_back(std::move(xgr));
896 m_yGraph.push_back(std::move(ygr));
897 Draw();
898}
899
900void ViewMedium::PlotLorentzAngle(const Axis xaxis, const Charge charge,
901 const bool same) {
902
903 // Make sure the medium is set.
904 if (!m_medium) {
905 std::cerr << m_className << "::PlotLorentzAngle: Medium is not defined.\n";
906 return;
907 }
908 if (xaxis != m_xaxis) {
909 ResetX(xaxis);
910 ResetY();
911 } else if (!same) {
912 ResetY();
913 } else if (!m_par.empty()) {
914 if (m_par[0] != Parameter::LorentzAngle) ResetY();
915 }
916
917 const size_t nX = m_xPlot.size();
918 std::vector<double> ypl(nX, 0.);
919 double ex = m_efield;
920 double ctheta = cos(m_angle);
921 double stheta = sin(m_angle);
922 double bx = m_bfield * ctheta;
923 double by = m_bfield * stheta;
924 for (size_t i = 0; i < nX; ++i) {
925 if (xaxis == Axis::E) {
926 ex = m_xPlot[i];
927 } else if (xaxis == Axis::B) {
928 bx = m_xPlot[i] * ctheta;
929 by = m_xPlot[i] * stheta;
930 } else {
931 bx = m_bfield * cos(m_xPlot[i]);
932 by = m_bfield * sin(m_xPlot[i]);
933 }
934 double y = 0.;
935 if (!m_medium->ElectronLorentzAngle(ex, 0, 0, bx, by, 0, y)) continue;
936 ypl[i] = y;
937 }
938
939 std::vector<double> xgr;
940 std::vector<double> ygr;
941
942 std::array<std::vector<double>, 3> grid;
943 int ie = 0, ib = 0, ia = 0;
944 if (GetGrid(grid, ie, ib, ia, xaxis)) {
945 const auto nPoints = xaxis == Axis::E ? grid[0].size() :
946 xaxis == Axis::B ? grid[1].size() : grid[2].size();
947 for (size_t j = 0; j < nPoints; ++j) {
948 double x = 0., y = 0.;
949 if (xaxis == Axis::E) {
950 ie = j;
951 x = m_medium->UnScaleElectricField(grid[0][j]);
952 } else if (xaxis == Axis::B) {
953 ib = j;
954 x = grid[1][j];
955 } else if (xaxis == Axis::Angle) {
956 ia = j;
957 x = grid[2][j];
958 }
959 if (m_medium->GetElectronLorentzAngle(ie, ib, ia, y)) {
960 xgr.push_back(x);
961 ygr.push_back(y);
962 }
963 }
964 }
965
966 if (!NonZero(ypl)) return;
967 m_yPlot.push_back(std::move(ypl));
968 m_par.push_back(Parameter::LorentzAngle);
969 m_q.push_back(charge);
970 m_xGraph.push_back(std::move(xgr));
971 m_yGraph.push_back(std::move(ygr));
972 Draw();
973}
974
975ViewMedium::Axis ViewMedium::GetAxis(const char xaxis) const {
976
977 if (std::toupper(xaxis) == 'E') {
978 return Axis::E;
979 } else if (std::toupper(xaxis) == 'B') {
980 return Axis::B;
981 } else if (std::toupper(xaxis) == 'A') {
982 return Axis::Angle;
983 }
984 return Axis::None;
985}
986
987bool ViewMedium::GetGrid(std::array<std::vector<double>, 3>& grid,
988 int& ie, int& ib, int& ia,
989 const Axis xaxis) const {
990
991 if (!m_medium) return false;
992 m_medium->GetFieldGrid(grid[0], grid[1], grid[2]);
993 if (grid[0].empty() || grid[1].empty() || grid[2].empty()) return false;
994 constexpr double eps = 1.e-3;
995 ie = FindIndex(grid[0], m_efield, eps);
996 ib = FindIndex(grid[1], m_bfield, eps);
997 ia = FindIndex(grid[2], m_angle, eps);
998 if (xaxis == Axis::E) {
999 if (ib < 0 || ia < 0) return false;
1000 } else if (xaxis == Axis::B) {
1001 if (ie < 0 || ia < 0) return false;
1002 } else if (xaxis == Axis::Angle) {
1003 if (ie < 0 || ib < 0) return false;
1004 } else {
1005 return false;
1006 }
1007 return true;
1008}
1009
1010}
Abstract base class for media.
Definition Medium.hh:16
std::string m_className
Definition ViewBase.hh:82
TPad * GetCanvas()
Retrieve the canvas.
Definition ViewBase.cc:74
ViewBase()=delete
Default constructor.
void SetRangeY(const double ymin, const double ymax, const bool logscale=false)
Set the range of the function (velocity etc.) to be plotted.
Definition ViewMedium.cc:88
void SetRangeB(const double bmin, const double bmax, const bool logscale)
Set the limits of the magnetic field.
Definition ViewMedium.cc:66
void PlotAttachment(const std::string &carriers, const char xaxis)
Plot the attachment coefficient.
void SetMedium(Medium *m)
Set the medium from which to retrieve the transport coefficients.
Definition ViewMedium.cc:45
void PlotAlphaEta(const std::string &carriers, const char xaxis)
Plot Townsend and attachment coefficients.
void PlotTownsend(const std::string &carriers, const char xaxis)
Plot the Townsend coefficient.
void PlotDiffusion(const std::string &carriers, const char xaxis)
Plot the transverse and longitudinal diffusion coefficients.
void SetRangeE(const double emin, const double emax, const bool logscale)
Set the limits of the electric field.
Definition ViewMedium.cc:54
void PlotVelocity(const std::string &carriers, const char xaxis)
void SetRangeA(const double amin, const double amax, const bool logscale)
Set the limits of the angle between electric and magnetic field.
Definition ViewMedium.cc:77
ViewMedium()
Constructor.
Definition ViewMedium.cc:43
DoubleAc cos(const DoubleAc &f)
Definition DoubleAc.cpp:432
DoubleAc pow(const DoubleAc &f, double p)
Definition DoubleAc.cpp:337
DoubleAc exp(const DoubleAc &f)
Definition DoubleAc.cpp:377
DoubleAc fabs(const DoubleAc &f)
Definition DoubleAc.h:615
DoubleAc sin(const DoubleAc &f)
Definition DoubleAc.cpp:384