19double Interpolate(
const std::vector<double>& y,
20 const std::vector<double>& x,
const double xx) {
22 const double tol = 1.e-6 *
fabs(
x.back() -
x.front());
23 if (xx <
x.front())
return y.front();
24 const auto it1 = std::upper_bound(
x.cbegin(),
x.cend(), xx);
25 if (it1 ==
x.cend())
return y.back();
26 const auto it0 = std::prev(it1);
27 const double dx = (*it1 - *it0);
28 if (dx < tol)
return y[it0 -
x.cbegin()];
29 const double f = (xx - *it0) / dx;
30 return y[it0 -
x.cbegin()] * (1. - f) + f * y[it1 -
x.cbegin()];
33bool OnLine(
const double x1,
const double y1,
const double x2,
const double y2,
34 const double u,
const double v) {
36 double epsx = 1.e-10 * std::max({
fabs(x1),
fabs(x2),
fabs(u)});
37 double epsy = 1.e-10 * std::max({
fabs(y1),
fabs(y2),
fabs(v)});
38 epsx = std::max(1.e-10, epsx);
39 epsy = std::max(1.e-10, epsy);
41 if ((
fabs(x1 - u) <= epsx &&
fabs(y1 - v) <= epsy) ||
42 (
fabs(x2 - u) <= epsx &&
fabs(y2 - v) <= epsy)) {
45 }
else if (
fabs(x1 - x2) <= epsx &&
fabs(y1 - y2) <= epsy) {
49 double xc = 0., yc = 0.;
52 const double dx = (x2 - x1);
53 const double dy = (y2 - y1);
54 const double xl = ((u - x1) * dx + (v - y1) * dy) / (dx * dx + dy * dy);
67 const double dx = (x1 - x2);
68 const double dy = (y1 - y2);
69 const double xl = ((u - x2) * dx + (v - y2) * dy) / (dx * dx + dy * dy);
82 if (
fabs(u - xc) < epsx &&
fabs(v - yc) < epsy) {
88bool Crossing(
const double x1,
const double y1,
const double x2,
89 const double y2,
const double u1,
const double v1,
90 const double u2,
const double v2) {
93 if (OnLine(x1, y1, x2, y2, u1, v1) || OnLine(x1, y1, x2, y2, u2, v2) ||
94 OnLine(u1, v1, u2, v2, x1, y1) || OnLine(u1, v1, u2, v2, x2, y2)) {
98 std::array<std::array<double, 2>, 2> a;
103 const double det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
107 epsx = std::max(epsx, 1.e-10);
108 epsy = std::max(epsy, 1.e-10);
109 if (
fabs(det) < epsx * epsy) {
114 const double aux = a[1][1];
115 a[1][1] = a[0][0] / det;
117 a[1][0] = -a[1][0] / det;
118 a[0][1] = -a[0][1] / det;
120 const double xc = a[0][0] * (x1 * y2 - x2 * y1) + a[1][0] * (u1 * v2 - u2 * v1);
121 const double yc = a[0][1] * (x1 * y2 - x2 * y1) + a[1][1] * (u1 * v2 - u2 * v1);
123 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc)) {
139 std::cerr <<
m_className <<
"::SetSensor: Null pointer.\n";
144 m_component =
nullptr;
149 std::cerr <<
m_className <<
"::SetComponent: Null pointer.\n";
160 std::cerr <<
m_className <<
"::SetAspectRatioSwitch: Value must be > 0.\n";
168 if (thr < 0. || thr > 1.) {
169 std::cerr <<
m_className <<
"::SetLoopThreshold:\n"
170 <<
" Value must be between 0 and 1.\n";
173 m_loopThreshold = thr;
178 if (thr < 0. || thr > 1.) {
179 std::cerr <<
m_className <<
"::SetConnectionThreshold:\n"
180 <<
" Value must be between 0 and 1.\n";
183 m_connectionThreshold = thr;
187 const std::vector<std::array<double, 3> >& points,
const bool rev,
188 const bool colour,
const bool markers,
const bool plotDriftLines) {
190 if (!m_sensor && !m_component) {
192 <<
" Neither sensor nor component are defined.\n";
196 std::cerr <<
m_className <<
"::PlotIsochrons: Time step must be > 0.\n";
199 if (points.empty()) {
201 <<
" No starting points provided.\n";
204 if (!SetPlotLimits())
return;
207 canvas->SetTitle(
"Isochrons");
210 frame->GetXaxis()->SetTitle(
LabelX().c_str());
211 frame->GetYaxis()->SetTitle(
LabelY().c_str());
218 std::vector<std::vector<std::array<double, 3> > > driftLines;
219 std::vector<std::array<double, 3> > startPoints;
220 std::vector<std::array<double, 3> > endPoints;
221 std::vector<int> statusCodes;
223 ComputeDriftLines(tstep, points, driftLines, startPoints, endPoints,
225 const unsigned int nDriftLines = driftLines.size();
226 if (nDriftLines < 2) {
227 std::cerr <<
m_className <<
"::PlotIsochrons: Too few drift lines.\n";
231 std::size_t nContours = 0;
232 for (
const auto& driftLine : driftLines) {
233 nContours = std::max(nContours, driftLine.size());
235 if (nContours == 0) {
236 std::cerr <<
m_className <<
"::PlotIsochrons: No contour lines.\n";
240 std::set<int> allStats;
241 for (
const auto stat : statusCodes) allStats.insert(stat);
246 <<
" Drawing " << nContours <<
" contours, "
247 << nDriftLines <<
" drift lines.\n";
248 std::printf(
" Connection threshold: %10.3f\n", m_connectionThreshold);
249 std::printf(
" Aspect ratio threshold: %10.3f\n", m_aspectRatio);
250 std::printf(
" Loop closing threshold: %10.3f\n", m_loopThreshold);
251 if (m_sortContours) {
252 std::cout <<
" Sort contours.\n";
254 std::cout <<
" Do not sort contours.\n";
256 if (m_checkCrossings) {
257 std::cout <<
" Check for crossings.\n";
259 std::cout <<
" Do not check for crossings.\n";
262 std::cout <<
" Mark isochron points.\n";
264 std::cout <<
" Draw isochron lines.\n";
270 graph.SetLineColor(kGray + 2);
271 graph.SetMarkerColor(kGray + 2);
272 graph.SetLineStyle(m_lineStyle);
273 graph.SetMarkerStyle(m_markerStyle);
274 const double colRange = double(gStyle->GetNumberOfColors()) / nContours;
275 for (
unsigned int ic = 0; ic < nContours; ++ic) {
277 const auto col = gStyle->GetColorPalette(
int((ic + 0.99) * colRange));
278 graph.SetLineColor(col);
279 graph.SetMarkerColor(col);
281 for (
int stat : allStats) {
282 std::vector<std::pair<std::array<double, 4>,
unsigned int> > contour;
284 for (
unsigned int k = 0; k < nDriftLines; ++k) {
285 const auto& dl = driftLines[k];
287 if (statusCodes[k] != stat || ic >= dl.size())
continue;
289 std::array<double, 4> point = {dl[ic][0], dl[ic][1], dl[ic][2], 0.};
290 contour.push_back(std::make_pair(point, k));
293 if (contour.empty())
continue;
296 if (m_sortContours && !markers) SortContour(contour, circle);
300 std::vector<double> xp;
301 std::vector<double> yp;
302 std::vector<double> zp;
303 for (
const auto& point : contour) {
304 const double x = point.first[0];
305 const double y = point.first[1];
306 const double z = point.first[2];
311 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Psame");
321 std::vector<double> xp;
322 std::vector<double> yp;
323 std::vector<double> zp;
324 const unsigned int nP = contour.size();
325 for (
unsigned int i = 0; i < nP; ++i) {
327 const auto x0 = contour[i].first[0];
328 const auto y0 = contour[i].first[1];
329 const auto z0 = contour[i].first[2];
333 if (i == nP - 1)
break;
334 const auto x1 = contour[i + 1].first[0];
335 const auto y1 = contour[i + 1].first[1];
337 if (fabs(x1 - x0) > tolx || fabs(y1 - y0) > toly) gap =
true;
340 const auto i0 = contour[i].second;
341 const auto i1 = contour[i + 1].second;
343 if (m_checkCrossings && !gap) {
344 for (
unsigned int k = 0; k < nDriftLines; ++k) {
345 const auto& dl = driftLines[k];
346 for (
unsigned int jc = 0; jc < dl.size(); ++jc) {
347 if ((i0 == k || i1 == k) && (jc == ic || jc + 1 == ic)) {
350 const auto& p0 = dl[jc];
351 const auto& p1 = jc == dl.size() - 1 ? endPoints[k] : dl[jc + 1];
352 if (Crossing(p0[0], p0[1], p1[0], p1[1], x0, y0, x1, y1)) {
358 if ((i0 == k || i1 == k) && ic == 0)
continue;
359 const auto& p0 = startPoints[k];
360 if (Crossing(p0[0], p0[1], dl[0][0], dl[0][1],
371 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Lsame");
374 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Psame");
383 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Lsame");
384 }
else if (!xp.empty()) {
385 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Psame");
391 if (!plotDriftLines)
return;
393 graph.SetLineStyle(1);
394 if (m_particle == Particle::Electron) {
395 graph.SetLineColor(kOrange - 3);
397 graph.SetLineColor(kRed + 1);
399 for (
unsigned int i = 0; i < nDriftLines; ++i) {
400 std::vector<double> xp;
401 std::vector<double> yp;
402 const double x0 = startPoints[i][0];
403 const double y0 = startPoints[i][1];
404 const double z0 = startPoints[i][2];
407 for (
const auto& point : driftLines[i]) {
408 const double x = point[0];
409 const double y = point[1];
410 const double z = point[2];
414 const double x1 = endPoints[i][0];
415 const double y1 = endPoints[i][1];
416 const double z1 = endPoints[i][2];
419 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Lsame");
423void ViewIsochrons::ComputeDriftLines(
const double tstep,
424 const std::vector<std::array<double, 3> >& points,
425 std::vector<std::vector<std::array<double, 3> > >& driftLines,
426 std::vector<std::array<double, 3> >& startPoints,
427 std::vector<std::array<double, 3> >& endPoints,
428 std::vector<int>& statusCodes,
const bool rev) {
442 for (
const auto& point : points) {
443 if (m_particle == Particle::Electron) {
451 drift.
DriftIon(point[0], point[1], point[2], 0.);
458 if (nu < 3)
continue;
460 double xf = 0., yf = 0., zf = 0., tf = 0.;
463 const unsigned int nSteps =
static_cast<unsigned int>(tf / tstep);
464 if (nSteps == 0)
continue;
465 std::vector<double> xu(nu, 0.);
466 std::vector<double> yu(nu, 0.);
467 std::vector<double> zu(nu, 0.);
468 std::vector<double> tu(nu, 0.);
469 for (
unsigned int i = 0; i < nu; ++i) {
473 for (
auto& t : tu) t = tf - t;
474 std::reverse(std::begin(xu), std::end(xu));
475 std::reverse(std::begin(yu), std::end(yu));
476 std::reverse(std::begin(zu), std::end(zu));
477 std::reverse(std::begin(tu), std::end(tu));
479 std::vector<std::array<double, 3> > tab;
481 for (
unsigned int i = 0; i < nSteps; ++i) {
482 const double t = (i + 1) * tstep;
486 std::array<double, 3> step = {Interpolate(xu, tu, t),
487 Interpolate(yu, tu, t),
488 Interpolate(zu, tu, t)};
491 driftLines.push_back(std::move(tab));
492 std::array<double, 3> start = {xu[0], yu[0], zu[0]};
493 std::array<double, 3> end = {xu[nu - 1], yu[nu - 1], zu[nu - 1]};
494 startPoints.push_back(std::move(start));
495 endPoints.push_back(std::move(end));
498 statusCodes.push_back(status);
500 statusCodes.push_back(0);
505bool ViewIsochrons::SetPlotLimits() {
508 double xmin = 0., ymin = 0., xmax = 0., ymax = 0.;
521 ok =
PlotLimits(m_sensor, xmin, ymin, xmax, ymax);
523 ok =
PlotLimits(m_component, xmin, ymin, xmax, ymax);
534void ViewIsochrons::SortContour(
535 std::vector<std::pair<std::array<double, 4>,
unsigned int> >& contour,
538 if (contour.size() < 2)
return;
542 for (
const auto& point : contour) {
543 xcog += point.first[0];
544 ycog += point.first[1];
546 const double scale = 1. / contour.size();
553 for (
const auto& point : contour) {
554 const double dx = point.first[0] - xcog;
555 const double dy = point.first[1] - ycog;
560 const double theta = 0.5 * atan2(2 * sxy, sxx - syy);
561 const double ct =
cos(theta);
562 const double st =
sin(theta);
566 for (
const auto& point : contour) {
567 const double dx = point.first[0] - xcog;
568 const double dy = point.first[1] - ycog;
569 sxx +=
fabs(+ct * dx + st * dy);
570 syy +=
fabs(-st * dx + ct * dy);
573 if (
fabs(sxx) > m_aspectRatio *
fabs(syy) ||
574 fabs(syy) > m_aspectRatio *
fabs(sxx)) {
580 for (
auto& point : contour) {
581 const double dx = point.first[0] - xcog;
582 const double dy = point.first[1] - ycog;
583 point.first[3] = circle ? atan2(dy, dx) : ct * dx +
st * dy;
586 std::sort(contour.begin(), contour.end(),
587 [](
const std::pair<std::array<double, 4>,
int>& p1,
588 const std::pair<std::array<double, 4>,
int>& p2) {
589 return p1.first[3] < p2.first[3]; }
596 unsigned int imax = 0;
597 const unsigned int nPoints = contour.size();
598 for (
unsigned int j = 0; j < nPoints; ++j) {
599 const auto& p1 = contour[j];
600 const auto& p0 = j > 0 ? contour[j - 1] : contour.back();
601 const double dx = p1.first[0] - p0.first[0];
602 const double dy = p1.first[1] - p0.first[1];
603 const double d =
sqrt(dx * dx + dy * dy);
604 if (j > 0) dsum += d;
610 if (dmax < m_loopThreshold * dsum) {
612 contour.push_back(contour[0]);
617 std::rotate(contour.begin(), contour.begin() + imax, contour.end());
Abstract base class for components.
void EnableSignalCalculation(const bool on=true)
Switch on/off calculation of induced currents (default: enabled).
bool DriftNegativeIon(const double x0, const double y0, const double z0, const double t0)
bool DriftIon(const double x0, const double y0, const double z0, const double t0)
Simulate the drift line of an ion with a given starting point.
void SetSensor(Sensor *s)
Set the sensor.
unsigned int GetNumberOfDriftLinePoints() const
Get the number of points of the most recent drift line.
bool DriftElectron(const double x0, const double y0, const double z0, const double t0)
Simulate the drift line of an electron with a given starting point.
bool DriftPositron(const double x0, const double y0, const double z0, const double t0)
void GetDriftLinePoint(const unsigned int i, double &x, double &y, double &z, double &t) const
Get the coordinates and time of a point along the most recent drift line.
void GetEndPoint(double &x, double &y, double &z, double &t, int &st) const
Get the end point and status flag of the most recent drift line.
void SetMaximumStepSize(const double ms)
Set the maximum step size that is allowed. By default, there is no limit.
void AddComponent(Component *comp)
Add a component.
Base class for visualization classes.
std::array< double, 4 > m_plane
std::array< std::array< double, 3 >, 3 > m_proj
TPad * GetCanvas()
Retrieve the canvas.
bool PlotLimits(Sensor *sensor, double &xmin, double &ymin, double &xmax, double &ymax) const
bool PlotLimitsFromUserBox(double &xmin, double &ymin, double &xmax, double &ymax) const
void SetComponent(Component *c)
Set the component.
void PlotIsochrons(const double tstep, const std::vector< std::array< double, 3 > > &points, const bool rev=false, const bool colour=false, const bool markers=false, const bool plotDriftLines=true)
void SetConnectionThreshold(const double thr)
void SetAspectRatioSwitch(const double ar)
ViewIsochrons()
Constructor.
void SetLoopThreshold(const double thr)
void SetSensor(Sensor *s)
Set the sensor.
DoubleAc cos(const DoubleAc &f)
DoubleAc fabs(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)