Garfield++ v1r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentNeBem2d.cc
Go to the documentation of this file.
1#include <iostream>
2#include <iomanip>
3#include <fstream>
4#include <cmath>
5
6#include "ComponentNeBem2d.hh"
7#include "Random.hh"
10
11namespace Garfield {
12
14 : projAxis(2),
15 nDivisions(5),
16 nCollocationPoints(3),
17 minSize(1.e-3),
18 autoSize(false),
19 randomCollocation(false),
20 nMaxIterations(3),
21 nPanels(0),
22 nWires(0),
23 nElements(0),
24 matrixInversionFlag(false) {
25
26 m_className = "ComponentNeBem2d";
27
28 influenceMatrix.clear();
29 inverseMatrix.clear();
30}
31
32void ComponentNeBem2d::ElectricField(const double x, const double y,
33 const double z, double& ex, double& ey,
34 double& ez, double& v, Medium*& m,
35 int& status) {
36
37 ex = ey = ez = v = 0.;
38 status = 0;
39 // Check if the requested point is inside a medium
40 m = GetMedium(x, y, z);
41 if (m == NULL) {
42 status = -6;
43 return;
44 }
45
46 if (!ready) {
47 if (!Initialise()) {
48 std::cerr << m_className << "::ElectricField:\n";
49 std::cerr << " Initialisation failed.\n";
50 status = -11;
51 return;
52 }
53 ready = true;
54 }
55
56 double dx = 0., dy = 0.;
57 double xLoc, yLoc;
58 double fx, fy, u;
59
60 // Sum up the contributions from all boundary elements
61 for (int i = nElements; i--;) {
62 dx = x - elements[i].cX;
63 dy = y - elements[i].cY;
64 // Transform to local coordinate system
65 Rotate(dx, dy, elements[i].phi, Global2Local, xLoc, yLoc);
66 // Compute the potential
67 if (!ComputePotential(elements[i].geoType, elements[i].len, xLoc, yLoc,
68 u)) {
69 std::cerr << m_className << "::ElectricField:\n";
70 std::cerr << " Potential contribution from element " << i
71 << " could not be calculated.\n";
72 status = -11;
73 return;
74 }
75 // Compute the field
76 if (!ComputeFlux(elements[i].geoType, elements[i].len, elements[i].phi,
77 xLoc, yLoc, fx, fy)) {
78 std::cerr << m_className << "::ElectricField:\n";
79 std::cerr << " Field contribution from element " << i
80 << " could not be calculated.\n";
81 status = -11;
82 return;
83 }
84 v += u * elements[i].solution;
85 ex += fx * elements[i].solution;
86 ey += fy * elements[i].solution;
87 }
88}
89
90void ComponentNeBem2d::ElectricField(const double x, const double y,
91 const double z, double& ex, double& ey,
92 double& ez, Medium*& m, int& status) {
93
94 double v = 0.;
95 ElectricField(x, y, z, ex, ey, ez, v, m, status);
96}
97
98bool ComponentNeBem2d::GetVoltageRange(double& vmin, double& vmax) {
99
100 if (nPanels <= 0 && nWires <= 0) return false;
101 bool gotValue = false;
102
103 for (int i = nPanels; i--;) {
104 if (panels[i].bcType != 0) continue;
105 if (!gotValue) {
106 vmin = vmax = panels[i].bcValue;
107 gotValue = true;
108 } else {
109 if (panels[i].bcValue < vmin) vmin = panels[i].bcValue;
110 if (panels[i].bcValue > vmax) vmax = panels[i].bcValue;
111 }
112 }
113
114 for (int i = nWires; i--;) {
115 if (!gotValue) {
116 vmin = vmax = wires[i].bcValue;
117 gotValue = true;
118 } else {
119 if (wires[i].bcValue < vmin) vmin = wires[i].bcValue;
120 if (wires[i].bcValue > vmax) vmax = wires[i].bcValue;
121 }
122 }
123
124 return gotValue;
125}
126
127void ComponentNeBem2d::AddPanel(const double x0, const double y0,
128 const double x1, const double y1,
129 const int bctype, const double bcval,
130 const double lambda) {
131
132 const double dx = x1 - x0;
133 const double dy = y1 - y0;
134 if (dx * dx + dy * dy <= Small) {
135 std::cerr << m_className << "::AddPanel:\n";
136 std::cerr << " Panel length must be greater than zero.\n";
137 return;
138 }
139
140 panel newPanel;
141 newPanel.x0 = x0;
142 newPanel.y0 = y0;
143 newPanel.x1 = x1;
144 newPanel.y1 = y1;
145 if (bctype < 0 || bctype > 3) {
146 std::cerr << m_className << "::AddPanel:\n";
147 std::cerr << " Unknown boundary condition type: " << bctype << "\n";
148 return;
149 }
150 newPanel.bcType = bctype;
151 newPanel.bcValue = bcval;
152 newPanel.lambda = lambda;
153 panels.push_back(newPanel);
154 ++nPanels;
155
156 if (debug) {
157 std::cout << m_className << "::AddPanel:\n";
158 std::cout << " From: (" << x0 << ", " << y0 << ")\n";
159 std::cout << " To: (" << x1 << ", " << y1 << ")\n";
160 switch (bctype) {
161 case 0:
162 std::cout << " Type: Conductor\n";
163 std::cout << " Potential: " << bcval << " V\n";
164 break;
165 case 1:
166 std::cout << " Floating conductor\n";
167 break;
168 case 2:
169 std::cout << " Dielectric-dielectric interface\n";
170 std::cout << " Lambda: " << lambda << "\n";
171 break;
172 case 3:
173 std::cout << " Surface charge\n";
174 break;
175 default:
176 std::cout << " Unknown boundary condition (program bug!)\n";
177 }
178 }
179
180 ready = false;
181 matrixInversionFlag = false;
182}
183
184void ComponentNeBem2d::AddWire(const double x0, const double y0, const double d,
185 const double bcval) {
186
187 if (d < Small) {
188 std::cerr << m_className << "::AddWire:\n";
189 std::cerr << " Wire diameter must be greater than zero.\n";
190 return;
191 }
192
193 wire newWire;
194 newWire.cX = x0;
195 newWire.cY = y0;
196 newWire.d = d;
197 newWire.bcValue = bcval;
198 wires.push_back(newWire);
199 ++nWires;
200
201 if (debug) {
202 std::cout << m_className << "::AddWire:\n";
203 std::cout << " Center: (" << x0 << ", " << y0 << ")\n";
204 std::cout << " Diameter: " << d << " cm\n";
205 std::cout << " Potential: " << bcval << " V\n";
206 }
207
208 ready = false;
209 matrixInversionFlag = false;
210}
211
213
214 if (ndiv <= 0) {
215 std::cerr << m_className << "::SetNumberOfDivisions:\n";
216 std::cerr << " Number of divisions must be greater than zero.\n";
217 return;
218 }
219
220 nDivisions = ndiv;
221 ready = false;
222 matrixInversionFlag = false;
223}
224
226
227 if (ncoll <= 0) {
228 std::cerr << m_className << "::SetNumberOfCollocationPoints:\n";
229 std::cerr << " Number of coll. points must be greater than zero.\n";
230 return;
231 }
232
233 nCollocationPoints = ncoll;
234 ready = false;
235 matrixInversionFlag = false;
236}
237
239
240 if (min < Small) {
241 std::cerr << m_className << "::SetMinimumElementSize:\n";
242 std::cerr << " Provided element size is too small.\n";
243 return;
244 }
245
246 minSize = min;
247 ready = false;
248 matrixInversionFlag = false;
249}
250
252
253 if (niter <= 0) {
254 std::cerr << m_className << "::SetMaxNumberOfIterations:\n";
255 std::cerr << " Number of iterations must be greater than zero.\n";
256 return;
257 }
258
259 nMaxIterations = niter;
260}
261
262bool ComponentNeBem2d::Initialise() {
263
264 // Break up panels into elements
265 if (!Discretise()) {
266 std::cerr << m_className << "::Initialise:\n";
267 std::cerr << " Discretisation failed.\n";
268 return false;
269 }
270 if (debug) {
271 std::cout << m_className << "::Initialise:\n";
272 std::cout << " Discretisation ok.\n";
273 }
274
275 bool converged = false;
276 int nIter = 0;
277 while (!converged) {
278 ++nIter;
279 if (autoSize) {
280 std::cout << m_className << "::Initialise:\n";
281 std::cout << " Iteration " << nIter << "\n";
282 }
283 // Compute the influence matrix
284 if (!ComputeInfluenceMatrix()) {
285 std::cerr << m_className << "::Initialise:\n";
286 std::cerr << " Error computing the influence matrix.\n";
287 return false;
288 }
289
290 // Invert the influence matrix
291 if (!InvertMatrix()) {
292 std::cerr << m_className << "::Initialise:\n";
293 std::cerr << " Error inverting the influence matrix.\n";
294 return false;
295 }
296
297 if (debug) {
298 std::cout << m_className << "::Initialise:\n";
299 std::cout << " Matrix inversion ok.\n";
300 }
301
302 // Compute the right hand side vector
303 if (!GetBoundaryConditions()) {
304 std::cerr << m_className << "::Initialise:\n";
305 std::cerr << " Error computing the potential vector.\n";
306 return false;
307 }
308
309 // Solve for the charge distribution
310 if (!Solve()) {
311 std::cerr << m_className << "::Initialise:\n";
312 std::cerr << " Error in Solve function.\n";
313 return false;
314 }
315 if (debug) {
316 std::cout << m_className << "::Initialise:\n";
317 std::cout << " Solution ok.\n";
318 }
319
320 converged = CheckConvergence();
321 if (!autoSize) break;
322 if (nIter >= nMaxIterations) break;
323 }
324
325 return true;
326}
327
328bool ComponentNeBem2d::Discretise() {
329
330 elements.clear();
331 nElements = 0;
332 element newElement;
333
334 if (debug) {
335 std::cout << m_className << "::Discretise:\n";
336 std::cout << " Panel BC Type Bc Value Rotation Length\n";
337 }
338 newElement.geoType = 0;
339 double dx = 0., dy = 0.;
340 for (int j = nPanels; j--;) {
341 newElement.bcType = panels[j].bcType;
342 newElement.bcValue = panels[j].bcValue;
343 newElement.lambda = panels[j].lambda;
344 dx = panels[j].x1 - panels[j].x0;
345 dy = panels[j].y1 - panels[j].y0;
346 newElement.phi = atan2(dy, dx);
347 newElement.len = 0.5 * sqrt(dx * dx + dy * dy) / nDivisions;
348 dx /= nDivisions;
349 dy /= nDivisions;
350 newElement.cX = panels[j].x0 - 0.5 * dx;
351 newElement.cY = panels[j].y0 - 0.5 * dy;
352 for (int i = 0; i < nDivisions; ++i) {
353 newElement.cX += dx;
354 newElement.cY += dy;
355 elements.push_back(newElement);
356 ++nElements;
357 if (debug) {
358 std::cout << " " << j << " " << newElement.bcType << " "
359 << newElement.bcValue << " " << newElement.phi << " "
360 << newElement.len << "\n";
361 }
362 }
363 }
364
365 newElement.geoType = 1;
366 newElement.phi = 0.;
367 // Treat wires always as conductors
368 newElement.bcType = 0;
369 newElement.lambda = 1.;
370 for (int j = nWires; j--;) {
371 newElement.cX = wires[j].cX;
372 newElement.cY = wires[j].cY;
373 newElement.len = wires[j].d / 2.;
374 newElement.bcValue = wires[j].bcValue;
375 elements.push_back(newElement);
376 ++nElements;
377 }
378
379 return true;
380}
381
382bool ComponentNeBem2d::ComputeInfluenceMatrix() {
383
384 if (matrixInversionFlag) return true;
385
386 // Coordinates, rotation and length of target and source element
387 double xF, yF, phiF;
388 double xS, yS, phiS, lenS;
389 // Geometric type
390 int gtS;
391 // Boundary type of target element
392 int etF;
393
394 // Global and local distance
395 double dx, dy;
396 double du, dv;
397 // Field components in local and global coordinates
398 double fx, fy;
399 double ex, ey;
400
401 // Influence coefficient
402 double infCoeff;
403
404 // Re-dimension the influence matrix
405 int nEntries = nElements + 1;
406 influenceMatrix.resize(nEntries);
407 for (int i = nEntries; i--;) influenceMatrix[i].resize(nEntries);
408
409 // Loop over the target elements (F)
410 for (int iF = 0; iF < nElements; ++iF) {
411 phiF = elements[iF].phi;
412 // Boundary type
413 etF = elements[iF].bcType;
414 // Collocation point
415 xF = elements[iF].cX;
416 yF = elements[iF].cY;
417
418 // Loop over the source elements (S)
419 for (int jS = 0; jS < nElements; ++jS) {
420 gtS = elements[jS].geoType;
421 xS = elements[jS].cX;
422 yS = elements[jS].cY;
423 phiS = elements[jS].phi;
424 lenS = elements[jS].len;
425 // Transform to local coordinate system of source element
426 dx = xF - xS;
427 dy = yF - yS;
428 Rotate(dx, dy, phiS, Global2Local, du, dv);
429 infCoeff = 0.;
430 // Depending on the element type at the field point
431 // different boundary conditions need to be applied
432 switch (etF) {
433 // Conductor at fixed potential
434 case 0:
435 if (!ComputePotential(gtS, lenS, du, dv, infCoeff)) {
436 return false;
437 }
438 break;
439 // Floating conductor (not implemented)
440 case 1:
441 if (!ComputePotential(gtS, lenS, du, dv, infCoeff)) {
442 return false;
443 }
444 break;
445 // Dielectric-dielectric interface
446 // Normal component of the displacement vector is continuous
447 case 2:
448 if (iF == jS) {
449 // Self-influence
450 infCoeff = 1. / (2. * elements[jS].lambda * VacuumPermittivity);
451 } else {
452 // Compute flux at field point in global coordinate system
453 if (!ComputeFlux(gtS, lenS, phiS, du, dv, fx, fy)) {
454 return false;
455 }
456 // Rotate to local coordinate system of field element
457 Rotate(fx, fy, phiF, Global2Local, ex, ey);
458 infCoeff = ey;
459 }
460 break;
461 default:
462 std::cerr << m_className << "::ComputeInfluenceMatrix:\n";
463 std::cerr << " Unknown boundary type: " << etF << ".\n";
464 return false;
465 break;
466 }
467 influenceMatrix[iF][jS] = infCoeff;
468 }
469 }
470
471 // Add charge neutrality condition
472 for (int i = 0; i < nElements; ++i) {
473 influenceMatrix[nElements][i] = elements[i].len;
474 influenceMatrix[i][nElements] = 0.;
475 }
476 influenceMatrix[nElements][nElements] = 0.;
477
478 return true;
479}
480
481void ComponentNeBem2d::SplitElement(const int iel) {
482
483 // Make sure the element is a line
484 if (elements[iel].geoType != 0) return;
485
486 double phi = elements[iel].phi;
487 double len = elements[iel].len / 2.;
488 elements[iel].len = len;
489 double x0 = elements[iel].cX;
490 double y0 = elements[iel].cY;
491
492 double dx = 0., dy = 0.;
493 Rotate(len, 0., phi, Local2Global, dx, dy);
494
495 element newElement;
496 newElement.geoType = elements[iel].geoType;
497 newElement.len = elements[iel].len;
498 newElement.phi = elements[iel].phi;
499 newElement.bcType = elements[iel].bcType;
500 newElement.bcValue = elements[iel].bcValue;
501 newElement.lambda = elements[iel].lambda;
502
503 elements[iel].cX = x0 + dx;
504 elements[iel].cY = y0 + dy;
505 newElement.cX = x0 - dx;
506 newElement.cY = y0 - dx;
507
508 elements.push_back(newElement);
509 ++nElements;
510}
511
512bool ComponentNeBem2d::InvertMatrix() {
513
514 // Check if matrix inversion has already been done
515 if (matrixInversionFlag) return true;
516
517 const int nEntries = nElements + 1;
518
519 // Initialise the inverse influence matrix
520 inverseMatrix.resize(nEntries);
521 for (int i = nEntries; i--;) inverseMatrix[i].resize(nEntries);
522
523 // Initialise temporary arrays for LU decomposition/substitution
524 col.resize(nEntries);
525 index.resize(nEntries);
526
527 // Decompose the influence matrix
528 if (!LUDecomposition()) {
529 std::cerr << m_className << "::InvertMatrix:\n";
530 std::cerr << " LU Decomposition failed.\n";
531 return false;
532 }
533
534 // Invert the matrix
535 for (int j = 0; j < nEntries; ++j) {
536 for (int i = 0; i < nEntries; ++i) col[i] = 0.;
537 col[j] = 1.;
538 LUSubstitution();
539 for (int i = 0; i < nEntries; ++i) inverseMatrix[i][j] = col[i];
540 }
541
542 // Clear the influence matrix and the temporary arrays
543 influenceMatrix.clear();
544 col.clear();
545 index.clear();
546
547 // Set flag that the matrix has been inverted
548 matrixInversionFlag = true;
549
550 return true;
551}
552
553bool ComponentNeBem2d::LUDecomposition() {
554
555 // The influence matrix is replaced by the LU decomposition of a rowwise
556 // permutation of itself.
557 // The implementation is based on:
558 // W. H. Press,
559 // Numerical recipes in C++: the Art of Scientific Computing (version 2.11)
560
561 const int n = nElements;
562
563 // v stores the implicit scaling of each row
564 std::vector<double> v;
565 v.resize(n);
566
567 int i, j, k;
568 // Loop over rows to get the implicit scaling information.
569 double big = 0., temp = 0.;
570 for (i = 0; i < n; ++i) {
571 big = 0.;
572 for (j = 0; j < n; ++j) {
573 temp = fabs(influenceMatrix[i][j]);
574 if (temp > big) big = temp;
575 }
576 if (big == 0.) return false;
577 // Save the scaling
578 v[i] = 1. / big;
579 }
580
581 // Loop over columns
582 double sum = 0., dum = 0.;
583 int imax = 0;
584 for (j = 0; j < n; ++j) {
585 for (i = 0; i < j; ++i) {
586 sum = influenceMatrix[i][j];
587 for (k = 0; k < i; ++k)
588 sum -= influenceMatrix[i][k] * influenceMatrix[k][j];
589 influenceMatrix[i][j] = sum;
590 }
591 // Initialise for the search for the largest pivot element
592 big = 0.;
593 for (i = j; i < n; ++i) {
594 sum = influenceMatrix[i][j];
595 for (k = 0; k < j; ++k)
596 sum -= influenceMatrix[i][k] * influenceMatrix[k][j];
597 influenceMatrix[i][j] = sum;
598 // Is the figure of merit for the pivot better than the best so far?
599 dum = v[i] * fabs(sum);
600 if (dum >= big) {
601 big = dum;
602 imax = i;
603 }
604 }
605 // Do we need to interchange rows?
606 if (j != imax) {
607 for (k = 0; k < n; ++k) {
608 dum = influenceMatrix[imax][k];
609 influenceMatrix[imax][k] = influenceMatrix[j][k];
610 influenceMatrix[j][k] = dum;
611 }
612 // Interchange the scale factor
613 v[imax] = v[j];
614 }
615 index[j] = imax;
616 if (influenceMatrix[j][j] == 0.) influenceMatrix[j][j] = Small;
617 if (j != n - 1) {
618 // Divide by the pivot element
619 dum = 1. / (influenceMatrix[j][j]);
620 for (i = j + 1; i < n; ++i) influenceMatrix[i][j] *= dum;
621 }
622 }
623
624 return true;
625}
626
627void ComponentNeBem2d::LUSubstitution() {
628
629 const int n = nElements;
630
631 double sum = 0.;
632 int i, j;
633 int ii = 0, ip = 0;
634
635 // Forward substitution
636 for (i = 0; i < n; ++i) {
637 ip = index[i];
638 sum = col[ip];
639 col[ip] = col[i];
640 if (ii != 0) {
641 for (j = ii - 1; j < i; ++j) sum -= influenceMatrix[i][j] * col[j];
642 } else if (sum != 0.) {
643 ii = i + 1;
644 }
645 col[i] = sum;
646 }
647
648 // Backsubstitution
649 for (i = n - 1; i >= 0; i--) {
650 sum = col[i];
651 for (j = i + 1; j < n; ++j) sum -= influenceMatrix[i][j] * col[j];
652 col[i] = sum / influenceMatrix[i][i];
653 }
654}
655
656bool ComponentNeBem2d::GetBoundaryConditions() {
657
658 const int nEntries = nElements + 1;
659
660 // Initialise the right-hand side vector
661 boundaryConditions.resize(nEntries);
662
663 for (int i = nElements; i--;) {
664 switch (elements[i].bcType) {
665 // Conductor at fixed potential
666 case 0:
667 boundaryConditions[i] = elements[i].bcValue;
668 break;
669 // Floating conductor
670 case 1:
671 boundaryConditions[i] = 0.;
672 break;
673 // Dielectric
674 case 2:
675 boundaryConditions[i] = 0.;
676 break;
677 // Other cases should not occur
678 default:
679 boundaryConditions[i] = 0.;
680 return false;
681 }
682 }
683 boundaryConditions[nElements] = 0.;
684
685 return true;
686}
687
688bool ComponentNeBem2d::Solve() {
689
690 const int nEntries = nElements + 1;
691
692 int i, j;
693 double solution = 0.;
694 for (i = nElements; i--;) {
695 solution = 0.;
696 for (j = nEntries; j--;) {
697 solution += inverseMatrix[i][j] * boundaryConditions[j];
698 }
699 elements[i].solution = solution;
700 }
701
702 if (debug) {
703 std::cout << m_className << "::Solve:\n";
704 std::cout << " Element Solution\n";
705 for (i = 0; i < nElements; ++i) {
706 std::cout << " " << i << " " << elements[i].solution << "\n";
707 }
708 }
709
710 return true;
711}
712
713bool ComponentNeBem2d::CheckConvergence() {
714
715 // Potential and normal component of the electric field
716 // evaluated at the collocation points
717 std::vector<double> v;
718 std::vector<double> ne;
719 v.resize(nCollocationPoints);
720 ne.resize(nCollocationPoints);
721
722 double ex = 0., ey = 0.;
723 double fx = 0., fy = 0., u = 0.;
724 double r;
725
726 double x = 0., y = 0.;
727 double dx = 0., dy = 0.;
728 double xLoc, yLoc;
729
730 if (debug) {
731 std::cout << m_className << "::CheckConvergence:\n";
732 std::cout << "element # type LHS RHS\n";
733 }
734 for (int i = nElements; i--;) {
735 for (int k = nCollocationPoints; k--;) v[k] = ne[k] = 0.;
736 // Sum up the contributions from all boundary elements
737 for (int j = nElements; j--;) {
738 // Loop over the collocation points
739 for (int k = nCollocationPoints; k--;) {
740 x = elements[i].cX;
741 y = elements[i].cY;
742 if (elements[i].geoType == 0) {
743 // Panel
744 Rotate(2. * elements[i].len, 0., elements[i].phi, Local2Global, dx,
745 dy);
746 x -= dx / 2.;
747 y -= dy / 2.;
748 if (randomCollocation) {
749 r = RndmUniformPos();
750 } else {
751 r = (k + 1.) / (nCollocationPoints + 1.);
752 }
753 x += r * dx;
754 y += r * dy;
755 } else {
756 // Wire
757 r = TwoPi * RndmUniform();
758 x += elements[i].len * cos(r);
759 y += elements[i].len * sin(r);
760 }
761
762 dx = x - elements[j].cX;
763 dy = y - elements[j].cY;
764 // Transform to local coordinate system
765 Rotate(dx, dy, elements[j].phi, Global2Local, xLoc, yLoc);
766 // Compute the potential
767 ComputePotential(elements[j].geoType, elements[j].len, xLoc, yLoc, u);
768 // Compute the field
769 ComputeFlux(elements[j].geoType, elements[j].len, elements[j].phi, xLoc,
770 yLoc, fx, fy);
771 // Rotate to the local coordinate system of the test element
772 Rotate(fx, fy, elements[i].phi, Global2Local, ex, ey);
773 v[k] += u * elements[j].solution;
774 ne[k] += ey * elements[j].solution;
775 }
776 }
777 double v0 = 0., ne0 = 0.;
778 for (int k = nCollocationPoints; k--;) {
779 v0 += v[k];
780 ne0 += ne[k];
781 }
782 v0 /= nCollocationPoints;
783 ne0 /= nCollocationPoints;
784 double ne1 = 0.;
785 if (elements[i].bcType == 2) {
786 // Dielectric-dielectric interface
787 ne1 = ne0 + elements[i].solution /
788 (2. * elements[i].lambda * VacuumPermittivity);
789 }
790 if (debug) {
791 if (elements[i].bcType == 0) {
792 std::cout << std::setw(5) << i << " cond. " << std::setw(10) << v0
793 << " " << std::setw(10) << elements[i].bcValue << "\n";
794 } else if (elements[i].bcType == 2) {
795 std::cout << std::setw(5) << i << " diel. " << std::setw(10) << ne0
796 << " " << ne1 << " 0\n";
797 }
798 }
799 }
800
801 return true;
802}
803
804void ComponentNeBem2d::Rotate(const double xIn, const double yIn,
805 const double phi, const int opt, double& xOut,
806 double& yOut) {
807
808 // Rotation angle represents clockwise rotation about the origin
809 // Transformation to local coordinates (opt = 1): clockwise rotation
810 // Transformation to global coordinates (opt = -1): anti-clockwise rotation
811
812 if (fabs(phi) < 1.e-12) {
813 xOut = xIn;
814 yOut = yIn;
815 return;
816 }
817
818 const double c = cos(phi);
819 const double s = opt * sin(phi);
820 xOut = c * xIn + s * yIn;
821 yOut = -s * xIn + c * yIn;
822}
823
824double ComponentNeBem2d::LinePotential(const double a, const double x,
825 const double y) {
826
827 double p = 0.;
828 const double amx = a - x;
829 const double apx = a + x;
830 if (fabs(y) > Small) {
831 const double y2 = y * y;
832 p = 2. * a - y * (atan(amx / y) + atan(apx / y)) -
833 0.5 * amx * log(amx * amx + y2) - 0.5 * apx * log(apx * apx + y2);
834 } else if (fabs(x) != a) {
835 p = 2. * a - 0.5 * amx * log(amx * amx) - 0.5 * apx * log(apx * apx);
836 } else {
837 p = 2. * a * (1. - log(2. * a));
838 }
839
840 return p / TwoPiEpsilon0;
841}
842
843double ComponentNeBem2d::WirePotential(const double r0, const double x,
844 const double y) {
845
846 const double r = sqrt(x * x + y * y);
847 if (r >= r0) {
848 return -log(r) * r0 / VacuumPermittivity;
849 }
850
851 // Inside the wire the potential is constant
852 return -log(r0) * r0 / VacuumPermittivity;
853}
854
855void ComponentNeBem2d::LineFlux(const double a, const double x, const double y,
856 double& ex, double& ey) {
857
858 const double amx = a - x;
859 const double apx = a + x;
860 if (fabs(y) > 0.) {
861 const double y2 = y * y;
862 ex = 0.5 * log((apx * apx + y2) / (amx * amx + y2));
863 ey = atan(amx / y) + atan(apx / y);
864 } else if (fabs(x) != a) {
865 ex = 0.5 * log(apx * apx / (amx * amx));
866 ey = 0.;
867 } else {
868 // Singularity at the end points of the line
869 const double eps = 1.e-12;
870 ex = 0.25 *
871 log(pow(apx * apx - eps * eps, 2) / pow(amx * amx - eps * eps, 2));
872 ey = 0.;
873 }
874
875 ex /= TwoPiEpsilon0;
876 ey /= TwoPiEpsilon0;
877}
878
879void ComponentNeBem2d::WireFlux(const double r0, const double x, const double y,
880 double& ex, double& ey) {
881
882 const double r = sqrt(x * x + y * y);
883 if (r > r0) {
884 const double r2 = r * r;
885 ex = x * r0 / r2;
886 ey = y * r0 / r2;
887 } else if (r == r0) {
888 ex = 0.5 * x / r0;
889 ey = 0.5 * y / r0;
890 } else {
891 // Inside the wire the field is zero.
892 ex = ey = 0.;
893 }
894
895 ex = ex / VacuumPermittivity;
896 ey = ey / VacuumPermittivity;
897}
898
899void ComponentNeBem2d::Reset() {
900
901 panels.clear();
902 nPanels = 0;
903
904 wires.clear();
905 nWires = 0;
906
907 elements.clear();
908 nElements = 0;
909}
910
911void ComponentNeBem2d::UpdatePeriodicity() {
912
913 std::cerr << m_className << "::UpdatePeriodicity:\n";
914 std::cerr << " Periodicities are not supported.\n";
915}
916}
DoubleAc cos(const DoubleAc &f)
Definition: DoubleAc.cpp:431
DoubleAc pow(const DoubleAc &f, double p)
Definition: DoubleAc.cpp:336
DoubleAc sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:313
DoubleAc sin(const DoubleAc &f)
Definition: DoubleAc.cpp:383
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:616
virtual Medium * GetMedium(const double &x, const double &y, const double &z)
void SetNumberOfDivisions(const int ndiv)
void SetMaxNumberOfIterations(const int niter)
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status)
void AddWire(const double x0, const double y0, const double d, const double bcval)
void SetNumberOfCollocationPoints(const int ncoll)
void AddPanel(const double x0, const double y0, const double x1, const double y1, const int bctype, const double bcval, const double lambda)
bool GetVoltageRange(double &vmin, double &vmax)
void SetMinimumElementSize(const double min)
double RndmUniform()
Definition: Random.hh:16
double RndmUniformPos()
Definition: Random.hh:19