16double Mag2(
const std::array<double, 2>& x) {
17 return x[0] *
x[0] +
x[1] *
x[1];
22bool OnLine(
const double x1,
const double y1,
const double x2,
const double y2,
23 const double u,
const double v) {
25 double epsx = 1.e-10 * std::max({
fabs(x1),
fabs(x2),
fabs(u)});
26 double epsy = 1.e-10 * std::max({
fabs(y1),
fabs(y2),
fabs(v)});
27 epsx = std::max(1.e-10, epsx);
28 epsy = std::max(1.e-10, epsy);
30 if ((
fabs(x1 - u) <= epsx &&
fabs(y1 - v) <= epsy) ||
31 (
fabs(x2 - u) <= epsx &&
fabs(y2 - v) <= epsy)) {
34 }
else if (
fabs(x1 - x2) <= epsx &&
fabs(y1 - y2) <= epsy) {
38 double xc = 0., yc = 0.;
41 const double dx = (x2 - x1);
42 const double dy = (y2 - y1);
43 const double xl = ((u - x1) * dx + (v - y1) * dy) / (dx * dx + dy * dy);
56 const double dx = (x1 - x2);
57 const double dy = (y1 - y2);
58 const double xl = ((u - x2) * dx + (v - y2) * dy) / (dx * dx + dy * dy);
71 if (
fabs(u - xc) < epsx &&
fabs(v - yc) < epsy) {
79bool Crossing(
const double x1,
const double y1,
const double x2,
80 const double y2,
const double u1,
const double v1,
81 const double u2,
const double v2,
double& xc,
double& yc) {
86 std::array<std::array<double, 2>, 2> a;
91 const double det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
93 const double epsx = std::max(1.e-10, 1.e-10 * std::max({
fabs(x1),
fabs(x2),
95 const double epsy = std::max(1.e-10, 1.e-10 * std::max({
fabs(y1),
fabs(y2),
98 if (
fabs(det) < epsx * epsy)
return false;
101 const double aux = a[1][1];
102 a[1][1] = a[0][0] / det;
104 a[1][0] = -a[1][0] / det;
105 a[0][1] = -a[0][1] / det;
107 xc = a[0][0] * (x1 * y2 - x2 * y1) + a[1][0] * (u1 * v2 - u2 * v1);
108 yc = a[0][1] * (x1 * y2 - x2 * y1) + a[1][1] * (u1 * v2 - u2 * v1);
110 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc)) {
119bool Intersecting(
const std::vector<double>& xp1,
120 const std::vector<double>& yp1,
121 const std::vector<double>& xp2,
122 const std::vector<double>& yp2) {
124 const double xmin1 = *std::min_element(std::begin(xp1), std::end(xp1));
125 const double ymin1 = *std::min_element(std::begin(yp1), std::end(yp1));
126 const double xmax1 = *std::max_element(std::begin(xp1), std::end(xp1));
127 const double ymax1 = *std::max_element(std::begin(yp1), std::end(yp1));
129 const double xmin2 = *std::min_element(std::begin(xp2), std::end(xp2));
130 const double ymin2 = *std::min_element(std::begin(yp2), std::end(yp2));
131 const double xmax2 = *std::max_element(std::begin(xp2), std::end(xp2));
132 const double ymax2 = *std::max_element(std::begin(yp2), std::end(yp2));
134 const double epsx = 1.e-6 * std::max({std::abs(xmax1), std::abs(xmin1),
135 std::abs(xmax2), std::abs(xmin2)});
136 const double epsy = 1.e-6 * std::max({std::abs(ymax1), std::abs(ymin1),
137 std::abs(ymax2), std::abs(ymin2)});
139 if (xmax1 + epsx < xmin2 || xmax2 + epsx < xmin1)
return false;
140 if (ymax1 + epsy < ymin2 || ymax2 + epsy < ymin1)
return false;
142 const unsigned int n1 = xp1.size();
143 const unsigned int n2 = xp2.size();
144 for (
unsigned int i = 0; i < n1; ++i) {
145 const double x0 = xp1[i];
146 const double y0 = yp1[i];
147 const unsigned int ii = i < n1 - 1 ? i + 1 : 0;
148 const double x1 = xp1[ii];
149 const double y1 = yp1[ii];
150 for (
unsigned int j = 0; j < n2; ++j) {
151 const unsigned int jj = j < n2 - 1 ? j + 1 : 0;
152 const double u0 = xp2[j];
153 const double v0 = yp2[j];
154 const double u1 = xp2[jj];
155 const double v1 = yp2[jj];
156 double xc = 0., yc = 0.;
157 if (!Crossing(x0, y0, x1, y1, u0, v0, u1, v1, xc, yc))
continue;
158 if ((OnLine(x0, y0, x1, y1, u0, v0) || OnLine(x0, y0, x1, y1, u1, v1)) &&
159 (OnLine(u0, v0, u1, v1, x0, y0) || OnLine(u0, v0, u1, v1, x1, y1))) {
169bool Enclosed(
const std::vector<double>& xp1,
170 const std::vector<double>& yp1,
171 const std::vector<double>& xp2,
172 const std::vector<double>& yp2) {
174 const double xmin1 = *std::min_element(std::begin(xp1), std::end(xp1));
175 const double ymin1 = *std::min_element(std::begin(yp1), std::end(yp1));
176 const double xmax1 = *std::max_element(std::begin(xp1), std::end(xp1));
177 const double ymax1 = *std::max_element(std::begin(yp1), std::end(yp1));
179 const double xmin2 = *std::min_element(std::begin(xp2), std::end(xp2));
180 const double ymin2 = *std::min_element(std::begin(yp2), std::end(yp2));
181 const double xmax2 = *std::max_element(std::begin(xp2), std::end(xp2));
182 const double ymax2 = *std::max_element(std::begin(yp2), std::end(yp2));
184 const double epsx = 1.e-6 * std::max({std::abs(xmax1), std::abs(xmin1),
185 std::abs(xmax2), std::abs(xmin2)});
186 const double epsy = 1.e-6 * std::max({std::abs(ymax1), std::abs(ymin1),
187 std::abs(ymax2), std::abs(ymin2)});
189 if (xmax1 + epsx < xmin2 || xmax2 + epsx < xmin1)
return false;
190 if (ymax1 + epsy < ymin2 || ymax2 + epsy < ymin1)
return false;
191 if (xmin1 + epsx < xmin2 || xmax1 > xmax2 + epsx)
return false;
192 if (ymin1 + epsy < ymin2 || ymax1 > ymax2 + epsy)
return false;
194 const unsigned int n1 = xp1.size();
195 for (
unsigned int i = 0; i < n1; ++i) {
196 bool inside =
false, edge =
false;
198 if (!inside)
return false;
207const double ComponentNeBem2d::InvEpsilon0 = 1. / VacuumPermittivity;
208const double ComponentNeBem2d::InvTwoPiEpsilon0 = 1. / TwoPiEpsilon0;
215 const double z,
double& ex,
double& ey,
216 double& ez,
double& v,
Medium*& m,
218 ex = ey = ez = v = 0.;
221 if (m_useRangeZ && (z < m_zmin || z > m_zmax)) {
236 for (
const auto& region : m_regions) {
237 bool inside =
false, edge =
false;
239 if (inside || edge) {
240 v = region.bc.second;
249 std::cerr <<
m_className <<
"::ElectricField: Initialisation failed.\n";
256 const unsigned int nWires = m_wires.size();
257 for (
unsigned int i = 0; i < nWires; ++i) {
258 const double dx = x - m_wires[i].x;
259 const double dy = y - m_wires[i].y;
260 if (dx * dx + dy * dy < m_wires[i].r * m_wires[i].r) {
268 for (
const auto& element : m_elements) {
269 const double cphi = element.cphi;
270 const double sphi = element.sphi;
272 double xL = 0., yL = 0.;
273 ToLocal(x - element.x, y - element.y, cphi, sphi, xL, yL);
275 v += LinePotential(element.a, xL, yL) * element.q;
277 double fx = 0., fy = 0.;
278 LineFlux(element.a, xL, yL, fx, fy);
280 ToGlobal(fx, fy, cphi, sphi, fx, fy);
281 ex += fx * element.q;
282 ey += fy * element.q;
286 for (
const auto& wire : m_wires) {
288 v += WirePotential(wire.r, x - wire.x, y - wire.y) * wire.q;
290 double fx = 0., fy = 0.;
291 WireFlux(wire.x, x - wire.x, y - wire.y, fx, fy);
299 const double z,
double& ex,
double& ey,
300 double& ez,
Medium*& m,
int& status) {
306 bool gotValue =
false;
307 for (
const auto& region : m_regions) {
308 if (region.bc.first != Voltage)
continue;
310 vmin = vmax = region.bc.second;
313 vmin = std::min(vmin, region.bc.second);
314 vmax = std::max(vmax, region.bc.second);
318 for (
const auto& segment : m_segments) {
319 if (segment.bc.first != Voltage)
continue;
321 vmin = vmax = segment.bc.second;
324 vmin = std::min(vmin, segment.bc.second);
325 vmax = std::max(vmax, segment.bc.second);
329 for (
const auto& wire : m_wires) {
331 vmin = vmax = wire.v;
334 vmin = std::min(vmin, wire.v);
335 vmax = std::max(vmax, wire.v);
345 for (
const auto& region : m_regions) {
346 bool inside =
false, edge =
false;
348 if (inside || edge)
return region.medium;
354 double& xmin,
double& ymin,
double& zmin,
355 double& xmax,
double& ymax,
double& zmax) {
362 bool gotValue =
false;
363 for (
const auto& region : m_regions) {
364 const auto& xv = region.xv;
365 const auto& yv = region.yv;
367 xmin = *std::min_element(std::begin(xv), std::end(xv));
368 ymin = *std::min_element(std::begin(yv), std::end(yv));
369 xmax = *std::max_element(std::begin(xv), std::end(xv));
370 ymax = *std::max_element(std::begin(yv), std::end(yv));
373 xmin = std::min(*std::min_element(std::begin(xv), std::end(xv)), xmin);
374 ymin = std::min(*std::min_element(std::begin(yv), std::end(yv)), ymin);
375 xmax = std::max(*std::max_element(std::begin(xv), std::end(xv)), xmax);
376 ymax = std::max(*std::max_element(std::begin(yv), std::end(yv)), ymax);
379 for (
const auto& seg : m_segments) {
381 xmin = std::min(seg.x0[0], seg.x1[0]);
382 xmax = std::max(seg.x0[0], seg.x1[0]);
383 ymin = std::min(seg.x0[1], seg.x1[1]);
384 ymax = std::max(seg.x0[1], seg.x1[1]);
387 xmin = std::min({xmin, seg.x0[0], seg.x1[0]});
388 xmax = std::max({xmax, seg.x0[0], seg.x1[0]});
389 ymin = std::min({ymin, seg.x0[1], seg.x1[1]});
390 ymax = std::max({ymax, seg.x0[1], seg.x1[1]});
393 for (
const auto& wire : m_wires) {
395 xmin = xmax = wire.x;
396 ymin = ymax = wire.y;
398 xmin = std::min(xmin, wire.x);
399 xmax = std::max(xmax, wire.x);
400 ymin = std::min(ymin, wire.y);
401 ymax = std::max(ymax, wire.y);
408 const double z0,
const double x1,
409 const double y1,
const double z1,
410 double& xc,
double& yc,
double& zc,
411 const bool centre,
double& rc) {
416 if (m_wires.empty())
return false;
418 const double dx = x1 - x0;
419 const double dy = y1 - y0;
420 const double d2 = dx * dx + dy * dy;
422 if (d2 < Small)
return false;
423 const double invd2 = 1. / d2;
430 for (
const auto& wire : m_wires) {
431 const double xw = wire.x;
432 const double yw = wire.y;
434 const double xIn0 = dx * (xw - x0) + dy * (yw - y0);
436 if (xIn0 < 0.)
continue;
437 const double xIn1 = -(dx * (xw - x1) + dy * (yw - y1));
439 if (xIn1 < 0.)
continue;
441 const double xw0 = xw - x0;
442 const double xw1 = xw - x1;
443 const double yw0 = yw - y0;
444 const double yw1 = yw - y1;
445 const double dw02 = xw0 * xw0 + yw0 * yw0;
446 const double dw12 = xw1 * xw1 + yw1 * yw1;
447 if (xIn1 * xIn1 * dw02 > xIn0 * xIn0 * dw12) {
448 dMin2 = dw02 - xIn0 * xIn0 * invd2;
450 dMin2 = dw12 - xIn1 * xIn1 * invd2;
452 const double r2 = wire.r * wire.r;
460 const double p = -xIn0 * invd2;
461 const double q = (dw02 - r2) * invd2;
462 const double s = sqrt(p * p - q);
463 const double t = std::min(-p + s, -p - s);
466 zc = z0 + t * (z1 - z0);
476 const double y,
const double ,
477 double& xw,
double& yw,
double& rw) {
479 for (
const auto& wire : m_wires) {
481 if (q * wire.q > 0.)
continue;
482 const double dx = wire.x - x;
483 const double dy = wire.y - y;
484 const double rt = wire.r * wire.ntrap;
485 if (dx * dx + dy * dy < rt * rt) {
497 if (fabs(zmax - zmin) <= 0.) {
498 std::cerr <<
m_className <<
"::SetRangeZ: Zero range is not permitted.\n";
501 m_zmin = std::min(zmin, zmax);
502 m_zmax = std::max(zmin, zmax);
507 const double x1,
const double y1,
508 const double v,
const int ndiv) {
509 const double dx = x1 - x0;
510 const double dy = y1 - y0;
511 if (dx * dx + dy * dy < Small) {
512 std::cerr <<
m_className <<
"::AddSegment: Length must be > 0.\n";
517 segment.x0 = {x0, y0};
518 segment.x1 = {x1, y1};
519 segment.bc = std::make_pair(Voltage, v);
520 segment.region1 = -1;
521 segment.region2 = -1;
523 m_segments.push_back(std::move(segment));
527 << x0 <<
", " << y0 <<
") - (" << x1 <<
", " << y1 <<
")\n"
528 <<
" Potential: " << v <<
" V\n";
536 const double v,
const int ntrap) {
538 std::cerr <<
m_className <<
"::AddWire: Diameter must be > 0.\n";
542 std::cerr <<
m_className <<
"::AddWire: Nbr. of trap radii must be > 0.\n";
553 m_wires.push_back(std::move(wire));
557 <<
" Centre: (" << x <<
", " << y <<
")\n"
558 <<
" Diameter: " << d <<
" cm\n"
559 <<
" Potential: " << v <<
" V\n";
567 const std::vector<double>& yp,
568 Medium* medium,
const unsigned int bctype,
569 const double v,
const int ndiv) {
571 if (xp.size() != yp.size()) {
573 <<
" Mismatch between number of x- and y-coordinates.\n";
577 std::cerr <<
m_className <<
"::AddRegion: Too few points.\n";
580 if (bctype != 1 && bctype != 4) {
581 std::cerr <<
m_className <<
"::AddRegion: Invalid boundary condition.\n";
586 const unsigned int np = xp.size();
588 for (
unsigned int i0 = 0; i0 < np; ++i0) {
589 const unsigned int i1 = i0 < np - 1 ? i0 + 1 : 0;
590 for (
unsigned int j = 0; j < np - 3; ++j) {
591 const unsigned int j0 = i1 < np - 1 ? i1 + 1 : 0;
592 const unsigned int j1 = j0 < np - 1 ? j0 + 1 : 0;
593 double xc = 0., yc = 0.;
594 if (Crossing(xp[i0], yp[i0], xp[i1], yp[i1],
595 xp[j0], yp[j0], xp[j1], yp[j1], xc, yc)) {
596 std::cerr <<
m_className <<
"::AddRegion: Edges cross each other.\n";
602 std::vector<double> xv = xp;
603 std::vector<double> yv = yp;
604 const double xmin = *std::min_element(std::begin(xv), std::end(xv));
605 const double ymin = *std::min_element(std::begin(yv), std::end(yv));
606 const double xmax = *std::max_element(std::begin(xv), std::end(xv));
607 const double ymax = *std::max_element(std::begin(yv), std::end(yv));
609 const double epsx = 1.e-6 * std::max(std::abs(xmax), std::abs(xmin));
610 const double epsy = 1.e-6 * std::max(std::abs(ymax), std::abs(ymin));
613 if (std::abs(f) < std::max(1.e-10, epsx * epsy)) {
614 std::cerr <<
m_className <<
"::AddRegion: Degenerate polygon.\n";
619 std::cout <<
m_className <<
"::AddRegion: Reversing orientation.\n";
621 std::reverse(xv.begin(), xv.end());
622 std::reverse(yv.begin(), yv.end());
624 for (
const auto& region : m_regions) {
625 if (Intersecting(xv, yv, region.xv, region.yv)) {
627 <<
" Polygon intersects an existing region.\n";
634 region.medium = medium;
636 region.bc = std::make_pair(Voltage, v);
637 }
else if (bctype == 4) {
638 region.bc = std::make_pair(Dielectric, v);
642 m_regions.push_back(std::move(region));
648 std::cerr <<
m_className <<
"::SetNumberOfDivisions:\n"
649 <<
" Number of divisions must be greater than zero.\n";
659 std::cerr <<
m_className <<
"::SetNumberOfCollocationPoints:\n"
660 <<
" Number of points must be greater than zero.\n";
664 m_nCollocationPoints = ncoll;
670 std::cerr <<
m_className <<
"::SetMaxNumberOfIterations:\n"
671 <<
" Number of iterations must be greater than zero.\n";
674 m_nMaxIterations = niter;
678 std::vector<double>& xv,
679 std::vector<double>& yv,
680 Medium*& medium,
unsigned int& bctype,
682 if (i >= m_regions.size())
return false;
686 const auto& region = m_regions[i];
689 medium = region.medium;
690 bctype = region.bc.first == Voltage ? 1 : 4;
691 v = region.bc.second;
696 double& x0,
double& y0,
double& x1,
double& y1,
double& v)
const {
698 if (i >= m_segments.size())
return false;
699 const auto& seg = m_segments[i];
709 double& x,
double& y,
double& d,
double& v,
double& q)
const {
711 if (i >= m_wires.size())
return false;
712 const auto& wire = m_wires[i];
722 double& x0,
double& y0,
double& x1,
double& y1,
double& q)
const {
724 if (i >= m_elements.size())
return false;
725 const auto& element = m_elements[i];
726 ToGlobal(-element.a, 0., element.cphi, element.sphi, x0, y0);
727 ToGlobal( element.a, 0., element.cphi, element.sphi, x1, y1);
741 double vmin = 0., vmax = 0.;
744 <<
" Could not determine the voltage range.\n";
747 if (fabs(vmin - vmax) < 1.e-6 * (vmin + vmax)) {
749 <<
" All potentials are the same.\n";
755 const unsigned int nRegions = m_regions.size();
756 if (
m_debug) std::cout <<
" " << nRegions <<
" regions.\n";
757 std::vector<std::vector<unsigned int> > motherRegions(nRegions);
758 for (
unsigned int i = 0; i < nRegions; ++i) {
759 auto& region = m_regions[i];
761 for (
unsigned int j = 0; j < nRegions; ++j) {
762 if (i == j)
continue;
763 const auto& other = m_regions[j];
764 if (!Enclosed(region.xv, region.yv, other.xv, other.yv))
continue;
765 motherRegions[i].push_back(j);
767 std::cout <<
" Region " << i <<
" is enclosed by region "
771 region.depth = motherRegions[i].size();
774 std::vector<Segment> segments;
775 for (
unsigned int i = 0; i < nRegions; ++i) {
776 const auto& region = m_regions[i];
777 int outerRegion = -1;
778 for (
const unsigned int k : motherRegions[i]) {
779 if (outerRegion < 0) {
781 }
else if (m_regions[outerRegion].depth < m_regions[k].depth) {
786 const unsigned int n = region.xv.size();
787 for (
unsigned int j = 0; j < n; ++j) {
788 const unsigned int k = j < n - 1 ? j + 1 : 0;
790 seg.x0 = {region.xv[j], region.yv[j]};
791 seg.x1 = {region.xv[k], region.yv[k]};
793 seg.region2 = outerRegion;
795 seg.ndiv = region.ndiv;
796 segments.push_back(std::move(seg));
800 segments.insert(segments.end(), m_segments.begin(), m_segments.end());
801 const unsigned int nSegments = segments.size();
802 if (
m_debug) std::cout <<
" " << nSegments <<
" segments.\n";
803 std::vector<bool> done(nSegments,
false);
805 for (
unsigned int i = 0; i < nSegments; ++i) {
806 if (done[i])
continue;
808 std::cout <<
" Segment " << i <<
". ("
809 << segments[i].x0[0] <<
", " << segments[i].x0[1] <<
") - ("
810 << segments[i].x1[0] <<
", " << segments[i].x1[1] <<
")\n";
812 const double x0 = segments[i].x0[0];
813 const double x1 = segments[i].x1[0];
814 const double y0 = segments[i].x0[1];
815 const double y1 = segments[i].x1[1];
817 std::vector<Segment> newSegments;
818 for (
unsigned int j = i + 1; j < nSegments; ++j) {
819 const double u0 = segments[j].x0[0];
820 const double u1 = segments[j].x1[0];
821 const double v0 = segments[j].x0[1];
822 const double v1 = segments[j].x1[1];
823 const double epsx = std::max(1.e-10 * std::max({fabs(x0), fabs(x1),
824 fabs(u0), fabs(u1)}),
826 const double epsy = std::max(1.e-10 * std::max({fabs(y0), fabs(y1),
827 fabs(v0), fabs(v1)}),
829 const double a00 = y1 - y0;
830 const double a01 = v1 - v0;
831 const double a10 = x0 - x1;
832 const double a11 = u0 - u1;
833 const double det = a00 * a11 - a10 * a01;
834 const double tol = epsx * epsy;
836 if (std::abs(det) > tol)
continue;
838 if (std::abs(x0 * (y1 - v0) + x1 * (v0 - y0) + u0 * (y0 - y1)) > tol) {
841 newSegments.push_back(segments[j]);
844 newSegments.push_back(segments[i]);
845 if (newSegments.size() > 1) {
847 std::cout <<
" Determining overlaps of " << newSegments.size()
848 <<
" collinear segments.\n";
850 EliminateOverlaps(newSegments);
852 std::cout <<
" " << newSegments.size()
853 <<
" segments after splitting/merging.\n";
856 for (
const auto& segment : newSegments) {
858 if (segment.bc.first == Dielectric) {
860 const int reg1 = segment.region1;
861 const int reg2 = segment.region2;
865 }
else if (m_regions[reg1].medium) {
866 eps1 = m_regions[reg1].medium->GetDielectricConstant();
871 }
else if (m_regions[reg2].medium) {
872 eps2 = m_regions[reg2].medium->GetDielectricConstant();
874 if (fabs(eps1 - eps2) < 1.e-6 * (1. + fabs(eps1) + fabs(eps2))) {
875 if (
m_debug) std::cout <<
" Same epsilon. Skip.\n";
879 lambda = (eps2 - eps1) / (eps1 + eps2);
880 if (
m_debug) std::cout <<
" Lambda = " << lambda <<
"\n";
882 const int ndiv = segment.ndiv <= 0 ? m_nDivisions : segment.ndiv;
883 Discretise(segment, m_elements, lambda, ndiv);
886 std::vector<std::vector<double> > influenceMatrix;
887 std::vector<std::vector<double> > inverseMatrix;
889 bool converged =
false;
890 unsigned int nIter = 0;
894 std::cout <<
m_className <<
"::Initialise: Iteration " << nIter <<
"\n";
896 const unsigned int nElements = m_elements.size();
897 const unsigned int nEntries = nElements + m_wires.size() + 1;
899 std::cout <<
" " << nElements <<
" elements.\n"
900 <<
" Matrix has " << nEntries <<
" rows/columns.\n";
903 influenceMatrix.assign(nEntries, std::vector<double>(nEntries, 0.));
904 if (!ComputeInfluenceMatrix(influenceMatrix)) {
906 <<
" Error computing the influence matrix.\n";
911 inverseMatrix.assign(nEntries, std::vector<double>(nEntries, 0.));
912 if (!InvertMatrix(influenceMatrix, inverseMatrix)) {
913 std::cerr <<
m_className <<
"::Initialise: Matrix inversion failed.\n";
916 if (
m_debug) std::cout <<
" Matrix inversion ok.\n";
919 std::vector<double> boundaryConditions(nEntries, 0.);
920 for (
unsigned int i = 0; i < nElements; ++i) {
921 if (m_elements[i].bc.first != Voltage)
continue;
922 boundaryConditions[i] = m_elements[i].bc.second;
924 const unsigned int nWires = m_wires.size();
925 for (
unsigned int i = 0; i < nWires; ++i) {
926 boundaryConditions[nElements + i] = m_wires[i].v;
930 if (!Solve(inverseMatrix, boundaryConditions)) {
931 std::cerr <<
m_className <<
"::Initialise: Solution failed.\n";
934 if (
m_debug) std::cout <<
" Solution ok.\n";
935 const double tol = 1.e-6 * fabs(vmax - vmin);
936 std::vector<bool> ok(nElements,
true);
937 converged = CheckConvergence(tol, ok);
938 if (!m_autoSize)
break;
939 if (nIter >= m_nMaxIterations)
break;
940 for (
unsigned int j = 0; j < nElements; ++j) {
942 SplitElement(m_elements[j], m_elements);
943 if (
m_debug) std::cout <<
" Splitting element " << j <<
".\n";
948 std::sort(m_regions.begin(), m_regions.end(),
949 [](
const Region& lhs,
const Region& rhs) {
950 return (lhs.depth > rhs.depth);
956void ComponentNeBem2d::EliminateOverlaps(std::vector<Segment>& segments) {
958 if (segments.empty())
return;
959 const unsigned int nIn = segments.size();
961 std::array<double, 2> x0 = segments[0].x0;
962 std::array<double, 2> x1 = segments[0].x1;
964 const unsigned int ic = fabs(x1[1] - x0[1]) > fabs(x1[0] - x0[0]) ? 1 : 0;
965 std::vector<bool> swapped(nIn,
false);
966 for (
unsigned int i = 0; i < nIn; ++i) {
967 const auto& seg = segments[i];
968 std::array<double, 2> u0 = seg.x0;
969 std::array<double, 2> u1 = seg.x1;
970 if (u0[ic] > u1[ic]) {
975 if (u0[ic] < x0[ic]) x0 = u0;
976 if (u1[ic] > x1[ic]) x1 = u1;
978 const std::array<double, 2> d = {x1[0] - x0[0], x1[1] - x0[1]};
981 std::vector<std::pair<double, std::vector<unsigned int> > > points;
982 for (
unsigned int i = 0; i < nIn; ++i) {
983 for (
const auto& xl : {segments[i].x0, segments[i].x1}) {
984 const std::array<double, 2> d0 = {xl[0] - x0[0], xl[1] - x0[1]};
985 const std::array<double, 2> d1 = {x1[0] - xl[0], x1[1] - xl[1]};
987 if (Mag2(d0) < Mag2(d1)) {
989 lambda = d0[ic] / d[ic];
992 lambda = 1. - d1[ic] / d[ic];
996 for (
auto& point : points) {
997 if (
fabs(point.first - lambda) < 1.e-6) {
999 point.second.push_back(i);
1003 if (found)
continue;
1004 points.push_back(std::make_pair(lambda,
1005 std::vector<unsigned int>({i})));
1009 std::sort(std::begin(points), std::end(points));
1011 std::vector<Segment> newSegments;
1012 const unsigned int nPoints = points.size();
1013 std::array<double, 2> xl = {x0[0] + points[0].first * d[0],
1014 x0[1] + points[0].first * d[1]};
1015 std::vector<unsigned int> left = points[0].second;
1016 for (
unsigned int i = 1; i < nPoints; ++i) {
1017 Segment seg = segments[left.front()];
1019 xl = {x0[0] + points[i].first * d[0], x0[1] + points[i].first * d[1]};
1021 if (swapped[left.front()]) std::swap(seg.x0, seg.x1);
1023 if (left.size() > 1) {
1024 for (
unsigned int j = 1; j < left.size(); ++j) {
1025 const auto& other = segments[left[j]];
1026 if (seg.bc.first == Dielectric) {
1027 if (other.bc.first == Dielectric) {
1029 if ((seg.x1[ic] - seg.x0[ic]) * (other.x1[ic] - other.x0[ic]) > 0) {
1033 seg.region2 = other.region1;
1037 }
else if (seg.bc.first != other.bc.first) {
1038 std::cerr <<
m_className <<
"::EliminateOverlaps:\n"
1039 <<
" Warning: conflicting boundary conditions.\n";
1043 newSegments.push_back(std::move(seg));
1044 for (
unsigned int k : points[i].second) {
1045 const auto it = std::find(left.begin(), left.end(), k);
1046 if (it == left.end()) {
1053 segments.swap(newSegments);
1056bool ComponentNeBem2d::Discretise(
const Segment& seg,
1057 std::vector<Element>& elements,
1058 const double lambda,
1059 const unsigned int ndiv) {
1062 std::cerr <<
m_className <<
"::Discretise: Number of elements < 1.\n";
1065 const double phi = atan2(seg.x1[1] - seg.x0[1], seg.x1[0] - seg.x0[0]);
1066 const double cphi =
cos(phi);
1067 const double sphi =
sin(phi);
1068 const double dx = (seg.x1[0] - seg.x0[0]) / ndiv;
1069 const double dy = (seg.x1[1] - seg.x0[1]) / ndiv;
1070 const double a = 0.5 *
sqrt(dx * dx + dy * dy);
1071 double x = seg.x0[0] - 0.5 * dx;
1072 double y = seg.x0[1] - 0.5 * dy;
1073 for (
unsigned int i = 0; i < ndiv; ++i) {
1077 element.cphi = cphi;
1078 element.sphi = sphi;
1082 element.bc = seg.bc;
1083 element.lambda = lambda;
1084 elements.push_back(std::move(element));
1089bool ComponentNeBem2d::ComputeInfluenceMatrix(
1090 std::vector<std::vector<double> >& infmat)
const {
1092 const unsigned int nL = m_elements.size();
1093 const unsigned int nE = nL + m_wires.size();
1095 for (
unsigned int iF = 0; iF < nE; ++iF) {
1096 const auto bcF = iF < nL ? m_elements[iF].bc.first : Voltage;
1097 const double cphiF = iF < nL ? m_elements[iF].cphi : 1.;
1098 const double sphiF = iF < nL ? m_elements[iF].sphi : 0.;
1100 const double xF = iF < nL ? m_elements[iF].x : m_wires[iF - nL].x;
1101 const double yF = iF < nL ? m_elements[iF].y : m_wires[iF - nL].y;
1104 for (
unsigned int jS = 0; jS < nE; ++jS) {
1106 double infCoeff = 0.;
1109 const auto& src = m_elements[jS];
1110 double xL = 0., yL = 0.;
1111 ToLocal(xF - src.x, yF - src.y, src.cphi, src.sphi, xL, yL);
1112 if (bcF == Voltage) {
1113 infCoeff = LinePotential(src.a, xL, yL);
1114 }
else if (bcF == Dielectric) {
1119 infCoeff = 1. / (2. * src.lambda * VacuumPermittivity);
1122 double fx = 0., fy = 0.;
1123 LineFlux(src.a, xL, yL, fx, fy);
1125 ToGlobal(fx, fy, src.cphi, src.sphi, fx, fy);
1127 ToLocal(fx, fy, cphiF, sphiF, fx, fy);
1133 const auto& src = m_wires[jS - nL];
1134 if (bcF == Voltage) {
1135 infCoeff = WirePotential(src.r, xF - src.x, yF - src.y);
1136 }
else if (bcF == Dielectric) {
1137 double fx = 0., fy = 0.;
1138 WireFlux(src.r, xF - src.x, yF - src.y, fx, fy);
1139 ToLocal(fx, fy, cphiF, sphiF, fx, fy);
1143 infmat[iF][jS] = infCoeff;
1148 for (
unsigned int i = 0; i < nE; ++i) {
1150 infmat[nE][i] = m_elements[i].a;
1152 infmat[nE][i] = m_wires[i - nL].r;
1156 infmat[nE][nE] = 0.;
1161void ComponentNeBem2d::SplitElement(Element& oldElement,
1162 std::vector<Element>& elements) {
1163 oldElement.a *= 0.5;
1165 Element newElement = oldElement;
1166 double dx = 0., dy = 0.;
1167 ToGlobal(newElement.a, 0., newElement.cphi, newElement.sphi, dx, dy);
1173 elements.push_back(std::move(newElement));
1176bool ComponentNeBem2d::InvertMatrix(
1177 std::vector<std::vector<double> >& influenceMatrix,
1178 std::vector<std::vector<double> >& inverseMatrix)
const {
1180 const unsigned int nEntries = influenceMatrix.size();
1183 std::vector<double> col(nEntries, 0.);
1184 std::vector<int> index(nEntries, 0);
1187 if (!LUDecomposition(influenceMatrix, index)) {
1188 std::cerr <<
m_className <<
"::InvertMatrix: LU decomposition failed.\n";
1193 inverseMatrix.assign(nEntries, std::vector<double>(nEntries, 0.));
1195 for (
unsigned int j = 0; j < nEntries; ++j) {
1196 col.assign(nEntries, 0.);
1198 LUSubstitution(influenceMatrix, index, col);
1199 for (
unsigned int i = 0; i < nEntries; ++i) inverseMatrix[i][j] = col[i];
1203 influenceMatrix.clear();
1208bool ComponentNeBem2d::LUDecomposition(std::vector<std::vector<double> >& mat,
1209 std::vector<int>& index)
const {
1215 const unsigned int n = m_elements.size() + m_wires.size();
1217 std::vector<double> v(n, 0.);
1220 for (
unsigned int i = 0; i < n; ++i) {
1222 for (
unsigned int j = 0; j < n; ++j) {
1223 big = std::max(big,
fabs(mat[i][j]));
1225 if (big == 0.)
return false;
1231 unsigned int imax = 0;
1232 for (
unsigned int j = 0; j < n; ++j) {
1233 for (
unsigned int i = 0; i < j; ++i) {
1234 double sum = mat[i][j];
1235 for (
unsigned int k = 0; k < i; ++k) {
1236 sum -= mat[i][k] * mat[k][j];
1242 for (
unsigned int i = j; i < n; ++i) {
1243 double sum = mat[i][j];
1244 for (
unsigned int k = 0; k < j; ++k) {
1245 sum -= mat[i][k] * mat[k][j];
1249 const double dum = v[i] *
fabs(sum);
1257 for (
unsigned k = 0; k < n; ++k) {
1258 const double dum = mat[imax][k];
1259 mat[imax][k] = mat[j][k];
1266 if (mat[j][j] == 0.) mat[j][j] = Small;
1269 const double dum = 1. / mat[j][j];
1270 for (
unsigned int i = j + 1; i < n; ++i) {
1279void ComponentNeBem2d::LUSubstitution(
1280 const std::vector<std::vector<double> >& mat,
const std::vector<int>& index,
1281 std::vector<double>& col)
const {
1283 const unsigned int n = m_elements.size() + m_wires.size();
1284 unsigned int ii = 0;
1286 for (
unsigned i = 0; i < n; ++i) {
1287 const unsigned int ip = index[i];
1288 double sum = col[ip];
1291 for (
unsigned j = ii - 1; j < i; ++j) {
1292 sum -= mat[i][j] * col[j];
1294 }
else if (sum != 0.) {
1301 for (
int i = n - 1; i >= 0; i--) {
1302 double sum = col[i];
1303 for (
unsigned j = i + 1; j < n; ++j) {
1304 sum -= mat[i][j] * col[j];
1306 col[i] = sum / mat[i][i];
1310bool ComponentNeBem2d::Solve(
const std::vector<std::vector<double> >& invmat,
1311 const std::vector<double>& bc) {
1312 const unsigned int nEntries = bc.size();
1313 const unsigned int nElements = m_elements.size();
1314 for (
unsigned int i = 0; i < nElements; ++i) {
1315 double solution = 0.;
1316 for (
unsigned int j = 0; j < nEntries; ++j) {
1317 solution += invmat[i][j] * bc[j];
1319 m_elements[i].q = solution;
1321 const unsigned int nWires = m_wires.size();
1322 for (
unsigned int i = 0; i < nWires; ++i) {
1323 double solution = 0.;
1324 for (
unsigned int j = 0; j < nEntries; ++j) {
1325 solution += invmat[nElements + i][j] * bc[j];
1327 m_wires[i].q = solution;
1331 std::cout <<
m_className <<
"::Solve:\n Element Solution\n";
1332 for (
unsigned int i = 0; i < nElements; ++i) {
1333 std::printf(
" %8u %15.5f\n", i, m_elements[i].q);
1335 if (!m_wires.empty()) {
1336 std::cout <<
" Wire Solution\n";
1337 for (
unsigned int i = 0; i < nWires; ++i) {
1338 std::printf(
" %8u %15.5f\n", i, m_wires[i].q);
1345bool ComponentNeBem2d::CheckConvergence(
const double tol,
1346 std::vector<bool>& ok) {
1350 std::vector<double> v(m_nCollocationPoints, 0.);
1351 std::vector<double> n(m_nCollocationPoints, 0.);
1354 std::cout <<
m_className <<
"::CheckConvergence:\n"
1355 <<
" element # type LHS RHS\n";
1357 const double scale = 1. / m_nCollocationPoints;
1359 for (
const auto& tgt : m_elements) {
1360 v.assign(m_nCollocationPoints, 0.);
1361 n.assign(m_nCollocationPoints, 0.);
1362 double dx = 0., dy = 0.;
1363 ToGlobal(2 * tgt.a, 0., tgt.cphi, tgt.sphi, dx, dy);
1364 const double x0 = tgt.x - 0.5 * dx;
1365 const double y0 = tgt.y - 0.5 * dy;
1367 for (
unsigned int k = 0; k < m_nCollocationPoints; ++k) {
1370 if (m_randomCollocation) {
1375 const double s = (k + 1.) / (m_nCollocationPoints + 1.);
1380 for (
const auto& src : m_elements) {
1381 double xL = 0., yL = 0.;
1383 ToLocal(xG - src.x, yG - src.y, src.cphi, src.sphi, xL, yL);
1385 v[k] += LinePotential(src.a, xL, yL) * src.q;
1387 double fx = 0., fy = 0.;
1388 LineFlux(src.a, xL, yL, fx, fy);
1390 ToGlobal(fx, fy, src.cphi, src.sphi, fx, fy);
1392 ToLocal(fx, fy, tgt.cphi, tgt.sphi, fx, fy);
1396 for (
const auto& src : m_wires) {
1398 v[k] += WirePotential(src.r, xG - src.x, yG - src.y) * src.q;
1400 double fx = 0., fy = 0.;
1401 WireFlux(src.r, xG - src.x, yG - src.y, fx, fy);
1403 ToLocal(fx, fy, tgt.cphi, tgt.sphi, fx, fy);
1407 const double v0 = scale * std::accumulate(v.begin(), v.end(), 0.);
1408 const double n0 = scale * std::accumulate(n.begin(), n.end(), 0.);
1410 if (tgt.bc.first == Voltage) {
1411 const double dv = v0 - tgt.bc.second;
1412 if (
fabs(dv) > tol) ok[i] =
false;
1414 std::printf(
" %8u cond. %15.5f %15.5f %15.5f\n",
1415 i, v0, tgt.bc.second, dv);
1417 }
else if (tgt.bc.first == Dielectric) {
1420 n1 = n0 + 0.5 * InvEpsilon0 * tgt.q / tgt.lambda;
1421 if (
m_debug) std::printf(
" %8u diel. %15.5f %15.5f\n", i, n0, n1);
1426 for (
const auto& tgt : m_wires) {
1427 v.assign(m_nCollocationPoints, 0.);
1431 for (
unsigned int k = 0; k < m_nCollocationPoints; ++k) {
1433 const double xG = x0 + tgt.r *
cos(phi);
1434 const double yG = y0 + tgt.r *
sin(phi);
1436 for (
const auto& src : m_elements) {
1438 double xL = 0., yL = 0.;
1439 ToLocal(xG - src.x, yG - src.y, src.cphi, src.sphi, xL, yL);
1441 v[k] += LinePotential(src.a, xL, yL) * src.q;
1443 for (
const auto& src : m_wires) {
1444 v[k] += WirePotential(src.r, xG - src.x, yG - src.y) * src.q;
1447 const double v0 = scale * std::accumulate(v.begin(), v.end(), 0.);
1449 std::printf(
" %8u wire %15.5f %15.5f\n", i, v0, tgt.v);
1457double ComponentNeBem2d::LinePotential(
const double a,
const double x,
1458 const double y)
const {
1460 const double amx = a -
x;
1461 const double apx = a +
x;
1462 if (
fabs(y) > Small) {
1463 const double y2 =
y *
y;
1464 p = 2. * a -
y * (atan(amx / y) + atan(apx / y)) -
1465 0.5 * amx * log(amx * amx + y2) - 0.5 * apx * log(apx * apx + y2);
1466 }
else if (
fabs(x) != a) {
1467 p = 2. * a - 0.5 * amx * log(amx * amx) - 0.5 * apx * log(apx * apx);
1469 p = 2. * a * (1. - log(2. * a));
1472 return InvTwoPiEpsilon0 * p;
1475double ComponentNeBem2d::WirePotential(
const double r0,
const double x,
1476 const double y)
const {
1477 const double r =
sqrt(x * x + y * y);
1479 return -log(r) * r0 * InvEpsilon0;
1483 return -log(r0) * r0 * InvEpsilon0;
1486void ComponentNeBem2d::LineFlux(
const double a,
const double x,
const double y,
1487 double& ex,
double& ey)
const {
1488 const double amx = a -
x;
1489 const double apx = a +
x;
1491 const double y2 =
y *
y;
1492 ex = 0.5 * log((apx * apx + y2) / (amx * amx + y2));
1493 ey = atan(amx / y) + atan(apx / y);
1494 }
else if (
fabs(x) != a) {
1495 ex = 0.5 * log(apx * apx / (amx * amx));
1499 constexpr double eps2 = 1.e-24;
1500 ex = 0.25 * log(
pow(apx * apx - eps2, 2) /
pow(amx * amx - eps2, 2));
1503 ex *= InvTwoPiEpsilon0;
1504 ey *= InvTwoPiEpsilon0;
1507void ComponentNeBem2d::WireFlux(
const double r0,
const double x,
const double y,
1508 double& ex,
double& ey)
const {
1509 const double r02 = r0 * r0;
1510 const double r2 =
x *
x +
y *
y;
1514 }
else if (r2 == r02) {
1526void ComponentNeBem2d::Reset() {
1534void ComponentNeBem2d::UpdatePeriodicity() {
1535 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
1536 <<
" Periodicities are not supported.\n";
1539void ComponentNeBem2d::ToLocal(
const double xIn,
const double yIn,
1540 const double cphi,
const double sphi,
1541 double& xOut,
double& yOut)
const {
1542 if (
fabs(sphi) < 1.e-12) {
1547 xOut = +cphi * xIn + sphi * yIn;
1548 yOut = -sphi * xIn + cphi * yIn;
1551void ComponentNeBem2d::ToGlobal(
const double xIn,
const double yIn,
1552 const double cphi,
const double sphi,
1553 double& xOut,
double& yOut)
const {
1554 if (
fabs(sphi) < 1.e-12) {
1559 xOut = cphi * xIn - sphi * yIn;
1560 yOut = sphi * xIn + cphi * yIn;
Abstract base class for components.
GeometryBase * m_geometry
Pointer to the geometry.
bool m_ready
Ready for use?
std::string m_className
Class name.
bool m_debug
Switch on/off debugging messages.
void SetNumberOfDivisions(const unsigned int ndiv)
Set the default number of elements per segment.
bool AddSegment(const double x0, const double y0, const double x1, const double y1, const double v, const int ndiv=-1)
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status) override
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
bool GetSegment(const unsigned int i, double &x0, double &y0, double &x1, double &x2, double &v) const
Return the coordinates and voltage of a given straight-line segment.
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
void SetRangeZ(const double zmin, const double zmax)
Set the extent of the drift region along z.
void SetNumberOfCollocationPoints(const unsigned int ncoll)
bool IsWireCrossed(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc, const bool centre, double &rc) override
bool Initialise()
Discretise the geometry and compute the solution.
bool GetRegion(const unsigned int i, std::vector< double > &xv, std::vector< double > &yv, Medium *&medium, unsigned int &bctype, double &v)
Return the properties of a given region.
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
bool GetWire(const unsigned int i, double &x, double &y, double &d, double &v, double &q) const
Return the coordinates, diameter, potential and charge of a given wire.
bool AddWire(const double x, const double y, const double d, const double v, const int ntrap=5)
ComponentNeBem2d()
Constructor.
void SetMaxNumberOfIterations(const unsigned int niter)
bool IsInTrapRadius(const double q0, const double x0, const double y0, const double z0, double &xw, double &yx, double &rw) override
bool AddRegion(const std::vector< double > &xp, const std::vector< double > &yp, Medium *medium, const unsigned int bctype=4, const double v=0., const int ndiv=-1)
bool GetElement(const unsigned int i, double &x0, double &y0, double &x1, double &y1, double &q) const
Return the coordinates and charge of a given boundary element.
virtual Medium * GetMedium(const double x, const double y, const double z) const =0
Retrieve the medium at a given point.
virtual bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)=0
Get the bounding box (envelope of the geometry).
Abstract base class for media.
double GetDielectricConstant() const
Get the relative static dielectric constant.
virtual bool IsConductor() const
Is this medium a conductor?
double Area(const std::vector< double > &xp, const std::vector< double > &yp)
Determine the (signed) area of a polygon.
void Inside(const std::vector< double > &xpl, const std::vector< double > &ypl, const double x, const double y, bool &inside, bool &edge)
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
DoubleAc cos(const DoubleAc &f)
DoubleAc pow(const DoubleAc &f, double p)
DoubleAc fabs(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)