17double Mag2(
const std::array<double, 2>& x) {
18 return x[0] *
x[0] +
x[1] *
x[1];
23bool OnLine(
const double x1,
const double y1,
const double x2,
const double y2,
24 const double u,
const double v) {
26 double epsx = 1.e-10 * std::max({
fabs(x1),
fabs(x2),
fabs(u)});
27 double epsy = 1.e-10 * std::max({
fabs(y1),
fabs(y2),
fabs(v)});
28 epsx = std::max(1.e-10, epsx);
29 epsy = std::max(1.e-10, epsy);
31 if ((
fabs(x1 - u) <= epsx &&
fabs(y1 - v) <= epsy) ||
32 (
fabs(x2 - u) <= epsx &&
fabs(y2 - v) <= epsy)) {
35 }
else if (
fabs(x1 - x2) <= epsx &&
fabs(y1 - y2) <= epsy) {
39 double xc = 0., yc = 0.;
42 const double dx = (x2 - x1);
43 const double dy = (y2 - y1);
44 const double xl = ((u - x1) * dx + (v - y1) * dy) / (dx * dx + dy * dy);
57 const double dx = (x1 - x2);
58 const double dy = (y1 - y2);
59 const double xl = ((u - x2) * dx + (v - y2) * dy) / (dx * dx + dy * dy);
72 if (
fabs(u - xc) < epsx &&
fabs(v - yc) < epsy) {
80bool Crossing(
const double x1,
const double y1,
const double x2,
81 const double y2,
const double u1,
const double v1,
82 const double u2,
const double v2,
double& xc,
double& yc) {
87 std::array<std::array<double, 2>, 2> a;
92 const double det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
94 const double epsx = std::max(1.e-10, 1.e-10 * std::max({
fabs(x1),
fabs(x2),
96 const double epsy = std::max(1.e-10, 1.e-10 * std::max({
fabs(y1),
fabs(y2),
99 if (
fabs(det) < epsx * epsy)
return false;
102 const double aux = a[1][1];
103 a[1][1] = a[0][0] / det;
105 a[1][0] = -a[1][0] / det;
106 a[0][1] = -a[0][1] / det;
108 xc = a[0][0] * (x1 * y2 - x2 * y1) + a[1][0] * (u1 * v2 - u2 * v1);
109 yc = a[0][1] * (x1 * y2 - x2 * y1) + a[1][1] * (u1 * v2 - u2 * v1);
111 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc)) {
120bool Intersecting(
const std::vector<double>& xp1,
121 const std::vector<double>& yp1,
122 const std::vector<double>& xp2,
123 const std::vector<double>& yp2) {
125 const double xmin1 = *std::min_element(std::begin(xp1), std::end(xp1));
126 const double ymin1 = *std::min_element(std::begin(yp1), std::end(yp1));
127 const double xmax1 = *std::max_element(std::begin(xp1), std::end(xp1));
128 const double ymax1 = *std::max_element(std::begin(yp1), std::end(yp1));
130 const double xmin2 = *std::min_element(std::begin(xp2), std::end(xp2));
131 const double ymin2 = *std::min_element(std::begin(yp2), std::end(yp2));
132 const double xmax2 = *std::max_element(std::begin(xp2), std::end(xp2));
133 const double ymax2 = *std::max_element(std::begin(yp2), std::end(yp2));
135 const double epsx = 1.e-6 * std::max({std::abs(xmax1), std::abs(xmin1),
136 std::abs(xmax2), std::abs(xmin2)});
137 const double epsy = 1.e-6 * std::max({std::abs(ymax1), std::abs(ymin1),
138 std::abs(ymax2), std::abs(ymin2)});
140 if (xmax1 + epsx < xmin2 || xmax2 + epsx < xmin1)
return false;
141 if (ymax1 + epsy < ymin2 || ymax2 + epsy < ymin1)
return false;
143 const unsigned int n1 = xp1.size();
144 const unsigned int n2 = xp2.size();
145 for (
unsigned int i = 0; i < n1; ++i) {
146 const double x0 = xp1[i];
147 const double y0 = yp1[i];
148 const unsigned int ii = i < n1 - 1 ? i + 1 : 0;
149 const double x1 = xp1[ii];
150 const double y1 = yp1[ii];
151 for (
unsigned int j = 0; j < n2; ++j) {
152 const unsigned int jj = j < n2 - 1 ? j + 1 : 0;
153 const double u0 = xp2[j];
154 const double v0 = yp2[j];
155 const double u1 = xp2[jj];
156 const double v1 = yp2[jj];
157 double xc = 0., yc = 0.;
158 if (!Crossing(x0, y0, x1, y1, u0, v0, u1, v1, xc, yc))
continue;
159 if ((OnLine(x0, y0, x1, y1, u0, v0) || OnLine(x0, y0, x1, y1, u1, v1)) &&
160 (OnLine(u0, v0, u1, v1, x0, y0) || OnLine(u0, v0, u1, v1, x1, y1))) {
170bool Enclosed(
const std::vector<double>& xp1,
171 const std::vector<double>& yp1,
172 const std::vector<double>& xp2,
173 const std::vector<double>& yp2) {
175 const double xmin1 = *std::min_element(std::begin(xp1), std::end(xp1));
176 const double ymin1 = *std::min_element(std::begin(yp1), std::end(yp1));
177 const double xmax1 = *std::max_element(std::begin(xp1), std::end(xp1));
178 const double ymax1 = *std::max_element(std::begin(yp1), std::end(yp1));
180 const double xmin2 = *std::min_element(std::begin(xp2), std::end(xp2));
181 const double ymin2 = *std::min_element(std::begin(yp2), std::end(yp2));
182 const double xmax2 = *std::max_element(std::begin(xp2), std::end(xp2));
183 const double ymax2 = *std::max_element(std::begin(yp2), std::end(yp2));
185 const double epsx = 1.e-6 * std::max({std::abs(xmax1), std::abs(xmin1),
186 std::abs(xmax2), std::abs(xmin2)});
187 const double epsy = 1.e-6 * std::max({std::abs(ymax1), std::abs(ymin1),
188 std::abs(ymax2), std::abs(ymin2)});
190 if (xmax1 + epsx < xmin2 || xmax2 + epsx < xmin1)
return false;
191 if (ymax1 + epsy < ymin2 || ymax2 + epsy < ymin1)
return false;
192 if (xmin1 + epsx < xmin2 || xmax1 > xmax2 + epsx)
return false;
193 if (ymin1 + epsy < ymin2 || ymax1 > ymax2 + epsy)
return false;
195 const unsigned int n1 = xp1.size();
196 for (
unsigned int i = 0; i < n1; ++i) {
197 bool inside =
false, edge =
false;
199 if (!inside)
return false;
208const double ComponentNeBem2d::InvEpsilon0 = 1. / VacuumPermittivity;
209const double ComponentNeBem2d::InvTwoPiEpsilon0 = 1. / TwoPiEpsilon0;
214 const double z,
double& ex,
double& ey,
215 double& ez,
double& v,
Medium*& m,
217 status = Field(x, y, z, ex, ey, ez, v, m,
true);
221 const double z,
double& ex,
double& ey,
222 double& ez,
Medium*& m,
int& status) {
224 status = Field(x, y, z, ex, ey, ez, v, m,
false);
227int ComponentNeBem2d::Field(
const double x,
const double y,
const double z,
double& ex,
double& ey,
double& ez,
double& v,
228 Medium*& m,
const bool opt) {
232 if (m_useRangeZ && (z < m_zmin || z > m_zmax))
return -6;
242 for (
const auto& region : m_regions) {
243 bool inside =
false, edge =
false;
245 if (inside || edge) {
246 v = region.bc.second;
255 std::cerr <<
m_className <<
"::ElectricField: Initialisation failed.\n";
261 const unsigned int nWires = m_wires.size();
262 for (
unsigned int i = 0; i < nWires; ++i) {
263 const double dx =
x - m_wires[i].x;
264 const double dy =
y - m_wires[i].y;
265 if (dx * dx + dy * dy < m_wires[i].r * m_wires[i].r) {
272 for (
const auto& element : m_elements) {
273 const double cphi = element.cphi;
274 const double sphi = element.sphi;
276 double xL = 0., yL = 0.;
277 ToLocal(x - element.x, y - element.y, cphi, sphi, xL, yL);
280 v += LinePotential(element.a, xL, yL) * element.q;
283 double fx = 0., fy = 0.;
284 LineField(element.a, xL, yL, fx, fy);
286 ToGlobal(fx, fy, cphi, sphi, fx, fy);
287 ex += fx * element.q;
288 ey += fy * element.q;
292 for (
const auto& wire : m_wires) {
295 v += WirePotential(wire.r, x - wire.x, y - wire.y) * wire.q;
298 double fx = 0., fy = 0.;
299 WireField(wire.x, x - wire.x, y - wire.y, fx, fy);
304 for (
const auto& box : m_spaceCharge) {
306 v += BoxPotential(box.a, box.b, x - box.x, y - box.y, box.v0) * box.q;
308 double fx = 0., fy = 0.;
309 BoxField(box.a, box.b, x - box.x, y - box.y, fx, fy);
317 bool gotValue =
false;
318 for (
const auto& region : m_regions) {
319 if (region.bc.first != Voltage)
continue;
321 vmin = vmax = region.bc.second;
324 vmin = std::min(vmin, region.bc.second);
325 vmax = std::max(vmax, region.bc.second);
329 for (
const auto& segment : m_segments) {
330 if (segment.bc.first != Voltage)
continue;
332 vmin = vmax = segment.bc.second;
335 vmin = std::min(vmin, segment.bc.second);
336 vmax = std::max(vmax, segment.bc.second);
340 for (
const auto& wire : m_wires) {
342 vmin = vmax = wire.v;
345 vmin = std::min(vmin, wire.v);
346 vmax = std::max(vmax, wire.v);
356 for (
const auto& region : m_regions) {
357 bool inside =
false, edge =
false;
359 if (inside || edge)
return region.medium;
365 double& xmin,
double& ymin,
double& zmin,
366 double& xmax,
double& ymax,
double& zmax) {
369 return m_geometry->GetBoundingBox(xmin, ymin, zmin, xmax, ymax, zmax);
375 double& xmin,
double& ymin,
double& zmin,
376 double& xmax,
double& ymax,
double& zmax) {
380 bool gotValue =
false;
381 for (
const auto& region : m_regions) {
382 const auto& xv = region.xv;
383 const auto& yv = region.yv;
385 xmin = *std::min_element(std::begin(xv), std::end(xv));
386 ymin = *std::min_element(std::begin(yv), std::end(yv));
387 xmax = *std::max_element(std::begin(xv), std::end(xv));
388 ymax = *std::max_element(std::begin(yv), std::end(yv));
391 xmin = std::min(*std::min_element(std::begin(xv), std::end(xv)), xmin);
392 ymin = std::min(*std::min_element(std::begin(yv), std::end(yv)), ymin);
393 xmax = std::max(*std::max_element(std::begin(xv), std::end(xv)), xmax);
394 ymax = std::max(*std::max_element(std::begin(yv), std::end(yv)), ymax);
397 for (
const auto& seg : m_segments) {
399 xmin = std::min(seg.x0[0], seg.x1[0]);
400 xmax = std::max(seg.x0[0], seg.x1[0]);
401 ymin = std::min(seg.x0[1], seg.x1[1]);
402 ymax = std::max(seg.x0[1], seg.x1[1]);
405 xmin = std::min({xmin, seg.x0[0], seg.x1[0]});
406 xmax = std::max({xmax, seg.x0[0], seg.x1[0]});
407 ymin = std::min({ymin, seg.x0[1], seg.x1[1]});
408 ymax = std::max({ymax, seg.x0[1], seg.x1[1]});
411 for (
const auto& wire : m_wires) {
413 xmin = xmax = wire.x;
414 ymin = ymax = wire.y;
416 xmin = std::min(xmin, wire.x);
417 xmax = std::max(xmax, wire.x);
418 ymin = std::min(ymin, wire.y);
419 ymax = std::max(ymax, wire.y);
426 const double x0,
const double y0,
const double z0,
427 const double x1,
const double y1,
const double z1,
428 double& xc,
double& yc,
double& zc,
const bool centre,
double& rc) {
433 if (m_wires.empty())
return false;
435 const double dx = x1 - x0;
436 const double dy = y1 - y0;
437 const double d2 = dx * dx + dy * dy;
439 if (d2 < Small)
return false;
440 const double invd2 = 1. / d2;
447 for (
const auto& wire : m_wires) {
448 const double xw = wire.x;
449 const double yw = wire.y;
451 const double xIn0 = dx * (xw - x0) + dy * (yw - y0);
453 if (xIn0 < 0.)
continue;
454 const double xIn1 = -(dx * (xw - x1) + dy * (yw - y1));
456 if (xIn1 < 0.)
continue;
458 const double xw0 = xw - x0;
459 const double xw1 = xw - x1;
460 const double yw0 = yw - y0;
461 const double yw1 = yw - y1;
462 const double dw02 = xw0 * xw0 + yw0 * yw0;
463 const double dw12 = xw1 * xw1 + yw1 * yw1;
464 if (xIn1 * xIn1 * dw02 > xIn0 * xIn0 * dw12) {
465 dMin2 = dw02 - xIn0 * xIn0 * invd2;
467 dMin2 = dw12 - xIn1 * xIn1 * invd2;
469 const double r2 = wire.r * wire.r;
477 const double p = -xIn0 * invd2;
478 const double q = (dw02 - r2) * invd2;
479 const double s = sqrt(p * p - q);
480 const double t = std::min(-p + s, -p - s);
483 zc = z0 + t * (z1 - z0);
493 const double y,
const double ,
494 double& xw,
double& yw,
double& rw) {
496 for (
const auto& wire : m_wires) {
498 if (q * wire.q > 0.)
continue;
499 const double dx = wire.x - x;
500 const double dy = wire.y - y;
501 const double rt = wire.r * wire.ntrap;
502 if (dx * dx + dy * dy < rt * rt) {
514 if (fabs(zmax - zmin) <= 0.) {
515 std::cerr <<
m_className <<
"::SetRangeZ: Zero range is not permitted.\n";
518 m_zmin = std::min(zmin, zmax);
519 m_zmax = std::max(zmin, zmax);
524 const double x1,
const double y1,
525 const double v,
const int ndiv) {
526 const double dx = x1 - x0;
527 const double dy = y1 - y0;
528 if (dx * dx + dy * dy < Small) {
529 std::cerr <<
m_className <<
"::AddSegment: Length must be > 0.\n";
534 segment.x0 = {x0, y0};
535 segment.x1 = {x1, y1};
536 segment.bc = std::make_pair(Voltage, v);
537 segment.region1 = -1;
538 segment.region2 = -1;
540 m_segments.push_back(std::move(segment));
544 << x0 <<
", " << y0 <<
") - (" << x1 <<
", " << y1 <<
")\n"
545 <<
" Potential: " << v <<
" V\n";
553 const double v,
const int ntrap) {
555 std::cerr <<
m_className <<
"::AddWire: Diameter must be > 0.\n";
559 std::cerr <<
m_className <<
"::AddWire: Nbr. of trap radii must be > 0.\n";
570 m_wires.push_back(std::move(wire));
574 <<
" Centre: (" << x <<
", " << y <<
")\n"
575 <<
" Diameter: " << d <<
" cm\n"
576 <<
" Potential: " << v <<
" V\n";
584 const std::vector<double>& yp,
585 Medium* medium,
const unsigned int bctype,
586 const double v,
const int ndiv) {
588 if (xp.size() != yp.size()) {
590 <<
" Mismatch between number of x- and y-coordinates.\n";
594 std::cerr <<
m_className <<
"::AddRegion: Too few points.\n";
597 if (bctype != 1 && bctype != 4) {
598 std::cerr <<
m_className <<
"::AddRegion: Invalid boundary condition.\n";
603 const unsigned int np = xp.size();
605 for (
unsigned int i0 = 0; i0 < np; ++i0) {
606 const unsigned int i1 = i0 < np - 1 ? i0 + 1 : 0;
607 for (
unsigned int j = 0; j < np - 3; ++j) {
608 const unsigned int j0 = i1 < np - 1 ? i1 + 1 : 0;
609 const unsigned int j1 = j0 < np - 1 ? j0 + 1 : 0;
610 double xc = 0., yc = 0.;
611 if (Crossing(xp[i0], yp[i0], xp[i1], yp[i1],
612 xp[j0], yp[j0], xp[j1], yp[j1], xc, yc)) {
613 std::cerr <<
m_className <<
"::AddRegion: Edges cross each other.\n";
619 std::vector<double> xv = xp;
620 std::vector<double> yv = yp;
621 const double xmin = *std::min_element(std::begin(xv), std::end(xv));
622 const double ymin = *std::min_element(std::begin(yv), std::end(yv));
623 const double xmax = *std::max_element(std::begin(xv), std::end(xv));
624 const double ymax = *std::max_element(std::begin(yv), std::end(yv));
626 const double epsx = 1.e-6 * std::max(std::abs(xmax), std::abs(xmin));
627 const double epsy = 1.e-6 * std::max(std::abs(ymax), std::abs(ymin));
630 if (std::abs(f) < std::max(1.e-10, epsx * epsy)) {
631 std::cerr <<
m_className <<
"::AddRegion: Degenerate polygon.\n";
636 std::cout <<
m_className <<
"::AddRegion: Reversing orientation.\n";
638 std::reverse(xv.begin(), xv.end());
639 std::reverse(yv.begin(), yv.end());
641 for (
const auto& region : m_regions) {
642 if (Intersecting(xv, yv, region.xv, region.yv)) {
644 <<
" Polygon intersects an existing region.\n";
651 region.medium = medium;
653 region.bc = std::make_pair(Voltage, v);
654 }
else if (bctype == 4) {
655 region.bc = std::make_pair(Dielectric, v);
659 m_regions.push_back(std::move(region));
664 const double a,
const double b,
666 if (a < Small || b < Small) {
667 std::cerr <<
m_className <<
"::AddChargeDistribution:\n"
668 <<
" Lengths must be > 0.\n";
671 const double a2 = a * a;
672 const double b2 = b * b;
673 const double v0 = -2 * (Pi * b2 + 2 * atan(b / a) * (a2 - b2));
681 m_spaceCharge.push_back(std::move(box));
686 std::cerr <<
m_className <<
"::SetNumberOfDivisions:\n"
687 <<
" Number of divisions must be greater than zero.\n";
697 std::cerr <<
m_className <<
"::SetNumberOfCollocationPoints:\n"
698 <<
" Number of points must be greater than zero.\n";
702 m_nCollocationPoints = ncoll;
708 std::cerr <<
m_className <<
"::SetMaxNumberOfIterations:\n"
709 <<
" Number of iterations must be greater than zero.\n";
712 m_nMaxIterations = niter;
716 std::vector<double>& xv,
717 std::vector<double>& yv,
718 Medium*& medium,
unsigned int& bctype,
720 if (i >= m_regions.size())
return false;
724 const auto& region = m_regions[i];
727 medium = region.medium;
728 bctype = region.bc.first == Voltage ? 1 : 4;
729 v = region.bc.second;
734 double& x0,
double& y0,
double& x1,
double& y1,
double& v)
const {
736 if (i >= m_segments.size())
return false;
737 const auto& seg = m_segments[i];
747 double& x,
double& y,
double& d,
double& v,
double& q)
const {
749 if (i >= m_wires.size())
return false;
750 const auto& wire = m_wires[i];
760 double& x0,
double& y0,
double& x1,
double& y1,
double& q)
const {
762 if (i >= m_elements.size())
return false;
763 const auto& element = m_elements[i];
764 ToGlobal(-element.a, 0., element.cphi, element.sphi, x0, y0);
765 ToGlobal( element.a, 0., element.cphi, element.sphi, x1, y1);
779 double vmin = 0., vmax = 0.;
782 <<
" Could not determine the voltage range.\n";
785 if (fabs(vmin - vmax) < 1.e-6 * (vmin + vmax)) {
787 <<
" All potentials are the same.\n";
793 const unsigned int nRegions = m_regions.size();
794 if (
m_debug) std::cout <<
" " << nRegions <<
" regions.\n";
795 std::vector<std::vector<unsigned int> > motherRegions(nRegions);
796 for (
unsigned int i = 0; i < nRegions; ++i) {
797 auto& region = m_regions[i];
799 for (
unsigned int j = 0; j < nRegions; ++j) {
800 if (i == j)
continue;
801 const auto& other = m_regions[j];
802 if (!Enclosed(region.xv, region.yv, other.xv, other.yv))
continue;
803 motherRegions[i].push_back(j);
805 std::cout <<
" Region " << i <<
" is enclosed by region "
809 region.depth = motherRegions[i].size();
812 std::vector<Segment> segments;
813 for (
unsigned int i = 0; i < nRegions; ++i) {
814 const auto& region = m_regions[i];
815 int outerRegion = -1;
816 for (
const unsigned int k : motherRegions[i]) {
817 if (outerRegion < 0) {
819 }
else if (m_regions[outerRegion].depth < m_regions[k].depth) {
824 const unsigned int n = region.xv.size();
825 for (
unsigned int j = 0; j < n; ++j) {
826 const unsigned int k = j < n - 1 ? j + 1 : 0;
828 seg.x0 = {region.xv[j], region.yv[j]};
829 seg.x1 = {region.xv[k], region.yv[k]};
831 seg.region2 = outerRegion;
833 seg.ndiv = region.ndiv;
834 segments.push_back(std::move(seg));
838 segments.insert(segments.end(), m_segments.begin(), m_segments.end());
839 const unsigned int nSegments = segments.size();
840 if (
m_debug) std::cout <<
" " << nSegments <<
" segments.\n";
841 std::vector<bool> done(nSegments,
false);
843 for (
unsigned int i = 0; i < nSegments; ++i) {
844 if (done[i])
continue;
846 std::cout <<
" Segment " << i <<
". ("
847 << segments[i].x0[0] <<
", " << segments[i].x0[1] <<
") - ("
848 << segments[i].x1[0] <<
", " << segments[i].x1[1] <<
")\n";
850 const double x0 = segments[i].x0[0];
851 const double x1 = segments[i].x1[0];
852 const double y0 = segments[i].x0[1];
853 const double y1 = segments[i].x1[1];
855 std::vector<Segment> newSegments;
856 for (
unsigned int j = i + 1; j < nSegments; ++j) {
857 const double u0 = segments[j].x0[0];
858 const double u1 = segments[j].x1[0];
859 const double v0 = segments[j].x0[1];
860 const double v1 = segments[j].x1[1];
861 const double epsx = std::max(1.e-10 * std::max({fabs(x0), fabs(x1),
862 fabs(u0), fabs(u1)}),
864 const double epsy = std::max(1.e-10 * std::max({fabs(y0), fabs(y1),
865 fabs(v0), fabs(v1)}),
867 const double a00 = y1 - y0;
868 const double a01 = v1 - v0;
869 const double a10 = x0 - x1;
870 const double a11 = u0 - u1;
871 const double det = a00 * a11 - a10 * a01;
872 const double tol = epsx * epsy;
874 if (std::abs(det) > tol)
continue;
876 if (std::abs(x0 * (y1 - v0) + x1 * (v0 - y0) + u0 * (y0 - y1)) > tol) {
879 newSegments.push_back(segments[j]);
882 newSegments.push_back(segments[i]);
883 if (newSegments.size() > 1) {
885 std::cout <<
" Determining overlaps of " << newSegments.size()
886 <<
" collinear segments.\n";
888 EliminateOverlaps(newSegments);
890 std::cout <<
" " << newSegments.size()
891 <<
" segments after splitting/merging.\n";
894 for (
const auto& segment : newSegments) {
896 if (segment.bc.first == Dielectric) {
898 const int reg1 = segment.region1;
899 const int reg2 = segment.region2;
902 if (m_medium) eps1 = m_medium->GetDielectricConstant();
903 }
else if (m_regions[reg1].medium) {
904 eps1 = m_regions[reg1].medium->GetDielectricConstant();
908 if (m_medium) eps2 = m_medium->GetDielectricConstant();
909 }
else if (m_regions[reg2].medium) {
910 eps2 = m_regions[reg2].medium->GetDielectricConstant();
912 if (fabs(eps1 - eps2) < 1.e-6 * (1. + fabs(eps1) + fabs(eps2))) {
913 if (
m_debug) std::cout <<
" Same epsilon. Skip.\n";
917 lambda = (eps2 - eps1) / (eps1 + eps2);
918 if (
m_debug) std::cout <<
" Lambda = " << lambda <<
"\n";
920 const int ndiv = segment.ndiv <= 0 ? m_nDivisions : segment.ndiv;
921 Discretise(segment, m_elements, lambda, ndiv);
924 std::vector<std::vector<double> > influenceMatrix;
925 std::vector<std::vector<double> > inverseMatrix;
927 bool converged =
false;
928 unsigned int nIter = 0;
932 std::cout <<
m_className <<
"::Initialise: Iteration " << nIter <<
"\n";
934 const unsigned int nElements = m_elements.size();
935 const unsigned int nEntries = nElements + m_wires.size() + 1;
937 std::cout <<
" " << nElements <<
" elements.\n"
938 <<
" Matrix has " << nEntries <<
" rows/columns.\n";
941 influenceMatrix.assign(nEntries, std::vector<double>(nEntries, 0.));
942 if (!ComputeInfluenceMatrix(influenceMatrix)) {
944 <<
" Error computing the influence matrix.\n";
949 inverseMatrix.assign(nEntries, std::vector<double>(nEntries, 0.));
951 std::cerr <<
m_className <<
"::Initialise: Matrix inversion failed.\n";
954 if (
m_debug) std::cout <<
" Matrix inversion ok.\n";
957 std::vector<double> boundaryConditions(nEntries, 0.);
958 for (
unsigned int i = 0; i < nElements; ++i) {
959 if (m_elements[i].bc.first == Voltage) {
960 boundaryConditions[i] = m_elements[i].bc.second;
961 for (
const auto& box : m_spaceCharge) {
962 const double x = m_elements[i].x - box.x;
963 const double y = m_elements[i].y - box.y;
964 const double vs = BoxPotential(box.a, box.b, x, y, box.v0) * box.q;
965 boundaryConditions[i] -= vs;
968 for (
const auto& box : m_spaceCharge) {
969 const double x = m_elements[i].x - box.x;
970 const double y = m_elements[i].y - box.y;
971 double fx = 0., fy = 0.;
972 BoxField(box.a, box.b, x, y, fx, fy);
974 ToLocal(fx, fy, m_elements[i].cphi, m_elements[i].sphi, fx, fy);
975 boundaryConditions[i] -= box.q * fy;
979 const unsigned int nWires = m_wires.size();
980 for (
unsigned int i = 0; i < nWires; ++i) {
981 boundaryConditions[nElements + i] = m_wires[i].v;
982 for (
const auto& box : m_spaceCharge) {
983 const double x = m_wires[i].x - box.x;
984 const double y = m_wires[i].y - box.y;
985 const double vs = BoxPotential(box.a, box.b, x, y, box.v0) * box.q;
986 boundaryConditions[nElements + i] -= vs;
991 for (
const auto& box : m_spaceCharge) {
992 qsum += 4 * box.q * box.a * box.b;
994 boundaryConditions.back() = -qsum;
997 if (!
Solve(inverseMatrix, boundaryConditions)) {
998 std::cerr <<
m_className <<
"::Initialise: Solution failed.\n";
1001 if (
m_debug) std::cout <<
" Solution ok.\n";
1002 const double tol = 1.e-6 * fabs(vmax - vmin);
1003 std::vector<bool> ok(nElements,
true);
1004 converged = CheckConvergence(tol, ok);
1005 if (!m_autoSize)
break;
1006 if (nIter >= m_nMaxIterations)
break;
1007 for (
unsigned int j = 0; j < nElements; ++j) {
1009 SplitElement(m_elements[j], m_elements);
1010 if (
m_debug) std::cout <<
" Splitting element " << j <<
".\n";
1015 std::sort(m_regions.begin(), m_regions.end(),
1016 [](
const Region& lhs,
const Region& rhs) {
1017 return (lhs.depth > rhs.depth);
1023void ComponentNeBem2d::EliminateOverlaps(std::vector<Segment>& segments) {
1025 if (segments.empty())
return;
1026 const unsigned int nIn = segments.size();
1028 std::array<double, 2> x0 = segments[0].x0;
1029 std::array<double, 2> x1 = segments[0].x1;
1031 const unsigned int ic = fabs(x1[1] - x0[1]) > fabs(x1[0] - x0[0]) ? 1 : 0;
1032 std::vector<bool> swapped(nIn,
false);
1033 for (
unsigned int i = 0; i < nIn; ++i) {
1034 const auto& seg = segments[i];
1035 std::array<double, 2> u0 = seg.x0;
1036 std::array<double, 2> u1 = seg.x1;
1037 if (u0[ic] > u1[ic]) {
1042 if (u0[ic] < x0[ic]) x0 = u0;
1043 if (u1[ic] > x1[ic]) x1 = u1;
1045 const std::array<double, 2> d = {x1[0] - x0[0], x1[1] - x0[1]};
1048 std::vector<std::pair<double, std::vector<unsigned int> > > points;
1049 for (
unsigned int i = 0; i < nIn; ++i) {
1050 for (
const auto& xl : {segments[i].x0, segments[i].x1}) {
1051 const std::array<double, 2> d0 = {xl[0] - x0[0], xl[1] - x0[1]};
1052 const std::array<double, 2> d1 = {x1[0] - xl[0], x1[1] - xl[1]};
1054 if (Mag2(d0) < Mag2(d1)) {
1056 lambda = d0[ic] / d[ic];
1059 lambda = 1. - d1[ic] / d[ic];
1063 for (
auto& point : points) {
1064 if (
fabs(point.first - lambda) < 1.e-6) {
1066 point.second.push_back(i);
1070 if (found)
continue;
1071 points.push_back(std::make_pair(lambda,
1072 std::vector<unsigned int>({i})));
1076 std::sort(std::begin(points), std::end(points));
1078 std::vector<Segment> newSegments;
1079 const unsigned int nPoints = points.size();
1080 std::array<double, 2> xl = {x0[0] + points[0].first * d[0],
1081 x0[1] + points[0].first * d[1]};
1082 std::vector<unsigned int> left = points[0].second;
1083 for (
unsigned int i = 1; i < nPoints; ++i) {
1084 Segment seg = segments[left.front()];
1086 xl = {x0[0] + points[i].first * d[0], x0[1] + points[i].first * d[1]};
1088 if (swapped[left.front()]) std::swap(seg.x0, seg.x1);
1090 if (left.size() > 1) {
1091 for (
unsigned int j = 1; j < left.size(); ++j) {
1092 const auto& other = segments[left[j]];
1093 if (seg.bc.first == Dielectric) {
1094 if (other.bc.first == Dielectric) {
1096 if ((seg.x1[ic] - seg.x0[ic]) * (other.x1[ic] - other.x0[ic]) > 0) {
1100 seg.region2 = other.region1;
1104 }
else if (seg.bc.first != other.bc.first) {
1105 std::cerr <<
m_className <<
"::EliminateOverlaps:\n"
1106 <<
" Warning: conflicting boundary conditions.\n";
1110 newSegments.push_back(std::move(seg));
1111 for (
unsigned int k : points[i].second) {
1112 const auto it = std::find(left.begin(), left.end(), k);
1113 if (it == left.end()) {
1120 segments.swap(newSegments);
1123bool ComponentNeBem2d::Discretise(
const Segment& seg,
1124 std::vector<Element>& elements,
1125 const double lambda,
1126 const unsigned int ndiv) {
1129 std::cerr <<
m_className <<
"::Discretise: Number of elements < 1.\n";
1132 const double phi = atan2(seg.x1[1] - seg.x0[1], seg.x1[0] - seg.x0[0]);
1133 const double cphi =
cos(phi);
1134 const double sphi =
sin(phi);
1135 const double dx = (seg.x1[0] - seg.x0[0]) / ndiv;
1136 const double dy = (seg.x1[1] - seg.x0[1]) / ndiv;
1137 const double a = 0.5 *
sqrt(dx * dx + dy * dy);
1138 double x = seg.x0[0] - 0.5 * dx;
1139 double y = seg.x0[1] - 0.5 * dy;
1140 for (
unsigned int i = 0; i < ndiv; ++i) {
1144 element.cphi = cphi;
1145 element.sphi = sphi;
1149 element.bc = seg.bc;
1150 element.lambda = lambda;
1151 elements.push_back(std::move(element));
1156bool ComponentNeBem2d::ComputeInfluenceMatrix(
1157 std::vector<std::vector<double> >& infmat)
const {
1159 const unsigned int nL = m_elements.size();
1160 const unsigned int nE = nL + m_wires.size();
1162 for (
unsigned int iF = 0; iF < nE; ++iF) {
1163 const auto bcF = iF < nL ? m_elements[iF].bc.first : Voltage;
1164 const double cphiF = iF < nL ? m_elements[iF].cphi : 1.;
1165 const double sphiF = iF < nL ? m_elements[iF].sphi : 0.;
1167 const double xF = iF < nL ? m_elements[iF].x : m_wires[iF - nL].x;
1168 const double yF = iF < nL ? m_elements[iF].y : m_wires[iF - nL].y;
1171 for (
unsigned int jS = 0; jS < nE; ++jS) {
1173 double infCoeff = 0.;
1176 const auto& src = m_elements[jS];
1177 double xL = 0., yL = 0.;
1178 ToLocal(xF - src.x, yF - src.y, src.cphi, src.sphi, xL, yL);
1179 if (bcF == Voltage) {
1180 infCoeff = LinePotential(src.a, xL, yL);
1181 }
else if (bcF == Dielectric) {
1186 infCoeff = 1. / (2. * src.lambda * VacuumPermittivity);
1189 double fx = 0., fy = 0.;
1190 LineField(src.a, xL, yL, fx, fy);
1192 ToGlobal(fx, fy, src.cphi, src.sphi, fx, fy);
1194 ToLocal(fx, fy, cphiF, sphiF, fx, fy);
1200 const auto& src = m_wires[jS - nL];
1201 if (bcF == Voltage) {
1202 infCoeff = WirePotential(src.r, xF - src.x, yF - src.y);
1203 }
else if (bcF == Dielectric) {
1204 double fx = 0., fy = 0.;
1205 WireField(src.r, xF - src.x, yF - src.y, fx, fy);
1206 ToLocal(fx, fy, cphiF, sphiF, fx, fy);
1210 infmat[iF][jS] = infCoeff;
1215 for (
unsigned int i = 0; i < nE; ++i) {
1217 infmat[nE][i] = m_elements[i].a;
1219 infmat[nE][i] = m_wires[i - nL].r;
1223 infmat[nE][nE] = 0.;
1228void ComponentNeBem2d::SplitElement(Element& oldElement,
1229 std::vector<Element>& elements) {
1230 oldElement.a *= 0.5;
1232 Element newElement = oldElement;
1233 double dx = 0., dy = 0.;
1234 ToGlobal(newElement.a, 0., newElement.cphi, newElement.sphi, dx, dy);
1240 elements.push_back(std::move(newElement));
1243bool ComponentNeBem2d::InvertMatrix(
1244 std::vector<std::vector<double> >& influenceMatrix,
1245 std::vector<std::vector<double> >& inverseMatrix)
const {
1247 const unsigned int nEntries = influenceMatrix.size();
1250 std::vector<double> col(nEntries, 0.);
1251 std::vector<int> index(nEntries, 0);
1254 if (!LUDecomposition(influenceMatrix, index)) {
1255 std::cerr <<
m_className <<
"::InvertMatrix: LU decomposition failed.\n";
1260 inverseMatrix.assign(nEntries, std::vector<double>(nEntries, 0.));
1262 for (
unsigned int j = 0; j < nEntries; ++j) {
1263 col.assign(nEntries, 0.);
1265 LUSubstitution(influenceMatrix, index, col);
1266 for (
unsigned int i = 0; i < nEntries; ++i) inverseMatrix[i][j] = col[i];
1270 influenceMatrix.clear();
1275bool ComponentNeBem2d::LUDecomposition(std::vector<std::vector<double> >& mat,
1276 std::vector<int>& index)
const {
1282 const unsigned int n = m_elements.size() + m_wires.size();
1284 std::vector<double> v(n, 0.);
1287 for (
unsigned int i = 0; i < n; ++i) {
1289 for (
unsigned int j = 0; j < n; ++j) {
1290 big = std::max(big,
fabs(mat[i][j]));
1292 if (big == 0.)
return false;
1298 unsigned int imax = 0;
1299 for (
unsigned int j = 0; j < n; ++j) {
1300 for (
unsigned int i = 0; i < j; ++i) {
1301 double sum = mat[i][j];
1302 for (
unsigned int k = 0; k < i; ++k) {
1303 sum -= mat[i][k] * mat[k][j];
1309 for (
unsigned int i = j; i < n; ++i) {
1310 double sum = mat[i][j];
1311 for (
unsigned int k = 0; k < j; ++k) {
1312 sum -= mat[i][k] * mat[k][j];
1316 const double dum = v[i] *
fabs(sum);
1324 for (
unsigned k = 0; k < n; ++k) {
1325 const double dum = mat[imax][k];
1326 mat[imax][k] = mat[j][k];
1333 if (mat[j][j] == 0.) mat[j][j] = Small;
1336 const double dum = 1. / mat[j][j];
1337 for (
unsigned int i = j + 1; i < n; ++i) {
1346void ComponentNeBem2d::LUSubstitution(
1347 const std::vector<std::vector<double> >& mat,
const std::vector<int>& index,
1348 std::vector<double>& col)
const {
1350 const unsigned int n = m_elements.size() + m_wires.size();
1351 unsigned int ii = 0;
1353 for (
unsigned i = 0; i < n; ++i) {
1354 const unsigned int ip = index[i];
1355 double sum = col[ip];
1358 for (
unsigned j = ii - 1; j < i; ++j) {
1359 sum -= mat[i][j] * col[j];
1361 }
else if (sum != 0.) {
1368 for (
int i = n - 1; i >= 0; i--) {
1369 double sum = col[i];
1370 for (
unsigned j = i + 1; j < n; ++j) {
1371 sum -= mat[i][j] * col[j];
1373 col[i] = sum / mat[i][i];
1377bool ComponentNeBem2d::Solve(
const std::vector<std::vector<double> >& invmat,
1378 const std::vector<double>& bc) {
1379 const unsigned int nEntries = bc.size();
1380 const unsigned int nElements = m_elements.size();
1381 for (
unsigned int i = 0; i < nElements; ++i) {
1382 double solution = 0.;
1383 for (
unsigned int j = 0; j < nEntries; ++j) {
1384 solution += invmat[i][j] * bc[j];
1386 m_elements[i].q = solution;
1388 const unsigned int nWires = m_wires.size();
1389 for (
unsigned int i = 0; i < nWires; ++i) {
1390 double solution = 0.;
1391 for (
unsigned int j = 0; j < nEntries; ++j) {
1392 solution += invmat[nElements + i][j] * bc[j];
1394 m_wires[i].q = solution;
1398 std::cout <<
m_className <<
"::Solve:\n Element Solution\n";
1399 for (
unsigned int i = 0; i < nElements; ++i) {
1400 std::printf(
" %8u %15.5f\n", i, m_elements[i].q);
1402 if (!m_wires.empty()) {
1403 std::cout <<
" Wire Solution\n";
1404 for (
unsigned int i = 0; i < nWires; ++i) {
1405 std::printf(
" %8u %15.5f\n", i, m_wires[i].q);
1412bool ComponentNeBem2d::CheckConvergence(
const double tol,
1413 std::vector<bool>& ok) {
1417 std::vector<double> v(m_nCollocationPoints, 0.);
1418 std::vector<double> n(m_nCollocationPoints, 0.);
1421 std::cout <<
m_className <<
"::CheckConvergence:\n"
1422 <<
" element # type LHS RHS\n";
1424 const double scale = 1. / m_nCollocationPoints;
1426 for (
const auto& tgt : m_elements) {
1427 v.assign(m_nCollocationPoints, 0.);
1428 n.assign(m_nCollocationPoints, 0.);
1429 double dx = 0., dy = 0.;
1430 ToGlobal(2 * tgt.a, 0., tgt.cphi, tgt.sphi, dx, dy);
1431 const double x0 = tgt.x - 0.5 * dx;
1432 const double y0 = tgt.y - 0.5 * dy;
1434 for (
unsigned int k = 0; k < m_nCollocationPoints; ++k) {
1437 if (m_randomCollocation) {
1442 const double s = (k + 1.) / (m_nCollocationPoints + 1.);
1447 for (
const auto& src : m_elements) {
1448 double xL = 0., yL = 0.;
1450 ToLocal(xG - src.x, yG - src.y, src.cphi, src.sphi, xL, yL);
1452 v[k] += LinePotential(src.a, xL, yL) * src.q;
1454 double fx = 0., fy = 0.;
1455 LineField(src.a, xL, yL, fx, fy);
1457 ToGlobal(fx, fy, src.cphi, src.sphi, fx, fy);
1459 ToLocal(fx, fy, tgt.cphi, tgt.sphi, fx, fy);
1463 for (
const auto& src : m_wires) {
1465 v[k] += WirePotential(src.r, xG - src.x, yG - src.y) * src.q;
1467 double fx = 0., fy = 0.;
1468 WireField(src.r, xG - src.x, yG - src.y, fx, fy);
1470 ToLocal(fx, fy, tgt.cphi, tgt.sphi, fx, fy);
1474 for (
const auto& box : m_spaceCharge) {
1475 const double xL = xG - box.x;
1476 const double yL = yG - box.y;
1477 v[k] += BoxPotential(box.a, box.b, xL, yL, box.v0) * box.q;
1478 double fx = 0., fy = 0.;
1479 BoxField(box.a, box.b, xL, yL, fx, fy);
1480 ToLocal(fx, fy, tgt.cphi, tgt.sphi, fx, fy);
1484 const double v0 = scale * std::accumulate(v.begin(), v.end(), 0.);
1485 const double n0 = scale * std::accumulate(n.begin(), n.end(), 0.);
1487 if (tgt.bc.first == Voltage) {
1488 const double dv = v0 - tgt.bc.second;
1489 if (
fabs(dv) > tol) ok[i] =
false;
1491 std::printf(
" %8u cond. %15.5f %15.5f %15.5f\n",
1492 i, v0, tgt.bc.second, dv);
1494 }
else if (tgt.bc.first == Dielectric) {
1497 n1 = n0 + 0.5 * InvEpsilon0 * tgt.q / tgt.lambda;
1498 if (
m_debug) std::printf(
" %8u diel. %15.5f %15.5f\n", i, n0, n1);
1503 for (
const auto& tgt : m_wires) {
1504 v.assign(m_nCollocationPoints, 0.);
1508 for (
unsigned int k = 0; k < m_nCollocationPoints; ++k) {
1510 const double xG = x0 + tgt.r *
cos(phi);
1511 const double yG = y0 + tgt.r *
sin(phi);
1513 for (
const auto& src : m_elements) {
1515 double xL = 0., yL = 0.;
1516 ToLocal(xG - src.x, yG - src.y, src.cphi, src.sphi, xL, yL);
1518 v[k] += LinePotential(src.a, xL, yL) * src.q;
1520 for (
const auto& src : m_wires) {
1521 v[k] += WirePotential(src.r, xG - src.x, yG - src.y) * src.q;
1523 for (
const auto& box : m_spaceCharge) {
1524 const double xL = xG - box.x;
1525 const double yL = yG - box.y;
1526 v[k] += BoxPotential(box.a, box.b, xL, yL, box.v0) * box.q;
1529 const double v0 = scale * std::accumulate(v.begin(), v.end(), 0.);
1531 std::printf(
" %8u wire %15.5f %15.5f\n", i, v0, tgt.v);
1539double ComponentNeBem2d::LinePotential(
const double a,
const double x,
1540 const double y)
const {
1542 const double amx = a -
x;
1543 const double apx = a +
x;
1544 if (
fabs(y) > Small) {
1545 const double y2 =
y *
y;
1546 p = 2. * a -
y * (atan(amx / y) + atan(apx / y)) -
1547 0.5 * amx * log(amx * amx + y2) - 0.5 * apx * log(apx * apx + y2);
1548 }
else if (
fabs(x) != a) {
1549 p = 2. * a - 0.5 * amx * log(amx * amx) - 0.5 * apx * log(apx * apx);
1551 p = 2. * a * (1. - log(2. * a));
1554 return InvTwoPiEpsilon0 * p;
1557double ComponentNeBem2d::WirePotential(
const double r0,
const double x,
1558 const double y)
const {
1559 const double r =
sqrt(x * x + y * y);
1561 return -log(r) * r0 * InvEpsilon0;
1565 return -log(r0) * r0 * InvEpsilon0;
1568void ComponentNeBem2d::LineField(
const double a,
1569 const double x,
const double y,
1570 double& ex,
double& ey)
const {
1571 const double amx = a -
x;
1572 const double apx = a +
x;
1574 const double y2 =
y *
y;
1575 ex = 0.5 * log((apx * apx + y2) / (amx * amx + y2));
1576 ey = atan(amx / y) + atan(apx / y);
1577 }
else if (
fabs(x) != a) {
1578 ex = 0.5 * log(apx * apx / (amx * amx));
1582 constexpr double eps2 = 1.e-24;
1583 ex = 0.25 * log(
pow(apx * apx - eps2, 2) /
pow(amx * amx - eps2, 2));
1586 ex *= InvTwoPiEpsilon0;
1587 ey *= InvTwoPiEpsilon0;
1590void ComponentNeBem2d::WireField(
const double r0,
1591 const double x,
const double y,
1592 double& ex,
double& ey)
const {
1593 const double r02 = r0 * r0;
1594 const double r2 =
x *
x +
y *
y;
1598 }
else if (r2 == r02) {
1610double ComponentNeBem2d::BoxPotential(
const double a,
const double b,
1611 const double x,
const double y,
1612 const double v0)
const {
1614 const double invc2 = 1. / (a * a + b * b);
1615 double v1 = 0., v2 = 0.;
1618 const std::array<double, 2> dx = {
x - a,
x + a};
1619 const std::array<double, 2> dy = {
y - b,
y + b};
1620 const std::array<double, 2> dx2 = {dx[0] * dx[0], dx[1] * dx[1]};
1621 const std::array<double, 2> dy2 = {dy[0] * dy[0], dy[1] * dy[1]};
1622 v1 = dx[0] * dy[0] * log((dx2[0] + dy2[0]) * invc2)
1623 - dx[1] * dy[0] * log((dx2[1] + dy2[0]) * invc2)
1624 + dx[1] * dy[1] * log((dx2[1] + dy2[1]) * invc2)
1625 - dx[0] * dy[1] * log((dx2[0] + dy2[1]) * invc2);
1626 std::array<double, 4>
alpha = {atan2(dy[0], dx[0]), atan2(dy[0], dx[1]),
1627 atan2(dy[1], dx[1]), atan2(dy[1], dx[0])};
1629 for (
size_t i = 0; i < 4; ++i)
if (alpha[i] < 0.)
alpha[i] += TwoPi;
1631 v2 = dx2[0] * (
alpha[0] -
alpha[3]) + dx2[1] * (alpha[2] - alpha[1]) +
1632 dy2[0] * (
alpha[1] -
alpha[0]) + dy2[1] * (alpha[3] - alpha[2]);
1635 const std::array<double, 2> dx = {a -
x, a +
x};
1636 const std::array<double, 2> dy = {b -
y, b +
y};
1637 const std::array<double, 2> dx2 = {dx[0] * dx[0], dx[1] * dx[1]};
1638 const std::array<double, 2> dy2 = {dy[0] * dy[0], dy[1] * dy[1]};
1639 v1 = dx[0] * dy[0] * log((dx2[0] + dy2[0]) * invc2) +
1640 dy[0] * dx[1] * log((dx2[1] + dy2[0]) * invc2) +
1641 dx[1] * dy[1] * log((dx2[1] + dy2[1]) * invc2) +
1642 dy[1] * dx[0] * log((dx2[0] + dy2[1]) * invc2);
1643 const double beta1 = atan2(dy[0], dx[0]);
1644 const double beta2 = atan2(dx[1], dy[0]);
1645 const double beta3 = atan2(dy[1], dx[1]);
1646 const double beta4 = atan2(dx[0], dy[1]);
1647 v2 = dx2[0] * (beta1 - beta4) + dy2[0] * (beta2 - beta1) +
1648 dx2[1] * (beta3 - beta2) + dy2[1] * (beta4 - beta3);
1649 v2 += HalfPi * (dx2[0] + dy2[0] + dx2[1] + dy2[1]);
1651 return -InvTwoPiEpsilon0 * 0.5 * (v1 + v2 - v0);
1654void ComponentNeBem2d::BoxField(
const double a,
const double b,
1655 const double x,
const double y,
1656 double& ex,
double& ey)
const {
1657 const std::array<double, 2> dx = {
x - a,
x + a};
1658 const std::array<double, 2> dy = {
y - b,
y + b};
1659 const std::array<double, 2> dx2 = {dx[0] * dx[0], dx[1] * dx[1]};
1660 const std::array<double, 2> dy2 = {dy[0] * dy[0], dy[1] * dy[1]};
1661 const double r1 = dx2[0] + dy2[0];
1662 const double r2 = dx2[1] + dy2[0];
1663 const double r3 = dx2[1] + dy2[1];
1664 const double r4 = dx2[0] + dy2[1];
1665 ex = 0.5 * (dy[0] * log(r1 / r2) + dy[1] * log(r3 / r4));
1666 ey = 0.5 * (dx[0] * log(r1 / r4) + dx[1] * log(r3 / r2));
1668 std::array<double, 4>
alpha = {atan2(dy[0], dx[0]), atan2(dy[0], dx[1]),
1669 atan2(dy[1], dx[1]), atan2(dy[1], dx[0])};
1671 for (
size_t i = 0; i < 4; ++i)
if (alpha[i] < 0.)
alpha[i] += TwoPi;
1673 ex += dx[0] * (
alpha[0] -
alpha[3]) + dx[1] * (alpha[2] - alpha[1]);
1674 ey -= dy[0] * (
alpha[0] -
alpha[1]) + dy[1] * (alpha[2] - alpha[3]);
1676 const double beta1 = atan2(-dy[0], -dx[0]);
1677 const double beta2 = atan2( dx[1], -dy[0]);
1678 const double beta3 = atan2( dy[1], dx[1]);
1679 const double beta4 = atan2(-dx[0], dy[1]);
1680 ex += dx[0] * (HalfPi + beta1 - beta4) + dx[1] * (HalfPi + beta3 - beta2);
1681 ey -= dy[0] * (beta1 - beta2 - HalfPi) + dy[1] * (beta3 - beta4 - HalfPi);
1683 ex *= InvTwoPiEpsilon0;
1684 ey *= InvTwoPiEpsilon0;
1687void ComponentNeBem2d::Reset() {
1692 m_spaceCharge.clear();
1696void ComponentNeBem2d::UpdatePeriodicity() {
1697 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
1698 <<
" Periodicities are not supported.\n";
1701void ComponentNeBem2d::ToLocal(
const double xIn,
const double yIn,
1702 const double cphi,
const double sphi,
1703 double& xOut,
double& yOut)
const {
1704 xOut = +cphi * xIn + sphi * yIn;
1705 yOut = -sphi * xIn + cphi * yIn;
1708void ComponentNeBem2d::ToGlobal(
const double xIn,
const double yIn,
1709 const double cphi,
const double sphi,
1710 double& xOut,
double& yOut)
const {
1711 xOut = cphi * xIn - sphi * yIn;
1712 yOut = sphi * xIn + cphi * yIn;
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 Initialise()
Discretise the geometry and compute the solution.
bool InTrapRadius(const double q0, const double x0, const double y0, const double z0, double &xw, double &yx, double &rw) override
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).
void AddChargeDistribution(const double x, const double y, const double a, const double b, const double rho)
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 CrossedWire(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 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 GetElementaryCell(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the coordinates of the elementary cell.
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.
bool m_debug
Switch on/off debugging messages.
Component()=delete
Default constructor.
std::string m_className
Class name.
Geometry * m_geometry
Pointer to the geometry.
bool m_ready
Ready for use?
Abstract base class for media.
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)