Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentNeBem3d.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cfloat>
5#include <cmath>
6#include <fstream>
7#include <iomanip>
8#include <iostream>
9#include <numeric>
10#include <set>
11#include <vector>
12
15#include "Garfield/Polygon.hh"
16#include "NR.h"
17#include "neBEM.h"
18#include "neBEMInterface.h"
19
20namespace {
21
22unsigned int NextPoint(const unsigned int i, const unsigned int n) {
23 const unsigned int j = i + 1;
24 return j < n ? j : 0;
25}
26
27unsigned int PrevPoint(const unsigned int i, const unsigned int n) {
28 return i > 0 ? i - 1 : n - 1;
29}
30
31double Mag(const std::array<double, 3>& a) {
32 return sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
33}
34
35std::array<double, 3> UnitVector(const std::array<double, 3>& a) {
36 const double mag = Mag(a);
37 if (mag < 1.e-12) return a;
38 const std::array<double, 3> b = {a[0] / mag, a[1] / mag, a[2] / mag};
39 return b;
40}
41
42std::array<double, 3> CrossProduct(const std::array<double, 3>& u,
43 const std::array<double, 3>& v) {
44 const std::array<double, 3> w = {u[1] * v[2] - u[2] * v[1],
45 u[2] * v[0] - u[0] * v[2],
46 u[0] * v[1] - u[1] * v[0]};
47 return w;
48}
49
50std::array<double, 3> LocalToGlobal(
51 const double x, const double y, const double z,
52 const std::array<std::array<double, 3>, 3>& dcos,
53 const std::array<double, 3>& t) {
54 // Initial vector.
55 std::array<double, 4> u = {x, y, z, 1.};
56
57 std::array<std::array<double, 4>, 4> rot;
58 rot[0] = {dcos[0][0], dcos[1][0], dcos[2][0], 0.};
59 rot[1] = {dcos[0][1], dcos[1][1], dcos[2][1], 0.};
60 rot[2] = {dcos[0][2], dcos[1][2], dcos[2][2], 0.};
61 rot[3] = {0., 0., 0., 1.};
62
63 std::array<double, 4> v = {0., 0., 0., 0.};
64 for (int i = 0; i < 4; ++i) {
65 for (int j = 0; j < 4; ++j) {
66 v[i] += rot[i][j] * u[j];
67 }
68 }
69 const std::array<double, 3> a = {v[0] + t[0], v[1] + t[1], v[2] + t[2]};
70 return a;
71}
72
73/// Compute lambda for a point on a line (0 = start, 1 = end).
74double Lambda(const double x1, const double x0, const double x2,
75 const double y1, const double y0, const double y2) {
76 // Segment of zero length.
77 if ((x1 - x2) == 0. && (y1 - y2) == 0.) {
78 std::cerr << "ComponentNeBem3d::Lambda: Zero length segment.\n";
79 return 2.;
80 }
81
82 double xl = 0.;
83 const double dx1 = x0 - x1;
84 const double dy1 = y0 - y1;
85 const double dx2 = x0 - x2;
86 const double dy2 = y0 - y2;
87 if (dx1 * dx1 + dy1 * dy1 < dx2 * dx2 + dy2 * dy2) {
88 // Point nearer to (x1, y1).
89 if (fabs(y1 - y2) > fabs(x1 - x2)) {
90 xl = dy1 / (y2 - y1);
91 } else {
92 xl = dx1 / (x2 - x1);
93 }
94 } else {
95 // Point nearer to (x2, y2).
96 if (fabs(y1 - y2) > fabs(x1 - x2)) {
97 xl = 1. - dy2 / (y1 - y2);
98 } else {
99 xl = 1. - dx2 / (x1 - x2);
100 }
101 }
102 return xl;
103}
104
105/// Determine whether a point (u, v) lies on a straight line
106/// (x1, y1) to (x2, y2).
107bool OnLine(const double x1, const double y1, const double x2, const double y2,
108 const double u, const double v) {
109 // Set tolerances.
110 double epsx = 1.e-10 * std::max({fabs(x1), fabs(x2), fabs(u)});
111 double epsy = 1.e-10 * std::max({fabs(y1), fabs(y2), fabs(v)});
112 epsx = std::max(1.e-10, epsx);
113 epsy = std::max(1.e-10, epsy);
114
115 if ((fabs(x1 - u) <= epsx && fabs(y1 - v) <= epsy) ||
116 (fabs(x2 - u) <= epsx && fabs(y2 - v) <= epsy)) {
117 // Point to be examined coincides with start or end.
118 return true;
119 } else if (fabs(x1 - x2) <= epsx && fabs(y1 - y2) <= epsy) {
120 // The line (x1, y1) to (x2, y2) is in fact a point.
121 return false;
122 }
123 double xc = 0., yc = 0.;
124 if (fabs(u - x1) + fabs(v - y1) < fabs(u - x2) + fabs(v - y2)) {
125 // (u, v) is nearer to (x1, y1).
126 const double dx = (x2 - x1);
127 const double dy = (y2 - y1);
128 const double xl = ((u - x1) * dx + (v - y1) * dy) / (dx * dx + dy * dy);
129 if (xl < 0.) {
130 xc = x1;
131 yc = y1;
132 } else if (xl > 1.) {
133 xc = x2;
134 yc = y2;
135 } else {
136 xc = x1 + xl * dx;
137 yc = y1 + xl * dy;
138 }
139 } else {
140 // (u, v) is nearer to (x2, y2).
141 const double dx = (x1 - x2);
142 const double dy = (y1 - y2);
143 const double xl = ((u - x2) * dx + (v - y2) * dy) / (dx * dx + dy * dy);
144 if (xl < 0.) {
145 xc = x2;
146 yc = y2;
147 } else if (xl > 1.) {
148 xc = x1;
149 yc = y1;
150 } else {
151 xc = x2 + xl * dx;
152 yc = y2 + xl * dy;
153 }
154 }
155 // See whether the point is on the line.
156 if (fabs(u - xc) < epsx && fabs(v - yc) < epsy) {
157 return true;
158 }
159 return false;
160}
161
162/// Determine whether the 2 straight lines (x1, y1) to (x2, y2)
163/// and (u1, v1) to (u2, v2) cross at an intermediate point for both lines.
164bool Crossing(const double x1, const double y1, const double x2,
165 const double y2, const double u1, const double v1,
166 const double u2, const double v2, double& xc, double& yc) {
167 /// Matrix to compute the crossing point.
168 std::array<std::array<double, 2>, 2> a;
169 a[0][0] = y2 - y1;
170 a[0][1] = v2 - v1;
171 a[1][0] = x1 - x2;
172 a[1][1] = u1 - u2;
173 const double det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
174 // Initial values.
175 xc = 0.;
176 yc = 0.;
177 // Set tolerances.
178 double epsx = 1.e-10 * std::max({fabs(x1), fabs(x2), fabs(u1), fabs(u2)});
179 double epsy = 1.e-10 * std::max({fabs(y1), fabs(y2), fabs(v1), fabs(v2)});
180 epsx = std::max(epsx, 1.e-10);
181 epsy = std::max(epsy, 1.e-10);
182 // Check for a point of one line located on the other line.
183 if (OnLine(x1, y1, x2, y2, u1, v1)) {
184 xc = u1;
185 yc = v1;
186 return true;
187 } else if (OnLine(x1, y1, x2, y2, u2, v2)) {
188 xc = u2;
189 yc = v2;
190 return true;
191 } else if (OnLine(u1, v1, u2, v2, x1, y1)) {
192 xc = x1;
193 yc = y1;
194 return true;
195 } else if (OnLine(u1, v1, u2, v2, x2, y2)) {
196 xc = x2;
197 yc = y2;
198 return true;
199 } else if (fabs(det) < epsx * epsy) {
200 // Parallel, non-touching.
201 return false;
202 }
203 // Crossing, non-trivial lines: solve crossing equations.
204 const double aux = a[1][1];
205 a[1][1] = a[0][0] / det;
206 a[0][0] = aux / det;
207 a[1][0] = -a[1][0] / det;
208 a[0][1] = -a[0][1] / det;
209 // Compute crossing point.
210 xc = a[0][0] * (x1 * y2 - x2 * y1) + a[1][0] * (u1 * v2 - u2 * v1);
211 yc = a[0][1] * (x1 * y2 - x2 * y1) + a[1][1] * (u1 * v2 - u2 * v1);
212 // See whether the crossing point is on both lines.
213 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc)) {
214 // Intersecting lines.
215 return true;
216 }
217 // Crossing point not on both lines.
218 return false;
219}
220
221void AddPoints(const std::vector<double>& xp1, const std::vector<double>& yp1,
222 const std::vector<double>& xp2, const std::vector<double>& yp2,
223 std::vector<double>& xl, std::vector<double>& yl,
224 std::vector<int>& flags, std::vector<double>& qs,
225 const double epsx, const double epsy) {
226 struct Point {
227 double x;
228 double y;
229 int flag;
230 double q;
231 };
232
233 std::vector<Point> points;
234
235 const unsigned int np1 = xp1.size();
236 const unsigned int np2 = xp2.size();
237 for (unsigned int i = 0; i < np1; ++i) {
238 const double xi0 = xp1[i];
239 const double yi0 = yp1[i];
240 const double xi1 = xp1[NextPoint(i, np1)];
241 const double yi1 = yp1[NextPoint(i, np1)];
242 // Add the vertex.
243 Point p1;
244 p1.x = xi0;
245 p1.y = yi0;
246 p1.flag = 1;
247 p1.q = 0.;
248 // If also on 2 or vertex of 2, flag it as crossing or foreign.
249 for (unsigned int j = 0; j < np2; ++j) {
250 const double xj0 = xp2[j];
251 const double yj0 = yp2[j];
252 if (fabs(xj0 - xi0) < epsx && fabs(yj0 - yi0) < epsy) {
253 p1.flag = 2;
254 }
255 const double xj1 = xp2[NextPoint(j, np2)];
256 const double yj1 = yp2[NextPoint(j, np2)];
257 if (OnLine(xj0, yj0, xj1, yj1, xi0, yi0) &&
258 (fabs(xj0 - xi0) > epsx || fabs(yj0 - yi0) > epsy) &&
259 (fabs(xj1 - xi0) > epsx || fabs(yj1 - yi0) > epsy)) {
260 p1.flag = 3;
261 }
262 }
263 points.push_back(std::move(p1));
264 // Go over the line segments of the other polygon.
265 std::vector<Point> pointsOther;
266 for (unsigned int j = 0; j < np2; ++j) {
267 const double xj0 = xp2[j];
268 const double yj0 = yp2[j];
269 // Add vertices of 2 that are on this line.
270 if (OnLine(xi0, yi0, xi1, yi1, xj0, yj0) &&
271 (fabs(xi0 - xj0) > epsx || fabs(yi0 - yj0) > epsy) &&
272 (fabs(xi1 - xj0) > epsx || fabs(yi1 - yj0) > epsy)) {
273 Point p2;
274 p2.x = xj0;
275 p2.y = yj0;
276 p2.flag = 2;
277 pointsOther.push_back(std::move(p2));
278 }
279 const double xj1 = xp2[NextPoint(j, np2)];
280 const double yj1 = yp2[NextPoint(j, np2)];
281 // Add crossing points.
282 double xc = 0., yc = 0.;
283 bool add = Crossing(xi0, yi0, xi1, yi1, xj0, yj0, xj1, yj1, xc, yc);
284 if (add) {
285 if ((fabs(xi0 - xc) < epsx && fabs(yi0 - yc) < epsy) ||
286 (fabs(xi1 - xc) < epsx && fabs(yi1 - yc) < epsy) ||
287 (fabs(xj0 - xc) < epsx && fabs(yj0 - yc) < epsy) ||
288 (fabs(xj1 - xc) < epsx && fabs(yj1 - yc) < epsy)) {
289 add = false;
290 }
291 if ((fabs(xi0 - xj0) < epsx && fabs(yi0 - yj0) < epsy) ||
292 (fabs(xi0 - xj1) < epsx && fabs(yi0 - yj1) < epsy) ||
293 (fabs(xi1 - xj0) < epsx && fabs(yi1 - yj0) < epsy) ||
294 (fabs(xi1 - xj1) < epsx && fabs(yi1 - yj1) < epsy)) {
295 add = false;
296 }
297 }
298 if (add) {
299 Point p2;
300 p2.x = xc;
301 p2.y = yc;
302 p2.flag = 3;
303 pointsOther.push_back(std::move(p2));
304 }
305 }
306 // Compute the lambdas for these points.
307 for (auto& p : pointsOther) {
308 p.q = Lambda(xi0, p.x, xi1, yi0, p.y, yi1);
309 }
310 // Sort the list by using the lambdas.
311 std::sort(
312 pointsOther.begin(), pointsOther.end(),
313 [](const Point& lhs, const Point& rhs) { return (lhs.q < rhs.q); });
314 points.insert(points.end(), pointsOther.begin(), pointsOther.end());
315 }
316
317 for (const Point& p : points) {
318 xl.push_back(p.x);
319 yl.push_back(p.y);
320 flags.push_back(p.flag);
321 qs.push_back(p.q);
322 }
323}
324
325/// Determine whether 2 panels are equal.
326bool Equal(const Garfield::Panel& panel1, const Garfield::Panel& panel2,
327 const double epsx, const double epsy) {
328 const auto& xp1 = panel1.xv;
329 const auto& yp1 = panel1.yv;
330 const auto& xp2 = panel2.xv;
331 const auto& yp2 = panel2.yv;
332 if (xp1.empty() || xp2.empty()) return false;
333 const unsigned int np1 = xp1.size();
334 const unsigned int np2 = xp2.size();
335
336 // Compare all points of 1 with all points of 2.
337 for (unsigned int i = 0; i < np1; ++i) {
338 // Loop over 2 until a match is found.
339 bool match = false;
340 for (unsigned int j = 0; j < np2; ++j) {
341 if (fabs(xp2[j] - xp1[i]) < epsx && fabs(yp2[j] - yp1[i]) < epsy) {
342 match = true;
343 break;
344 }
345 const unsigned int jj = NextPoint(j, np2);
346 if (OnLine(xp2[j], yp2[j], xp2[jj], yp2[jj], xp1[i], yp1[i])) {
347 match = true;
348 break;
349 }
350 }
351 if (!match) return false;
352 }
353
354 // Compare all points of 2 with all points of 1.
355 for (unsigned int i = 0; i < np2; ++i) {
356 // Loop over 1 until a match is found.
357 bool match = false;
358 for (unsigned int j = 0; j < np1; ++j) {
359 if (fabs(xp2[i] - xp1[j]) < epsx && fabs(yp2[i] - yp1[j]) < epsy) {
360 match = true;
361 break;
362 }
363 const unsigned int jj = NextPoint(j, np1);
364 if (OnLine(xp1[j], yp1[j], xp1[jj], yp1[jj], xp2[i], yp2[i])) {
365 match = true;
366 break;
367 }
368 }
369 if (!match) return false;
370 }
371
372 // If we get this far, the curves are the same.
373 return true;
374}
375
376} // namespace
377
378namespace Garfield {
379
381
383 InitValues();
384}
385
386Medium* ComponentNeBem3d::GetMedium(const double x, const double y,
387 const double z) {
388 if (!m_geometry) return nullptr;
389 return m_geometry->GetMedium(x, y, z, true);
390}
391
392void ComponentNeBem3d::InitValues() {
393
394 // Set initial values of weighting field fast volume related parameters
395 // Since MAXWtFld is not a variable, we do not need neBEM::
396 for(int id = 1; id < MAXWtFld; ++id) {
397 neBEM::OptFixedWtField[id] = 0;
398 neBEM::OptWtFldFastVol[id] = 0;
399 m_optWtFldFastVol[id] = 0;
400 m_optCreateWtFldFastPF[id] = 0;
401 m_optReadWtFldFastPF[id] = 0;
402 }
403}
404
405void ComponentNeBem3d::ElectricField(const double x, const double y,
406 const double z, double& ex, double& ey,
407 double& ez, double& v, Medium*& m,
408 int& status) {
409 ex = ey = ez = v = 0.;
410 status = 0;
411 // Check if the requested point is inside a medium
412 m = GetMedium(x, y, z);
413 if (!m) {
414 status = -6;
415 } else if (!m->IsDriftable()) {
416 status = -5;
417 }
418
419 if (!m_ready) {
420 if (!Initialise()) {
421 std::cerr << m_className << "::ElectricField: Initialisation failed.\n";
422 status = -11;
423 return;
424 }
425 m_ready = true;
426 }
427
428 // Construct a point.
429 neBEM::Point3D point;
430 point.X = 0.01 * x;
431 point.Y = 0.01 * y;
432 point.Z = 0.01 * z;
433
434 // Compute the field.
435 neBEM::Vector3D field;
436 if (neBEM::neBEMPF(&point, &v, &field) != 0) {
437 status = -10;
438 return;
439 }
440 ex = 0.01 * field.X;
441 ey = 0.01 * field.Y;
442 ez = 0.01 * field.Z;
443}
444
445void ComponentNeBem3d::ElectricField(const double x, const double y,
446 const double z, double& ex, double& ey,
447 double& ez, Medium*& m, int& status) {
448 double v = 0.;
449 ElectricField(x, y, z, ex, ey, ez, v, m, status);
450}
451
452bool ComponentNeBem3d::GetVoltageRange(double& vmin, double& vmax) {
453 // Voltage and other bc have to come from the solids
454 vmin = vmax = 0;
455 return true;
456}
457
458void ComponentNeBem3d::WeightingField(const double x, const double y,
459 const double z, double& wx, double& wy,
460 double& wz, const std::string& label) {
461 wx = wy = wz = 0.;
462 if (m_wfields.count(label) == 0) return;
463 const int id = m_wfields[label];
464 neBEM::Point3D point;
465 point.X = 0.01 * x;
466 point.Y = 0.01 * y;
467 point.Z = 0.01 * z;
468 neBEM::Vector3D field;
469 if (neBEM::neBEMWeightingField(&point, &field, id) == DBL_MAX) {
470 std::cerr << m_className << "::WeightingField: Evaluation failed.\n";
471 return;
472 }
473 wx = 0.01 * field.X;
474 wy = 0.01 * field.Y;
475 wz = 0.01 * field.Z;
476}
477
478double ComponentNeBem3d::WeightingPotential(const double x, const double y,
479 const double z,
480 const std::string& label) {
481 if (m_wfields.count(label) == 0) return 0.;
482 const int id = m_wfields[label];
483 neBEM::Point3D point;
484 point.X = 0.01 * x;
485 point.Y = 0.01 * y;
486 point.Z = 0.01 * z;
487 neBEM::Vector3D field;
488 const double v = neBEM::neBEMWeightingField(&point, &field, id);
489 return v == DBL_MAX ? 0. : v;
490}
491
492void ComponentNeBem3d::AddPlaneX(const double x, const double v) {
493 if (m_ynplan[0] && m_ynplan[1]) {
494 std::cerr << m_className << "::AddPlaneX:\n"
495 << " Cannot have more than two planes at constant x.\n";
496 return;
497 }
498
499 if (m_ynplan[0]) {
500 m_ynplan[1] = true;
501 if (x < m_coplan[0]) {
502 m_coplan[1] = m_coplan[0];
503 m_vtplan[1] = m_vtplan[0];
504 m_coplan[0] = x;
505 m_vtplan[0] = v;
506 } else {
507 m_coplan[1] = x;
508 m_vtplan[1] = v;
509 }
510 } else {
511 m_ynplan[0] = true;
512 m_coplan[0] = x;
513 m_vtplan[0] = v;
514 }
515 m_ready = false;
516}
517
518void ComponentNeBem3d::AddPlaneY(const double y, const double v) {
519 if (m_ynplan[2] && m_ynplan[3]) {
520 std::cerr << m_className << "::AddPlaneY:\n"
521 << " Cannot have more than two planes at constant y.\n";
522 return;
523 }
524
525 if (m_ynplan[2]) {
526 m_ynplan[3] = true;
527 if (y < m_coplan[2]) {
528 m_coplan[3] = m_coplan[2];
529 m_vtplan[3] = m_vtplan[2];
530 m_coplan[2] = y;
531 m_vtplan[2] = v;
532 } else {
533 m_coplan[3] = y;
534 m_vtplan[3] = v;
535 }
536 } else {
537 m_ynplan[2] = true;
538 m_coplan[2] = y;
539 m_vtplan[2] = v;
540 }
541 m_ready = false;
542}
543
544void ComponentNeBem3d::AddPlaneZ(const double z, const double v) {
545 if (m_ynplan[4] && m_ynplan[5]) {
546 std::cerr << m_className << "::AddPlaneZ:\n"
547 << " Cannot have more than two planes at constant z.\n";
548 return;
549 }
550
551 if (m_ynplan[4]) {
552 m_ynplan[5] = true;
553 if (z < m_coplan[4]) {
554 m_coplan[5] = m_coplan[4];
555 m_vtplan[5] = m_vtplan[4];
556 m_coplan[4] = z;
557 m_vtplan[4] = v;
558 } else {
559 m_coplan[5] = z;
560 m_vtplan[5] = v;
561 }
562 } else {
563 m_ynplan[4] = true;
564 m_coplan[4] = z;
565 m_vtplan[4] = v;
566 }
567 m_ready = false;
568}
569
571 if (m_ynplan[0] && m_ynplan[1]) {
572 return 2;
573 } else if (m_ynplan[0] || m_ynplan[1]) {
574 return 1;
575 }
576 return 0;
577}
578
580 if (m_ynplan[2] && m_ynplan[3]) {
581 return 2;
582 } else if (m_ynplan[2] || m_ynplan[3]) {
583 return 1;
584 }
585 return 0;
586}
587
589 if (m_ynplan[4] && m_ynplan[5]) {
590 return 2;
591 } else if (m_ynplan[4] || m_ynplan[5]) {
592 return 1;
593 }
594 return 0;
595}
596
597bool ComponentNeBem3d::GetPlaneX(const unsigned int i, double& x,
598 double& v) const {
599 if (i >= 2 || (i == 1 && !m_ynplan[1])) {
600 std::cerr << m_className << "::GetPlaneX: Index out of range.\n";
601 return false;
602 }
603 x = m_coplan[i];
604 v = m_vtplan[i];
605 return true;
606}
607
608bool ComponentNeBem3d::GetPlaneY(const unsigned int i, double& y,
609 double& v) const {
610 if (i >= 2 || (i == 1 && !m_ynplan[3])) {
611 std::cerr << m_className << "::GetPlaneY: Index out of range.\n";
612 return false;
613 }
614 y = m_coplan[i + 2];
615 v = m_vtplan[i + 2];
616 return true;
617}
618
619bool ComponentNeBem3d::GetPlaneZ(const unsigned int i, double& z,
620 double& v) const {
621 if (i >= 2 || (i == 1 && !m_ynplan[5])) {
622 std::cerr << m_className << "::GetPlaneZ: Index out of range.\n";
623 return false;
624 }
625 z = m_coplan[i + 4];
626 v = m_vtplan[i + 4];
627 return true;
628}
629
630void ComponentNeBem3d::SetTargetElementSize(const double length) {
631 if (length < MinDist) {
632 std::cerr << m_className << "::SetTargetElementSize: Value must be > "
633 << MinDist << ".\n";
634 return;
635 }
636 m_targetElementSize = length;
637}
638
640 const unsigned int nmax) {
641 if (nmin == 0 || nmax == 0) {
642 std::cerr << m_className << "::SetMinMaxNumberOfElements:\n"
643 << " Values must be non-zero.\n";
644 return;
645 }
646 m_minNbElementsOnLength = std::min(nmin, nmax);
647 m_maxNbElementsOnLength = std::max(nmin, nmax);
648}
649
650void ComponentNeBem3d::SetNewModel(const unsigned int NewModel) {
651 m_newModel = NewModel;
652}
653
654void ComponentNeBem3d::SetNewMesh(const unsigned int NewMesh) {
655 m_newMesh = NewMesh;
656}
657
658void ComponentNeBem3d::SetNewBC(const unsigned int NewBC) { m_newBC = NewBC; }
659
660void ComponentNeBem3d::SetNewPP(const unsigned int NewPP) { m_newPP = NewPP; }
661
663 const unsigned int NewMesh,
664 const unsigned int NewBC,
665 const unsigned int NewPP) {
666 m_newModel = NewModel;
667 m_newMesh = NewMesh;
668 m_newBC = NewBC;
669 m_newPP = NewPP;
670}
671
673 const unsigned int OptStoreInflMatrix) {
674 m_optStoreInflMatrix = OptStoreInflMatrix;
675}
676
678 m_optReadInflMatrix = OptReadInflMatrix;
679}
680
682 m_optStoreInvMatrix = OptStoreInvMatrix;
683}
684
686 m_optReadInvMatrix = OptReadInvMatrix;
687}
688
690 const unsigned int OptStorePrimitives) {
691 m_optStorePrimitives = OptStorePrimitives;
692}
693
695 m_optReadPrimitives = OptReadPrimitives;
696}
697
699 m_optStoreElements = OptStoreElements;
700}
701
703 m_optReadElements = OptReadElements;
704}
705
707 m_optStoreFormatted = OptFormattedFile;
708}
709
711 const unsigned int OptUnformattedFile) {
712 m_optStoreUnformatted = OptUnformattedFile;
713}
714
716 const unsigned int OptStoreInflMatrix, const unsigned int OptReadInflMatrix,
717 const unsigned int OptStoreInvMatrix, const unsigned int OptReadInvMatrix,
718 const unsigned int OptStorePrimitives, const unsigned int OptReadPrimitives,
719 const unsigned int OptStoreElements, const unsigned int OptReadElements,
720 const unsigned int OptFormattedFile,
721 const unsigned int OptUnformattedFile) {
722 m_optStoreInflMatrix = OptStoreInflMatrix;
723 m_optReadInflMatrix = OptReadInflMatrix;
724 m_optStoreInvMatrix = OptStoreInvMatrix;
725 m_optReadInvMatrix = OptReadInvMatrix;
726 m_optStorePrimitives = OptStorePrimitives;
727 m_optReadPrimitives = OptReadPrimitives;
728 m_optStoreElements = OptStoreElements;
729 m_optReadElements = OptReadElements;
730 m_optStoreFormatted = OptFormattedFile;
731 m_optStoreUnformatted = OptUnformattedFile;
732}
733
735 m_newModel = 0;
736 m_newMesh = 0;
737 m_newBC = 1;
738 m_optReadInvMatrix = 1;
739}
740
742 const unsigned int OptSystemChargeZero) {
743 m_optSystemChargeZero = OptSystemChargeZero;
744}
745
747 const unsigned int OptValidateSolution) {
748 m_optValidateSolution = OptValidateSolution;
749}
750
752 const unsigned int OptForceValidation) {
753 m_optForceValidation = OptForceValidation;
754}
755
757 m_optRepeatLHMatrix = OptRepeatLHMatrix;
758}
759
761 const unsigned int OptValidateSolution,
762 const unsigned int OptForceVaildation,
763 const unsigned int OptRepeatLHMatrix) {
764 m_optSystemChargeZero = OptSystemChargeZero;
765 m_optValidateSolution = OptValidateSolution;
766 m_optForceValidation = OptForceVaildation;
767 m_optRepeatLHMatrix = OptRepeatLHMatrix;
768}
769
770// Fast volume options (Physical potential and field)
772 const unsigned int OptCreateFastPF,
773 const unsigned int OptReadFastPF) {
774 m_optFastVol = OptFastVol;
775 m_optCreateFastPF = OptCreateFastPF;
776 m_optReadFastPF = OptReadFastPF;
777}
778
779// Fast volume version
781 m_versionFV = VersionFV;
782}
783
784// Fast volume blocks
786 m_nbBlocksFV = NbBlocksFV;
787}
788
789// Needs to include IdWtField information for each of these WtFld functions
790// Weighting potential and field related Fast volume options
792 const unsigned int IdWtField, const unsigned int OptWtFldFastVol,
793 const unsigned int OptCreateWtFldFastPF,
794 const unsigned int OptReadWtFldFastPF) {
795 m_idWtField = IdWtField;
796 m_optWtFldFastVol[IdWtField] = OptWtFldFastVol;
797 m_optCreateWtFldFastPF[IdWtField] = OptCreateWtFldFastPF;
798 m_optReadWtFldFastPF[IdWtField] = OptReadWtFldFastPF;
799}
800
801// Weighting field Fast volume version
803 const unsigned int IdWtField, const unsigned int VersionWtFldFV) {
804 m_idWtField = IdWtField;
805 m_versionWtFldFV[IdWtField] = VersionWtFldFV;
806}
807
808// Weighting field Fast volume blocks
810 const unsigned int IdWtField, const unsigned int NbBlocksWtFldFV) {
811 m_idWtField = IdWtField;
812 m_nbBlocksWtFldFV[IdWtField] = NbBlocksWtFldFV;
813}
814
815// Known charge options
817 const unsigned int OptKnownCharge) {
818 m_optKnownCharge = OptKnownCharge;
819}
820
821// Charging up options
823 m_optChargingUp = OptChargingUp;
824}
825
826void ComponentNeBem3d::SetPeriodicCopies(const unsigned int nx,
827 const unsigned int ny,
828 const unsigned int nz) {
829 m_nCopiesX = nx;
830 m_nCopiesY = ny;
831 m_nCopiesZ = nz;
832}
833
835 if (s < Small) {
836 std::cerr << m_className << "::SetPeriodicityX:\n"
837 << " Periodic length must be greater than zero.\n";
838 return;
839 }
840 m_periodicLength[0] = s;
841 m_periodic[0] = true;
842 m_mirrorPeriodic[0] = false;
844}
845
847 if (s < Small) {
848 std::cerr << m_className << "::SetPeriodicityY:\n"
849 << " Periodic length must be greater than zero.\n";
850 return;
851 }
852 m_periodicLength[1] = s;
853 m_periodic[1] = true;
854 m_mirrorPeriodic[1] = false;
856}
857
859 if (s < Small) {
860 std::cerr << m_className << "::SetPeriodicityZ:\n"
861 << " Periodic length must be greater than zero.\n";
862 return;
863 }
864 m_periodicLength[2] = s;
865 m_periodic[2] = true;
866 m_mirrorPeriodic[2] = false;
868}
869
871 if (s < Small) {
872 std::cerr << m_className << "::SetMirrorPeriodicityX:\n"
873 << " Periodic length must be greater than zero.\n";
874 return;
875 }
876 m_periodicLength[0] = s;
877 m_periodic[0] = false;
878 m_mirrorPeriodic[0] = true;
880}
881
883 if (s < Small) {
884 std::cerr << m_className << "::SetMirrorPeriodicityY:\n"
885 << " Periodic length must be greater than zero.\n";
886 return;
887 }
888 m_periodicLength[1] = s;
889 m_periodic[1] = false;
890 m_mirrorPeriodic[1] = true;
892}
893
895 if (s < Small) {
896 std::cerr << m_className << "::SetMirrorPeriodicityZ:\n"
897 << " Periodic length must be greater than zero.\n";
898 return;
899 }
900 m_periodicLength[2] = s;
901 m_periodic[2] = false;
902 m_mirrorPeriodic[2] = true;
904}
905
907 if (!m_periodic[0] && !m_mirrorPeriodic[0]) {
908 s = 0.;
909 return false;
910 }
911 s = m_periodicLength[0];
912 return true;
913}
914
916 if (!m_periodic[1] && !m_mirrorPeriodic[1]) {
917 s = 0.;
918 return false;
919 }
920 s = m_periodicLength[1];
921 return true;
922}
923
925 if (!m_periodic[2] && !m_mirrorPeriodic[2]) {
926 s = 0.;
927 return false;
928 }
929 s = m_periodicLength[2];
930 return true;
931}
932
934 // Reset the lists.
935 m_primitives.clear();
936 m_elements.clear();
937
938 if (!m_geometry) {
939 std::cerr << m_className << "::Initialise: Geometry not set.\n";
940 return false;
941 }
942 // Be sure we won't have intersections with the bounding box.
943 // TODO! Loop over the solids and call PLACYE, PLACHE, PLABXE, PLASPE, ...
944 // Loop over the solids.
945 std::map<int, Solid*> solids;
946 std::map<int, Solid::BoundaryCondition> bc;
947 std::map<int, double> volt;
948 std::map<int, double> eps;
949 std::map<int, double> charge;
950 const unsigned int nSolids = m_geometry->GetNumberOfSolids();
951 std::vector<Panel> panelsIn;
952 for (unsigned int i = 0; i < nSolids; ++i) {
953 Medium* medium = nullptr;
954 const auto solid = m_geometry->GetSolid(i, medium);
955 if (!solid) continue;
956 // Get the panels.
957 solid->SolidPanels(panelsIn);
958 // Get the boundary condition.
959 const auto id = solid->GetId();
960 solids[id] = solid;
961 bc[id] = solid->GetBoundaryConditionType();
962 volt[id] = solid->GetBoundaryPotential();
963 if (bc[id] == Solid::Unknown) {
964 std::cout << m_className << "::Initialise:\n"
965 << " Boundary conditions for solid " << id << " not set.\n";
966 if (medium && medium->IsConductor()) {
967 std::cout << " Assuming the panels to be grounded.\n";
968 bc[id] = Solid::Voltage;
969 volt[id] = 0.;
970 } else {
971 std::cout << " Assuming dielectric-dielectric interfaces.\n";
972 bc[id] = Solid::Dielectric;
973 }
974 }
975 charge[id] = solid->GetBoundaryChargeDensity();
976 if (!medium) {
977 eps[id] = 1.;
978 } else {
979 eps[id] = medium->GetDielectricConstant();
980 }
981 }
982 // Apply cuts.
983 // CALL CELSCT('APPLY')
984 // Reduce to basic periodic copy.
985 ShiftPanels(panelsIn);
986
987 // Find contact panels and split into primitives.
988
989 // *---------------------------------------------------------------------
990 // * PLABEM - Prepares panels for BEM applications: removes the contacts
991 // * and cuts polygons to rectangles and right-angle triangles.
992 // *---------------------------------------------------------------------
993
994 // Establish tolerances.
995 const double epsang = 1.e-6; // BEMEPA
996 const double epsxyz = 1.e-6; // BEMEPD
997 // CALL EPSSET('SET',EPSXYZ,EPSXYZ,EPSXYZ)
998
999 const unsigned int nIn = panelsIn.size();
1000 if (m_debug) {
1001 std::cout << m_className << "::Initialise: Retrieved " << nIn
1002 << " panels from the solids.\n";
1003 }
1004 // Keep track of which panels have been processed.
1005 std::vector<bool> mark(nIn, false);
1006 // Count the number of interface panels that have been discarded.
1007 unsigned int nTrivial = 0;
1008 unsigned int nConflicting = 0;
1009 unsigned int nNotImplemented = 0;
1010 // Pick up panels which coincide potentially.
1011 for (unsigned int i = 0; i < nIn; ++i) {
1012 // Skip panels already done.
1013 if (mark[i]) continue;
1014 // Fetch panel parameters.
1015 const double a1 = panelsIn[i].a;
1016 const double b1 = panelsIn[i].b;
1017 const double c1 = panelsIn[i].c;
1018 const auto& xp1 = panelsIn[i].xv;
1019 const auto& yp1 = panelsIn[i].yv;
1020 const auto& zp1 = panelsIn[i].zv;
1021 const unsigned int np1 = xp1.size();
1022 // Establish its norm and offset.
1023 const double d1 = a1 * xp1[0] + b1 * yp1[0] + c1 * zp1[0];
1024 if (m_debug) {
1025 std::cout << " Panel " << i << "\n Norm vector: " << a1 << ", " << b1
1026 << ", " << c1 << ", " << d1 << ".\n";
1027 }
1028 // Rotation matrix.
1029 std::array<std::array<double, 3>, 3> rot;
1030 if (fabs(c1) <= fabs(a1) && fabs(c1) <= fabs(b1)) {
1031 // Rotation: removing C
1032 rot[0][0] = b1 / sqrt(a1 * a1 + b1 * b1);
1033 rot[0][1] = -a1 / sqrt(a1 * a1 + b1 * b1);
1034 rot[0][2] = 0.;
1035 } else if (fabs(b1) <= fabs(a1) && fabs(b1) <= fabs(c1)) {
1036 // Rotation: removing B
1037 rot[0][0] = c1 / sqrt(a1 * a1 + c1 * c1);
1038 rot[0][1] = 0.;
1039 rot[0][2] = -a1 / sqrt(a1 * a1 + c1 * c1);
1040 } else {
1041 // Rotation: removing A
1042 rot[0][0] = 0.;
1043 rot[0][1] = c1 / sqrt(b1 * b1 + c1 * c1);
1044 rot[0][2] = -b1 / sqrt(b1 * b1 + c1 * c1);
1045 }
1046 rot[2][0] = a1;
1047 rot[2][1] = b1;
1048 rot[2][2] = c1;
1049 rot[1][0] = rot[2][1] * rot[0][2] - rot[2][2] * rot[0][1];
1050 rot[1][1] = rot[2][2] * rot[0][0] - rot[2][0] * rot[0][2];
1051 rot[1][2] = rot[2][0] * rot[0][1] - rot[2][1] * rot[0][0];
1052 // Rotate to the x, y plane.
1053 std::vector<double> xp(np1, 0.);
1054 std::vector<double> yp(np1, 0.);
1055 std::vector<double> zp(np1, 0.);
1056 for (unsigned int k = 0; k < np1; ++k) {
1057 xp[k] = rot[0][0] * xp1[k] + rot[0][1] * yp1[k] + rot[0][2] * zp1[k];
1058 yp[k] = rot[1][0] * xp1[k] + rot[1][1] * yp1[k] + rot[1][2] * zp1[k];
1059 zp[k] = rot[2][0] * xp1[k] + rot[2][1] * yp1[k] + rot[2][2] * zp1[k];
1060 }
1061 // Store it.
1062 std::vector<Panel> newPanels;
1063 std::vector<int> vol1;
1064 std::vector<int> vol2;
1065 Panel panel1 = panelsIn[i];
1066 panel1.xv = xp;
1067 panel1.yv = yp;
1068 panel1.zv = zp;
1069 vol1.push_back(panel1.volume);
1070 vol2.push_back(-1);
1071 newPanels.push_back(std::move(panel1));
1072 // Pick up all matching planes.
1073 for (unsigned int j = i + 1; j < nIn; ++j) {
1074 if (mark[j]) continue;
1075 const double a2 = panelsIn[j].a;
1076 const double b2 = panelsIn[j].b;
1077 const double c2 = panelsIn[j].c;
1078 const auto& xp2 = panelsIn[j].xv;
1079 const auto& yp2 = panelsIn[j].yv;
1080 const auto& zp2 = panelsIn[j].zv;
1081 const unsigned int np2 = xp2.size();
1082 // See whether this matches the first.
1083 const double d2 = a2 * xp2[0] + b2 * yp2[0] + c2 * zp2[0];
1084 // Inner product.
1085 const double dot = a1 * a2 + b1 * b2 + c1 * c2;
1086 // Offset between the two planes.
1087 const double offset = d1 - d2 * dot;
1088 if (fabs(fabs(dot) - 1.) > epsang || fabs(offset) > epsxyz) continue;
1089 // Found a match.
1090 mark[j] = true;
1091 if (m_debug) std::cout << " Match with panel " << j << ".\n";
1092 // Rotate this plane too.
1093 xp.assign(np2, 0.);
1094 yp.assign(np2, 0.);
1095 zp.assign(np2, 0.);
1096 for (unsigned int k = 0; k < np2; ++k) {
1097 xp[k] = rot[0][0] * xp2[k] + rot[0][1] * yp2[k] + rot[0][2] * zp2[k];
1098 yp[k] = rot[1][0] * xp2[k] + rot[1][1] * yp2[k] + rot[1][2] * zp2[k];
1099 zp[k] = rot[2][0] * xp2[k] + rot[2][1] * yp2[k] + rot[2][2] * zp2[k];
1100 }
1101 // Store it.
1102 Panel panel2 = panelsIn[j];
1103 panel2.xv = xp;
1104 panel2.yv = yp;
1105 panel2.zv = zp;
1106 vol1.push_back(panel2.volume);
1107 vol2.push_back(-1);
1108 newPanels.push_back(std::move(panel2));
1109 }
1110 std::vector<bool> obsolete(newPanels.size(), false);
1111 // Cut them as long as needed till no contacts remain.
1112 unsigned int jmin = 0;
1113 bool change = true;
1114 while (change) {
1115 change = false;
1116 const unsigned int n = newPanels.size();
1117 for (unsigned int j = 0; j < n; ++j) {
1118 if (obsolete[j] || j < jmin) continue;
1119 if (vol1[j] >= 0 && vol2[j] >= 0) continue;
1120 const auto& panelj = newPanels[j];
1121 for (unsigned int k = j + 1; k < n; ++k) {
1122 if (obsolete[k]) continue;
1123 if (vol1[k] >= 0 && vol2[k] >= 0) continue;
1124 const auto& panelk = newPanels[k];
1125 if (m_debug) std::cout << " Cutting " << j << ", " << k << ".\n";
1126 // Separate contact and non-contact areas.
1127 std::vector<Panel> panelsOut;
1128 std::vector<int> itypo;
1129 EliminateOverlaps(panelj, panelk, panelsOut, itypo);
1130 const unsigned int nOut = panelsOut.size();
1131 if (nOut == 2) {
1132 // TODO: retrieve epsx, epsy from overlap finding?
1133 const double epsx = epsxyz;
1134 const double epsy = epsxyz;
1135 // If there are just 2 panels, see whether there is a new one.
1136 const bool equal1 = Equal(panelj, panelsOut[0], epsx, epsy);
1137 const bool equal2 = Equal(panelj, panelsOut[1], epsx, epsy);
1138 const bool equal3 = Equal(panelk, panelsOut[0], epsx, epsy);
1139 const bool equal4 = Equal(panelk, panelsOut[1], epsx, epsy);
1140 if ((equal1 || equal3) && (equal2 || equal4)) {
1141 if (m_debug) {
1142 std::cout << " Original and new panels are identical.\n";
1143 }
1144 } else {
1145 change = true;
1146 }
1147 } else {
1148 change = true;
1149 }
1150 if (m_debug) std::cout << " Change flag: " << change << ".\n";
1151 // If there is no change, keep the two panels and proceed.
1152 if (!change) continue;
1153 // Flag the existing panels as inactive.
1154 obsolete[j] = true;
1155 obsolete[k] = true;
1156
1157 // Add the new panels.
1158 for (unsigned int l = 0; l < nOut; ++l) {
1159 if (itypo[l] == 1) {
1160 vol1.push_back(std::max(vol1[j], vol2[j]));
1161 vol2.push_back(-1);
1162 } else if (itypo[l] == 2) {
1163 vol1.push_back(std::max(vol1[k], vol2[k]));
1164 vol2.push_back(-1);
1165 } else {
1166 vol1.push_back(std::max(vol1[j], vol2[j]));
1167 vol2.push_back(std::max(vol1[k], vol2[k]));
1168 }
1169 newPanels.push_back(std::move(panelsOut[l]));
1170 obsolete.push_back(false);
1171 }
1172 jmin = j + 1;
1173 // Restart the loops.
1174 break;
1175 }
1176 if (change) break;
1177 }
1178 }
1179 // And rotate the panels back in place.
1180 const unsigned int nNew = newPanels.size();
1181 for (unsigned int j = 0; j < nNew; ++j) {
1182 if (obsolete[j]) continue;
1183 // Examine the boundary conditions.
1184 int interfaceType = 0;
1185 double potential = 0.;
1186 double lambda = 0.;
1187 double chargeDensity = 0.;
1188 if (m_debug) {
1189 std::cout << " Volume 1: " << vol1[j] << ". Volume 2: " << vol2[j]
1190 << ".\n";
1191 }
1192 if (vol1[j] < 0 && vol2[j] < 0) {
1193 // Shouldn't happen.
1194 continue;
1195 } else if (vol1[j] < 0 || vol2[j] < 0) {
1196 // Interface between a solid and vacuum/background.
1197 const auto vol = vol1[j] < 0 ? vol2[j] : vol1[j];
1198 interfaceType = InterfaceType(bc[vol]);
1199 if (bc[vol] == Solid::Dielectric ||
1200 bc[vol] == Solid::DielectricCharge) {
1201 if (fabs(eps[vol] - 1.) < 1.e-6) {
1202 // Same epsilon on both sides. Skip.
1203 interfaceType = 0;
1204 } else {
1205 lambda = (eps[vol] - 1.) / (eps[vol] + 1.);
1206 }
1207 } else if (bc[vol] == Solid::Voltage) {
1208 potential = volt[vol];
1209 }
1210 if (bc[vol] == Solid::Charge || bc[vol] == Solid::DielectricCharge) {
1211 chargeDensity = charge[vol];
1212 }
1213 } else {
1214 const auto bc1 = bc[vol1[j]];
1215 const auto bc2 = bc[vol2[j]];
1216 if (bc1 == Solid::Voltage || bc1 == Solid::Charge ||
1217 bc1 == Solid::Float) {
1218 interfaceType = InterfaceType(bc1);
1219 // First volume is a conductor. Other volume must be a dielectric.
1220 if (bc2 == Solid::Dielectric || bc2 == Solid::DielectricCharge) {
1221 if (bc1 == Solid::Voltage) {
1222 potential = volt[vol1[j]];
1223 } else if (bc1 == Solid::Charge) {
1224 chargeDensity = charge[vol1[j]];
1225 }
1226 } else {
1227 interfaceType = -1;
1228 }
1229 if (bc1 == Solid::Voltage && bc2 == Solid::Voltage) {
1230 const double v1 = volt[vol1[j]];
1231 const double v2 = volt[vol2[j]];
1232 if (fabs(v1 - v2) < 1.e-6 * (1. + fabs(v1) + fabs(v2))) {
1233 interfaceType = 0;
1234 }
1235 }
1236 } else if (bc1 == Solid::Dielectric || bc1 == Solid::DielectricCharge) {
1237 interfaceType = InterfaceType(bc2);
1238 // First volume is a dielectric.
1239 if (bc2 == Solid::Voltage) {
1240 potential = volt[vol2[j]];
1241 } else if (bc2 == Solid::Charge) {
1242 chargeDensity = charge[vol2[j]];
1243 } else if (bc2 == Solid::Dielectric ||
1244 bc2 == Solid::DielectricCharge) {
1245 const double eps1 = eps[vol1[j]];
1246 const double eps2 = eps[vol2[j]];
1247 if (fabs(eps1 - eps2) < 1.e-6 * (1. + fabs(eps1) + fabs(eps2))) {
1248 // Same epsilon. Skip.
1249 interfaceType = 0;
1250 } else {
1251 lambda = (eps1 - eps2) / (eps1 + eps2);
1252 }
1253 }
1254 }
1255 }
1256 if (m_debug) {
1257 if (interfaceType < 0) {
1258 std::cout << " Conflicting boundary conditions. Skip.\n";
1259 } else if (interfaceType < 1) {
1260 std::cout << " Trivial interface. Skip.\n";
1261 } else if (interfaceType > 5) {
1262 std::cout << " Interface type " << interfaceType
1263 << " is not implemented. Skip.\n";
1264 }
1265 }
1266 if (interfaceType < 0) {
1267 ++nConflicting;
1268 } else if (interfaceType < 1) {
1269 ++nTrivial;
1270 } else if (interfaceType > 5) {
1271 ++nNotImplemented;
1272 }
1273 if (interfaceType < 1 || interfaceType > 5) continue;
1274
1275 std::vector<Panel> panelsOut;
1276 // Reduce to rectangles and right-angle triangles.
1277 if (m_debug) std::cout << " Creating primitives.\n";
1278 MakePrimitives(newPanels[j], panelsOut);
1279 // Loop over the rectangles and triangles.
1280 for (auto& panel : panelsOut) {
1281 const auto& up = panel.xv;
1282 const auto& vp = panel.yv;
1283 const auto& wp = panel.zv;
1284 const unsigned int np = up.size();
1285 // Rotate.
1286 xp.assign(np, 0.);
1287 yp.assign(np, 0.);
1288 zp.assign(np, 0.);
1289 for (unsigned int k = 0; k < np; ++k) {
1290 xp[k] = rot[0][0] * up[k] + rot[1][0] * vp[k] + rot[2][0] * wp[k];
1291 yp[k] = rot[0][1] * up[k] + rot[1][1] * vp[k] + rot[2][1] * wp[k];
1292 zp[k] = rot[0][2] * up[k] + rot[1][2] * vp[k] + rot[2][2] * wp[k];
1293 }
1294 Primitive primitive;
1295 primitive.a = panel.a;
1296 primitive.b = panel.b;
1297 primitive.c = panel.c;
1298 primitive.xv = xp;
1299 primitive.yv = yp;
1300 primitive.zv = zp;
1301 primitive.v = potential;
1302 primitive.q = chargeDensity;
1303 primitive.lambda = lambda;
1304 primitive.interface = interfaceType;
1305 // Set the requested discretization level (target element size).
1306 primitive.elementSize = -1.;
1307 if (solids.find(vol1[j]) != solids.end()) {
1308 const auto solid = solids[vol1[j]];
1309 if (solid) {
1310 primitive.elementSize = solid->GetDiscretisationLevel(panel);
1311 }
1312 }
1313 primitive.vol1 = vol1[j];
1314 primitive.vol2 = vol2[j];
1315 m_primitives.push_back(std::move(primitive));
1316 }
1317 }
1318 }
1319
1320 // Add the wires.
1321 for (unsigned int i = 0; i < nSolids; ++i) {
1322 const auto solid = m_geometry->GetSolid(i);
1323 if (!solid) continue;
1324 if (!solid->IsWire()) continue;
1325 double x0 = 0., y0 = 0., z0 = 0.;
1326 solid->GetCentre(x0, y0, z0);
1327 double dx = 0., dy = 0., dz = 1.;
1328 solid->GetDirection(dx, dy, dz);
1329 const double dnorm = sqrt(dx * dx + dy * dy + dz * dz);
1330 if (dnorm < Small) {
1331 std::cerr << m_className << "::Initialise:\n"
1332 << " Wire has zero norm direction vector; skipped.\n";
1333 continue;
1334 }
1335 dx /= dnorm;
1336 dy /= dnorm;
1337 dz /= dnorm;
1338 const double h = solid->GetHalfLengthZ();
1339 Primitive primitive;
1340 primitive.a = solid->GetRadius();
1341 primitive.b = 0.;
1342 primitive.c = 0.;
1343 primitive.xv = {x0 - h * dx, x0 + h * dx};
1344 primitive.yv = {y0 - h * dy, y0 + h * dy};
1345 primitive.zv = {z0 - h * dz, z0 + h * dz};
1346 primitive.v = solid->GetBoundaryPotential();
1347 primitive.q = solid->GetBoundaryChargeDensity();
1348 primitive.lambda = 0.;
1349 primitive.interface = InterfaceType(solid->GetBoundaryConditionType());
1350 // Set the requested discretization level (target element size).
1351 Panel panel;
1352 primitive.elementSize = solid->GetDiscretisationLevel(panel);
1353 primitive.vol1 = solid->GetId();
1354 primitive.vol2 = -1;
1355 m_primitives.push_back(std::move(primitive));
1356 }
1357 // Print a warning if we have discarded some panels during the process.
1358 if (nTrivial > 0 || nConflicting > 0 || nNotImplemented > 0) {
1359 std::cerr << m_className << "::Initialise:\n";
1360 if (nConflicting > 0) {
1361 std::cerr << " Skipped " << nConflicting
1362 << " panels with conflicting boundary conditions.\n";
1363 }
1364 if (nNotImplemented > 0) {
1365 std::cerr << " Skipped " << nNotImplemented
1366 << " panels with not yet available boundary conditions.\n";
1367 }
1368 if (nTrivial > 0) {
1369 std::cerr << " Skipped " << nTrivial
1370 << " panels with trivial boundary conditions.\n";
1371 }
1372 }
1373 if (m_debug) {
1374 std::cout << m_className << "::Initialise:\n"
1375 << " Created " << m_primitives.size() << " primitives.\n";
1376 }
1377
1378 // Discretize the primitives.
1379 for (const auto& primitive : m_primitives) {
1380 const auto nVertices = primitive.xv.size();
1381 if (nVertices < 2 || nVertices > 4) continue;
1382 std::vector<Element> elements;
1383 // Get the target element size.
1384 double targetSize = primitive.elementSize;
1385 if (targetSize < MinDist) targetSize = m_targetElementSize;
1386 if (nVertices == 2) {
1387 DiscretizeWire(primitive, targetSize, elements);
1388 } else if (nVertices == 3) {
1389 DiscretizeTriangle(primitive, targetSize, elements);
1390 } else if (nVertices == 4) {
1391 DiscretizeRectangle(primitive, targetSize, elements);
1392 }
1393 for (auto& element : elements) {
1394 element.interface = primitive.interface;
1395 element.lambda = primitive.lambda;
1396 element.bc = primitive.v;
1397 element.assigned = primitive.q;
1398 element.solution = 0.;
1399 }
1400 m_elements.insert(m_elements.end(),
1401 std::make_move_iterator(elements.begin()),
1402 std::make_move_iterator(elements.end()));
1403 }
1404
1405 // Set the user options.
1406 // Number of threads and use of primary average properties
1407 neBEM::NbThreads = m_nThreads;
1408 neBEM::PrimAfter = m_primAfter;
1409 neBEM::WtFldPrimAfter = m_wtFldPrimAfter;
1410 neBEM::OptRmPrim = m_optRmPrim;
1411
1412 // Fast volume details (physical potential and field related)
1413 neBEM::OptFastVol = m_optFastVol;
1414 neBEM::OptCreateFastPF = m_optCreateFastPF;
1415 neBEM::OptReadFastPF = m_optReadFastPF;
1416 neBEM::VersionFV = m_versionFV;
1417 neBEM::NbBlocksFV = m_nbBlocksFV;
1418 // Weighting potential and field related Fast volume details
1419 // Since MAXWtFld is not a variable, we do not need neBEM::
1420 for (int id = 1; id < MAXWtFld; ++id) {
1421 neBEM::OptWtFldFastVol[id] = m_optWtFldFastVol[id];
1422 neBEM::OptCreateWtFldFastPF[id] = m_optCreateWtFldFastPF[id];
1423 neBEM::OptReadWtFldFastPF[id] = m_optReadWtFldFastPF[id];
1424 neBEM::VersionWtFldFV[id] = m_versionWtFldFV[id];
1425 neBEM::NbBlocksWtFldFV[id] = m_nbBlocksWtFldFV[id];
1426 }
1427 // Known charge options
1428 neBEM::OptKnCh = m_optKnownCharge;
1429 // Charging up options
1430 neBEM::OptChargingUp = m_optChargingUp;
1431
1432 // Initialize neBEM
1433 if (neBEM::neBEMInitialize() != 0) {
1434 std::cerr << m_className << "::Initialise:\n"
1435 << " Initialising neBEM failed.\n";
1436 return false;
1437 }
1438 gComponentNeBem3d = this;
1439
1440 // Discretization related
1441 neBEM::MinNbElementsOnLength = m_minNbElementsOnLength;
1442 neBEM::MaxNbElementsOnLength = m_maxNbElementsOnLength;
1443 neBEM::ElementLengthRqstd = m_targetElementSize * 0.01;
1444
1445 // New model / reuse existing model flag.
1446 neBEM::NewModel = m_newModel;
1447 neBEM::NewMesh = m_newMesh;
1448 neBEM::NewBC = m_newBC;
1449 neBEM::NewPP = m_newPP;
1450
1451 // Store and read options.
1452 neBEM::OptStoreInflMatrix = m_optStoreInflMatrix;
1453 neBEM::OptReadInflMatrix = m_optReadInflMatrix;
1454 neBEM::OptStoreInvMatrix = m_optStoreInvMatrix;
1455 neBEM::OptReadInvMatrix = m_optReadInvMatrix;
1456 neBEM::OptStorePrimitives = m_optStorePrimitives;
1457 neBEM::OptReadPrimitives = m_optReadPrimitives;
1458 neBEM::OptStoreElements = m_optStoreElements;
1459 neBEM::OptReadElements = m_optReadElements;
1460 neBEM::OptFormattedFile = m_optStoreFormatted;
1461 neBEM::OptUnformattedFile = m_optStoreUnformatted;
1462 neBEM::OptSystemChargeZero = m_optSystemChargeZero;
1463 neBEM::OptValidateSolution = m_optValidateSolution;
1464 neBEM::OptForceValidation = m_optForceValidation;
1465 neBEM::OptRepeatLHMatrix = m_optRepeatLHMatrix;
1466
1467 // Compute options
1468 neBEM::OptSystemChargeZero = m_optSystemChargeZero;
1469 neBEM::OptValidateSolution = m_optValidateSolution;
1470 neBEM::OptForceValidation = m_optForceValidation;
1471 neBEM::OptRepeatLHMatrix = m_optRepeatLHMatrix;
1472
1473 // Pass debug level.
1474 neBEM::DebugLevel = m_debug ? 101 : 0;
1475
1476 // Matrix inversion method (LU or SVD).
1477 if (m_inversion == Inversion::LU) {
1478 neBEM::OptInvMatProc = 0;
1479 } else {
1480 neBEM::OptInvMatProc = 1;
1481 }
1482 // Delete existing weighting fields (if any).
1483 if (neBEM::WtFieldChDen != NULL) {
1484 neBEM::neBEMDeleteAllWeightingFields();
1485 }
1486 // Transfer the geometry.
1487 if (neBEM::neBEMReadGeometry() != 0) {
1488 std::cerr << m_className << "::Initialise:\n"
1489 << " Transferring the geometry to neBEM failed.\n";
1490 return false;
1491 }
1492
1493 // Discretization.
1494 int** elementNbs = neBEM::imatrix(1, neBEM::NbPrimitives, 1, 2);
1495 for (int i = 1; i <= neBEM::NbPrimitives; ++i) {
1496 const int vol1 = neBEM::VolRef1[i];
1497 double size1 = -1.;
1498 if (solids.find(vol1) != solids.end()) {
1499 const auto solid = solids[vol1];
1500 if (solid) {
1501 Panel panel;
1502 panel.a = neBEM::XNorm[i];
1503 panel.b = neBEM::YNorm[i];
1504 panel.c = neBEM::ZNorm[i];
1505 const int nv = neBEM::NbVertices[i];
1506 panel.xv.resize(nv);
1507 panel.yv.resize(nv);
1508 panel.zv.resize(nv);
1509 for (int j = 0; j < nv; ++j) {
1510 panel.xv[j] = neBEM::XVertex[i][j];
1511 panel.yv[j] = neBEM::YVertex[i][j];
1512 panel.zv[j] = neBEM::ZVertex[i][j];
1513 }
1514 size1 = solid->GetDiscretisationLevel(panel);
1515 }
1516 }
1517 int vol2 = neBEM::VolRef2[i];
1518 double size2 = -1.;
1519 if (solids.find(vol2) != solids.end()) {
1520 const auto solid = solids[vol2];
1521 if (solid) {
1522 Panel panel;
1523 panel.a = -neBEM::XNorm[i];
1524 panel.b = -neBEM::YNorm[i];
1525 panel.c = -neBEM::ZNorm[i];
1526 const int nv = neBEM::NbVertices[i];
1527 panel.xv.resize(nv);
1528 panel.yv.resize(nv);
1529 panel.zv.resize(nv);
1530 for (int j = 0; j < nv; ++j) {
1531 panel.xv[j] = neBEM::XVertex[i][j];
1532 panel.yv[j] = neBEM::YVertex[i][j];
1533 panel.zv[j] = neBEM::ZVertex[i][j];
1534 }
1535 size2 = solid->GetDiscretisationLevel(panel);
1536 }
1537 }
1538 double size = m_targetElementSize;
1539 if (size1 > 0. && size2 > 0.) {
1540 size = std::min(size1, size2);
1541 } else if (size1 > 0.) {
1542 size = size1;
1543 } else if (size2 > 0.) {
1544 size = size2;
1545 }
1546 // Convert from cm to m.
1547 size *= 0.01;
1548
1549 // Work out the element dimensions.
1550 const double dx1 = neBEM::XVertex[i][0] - neBEM::XVertex[i][1];
1551 const double dy1 = neBEM::YVertex[i][0] - neBEM::YVertex[i][1];
1552 const double dz1 = neBEM::ZVertex[i][0] - neBEM::ZVertex[i][1];
1553 const double l1 = dx1 * dx1 + dy1 * dy1 + dz1 * dz1;
1554 int nb1 = (int)(sqrt(l1) / size);
1555 // Truncate to desired range.
1556 if (nb1 < neBEM::MinNbElementsOnLength) {
1557 nb1 = neBEM::MinNbElementsOnLength;
1558 } else if (nb1 > neBEM::MaxNbElementsOnLength) {
1559 nb1 = neBEM::MaxNbElementsOnLength;
1560 }
1561 int nb2 = 0;
1562 if (neBEM::NbVertices[i] > 2) {
1563 const double dx2 = neBEM::XVertex[i][2] - neBEM::XVertex[i][1];
1564 const double dy2 = neBEM::YVertex[i][2] - neBEM::YVertex[i][1];
1565 const double dz2 = neBEM::ZVertex[i][2] - neBEM::ZVertex[i][1];
1566 const double l2 = dx2 * dx2 + dy2 * dy2 + dz2 * dz2;
1567 nb2 = (int)(sqrt(l2) / size);
1568 if (nb2 < neBEM::MinNbElementsOnLength) {
1569 nb2 = neBEM::MinNbElementsOnLength;
1570 } else if (nb2 > neBEM::MaxNbElementsOnLength) {
1571 nb2 = neBEM::MaxNbElementsOnLength;
1572 }
1573 }
1574 elementNbs[i][1] = nb1;
1575 elementNbs[i][2] = nb2;
1576 }
1577
1578 if (neBEM::neBEMDiscretize(elementNbs) != 0) {
1579 std::cerr << m_className << "::Initialise: Discretization failed.\n";
1580 neBEM::free_imatrix(elementNbs, 1, neBEM::NbPrimitives, 1, 2);
1581 return false;
1582 }
1583 neBEM::free_imatrix(elementNbs, 1, neBEM::NbPrimitives, 1, 2);
1584 if (neBEM::neBEMBoundaryInitialConditions() != 0) {
1585 std::cerr << m_className << "::Initialise:\n"
1586 << " Setting the boundary and initial conditions failed.\n";
1587 return false;
1588 }
1589 if (neBEM::neBEMSolve() != 0) {
1590 std::cerr << m_className << "::Initialise: Solution failed.\n";
1591 return false;
1592 }
1593 // Now the weighting fields.
1594 std::set<std::string> labels;
1595 for (unsigned int i = 0; i < nSolids; ++i) {
1596 const auto solid = m_geometry->GetSolid(i);
1597 if (!solid) continue;
1598 const std::string label = solid->GetLabel();
1599 if (!label.empty()) labels.insert(label);
1600 }
1601 for (const auto& label : labels) {
1602 std::vector<int> primitives;
1603 for (unsigned int i = 0; i < nSolids; ++i) {
1604 const auto solid = m_geometry->GetSolid(i);
1605 if (!solid) continue;
1606 if (solid->GetLabel() != label) continue;
1607 const int id = solid->GetId();
1608 // Add the primitives associated to this solid to the list.
1609 for (int j = 1; j <= neBEM::NbPrimitives; ++j) {
1610 if (neBEM::VolRef1[j] == id || neBEM::VolRef2[j] == id) {
1611 primitives.push_back(j);
1612 }
1613 }
1614 }
1615 // Request the weighting field for this list of primitives.
1616 const int np = primitives.size();
1617 const int id = neBEM::neBEMPrepareWeightingField(np, primitives.data());
1618 if (id < 0) {
1619 std::cerr << m_className << "::Initialise:\n"
1620 << " Weighting field calculation for readout group \""
1621 << label << "\" failed.\n";
1622 continue;
1623 } else {
1624 std::cout << m_className << "::Initialise:\n"
1625 << " Prepared weighting field for readout group \"" << label
1626 << "\".\n";
1627 m_wfields[label] = id;
1628 }
1629 }
1630 // TODO! Not sure if we should call this here.
1631 // neBEM::neBEMEnd();
1632 m_ready = true;
1633 return true;
1634}
1635
1636void ComponentNeBem3d::ShiftPanels(std::vector<Panel>& panels) const {
1637 // *---------------------------------------------------------------------
1638 // * BEMBAS - Reduces panels to the basic period.
1639 // *---------------------------------------------------------------------
1640 const bool perx = m_periodic[0] || m_mirrorPeriodic[0];
1641 const bool pery = m_periodic[1] || m_mirrorPeriodic[1];
1642 const bool perz = m_periodic[2] || m_mirrorPeriodic[2];
1643 // Nothing to do if there is no periodicity.
1644 if (!perx && !pery && !perz) return;
1645
1646 // Loop over the panels.
1647 for (auto& panel : panels) {
1648 const auto nv = panel.xv.size();
1649 if (nv == 0) continue;
1650 // Determine the centre of gravity.
1651 double xc = std::accumulate(panel.xv.begin(), panel.xv.end(), 0.);
1652 double yc = std::accumulate(panel.yv.begin(), panel.yv.end(), 0.);
1653 double zc = std::accumulate(panel.zv.begin(), panel.zv.end(), 0.);
1654 xc /= nv;
1655 yc /= nv;
1656 zc /= nv;
1657 // Any change ?
1658 constexpr double eps = 1.e-6;
1659 double rx = 0.;
1660 int nx = 0;
1661 if (perx && m_periodicLength[0] > 0.) {
1662 rx = xc / m_periodicLength[0];
1663 nx = std::round(rx);
1664 if (std::abs(rx - nx - 0.5) < eps) ++nx;
1665 }
1666 double ry = 0.;
1667 int ny = 0;
1668 if (pery && m_periodicLength[1] > 0.) {
1669 ry = yc / m_periodicLength[1];
1670 ny = std::round(ry);
1671 if (std::abs(ry - ny - 0.5) < eps) ++ny;
1672 }
1673 double rz = 0.;
1674 int nz = 0;
1675 if (perz && m_periodicLength[2] > 0.) {
1676 rz = zc / m_periodicLength[2];
1677 nz = std::round(rz);
1678 if (std::abs(rz - nz - 0.5) < eps) ++nz;
1679 }
1680 // Skip if there is no shift.
1681 if (nx == 0 && ny == 0 && nz == 0) continue;
1682 // Shift for x-periodicity.
1683 if (nx != 0) {
1684 const double shift = nx * m_periodicLength[0];
1685 for (auto& x : panel.xv) x -= shift;
1686 }
1687 // Shift for y-periodicity.
1688 if (ny != 0) {
1689 const double shift = ny * m_periodicLength[1];
1690 for (auto& y : panel.yv) y -= shift;
1691 }
1692 // Shift for z-periodicity.
1693 if (nz != 0) {
1694 const double shift = nz * m_periodicLength[2];
1695 for (auto& z : panel.zv) z -= shift;
1696 }
1697 }
1698}
1699
1700int ComponentNeBem3d::InterfaceType(const Solid::BoundaryCondition bc) const {
1701 // 0: To be skipped
1702 // 1: Conductor-dielectric
1703 // 2: Conductor with known charge
1704 // 3: Conductor at floating potential
1705 // 4: Dielectric-dielectric
1706 // 5: Dielectric with given surface charge
1707 //
1708 // Check dielectric-dielectric formulation in
1709 // Jaydeep P. Bardhan,
1710 // Numerical solution of boundary-integral equations for molecular
1711 // electrostatics, J. Chem. Phys. 130, 094102 (2009)
1712 // https://doi.org/10.1063/1.3080769
1713
1714 switch (bc) {
1715 case Solid::Voltage:
1716 return 1;
1717 break;
1718 case Solid::Charge:
1719 return 2;
1720 break;
1721 case Solid::Float:
1722 return 3;
1723 break;
1724 case Solid::Dielectric:
1725 return 4;
1726 break;
1728 return 5;
1729 break;
1731 // Symmetry boundary, E parallel.
1732 return 6;
1733 break;
1735 // Symmetry boundary, E perpendicular.
1736 return 7;
1737 break;
1738 default:
1739 break;
1740 }
1741 return 0;
1742}
1743
1744bool ComponentNeBem3d::DiscretizeWire(const Primitive& primitive,
1745 const double targetSize,
1746 std::vector<Element>& elements) const {
1747 const double dx = primitive.xv[1] - primitive.xv[0];
1748 const double dy = primitive.yv[1] - primitive.yv[0];
1749 const double dz = primitive.zv[1] - primitive.zv[0];
1750 const double lw = sqrt(dx * dx + dy * dy + dz * dz);
1751 // Determine the number of segments along the wire.
1752 unsigned int nSegments = NbOfSegments(lw, targetSize);
1753 const double elementSize = lw / nSegments;
1754
1755 // Determine the direction cosines.
1756 // The z axis of the local coordinate system is along the wire.
1757 const std::array<double, 3> zu = {dx / lw, dy / lw, dz / lw};
1758 // Make the x axis orthogonal in the two largest components.
1759 std::array<double, 3> xu;
1760 if (fabs(zu[0]) >= fabs(zu[2]) && fabs(zu[1]) >= fabs(zu[2])) {
1761 xu = {-zu[1], zu[0], 0.};
1762 } else if (fabs(zu[0]) >= fabs(zu[1]) && fabs(zu[2]) >= fabs(zu[1])) {
1763 xu = {-zu[2], 0., zu[0]};
1764 } else {
1765 xu = {0., zu[2], -zu[1]};
1766 }
1767 xu = UnitVector(xu);
1768 // The y axis is given by the vectorial product of z and x.
1769 const std::array<double, 3> yu = UnitVector(CrossProduct(zu, xu));
1770 const std::array<std::array<double, 3>, 3> dcos = {xu, yu, zu};
1771
1772 const double xincr = dx / nSegments;
1773 const double yincr = dy / nSegments;
1774 const double zincr = dz / nSegments;
1775
1776 // TODO!
1777 const double radius = 1.;
1778 const double dA = TwoPi * radius * elementSize;
1779 for (unsigned int i = 0; i < nSegments; ++i) {
1780 const double x0 = primitive.xv[0] + i * xincr;
1781 const double y0 = primitive.yv[0] + i * yincr;
1782 const double z0 = primitive.zv[0] + i * zincr;
1783 Element element;
1784 element.origin = {x0 + 0.5 * xincr, y0 + 0.5 * yincr, z0 + 0.5 * zincr};
1785 element.xv = {x0, x0 + xincr};
1786 element.yv = {y0, y0 + yincr};
1787 element.zv = {z0, z0 + zincr};
1788 element.lx = radius;
1789 element.lz = elementSize;
1790 element.dA = dA;
1791 element.dcos = dcos;
1792 // Modify to be on surface?
1793 element.collocationPoint = element.origin;
1794 elements.push_back(std::move(element));
1795 }
1796 return true;
1797}
1798
1799bool ComponentNeBem3d::DiscretizeTriangle(
1800 const Primitive& primitive, const double targetSize,
1801 std::vector<Element>& elements) const {
1802 // Origin of the local coordinate system is at the right angle corner.
1803 std::array<double, 3> corner = {primitive.xv[1], primitive.yv[1],
1804 primitive.zv[1]};
1805 // Determine the direction cosines.
1806 const double dx1 = primitive.xv[0] - primitive.xv[1];
1807 const double dy1 = primitive.yv[0] - primitive.yv[1];
1808 const double dz1 = primitive.zv[0] - primitive.zv[1];
1809 const double dx2 = primitive.xv[2] - primitive.xv[1];
1810 const double dy2 = primitive.yv[2] - primitive.yv[1];
1811 const double dz2 = primitive.zv[2] - primitive.zv[1];
1812 // Normal vector of the primitive.
1813 std::array<double, 3> nu = {primitive.a, primitive.b, primitive.c};
1814 nu = UnitVector(nu);
1815 // int flagDC = 1;
1816 // We begin with trial 1: one of the possible orientations.
1817 double lx = sqrt(dx1 * dx1 + dy1 * dy1 + dz1 * dz1);
1818 double lz = sqrt(dx2 * dx2 + dy2 * dy2 + dz2 * dz2);
1819 std::array<double, 3> xu = {dx1 / lx, dy1 / lx, dz1 / lx};
1820 std::array<double, 3> zu = {dx2 / lz, dy2 / lz, dz2 / lz};
1821 std::array<double, 3> yu = CrossProduct(zu, xu);
1822 constexpr double tol = 1.e-3;
1823 if ((fabs(yu[0] - nu[0]) > tol) || (fabs(yu[1] - nu[1]) > tol) ||
1824 (fabs(yu[2] - nu[2]) > tol)) {
1825 // flagDC = 2;
1826 // Try the other orientation.
1827 std::swap(lx, lz);
1828 xu = {dx2 / lx, dy2 / lx, dz2 / lx};
1829 zu = {dx1 / lz, dy1 / lz, dz1 / lz};
1830 yu = CrossProduct(zu, xu);
1831 if ((fabs(yu[0] - nu[0]) > tol) || (fabs(yu[1] - nu[1]) > tol) ||
1832 (fabs(yu[2] - nu[2]) > tol)) {
1833 // No other possibility, search failed.
1834 std::cerr << m_className << "::DiscretizeTriangle:\n"
1835 << " Could not establish direction vectors.\n";
1836 return false;
1837 }
1838 }
1839 std::array<std::array<double, 3>, 3> dcos = {xu, yu, zu};
1840
1841 // TODO!
1842 /*
1843 double eps1 = primitive.eps1;
1844 double eps2 = primitive.eps2;
1845 if (flagDC == 1) std::swap(eps1, eps2);
1846 */
1847
1848 // Determine the number of elements.
1849 unsigned int nx = NbOfSegments(lx, targetSize);
1850 unsigned int nz = NbOfSegments(lz, targetSize);
1851 double elementSizeX = lx / nx;
1852 double elementSizeZ = lz / nz;
1853
1854 // Analyse the aspect ratio.
1855 double ar = elementSizeX / elementSizeZ;
1856 if (ar > 10.) {
1857 // Reduce nz.
1858 elementSizeZ = 0.1 * elementSizeX;
1859 nz = std::max(static_cast<int>(lz / elementSizeZ), 1);
1860 elementSizeZ = lz / nz;
1861 }
1862 if (ar < 0.1) {
1863 // Reduce nx.
1864 elementSizeX = 0.1 * elementSizeZ;
1865 nx = std::max(static_cast<int>(lx / elementSizeX), 1);
1866 elementSizeX = lx / nx;
1867 }
1868 const double dxdz = lx / lz;
1869 for (unsigned int k = 0; k < nz; ++k) {
1870 // Consider the k-th row.
1871 const double zlo = k * elementSizeZ;
1872 const double zhi = (k + 1) * elementSizeZ;
1873 double xlo = (lz - zlo) * dxdz;
1874 double xhi = (lz - zhi) * dxdz;
1875 // Triangular element on the k-th row.
1876 Element triangle;
1877 triangle.origin = LocalToGlobal(xhi, 0., zlo, dcos, corner);
1878 // Assign element values.
1879 const double triangleSizeX = xlo - xhi;
1880 triangle.lx = triangleSizeX;
1881 triangle.lz = elementSizeZ;
1882 triangle.dA = 0.5 * triangleSizeX * elementSizeZ;
1883 triangle.dcos = dcos;
1884 // Calculate the element vertices.
1885 const double xv0 = triangle.origin[0];
1886 const double yv0 = triangle.origin[1];
1887 const double zv0 = triangle.origin[2];
1888 const double xv1 = xv0 + triangleSizeX * xu[0];
1889 const double yv1 = yv0 + triangleSizeX * xu[1];
1890 const double zv1 = zv0 + triangleSizeX * xu[2];
1891 const double xv2 = xv0 + elementSizeZ * zu[0];
1892 const double yv2 = yv0 + elementSizeZ * zu[1];
1893 const double zv2 = zv0 + elementSizeZ * zu[2];
1894 // Assign vertices of the element.
1895 triangle.xv = {xv0, xv1, xv2};
1896 triangle.yv = {yv0, yv1, yv2};
1897 triangle.zv = {zv0, zv1, zv2};
1898
1899 // Boundary condition is applied at the barycentre, not at the origin
1900 // of the element coordinate system which is at the right-angled corner.
1901 const double xb = triangleSizeX / 3.;
1902 const double yb = 0.;
1903 const double zb = elementSizeZ / 3.;
1904 triangle.collocationPoint =
1905 LocalToGlobal(xb, yb, zb, dcos, triangle.origin);
1906 elements.push_back(std::move(triangle));
1907 // No rectangular element on the last row.
1908 if (k == nz - 1) continue;
1909
1910 // Determine number and size in x of the rectangular elements.
1911 const int nRect = xhi <= elementSizeX ? 1 : (int)(xhi / elementSizeX);
1912 const double rectSizeX = xhi / nRect;
1913 const double zc = 0.5 * (zlo + zhi);
1914 for (int i = 0; i < nRect; ++i) {
1915 // Centroid of the rectangular element.
1916 const double xc = (i + 0.5) * rectSizeX;
1917 const double yc = 0.;
1918 std::array<double, 3> centre = LocalToGlobal(xc, yc, zc, dcos, corner);
1919 // Assign element values.
1920 Element rect;
1921 rect.origin = centre;
1922 rect.lx = rectSizeX;
1923 rect.lz = elementSizeZ;
1924 rect.dA = rectSizeX * elementSizeZ;
1925 rect.dcos = dcos;
1926 // Boundary condition is applied at the origin of the rectangular
1927 // element coordinate system.
1928 rect.collocationPoint = centre;
1929 // Calculate the element vertices.
1930 const double hx = 0.5 * rectSizeX;
1931 const double hz = 0.5 * elementSizeZ;
1932 std::array<double, 3> p0 = LocalToGlobal(-hx, 0., -hz, dcos, centre);
1933 std::array<double, 3> p1 = LocalToGlobal(hx, 0., -hz, dcos, centre);
1934 std::array<double, 3> p2 = LocalToGlobal(hx, 0., hz, dcos, centre);
1935 std::array<double, 3> p3 = LocalToGlobal(-hx, 0., hz, dcos, centre);
1936 // Assign the vertices of the element.
1937 rect.xv = {p0[0], p1[0], p2[0], p3[0]};
1938 rect.yv = {p0[1], p1[1], p2[1], p3[1]};
1939 rect.zv = {p0[2], p1[2], p2[2], p3[2]};
1940 elements.push_back(std::move(rect));
1941 }
1942 }
1943 return true;
1944}
1945
1946bool ComponentNeBem3d::DiscretizeRectangle(
1947 const Primitive& primitive, const double targetSize,
1948 std::vector<Element>& elements) const {
1949 std::array<double, 3> origin = {
1950 0.25 * std::accumulate(primitive.xv.begin(), primitive.xv.end(), 0.),
1951 0.25 * std::accumulate(primitive.yv.begin(), primitive.yv.end(), 0.),
1952 0.25 * std::accumulate(primitive.zv.begin(), primitive.zv.end(), 0.)};
1953
1954 // Determine the direction cosines.
1955 const double dx1 = primitive.xv[1] - primitive.xv[0];
1956 const double dy1 = primitive.yv[1] - primitive.yv[0];
1957 const double dz1 = primitive.zv[1] - primitive.zv[0];
1958 const double dx2 = primitive.xv[2] - primitive.xv[1];
1959 const double dy2 = primitive.yv[2] - primitive.yv[1];
1960 const double dz2 = primitive.zv[2] - primitive.zv[1];
1961 double lx = sqrt(dx1 * dx1 + dy1 * dy1 + dz1 * dz1);
1962 double lz = sqrt(dx2 * dx2 + dy2 * dy2 + dz2 * dz2);
1963 const std::array<double, 3> xu = {dx1 / lx, dy1 / lx, dz1 / lx};
1964 const std::array<double, 3> yu = {primitive.a, primitive.b, primitive.c};
1965 const std::array<double, 3> zu = CrossProduct(xu, yu);
1966 const std::array<std::array<double, 3>, 3> dcos = {xu, yu, zu};
1967
1968 // Determine the number of elements.
1969 unsigned int nx = NbOfSegments(lx, targetSize);
1970 unsigned int nz = NbOfSegments(lz, targetSize);
1971
1972 double elementSizeX = lx / nx;
1973 double elementSizeZ = lz / nz;
1974
1975 // Analyze the element aspect ratio.
1976 double ar = elementSizeX / elementSizeZ;
1977 if (ar > 10.) {
1978 // Reduce nz.
1979 elementSizeZ = 0.1 * elementSizeX;
1980 nz = std::max(static_cast<int>(lz / elementSizeZ), 1);
1981 elementSizeZ = lz / nz;
1982 }
1983 if (ar < 0.1) {
1984 // Reduce nx.
1985 elementSizeX = 0.1 * elementSizeZ;
1986 nx = std::max(static_cast<int>(lx / elementSizeX), 1);
1987 elementSizeX = lx / nx;
1988 }
1989
1990 const double dA = elementSizeX * elementSizeZ;
1991 for (unsigned int i = 0; i < nx; ++i) {
1992 // Centroid of the element.
1993 const double xav = -0.5 * lx + (i + 0.5) * elementSizeX;
1994 for (unsigned int k = 0; k < nz; ++k) {
1995 const double zav = -0.5 * lz + (k + 0.5) * elementSizeZ;
1996
1997 std::array<double, 3> centre = LocalToGlobal(xav, 0., zav, dcos, origin);
1998 Element element;
1999 element.origin = centre;
2000 element.lx = elementSizeX;
2001 element.lz = elementSizeZ;
2002 element.dA = dA;
2003 element.dcos = dcos;
2004 element.collocationPoint = centre;
2005 // Calculate the element vertices.
2006 const double hx = 0.5 * elementSizeX;
2007 const double hz = 0.5 * elementSizeZ;
2008 std::array<double, 3> p0 = LocalToGlobal(-hx, 0., -hz, dcos, centre);
2009 std::array<double, 3> p1 = LocalToGlobal(hx, 0., -hz, dcos, centre);
2010 std::array<double, 3> p2 = LocalToGlobal(hx, 0., hz, dcos, centre);
2011 std::array<double, 3> p3 = LocalToGlobal(-hx, 0., hz, dcos, centre);
2012 // Assign the vertices of the element.
2013 element.xv = {p0[0], p1[0], p2[0], p3[0]};
2014 element.yv = {p0[1], p1[1], p2[1], p3[1]};
2015 element.zv = {p0[2], p1[2], p2[2], p3[2]};
2016 elements.push_back(std::move(element));
2017 }
2018 }
2019 return true;
2020}
2021
2022unsigned int ComponentNeBem3d::NbOfSegments(const double length,
2023 const double target) const {
2024 // Check whether the length of the primitive is long enough.
2025 if (length < MinDist) return 1;
2026 unsigned int n = static_cast<unsigned int>(length / target);
2027 if (n < m_minNbElementsOnLength) {
2028 // Need to have a minimum number of elements per primitive...
2029 n = m_minNbElementsOnLength;
2030 if (length < n * MinDist) {
2031 // ...which may not be possible if the length is small.
2032 n = static_cast<unsigned int>(length / MinDist);
2033 if (n < 1) {
2034 // However, it is necessary to have at least one element!
2035 n = 1;
2036 }
2037 }
2038 }
2039 return std::min(n, m_maxNbElementsOnLength);
2040}
2041
2042bool ComponentNeBem3d::EliminateOverlaps(const Panel& panel1,
2043 const Panel& panel2,
2044 std::vector<Panel>& panelsOut,
2045 std::vector<int>& itypo) {
2046 // *-----------------------------------------------------------------------
2047 // * PLAOVL - Isolates the parts of plane 1 that are not hidden by 2.
2048 // *-----------------------------------------------------------------------
2049
2050 const auto& xp1 = panel1.xv;
2051 const auto& yp1 = panel1.yv;
2052 const auto& zp1 = panel1.zv;
2053 const auto& xp2 = panel2.xv;
2054 const auto& yp2 = panel2.yv;
2055 const auto& zp2 = panel2.zv;
2056 // If the size of either is less than 3, simply return.
2057 if (xp1.size() <= 2 || xp2.size() <= 2) {
2058 return true;
2059 }
2060 // Compute the various tolerances.
2061 const double xmin1 = *std::min_element(std::begin(xp1), std::end(xp1));
2062 const double ymin1 = *std::min_element(std::begin(yp1), std::end(yp1));
2063 const double xmax1 = *std::max_element(std::begin(xp1), std::end(xp1));
2064 const double ymax1 = *std::max_element(std::begin(yp1), std::end(yp1));
2065
2066 const double xmin2 = *std::min_element(std::begin(xp2), std::end(xp2));
2067 const double ymin2 = *std::min_element(std::begin(yp2), std::end(yp2));
2068 const double xmax2 = *std::max_element(std::begin(xp2), std::end(xp2));
2069 const double ymax2 = *std::max_element(std::begin(yp2), std::end(yp2));
2070
2071 const double xmin = std::min(xmin1, xmin2);
2072 const double ymin = std::min(ymin1, ymin2);
2073 const double xmax = std::max(xmax1, xmax2);
2074 const double ymax = std::max(ymax1, ymax2);
2075
2076 const double epsx = 1.e-6 * std::max(std::abs(xmax), std::abs(xmin));
2077 const double epsy = 1.e-6 * std::max(std::abs(ymax), std::abs(ymin));
2078
2079 const double zsum1 = std::accumulate(std::begin(zp1), std::end(zp1), 0.);
2080 const double zsum2 = std::accumulate(std::begin(zp2), std::end(zp2), 0.);
2081 const double zmean = (zsum1 + zsum2) / (zp1.size() + zp2.size());
2082
2083 std::array<std::vector<double>, 2> xl;
2084 std::array<std::vector<double>, 2> yl;
2085 std::array<std::vector<int>, 2> flags;
2086 std::array<std::vector<double>, 2> qs;
2087 // Establish the list of special points around polygon 1.
2088 AddPoints(xp1, yp1, xp2, yp2, xl[0], yl[0], flags[0], qs[0], epsx, epsy);
2089 // Establish the list of special points around polygon 2.
2090 AddPoints(xp2, yp2, xp1, yp1, xl[1], yl[1], flags[1], qs[1], epsx, epsy);
2091
2092 bool ok = true;
2093 // Look up the cross-links: from plane 1 (2) to plane 2 (1).
2094 std::array<std::vector<int>, 2> links;
2095 for (unsigned int ic = 0; ic < 2; ++ic) {
2096 const unsigned int n1 = xl[ic].size();
2097 links[ic].assign(n1, -1);
2098 const unsigned int jc = ic == 0 ? 1 : 0;
2099 const unsigned int n2 = xl[jc].size();
2100 for (unsigned int i = 0; i < n1; ++i) {
2101 unsigned int nFound = 0;
2102 for (unsigned int j = 0; j < n2; ++j) {
2103 if (fabs(xl[ic][i] - xl[jc][j]) < epsx &&
2104 fabs(yl[ic][i] - yl[jc][j]) < epsy) {
2105 ++nFound;
2106 links[ic][i] = j;
2107 }
2108 }
2109 if (nFound == 0 && (flags[ic][i] == 2 || flags[ic][i] == 3)) {
2110 std::cerr << m_className << "::EliminateOverlaps: "
2111 << "Warning. Expected match not found (" << ic << "-" << jc
2112 << ").\n";
2113 links[ic][i] = -1;
2114 ok = false;
2115 } else if (nFound > 1) {
2116 std::cerr << m_className << "::EliminateOverlaps: "
2117 << "Warning. More than 1 match found (" << ic << "-" << jc
2118 << ").\n";
2119 links[ic][i] = -1;
2120 ok = false;
2121 }
2122 }
2123 }
2124
2125 // List the points for debugging.
2126 if (m_debug) {
2127 for (unsigned int j = 0; j < 2; ++j) {
2128 std::cout << " Polygon " << j << "\n "
2129 << " No Type x y Q links\n";
2130 const unsigned int n = xl[j].size();
2131 for (unsigned int i = 0; i < n; ++i) {
2132 printf(" %3u %5d %13.6f %13.6f %5.3f %3d\n", i, flags[j][i],
2133 xl[j][i], yl[j][i], qs[j][i], links[j][i]);
2134 }
2135 }
2136 }
2137 if (!ok) return false;
2138
2139 for (unsigned int ic = 0; ic < 2; ++ic) {
2140 // See whether all of 1 (2) is inside 2 (1).
2141 bool allInside = true;
2142 const unsigned int np = xl[ic].size();
2143 for (unsigned int i = 0; i < np; ++i) {
2144 if (flags[ic][i] != 1) {
2145 allInside = false;
2146 break;
2147 }
2148 bool inside = false, edge = false;
2149 if (ic == 0) {
2150 Polygon::Inside(xp2, yp2, xl[ic][i], yl[ic][i], inside, edge);
2151 } else {
2152 Polygon::Inside(xp1, yp1, xl[ic][i], yl[ic][i], inside, edge);
2153 }
2154 if (!(inside || edge)) {
2155 allInside = false;
2156 break;
2157 }
2158 }
2159 if (allInside) {
2160 // Apparently 1 (2) really is fully inside 2 (1).
2161 if (ic == 0) {
2162 if (m_debug) std::cout << " Curve 0 fully inside 1.\n";
2163 // Write out curve 1.
2164 panelsOut.push_back(panel1);
2165 } else {
2166 if (m_debug) std::cout << " Curve 1 fully inside 0.\n";
2167 // Write out curve 2.
2168 panelsOut.push_back(panel2);
2169 }
2170 panelsOut.back().zv.assign(panelsOut.back().xv.size(), zmean);
2171 itypo.push_back(3);
2172 std::vector<Panel> newPanels;
2173 if (ic == 0) {
2174 if (!TraceEnclosed(xl[0], yl[0], xl[1], yl[1], panel2, newPanels)) {
2175 return false;
2176 }
2177 } else {
2178 if (!TraceEnclosed(xl[1], yl[1], xl[0], yl[0], panel1, newPanels)) {
2179 return false;
2180 }
2181 }
2182 for (auto& panel : newPanels) {
2183 panel.zv.assign(panel.xv.size(), zmean);
2184 if (ic == 0) {
2185 itypo.push_back(2);
2186 } else {
2187 itypo.push_back(1);
2188 }
2189 }
2190 panelsOut.insert(panelsOut.end(), newPanels.begin(), newPanels.end());
2191 return true;
2192 }
2193 }
2194
2195 for (unsigned int ic = 0; ic < 2; ++ic) {
2196 std::vector<Panel> newPanels;
2197 const unsigned int n = xl[ic].size();
2198 // Identify the parts of 1 (2) that are not overlapped, first mark.
2199 std::vector<bool> mark(n, false);
2200 bool done = false;
2201 while (!done) {
2202 if (m_debug) {
2203 std::cout << " Searching for starting point on " << ic << ".\n";
2204 }
2205 done = true;
2206 // Try and find a new starting point
2207 for (unsigned int i = 0; i < n; ++i) {
2208 const unsigned int ii = NextPoint(i, n);
2209 // Skip parts already processed.
2210 if (mark[i] || mark[ii]) continue;
2211 // Skip if mid point is inside other volume.
2212 bool inside = false, edge = false;
2213 const double xm = 0.5 * (xl[ic][i] + xl[ic][ii]);
2214 const double ym = 0.5 * (yl[ic][i] + yl[ic][ii]);
2215 if (ic == 0) {
2216 Polygon::Inside(xp2, yp2, xm, ym, inside, edge);
2217 } else {
2218 Polygon::Inside(xp1, yp1, xm, ym, inside, edge);
2219 }
2220 if (inside || edge) continue;
2221 // Found one.
2222 done = false;
2223
2224 if (ic == 0) {
2225 // Trace this part of 1 outside 2.
2226 TraceNonOverlap(xp1, yp1, xl[0], yl[0], xl[1], yl[1], flags[0],
2227 flags[1], links[0], links[1], mark, i, panel1,
2228 newPanels);
2229 } else {
2230 // Trace this part of 2 outside 1.
2231 TraceNonOverlap(xp2, yp2, xl[1], yl[1], xl[0], yl[0], flags[1],
2232 flags[0], links[1], links[0], mark, i, panel2,
2233 newPanels);
2234 }
2235 break;
2236 }
2237 }
2238 for (auto& panel : newPanels) {
2239 panel.zv.assign(panel.xv.size(), zmean);
2240 itypo.push_back(ic + 1);
2241 }
2242 panelsOut.insert(panelsOut.end(), newPanels.begin(), newPanels.end());
2243 if (m_debug) {
2244 std::cout << " No further non-overlapped areas of " << ic << ".\n";
2245 }
2246 }
2247
2248 // Look for the overlapped parts.
2249 std::vector<Panel> newPanels;
2250 const unsigned int n1 = xl[0].size();
2251 std::vector<bool> mark1(n1, false);
2252 bool done = false;
2253 while (!done) {
2254 done = true;
2255 if (m_debug) {
2256 std::cout << " Searching for starting point on overlap.\n";
2257 }
2258 for (unsigned int i = 0; i < n1; ++i) {
2259 // Skip points already processed.
2260 if (mark1[i]) continue;
2261 // Skip if not an edge point on both 1 and 2 or internal in 2.
2262 int ip1 = i;
2263 int ip2 = links[0][ip1];
2264 if (ip2 < 0 || flags[0][ip1] == 1) {
2265 bool inside = false, edge = false;
2266 Polygon::Inside(xp2, yp2, xl[0][ip1], yl[0][ip1], inside, edge);
2267 if (!(inside || edge)) continue;
2268 } else if (flags[1][ip2] == 1) {
2269 continue;
2270 }
2271 // Found one.
2272 done = false;
2273 TraceOverlap(xp1, yp1, xp2, yp2, xl[0], yl[0], xl[1], yl[1], flags[0],
2274 links[0], links[1], mark1, ip1, ip2, panel1, newPanels);
2275 break;
2276 }
2277 }
2278 for (auto& panel : newPanels) {
2279 panel.zv.assign(panel.xv.size(), zmean);
2280 itypo.push_back(3);
2281 }
2282 panelsOut.insert(panelsOut.end(), newPanels.begin(), newPanels.end());
2283 // Finished
2284 if (m_debug) std::cout << " No further overlapped areas.\n";
2285 return true;
2286}
2287
2288bool ComponentNeBem3d::TraceEnclosed(const std::vector<double>& xl1,
2289 const std::vector<double>& yl1,
2290 const std::vector<double>& xl2,
2291 const std::vector<double>& yl2,
2292 const Panel& panel2,
2293 std::vector<Panel>& panelsOut) const {
2294 const int n1 = xl1.size();
2295 const int n2 = xl2.size();
2296 // Find 2 non-crossing connections: JP1-JP2 and KP1-KP2.
2297 unsigned int nFound = 0;
2298 int jp1 = 0, jp2 = 0;
2299 int kp1 = 0, kp2 = 0;
2300 for (int ip1 = 0; ip1 < n1; ++ip1) {
2301 const double x1 = xl1[ip1];
2302 const double y1 = yl1[ip1];
2303 for (int ip2 = 0; ip2 < n2; ++ip2) {
2304 if (nFound > 0 && ip2 == jp2) continue;
2305 const double x2 = xl2[ip2];
2306 const double y2 = yl2[ip2];
2307 bool cross = false;
2308 for (int k = 0; k < n1; ++k) {
2309 const int kk = NextPoint(k, n1);
2310 if (k == ip1 || kk == ip1) continue;
2311 double xc = 0., yc = 0.;
2312 cross =
2313 Crossing(x1, y1, x2, y2, xl1[k], yl1[k], xl1[kk], yl1[kk], xc, yc);
2314 if (cross) break;
2315 }
2316 if (cross) continue;
2317 if (m_debug) std::cout << " No crossing with 1.\n";
2318 for (int k = 0; k < n2; ++k) {
2319 const int kk = NextPoint(k, n2);
2320 if (k == ip2 || kk == ip2) continue;
2321 double xc = 0., yc = 0.;
2322 cross =
2323 Crossing(x1, y1, x2, y2, xl2[k], yl2[k], xl2[kk], yl2[kk], xc, yc);
2324 if (cross) break;
2325 }
2326 if (cross) continue;
2327 if (nFound == 0) {
2328 jp1 = ip1;
2329 jp2 = ip2;
2330 if (m_debug) {
2331 std::cout << " 1st junction: " << jp1 << ", " << jp2 << ".\n";
2332 }
2333 ++nFound;
2334 break;
2335 } else {
2336 kp1 = ip1;
2337 kp2 = ip2;
2338 double xc = 0., yc = 0.;
2339 cross = Crossing(x1, y1, x2, y2, xl1[jp1], yl1[jp1], xl2[jp2], yl2[jp2],
2340 xc, yc);
2341 if (!cross) {
2342 if (m_debug) {
2343 std::cout << " 2nd junction: " << kp1 << ", " << kp2 << ".\n";
2344 }
2345 ++nFound;
2346 break;
2347 }
2348 }
2349 }
2350 if (nFound > 1) break;
2351 }
2352 if (nFound < 2) {
2353 std::cerr << m_className << "::TraceEnclosed: Found no cut-out.\n";
2354 return false;
2355 }
2356
2357 // Create part 1 of area 2.
2358 std::vector<double> xpl;
2359 std::vector<double> ypl;
2360 if (m_debug) std::cout << " Creating part 1 of area 2.\n";
2361 for (int ip1 = jp1; ip1 <= kp1; ++ip1) {
2362 if (m_debug) std::cout << " Adding " << ip1 << " on 1.\n";
2363 xpl.push_back(xl1[ip1]);
2364 ypl.push_back(yl1[ip1]);
2365 }
2366 // Try one way.
2367 int imax = jp2 < kp2 ? jp2 + n2 : jp2;
2368 int dir = +1;
2369 for (int i = kp2; i <= imax; ++i) {
2370 int ip2 = i % n2;
2371 if (m_debug) std::cout << " Adding " << ip2 << " on 2.\n";
2372 xpl.push_back(xl2[ip2]);
2373 ypl.push_back(yl2[ip2]);
2374 }
2375 // Check for undesirable crossings.
2376 bool ok = true;
2377 for (int ip1 = 0; ip1 < n1; ++ip1) {
2378 if (ip1 == jp1 || ip1 == kp1) continue;
2379 bool inside = false, edge = false;
2380 Polygon::Inside(xpl, ypl, xl1[ip1], yl1[ip1], inside, edge);
2381 if (inside) {
2382 ok = false;
2383 break;
2384 }
2385 }
2386 if (!ok) {
2387 // Use the other way if this failed
2388 if (m_debug) std::cout << " Trying the other direction.\n";
2389 xpl.resize(kp1 - jp1 + 1);
2390 ypl.resize(kp1 - jp1 + 1);
2391 imax = jp2 < kp2 ? kp2 : kp2 + n2;
2392 dir = -1;
2393 for (int i = imax; i >= jp2; --i) {
2394 const int ip2 = i % n2;
2395 if (m_debug) std::cout << " Adding " << ip2 << " on 2.\n";
2396 xpl.push_back(xl2[ip2]);
2397 ypl.push_back(yl2[ip2]);
2398 }
2399 }
2400
2401 // Save this part.
2402 Panel newPanel1 = panel2;
2403 newPanel1.xv = xpl;
2404 newPanel1.yv = ypl;
2405 panelsOut.push_back(std::move(newPanel1));
2406 if (m_debug) std::cout << " Part 1 has " << xpl.size() << " nodes.\n";
2407
2408 // Create part 2 of area 2.
2409 xpl.clear();
2410 ypl.clear();
2411 if (m_debug) std::cout << " Creating part 2 of area 2.\n";
2412 imax = jp1 + n1;
2413 for (int i = kp1; i <= imax; ++i) {
2414 const int ip1 = i % n1;
2415 if (m_debug) std::cout << " Adding " << ip1 << " on 1.\n";
2416 xpl.push_back(xl1[ip1]);
2417 ypl.push_back(yl1[ip1]);
2418 }
2419 // Add the part over area 2.
2420 if (dir == -1) {
2421 imax = jp2 > kp2 ? jp2 : jp2 + n2;
2422 for (int i = imax; i >= kp2; --i) {
2423 const int ip2 = i % n2;
2424 if (m_debug) std::cout << " Adding " << ip2 << " on 2.\n";
2425 xpl.push_back(xl2[ip2]);
2426 ypl.push_back(yl2[ip2]);
2427 }
2428 } else {
2429 imax = jp2 > kp2 ? kp2 + n2 : kp2;
2430 for (int i = jp2; i <= imax; ++i) {
2431 const int ip2 = i % n2;
2432 if (m_debug) std::cout << " Adding " << ip2 << " on 2.\n";
2433 xpl.push_back(xl2[ip2]);
2434 ypl.push_back(yl2[ip2]);
2435 }
2436 }
2437 // Save this part.
2438 Panel newPanel2 = panel2;
2439 newPanel2.xv = xpl;
2440 newPanel2.yv = ypl;
2441 panelsOut.push_back(std::move(newPanel2));
2442 if (m_debug) std::cout << " Part 1 has " << xpl.size() << " nodes.\n";
2443 return true;
2444}
2445
2446void ComponentNeBem3d::TraceNonOverlap(
2447 const std::vector<double>& xp1, const std::vector<double>& yp1,
2448 const std::vector<double>& xl1, const std::vector<double>& yl1,
2449 const std::vector<double>& xl2, const std::vector<double>& yl2,
2450 const std::vector<int>& flags1, const std::vector<int>& flags2,
2451 const std::vector<int>& links1, const std::vector<int>& links2,
2452 std::vector<bool>& mark1, int ip1, const Panel& panel1,
2453 std::vector<Panel>& panelsOut) const {
2454 const unsigned int n1 = xl1.size();
2455 const unsigned int n2 = xl2.size();
2456
2457 // Remember the starting point.
2458 const int is1 = ip1;
2459 std::vector<double> xpl;
2460 std::vector<double> ypl;
2461 // Add the starting point.
2462 xpl.push_back(xl1[ip1]);
2463 ypl.push_back(yl1[ip1]);
2464 mark1[ip1] = true;
2465 if (m_debug) {
2466 std::cout << " Start from point " << ip1 << " on curve 1.\n";
2467 }
2468 // Next point.
2469 ip1 = NextPoint(ip1, n1);
2470 xpl.push_back(xl1[ip1]);
2471 ypl.push_back(yl1[ip1]);
2472 mark1[ip1] = true;
2473 if (m_debug) {
2474 std::cout << " Next point is " << ip1 << " on curve 1.\n";
2475 }
2476
2477 // Keep track of the curve we are currently following.
2478 unsigned int il = 1;
2479 // Direction flag (-1: backward, 0: not set, 1: forward).
2480 int dir = 0;
2481 // End-of-curve flag.
2482 bool eoc = false;
2483 int ip2 = 0;
2484 while (!eoc) {
2485 if (il == 1 && flags1[std::max(ip1, 0)] == 1) {
2486 // On curve 1 and not on the edge of curve 2?
2487 ip1 = NextPoint(ip1, n1);
2488 if (ip1 == is1) {
2489 eoc = true;
2490 continue;
2491 }
2492 mark1[ip1] = true;
2493 xpl.push_back(xl1[ip1]);
2494 ypl.push_back(yl1[ip1]);
2495 if (m_debug) {
2496 std::cout << " Went to point " << ip1 << " on curve 1.\n";
2497 }
2498 } else if (il == 1) {
2499 // On curve 1 and on the edge of curve 2?
2500 ip2 = links1[ip1];
2501 bool added = false;
2502 if (dir == +1 || dir == 0) {
2503 const double xm = 0.5 * (xl2[ip2] + xl2[NextPoint(ip2, n2)]);
2504 const double ym = 0.5 * (yl2[ip2] + yl2[NextPoint(ip2, n2)]);
2505 bool inside = false, edge = false;
2506 Polygon::Inside(xp1, yp1, xm, ym, inside, edge);
2507 if (inside) {
2508 ip2 = NextPoint(ip2, n2);
2509 il = 2;
2510 dir = +1;
2511 ip1 = links2[ip2];
2512 if (ip1 == is1) {
2513 eoc = true;
2514 continue;
2515 } else if (ip1 >= 0) {
2516 mark1[ip1] = true;
2517 }
2518 xpl.push_back(xl2[ip2]);
2519 ypl.push_back(yl2[ip2]);
2520 added = true;
2521 if (m_debug) {
2522 std::cout << " Added point " << ip2 << " along 2 +.\n";
2523 }
2524 }
2525 }
2526 if (dir == -1 || dir == 0) {
2527 const double xm = 0.5 * (xl2[ip2] + xl2[PrevPoint(ip2, n2)]);
2528 const double ym = 0.5 * (yl2[ip2] + yl2[PrevPoint(ip2, n2)]);
2529 bool inside = false, edge = false;
2530 Polygon::Inside(xp1, yp1, xm, ym, inside, edge);
2531 if (inside) {
2532 ip2 = PrevPoint(ip2, n2);
2533 il = 2;
2534 dir = -1;
2535 ip1 = links2[ip2];
2536 if (ip1 == is1) {
2537 eoc = true;
2538 continue;
2539 } else if (ip1 >= 0) {
2540 mark1[ip1] = true;
2541 }
2542 xpl.push_back(xl2[ip2]);
2543 ypl.push_back(yl2[ip2]);
2544 added = true;
2545 if (m_debug) {
2546 std::cout << " Added point " << ip2 << " along 2 -\n";
2547 }
2548 }
2549 }
2550 if (!added) {
2551 ip1 = NextPoint(ip1, n1);
2552 if (ip1 == is1) {
2553 eoc = true;
2554 continue;
2555 } else if (ip1 >= 0) {
2556 mark1[ip1] = true;
2557 }
2558 xpl.push_back(xl1[ip1]);
2559 ypl.push_back(yl1[ip1]);
2560 if (m_debug) std::cout << " Continued over 1.\n";
2561 }
2562 } else if (il == 2 && flags2[std::max(ip2, 0)] == 1) {
2563 // On curve 2 normal vertex (outside 1 hopefully).
2564 ip2 = dir > 0 ? NextPoint(ip2, n2) : PrevPoint(ip2, n2);
2565 ip1 = links2[ip2];
2566 if (ip1 == is1) {
2567 eoc = true;
2568 continue;
2569 } else if (ip1 >= 0) {
2570 mark1[ip1] = true;
2571 }
2572 xpl.push_back(xl2[ip2]);
2573 ypl.push_back(yl2[ip2]);
2574 if (m_debug) {
2575 std::cout << " Went to point " << ip2 << " on 2.\n";
2576 }
2577 } else if (il == 2) {
2578 // On curve 2 and on edge of 1.
2579 ip1 = links2[ip2];
2580 ip1 = NextPoint(ip1, n1);
2581 il = 1;
2582 if (ip1 == is1) {
2583 eoc = true;
2584 continue;
2585 }
2586 xpl.push_back(xl1[ip1]);
2587 ypl.push_back(yl1[ip1]);
2588 if (m_debug) std::cout << " Resumed 1 at point " << ip1 << ".\n";
2589 } else {
2590 // Other cases should not occur.
2591 std::cerr << m_className << "::TraceNonOverlap: Unexpected case.\n";
2592 return;
2593 }
2594 }
2595
2596 Panel newPanel = panel1;
2597 newPanel.xv = xpl;
2598 newPanel.yv = ypl;
2599 panelsOut.push_back(std::move(newPanel));
2600 if (m_debug) {
2601 std::cout << " End of curve reached, " << xpl.size() << " points.\n";
2602 }
2603}
2604
2605void ComponentNeBem3d::TraceOverlap(
2606 const std::vector<double>& xp1, const std::vector<double>& yp1,
2607 const std::vector<double>& xp2, const std::vector<double>& yp2,
2608 const std::vector<double>& xl1, const std::vector<double>& yl1,
2609 const std::vector<double>& xl2, const std::vector<double>& yl2,
2610 const std::vector<int>& flags1, const std::vector<int>& links1,
2611 const std::vector<int>& links2, std::vector<bool>& mark1, int ip1, int ip2,
2612 const Panel& panel1, std::vector<Panel>& panelsOut) const {
2613 int ip1L = -1;
2614 int ip1LL = -1;
2615
2616 const unsigned int n1 = xl1.size();
2617 const unsigned int n2 = xl2.size();
2618
2619 // Remember the starting points.
2620 const int is1 = ip1;
2621 const int is2 = ip2;
2622 std::vector<double> xpl;
2623 std::vector<double> ypl;
2624 xpl.push_back(xl1[ip1]);
2625 ypl.push_back(yl1[ip1]);
2626 mark1[ip1] = true;
2627
2628 // Keep track of the curve we are currently following.
2629 unsigned int il = 1;
2630 // Direction flag (-1: backward, 0: not set, 1: forward).
2631 int dir = 0;
2632 // End-of-curve flag.
2633 bool eoc = false;
2634
2635 if (m_debug) {
2636 std::cout << " Start from point " << ip1 << " on curve " << il << "\n";
2637 }
2638 while (!eoc) {
2639 ip1LL = ip1L;
2640 ip1L = ip1;
2641 if (il == 1) {
2642 // We are on curve 1. Move to the next point.
2643 const int ii = NextPoint(ip1, n1);
2644 // Maybe finished over line 1?
2645 if (ii == is1) {
2646 eoc = true;
2647 continue;
2648 }
2649 // See whether the next point of 1 is on the edge or inside of 2.
2650 bool inside = false, edge = false;
2651 if (links1[ii] >= 0) {
2652 edge = true;
2653 } else if (flags1[ii] == 1) {
2654 Polygon::Inside(xp2, yp2, xl1[ii], yl1[ii], inside, edge);
2655 }
2656 // If it is, check that it doesn't leave 2 at any stage.
2657 if (inside || edge) {
2658 const double xm = 0.5 * (xl1[ip1] + xl1[ii]);
2659 const double ym = 0.5 * (yl1[ip1] + yl1[ii]);
2660 Polygon::Inside(xp2, yp2, xm, ym, inside, edge);
2661 }
2662 // If it is, continue over 1.
2663 if (inside || edge) {
2664 ip1 = ii;
2665 if (m_debug) {
2666 std::cout << " Continued to point " << ip1 << " on " << il
2667 << "\n";
2668 }
2669 xpl.push_back(xl1[ip1]);
2670 ypl.push_back(yl1[ip1]);
2671 mark1[ip1] = true;
2672 continue;
2673 }
2674 // Else we have to continue over 2, ensure we really are on curve 2.
2675 ip2 = links1[ip1];
2676 if (ip2 < 0) {
2677 std::cerr << m_className << "::TraceOverlap: "
2678 << "No point 2 reference found; abandoned.\n";
2679 return;
2680 }
2681 // Impose a direction on 2 to avoid returning.
2682 if (dir == 0) {
2683 if (links2[NextPoint(ip2, n2)] == ip1LL &&
2684 links2[PrevPoint(ip2, n2)] == ip1LL) {
2685 std::cerr << m_className << "::TraceOverlap: "
2686 << "Both 2+ and 2- return on 1; not stored.\n";
2687 return;
2688 } else if (links2[NextPoint(ip2, n2)] == ip1LL) {
2689 if (m_debug) {
2690 std::cout << " 2+ is a return to previous point on 1.\n";
2691 }
2692 dir = -1;
2693 } else if (links2[PrevPoint(ip2, n2)] == ip1LL) {
2694 if (m_debug) {
2695 std::cout << " 2- is a return to previous point on 1.\n";
2696 }
2697 dir = +1;
2698 } else {
2699 if (m_debug) std::cout << " Both ways are OK.\n";
2700 }
2701 }
2702 // If not, try to continue over 2 in the + direction.
2703 if (dir == +1 || dir == 0) {
2704 ip2 = NextPoint(ip2, n2);
2705 if (ip2 == is2) {
2706 if (m_debug) std::cout << " Return to start over 2+.\n";
2707 eoc = true;
2708 continue;
2709 }
2710 Polygon::Inside(xp1, yp1, xl2[ip2], yl2[ip2], inside, edge);
2711 if (inside || edge) {
2712 if (m_debug) {
2713 std::cout << " Going to 2+ (point " << ip2 << " of 2).\n";
2714 }
2715 xpl.push_back(xl2[ip2]);
2716 ypl.push_back(yl2[ip2]);
2717 dir = +1;
2718 if (links2[ip2] >= 0) {
2719 ip1 = links2[ip2];
2720 mark1[ip1] = true;
2721 il = 1;
2722 if (m_debug) {
2723 std::cout << " This point is also on curve 1: " << ip1
2724 << "\n";
2725 }
2726 } else {
2727 il = 2;
2728 }
2729 continue;
2730 }
2731 // Continuing in the + direction didn't work so go back a step.
2732 ip2 = PrevPoint(ip2, n2);
2733 }
2734 // Or if this still fails, try 2 in the - direction.
2735 if (dir == -1 || dir == 0) {
2736 ip2 = PrevPoint(ip2, n2);
2737 if (ip2 == is2) {
2738 if (m_debug) std::cout << " Return to start over 2-\n";
2739 eoc = true;
2740 continue;
2741 }
2742 Polygon::Inside(xp1, yp1, xl2[ip2], yl2[ip2], inside, edge);
2743 if (inside || edge) {
2744 if (m_debug) {
2745 std::cout << " Going to 2- (point " << ip2 << " of 2).\n";
2746 }
2747 xpl.push_back(xl2[ip2]);
2748 ypl.push_back(yl2[ip2]);
2749 dir = -1;
2750 if (links2[ip2] >= 0) {
2751 ip1 = links2[ip2];
2752 mark1[ip1] = true;
2753 il = 1;
2754 if (m_debug) {
2755 std::cout << " This point is also on 1: " << ip1 << ".\n";
2756 }
2757 } else {
2758 il = 2;
2759 }
2760 continue;
2761 }
2762 }
2763 // Should not get here.
2764 if (m_debug) std::cout << " Dead end.\n";
2765 return;
2766 } else if (il == 2) {
2767 // We are on curve 2. Ensure the direction is set.
2768 if (dir == 0) {
2769 std::cerr << m_className << "::TraceOverlap: "
2770 << "Direction not set; abandoned.\n";
2771 return;
2772 }
2773 // Move to the next point.
2774 ip2 = dir > 0 ? NextPoint(ip2, n2) : PrevPoint(ip2, n2);
2775 // Maybe finished over line 2?
2776 if (ip2 == is2) {
2777 // Reached the end.
2778 eoc = true;
2779 continue;
2780 }
2781 // Next step over 2.
2782 if (m_debug) {
2783 std::cout << " Stepped over 2 to point " << ip2 << " of 2.\n";
2784 }
2785 xpl.push_back(xl2[ip2]);
2786 ypl.push_back(yl2[ip2]);
2787 if (links2[ip2] >= 0) {
2788 ip1 = links2[ip2];
2789 mark1[ip1] = true;
2790 il = 1;
2791 if (m_debug) {
2792 std::cout << " This point is also on curve 1: " << ip1 << ".\n";
2793 }
2794 } else {
2795 il = 2;
2796 }
2797 }
2798 }
2799
2800 if (xpl.size() <= 2) {
2801 if (m_debug) std::cout << " Too few points.\n";
2802 } else {
2803 Panel newPanel = panel1;
2804 newPanel.xv = xpl;
2805 newPanel.yv = ypl;
2806 panelsOut.push_back(std::move(newPanel));
2807 }
2808
2809 if (m_debug) {
2810 std::cout << " End of curve reached, " << xpl.size() << " points.\n";
2811 }
2812}
2813
2814bool ComponentNeBem3d::MakePrimitives(const Panel& panelIn,
2815 std::vector<Panel>& panelsOut) const {
2816 // *-----------------------------------------------------------------------
2817 // * PLATRC - Cuts a polygon into right-angled triangles.
2818 // *-----------------------------------------------------------------------
2819
2820 // Establish tolerances.
2821 // TODO! Class member?
2822 const double epsang = 1.e-6; // BEMEPA
2823
2824 if (panelIn.xv.empty() || panelIn.yv.empty() || panelIn.zv.empty()) {
2825 return false;
2826 }
2827 // Determine the mean z value.
2828 const double zsum =
2829 std::accumulate(std::begin(panelIn.zv), std::end(panelIn.zv), 0.);
2830 const double zmean = zsum / panelIn.zv.size();
2831
2832 std::vector<Panel> stack;
2833 stack.push_back(panelIn);
2834 stack.back().zv.clear();
2835 for (unsigned int k = 0; k < stack.size(); ++k) {
2836 // Next polygon.
2837 const auto& xp1 = stack[k].xv;
2838 const auto& yp1 = stack[k].yv;
2839 const unsigned int np = xp1.size();
2840 if (m_debug) {
2841 std::cout << " Polygon " << k << " with " << np << " nodes.\n";
2842 }
2843 if (np <= 2) {
2844 // Too few nodes.
2845 if (m_debug) std::cout << " Too few points.\n";
2846 continue;
2847 }
2848
2849 // See whether this is a right-angled triangle.
2850 if (np == 3) {
2851 const double x12 = xp1[0] - xp1[1];
2852 const double y12 = yp1[0] - yp1[1];
2853 const double x23 = xp1[1] - xp1[2];
2854 const double y23 = yp1[1] - yp1[2];
2855 const double x31 = xp1[2] - xp1[0];
2856 const double y31 = yp1[2] - yp1[0];
2857 const double x32 = xp1[2] - xp1[1];
2858 const double y32 = yp1[2] - yp1[1];
2859 const double x13 = xp1[0] - xp1[2];
2860 const double y13 = yp1[0] - yp1[2];
2861 const double x21 = xp1[1] - xp1[0];
2862 const double y21 = yp1[1] - yp1[0];
2863 const double s12 = x12 * x12 + y12 * y12;
2864 const double s32 = x32 * x32 + y32 * y32;
2865 const double s13 = x13 * x13 + y13 * y13;
2866 const double s23 = x23 * x23 + y23 * y23;
2867 const double s31 = x31 * x31 + y31 * y31;
2868 const double s21 = x21 * x21 + y21 * y21;
2869 if (fabs(x12 * x32 + y12 * y32) < epsang * sqrt(s12 * s32)) {
2870 if (m_debug) {
2871 std::cout << " Right-angled triangle node 2 - done.\n";
2872 }
2873 panelsOut.push_back(stack[k]);
2874 continue;
2875 } else if (fabs(x13 * x23 + y13 * y23) < epsang * sqrt(s13 * s23)) {
2876 if (m_debug) {
2877 std::cout << " Right-angled triangle node 3 - rearrange.\n";
2878 }
2879 Panel panel = stack[k];
2880 panel.xv = {xp1[1], xp1[2], xp1[0]};
2881 panel.yv = {yp1[1], yp1[2], yp1[0]};
2882 panelsOut.push_back(std::move(panel));
2883 continue;
2884 } else if (fabs(x31 * x21 + y31 * y21) < epsang * sqrt(s31 * s21)) {
2885 if (m_debug) {
2886 std::cout << " Right-angled triangle node 1 - rearrange.\n";
2887 }
2888 Panel panel = stack[k];
2889 panel.xv = {xp1[2], xp1[0], xp1[1]};
2890 panel.yv = {yp1[2], yp1[0], yp1[1]};
2891 panelsOut.push_back(std::move(panel));
2892 continue;
2893 }
2894 }
2895 // See whether this is a rectangle.
2896 if (np == 4) {
2897 const double x12 = xp1[0] - xp1[1];
2898 const double y12 = yp1[0] - yp1[1];
2899 const double x23 = xp1[1] - xp1[2];
2900 const double y23 = yp1[1] - yp1[2];
2901 const double x34 = xp1[2] - xp1[3];
2902 const double y34 = yp1[2] - yp1[3];
2903 const double x43 = xp1[3] - xp1[2];
2904 const double y43 = yp1[3] - yp1[2];
2905 const double x14 = xp1[0] - xp1[3];
2906 const double y14 = yp1[0] - yp1[3];
2907 const double x32 = xp1[2] - xp1[1];
2908 const double y32 = yp1[2] - yp1[1];
2909
2910 const double s12 = x12 * x12 + y12 * y12;
2911 const double s23 = x23 * x23 + y23 * y23;
2912 const double s32 = x32 * x32 + y32 * y32;
2913 const double s34 = x34 * x34 + y34 * y34;
2914 const double s43 = x43 * x43 + y43 * y43;
2915 const double s14 = x14 * x14 + y14 * y14;
2916 if (fabs(x12 * x32 + y12 * y32) < epsang * sqrt(s12 * s32) &&
2917 fabs(x23 * x43 + y23 * y43) < epsang * sqrt(s23 * s43) &&
2918 fabs(x14 * x34 + y14 * y34) < epsang * sqrt(s14 * s34)) {
2919 if (m_debug) std::cout << " Rectangle.\n";
2920 panelsOut.push_back(stack[k]);
2921 continue;
2922 }
2923 }
2924 // See whether there are parallel sides, e.g. a trapezium (UK English).
2925 if (np >= 4 && SplitTrapezium(stack[k], stack, panelsOut, epsang)) continue;
2926
2927 // Find a right-angled corner we can cut off.
2928 if (m_debug) std::cout << " Trying to find a right-angle\n";
2929 bool corner = false;
2930 for (unsigned int ip = 0; ip < np; ++ip) {
2931 // Take only right angles.
2932 const unsigned int inext = NextPoint(ip, np);
2933 const unsigned int iprev = PrevPoint(ip, np);
2934
2935 const double dxprev = xp1[iprev] - xp1[ip];
2936 const double dyprev = yp1[iprev] - yp1[ip];
2937 const double dxnext = xp1[inext] - xp1[ip];
2938 const double dynext = yp1[inext] - yp1[ip];
2939 if (fabs(dxprev * dxnext + dyprev * dynext) >
2940 epsang * sqrt((dxprev * dxprev + dyprev * dyprev) *
2941 (dxnext * dxnext + dynext * dynext))) {
2942 continue;
2943 }
2944 // Ensure the midpoint is internal.
2945 if (np > 3) {
2946 const double xm = 0.5 * (xp1[iprev] + xp1[inext]);
2947 const double ym = 0.5 * (yp1[iprev] + yp1[inext]);
2948 bool inside = false, edge = false;
2949 Polygon::Inside(xp1, yp1, xm, ym, inside, edge);
2950 if (!inside) continue;
2951 }
2952 // Check all vertex crossings.
2953 bool cross = false;
2954 for (unsigned int jp = 0; jp < np; ++jp) {
2955 // Accept immediate contact.
2956 const unsigned int jnext = NextPoint(jp, np);
2957 if (jp == iprev || jp == ip || jp == inext || jnext == iprev ||
2958 jnext == ip || jnext == inext)
2959 continue;
2960 // Check crossing.
2961 double xc = 0., yc = 0.;
2962 if (Crossing(xp1[iprev], yp1[iprev], xp1[inext], yp1[inext], xp1[jp],
2963 yp1[jp], xp1[jnext], yp1[jnext], xc, yc)) {
2964 cross = true;
2965 break;
2966 }
2967 }
2968 if (cross) continue;
2969 // Found a triangle, introduce shorthand node references.
2970 if (m_debug) {
2971 std::cout << " Cutting at right-angled corner " << ip << "\n";
2972 }
2973 corner = true;
2974 Panel panel = stack[k];
2975 panel.xv = {xp1[iprev], xp1[ip], xp1[inext]};
2976 panel.yv = {yp1[iprev], yp1[ip], yp1[inext]};
2977 stack.push_back(std::move(panel));
2978 // Eliminate this node from the polygon.
2979 stack[k].xv.erase(stack[k].xv.begin() + ip);
2980 stack[k].yv.erase(stack[k].yv.begin() + ip);
2981 stack.push_back(std::move(stack[k]));
2982 if (m_debug) {
2983 std::cout << " Going for another pass, NP = " << np << "\n";
2984 }
2985 break;
2986 }
2987 if (corner) continue;
2988
2989 // Find any corner we can cut off.
2990 if (m_debug) std::cout << " Trying to find a corner\n";
2991 corner = false;
2992 for (unsigned int ip = 0; ip < np; ++ip) { // 20
2993 const unsigned int iprev = PrevPoint(ip, np);
2994 const unsigned int inext = NextPoint(ip, np);
2995 // Ensure the midpoint is internal.
2996 if (np > 3) {
2997 const double xm = 0.5 * (xp1[iprev] + xp1[inext]);
2998 const double ym = 0.5 * (yp1[iprev] + yp1[inext]);
2999 bool inside = false, edge = false;
3000 Polygon::Inside(xp1, yp1, xm, ym, inside, edge);
3001 if (!inside) continue;
3002 }
3003 // Check all vertex crossings.
3004 bool cross = false;
3005 for (unsigned int jp = 0; jp < np; ++jp) {
3006 const unsigned int jj = NextPoint(jp, np);
3007 // Accept immediate contact.
3008 if (jp == iprev || jp == ip || jp == inext || jj == iprev || jj == ip ||
3009 jj == inext)
3010 continue;
3011 // Check crossing.
3012 double xc = 0., yc = 0.;
3013 if (Crossing(xp1[iprev], yp1[iprev], xp1[inext], yp1[inext], xp1[jp],
3014 yp1[jp], xp1[jj], yp1[jj], xc, yc)) {
3015 cross = true;
3016 break;
3017 }
3018 }
3019 if (cross) continue;
3020 // Found a triangle, introduce shorthand node references.
3021 if (m_debug) std::cout << " Cutting at corner " << ip << "\n";
3022 corner = true;
3023 const double x1 = xp1[iprev];
3024 const double x2 = xp1[ip];
3025 const double x3 = xp1[inext];
3026 const double y1 = yp1[iprev];
3027 const double y2 = yp1[ip];
3028 const double y3 = yp1[inext];
3029 const double s21 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
3030 const double s31 = (x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1);
3031 const double s32 = (x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2);
3032 const double s12 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
3033 const double s13 = (x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3);
3034 const double s23 = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3);
3035 // Find the biggest opening angle.
3036 const double a1 =
3037 ((x2 - x1) * (x3 - x1) + (y2 - y1) * (y3 - y1)) / sqrt(s21 * s31);
3038 const double a2 =
3039 ((x3 - x2) * (x1 - x2) + (y3 - y2) * (y1 - y2)) / sqrt(s32 * s12);
3040 const double a3 =
3041 ((x1 - x3) * (x2 - x3) + (y1 - y3) * (y2 - y3)) / sqrt(s13 * s23);
3042 if (m_debug) {
3043 const double phi1 = acos(a1);
3044 const double phi2 = acos(a2);
3045 const double phi3 = acos(a3);
3046 std::cout << " Angles: " << RadToDegree * phi1 << ", "
3047 << RadToDegree * phi2 << ", " << RadToDegree * phi3 << "\n";
3048 const double sumphi = phi1 + phi2 + phi3;
3049 std::cout << " Sum = " << RadToDegree * sumphi << "\n";
3050 }
3051 // See whether one angle is more or less right-angled.
3052 if (fabs(a1) < epsang || fabs(a2) < epsang || fabs(a3) < epsang) {
3053 if (m_debug) std::cout << " Right-angled corner cut off.\n";
3054 Panel panel = stack[k];
3055 if (fabs(a1) < epsang) {
3056 panel.xv = {x3, x1, x2};
3057 panel.yv = {y3, y1, y2};
3058 } else if (fabs(a2) < epsang) {
3059 panel.xv = {x1, x2, x3};
3060 panel.yv = {y1, y2, y3};
3061 } else {
3062 panel.xv = {x2, x3, x1};
3063 panel.yv = {y2, y3, y1};
3064 }
3065 stack.push_back(std::move(panel));
3066 } else if (a1 <= a2 && a1 <= a3) {
3067 if (m_debug) std::cout << " A1 < A2, A3 - adding 2 triangles.\n";
3068 const double xc = x2 + a2 * (x3 - x2) * sqrt(s12 / s32);
3069 const double yc = y2 + a2 * (y3 - y2) * sqrt(s12 / s32);
3070 Panel panel1 = stack[k];
3071 panel1.xv = {x3, xc, x1};
3072 panel1.yv = {y3, yc, y1};
3073 stack.push_back(std::move(panel1));
3074 Panel panel2 = stack[k];
3075 panel2.xv = {x2, xc, x1};
3076 panel2.yv = {y2, yc, y1};
3077 stack.push_back(std::move(panel2));
3078 } else if (a2 <= a1 && a2 <= a3) {
3079 if (m_debug) std::cout << " A2 < A1, A3 - adding 2 triangles.\n";
3080 const double xc = x3 + a3 * (x1 - x3) * sqrt(s23 / s13);
3081 const double yc = y3 + a3 * (y1 - y3) * sqrt(s23 / s13);
3082 Panel panel1 = stack[k];
3083 panel1.xv = {x1, xc, x2};
3084 panel1.yv = {y1, yc, y2};
3085 stack.push_back(std::move(panel1));
3086 Panel panel2 = stack[k];
3087 panel2.xv = {x3, xc, x2};
3088 panel2.yv = {y3, yc, y2};
3089 stack.push_back(std::move(panel2));
3090 } else {
3091 if (m_debug) std::cout << " A3 < A1, A2 - adding 2 triangles.\n";
3092 const double xc = x1 + a1 * (x2 - x1) * sqrt(s31 / s21);
3093 const double yc = y1 + a1 * (y2 - y1) * sqrt(s31 / s21);
3094 Panel panel1 = stack[k];
3095 panel1.xv = {x1, xc, x3};
3096 panel1.yv = {y1, yc, y3};
3097 stack.push_back(std::move(panel1));
3098 Panel panel2 = stack[k];
3099 panel2.xv = {x2, xc, x3};
3100 panel2.yv = {y2, yc, y3};
3101 stack.push_back(std::move(panel2));
3102 }
3103 // Eliminate this node from the polygon.
3104 stack[k].xv.erase(stack[k].xv.begin() + ip);
3105 stack[k].yv.erase(stack[k].yv.begin() + ip);
3106 stack.push_back(std::move(stack[k]));
3107 if (m_debug) {
3108 std::cout << " Going for another pass, NP = " << np << "\n";
3109 }
3110 break;
3111 }
3112 if (corner) continue;
3113 std::cerr << m_className << "::MakePrimitives:\n "
3114 << "Unable to identify a corner to cut, probably a degenerate "
3115 "polygon.\n";
3116 // Next stack element.
3117 }
3118 for (auto& panel : panelsOut) {
3119 panel.zv.assign(panel.xv.size(), zmean);
3120 }
3121 return true;
3122}
3123
3124bool ComponentNeBem3d::SplitTrapezium(const Panel panelIn,
3125 std::vector<Panel>& stack,
3126 std::vector<Panel>& panelsOut,
3127 const double epsang) const {
3128 const auto xp1 = panelIn.xv;
3129 const auto yp1 = panelIn.yv;
3130 const unsigned int np = xp1.size();
3131 for (unsigned int ip = 0; ip < np; ++ip) {
3132 const unsigned int inext = NextPoint(ip, np);
3133 const double xi0 = xp1[ip];
3134 const double yi0 = yp1[ip];
3135 const double xi1 = xp1[inext];
3136 const double yi1 = yp1[inext];
3137 const double dxi = xi0 - xi1;
3138 const double dyi = yi0 - yi1;
3139 const double si2 = dxi * dxi + dyi * dyi;
3140 for (unsigned int jp = ip + 2; jp < np; ++jp) {
3141 const unsigned int jnext = NextPoint(jp, np);
3142 // Skip adjacent segments.
3143 if (ip == jp || ip == jnext || inext == jp || inext == jnext) {
3144 continue;
3145 }
3146 const double xj0 = xp1[jp];
3147 const double yj0 = yp1[jp];
3148 const double xj1 = xp1[jnext];
3149 const double yj1 = yp1[jnext];
3150 const double dxj = xj0 - xj1;
3151 const double dyj = yj0 - yj1;
3152 const double sj2 = dxj * dxj + dyj * dyj;
3153 // Require parallelism.
3154 const double sij = sqrt(si2 * sj2);
3155 if (fabs(dxi * dxj + dyi * dyj + sij) > epsang * sij) continue;
3156 if (m_debug) {
3157 std::cout << " Found parallel sections: " << ip << ", " << jp
3158 << "\n";
3159 }
3160 // Avoid division by zero.
3161 if (sj2 <= 0 || si2 <= 0) {
3162 std::cerr << m_className << "::SplitTrapezium:\n "
3163 << "Zero norm segment found; skipped.\n";
3164 continue;
3165 }
3166 // Establish the cutting lines.
3167 const double xl1 =
3168 ((xi0 - xj0) * (xj1 - xj0) + (yi0 - yj0) * (yj1 - yj0)) / sj2;
3169 const double xl2 =
3170 ((xi1 - xj0) * (xj1 - xj0) + (yi1 - yj0) * (yj1 - yj0)) / sj2;
3171 const double xl3 =
3172 ((xj0 - xi0) * (xi1 - xi0) + (yj0 - yi0) * (yi1 - yi0)) / si2;
3173 const double xl4 =
3174 ((xj1 - xi0) * (xi1 - xi0) + (yj1 - yi0) * (yi1 - yi0)) / si2;
3175 if (m_debug) {
3176 std::cout << " xl1 = " << xl1 << ", xl2 = " << xl2 << ", "
3177 << "xl3 = " << xl3 << ", xl4 = " << xl4 << "\n";
3178 }
3179 // Check that there is at all a rectangle.
3180 const double r1 = (xl1 + epsang) * (1. + epsang - xl1);
3181 const double r2 = (xl2 + epsang) * (1. + epsang - xl2);
3182 const double r3 = (xl3 + epsang) * (1. + epsang - xl3);
3183 const double r4 = (xl4 + epsang) * (1. + epsang - xl4);
3184 if ((r1 < 0 && r4 < 0) || (r2 < 0 && r3 < 0)) {
3185 if (m_debug) std::cout << " No rectangle.\n";
3186 continue;
3187 }
3188 // Determine the rectangular part.
3189 std::vector<double> xpl(4, 0.);
3190 std::vector<double> ypl(4, 0.);
3191 if (r1 >= 0) {
3192 xpl[0] = xi0;
3193 ypl[0] = yi0;
3194 xpl[1] = xj0 + xl1 * (xj1 - xj0);
3195 ypl[1] = yj0 + xl1 * (yj1 - yj0);
3196 } else if (r4 >= 0) {
3197 xpl[0] = xi0 + xl4 * (xi1 - xi0);
3198 ypl[0] = yi0 + xl4 * (yi1 - yi0);
3199 xpl[1] = xj1;
3200 ypl[1] = yj1;
3201 }
3202 if (r2 >= 0) {
3203 xpl[2] = xj0 + xl2 * (xj1 - xj0);
3204 ypl[2] = yj0 + xl2 * (yj1 - yj0);
3205 xpl[3] = xi1;
3206 ypl[3] = yi1;
3207 } else if (r3 >= 0) {
3208 xpl[2] = xj0;
3209 ypl[2] = yj0;
3210 xpl[3] = xi0 + xl3 * (xi1 - xi0);
3211 ypl[3] = yi0 + xl3 * (yi1 - yi0);
3212 }
3213 // Verify that the midpoints of these lines are internal.
3214 double xm = 0.5 * (xpl[0] + xpl[1]);
3215 double ym = 0.5 * (ypl[0] + ypl[1]);
3216 bool inside = false, edge = false;
3217 Polygon::Inside(xp1, yp1, xm, ym, inside, edge);
3218 if (!(inside || edge)) {
3219 if (m_debug) std::cout << " Midpoint 1 not internal.\n";
3220 continue;
3221 }
3222 xm = 0.5 * (xpl[2] + xpl[3]);
3223 ym = 0.5 * (ypl[2] + ypl[3]);
3224 Polygon::Inside(xp1, yp1, xm, ym, inside, edge);
3225 if (!(inside || edge)) {
3226 if (m_debug) std::cout << " Midpoint 2 not internal.\n";
3227 continue;
3228 }
3229
3230 const unsigned int iprev = PrevPoint(ip, np);
3231 const unsigned int jprev = PrevPoint(jp, np);
3232 // Ensure there are no crossings, accepting contact.
3233 bool cross = false;
3234 for (unsigned int i = 0; i < np; ++i) {
3235 if ((i == iprev && r1 >= 0) || i == ip || (i == inext && r2 >= 0) ||
3236 (i == jprev && r3 >= 0) || i == jp || (i == jnext && r4 >= 0))
3237 continue;
3238 const unsigned int ii = NextPoint(i, np);
3239 double xc = 0., yc = 0.;
3240 if (Crossing(xp1[i], yp1[i], xp1[ii], yp1[ii], xpl[0], ypl[0], xpl[1],
3241 ypl[1], xc, yc) ||
3242 Crossing(xp1[i], yp1[i], xp1[ii], yp1[ii], xpl[2], ypl[2], xpl[3],
3243 ypl[3], xc, yc)) {
3244 if (m_debug) {
3245 std::cout << " Crossing (edge " << i << ", " << ii
3246 << "), ip = " << ip << ", jp = " << jp << "\n";
3247 }
3248 cross = true;
3249 break;
3250 }
3251 }
3252 if (cross) continue;
3253 // Add the rectangular part.
3254 if ((fabs(xl1) < epsang && fabs(xl3) < epsang) ||
3255 (fabs(1. - xl2) < epsang && fabs(1. - xl4) < epsang)) {
3256 if (m_debug) std::cout << " Not stored, degenerate.\n";
3257 } else {
3258 if (m_debug) std::cout << " Adding rectangle.\n";
3259 Panel panel = panelIn;
3260 panel.xv = xpl;
3261 panel.yv = ypl;
3262 panelsOut.push_back(std::move(panel));
3263 }
3264 // First non-rectangular section.
3265 xpl.clear();
3266 ypl.clear();
3267 if (m_debug) std::cout << " First non-rectangular section.\n";
3268 for (unsigned int i = jp + 1; i <= ip + np; ++i) {
3269 const unsigned int ii = i % np;
3270 xpl.push_back(xp1[ii]);
3271 ypl.push_back(yp1[ii]);
3272 }
3273 if (r1 >= 0 && r4 >= 0) {
3274 if (m_debug) std::cout << " 1-4 degenerate\n";
3275 } else if (r1 >= 0) {
3276 if (m_debug) std::cout << " Using 1\n";
3277 xpl.push_back(xj0 + xl1 * (xj1 - xj0));
3278 ypl.push_back(yj0 + xl1 * (yj1 - yj0));
3279 } else if (r4 >= 0) {
3280 if (m_debug) std::cout << " Using 4\n";
3281 xpl.push_back(xi0 + xl4 * (xi1 - xi0));
3282 ypl.push_back(yi0 + xl4 * (yi1 - yi0));
3283 } else {
3284 if (m_debug) std::cout << " Neither 1 nor 4, should not happen\n";
3285 }
3286 if (xpl.size() < 3) {
3287 if (m_debug) {
3288 std::cout << " Not stored, only " << xpl.size()
3289 << " vertices.\n";
3290 }
3291 } else {
3292 if (m_debug) {
3293 std::cout << " Adding non-rectangular part with " << xpl.size()
3294 << " vertices.\n";
3295 }
3296 Panel panel = panelIn;
3297 panel.xv = xpl;
3298 panel.yv = ypl;
3299 stack.push_back(std::move(panel));
3300 }
3301 // Second non-rectangular section.
3302 xpl.clear();
3303 ypl.clear();
3304 if (m_debug) std::cout << " Second non-rectangular section.\n";
3305 for (unsigned int i = ip + 1; i <= jp; ++i) {
3306 const unsigned int ii = i % np;
3307 xpl.push_back(xp1[ii]);
3308 ypl.push_back(yp1[ii]);
3309 }
3310 if (r2 >= 0 && r3 >= 0) {
3311 if (m_debug) std::cout << " 2-3 degenerate\n";
3312 } else if (r2 >= 0) {
3313 if (m_debug) std::cout << " Using 2\n";
3314 xpl.push_back(xj0 + xl2 * (xj1 - xj0));
3315 ypl.push_back(yj0 + xl2 * (yj1 - yj0));
3316 } else if (r3 >= 0) {
3317 if (m_debug) std::cout << " Using 3\n";
3318 xpl.push_back(xi0 + xl3 * (xi1 - xi0));
3319 ypl.push_back(yi0 + xl3 * (yi1 - yi0));
3320 } else {
3321 if (m_debug) std::cout << " Neither 2 nor 3, should not happen\n";
3322 }
3323 if (xpl.size() < 3) {
3324 if (m_debug) {
3325 std::cout << " Not stored, only " << xpl.size()
3326 << " vertices.\n";
3327 }
3328 } else {
3329 if (m_debug) {
3330 std::cout << " Adding non-rectangular part with " << xpl.size()
3331 << " vertices.\n";
3332 }
3333 Panel panel = panelIn;
3334 panel.xv = xpl;
3335 panel.yv = ypl;
3336 stack.push_back(std::move(panel));
3337 }
3338 return true;
3339 }
3340 }
3341 return false;
3342}
3343
3344bool ComponentNeBem3d::GetPrimitive(const unsigned int i, double& a, double& b,
3345 double& c, std::vector<double>& xv,
3346 std::vector<double>& yv,
3347 std::vector<double>& zv, int& interface,
3348 double& v, double& q,
3349 double& lambda) const {
3350 if (i >= m_primitives.size()) {
3351 std::cerr << m_className << "::GetPrimitive: Index out of range.\n";
3352 return false;
3353 }
3354 const auto& primitive = m_primitives[i];
3355 a = primitive.a;
3356 b = primitive.b;
3357 c = primitive.c;
3358 xv = primitive.xv;
3359 yv = primitive.yv;
3360 zv = primitive.zv;
3361 interface = primitive.interface;
3362 v = primitive.v;
3363 q = primitive.q;
3364 lambda = primitive.lambda;
3365 return true;
3366}
3367
3368bool ComponentNeBem3d::GetPrimitive(const unsigned int i, double& a, double& b,
3369 double& c, std::vector<double>& xv,
3370 std::vector<double>& yv,
3371 std::vector<double>& zv, int& vol1,
3372 int& vol2) const {
3373 if (i >= m_primitives.size()) {
3374 std::cerr << m_className << "::GetPrimitive: Index out of range.\n";
3375 return false;
3376 }
3377 const auto& primitive = m_primitives[i];
3378 a = primitive.a;
3379 b = primitive.b;
3380 c = primitive.c;
3381 xv = primitive.xv;
3382 yv = primitive.yv;
3383 zv = primitive.zv;
3384 vol1 = primitive.vol1;
3385 vol2 = primitive.vol2;
3386 return true;
3387}
3388
3389bool ComponentNeBem3d::GetVolume(const unsigned int vol, int& shape,
3390 int& material, double& epsilon,
3391 double& potential, double& charge, int& bc) {
3392 if (!m_geometry) return false;
3393 const unsigned int nSolids = m_geometry->GetNumberOfSolids();
3394 for (unsigned int i = 0; i < nSolids; ++i) {
3395 Medium* medium = nullptr;
3396 const auto solid = m_geometry->GetSolid(i, medium);
3397 if (!solid) continue;
3398 if (solid->GetId() != vol) continue;
3399 if (solid->IsTube() || solid->IsWire()) {
3400 shape = 1;
3401 } else if (solid->IsHole()) {
3402 shape = 2;
3403 } else if (solid->IsBox()) {
3404 shape = 3;
3405 } else if (solid->IsSphere()) {
3406 shape = 4;
3407 } else if (solid->IsRidge()) {
3408 shape = 5;
3409 } else if (solid->IsExtrusion()) {
3410 shape = 6;
3411 } else {
3412 std::cerr << m_className << "::GetVolume: Unknown solid shape.\n";
3413 return false;
3414 }
3415 material = medium ? medium->GetId() : 11;
3416 epsilon = medium ? medium->GetDielectricConstant() : 1.;
3417 potential = solid->GetBoundaryPotential();
3418 charge = solid->GetBoundaryChargeDensity();
3419 bc = solid->GetBoundaryConditionType();
3420 return true;
3421 }
3422 return false;
3423}
3424
3425int ComponentNeBem3d::GetVolume(const double x, const double y,
3426 const double z) {
3427 if (!m_geometry) return -1;
3428 const unsigned int nSolids = m_geometry->GetNumberOfSolids();
3429 for (unsigned int i = 0; i < nSolids; ++i) {
3430 Medium* medium = nullptr;
3431 const auto solid = m_geometry->GetSolid(i, medium);
3432 if (!solid) continue;
3433 if (solid->IsInside(x, y, z)) return solid->GetId();
3434 }
3435 return -1;
3436}
3437
3438bool ComponentNeBem3d::GetElement(const unsigned int i, std::vector<double>& xv,
3439 std::vector<double>& yv,
3440 std::vector<double>& zv, int& interface,
3441 double& bc, double& lambda) const {
3442 if (i >= m_elements.size()) {
3443 std::cerr << m_className << "::GetElement: Index out of range.\n";
3444 return false;
3445 }
3446 const auto& element = m_elements[i];
3447 xv = element.xv;
3448 yv = element.yv;
3449 zv = element.zv;
3450 interface = element.interface;
3451 bc = element.bc;
3452 lambda = element.lambda;
3453 return true;
3454}
3455
3457 m_primitives.clear();
3458 m_elements.clear();
3459 m_ynplan.fill(false);
3460 m_coplan.fill(0.);
3461 m_vtplan.fill(0.);
3462 m_ready = false;
3463}
3464
3466 for (unsigned int i = 0; i < 3; ++i) {
3467 // Cannot have regular and mirror periodicity at the same time.
3468 if (m_periodic[i] && m_mirrorPeriodic[i]) {
3469 std::cerr << m_className << "::UpdatePeriodicity:\n"
3470 << " Both simple and mirror periodicity requested. Reset.\n";
3471 m_periodic[i] = false;
3472 m_mirrorPeriodic[i] = false;
3473 continue;
3474 }
3475 if ((m_periodic[i] || m_mirrorPeriodic[i]) && m_periodicLength[i] < Small) {
3476 std::cerr << m_className << "::UpdatePeriodicity:\n"
3477 << " Periodic length is not set. Reset.\n";
3478 m_periodic[i] = m_mirrorPeriodic[i] = false;
3479 }
3480 }
3481
3483 std::cerr << m_className << "::UpdatePeriodicity:\n"
3484 << " Axial periodicity is not available.\n";
3485 m_axiallyPeriodic.fill(false);
3486 }
3487
3490 std::cerr << m_className << "::UpdatePeriodicity:\n"
3491 << " Rotation symmetry is not available.\n";
3492 m_rotationSymmetric.fill(false);
3493 }
3494}
3495} // namespace Garfield
bool GetPlaneZ(const unsigned int i, double &z, double &v) const
Retrieve the parameters of a plane at constant z.
bool GetPlaneY(const unsigned int i, double &y, double &v) const
Retrieve the parameters of a plane at constant y.
void SetSystemChargeZero(const unsigned int OptSystemChargeZero)
void AddPlaneZ(const double z, const double voltage)
Add a plane at constant z.
bool GetPeriodicityX(double &s) const
Get the periodic length in the x-direction.
void SetFastVolBlocks(const unsigned int NbBlocksFV)
void SetStoreElements(const unsigned int OptStoreElements)
void SetWtFldFastVolVersion(const unsigned int IdWtField, const unsigned int VersionWtFldFV)
bool GetPeriodicityZ(double &s) const
Get the periodic length in the z-direction.
void Reset() override
Reset the component.
void SetUnformattedFile(const unsigned int OptUnformattedFile)
void SetMirrorPeriodicityX(const double s)
Set the periodic length [cm] in the x-direction.
void SetPeriodicityX(const double s)
Set the periodic length [cm] in the x-direction.
void SetWtFldFastVolOptions(const unsigned int IdWtField, const unsigned int OptWtFldFastVol, const unsigned int OptCreateWtFldFastPF, const unsigned int OptReadWtFldFastPF)
void UpdatePeriodicity() override
Verify periodicities.
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
void SetStoreReadOptions(const unsigned int OptStoreInflMatrix, const unsigned int OptReadInflMatrix, const unsigned int OptStoreInvMatrix, const unsigned int OptReadInvMatrix, const unsigned int OptStorePrimitives, const unsigned int OptReadPrimitives, const unsigned int OptStoreElements, const unsigned int OptReadElements, const unsigned int OptFormattedFile, const unsigned int OptUnformattedFile)
unsigned int GetNumberOfPlanesY() const
Get the number of equipotential planes at constant y.
bool GetPeriodicityY(double &s) const
Get the periodic length in the y-direction.
void SetReadPrimitives(const unsigned int OptReadPrimitives)
unsigned int GetNumberOfPlanesZ() const
Get the number of equipotential planes at constant z.
void SetReadInvMatrix(const unsigned int OptReadInvMatrix)
void AddPlaneY(const double y, const double voltage)
Add a plane at constant y.
void AddPlaneX(const double x, const double voltage)
Add a plane at constant x.
void SetForceValidation(const unsigned int OptForceValidation)
void SetRepeatLHMatrix(const unsigned int OptRepeatLHMatrix)
void SetValidateSolution(const unsigned int OptValidateSolution)
void SetMirrorPeriodicityZ(const double s)
Set the periodic length [cm] in the z-direction.
void SetPeriodicityZ(const double s)
Set the periodic length [cm] in the z-direction.
bool GetVolume(const unsigned int vol, int &shape, int &material, double &eps, double &potential, double &charge, int &bc)
void SetPeriodicityY(const double s)
Set the periodic length [cm] in the y-direction.
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
void SetNewMesh(const unsigned int NewMesh)
unsigned int GetNumberOfPlanesX() const
Get the number of equipotential planes at constant x.
bool GetElement(const unsigned int i, std::vector< double > &xv, std::vector< double > &yv, std::vector< double > &zv, int &interface, double &bc, double &lambda) const
void SetTargetElementSize(const double length)
void SetStoreInvMatrix(const unsigned int OptStoreInvMatrix)
void SetWtFldFastVolBlocks(const unsigned int IdWtField, const unsigned int NbBlocksWtFldFV)
void SetMirrorPeriodicityY(const double s)
Set the periodic length [cm] in the y-direction.
void SetPeriodicCopies(const unsigned int nx, const unsigned int ny, const unsigned int nz)
void SetComputeOptions(const unsigned int OptSystemChargeZero, const unsigned int OptValidateSolution, const unsigned int OptForceValidation, const unsigned int OptRepeatLHMatrix)
bool GetPrimitive(const unsigned int i, double &a, double &b, double &c, std::vector< double > &xv, std::vector< double > &yv, std::vector< double > &zv, int &interface, double &v, double &q, double &lambda) const
void SetFastVolOptions(const unsigned int OptFastVol, const unsigned int OptCreateFastPF, const unsigned int OptReadFastPF)
void SetMinMaxNumberOfElements(const unsigned int nmin, const unsigned int nmax)
void SetReadInflMatrix(const unsigned int OptReadInflMatrix)
void SetChargingUpOptions(const unsigned int OptChargingUp)
void SetNewBC(const unsigned int NewBC)
void SetModelOptions(const unsigned int NewModel, const unsigned int NewMesh, const unsigned int NewBC, const unsigned int NewPP)
void SetFastVolVersion(const unsigned int VersionFV)
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status) override
void SetStoreInflMatrix(const unsigned int OptStoreInflMatrix)
void SetNewPP(const unsigned int NewPP)
void SetNewModel(const unsigned int NewModel)
void SetReadElements(const unsigned int OptReadElements)
bool GetPlaneX(const unsigned int i, double &x, double &v) const
Retrieve the parameters of a plane at constant x.
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
void SetKnownChargeOptions(const unsigned int OptKnownCharge)
void SetStorePrimitives(const unsigned int OptStorePrimitives)
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
void SetFormattedFile(const unsigned int OptFormattedFile)
std::array< bool, 3 > m_rotationSymmetric
Rotation symmetry around x-axis, y-axis, z-axis.
Definition Component.hh:380
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
Definition Component.hh:376
bool m_debug
Switch on/off debugging messages.
Definition Component.hh:371
Component()=delete
Default constructor.
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition Component.hh:374
std::string m_className
Class name.
Definition Component.hh:359
Geometry * m_geometry
Pointer to the geometry.
Definition Component.hh:362
bool m_ready
Ready for use?
Definition Component.hh:368
std::array< bool, 3 > m_axiallyPeriodic
Axial periodicity in x, y, z.
Definition Component.hh:378
Abstract base class for media.
Definition Medium.hh:16
double GetDielectricConstant() const
Get the relative static dielectric constant.
Definition Medium.hh:45
int GetId() const
Return the id number of the class instance.
Definition Medium.hh:24
virtual bool IsConductor() const
Is this medium a conductor?
Definition Medium.hh:32
bool IsDriftable() const
Is charge carrier transport enabled in this medium?
Definition Medium.hh:77
void Inside(const std::vector< double > &xpl, const std::vector< double > &ypl, const double x, const double y, bool &inside, bool &edge)
Definition Polygon.cc:187
ComponentNeBem3d * gComponentNeBem3d
DoubleAc acos(const DoubleAc &f)
Definition DoubleAc.cpp:490
DoubleAc fabs(const DoubleAc &f)
Definition DoubleAc.h:615
DoubleAc sqrt(const DoubleAc &f)
Definition DoubleAc.cpp:314
neBEMGLOBAL int OptRepeatLHMatrix
Definition neBEM.h:58
neBEMGLOBAL int OptReadPrimitives
Definition neBEM.h:52
neBEMGLOBAL int OptStorePrimitives
Definition neBEM.h:51
neBEMGLOBAL int NewModel
Definition neBEM.h:180
neBEMGLOBAL int OptFormattedFile
Definition neBEM.h:56
neBEMGLOBAL int OptChargingUp
Definition neBEM.h:60
neBEMGLOBAL int NewMesh
Definition neBEM.h:180
neBEMGLOBAL int OptWtFldFastVol[MAXWtFld]
Definition neBEM.h:492
neBEMGLOBAL int NbBlocksWtFldFV[MAXWtFld]
Definition neBEM.h:497
neBEMGLOBAL int VersionFV
Definition neBEM.h:422
neBEMGLOBAL int OptCreateFastPF
Definition neBEM.h:420
#define MAXWtFld
Definition neBEM.h:30
neBEMGLOBAL double * Lambda
Definition neBEM.h:82
neBEMGLOBAL int OptSystemChargeZero
Definition neBEM.h:125
neBEMGLOBAL int NewPP
Definition neBEM.h:180
neBEMGLOBAL int OptCreateWtFldFastPF[MAXWtFld]
Definition neBEM.h:494
neBEMGLOBAL int OptFastVol
Definition neBEM.h:418
neBEMGLOBAL int * InterfaceType
Definition neBEM.h:75
neBEMGLOBAL int OptReadFastPF
Definition neBEM.h:421
neBEMGLOBAL int OptStoreInflMatrix
Definition neBEM.h:47
neBEMGLOBAL int OptReadInflMatrix
Definition neBEM.h:48
neBEMGLOBAL int OptForceValidation
Definition neBEM.h:45
neBEMGLOBAL int OptUnformattedFile
Definition neBEM.h:57
neBEMGLOBAL int OptReadInvMatrix
Definition neBEM.h:50
neBEMGLOBAL int NbBlocksFV
Definition neBEM.h:423
neBEMGLOBAL int VersionWtFldFV[MAXWtFld]
Definition neBEM.h:496
neBEMGLOBAL int OptValidateSolution
Definition neBEM.h:44
neBEMGLOBAL int OptReadElements
Definition neBEM.h:55
neBEMGLOBAL int OptStoreElements
Definition neBEM.h:54
neBEMGLOBAL int OptStoreInvMatrix
Definition neBEM.h:49
neBEMGLOBAL int OptReadWtFldFastPF[MAXWtFld]
Definition neBEM.h:495
neBEMGLOBAL int NewBC
Definition neBEM.h:180
Surface panel.
Definition Solid.hh:11
std::vector< double > zv
Z-coordinates of vertices.
Definition Solid.hh:19
int volume
Reference to solid to which the panel belongs.
Definition Solid.hh:23
double a
Perpendicular vector.
Definition Solid.hh:13
std::vector< double > xv
X-coordinates of vertices.
Definition Solid.hh:15
std::vector< double > yv
Y-coordinates of vertices.
Definition Solid.hh:17