22void SampleRange(
const double xmin,
const double ymin,
const double xmax,
23 const double ymax, TF2* f,
double& zmin,
double& zmax) {
24 constexpr unsigned int n = 1000;
25 const double dx = xmax - xmin;
26 const double dy = ymax - ymin;
27 zmin = std::numeric_limits<double>::max();
29 for (
unsigned int i = 0; i < n; ++i) {
32 if (z < zmin) zmin =
z;
33 if (z > zmax) zmax =
z;
37void SampleRange(TF1* f,
double& ymin,
double& ymax) {
38 constexpr unsigned int n = 1000;
39 ymin = std::numeric_limits<double>::max();
43 f->GetRange(xmin, xmax);
44 const double dx = xmax - xmin;
45 for (
unsigned int i = 0; i < n; ++i) {
47 if (y < ymin) ymin =
y;
48 if (y > ymax) ymax =
y;
52double Interpolate(
const std::array<double, 1000>& y,
53 const std::array<double, 1000>& x,
const double xx) {
55 const double tol = 1.e-6 *
fabs(
x.back() -
x.front());
56 if (xx < x[0])
return y[0];
57 const auto it1 = std::upper_bound(
x.cbegin(),
x.cend(), xx);
58 if (it1 ==
x.cend())
return y.back();
59 const auto it0 = std::prev(it1);
60 const double dx = (*it1 - *it0);
61 if (dx < tol)
return y[it0 -
x.cbegin()];
62 const double f = (xx - *it0) / dx;
63 return y[it0 -
x.cbegin()] * (1. - f) + f * y[it1 -
x.cbegin()];
74 std::cerr <<
m_className <<
"::SetSensor: Null pointer.\n";
78 m_component =
nullptr;
83 std::cerr <<
m_className <<
"::SetComponent: Null pointer.\n";
91 m_vmin = std::min(vmin, vmax);
92 m_vmax = std::max(vmin, vmax);
93 m_useAutoRange =
false;
97 m_emin = std::min(emin, emax);
98 m_emax = std::max(emin, emax);
99 m_useAutoRange =
false;
103 m_wmin = std::min(wmin, wmax);
104 m_wmax = std::max(wmin, wmax);
105 m_useAutoRange =
false;
109 if (n > 0) m_nContours = n;
113 m_nSamples1d = std::max(4u, n);
117 const unsigned int ny) {
118 m_nSamples2dX = std::max(4u, nx);
119 m_nSamples2dY = std::max(4u, ny);
123 Draw2d(option,
true,
false,
"",
"CONT1Z");
128 std::transform(drawopt.begin(), drawopt.end(),
129 std::back_inserter(opt1), toupper);
130 if (opt1.find(
"CONT") != std::string::npos) {
131 Draw2d(option,
true,
false,
"", drawopt);
133 Draw2d(option,
false,
false,
"", drawopt);
138 const double x1,
const double y1,
const double z1,
139 const std::string& option,
const bool normalised) {
140 DrawProfile(x0, y0, z0, x1, y1, z1, option,
false,
"", normalised);
144 const std::string& option,
145 const std::string& drawopt) {
146 Draw2d(option,
false,
true, label, drawopt);
150 const std::string& option) {
151 Draw2d(option,
true,
true, label,
"CONT1Z");
155 const double x0,
const double y0,
156 const double z0,
const double x1,
157 const double y1,
const double z1,
158 const std::string& option,
159 const bool normalised) {
160 DrawProfile(x0, y0, z0, x1, y1, z1, option,
true, label, normalised);
164ViewField::Parameter ViewField::GetPar(
const std::string& option,
165 std::string& title)
const {
168 std::transform(option.begin(), option.end(),
169 std::back_inserter(opt), toupper);
170 if (opt ==
"V" || opt ==
"P" || opt ==
"PHI" ||
171 opt.find(
"VOLT") != std::string::npos ||
172 opt.find(
"POT") != std::string::npos) {
174 return Parameter::Potential;
175 }
else if (opt ==
"E" || opt ==
"FIELD" || opt ==
"NORM" ||
176 opt.find(
"MAG") != std::string::npos) {
178 return Parameter::Magnitude;
179 }
else if (opt.find(
"X") != std::string::npos) {
180 title =
"field (x-component)";
181 return Parameter::Ex;
182 }
else if (opt.find(
"Y") != std::string::npos) {
183 title =
"field (y-component)";
184 return Parameter::Ey;
185 }
else if (opt.find(
"Z") != std::string::npos) {
186 title =
"field (z-component)";
187 return Parameter::Ez;
189 std::cerr <<
m_className <<
"::GetPar: Unknown option (" << option <<
").\n";
191 return Parameter::Potential;
194void ViewField::Draw2d(
const std::string& option,
const bool contour,
195 const bool wfield,
const std::string& electrode,
196 const std::string& drawopt) {
197 if (!m_sensor && !m_component) {
199 <<
" Neither sensor nor component are defined.\n";
204 if (!SetPlotLimits())
return;
208 const Parameter par = GetPar(option, title);
210 auto eval = [
this, par, wfield, electrode](
double* u,
double* ) {
215 return wfield ? Wfield(x, y, z, par, electrode) : Field(
x,
y,
z, par);
224 double zmin = m_vmin;
225 double zmax = m_vmax;
228 title =
"Contours of the weighting " + title;
230 title =
"Weighting " + title;
232 if (m_useAutoRange) {
235 }
else if (par == Parameter::Potential) {
244 title =
"Contours of the electric " + title;
246 title =
"Electric " + title;
248 if (par == Parameter::Potential) {
249 if (m_useAutoRange) {
255 }
else if (m_sensor) {
266 if (m_useAutoRange) {
280 std::vector<double> level(m_nContours, 0.);
281 if (m_nContours > 1) {
282 const double step = (zmax - zmin) / (m_nContours - 1.);
283 for (
unsigned int i = 0; i < m_nContours; ++i) {
284 level[i] = zmin + i * step;
287 level[0] = 0.5 * (zmax + zmin);
291 <<
" Number of contours: " << m_nContours <<
"\n";
292 for (
unsigned int i = 0; i < m_nContours; ++i) {
293 std::cout <<
" Level " << i <<
" = " << level[i] <<
"\n";
296 f2.SetContour(m_nContours, level.data());
300 f2.SetNpx(m_nSamples2dX);
301 f2.SetNpy(m_nSamples2dY);
305 f2.SetTitle(labels.c_str());
309 pad->SetTitle(title.c_str());
310 f2.DrawCopy(drawopt.c_str());
311 gPad->SetRightMargin(0.15);
315void ViewField::DrawProfile(
const double x0,
const double y0,
const double z0,
316 const double x1,
const double y1,
const double z1,
317 const std::string& option,
318 const bool wfield,
const std::string& electrode,
319 const bool normalised) {
320 if (!m_sensor && !m_component) {
322 <<
" Neither sensor nor component are defined.\n";
330 if (dx * dx + dy * dy + dz * dz <= 0.) {
332 <<
" Start and end points coincide.\n";
338 const Parameter par = GetPar(option, title);
342 unsigned int dir = 3;
355 }
else if (!normalised) {
356 t1 =
sqrt(dx * dx + dy * dy + dz * dz);
362 auto eval = [
this, par, wfield, electrode, dir,
363 x0, y0, z0, dx, dy, dz](
double* u,
double* ) {
365 const double t = u[0];
366 double x = dir == 0 ? t : x0;
367 double y = dir == 1 ? t : y0;
368 double z = dir == 2 ? t : z0;
374 return wfield ? Wfield(x, y, z, par, electrode) : Field(
x,
y,
z, par);
378 TF1 f1(fname.c_str(), eval, t0, t1, 0);
380 double fmin = m_vmin;
381 double fmax = m_vmax;
383 title =
"weighting " + title;
384 if (par == Parameter::Potential) {
385 if (m_useAutoRange && m_samplePotential) {
386 SampleRange(&f1, fmin, fmax);
392 if (m_useAutoRange) {
393 SampleRange(&f1, fmin, fmax);
400 title =
"electric " + title;
401 if (par == Parameter::Potential) {
402 if (m_useAutoRange) {
405 SampleRange(&f1, fmin, fmax);
407 }
else if (m_sensor) {
409 SampleRange(&f1, fmin, fmax);
417 if (m_useAutoRange) {
418 SampleRange(&f1, fmin, fmax);
428 std::string labels =
";normalised distance;";
430 labels =
";#it{x} [cm];";
431 }
else if (dir == 1) {
432 labels =
";#it{y} [cm];";
433 }
else if (dir == 2) {
434 labels =
";#it{z} [cm];";
435 }
else if (!normalised) {
436 labels =
";distance [cm];";
438 if (par == Parameter::Potential) {
449 if (par != Parameter::Magnitude) labels +=
",";
450 }
else if (par != Parameter::Magnitude) {
453 if (par == Parameter::Ex) {
455 }
else if (par == Parameter::Ey) {
457 }
else if (par == Parameter::Ez) {
460 if (wfield || par != Parameter::Magnitude) labels +=
"}";
467 f1.SetTitle(labels.c_str());
468 f1.SetNpx(m_nSamples1d);
472 title =
"Profile plot of the " + title;
473 pad->SetTitle(title.c_str());
478bool ViewField::SetPlotLimits() {
481 double xmin = 0., ymin = 0., xmax = 0., ymax = 0.;
495 ok =
PlotLimits(m_sensor, xmin, ymin, xmax, ymax);
497 ok =
PlotLimits(m_component, xmin, ymin, xmax, ymax);
508double ViewField::Field(
const double x,
const double y,
const double z,
509 const Parameter par)
const {
512 double ex = 0., ey = 0., ez = 0., volt = 0.;
514 Medium* medium =
nullptr;
516 m_component->
ElectricField(x, y, z, ex, ey, ez, volt, medium, status);
518 m_sensor->
ElectricField(x, y, z, ex, ey, ez, volt, medium, status);
520 if (m_useStatus && status != 0)
return m_vBkg;
522 case Parameter::Potential:
524 case Parameter::Magnitude:
525 return sqrt(ex * ex + ey * ey + ez * ez);
538double ViewField::Wfield(
const double x,
const double y,
const double z,
540 const std::string& electrode)
const {
542 if (par == Parameter::Potential) {
550 double ex = 0., ey = 0., ez = 0.;
558 case Parameter::Magnitude:
559 return sqrt(ex * ex + ey * ey + ez * ez);
573 const std::vector<double>& y0,
574 const std::vector<double>& z0,
575 const bool electron,
const bool axis,
578 if (x0.empty() || y0.empty() || z0.empty())
return;
579 const size_t nLines = x0.size();
580 if (y0.size() != nLines || x0.size() != nLines) {
582 <<
" Mismatch in number of x/y/z coordinates.\n";
586 if (!m_sensor && !m_component) {
588 <<
" Neither sensor nor component are defined.\n";
594 pad->SetTitle(
"Field lines");
596 const bool rangeSet =
RangeSet(pad);
597 if (axis || !rangeSet) {
599 if (!SetPlotLimits()) {
601 <<
" Could not determine the plot limits.\n";
608 frame->GetXaxis()->SetTitle(
LabelX().c_str());
609 frame->GetYaxis()->SetTitle(
LabelY().c_str());
611 }
else if (!rangeSet) {
620 double xmin = 0., ymin = 0., zmin = 0.;
621 double xmax = 0., ymax = 0., zmax = 0.;
622 if (!m_component->
GetBoundingBox(xmin, ymin, zmin, xmax, ymax, zmax)) {
634 for (
size_t i = 0; i < nLines; ++i) {
635 std::vector<std::array<float, 3> > xl;
636 if (!drift.
FieldLine(x0[i], y0[i], z0[i], xl, electron))
continue;
643 const double x0,
const double y0,
const double z0,
644 const double x1,
const double y1,
const double z1,
645 std::vector<double>& xf, std::vector<double>& yf,
646 std::vector<double>& zf,
647 const unsigned int nPoints)
const {
650 std::cerr <<
m_className <<
"::EqualFluxIntervals:\n"
651 <<
" Number of flux lines must be > 1.\n";
656 constexpr unsigned int nV = 5;
658 const double xp = (y1 - y0) *
m_plane[2] - (z1 - z0) *
m_plane[1];
659 const double yp = (z1 - z0) *
m_plane[0] - (x1 - x0) *
m_plane[2];
660 const double zp = (x1 - x0) *
m_plane[1] - (y1 - y0) *
m_plane[0];
665 xp, yp, zp, 20 * nV, 0);
668 xp, yp, zp, 20 * nV, 0);
670 const int isign = q > 0 ? +1 : -1;
672 std::cout <<
m_className <<
"::EqualFluxIntervals:\n";
673 std::printf(
" Total flux: %15.e8\n", q);
677 unsigned int nOtherSign = 0;
680 constexpr size_t nSteps = 1000;
681 std::array<double, nSteps> sTab;
682 std::array<double, nSteps> fTab;
683 constexpr double ds = 1. / nSteps;
684 const double dx = (x1 - x0) * ds;
685 const double dy = (y1 - y0) * ds;
686 const double dz = (z1 - z0) * ds;
687 for (
size_t i = 0; i < nSteps; ++i) {
688 const double x = x0 + i * dx;
689 const double y = y0 + i * dy;
690 const double z = z0 + i * dz;
693 xp, yp, zp, nV, isign);
696 xp, yp, zp, nV, isign);
698 sTab[i] = (i + 1) * ds;
701 if (s0 < -0.5) s0 = i * ds;
704 if (q < 0) ++nOtherSign;
708 std::printf(
" Used flux: %15.8e V. Start: %10.3f End: %10.3f\n",
713 std::cerr <<
m_className <<
"::EqualFluxIntervals:\n"
714 <<
" 1-Sided flux integral is not > 0.\n";
716 }
else if (s0 < -0.5 || s1 < -0.5 || s1 <= s0) {
717 std::cerr <<
m_className <<
"::EqualFluxIntervals:\n"
718 <<
" No flux interval without sign change found.\n";
720 }
else if (nOtherSign > 0) {
721 std::cerr <<
m_className <<
"::EqualFluxIntervals:\n"
722 <<
" The flux changes sign over the line.\n";
725 const double scale = (nPoints - 1) / fsum;
726 for (
size_t i = 0; i < nSteps; ++i) fTab[i] *= scale;
729 for (
size_t i = 0; i < nPoints; ++i) {
730 double s = std::min(s1, std::max(s0, Interpolate(sTab, fTab, i)));
731 xf.push_back(x0 + s * (x1 - x0));
732 yf.push_back(y0 + s * (y1 - y0));
733 zf.push_back(z0 + s * (z1 - z0));
739 const double x0,
const double y0,
const double z0,
740 const double x1,
const double y1,
const double z1,
741 std::vector<double>& xf, std::vector<double>& yf,
742 std::vector<double>& zf,
const double interval)
const {
744 if (interval <= 0.) {
745 std::cerr <<
m_className <<
"::FixedFluxIntervals:\n"
746 <<
" Flux interval must be > 0.\n";
751 constexpr unsigned int nV = 5;
753 const double xp = (y1 - y0) *
m_plane[2] - (z1 - z0) *
m_plane[1];
754 const double yp = (z1 - z0) *
m_plane[0] - (x1 - x0) *
m_plane[2];
755 const double zp = (x1 - x0) *
m_plane[1] - (y1 - y0) *
m_plane[0];
760 xp, yp, zp, 20 * nV, 0);
763 xp, yp, zp, 20 * nV, 0);
765 const int isign = q > 0 ? +1 : -1;
767 std::cout <<
m_className <<
"::FixedFluxIntervals:\n";
768 std::printf(
" Total flux: %15.8e V\n", q);
772 unsigned int nOtherSign = 0;
774 constexpr size_t nSteps = 1000;
775 std::array<double, nSteps> sTab;
776 std::array<double, nSteps> fTab;
777 constexpr double ds = 1. / nSteps;
778 const double dx = (x1 - x0) * ds;
779 const double dy = (y1 - y0) * ds;
780 const double dz = (z1 - z0) * ds;
781 for (
size_t i = 0; i < nSteps; ++i) {
782 const double x = x0 + i * dx;
783 const double y = y0 + i * dy;
784 const double z = z0 + i * dz;
787 xp, yp, zp, nV, isign);
790 xp, yp, zp, nV, isign);
792 sTab[i] = (i + 1) * ds;
795 if (s0 < -0.5) s0 = i * ds;
797 if (q < 0) ++nOtherSign;
802 std::printf(
" Used flux: %15.8e V. Start offset: %10.3f\n", fsum, s0);
805 std::cerr <<
m_className <<
"::FixedFluxIntervals:\n"
806 <<
" 1-Sided flux integral is not > 0.\n";
808 }
else if (s0 < -0.5) {
809 std::cerr <<
m_className <<
"::FixedFluxIntervals:\n"
810 <<
" No flux interval without sign change found.\n";
812 }
else if (nOtherSign > 0) {
813 std::cerr <<
m_className <<
"::FixedFluxIntervals:\n"
814 <<
" Warning: The flux changes sign over the line.\n";
819 const double s = Interpolate(sTab, fTab, f);
821 xf.push_back(x0 + s * (x1 - x0));
822 yf.push_back(y0 + s * (y1 - y0));
823 zf.push_back(z0 + s * (z1 - z0));
Abstract base class for components.
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 ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status)=0
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].
double IntegrateFluxLine(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const double xp, const double yp, const double zp, const unsigned int nI, const int isign=0)
void SetSensor(Sensor *s)
Set the sensor.
bool FieldLine(const double xi, const double yi, const double zi, std::vector< std::array< float, 3 > > &xl, const bool electron=true)
void SetMaximumStepSize(const double ms)
Set the maximum step size that is allowed. By default, there is no limit.
bool GetVoltageRange(double &vmin, double &vmax)
Return the voltage range.
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).
void AddComponent(Component *comp)
Add a component.
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).
bool SetArea()
Set the user area to the default.
double IntegrateFluxLine(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const double xp, const double yp, const double zp, const unsigned int nI, const int isign=0)
double WeightingPotential(const double x, const double y, const double z, const std::string &label)
Get the weighting potential at (x, y, z).
Base class for visualization classes.
std::array< double, 4 > m_plane
void DrawLine(const std::vector< std::array< float, 3 > > &xl, const short col, const short lw)
std::array< std::array< double, 3 >, 3 > m_proj
TPad * GetCanvas()
Retrieve the canvas.
void SetRange(TPad *pad, const double x0, const double y0, const double x1, const double y1)
bool PlotLimits(Sensor *sensor, double &xmin, double &ymin, double &xmax, double &ymax) const
bool PlotLimitsFromUserBox(double &xmin, double &ymin, double &xmax, double &ymax) const
static std::string FindUnusedFunctionName(const std::string &s)
Find an unused function name.
void PlotProfileWeightingField(const std::string &label, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const std::string &option="v", const bool normalised=true)
void SetElectricFieldRange(const double emin, const double emax)
Set the plot limits for the electric field.
void PlotWeightingField(const std::string &label, const std::string &option, const std::string &drawopt)
void SetNumberOfSamples2d(const unsigned int nx, const unsigned int ny)
Set the number of points used for drawing 2D functions.
bool FixedFluxIntervals(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, std::vector< double > &xf, std::vector< double > &yf, std::vector< double > &zf, const double interval=10.) const
Generate points along a line, spaced by a given flux interval.
void SetComponent(Component *c)
Set the component for which to plot the field.
void SetNumberOfSamples1d(const unsigned int n)
Set the number of points used for drawing 1D functions.
void PlotFieldLines(const std::vector< double > &x0, const std::vector< double > &y0, const std::vector< double > &z0, const bool electron=true, const bool axis=true, const short col=kOrange - 3)
Draw electric field lines from a set of starting points.
void SetVoltageRange(const double vmin, const double vmax)
Set the plot limits for the potential.
void PlotProfile(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const std::string &option="v", const bool normalised=true)
bool EqualFluxIntervals(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, std::vector< double > &xf, std::vector< double > &yf, std::vector< double > &zf, const unsigned int nPoints=20) const
Generates point along a line, spaced by equal flux intervals.
void PlotContour(const std::string &option="v")
void Plot(const std::string &option="v", const std::string &drawopt="arr")
void SetNumberOfContours(const unsigned int n)
Set the number of contour levels.
void SetWeightingFieldRange(const double wmin, const double wmax)
Set the plot limits for the weighting field.
void SetSensor(Sensor *s)
Set the sensor for which to plot the field.
void PlotContourWeightingField(const std::string &label, const std::string &option)
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
DoubleAc fabs(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)