20int FindIndex(
const std::vector<double>& fields,
const double field,
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;
33bool NonZero(
const std::vector<double>& v) {
35 constexpr double tol = 1.e-10;
36 return std::any_of(v.cbegin(), v.cend(), [](
double x){ return fabs(x) > tol; });
47 std::cerr <<
m_className <<
"::SetMedium: Null pointer.\n";
55 const bool logscale) {
56 if (emin >= emax || emin < 0.) {
57 std::cerr <<
m_className <<
"::SetRangeE: Incorrect range.\n";
67 if (bmin >= bmax || bmin < 0.) {
68 std::cerr <<
m_className <<
"::SetRangeB: Incorrect range.\n";
78 if (amin >= amax || amin < 0.) {
79 std::cerr <<
m_className <<
"::SetRangeA: Incorrect range.\n";
89 const bool logscale) {
90 if (ymin >= ymax || ymin < 0.) {
91 std::cerr <<
m_className <<
"::SetRangeY: Incorrect range.\n";
100void ViewMedium::Draw() {
102 if (m_yPlot.empty())
return;
107 const double xmin = m_xPlot.front();
108 const double xmax = m_xPlot.back();
109 double ymin = m_yMin;
110 double ymax = m_yMax;
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()));
118 const double dy = 0.05 * fabs(ymax - ymin);
119 ymin = std::max(0., ymin - dy);
122 auto frame = canvas->DrawFrame(xmin, ymin, xmax, ymax);
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]");
131 std::cerr <<
m_className <<
"::Draw: Invalid x axis.\n";
134 auto yaxis = frame->GetYaxis();
136 case Parameter::VelocityE:
137 case Parameter::VelocityB:
138 case Parameter::VelocityExB:
139 yaxis->SetTitle(
"drift velocity [cm/ns]");
140 canvas->SetTitle(
"Drift velocity");
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");
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");
158 yaxis->SetTitle(
"#it{#alpha}, #it{#eta} [1/cm]");
159 canvas->SetTitle(
"Multiplication and attachment");
162 case Parameter::LorentzAngle:
163 yaxis->SetTitle(
"Angle between #bf{v} and #bf{E} [rad]");
164 canvas->SetTitle(
"Lorentz angle");
169 yaxis->SetTitleOffset(0);
170 gPad->SetLeftMargin(0.15);
173 const size_t nPlots = m_yPlot.size();
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;
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;
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;
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;
194 for (
size_t i = 0; i < nPlots; ++i) {
195 cols[i] = m_colours[i % m_colours.size()];
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());
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) {
214 allEqual = std::equal(m_par.begin() + 1, m_par.end(), m_par.begin());
216 for (
size_t i = 0; i < nPlots; ++i) {
217 if (!labels[i].empty()) labels[i] +=
", ";
219 case Parameter::VelocityE:
220 labels[i] +=
"#it{v}_{#it{E}}";
222 case Parameter::VelocityB:
223 labels[i] +=
"#it{v}_{#it{B}#kern[0.1]{t}}";
225 case Parameter::VelocityExB:
226 labels[i] +=
"#it{v}_{#it{E}#kern[0.05]{#times}#it{B}}";
228 case Parameter::LongitudinalDiffusion:
229 labels[i] +=
"#it{D}_{L}";
231 case Parameter::TransverseDiffusion:
232 labels[i] +=
"#it{D}_{T}";
234 case Parameter::Townsend:
235 labels[i] +=
"#alpha";
237 case Parameter::Attachment:
240 case Parameter::LorentzAngle:
241 labels[i] +=
"Lorentz angle";
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");
261 if (m_logX && xmin > 0.) {
266 if (m_logY && ymin > 0. && !m_autoRangeY) {
274 double xSize = 0., ySize = 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;
283 const double xSizeNDC = xSize / (gPad->GetX2() - gPad->GetX1());
284 const double ySizeNDC = ySize / (gPad->GetY2() - gPad->GetY1());
286 double xLabel = 0., yLabel = 1.;
287 constexpr bool autoPlace =
false;
289 !gPad->PlaceBox(&latex, xSizeNDC, nLabels * ySizeNDC, xLabel, yLabel)) {
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;
299 xLabel = lm + 0.1 * (rm - lm);
301 yLabel = tm - 0.1 * (tm - bm);
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;
313 if (!m_outfile.empty()) Export();
316void ViewMedium::Export() {
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;
325 case Charge::Electron:
326 ylabel[i] =
"electron ";
338 case Parameter::VelocityE:
339 ylabel[i] +=
"drift velocity along E [cm/ns]";
341 case Parameter::VelocityB:
342 ylabel[i] +=
"drift velocity along Bt [cm/ns]";
344 case Parameter::VelocityExB:
345 ylabel[i] +=
"drift velocity along ExB [cm/ns]";
347 case Parameter::LongitudinalDiffusion:
348 ylabel[i] +=
"longitudinal diffusion [cm1/2]";
350 case Parameter::TransverseDiffusion:
351 ylabel[i] +=
"transverse diffusion [cm1/2]";
353 case Parameter::Townsend:
354 ylabel[i] +=
"Townsend coefficient [1/cm]";
356 case Parameter::Attachment:
357 ylabel[i] +=
"attachment coefficient [1/cm]";
359 case Parameter::LorentzAngle:
360 ylabel[i] +=
"Lorentz angle [rad]";
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) {
372 }
else if (m_xaxis == Axis::Angle) {
373 outfile <<
"Theta [rad]";
376 outfile <<
"# " << nPlots <<
" plots:\n";
377 for (
size_t i = 0; i < nPlots; ++i) {
378 outfile <<
"# " << ylabel[i] <<
"\n";
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];
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";
398void ViewMedium::ResetY() {
406void ViewMedium::ResetX(
const Axis xaxis) {
408 double xmin = 0., xmax = 0.;
410 if (xaxis == Axis::E) {
414 }
else if (xaxis == Axis::B) {
418 }
else if (xaxis == Axis::Angle) {
423 m_xaxis = Axis::None;
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()) {
436 xmin = efields.front() / 1.5;
437 xmax = efields.back() * 1.5;
440 const double dx = 0.05 *
fabs(efields.back() - efields.front());
441 xmin = std::max(0., efields.front() - dx);
442 xmax = efields.back() + dx;
444 }
else if (xaxis == Axis::B && !bfields.empty()) {
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()) {
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);
457 constexpr size_t nX = 1000;
458 m_xPlot.assign(nX, 0.);
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;
466 const double dx = (xmax - xmin) / (nX - 1);
467 for (
size_t i = 0; i < nX; ++i) {
468 m_xPlot[i] = xmin + i * dx;
479 std::cerr <<
m_className <<
"::PlotDiffusion: Medium is not defined.\n";
482 if (xaxis != m_xaxis) {
487 }
else if (!m_par.empty()) {
488 if (m_par[0] != Parameter::TransverseDiffusion &&
489 m_par[0] != Parameter::LongitudinalDiffusion) {
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.);
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) {
505 }
else if (xaxis == Axis::B) {
506 bx = m_xPlot[i] * ctheta;
507 by = m_xPlot[i] * stheta;
509 bx = m_bfield *
cos(m_xPlot[i]);
510 by = m_bfield *
sin(m_xPlot[i]);
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;
518 if (!m_medium->IonDiffusion(ex, 0, 0, bx, by, 0, dl, dt))
continue;
524 std::array<std::vector<double>, 2> xgr;
525 std::array<std::vector<double>, 2> ygr;
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) {
536 x = m_medium->UnScaleElectricField(grid[0][j]);
537 }
else if (xaxis == Axis::B) {
540 }
else if (xaxis == Axis::Angle) {
544 if (charge == Charge::Electron) {
545 if (m_medium->GetElectronTransverseDiffusion(ie, ib, ia, y)) {
547 ygr[1].push_back(m_medium->ScaleDiffusion(y));
549 if (m_medium->GetElectronLongitudinalDiffusion(ie, ib, ia, y)) {
551 ygr[0].push_back(m_medium->ScaleDiffusion(y));
553 }
else if (charge == Charge::Hole) {
554 if (m_medium->GetHoleTransverseDiffusion(ie, ib, ia, y)) {
556 ygr[1].push_back(m_medium->ScaleDiffusion(y));
558 if (m_medium->GetHoleLongitudinalDiffusion(ie, ib, ia, y)) {
560 ygr[0].push_back(m_medium->ScaleDiffusion(y));
563 if (m_medium->GetIonTransverseDiffusion(ie, ib, ia, y)) {
565 ygr[1].push_back(m_medium->ScaleDiffusion(y));
567 if (m_medium->GetIonLongitudinalDiffusion(ie, ib, ia, y)) {
569 ygr[0].push_back(m_medium->ScaleDiffusion(y));
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]));
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;
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;
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);
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);
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);
652 for (
const auto c : carriers) {
653 Plot(ax, c, Parameter::Townsend, same);
654 Plot(ax, c, Parameter::Attachment,
true);
663 std::cerr <<
m_className <<
"::PlotVelocity: Medium is not defined.\n";
666 if (xaxis != m_xaxis) {
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) {
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.);
682 double e0 = m_efield;
683 double b0 = m_bfield;
684 double ctheta =
cos(m_angle);
685 double stheta =
sin(m_angle);
687 for (
size_t i = 0; i < nX; ++i) {
688 if (xaxis == Axis::E) {
690 }
else if (xaxis == Axis::B) {
693 ctheta =
cos(m_xPlot[i]);
694 stheta =
sin(m_xPlot[i]);
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,
700 ypl[0][i] =
fabs(vx);
701 ypl[1][i] =
fabs(vy);
702 ypl[2][i] =
fabs(vz);
704 }
else if (charge == Charge::Hole) {
705 if (m_medium->HoleVelocity(e0, 0, 0, b0 * ctheta, b0 * stheta, 0,
707 ypl[0][i] =
fabs(vx);
708 ypl[1][i] =
fabs(vy);
709 ypl[2][i] =
fabs(vz);
712 if (m_medium->IonVelocity(e0, 0, 0, b0 * ctheta, b0 * stheta, 0,
714 ypl[0][i] =
fabs(vx);
718 std::array<std::vector<double>, 3> xgr;
719 std::array<std::vector<double>, 3> ygr;
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) {
730 x = m_medium->UnScaleElectricField(grid[0][j]);
731 }
else if (xaxis == Axis::B) {
734 }
else if (xaxis == Axis::Angle) {
738 if (charge == Charge::Electron) {
739 if (m_medium->GetElectronVelocityE(ie, ib, ia, y)) {
741 ygr[0].push_back(m_medium->ScaleVelocity(y));
743 if (m_medium->GetElectronVelocityB(ie, ib, ia, y)) {
745 ygr[1].push_back(
fabs(m_medium->ScaleVelocity(y)));
747 if (m_medium->GetElectronVelocityExB(ie, ib, ia, y)) {
749 ygr[2].push_back(
fabs(m_medium->ScaleVelocity(y)));
751 }
else if (charge == Charge::Hole) {
752 if (m_medium->GetHoleVelocityE(ie, ib, ia, y)) {
754 ygr[0].push_back(m_medium->ScaleVelocity(y));
756 if (m_medium->GetHoleVelocityB(ie, ib, ia, y)) {
758 ygr[1].push_back(
fabs(m_medium->ScaleVelocity(y)));
760 if (m_medium->GetHoleVelocityExB(ie, ib, ia, y)) {
762 ygr[2].push_back(
fabs(m_medium->ScaleVelocity(y)));
765 if (m_medium->GetIonMobility(ie, ib, ia, y)) {
767 ygr[0].push_back(y * m_medium->UnScaleElectricField(grid[0][ie]));
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]));
787void ViewMedium::Plot(
const Axis xaxis,
const Charge charge,
788 const Parameter par,
const bool same) {
792 std::cerr <<
m_className <<
"::Plot: Medium is not defined.\n";
795 if (xaxis != m_xaxis) {
800 }
else if (!m_par.empty()) {
801 if (m_par[0] != Parameter::Townsend &&
802 m_par[0] != Parameter::Attachment) {
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) {
817 }
else if (xaxis == Axis::B) {
818 bx = m_xPlot[i] * ctheta;
819 by = m_xPlot[i] * stheta;
821 bx = m_bfield *
cos(m_xPlot[i]);
822 by = m_bfield *
sin(m_xPlot[i]);
825 if (charge == Charge::Electron) {
826 if (par == Parameter::Townsend) {
827 if (!m_medium->ElectronTownsend(ex, 0, 0, bx, by, 0, y))
continue;
829 if (!m_medium->ElectronAttachment(ex, 0, 0, bx, by, 0, y))
continue;
833 if (par == Parameter::Townsend) {
834 if (!m_medium->HoleTownsend(ex, 0, 0, bx, by, 0, y))
continue;
836 if (!m_medium->HoleAttachment(ex, 0, 0, bx, by, 0, y))
continue;
843 std::vector<double> xgr;
844 std::vector<double> ygr;
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) {
855 x = m_medium->UnScaleElectricField(grid[0][j]);
856 }
else if (xaxis == Axis::B) {
859 }
else if (xaxis == Axis::Angle) {
863 if (charge == Charge::Electron) {
864 if (par == Parameter::Townsend) {
865 if (m_medium->GetElectronTownsend(ie, ib, ia, y)) {
867 ygr.push_back(m_medium->ScaleTownsend(
exp(y)));
870 if (m_medium->GetElectronAttachment(ie, ib, ia, y)) {
872 ygr.push_back(m_medium->ScaleAttachment(
exp(y)));
875 }
else if (charge == Charge::Hole) {
876 if (par == Parameter::Townsend) {
877 if (m_medium->GetHoleTownsend(ie, ib, ia, y)) {
879 ygr.push_back(m_medium->ScaleTownsend(
exp(y)));
882 if (m_medium->GetHoleAttachment(ie, ib, ia, y)) {
884 ygr.push_back(m_medium->ScaleAttachment(
exp(y)));
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));
900void ViewMedium::PlotLorentzAngle(
const Axis xaxis,
const Charge charge,
905 std::cerr <<
m_className <<
"::PlotLorentzAngle: Medium is not defined.\n";
908 if (xaxis != m_xaxis) {
913 }
else if (!m_par.empty()) {
914 if (m_par[0] != Parameter::LorentzAngle) ResetY();
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) {
927 }
else if (xaxis == Axis::B) {
928 bx = m_xPlot[i] * ctheta;
929 by = m_xPlot[i] * stheta;
931 bx = m_bfield *
cos(m_xPlot[i]);
932 by = m_bfield *
sin(m_xPlot[i]);
935 if (!m_medium->ElectronLorentzAngle(ex, 0, 0, bx, by, 0, y))
continue;
939 std::vector<double> xgr;
940 std::vector<double> ygr;
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) {
951 x = m_medium->UnScaleElectricField(grid[0][j]);
952 }
else if (xaxis == Axis::B) {
955 }
else if (xaxis == Axis::Angle) {
959 if (m_medium->GetElectronLorentzAngle(ie, ib, ia, y)) {
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));
975ViewMedium::Axis ViewMedium::GetAxis(
const char xaxis)
const {
977 if (std::toupper(xaxis) ==
'E') {
979 }
else if (std::toupper(xaxis) ==
'B') {
981 }
else if (std::toupper(xaxis) ==
'A') {
987bool ViewMedium::GetGrid(std::array<std::vector<double>, 3>& grid,
988 int& ie,
int& ib,
int& ia,
989 const Axis xaxis)
const {
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;
Abstract base class for media.
TPad * GetCanvas()
Retrieve the canvas.
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.
void SetRangeB(const double bmin, const double bmax, const bool logscale)
Set the limits of the magnetic field.
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.
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.
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.
DoubleAc cos(const DoubleAc &f)
DoubleAc pow(const DoubleAc &f, double p)
DoubleAc exp(const DoubleAc &f)
DoubleAc fabs(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)