Garfield++ v1r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentAnalyticField.cc
Go to the documentation of this file.
1#include <iostream>
2#include <iomanip>
3#include <fstream>
4
5#include "Numerics.hh"
8
9namespace Garfield {
10
12
13 m_className = "ComponentAnalyticField";
14 chargeCheck = false;
15 CellInit();
16}
17
18void ComponentAnalyticField::ElectricField(const double x, const double y,
19 const double z, double& ex,
20 double& ey, double& ez, Medium*& m,
21 int& status) {
22
23 // Initialize electric field and medium.
24 ex = ey = ez = 0.;
25 m = NULL;
26
27 // Make sure the charges have been calculated.
28 if (!cellset) {
29 if (!Prepare()) {
30 status = -11;
31 return;
32 }
33 }
34
35 // Disable calculation of the potential.
36 const bool opt = false;
37 double v = 0.;
38
39 // Calculate the field.
40 status = Field(x, y, z, ex, ey, ez, v, opt);
41
42 // If the field is ok, get the medium.
43 if (status == 0) {
44 m = GetMedium(x, y, z);
45 if (m == NULL) {
46 status = -6;
47 } else if (!m->IsDriftable()) {
48 status = -5;
49 }
50 }
51}
52
53void ComponentAnalyticField::ElectricField(const double x, const double y,
54 const double z, double& ex,
55 double& ey, double& ez, double& v,
56 Medium*& m, int& status) {
57
58 // Initialize electric field and medium.
59 ex = ey = ez = v = 0.;
60 m = NULL;
61
62 // Make sure the charges have been calculated.
63 if (!cellset) {
64 if (!Prepare()) {
65 status = -11;
66 return;
67 }
68 }
69
70 // Request calculation of the potential.
71 const bool opt = true;
72
73 // Calculate the field.
74 status = Field(x, y, z, ex, ey, ez, v, opt);
75
76 // If the field is ok, get the medium.
77 if (status == 0) {
78 m = GetMedium(x, y, z);
79 if (m == NULL) {
80 status = -6;
81 } else if (!m->IsDriftable()) {
82 status = -5;
83 }
84 }
85}
86
87bool ComponentAnalyticField::GetVoltageRange(double& pmin, double& pmax) {
88
89 // Make sure the cell is prepared.
90 if (!cellset) {
91 if (!Prepare()) {
92 std::cerr << m_className << "::GetVoltageRange:\n";
93 std::cerr << " Unable to return voltage range.\n";
94 std::cerr << " Cell could not be setup.\n";
95 return false;
96 }
97 }
98
99 pmin = vmin;
100 pmax = vmax;
101 return true;
102}
103
104void ComponentAnalyticField::WeightingField(const double x, const double y,
105 const double z, double& wx,
106 double& wy, double& wz,
107 const std::string label) {
108
109 wx = wy = wz = 0.;
110
111 if (nReadout <= 0) return;
112 if (!sigset) {
113 if (!PrepareSignals()) {
114 std::cerr << m_className << "::WeightingField::\n";
115 std::cerr << " Unable to calculate weighting fields.\n";
116 return;
117 }
118 }
119
120 if (label.empty()) return;
121 int index = -1;
122 for (int i = nReadout; i--;) {
123 if (readout[i] == label) {
124 index = i;
125 break;
126 }
127 }
128 if (index < 0) return;
129
130 double volt = 0.;
131 Wfield(x, y, z, wx, wy, wz, volt, index, false);
132}
133
135 const double y,
136 const double z,
137 const std::string label) {
138
139 double volt = 0.;
140
141 if (nReadout <= 0) return volt;
142 if (!sigset) {
143 if (!PrepareSignals()) {
144 std::cerr << m_className << "::WeightingPotential::\n";
145 std::cerr << " Unable to calculate weighting fields.\n";
146 return volt;
147 }
148 }
149
150 if (label.empty()) return volt;
151 int index = -1;
152 for (int i = nReadout; i--;) {
153 if (readout[i] == label) {
154 index = i;
155 break;
156 }
157 }
158 if (index < 0) return volt;
159
160 double wx = 0., wy = 0., wz = 0.;
161 Wfield(x, y, z, wx, wy, wz, volt, index, true);
162 return volt;
163}
164
165bool ComponentAnalyticField::GetBoundingBox(double& x0, double& y0, double& z0,
166 double& x1, double& y1,
167 double& z1) {
168
169 // If a geometry is present, try to get the bounding box from there.
170 if (theGeometry != 0) {
171 if (theGeometry->GetBoundingBox(x0, y0, z0, x1, y1, z1)) return true;
172 }
173 // Otherwise, return the cell dimensions.
174 if (!cellset) return false;
175 x0 = xmin;
176 y0 = ymin;
177 z0 = zmin;
178 x1 = xmax;
179 y1 = ymax;
180 z1 = zmax;
181 return true;
182}
183
184bool ComponentAnalyticField::IsWireCrossed(double x0, double y0, double z0,
185 double x1, double y1, double z1,
186 double& xc, double& yc, double& zc) {
187
188 xc = x0;
189 yc = y0;
190 zc = z0;
191
192 if (nWires <= 0) return false;
193
194 const double dx = x1 - x0;
195 const double dy = y1 - y0;
196 const double d2 = dx * dx + dy * dy;
197 // Check that the step length is non-zero.
198 if (d2 < Small) return false;
199
200 // Check if a whole period has been crossed.
201 if ((perx && fabs(dx) >= sx) || (pery && fabs(dy) >= sy)) {
202 std::cerr << m_className << "::IsWireCrossed:\n";
203 std::cerr << " Particle crossed more than one period.\n";
204 return false;
205 }
206
207 // Both coordinates are assumed to be located inside
208 // the drift area and inside a drift medium.
209 // This should have been checked before this call.
210
211 const double xm = 0.5 * (x0 + x1);
212 const double ym = 0.5 * (y0 + y1);
213 double dMin2 = 0.;
214 for (int i = nWires; i--;) {
215 double xw = w[i].x, yw = w[i].y;
216 if (perx) {
217 xw += sx * int(round((xm - xw) / sx));
218 }
219 if (pery) {
220 yw += sy * int(round((ym - yw) / sy));
221 }
222 // Calculate the smallest distance between track and wire.
223 const double xIn0 = dx * (xw - x0) + dy * (yw - y0);
224 // Check if the minimum is located before (x0, y0).
225 if (xIn0 < 0.) continue;
226 const double xIn1 = -(dx * (xw - x1) + dy * (yw - y1));
227 // Check if the minimum is located behind (x1, y1).
228 if (xIn1 < 0.) continue;
229 // Minimum is located between (x0, y0) and (x1, y1).
230 const double dw02 = pow(xw - x0, 2) + pow(yw - y0, 2);
231 const double dw12 = pow(xw - x1, 2) + pow(yw - y1, 2);
232 if (xIn1 * xIn1 * dw02 > xIn0 * xIn0 * dw12) {
233 dMin2 = dw02 - xIn0 * xIn0 / d2;
234 } else {
235 dMin2 = dw12 - xIn1 * xIn1 / d2;
236 }
237 // Add in the times nTrap to account for the trap radius.
238 const double r2 = 0.25 * w[i].d * w[i].d;
239 if (dMin2 < r2) {
240 // Wire has been crossed.
241 // Find the point of intersection.
242 const double p = -xIn0 / d2;
243 const double q = (dw02 - r2) / d2;
244 const double t1 = -p + sqrt(p * p - q);
245 const double t2 = -p - sqrt(p * p - q);
246 const double t = std::min(t1, t2);
247 xc = x0 + t * dx;
248 yc = y0 + t * dy;
249 zc = z0 + t * (z1 - z0);
250 return true;
251 }
252 }
253 return false;
254}
255
256bool ComponentAnalyticField::IsInTrapRadius(double xin, double yin, double zin,
257 double& xw, double& yw,
258 double& rw) {
259
260 // In case of periodicity, move the point into the basic cell.
261 double x0 = xin, y0 = yin;
262 int nX = 0, nY = 0, nPhi = 0;
263 if (perx) {
264 nX = int(round(xin / sx));
265 x0 -= sx * nX;
266 }
267 if (pery && tube) {
268 Cartesian2Polar(xin, yin, x0, y0);
269 nPhi = int(round((Pi * y0) / (sy * 180.)));
270 y0 -= 180. * sy * nPhi / Pi;
271 Polar2Cartesian(x0, y0, x0, y0);
272 } else if (pery) {
273 nY = int(round(yin / sy));
274 y0 -= sy * nY;
275 }
276
277 // Move the point to the correct side of the plane.
278 if (perx && ynplan[0] && x0 <= coplan[0]) x0 += sx;
279 if (perx && ynplan[1] && x0 >= coplan[1]) x0 -= sx;
280 if (pery && ynplan[2] && y0 <= coplan[2]) y0 += sy;
281 if (pery && ynplan[3] && y0 >= coplan[3]) y0 -= sy;
282
283 for (int i = 0; i < nWires; i++) {
284 const double xwc = w[i].x;
285 const double yxc = w[i].y;
286 const double r = sqrt(pow(xwc - x0, 2) + pow(yxc - y0, 2));
287 const double rTrap = 0.5 * w[i].d * w[i].nTrap;
288 if (r < rTrap) {
289 xw = w[i].x;
290 yw = w[i].y;
291 rw = w[i].d * 0.5;
292 if (perx && ynplan[0] && x0 <= coplan[0]) x0 -= sx;
293 if (perx && ynplan[1] && x0 >= coplan[1]) x0 += sx;
294 if (pery && ynplan[2] && y0 <= coplan[2]) y0 -= sy;
295 if (pery && ynplan[3] && y0 >= coplan[3]) y0 += sy;
296 if (pery && tube) {
297 double rhow, phiw;
298 Cartesian2Polar(xw, yw, rhow, phiw);
299 phiw += 180. * sy * nPhi / Pi;
300 Polar2Cartesian(rhow, phiw, xw, yw);
301 } else if (pery) {
302 y0 += sy * nY;
303 }
304 if (perx) xw += sx * nX;
305 if (debug) {
306 std::cout << m_className << "::IsInTrapRadius:\n";
307 std::cout << " (" << xin << ", " << yin << ", " << zin << ")"
308 << " within trap radius of wire " << i << ".\n";
309 }
310 return true;
311 }
312 }
313
314 return false;
315}
316
317void ComponentAnalyticField::AddWire(const double x, const double y,
318 const double diameter,
319 const double voltage,
320 const std::string label,
321 const double length, const double tension,
322 double rho, const int ntrap) {
323
324 // Check if the provided parameters make sense.
325 if (diameter <= 0.) {
326 std::cerr << m_className << "::AddWire:\n";
327 std::cerr << " Unphysical wire diameter.\n";
328 return;
329 }
330
331 if (tension <= 0.) {
332 std::cerr << m_className << "::AddWire:\n";
333 std::cerr << " Unphysical wire tension.\n";
334 return;
335 }
336
337 if (rho <= 0.0) {
338 std::cerr << m_className << "::AddWire:\n";
339 std::cerr << " Unphysical wire density.\n";
340 return;
341 }
342
343 if (length <= 0.0) {
344 std::cerr << m_className << "::AddWire:\n";
345 std::cerr << " Unphysical wire length.\n";
346 return;
347 }
348
349 if (ntrap <= 0) {
350 std::cerr << m_className << "::AddWire:\n";
351 std::cerr << " Number of trap radii must be > 0.\n";
352 return;
353 }
354 // Create a new wire
355 wire newWire;
356 newWire.x = x;
357 newWire.y = y;
358 newWire.d = diameter;
359 newWire.v = voltage;
360 newWire.u = length;
361 newWire.type = label;
362 newWire.e = 0.;
363 newWire.ind = -1;
364 newWire.nTrap = ntrap;
365 // Add the wire to the list
366 w.push_back(newWire);
367 ++nWires;
368
369 // Force recalculation of the capacitance and signal matrices.
370 cellset = false;
371 sigset = false;
372}
373
374void ComponentAnalyticField::AddTube(const double radius, const double voltage,
375 const int nEdges,
376 const std::string label) {
377
378 // Check if the provided parameters make sense.
379 if (radius <= 0.0) {
380 std::cerr << m_className << "::AddTube:\n";
381 std::cerr << " Unphysical tube dimension.\n";
382 return;
383 }
384
385 if (nEdges < 3 && nEdges != 0) {
386 std::cerr << m_className << "::AddTube:\n";
387 std::cerr << " Unphysical number of tube edges (" << nEdges << ")\n";
388 return;
389 }
390
391 // If there is already a tube defined, print a warning message.
392 if (tube) {
393 std::cout << m_className << "::AddTube:\n";
394 std::cout << " Warning: Existing tube settings will be overwritten.\n";
395 }
396
397 // Set the coordinate system.
398 tube = true;
399 polar = false;
400
401 // Set the tube parameters.
402 cotube = radius;
403 vttube = voltage;
404
405 ntube = nEdges;
406
407 planes[4].type = label;
408 planes[4].ind = -1;
409
410 // Force recalculation of the capacitance and signal matrices.
411 cellset = false;
412 sigset = false;
413}
414
415void ComponentAnalyticField::AddPlaneX(const double x, const double v,
416 const std::string lab) {
417
418 if (ynplan[0] && ynplan[1]) {
419 std::cerr << m_className << "::AddPlaneX:\n";
420 std::cerr << " There are already two x planes defined.\n";
421 return;
422 }
423
424 if (ynplan[0]) {
425 ynplan[1] = true;
426 coplan[1] = x;
427 vtplan[1] = v;
428 planes[1].type = lab;
429 planes[1].ind = -1;
430 } else {
431 ynplan[0] = true;
432 coplan[0] = x;
433 vtplan[0] = v;
434 planes[0].type = lab;
435 planes[0].ind = -1;
436 }
437
438 // Force recalculation of the capacitance and signal matrices.
439 cellset = false;
440 sigset = false;
441}
442
443void ComponentAnalyticField::AddPlaneY(const double y, const double v,
444 const std::string lab) {
445
446 if (ynplan[2] && ynplan[3]) {
447 std::cerr << m_className << "::AddPlaneY:\n";
448 std::cerr << " There are already two y planes defined.\n";
449 return;
450 }
451
452 if (ynplan[2]) {
453 ynplan[3] = true;
454 coplan[3] = y;
455 vtplan[3] = v;
456 planes[3].type = lab;
457 planes[3].ind = -1;
458 } else {
459 ynplan[2] = true;
460 coplan[2] = y;
461 vtplan[2] = v;
462 planes[2].type = lab;
463 planes[2].ind = -1;
464 }
465
466 // Force recalculation of the capacitance and signal matrices.
467 cellset = false;
468 sigset = false;
469}
470
472 const double x, const double smin,
473 const double smax,
474 const std::string label,
475 const double gap) {
476
477 if (!ynplan[0] && !ynplan[1]) {
478 std::cerr << m_className << "::AddStripOnPlaneX:\n";
479 std::cerr << " There are no planes at constant x defined.\n";
480 return;
481 }
482
483 if (direction != 'y' && direction != 'Y' && direction != 'z' &&
484 direction != 'Z') {
485 std::cerr << m_className << "::AddStripOnPlaneX:\n";
486 std::cerr << " Invalid direction (" << direction << ").\n";
487 std::cerr << " Only strips in y or z direction are possible.\n";
488 return;
489 }
490
491 if (fabs(smax - smin) < Small) {
492 std::cerr << m_className << "::AddStripOnPlaneX:\n";
493 std::cerr << " Strip width must be greater than zero.\n";
494 return;
495 }
496
497 strip newStrip;
498 newStrip.type = label;
499 newStrip.ind = -1;
500 newStrip.smin = std::min(smin, smax);
501 newStrip.smax = std::max(smin, smax);
502 if (gap > Small) {
503 newStrip.gap = gap;
504 } else {
505 newStrip.gap = -1.;
506 }
507
508 int iplane = 0;
509 if (ynplan[1]) {
510 const double d0 = fabs(coplan[0] - x);
511 const double d1 = fabs(coplan[1] - x);
512 if (d1 < d0) iplane = 1;
513 }
514
515 if (direction == 'y' || direction == 'Y') {
516 planes[iplane].nStrips1++;
517 planes[iplane].strips1.push_back(newStrip);
518 } else {
519 planes[iplane].nStrips2++;
520 planes[iplane].strips2.push_back(newStrip);
521 }
522}
523
525 const double y, const double smin,
526 const double smax,
527 const std::string label,
528 const double gap) {
529
530 if (!ynplan[2] && !ynplan[3]) {
531 std::cerr << m_className << "::AddStripOnPlaneY:\n";
532 std::cerr << " There are no planes at constant y defined.\n";
533 return;
534 }
535
536 if (direction != 'x' && direction != 'X' && direction != 'z' &&
537 direction != 'Z') {
538 std::cerr << m_className << "::AddStripOnPlaneY:\n";
539 std::cerr << " Invalid direction (" << direction << ").\n";
540 std::cerr << " Only strips in x or z direction are possible.\n";
541 return;
542 }
543
544 if (fabs(smax - smin) < Small) {
545 std::cerr << m_className << "::AddStripOnPlaneY:\n";
546 std::cerr << " Strip width must be greater than zero.\n";
547 return;
548 }
549
550 strip newStrip;
551 newStrip.type = label;
552 newStrip.ind = -1;
553 newStrip.smin = std::min(smin, smax);
554 newStrip.smax = std::max(smin, smax);
555 if (gap > Small) {
556 newStrip.gap = gap;
557 } else {
558 newStrip.gap = -1.;
559 }
560
561 int iplane = 2;
562 if (ynplan[3]) {
563 const double d2 = fabs(coplan[2] - y);
564 const double d3 = fabs(coplan[3] - y);
565 if (d3 < d2) iplane = 3;
566 }
567
568 if (direction == 'x' || direction == 'X') {
569 planes[iplane].nStrips1++;
570 planes[iplane].strips1.push_back(newStrip);
571 } else {
572 planes[iplane].nStrips2++;
573 planes[iplane].strips2.push_back(newStrip);
574 }
575}
576
578
579 if (s < Small) {
580 std::cerr << m_className << "::SetPeriodicityX:\n";
581 std::cerr << " Periodic length must be greater than zero.\n";
582 return;
583 }
584
585 xPeriodic = true;
586 sx = s;
587 UpdatePeriodicity();
588}
589
591
592 if (s < Small) {
593 std::cerr << m_className << "::SetPeriodicityY:\n";
594 std::cerr << " Periodic length must be greater than zero.\n";
595 return;
596 }
597
598 yPeriodic = true;
599 sy = s;
600 UpdatePeriodicity();
601}
602
604
605 if (!xPeriodic) {
606 s = 0.;
607 return false;
608 }
609
610 s = sx;
611 return true;
612}
613
615
616 if (!yPeriodic) {
617 s = 0.;
618 return false;
619 }
620
621 s = sy;
622 return true;
623}
624
625void ComponentAnalyticField::UpdatePeriodicity() {
626
627 // Check if the settings have actually changed.
628 if (perx && !xPeriodic) {
629 perx = false;
630 cellset = false;
631 sigset = false;
632 } else if (!perx && xPeriodic) {
633 if (sx < Small) {
634 std::cerr << m_className << "::UpdatePeriodicity:\n";
635 std::cerr << " Periodicity in x direction was enabled"
636 << " but periodic length is not set.\n";
637 } else {
638 perx = true;
639 cellset = false;
640 sigset = false;
641 }
642 }
643
644 if (pery && !yPeriodic) {
645 pery = false;
646 cellset = false;
647 sigset = false;
648 } else if (!pery && yPeriodic) {
649 if (sy < Small) {
650 std::cerr << m_className << "::UpdatePeriodicity:\n";
651 std::cerr << " Periodicity in y direction was enabled"
652 << " but periodic length is not set.\n";
653 } else {
654 pery = true;
655 cellset = false;
656 sigset = false;
657 }
658 }
659
660 // Check if symmetries other than x/y periodicity have been requested
661 if (zPeriodic) {
662 std::cerr << m_className << "::UpdatePeriodicity:\n";
663 std::cerr << " Periodicity in z is not possible.\n";
664 }
665
667 std::cerr << m_className << "::UpdatePeriodicity:\n";
668 std::cerr << " Mirror periodicity is not possible.\n";
669 }
670
672 std::cerr << m_className << "::UpdatePeriodicity:\n";
673 std::cerr << " Axial periodicity is not possible.\n";
674 }
675
677 std::cerr << m_className << "::UpdatePeriodicity:\n";
678 std::cerr << " Rotation symmetry is not possible.\n";
679 }
680}
681
682void ComponentAnalyticField::AddCharge(const double x, const double y,
683 const double z, const double q) {
684
685 // Convert from fC to internal units (division by 4 pi epsilon0).
686 charge3d newCharge;
687 newCharge.x = x;
688 newCharge.y = y;
689 newCharge.z = z;
690 newCharge.e = q / FourPiEpsilon0;
691 ch3d.push_back(newCharge);
692 ++n3d;
693}
694
696
697 n3d = 0;
698 ch3d.clear();
699 nTermBessel = 10;
700 nTermPoly = 100;
701}
702
704
705 std::cout << m_className << "::PrintCharges:\n";
706 if (n3d <= 0) {
707 std::cout << " No charges present.\n";
708 return;
709 }
710 std::cout << " x [cm] y [cm] z [cm] charge [fC]\n";
711 for (int i = 0; i < n3d; ++i) {
712 std::cout << " " << std::setw(9) << ch3d[i].x << " " << std::setw(9)
713 << ch3d[i].y << " " << std::setw(9) << ch3d[i].z << " "
714 << std::setw(11) << ch3d[i].e * FourPiEpsilon0 << "\n";
715 }
716}
717
719
720 if (ynplan[0] && ynplan[1]) {
721 return 2;
722 } else if (ynplan[0] || ynplan[1]) {
723 return 1;
724 }
725 return 0;
726}
727
729
730 if (ynplan[2] && ynplan[3]) {
731 return 2;
732 } else if (ynplan[2] || ynplan[3]) {
733 return 1;
734 }
735 return 0;
736}
737
738bool ComponentAnalyticField::GetWire(const int i, double& x, double& y,
739 double& diameter, double& voltage,
740 std::string& label, double& length,
741 double& charge, int& ntrap) {
742
743 if (i < 0 || i >= nWires) {
744 std::cerr << m_className << "::GetWire:\n";
745 std::cerr << " Wire index is out of range.\n";
746 return false;
747 }
748
749 x = w[i].x;
750 y = w[i].y;
751 diameter = w[i].d;
752 voltage = w[i].v;
753 label = w[i].type;
754 length = w[i].u;
755 charge = w[i].e;
756 ntrap = w[i].nTrap;
757 return true;
758}
759
760bool ComponentAnalyticField::GetPlaneX(const int i, double& x, double& voltage,
761 std::string& label) {
762
763 if (i < 0 || i >= 2 || (i == 1 && !ynplan[1])) {
764 std::cerr << m_className << "::GetPlaneX:\n";
765 std::cerr << " Plane index is out of range.\n";
766 return false;
767 }
768
769 x = coplan[i];
770 voltage = vtplan[i];
771 label = planes[i].type;
772 return true;
773}
774
775bool ComponentAnalyticField::GetPlaneY(const int i, double& y, double& voltage,
776 std::string& label) {
777
778 if (i < 0 || i >= 2 || (i == 1 && !ynplan[3])) {
779 std::cerr << m_className << "::GetPlaneY:\n";
780 std::cerr << " Plane index is out of range.\n";
781 return false;
782 }
783
784 y = coplan[i + 2];
785 voltage = vtplan[i + 2];
786 label = planes[i + 2].type;
787 return true;
788}
789
790bool ComponentAnalyticField::GetTube(double& r, double& voltage, int& nEdges,
791 std::string& label) {
792
793 if (!tube) return false;
794 r = cotube;
795 voltage = vttube;
796 nEdges = ntube;
797 label = planes[4].type;
798 return true;
799}
800
801int ComponentAnalyticField::Field(const double xin, const double yin,
802 const double zin, double& ex, double& ey,
803 double& ez, double& volt, const bool opt) {
804
805 //-----------------------------------------------------------------------
806 // EFIELD - Subroutine calculating the electric field and the potential
807 // at a given place. It makes use of the routines POT...,
808 // depending on the type of the cell.
809 // VARIABLES : XPOS : x-coordinate of the place where the field
810 // is to be calculated.
811 // YPOS, ZPOS : y- and z-coordinates
812 // EX, EY, EZ : x-, y-, z-component of the electric field.
813 // VOLT : potential at (XPOS,YPOS).
814 // IOPT : 1 if both E and V are required, 0 if only E
815 // is to be computed.
816 // ILOC : Tells where the point is located (0: normal
817 // I > 0: in wire I, -1: outside a plane,
818 // -5: in a material, -6: outside the mesh,
819 // -10: unknown potential).
820 // (Last changed on 28/ 9/07.)
821 //-----------------------------------------------------------------------
822
823 // Initialise the field for returns without actual calculations.
824 ex = ey = ez = volt = 0.;
825
826 double xpos = xin, ypos = yin;
827
828 // In case of periodicity, move the point into the basic cell.
829 if (perx) {
830 xpos -= sx * int(round(xin / sx));
831 }
832 double arot = 0.;
833 if (pery && tube) {
834 Cartesian2Polar(xin, yin, xpos, ypos);
835 arot = 180. * sy * int(round((Pi * ypos) / (sy * 180.))) / Pi;
836 ypos -= arot;
837 Polar2Cartesian(xpos, ypos, xpos, ypos);
838 } else if (pery) {
839 ypos -= sy * int(round(yin / sy));
840 }
841
842 // Move the point to the correct side of the plane.
843 if (perx && ynplan[0] && xpos <= coplan[0]) xpos += sx;
844 if (perx && ynplan[1] && xpos >= coplan[1]) xpos -= sx;
845 if (pery && ynplan[2] && ypos <= coplan[2]) ypos += sy;
846 if (pery && ynplan[3] && ypos >= coplan[3]) ypos -= sy;
847
848 // In case (XPOS,YPOS) is located behind a plane there is no field.
849 if (tube) {
850 if (!InTube(xpos, ypos, cotube, ntube)) {
851 volt = vttube;
852 return -4;
853 }
854 } else {
855 if (ynplan[0] && xpos < coplan[0]) {
856 volt = vtplan[0];
857 return -4;
858 }
859 if (ynplan[1] && xpos > coplan[1]) {
860 volt = vtplan[1];
861 return -4;
862 }
863 if (ynplan[2] && ypos < coplan[2]) {
864 volt = vtplan[2];
865 return -4;
866 }
867 if (ynplan[3] && ypos > coplan[3]) {
868 volt = vtplan[3];
869 return -4;
870 }
871 }
872
873 // If (xpos, ypos) is within a wire, there is no field either.
874 for (int i = nWires; i--;) {
875 double dxwir = xpos - w[i].x;
876 double dywir = ypos - w[i].y;
877 // Correct for periodicities.
878 if (perx) dxwir -= sx * int(round(dxwir / sx));
879 if (pery) dywir -= sy * int(round(dywir / sy));
880 // Check the actual position.
881 if (dxwir * dxwir + dywir * dywir < 0.25 * w[i].d * w[i].d) {
882 volt = w[i].v;
883 return i + 1;
884 }
885 }
886
887 // Call the appropriate potential calculation function.
888 switch (iCellType) {
889 case 1:
890 FieldA00(xpos, ypos, ex, ey, volt, opt);
891 break;
892 case 2:
893 FieldB1X(xpos, ypos, ex, ey, volt, opt);
894 break;
895 case 3:
896 FieldB1Y(xpos, ypos, ex, ey, volt, opt);
897 break;
898 case 4:
899 FieldB2X(xpos, ypos, ex, ey, volt, opt);
900 break;
901 case 5:
902 FieldB2Y(xpos, ypos, ex, ey, volt, opt);
903 break;
904 case 6:
905 FieldC10(xpos, ypos, ex, ey, volt, opt);
906 break;
907 case 7:
908 FieldC2X(xpos, ypos, ex, ey, volt, opt);
909 break;
910 case 8:
911 FieldC2Y(xpos, ypos, ex, ey, volt, opt);
912 break;
913 case 9:
914 FieldC30(xpos, ypos, ex, ey, volt, opt);
915 break;
916 case 10:
917 FieldD10(xpos, ypos, ex, ey, volt, opt);
918 break;
919 case 11:
920 FieldD20(xpos, ypos, ex, ey, volt, opt);
921 break;
922 case 12:
923 FieldD30(xpos, ypos, ex, ey, volt, opt);
924 break;
925 default:
926 // Unknown cell type
927 std::cerr << m_className << "::Field:\n";
928 std::cerr << " Unknown cell type (id " << iCellType << ")\n";
929 return -10;
930 break;
931 }
932
933 // Add dipole terms if requested
934 if (dipole) {
935 double exd = 0., eyd = 0., voltd = 0.;
936 switch (iCellType) {
937 case 1:
938 // CALL EMCA00(XPOS,YPOS,EXD,EYD,VOLTD,IOPT)
939 break;
940 case 2:
941 // CALL EMCB1X(XPOS,YPOS,EXD,EYD,VOLTD,IOPT)
942 break;
943 case 3:
944 // CALL EMCB1Y(XPOS,YPOS,EXD,EYD,VOLTD,IOPT)
945 break;
946 case 4:
947 // CALL EMCB2X(XPOS,YPOS,EXD,EYD,VOLTD,IOPT)
948 break;
949 case 5:
950 // CALL EMCB2Y(XPOS,YPOS,EXD,EYD,VOLTD,IOPT)
951 break;
952 default:
953 break;
954 }
955 ex += exd;
956 ey += eyd;
957 volt += voltd;
958 }
959
960 // Rotate the field in some special cases.
961 if (pery && tube) {
962 double xaux, yaux;
963 Cartesian2Polar(ex, ey, xaux, yaux);
964 yaux += arot;
965 Polar2Cartesian(xaux, yaux, ex, ey);
966 }
967
968 // Correct for the equipotential planes.
969 ex -= corvta;
970 ey -= corvtb;
971 volt += corvta * xpos + corvtb * ypos + corvtc;
972
973 // Add three dimensional point charges.
974 if (n3d > 0) {
975 double ex3d = 0., ey3d = 0., ez3d = 0., volt3d = 0.;
976 switch (iCellType) {
977 case 1:
978 case 2:
979 case 3:
980 Field3dA00(xin, yin, zin, ex3d, ey3d, ez3d, volt3d);
981 break;
982 case 4:
983 Field3dB2X(xin, yin, zin, ex3d, ey3d, ez3d, volt3d);
984 break;
985 case 5:
986 Field3dB2Y(xin, yin, zin, ex3d, ey3d, ez3d, volt3d);
987 break;
988 case 10:
989 Field3dD10(xin, yin, zin, ex3d, ey3d, ez3d, volt3d);
990 break;
991 default:
992 Field3dA00(xin, yin, zin, ex3d, ey3d, ez3d, volt3d);
993 break;
994 }
995 ex += ex3d;
996 ey += ey3d;
997 ez += ez3d;
998 volt += volt3d;
999 }
1000
1001 return 0;
1002}
1003
1004void ComponentAnalyticField::CellInit() {
1005
1006 cellset = false;
1007 sigset = false;
1008
1009 // Coordinate system
1010 polar = false;
1011
1012 // Cell type
1013 cellType = "A ";
1014 iCellType = 1;
1015
1016 // Bounding box and voltage range.
1017 xmin = xmax = 0.;
1018 ymin = ymax = 0.;
1019 zmin = zmax = 0.;
1020 vmin = vmax = 0.;
1021
1022 // Periodicities
1023 perx = pery = false;
1024 sx = sy = 1.;
1025
1026 // Signals
1027 nFourier = 1;
1028 cellTypeFourier = "A ";
1029 fperx = fpery = false;
1030 mxmin = mxmax = mymin = mymax = 0;
1031 mfexp = 0;
1032
1033 nReadout = 0;
1034 readout.clear();
1035
1036 // Wires.
1037 nWires = 0;
1038 w.clear();
1039
1040 // Force calculation parameters
1041 weight.clear();
1042 dens.clear();
1043 cnalso.clear();
1044
1045 // Dipole settings
1046 dipole = false;
1047 cosph2.clear();
1048 sinph2.clear();
1049 amp2.clear();
1050
1051 // B2 type cells
1052 b2sin.clear();
1053 // C type cells
1054 mode = 0;
1055 zmult = std::complex<double>(0., 0.);
1056 p1 = p2 = c1 = 0.;
1057 // D3 type cells
1058 wmap.clear();
1059 kappa = 0.;
1060 cc1.clear();
1061 cc2.clear();
1062
1063 // Reference potential
1064 v0 = 0.;
1065 corvta = corvtb = corvtc = 0.;
1066
1067 // Planes
1068 planes.clear();
1069 planes.resize(5);
1070 for (int i = 0; i < 4; ++i) {
1071 ynplan[i] = false;
1072 coplan[i] = 0.;
1073 vtplan[i] = 0.;
1074 }
1075 // Plane shorthand
1076 ynplax = ynplay = false;
1077 coplax = coplay = 1.;
1078
1079 for (int i = 0; i < 5; ++i) {
1080 planes[i].type = '?';
1081 planes[i].ind = -1;
1082 planes[i].ewxcor = 0.;
1083 planes[i].ewycor = 0.;
1084 planes[i].nStrips1 = 0;
1085 planes[i].nStrips2 = 0;
1086 planes[i].strips1.clear();
1087 planes[i].strips2.clear();
1088 }
1089
1090 // Tube properties
1091 tube = false;
1092 ntube = 0;
1093 mtube = 1;
1094 cotube = 1.;
1095 vttube = 0.;
1096
1097 // Capacitance matrices
1098 a.clear();
1099 sigmat.clear();
1100 qplane.clear();
1101
1102 // 3D charges
1103 n3d = 0;
1104 ch3d.clear();
1105 nTermBessel = 10;
1106 nTermPoly = 100;
1107
1108 // Gravity
1109 down[0] = down[1] = 0.;
1110 down[2] = 1.;
1111}
1112
1113bool ComponentAnalyticField::Prepare() {
1114
1115 // Check that the cell makes sense.
1116 if (!CellCheck()) {
1117 std::cerr << m_className << "::Prepare:\n";
1118 std::cerr << " The cell does not meet the requirements.\n";
1119 return false;
1120 }
1121 if (debug) {
1122 std::cout << m_className << "::Prepare:\n";
1123 std::cout << " Cell check ok.\n";
1124 }
1125
1126 // Determine the cell type.
1127 if (!CellType()) {
1128 std::cerr << m_className << "::Prepare:\n";
1129 std::cerr << " Type identification of the cell failed.\n";
1130 return false;
1131 }
1132 if (debug) {
1133 std::cout << m_className << "::Prepare:\n";
1134 std::cout << " Cell is of type " << cellType << ".\n";
1135 }
1136
1137 // Calculate the charges.
1138 if (!Setup()) {
1139 std::cerr << m_className << "::Prepare:\n";
1140 std::cerr << " Calculation of charges failed.\n";
1141 return false;
1142 }
1143 if (debug) {
1144 std::cout << m_className << "::Prepare:\n";
1145 std::cout << " Calculation of charges was successful.\n";
1146 }
1147
1148 // Assign default strip widths.
1149 if (!PrepareStrips()) {
1150 std::cerr << m_className << "::Prepare:\n";
1151 std::cerr << " Strip preparation failed.\n";
1152 return false;
1153 }
1154
1155 cellset = true;
1156 return true;
1157}
1158
1159bool ComponentAnalyticField::CellCheck() {
1160
1161 //-----------------------------------------------------------------------
1162 // CELCHK - Subroutine checking the wire positions, The equipotential
1163 // planes and the periodicity. Two planes having different
1164 // voltages are not allowed to have a common line, wires are
1165 // not allowed to be at the same position etc.
1166 // This routine determines also the cell-dimensions.
1167 // VARIABLE : WRONG(I) : .TRUE. if wire I will be removed
1168 // IPLAN. : Number of wires with coord > than plane .
1169 // (Last changed on 16/ 2/05.)
1170 //-----------------------------------------------------------------------
1171
1172 // Checks on the planes, first move the x planes to the basic cell.
1173 if (perx) {
1174 double conew1 = coplan[0] - sx * int(round(coplan[0] / sx));
1175 double conew2 = coplan[1] - sx * int(round(coplan[1] / sx));
1176 // Check that they are not one on top of the other.
1177 if (ynplan[0] && ynplan[1] && conew1 == conew2) {
1178 if (conew1 > 0.)
1179 conew1 -= sx;
1180 else
1181 conew2 += sx;
1182 }
1183 // Print some warnings if the planes have been moved.
1184 if ((conew1 != coplan[0] && ynplan[0]) ||
1185 (conew2 != coplan[1] && ynplan[1])) {
1186 std::cout << m_className << "::CellCheck:\n";
1187 std::cout << " The planes in x or r are moved to the basic period.\n";
1188 std::cout << " This should not affect the results.";
1189 }
1190 coplan[0] = conew1;
1191 coplan[1] = conew2;
1192
1193 // Two planes should now be separated by SX, cancel PERX if not.
1194 if (ynplan[0] && ynplan[1] && fabs(coplan[1] - coplan[0]) != sx) {
1195 std::cerr << m_className << "::CellCheck:\n";
1196 std::cerr << " The separation of the x or r planes"
1197 << " does not match the period.\b";
1198 std::cerr << " The periodicity is cancelled.\n";
1199 perx = false;
1200 }
1201 // If there are two planes left, they should have identical V's.
1202 if (ynplan[0] && ynplan[1] && vtplan[0] != vtplan[1]) {
1203 std::cerr << m_className << "::CellCheck\n";
1204 std::cerr << " The voltages of the two x (or r) planes differ.\n";
1205 std::cerr << " The periodicity is cancelled.\n";
1206 perx = false;
1207 }
1208 }
1209
1210 // Idem for the y or r planes: move them to the basic period.
1211 if (pery) {
1212 double conew3 = coplan[2] - sy * int(round(coplan[2] / sy));
1213 double conew4 = coplan[3] - sy * int(round(coplan[3] / sy));
1214 // Check that they are not one on top of the other.
1215 if (ynplan[2] && ynplan[3] && conew3 == conew4) {
1216 if (conew3 > 0.)
1217 conew3 -= sy;
1218 else
1219 conew4 += sy;
1220 }
1221 // Print some warnings if the planes have been moved.
1222 if ((conew3 != coplan[2] && ynplan[2]) ||
1223 (conew4 != coplan[3] && ynplan[3])) {
1224 std::cout << m_className << "::CellCheck:\n";
1225 std::cout << " The planes in y are moved to the basic period.\n";
1226 std::cout << " This should not affect the results.";
1227 }
1228 coplan[2] = conew3;
1229 coplan[3] = conew4;
1230
1231 // Two planes should now be separated by SY, cancel PERY if not.
1232 if (ynplan[2] && ynplan[3] && fabs(coplan[3] - coplan[2]) != sy) {
1233 std::cerr << m_className << "::CellCheck:\n";
1234 std::cerr << " The separation of the two y planes"
1235 << " does not match the period.\b";
1236 std::cerr << " The periodicity is cancelled.\n";
1237 pery = false;
1238 }
1239 // If there are two planes left, they should have identical V's.
1240 if (ynplan[2] && ynplan[3] && vtplan[2] != vtplan[3]) {
1241 std::cerr << m_className << "::CellCheck\n";
1242 std::cerr << " The voltages of the two y planes differ.\n";
1243 std::cerr << " The periodicity is cancelled.\n";
1244 pery = false;
1245 }
1246 }
1247
1248 // Check that there is no voltage conflict of crossing planes.
1249 for (int i = 0; i < 2; ++i) {
1250 for (int j = 2; j < 3; ++j) {
1251 if (ynplan[i] && ynplan[j] && vtplan[i] != vtplan[j]) {
1252 std::cerr << m_className << "::CellCheck\n";
1253 std::cerr << " Conflicting potential of 2 crossing planes.\n";
1254 std::cerr << " One y (or phi) plane is removed.\n";
1255 ynplan[j] = false;
1256 }
1257 }
1258 }
1259
1260 // Make sure the the coordinates of the planes are properly ordered.
1261 for (int i = 0; i < 3; i += 2) {
1262 if (ynplan[i] && ynplan[i + 1]) {
1263 if (coplan[i] == coplan[i + 1]) {
1264 std::cerr << m_className << "::CellCheck:\n";
1265 std::cerr << " Two planes are on top of each other.\n";
1266 std::cerr << " One of them is removed.\n";
1267 ynplan[i + 1] = false;
1268 }
1269 if (coplan[i] > coplan[i + 1]) {
1270 if (debug) {
1271 std::cout << m_className << "::CellCheck:\n";
1272 std::cout << " Planes " << i << " and " << i + 1
1273 << " are interchanged.\n";
1274 }
1275 // Interchange the two planes.
1276 const double cohlp = coplan[i];
1277 coplan[i] = coplan[i + 1];
1278 coplan[i + 1] = cohlp;
1279
1280 const double vthlp = vtplan[i];
1281 vtplan[i] = vtplan[i + 1];
1282 vtplan[i + 1] = vthlp;
1283
1284 plane plahlp = planes[i];
1285 planes[i] = planes[i + 1];
1286 planes[i + 1] = plahlp;
1287 }
1288 }
1289 }
1290
1291 // Checks on the wires, start moving them to the basic x period.
1292 if (perx) {
1293 for (int i = 0; i < nWires; ++i) {
1294 const double xnew = w[i].x - sx * int(round(w[i].x / sx));
1295 if (int(round(w[i].x / sx)) != 0) {
1296 double xprt = w[i].x;
1297 double yprt = w[i].y;
1298 if (polar) RTheta2RhoPhi(xprt, yprt, xprt, yprt);
1299 std::cout << m_className << "::CellCheck:\n";
1300 std::cout << " The " << w[i].type << "-wire at (" << xprt << ", "
1301 << yprt << ") is moved to the basic x (or r) period.\n";
1302 std::cout << " This should not affect the results.\n";
1303 }
1304 w[i].x = xnew;
1305 }
1306 }
1307
1308 // In case of y-periodicity, all wires should be in the first y-period.
1309 if (tube && pery) {
1310 for (int i = 0; i < nWires; ++i) {
1311 double xnew = w[i].x;
1312 double ynew = w[i].y;
1313 Cartesian2Polar(xnew, ynew, xnew, ynew);
1314 if (int(round((Pi / ynew) / (sy * 180.))) != 0) {
1315 std::cout << m_className << "::CellCheck:\n";
1316 std::cout << " The " << w[i].type << "-wire at (" << w[i].x << ", "
1317 << w[i].y << ") is moved to the basic phi period.\n";
1318 std::cout << " This should not affect the results.\n";
1319 ynew -= 180 * sy * int(round((Pi * ynew) / (sy * 180.))) / Pi;
1320 Polar2Cartesian(xnew, ynew, w[i].x, w[i].y);
1321 }
1322 }
1323 } else if (pery) {
1324 for (int i = 0; i < nWires; ++i) {
1325 double ynew = w[i].y - sy * int(round(w[i].y / sy));
1326 if (int(round(w[i].y / sy)) != 0) {
1327 double xprt = w[i].x;
1328 double yprt = w[i].y;
1329 if (polar) RTheta2RhoPhi(xprt, yprt, xprt, yprt);
1330 std::cout << m_className << "::CellCheck:\n";
1331 std::cout << " The " << w[i].type << "-wire at (" << xprt << ", "
1332 << yprt << ") is moved to the basic y period.\n";
1333 std::cout << " This should not affect the results.\n";
1334 }
1335 w[i].y = ynew;
1336 }
1337 }
1338
1339 // Make sure the plane numbering is standard: P1 wires P2, P3 wires P4.
1340 int iplan1 = 0, iplan2 = 0, iplan3 = 0, iplan4 = 0;
1341 for (int i = 0; i < nWires; ++i) {
1342 if (ynplan[0] && w[i].x <= coplan[0]) ++iplan1;
1343 if (ynplan[1] && w[i].x <= coplan[1]) ++iplan2;
1344 if (ynplan[2] && w[i].y <= coplan[2]) ++iplan3;
1345 if (ynplan[3] && w[i].y <= coplan[3]) ++iplan4;
1346 }
1347
1348 // Find out whether smaller (-1) or larger (+1) coord. are to be kept.
1349 if (ynplan[0] && ynplan[1]) {
1350 if (iplan1 > nWires / 2) {
1351 ynplan[1] = false;
1352 iplan1 = -1;
1353 } else {
1354 iplan1 = +1;
1355 }
1356 if (iplan2 < nWires / 2) {
1357 ynplan[0] = false;
1358 iplan2 = +1;
1359 } else {
1360 iplan2 = -1;
1361 }
1362 }
1363 if (ynplan[0] && !ynplan[1]) {
1364 if (iplan1 > nWires / 2)
1365 iplan1 = -1;
1366 else
1367 iplan1 = +1;
1368 }
1369 if (ynplan[1] && !ynplan[0]) {
1370 if (iplan2 < nWires / 2)
1371 iplan2 = +1;
1372 else
1373 iplan2 = -1;
1374 }
1375
1376 if (ynplan[2] && ynplan[3]) {
1377 if (iplan3 > nWires / 2) {
1378 ynplan[3] = false;
1379 iplan3 = -1;
1380 } else {
1381 iplan3 = +1;
1382 }
1383 if (iplan4 < nWires / 2) {
1384 ynplan[2] = false;
1385 iplan4 = +1;
1386 } else {
1387 iplan4 = -1;
1388 }
1389 }
1390 if (ynplan[2] && !ynplan[3]) {
1391 if (iplan3 > nWires / 2)
1392 iplan3 = -1;
1393 else
1394 iplan3 = +1;
1395 }
1396 if (ynplan[3] && !ynplan[2]) {
1397 if (iplan4 < nWires / 2)
1398 iplan4 = +1;
1399 else
1400 iplan4 = -1;
1401 }
1402
1403 // Adapt the numbering of the planes if necessary.
1404 if (iplan1 == -1) {
1405 ynplan[0] = false;
1406 ynplan[1] = true;
1407 coplan[1] = coplan[0];
1408 vtplan[1] = vtplan[0];
1409 planes[1] = planes[0];
1410 }
1411
1412 if (iplan2 == +1) {
1413 ynplan[1] = false;
1414 ynplan[0] = true;
1415 coplan[0] = coplan[1];
1416 vtplan[0] = vtplan[1];
1417 planes[0] = planes[1];
1418 }
1419
1420 if (iplan3 == -1) {
1421 ynplan[2] = false;
1422 ynplan[3] = true;
1423 coplan[3] = coplan[2];
1424 vtplan[3] = vtplan[2];
1425 planes[3] = planes[2];
1426 }
1427
1428 if (iplan4 == +1) {
1429 ynplan[3] = false;
1430 ynplan[2] = true;
1431 coplan[2] = coplan[3];
1432 vtplan[2] = vtplan[3];
1433 planes[2] = planes[3];
1434 }
1435
1436 std::vector<bool> wrong(nWires, false);
1437 // Second pass for the wires, check position relative to the planes.
1438 for (int i = 0; i < nWires; ++i) {
1439 if (ynplan[0] && w[i].x - 0.5 * w[i].d <= coplan[0]) wrong[i] = true;
1440 if (ynplan[1] && w[i].x + 0.5 * w[i].d >= coplan[1]) wrong[i] = true;
1441 if (ynplan[2] && w[i].y - 0.5 * w[i].d <= coplan[2]) wrong[i] = true;
1442 if (ynplan[3] && w[i].y + 0.5 * w[i].d >= coplan[3]) wrong[i] = true;
1443 if (tube) {
1444 if (!InTube(w[i].x, w[i].y, cotube, ntube)) {
1445 std::cerr << m_className << "::CellCheck:\n";
1446 std::cerr << " The " << w[i].type << "-wire at (" << w[i].x << ", "
1447 << w[i].y << ") is located outside the tube.\n";
1448 std::cerr << " This wire is removed.\n";
1449 wrong[i] = true;
1450 }
1451 } else if (wrong[i]) {
1452 double xprt = w[i].x;
1453 double yprt = w[i].y;
1454 if (polar) RTheta2RhoPhi(xprt, yprt, xprt, yprt);
1455 std::cerr << m_className << "::CellCheck:\n";
1456 std::cerr << " The " << w[i].type << "-wire at (" << xprt << ", "
1457 << yprt << ") is located outside the planes.\n";
1458 std::cerr << " This wire is removed.\n";
1459 } else if ((perx && w[i].d >= sx) || (pery && w[i].d >= sy)) {
1460 double xprt = w[i].x;
1461 double yprt = w[i].y;
1462 if (polar) RTheta2RhoPhi(xprt, yprt, xprt, yprt);
1463 std::cerr << m_className << "::CellCheck:\n";
1464 std::cerr << " The diameter of the " << w[i].type << "-wire at ("
1465 << xprt << ", " << yprt << ") exceeds 1 period.\n";
1466 std::cerr << " This wire is removed.\n";
1467 wrong[i] = true;
1468 }
1469 }
1470
1471 // Check the wire spacing.
1472 for (int i = 0; i < nWires; ++i) {
1473 if (wrong[i]) continue;
1474 for (int j = i + 1; j < nWires; ++j) {
1475 if (wrong[j]) continue;
1476 double xsepar = 0.;
1477 double ysepar = 0.;
1478 if (tube) {
1479 if (pery) {
1480 double xaux1, xaux2, yaux1, yaux2;
1481 Cartesian2Polar(w[i].x, w[i].y, xaux1, yaux1);
1482 Cartesian2Polar(w[j].x, w[j].y, xaux2, yaux2);
1483 yaux1 -= sy * int(round(yaux1 / sy));
1484 yaux2 -= sy * int(round(yaux2 / sy));
1485 Polar2Cartesian(xaux1, yaux1, xaux1, yaux1);
1486 Polar2Cartesian(xaux2, yaux2, xaux2, yaux2);
1487 xsepar = xaux1 - xaux2;
1488 ysepar = yaux1 - yaux2;
1489 } else {
1490 xsepar = w[i].x - w[j].x;
1491 ysepar = w[i].y - w[j].y;
1492 }
1493 } else {
1494 xsepar = fabs(w[i].x - w[j].x);
1495 if (perx) xsepar -= sx * int(round(xsepar / sx));
1496 ysepar = fabs(w[i].y - w[j].y);
1497 if (pery) ysepar -= sy * int(round(ysepar / sy));
1498 }
1499 if (xsepar * xsepar + ysepar * ysepar < 0.25 * pow(w[i].d + w[j].d, 2)) {
1500 double xprti = w[i].x;
1501 double yprti = w[i].y;
1502 double xprtj = w[j].x;
1503 double yprtj = w[j].y;
1504 if (polar) RTheta2RhoPhi(xprti, yprti, xprti, yprti);
1505 if (polar) RTheta2RhoPhi(xprtj, yprtj, xprtj, yprtj);
1506 std::cerr << m_className << "::CellCheck:\n";
1507 std::cerr << " The " << w[i].type << "-wire at (" << xprti << ", "
1508 << yprti << ")\n"
1509 << " and the " << w[j].type << "-wire at (" << xprtj
1510 << ", " << yprtj << ") overlap at least partially.\n";
1511 std::cerr << " The latter wire is removed.\n";
1512 wrong[j] = true;
1513 }
1514 }
1515 }
1516
1517 // Remove the wires which are not acceptable for one reason or another.
1518 const int iWires = nWires;
1519 nWires = 0;
1520 for (int i = 0; i < iWires; ++i) {
1521 if (!wrong[i]) {
1522 w[nWires].x = w[i].x;
1523 w[nWires].y = w[i].y;
1524 w[nWires].d = w[i].d;
1525 w[nWires].v = w[i].v;
1526 w[nWires].type = w[i].type;
1527 w[nWires].u = w[i].u;
1528 w[nWires].e = w[i].e;
1529 w[nWires].ind = w[i].ind;
1530 w[nWires].nTrap = w[i].nTrap;
1531 ++nWires;
1532 }
1533 }
1534
1535 // Ensure that some elements are left.
1536 int nElements = nWires;
1537 if (ynplan[0]) ++nElements;
1538 if (ynplan[1]) ++nElements;
1539 if (ynplan[2]) ++nElements;
1540 if (ynplan[3]) ++nElements;
1541 if (tube) ++nElements;
1542
1543 if (nElements < 2) {
1544 std::cerr << m_className << "::CellCheck:\n";
1545 std::cerr << " At least 2 elements are necessary.\n";
1546 std::cerr << " Cell rejected.\n";
1547 return false;
1548 }
1549
1550 // Determine maximum and minimum coordinates and potentials.
1551 bool setx = false;
1552 bool sety = false;
1553 bool setz = false;
1554 bool setv = false;
1555
1556 xmin = xmax = 0.;
1557 ymin = ymax = 0.;
1558 zmin = zmax = 0.;
1559 vmin = vmax = 0.;
1560
1561 // Loop over the wires.
1562 for (int i = nWires; i--;) {
1563 if (setx) {
1564 xmin = std::min(xmin, w[i].x - w[i].d / 2.);
1565 xmax = std::max(xmax, w[i].x + w[i].d / 2.);
1566 } else {
1567 xmin = w[i].x - w[i].d / 2.;
1568 xmax = w[i].x + w[i].d / 2.;
1569 setx = true;
1570 }
1571 if (sety) {
1572 ymin = std::min(ymin, w[i].y - w[i].d / 2.);
1573 ymax = std::max(ymax, w[i].y + w[i].d / 2.);
1574 } else {
1575 ymin = w[i].y - w[i].d / 2.;
1576 ymax = w[i].y + w[i].d / 2.;
1577 sety = true;
1578 }
1579 if (setz) {
1580 zmin = std::min(zmin, -w[i].u / 2.);
1581 zmax = std::max(zmax, +w[i].u / 2.);
1582 } else {
1583 zmin = -w[i].u / 2.;
1584 zmax = +w[i].u / 2.;
1585 setz = true;
1586 }
1587 if (setv) {
1588 vmin = std::min(vmin, w[i].v);
1589 vmax = std::max(vmax, w[i].v);
1590 } else {
1591 vmin = vmax = w[i].v;
1592 setv = true;
1593 }
1594 }
1595 // Consider the planes.
1596 for (int i = 0; i < 4; ++i) {
1597 if (!ynplan[i]) continue;
1598 if (i < 2) {
1599 if (setx) {
1600 xmin = std::min(xmin, coplan[i]);
1601 xmax = std::max(xmax, coplan[i]);
1602 } else {
1603 xmin = xmax = coplan[i];
1604 setx = true;
1605 }
1606 } else {
1607 if (sety) {
1608 ymin = std::min(ymin, coplan[i]);
1609 ymax = std::max(ymax, coplan[i]);
1610 } else {
1611 ymin = ymax = coplan[i];
1612 sety = true;
1613 }
1614 }
1615 if (setv) {
1616 vmin = std::min(vmin, vtplan[i]);
1617 vmax = std::max(vmax, vtplan[i]);
1618 } else {
1619 vmin = vmax = vtplan[i];
1620 setv = true;
1621 }
1622 }
1623
1624 // Consider the tube.
1625 if (tube) {
1626 xmin = -1.1 * cotube;
1627 xmax = +1.1 * cotube;
1628 setx = true;
1629 ymin = -1.1 * cotube;
1630 ymax = +1.1 * cotube;
1631 sety = true;
1632 vmin = std::min(vmin, vttube);
1633 vmax = std::max(vmax, vttube);
1634 setv = true;
1635 }
1636
1637 // In case of x-periodicity, XMAX-XMIN should be SX,
1638 if (perx && sx > (xmax - xmin)) {
1639 xmin = -sx / 2.;
1640 xmax = sx / 2.;
1641 setx = true;
1642 }
1643 // in case of y-periodicity, YMAX-YMIN should be SY,
1644 if (pery && sy > (ymax - ymin)) {
1645 ymin = -sy / 2.;
1646 ymax = sy / 2.;
1647 sety = true;
1648 }
1649 // in case the cell is polar, the y range should be < 2 pi.
1650 if (polar && (ymax - ymin) >= TwoPi) {
1651 ymin = -Pi;
1652 ymax = +Pi;
1653 sety = true;
1654 }
1655
1656 // Fill in missing dimensions.
1657 if (setx && xmin != xmax && (ymin == ymax || !sety)) {
1658 ymin -= fabs(xmax - xmin) / 2.;
1659 ymax += fabs(xmax - xmin) / 2.;
1660 sety = true;
1661 }
1662 if (sety && ymin != ymax && (xmin == xmax || !setx)) {
1663 xmin -= fabs(ymax - ymin) / 2.;
1664 xmax += fabs(ymax - ymin) / 2.;
1665 setx = true;
1666 }
1667
1668 if (!setz) {
1669 zmin = -(fabs(xmax - xmin) + fabs(ymax - ymin)) / 4.;
1670 zmax = +(fabs(xmax - xmin) + fabs(ymax - ymin)) / 4.;
1671 setz = true;
1672 }
1673
1674 // Ensure that all dimensions are now set.
1675 if (!(setx && sety && setz)) {
1676 std::cerr << m_className << "::CellCheck:\n";
1677 std::cerr << " Unable to establish"
1678 << " default dimensions in all directions.\n";
1679 }
1680
1681 // Check that at least some different voltages are present.
1682 if (vmin == vmax || !setv) {
1683 std::cerr << m_className << "::CellCheck:\n";
1684 std::cerr << " All potentials in the cell are the same.\n";
1685 std::cerr << " There is no point in going on.\n";
1686 return false;
1687 }
1688
1689 // Cell seems to be alright since it passed all critical tests.
1690 return true;
1691}
1692
1693bool ComponentAnalyticField::CellType() {
1694
1695 // Tube geometries
1696 if (tube) {
1697 if (ntube == 0) {
1698 if (pery) {
1699 cellType = "D2 ";
1700 iCellType = 11;
1701 } else {
1702 cellType = "D1 ";
1703 iCellType = 10;
1704 }
1705 } else if (ntube >= 3 && ntube <= 8) {
1706 if (pery) {
1707 cellType = "D4 ";
1708 iCellType = 13;
1709 } else {
1710 cellType = "D3 ";
1711 iCellType = 12;
1712 }
1713 } else {
1714 std::cerr << m_className << "::CellType:\n";
1715 std::cerr << " Potentials for tube with " << ntube
1716 << " edges are not yet available.\n";
1717 std::cerr << " Using a round tube instead.\n";
1718 cellType = "D3 ";
1719 ntube = 0;
1720 iCellType = 12;
1721 }
1722 return true;
1723 }
1724
1725 // Find the 'A' type cell.
1726 if (!(perx || pery) && !(ynplan[0] && ynplan[1]) &&
1727 !(ynplan[2] && ynplan[3])) {
1728 cellType = "A ";
1729 iCellType = 1;
1730 return true;
1731 }
1732
1733 // Find the 'B1X' type cell.
1734 if (perx && !pery && !(ynplan[0] || ynplan[1]) && !(ynplan[2] && ynplan[3])) {
1735 cellType = "B1X";
1736 iCellType = 2;
1737 return true;
1738 }
1739
1740 // Find the 'B1Y' type cell.
1741 if (pery && !perx && !(ynplan[0] && ynplan[1]) && !(ynplan[2] || ynplan[3])) {
1742 cellType = "B1Y";
1743 iCellType = 3;
1744 return true;
1745 }
1746
1747 // Find the 'B2X' type cell.
1748 if (perx && !pery && !(ynplan[2] && ynplan[3])) {
1749 cellType = "B2X";
1750 iCellType = 4;
1751 return true;
1752 }
1753
1754 if (!(perx || pery) && !(ynplan[2] && ynplan[3]) &&
1755 (ynplan[0] && ynplan[1])) {
1756 sx = fabs(coplan[1] - coplan[0]);
1757 cellType = "B2X";
1758 iCellType = 4;
1759 return true;
1760 }
1761
1762 // Find the 'B2Y' type cell.
1763 if (pery && !perx && !(ynplan[0] && ynplan[1])) {
1764 cellType = "B2Y";
1765 iCellType = 5;
1766 return true;
1767 }
1768
1769 if (!(perx || pery) && !(ynplan[0] && ynplan[1]) &&
1770 (ynplan[2] && ynplan[3])) {
1771 sy = fabs(coplan[3] - coplan[2]);
1772 cellType = "B2Y";
1773 iCellType = 5;
1774 return true;
1775 }
1776
1777 // Find the 'C1 ' type cell.
1778 if (!(ynplan[0] || ynplan[1] || ynplan[2] || ynplan[3]) && perx && pery) {
1779 cellType = "C1 ";
1780 iCellType = 6;
1781 return true;
1782 }
1783
1784 // Find the 'C2X' type cell.
1785 if (!((ynplan[2] && pery) || (ynplan[2] && ynplan[3]))) {
1786 if (ynplan[0] && ynplan[1]) {
1787 sx = fabs(coplan[1] - coplan[0]);
1788 cellType = "C2X";
1789 iCellType = 7;
1790 return true;
1791 }
1792 if (perx && ynplan[0]) {
1793 cellType = "C2X";
1794 iCellType = 7;
1795 return true;
1796 }
1797 }
1798
1799 // Find the 'C2Y' type cell.
1800 if (!((ynplan[0] && perx) || (ynplan[0] && ynplan[1]))) {
1801 if (ynplan[2] && ynplan[3]) {
1802 sy = fabs(coplan[3] - coplan[2]);
1803 cellType = "C2Y";
1804 iCellType = 8;
1805 return true;
1806 }
1807 if (pery && ynplan[2]) {
1808 cellType = "C2Y";
1809 iCellType = 8;
1810 return true;
1811 }
1812 }
1813
1814 // Find the 'C3 ' type cell.
1815 if (perx && pery) {
1816 cellType = "C3 ";
1817 iCellType = 9;
1818 return true;
1819 }
1820
1821 if (perx) {
1822 sy = fabs(coplan[3] - coplan[2]);
1823 cellType = "C3 ";
1824 iCellType = 9;
1825 return true;
1826 }
1827
1828 if (pery) {
1829 sx = fabs(coplan[1] - coplan[0]);
1830 cellType = "C3 ";
1831 iCellType = 9;
1832 return true;
1833 }
1834
1835 if (ynplan[0] && ynplan[1] && ynplan[2] && ynplan[3]) {
1836 cellType = "C3 ";
1837 sx = fabs(coplan[1] - coplan[0]);
1838 sy = fabs(coplan[3] - coplan[2]);
1839 iCellType = 9;
1840 return true;
1841 }
1842
1843 // Cell is not recognised.
1844 return false;
1845}
1846
1847bool ComponentAnalyticField::PrepareStrips() {
1848
1849 // -----------------------------------------------------------------------
1850 // CELSTR - Assigns default anode-cathode gaps, if applicable.
1851 // (Last changed on 7/12/00.)
1852 // -----------------------------------------------------------------------
1853
1854 double gapDef[4] = {0., 0., 0., 0.};
1855
1856 // Compute default gaps.
1857 if (ynplan[0]) {
1858 if (ynplan[1]) {
1859 gapDef[0] = coplan[1] - coplan[0];
1860 } else if (nWires <= 0) {
1861 gapDef[0] = -1.;
1862 } else {
1863 gapDef[0] = w[0].x - coplan[0];
1864 for (int i = nWires; i--;) {
1865 if (w[i].x - coplan[0] < gapDef[0]) gapDef[0] = w[i].x - coplan[0];
1866 }
1867 }
1868 }
1869
1870 if (ynplan[1]) {
1871 if (ynplan[0]) {
1872 gapDef[1] = coplan[1] - coplan[0];
1873 } else if (nWires <= 0) {
1874 gapDef[1] = -1.;
1875 } else {
1876 gapDef[1] = coplan[1] - w[0].x;
1877 for (int i = nWires; i--;) {
1878 if (coplan[1] - w[i].x < gapDef[1]) gapDef[1] = coplan[1] - w[i].x;
1879 }
1880 }
1881 }
1882
1883 if (ynplan[2]) {
1884 if (ynplan[3]) {
1885 gapDef[2] = coplan[3] - coplan[2];
1886 } else if (nWires <= 0) {
1887 gapDef[2] = -1.;
1888 } else {
1889 gapDef[2] = w[0].y - coplan[2];
1890 for (int i = nWires; i--;) {
1891 if (w[i].y - coplan[2] < gapDef[2]) gapDef[2] = w[i].y - coplan[2];
1892 }
1893 }
1894 }
1895
1896 if (ynplan[3]) {
1897 if (ynplan[2]) {
1898 gapDef[3] = coplan[3] - coplan[2];
1899 } else if (nWires <= 0) {
1900 gapDef[3] = -1.;
1901 } else {
1902 gapDef[3] = coplan[3] - w[0].y;
1903 for (int i = nWires; i--;) {
1904 if (coplan[3] - w[i].y < gapDef[3]) gapDef[3] = coplan[3] - w[i].y;
1905 }
1906 }
1907 }
1908
1909 // Assign.
1910 for (int i = 0; i < 4; ++i) {
1911 for (int j = planes[i].nStrips1; j--;) {
1912 if (planes[i].strips1[j].gap < 0.) {
1913 planes[i].strips1[j].gap = gapDef[i];
1914 }
1915 if (planes[i].strips1[j].gap < 0.) {
1916 std::cerr << m_className << "::PrepareStrips:\n";
1917 std::cerr << " Not able to set a default anode-cathode gap\n";
1918 std::cerr << " for x/y-strip " << j << " of plane " << i << ".\n";
1919 return false;
1920 }
1921 }
1922 for (int j = planes[i].nStrips2; j--;) {
1923 if (planes[i].strips2[j].gap < 0.) {
1924 planes[i].strips2[j].gap = gapDef[i];
1925 }
1926 if (planes[i].strips2[j].gap < 0.) {
1927 std::cerr << m_className << "::PrepareStrips:\n";
1928 std::cerr << " Not able to set a default anode-cathode gap\n";
1929 std::cerr << " for z-strip " << j << " of plane " << i << ".\n";
1930 return false;
1931 }
1932 }
1933 }
1934
1935 return true;
1936}
1937
1938void ComponentAnalyticField::AddReadout(const std::string label) {
1939
1940 // Check if this readout group already exists.
1941 for (int i = 0; i < nReadout; ++i) {
1942 if (readout[i] == label) {
1943 std::cout << m_className << "::AddReadout:\n";
1944 std::cout << " Readout group " << label << " already exists.\n";
1945 return;
1946 }
1947 }
1948
1949 readout.push_back(label);
1950 ++nReadout;
1951
1952 int nWiresFound = 0;
1953 for (int i = 0; i < nWires; ++i) {
1954 if (w[i].type == label) ++nWiresFound;
1955 }
1956
1957 int nPlanesFound = 0;
1958 int nStripsFound = 0;
1959 for (int i = 0; i < 5; ++i) {
1960 if (planes[i].type == label) ++nPlanesFound;
1961 for (int j = 0; j < planes[i].nStrips1; ++j) {
1962 if (planes[i].strips1[j].type == label) ++nStripsFound;
1963 }
1964 for (int j = 0; j < planes[i].nStrips2; ++j) {
1965 if (planes[i].strips2[j].type == label) ++nStripsFound;
1966 }
1967 }
1968
1969 if (nWiresFound == 0 && nPlanesFound == 0 && nStripsFound == 0) {
1970 std::cerr << m_className << "::AddReadout:\n";
1971 std::cerr << " At present there are no wires, planes or strips\n";
1972 std::cerr << " associated to readout group " << label << ".\n";
1973 } else {
1974 std::cout << m_className << "::AddReadout:\n";
1975 std::cout << " Readout group " << label << " comprises:\n";
1976 if (nWiresFound > 1) {
1977 std::cout << " " << nWiresFound << " wires\n";
1978 } else if (nWiresFound == 1) {
1979 std::cout << " 1 wire\n";
1980 }
1981 if (nPlanesFound > 1) {
1982 std::cout << " " << nPlanesFound << " planes\n";
1983 } else if (nPlanesFound == 1) {
1984 std::cout << " 1 plane\n";
1985 }
1986 if (nStripsFound > 1) {
1987 std::cout << " " << nStripsFound << " strips\n";
1988 } else if (nStripsFound == 1) {
1989 std::cout << " 1 strip\n";
1990 }
1991 }
1992
1993 sigset = false;
1994}
1995
1996bool ComponentAnalyticField::Setup() {
1997
1998 //-----------------------------------------------------------------------
1999 // SETUP - Routine calling the appropriate setup routine.
2000 // (Last changed on 19/ 9/07.)
2001 //-----------------------------------------------------------------------
2002
2003 // Set a separate set of plane variables to avoid repeated loops.
2004 if (ynplan[0]) {
2005 coplax = coplan[0];
2006 ynplax = true;
2007 } else if (ynplan[1]) {
2008 coplax = coplan[1];
2009 ynplax = true;
2010 } else {
2011 ynplax = false;
2012 }
2013
2014 if (ynplan[2]) {
2015 coplay = coplan[2];
2016 ynplay = true;
2017 } else if (ynplan[3]) {
2018 coplay = coplan[3];
2019 ynplay = true;
2020 } else {
2021 ynplay = false;
2022 }
2023
2024 // Set the correction parameters for the planes.
2025 if (tube) {
2026 corvta = 0.;
2027 corvtb = 0.;
2028 corvtc = vttube;
2029 } else if ((ynplan[0] && ynplan[1]) && !(ynplan[2] || ynplan[3])) {
2030 corvta = (vtplan[0] - vtplan[1]) / (coplan[0] - coplan[1]);
2031 corvtb = 0.;
2032 corvtc = (vtplan[1] * coplan[0] - vtplan[0] * coplan[1]) /
2033 (coplan[0] - coplan[1]);
2034 } else if ((ynplan[2] && ynplan[3]) && !(ynplan[0] || ynplan[1])) {
2035 corvta = 0.;
2036 corvtb = (vtplan[2] - vtplan[3]) / (coplan[2] - coplan[3]);
2037 corvtc = (vtplan[3] * coplan[2] - vtplan[2] * coplan[3]) /
2038 (coplan[2] - coplan[3]);
2039 } else {
2040 corvta = corvtb = corvtc = 0.;
2041 if (ynplan[0]) corvtc = vtplan[0];
2042 if (ynplan[1]) corvtc = vtplan[1];
2043 if (ynplan[2]) corvtc = vtplan[2];
2044 if (ynplan[3]) corvtc = vtplan[3];
2045 }
2046
2047 // Skip wire calculations if there aren't any.
2048 if (nWires <= 0) return true;
2049
2050 // Redimension the capacitance matrix
2051 a.resize(nWires);
2052 for (int i = 0; i < nWires; ++i) {
2053 a[i].resize(nWires);
2054 }
2055
2056 bool ok = true;
2057
2058 // Call the set routine appropriate for the present cell type.
2059 if (cellType == "A ") ok = SetupA00();
2060 if (cellType == "B1X") ok = SetupB1X();
2061 if (cellType == "B1Y") ok = SetupB1Y();
2062 if (cellType == "B2X") ok = SetupB2X();
2063 if (cellType == "B2Y") ok = SetupB2Y();
2064 if (cellType == "C1 ") ok = SetupC10();
2065 if (cellType == "C2X") ok = SetupC2X();
2066 if (cellType == "C2Y") ok = SetupC2Y();
2067 if (cellType == "C3 ") ok = SetupC30();
2068 if (cellType == "D1 ") ok = SetupD10();
2069 if (cellType == "D2 ") ok = SetupD20();
2070 if (cellType == "D3 ") ok = SetupD30();
2071
2072 // Add dipole terms if required
2073 if (ok && dipole) {
2074 ok = SetupDipole();
2075 if (!ok) {
2076 std::cerr << m_className << "::Setup:\n";
2077 std::cerr << " Computing the dipole moments failed.\n";
2078 }
2079 }
2080
2081 a.clear();
2082
2083 if (!ok) {
2084 std::cerr << m_className << "::Setup:\n";
2085 std::cerr << " Preparing the cell for field calculations"
2086 << " did not succeed.\n";
2087 return false;
2088 }
2089 return true;
2090}
2091
2092bool ComponentAnalyticField::SetupA00() {
2093
2094 //-----------------------------------------------------------------------
2095 // SETA00 - Subroutine preparing the field calculations by calculating
2096 // the charges on the wires, for the cell with one charge and
2097 // not more than one plane in either x or y.
2098 // The potential used is log(r).
2099 //-----------------------------------------------------------------------
2100
2101 // Loop over all wire combinations.
2102 for (int i = 0; i < nWires; ++i) {
2103 a[i][i] = 0.25 * w[i].d * w[i].d;
2104 // Take care of the equipotential planes.
2105 if (ynplax) a[i][i] /= 4. * pow(w[i].x - coplax, 2);
2106 if (ynplay) a[i][i] /= 4. * pow(w[i].y - coplay, 2);
2107 // Take care of combinations of equipotential planes.
2108 if (ynplax && ynplay)
2109 a[i][i] *= 4.0 * (pow(w[i].x - coplax, 2) + pow(w[i].y - coplay, 2));
2110 // Define the final version of a[i][i].
2111 a[i][i] = -0.5 * log(a[i][i]);
2112 // Loop over all other wires for the off-diagonal elements.
2113 for (int j = i + 1; j < nWires; ++j) {
2114 a[i][j] = pow(w[i].x - w[j].x, 2) + pow(w[i].y - w[j].y, 2);
2115 // Take care of equipotential planes.
2116 if (ynplax)
2117 a[i][j] = a[i][j] / (pow(w[i].x + w[j].x - 2. * coplax, 2) +
2118 pow(w[i].y - w[j].y, 2));
2119 if (ynplay)
2120 a[i][j] = a[i][j] / (pow(w[i].x - w[j].x, 2) +
2121 pow(w[i].y + w[j].y - 2. * coplay, 2));
2122 // Take care of pairs of equipotential planes in different directions.
2123 if (ynplax && ynplay)
2124 a[i][j] *= pow(w[i].x + w[j].x - 2. * coplax, 2) +
2125 pow(w[i].y + w[j].y - 2. * coplay, 2);
2126 // Define a final version of a[i][j].
2127 a[i][j] = -0.5 * log(a[i][j]);
2128 // Copy this to a[j][i] since the capacitance matrix is symmetric.
2129 a[j][i] = a[i][j];
2130 }
2131 }
2132 // Call CHARGE to calculate the charges really.
2133 return Charge();
2134}
2135
2136bool ComponentAnalyticField::SetupB1X() {
2137
2138 //-----------------------------------------------------------------------
2139 // SETB1X - Routine preparing the field calculations by filling the
2140 // c-matrix, the potential used is re(log(sin Pi/s (z-z0))).
2141 // VARIABLES : xx : Difference in x of two wires * factor.
2142 // yy : Difference in y of two wires * factor.
2143 // yymirr : Difference in y of one wire and the mirror
2144 // image of another * factor.
2145 // r2plan : Periodic length of (xx,yymirr)
2146 //-----------------------------------------------------------------------
2147
2148 double xx = 0., yy = 0., yymirr = 0.;
2149 double r2plan = 0.;
2150
2151 // Loop over all wires and calculate the diagonal elements first.
2152 for (int i = 0; i < nWires; ++i) {
2153 a[i][i] = -log(0.5 * w[i].d * Pi / sx);
2154 // Take care of a plane at constant y if it exist.
2155 if (ynplay) {
2156 yy = (Pi / sx) * 2. * (w[i].y - coplay);
2157 if (fabs(yy) > 20.) a[i][i] += fabs(yy) - CLog2;
2158 if (fabs(yy) <= 20.) a[i][i] += log(fabs(sinh(yy)));
2159 }
2160 // Loop over all other wires to obtain off-diagonal elements.
2161 for (int j = i + 1; j < nWires; ++j) {
2162 xx = (Pi / sx) * (w[i].x - w[j].x);
2163 yy = (Pi / sx) * (w[i].y - w[j].y);
2164 if (fabs(yy) > 20.) a[i][j] = -fabs(yy) + CLog2;
2165 if (fabs(yy) <= 20.)
2166 a[i][j] = -0.5 * log(pow(sinh(yy), 2) + pow(sin(xx), 2));
2167 // Take equipotential planes into account if they exist.
2168 if (ynplay) {
2169 r2plan = 0.;
2170 yymirr = (Pi / sx) * (w[i].y + w[j].y - 2. * coplay);
2171 if (fabs(yymirr) > 20.) r2plan = fabs(yymirr) - CLog2;
2172 if (fabs(yymirr) <= 20.)
2173 r2plan = 0.5 * log(pow(sinh(yymirr), 2) + pow(sin(xx), 2));
2174 a[i][j] += r2plan;
2175 }
2176 // Copy a[i][j] to a[j][i], the capactance matrix is symmetric.
2177 a[j][i] = a[i][j];
2178 }
2179 }
2180 // Call function CHARGE calculating all kinds of useful things.
2181 return Charge();
2182}
2183
2184bool ComponentAnalyticField::SetupB1Y() {
2185
2186 //-----------------------------------------------------------------------
2187 // SETB1Y - Routine preparing the field calculations by setting the
2188 // charges. The potential used is Re log(sinh Pi/sy(z-z0)).
2189 // VARIABLES : yy : Difference in y of two wires * factor.
2190 // xxmirr : Difference in x of one wire and the mirror
2191 // image of another * factor.
2192 // r2plan : Periodic length of (xxmirr,yy).
2193 //-----------------------------------------------------------------------
2194
2195 double xx = 0., yy = 0., xxmirr = 0.;
2196 double r2plan = 0.;
2197
2198 // Loop over all wires and calculate the diagonal elements first.
2199 for (int i = 0; i < nWires; ++i) {
2200 a[i][i] = -log(0.5 * w[i].d * Pi / sy);
2201 // Take care of planes 1 and 2 if present.
2202 if (ynplax) {
2203 xx = (Pi / sy) * 2. * (w[i].x - coplax);
2204 if (fabs(xx) > 20.) a[i][i] += fabs(xx) - CLog2;
2205 if (fabs(xx) <= 20.) a[i][i] += log(fabs(sinh(xx)));
2206 }
2207 // Loop over all other wires to obtain off-diagonal elements.
2208 for (int j = i + 1; j < nWires; ++j) {
2209 xx = (Pi / sy) * (w[i].x - w[j].x);
2210 yy = (Pi / sy) * (w[i].y - w[j].y);
2211 if (fabs(xx) > 20.) a[i][j] = -fabs(xx) + CLog2;
2212 if (fabs(xx) <= 20.)
2213 a[i][j] = -0.5 * log(pow(sinh(xx), 2.) + pow(sin(yy), 2.));
2214 // Take care of a plane at constant x.
2215 if (ynplax) {
2216 xxmirr = (Pi / sy) * (w[i].x + w[j].x - 2. * coplax);
2217 r2plan = 0.;
2218 if (fabs(xxmirr) > 20.) r2plan = fabs(xxmirr) - CLog2;
2219 if (fabs(xxmirr) <= 20.)
2220 r2plan = 0.5 * log(pow(sinh(xxmirr), 2.) + pow(sin(yy), 2.));
2221 a[i][j] += r2plan;
2222 }
2223 // Copy a[i][j] to a[j][i], the capacitance matrix is symmetric.
2224 a[j][i] = a[i][j];
2225 }
2226 }
2227 // Call function CHARGE calculating all kinds of useful things.
2228 return Charge();
2229}
2230
2231bool ComponentAnalyticField::SetupB2X() {
2232
2233 //-----------------------------------------------------------------------
2234 // SETB2X - Routine preparing the field calculations by setting the
2235 // charges.
2236 // VARIABLES : xx : Difference in x of two wires * factor.
2237 // yy : Difference in y of two wires * factor.
2238 // xxneg : Difference in x of one wire and the mirror
2239 // image in period direction of another * fac.
2240 // yymirr : Difference in y of one wire and the mirror
2241 // image of another * factor.
2242 //-----------------------------------------------------------------------
2243
2244 b2sin.resize(nWires);
2245
2246 double xx, yy, xxneg, yymirr;
2247
2248 // Loop over all wires and calculate the diagonal elements first.
2249 for (int i = 0; i < nWires; ++i) {
2250 xx = (Pi / sx) * (w[i].x - coplax);
2251 a[i][i] = (0.25 * w[i].d * Pi / sx) / sin(xx);
2252 // Take care of a plane at constant y if it exists.
2253 if (ynplay) {
2254 yymirr = (Pi / sx) * (w[i].y - coplay);
2255 if (fabs(yymirr) <= 20.) {
2256 a[i][i] *= sqrt(pow(sinh(yymirr), 2) + pow(sin(xx), 2)) / sinh(yymirr);
2257 }
2258 }
2259 // Store the true value of a[i][i].
2260 a[i][i] = -log(fabs(a[i][i]));
2261 // Loop over all other wires to obtain off-diagonal elements.
2262 for (int j = i + 1; j < nWires; ++j) {
2263 xx = HalfPi * (w[i].x - w[j].x) / sx;
2264 yy = HalfPi * (w[i].y - w[j].y) / sx;
2265 xxneg = HalfPi * (w[i].x + w[j].x - 2. * coplax) / sx;
2266 if (fabs(yy) <= 20.) {
2267 a[i][j] = (pow(sinh(yy), 2) + pow(sin(xx), 2)) /
2268 (pow(sinh(yy), 2) + pow(sin(xxneg), 2));
2269 }
2270 if (fabs(yy) > 20.) a[i][j] = 1.0;
2271 // Take an equipotential plane at constant y into account.
2272 if (ynplay) {
2273 yymirr = HalfPi * (w[i].y + w[j].y - 2. * coplay) / sx;
2274 if (fabs(yymirr) <= 20.) {
2275 a[i][j] *= (pow(sinh(yymirr), 2) + pow(sin(xxneg), 2)) /
2276 (pow(sinh(yymirr), 2) + pow(sin(xx), 2));
2277 }
2278 }
2279 // Store the true value of a[i][j] in both a[i][j] and a[j][i].
2280 a[i][j] = -0.5 * log(a[i][j]);
2281 a[j][i] = a[i][j];
2282 }
2283 // Set the b2sin vector.
2284 b2sin[i] = sin(Pi * (coplax - w[i].x) / sx);
2285 }
2286 // Call function CHARGE calculating all kinds of useful things.
2287 return Charge();
2288}
2289
2290bool ComponentAnalyticField::SetupB2Y() {
2291
2292 //-----------------------------------------------------------------------
2293 // SETB2Y - Routine preparing the field calculations by setting the
2294 // charges.
2295 // VARIABLES : xx : Difference in x of two wires * factor.
2296 // yy : Difference in y of two wires * factor.
2297 // xxmirr : Difference in x of one wire and the mirror
2298 // image of another * factor.
2299 // yyneg : Difference in y of one wire and the mirror
2300 // image in period direction of another * fac.
2301 //-----------------------------------------------------------------------
2302
2303 b2sin.resize(nWires);
2304
2305 double xx = 0., yy = 0.;
2306 double xxmirr = 0.;
2307 double yyneg = 0.;
2308
2309 // Loop over all wires and calculate the diagonal elements first.
2310 for (int i = 0; i < nWires; ++i) {
2311 yy = (Pi / sy) * (w[i].y - coplay);
2312 a[i][i] = (0.25 * w[i].d * Pi / sy) / sin(yy);
2313 // Take care of a plane at constant x if present.
2314 if (ynplax) {
2315 xxmirr = (Pi / sy) * (w[i].x - coplax);
2316 if (fabs(xxmirr) <= 20.) {
2317 a[i][i] *= sqrt(pow(sinh(xxmirr), 2) + pow(sin(yy), 2)) / sinh(xxmirr);
2318 }
2319 }
2320 // Store the true value of a[i][i].
2321 a[i][i] = -log(fabs(a[i][i]));
2322 // Loop over all other wires to obtain off-diagonal elements.
2323 for (int j = i + 1; j < nWires; j++) {
2324 xx = HalfPi * (w[i].x - w[j].x) / sy;
2325 yy = HalfPi * (w[i].y - w[j].y) / sy;
2326 yyneg = HalfPi * (w[i].y + w[j].y - 2. * coplay) / sy;
2327 if (fabs(xx) <= 20.) {
2328 a[i][j] = (pow(sinh(xx), 2) + pow(sin(yy), 2)) /
2329 (pow(sinh(xx), 2) + pow(sin(yyneg), 2));
2330 }
2331 if (fabs(xx) > 20.) a[i][j] = 1.0;
2332 // Take an equipotential plane at constant x into account.
2333 if (ynplax) {
2334 xxmirr = HalfPi * (w[i].x + w[j].x - 2. * coplax) / sy;
2335 if (fabs(xxmirr) <= 20.) {
2336 a[i][j] *= (pow(sinh(xxmirr), 2) + pow(sin(yyneg), 2)) /
2337 (pow(sinh(xxmirr), 2) + pow(sin(yy), 2));
2338 }
2339 }
2340 // Store the true value of a[i][j] in both a[i][j] and a[j][i].
2341 a[i][j] = -0.5 * log(a[i][j]);
2342 a[j][i] = a[i][j];
2343 }
2344 // Set the b2sin vector.
2345 b2sin[i] = sin(Pi * (coplay - w[i].y) / sy);
2346 }
2347 // Call function CHARGE calculating all kinds of useful things.
2348 return Charge();
2349}
2350
2351bool ComponentAnalyticField::SetupC10() {
2352 //-----------------------------------------------------------------------
2353 // SETC10 - This initialising routine computes the wire charges E and
2354 // sets certain constants in common. The wire are located at
2355 // (x[j],y[j])+(LX*SX,LY*SY), J=1(1)NWIRE,
2356 // LX=-infinity(1)infinity, LY=-infinity(1)infinity.
2357 // Use is made of the function PH2.
2358 //
2359 // (Written by G.A.Erskine/DD, 14.8.1984 modified to some extent)
2360 //-----------------------------------------------------------------------
2361
2362 // Initialise the constants.
2363 double p = 0.;
2364 p1 = p2 = 0.;
2365
2366 mode = 0;
2367 if (sx <= sy) {
2368 mode = 1;
2369 if (sy / sx < 8.) p = exp(-Pi * sy / sx);
2370 zmult = std::complex<double>(Pi / sx, 0.);
2371 } else {
2372 mode = 0;
2373 if (sx / sy < 8.) p = exp(-Pi * sx / sy);
2374 zmult = std::complex<double>(0., Pi / sy);
2375 }
2376 p1 = p * p;
2377 if (p1 > 1.e-10) p2 = pow(p, 6);
2378
2379 if (debug) {
2380 std::cout << m_className << "::SetupC10:\n";
2381 std::cout << " p, p1, p2 = " << p << ", " << p1 << ", " << p2 << "\n";
2382 std::cout << " zmult = " << zmult << "\n";
2383 std::cout << " mode = " << mode << "\n";
2384 }
2385
2386 double xyi = 0., xyj = 0., temp = 0.;
2387 // Store the capacitance matrix.
2388 for (int i = 0; i < nWires; ++i) {
2389 for (int j = 0; j < nWires; ++j) {
2390 xyi = mode == 0 ? w[i].x : w[i].y;
2391 xyj = mode == 0 ? w[j].x : w[j].y;
2392 temp = xyi * xyj * TwoPi / (sx * sy);
2393 if (i == j) {
2394 a[i][j] = Ph2Lim(0.5 * w[i].d) - temp;
2395 } else {
2396 a[i][j] = Ph2(w[i].x - w[j].x, w[i].y - w[j].y) - temp;
2397 }
2398 }
2399 }
2400 // Call CHARGE to find the charges.
2401 if (!Charge()) return false;
2402 // Calculate the non-logarithmic term in the potential.
2403 double s = 0.;
2404 for (int j = 0; j < nWires; ++j) {
2405 xyj = mode == 0 ? w[j].x : w[j].y;
2406 s += w[j].e * xyj;
2407 }
2408 c1 = -s * 2. * Pi / (sx * sy);
2409 return true;
2410}
2411
2412bool ComponentAnalyticField::SetupC2X() {
2413
2414 //-----------------------------------------------------------------------
2415 // SETC2X - This initializing subroutine stores the capacitance matrix
2416 // for the configuration:
2417 // wires at zw(j)+cmplx(lx*2*sx,ly*sy),
2418 // j=1(1)n, lx=-infinity(1)infinity, ly=-infinity(1)infinity.
2419 // but the signs of the charges alternate in the x-direction
2420 //-----------------------------------------------------------------------
2421
2422 // Initialise the constants.
2423 double p = 0.;
2424 p1 = p2 = 0.;
2425
2426 mode = 0;
2427 if (2. * sx <= sy) {
2428 mode = 1;
2429 if (sy / sx < 25.) p = exp(-HalfPi * sy / sx);
2430 zmult = std::complex<double>(HalfPi / sx, 0.);
2431 } else {
2432 mode = 0;
2433 if (sx / sy < 6.) p = exp(-2. * Pi * sx / sy);
2434 zmult = std::complex<double>(0., Pi / sy);
2435 }
2436 p1 = p * p;
2437 if (p1 > 1.e-10) p2 = pow(p, 6);
2438
2439 if (debug) {
2440 std::cout << m_className << "::SetupC2X:\n";
2441 std::cout << " p, p1, p2 = " << p << ", " << p1 << ", " << p2 << "\n";
2442 std::cout << " zmult = " << zmult << "\n";
2443 std::cout << " mode = " << mode << "\n";
2444 }
2445
2446 double temp = 0.;
2447 // Fill the capacitance matrix.
2448 for (int i = 0; i < nWires; ++i) {
2449 const double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
2450 for (int j = 0; j < nWires; ++j) {
2451 temp = 0.;
2452 if (mode == 0) temp = (w[i].x - cx) * (w[j].x - cx) * TwoPi / (sx * sy);
2453 if (i == j) {
2454 a[i][i] = Ph2Lim(0.5 * w[i].d) - Ph2(2. * (w[i].x - cx), 0.) - temp;
2455 } else {
2456 a[i][j] = Ph2(w[i].x - w[j].x, w[i].y - w[j].y) -
2457 Ph2(w[i].x + w[j].x - 2. * cx, w[i].y - w[j].y) - temp;
2458 }
2459 }
2460 }
2461 // Call CHARGE to find the wire charges.
2462 if (!Charge()) return false;
2463 // Determine the non-logarithmic part of the potential (0 if MODE=1).
2464 c1 = 0.;
2465 if (mode == 0) {
2466 double s = 0.;
2467 for (int i = 0; i < nWires; ++i) {
2468 const double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
2469 s += w[i].e * (w[i].x - cx);
2470 }
2471 c1 = -s * TwoPi / (sx * sy);
2472 }
2473 return true;
2474}
2475
2476bool ComponentAnalyticField::SetupC2Y() {
2477
2478 //-----------------------------------------------------------------------
2479 // SETC2Y - This initializing subroutine stores the capacitance matrix
2480 // for the configuration:
2481 // wires at zw(j)+cmplx(lx*sx,ly*2*sy),
2482 // j=1(1)n, lx=-infinity(1)infinity, ly=-infinity(1)infinity.
2483 // but the signs of the charges alternate in the y-direction
2484 //-----------------------------------------------------------------------
2485
2486 // Initialise the constants.
2487 double p = 0.;
2488 p1 = p2 = 0.;
2489
2490 mode = 0;
2491 if (sx <= 2. * sy) {
2492 mode = 1;
2493 if (sy / sx <= 6.) p = exp(-2. * Pi * sy / sx);
2494 zmult = std::complex<double>(Pi / sx, 0.);
2495 } else {
2496 mode = 0;
2497 if (sx / sy <= 25.) p = exp(-HalfPi * sx / sy);
2498 zmult = std::complex<double>(0., HalfPi / sy);
2499 }
2500 p1 = p * p;
2501 if (p1 > 1.e-10) p2 = pow(p, 6);
2502
2503 if (debug) {
2504 std::cout << m_className << "::SetupC2Y:\n";
2505 std::cout << " p, p1, p2 = " << p << ", " << p1 << ", " << p2 << "\n";
2506 std::cout << " zmult = " << zmult << "\n";
2507 std::cout << " mode = " << mode << "\n";
2508 }
2509
2510 // Fill the capacitance matrix.
2511 double temp = 0.;
2512 for (int i = 0; i < nWires; ++i) {
2513 const double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
2514 for (int j = 0; j < nWires; ++j) {
2515 temp = 0.;
2516 if (mode == 1) temp = (w[i].y - cy) * (w[j].y - cy) * TwoPi / (sx * sy);
2517 if (i == j) {
2518 a[i][i] = Ph2Lim(0.5 * w[i].d) - Ph2(0., 2. * (w[j].y - cy)) - temp;
2519 } else {
2520 a[i][j] = Ph2(w[i].x - w[j].x, w[i].y - w[j].y) -
2521 Ph2(w[i].x - w[j].x, w[i].y + w[j].y - 2. * cy) - temp;
2522 }
2523 }
2524 }
2525 // Call CHARGE to find the wire charges.
2526 if (!Charge()) return false;
2527 // The non-logarithmic part of the potential is zero if MODE=0.
2528 c1 = 0.;
2529 if (mode == 1) {
2530 double s = 0.;
2531 for (int i = 0; i < nWires; ++i) {
2532 const double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
2533 s += w[i].e * (w[i].y - cy);
2534 }
2535 c1 = -s * TwoPi / (sx * sy);
2536 }
2537 return true;
2538}
2539
2540bool ComponentAnalyticField::SetupC30() {
2541
2542 //-----------------------------------------------------------------------
2543 // SETC30 - This initializing subroutine stores the capacitance matrix
2544 // for a configuration with
2545 // wires at zw(j)+cmplx(lx*2*sx,ly*2*sy),
2546 // j=1(1)n, lx=-infinity(1)infinity, ly=-infinity(1)infinity.
2547 // but the signs of the charges alternate in both directions.
2548 //-----------------------------------------------------------------------
2549
2550 // Initialise the constants.
2551 double p = 0.;
2552 p1 = p2 = 0.;
2553
2554 mode = 0;
2555 if (sx <= sy) {
2556 mode = 1;
2557 if (sy / sx <= 13.) p = exp(-Pi * sy / sx);
2558 zmult = std::complex<double>(HalfPi / sx, 0.);
2559 } else {
2560 mode = 0;
2561 if (sx / sy <= 13.) p = exp(-Pi * sx / sy);
2562 zmult = std::complex<double>(0., HalfPi / sy);
2563 }
2564 p1 = p * p;
2565 if (p1 > 1.e-10) p2 = pow(p, 6);
2566
2567 if (debug) {
2568 std::cout << m_className << "::SetupC30:\n";
2569 std::cout << " p, p1, p2 = " << p << ", " << p1 << ", " << p2 << "\n";
2570 std::cout << " zmult = " << zmult << "\n";
2571 std::cout << " mode = " << mode << "\n";
2572 }
2573
2574 // Fill the capacitance matrix.
2575 for (int i = 0; i < nWires; ++i) {
2576 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
2577 double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
2578 for (int j = 0; j < nWires; ++j) {
2579 if (i == j) {
2580 a[i][i] = Ph2Lim(0.5 * w[i].d) - Ph2(0., 2. * (w[i].y - cy)) -
2581 Ph2(2. * (w[i].x - cx), 0.) +
2582 Ph2(2. * (w[i].x - cx), 2. * (w[i].y - cy));
2583 } else {
2584 a[i][j] = Ph2(w[i].x - w[j].x, w[i].y - w[j].y) -
2585 Ph2(w[i].x - w[j].x, w[i].y + w[j].y - 2. * cy) -
2586 Ph2(w[i].x + w[j].x - 2. * cx, w[i].y - w[j].y) +
2587 Ph2(w[i].x + w[j].x - 2. * cx, w[i].y + w[j].y - 2. * cy);
2588 }
2589 }
2590 }
2591 // Call CHARGE to find the wire charges.
2592 if (!Charge()) return false;
2593 // The non-logarithmic part of the potential is zero in this case.
2594 c1 = 0.;
2595 return true;
2596}
2597
2598bool ComponentAnalyticField::SetupD10() {
2599
2600 //-----------------------------------------------------------------------
2601 // SETD10 - Subroutine preparing the field calculations by calculating
2602 // the charges on the wires, for cells with a tube.
2603 //
2604 // (Last changed on 4/ 9/95.)
2605 //-----------------------------------------------------------------------
2606
2607 std::complex<double> zi, zj;
2608
2609 // Loop over all wires.
2610 for (int i = 0; i < nWires; ++i) {
2611 // Set the diagonal terms.
2612 a[i][i] = -log(0.5 * w[i].d * cotube /
2613 (cotube * cotube - (w[i].x * w[i].x + w[i].y * w[i].y)));
2614 // Set a complex wire-coordinate to make things a little easier.
2615 zi = std::complex<double>(w[i].x, w[i].y);
2616 // Loop over all other wires for the off-diagonal elements.
2617 for (int j = i + 1; j < nWires; j++) {
2618 // Set a complex wire-coordinate to make things a little easier.
2619 zj = std::complex<double>(w[j].x, w[j].y);
2620 a[i][j] =
2621 -log(abs(cotube * (zi - zj) / (cotube * cotube - conj(zi) * zj)));
2622 // Copy this to a[j][i] since the capacitance matrix is symmetric.
2623 a[j][i] = a[i][j];
2624 }
2625 }
2626 // Call CHARGE to calculate the charges really.
2627 return Charge();
2628}
2629
2630bool ComponentAnalyticField::SetupD20() {
2631
2632 //-----------------------------------------------------------------------
2633 // SETD20 - Subroutine preparing the field calculations by calculating
2634 // the charges on the wires, for cells with a tube and a
2635 // phi periodicity. Assymetric capacitance matrix !
2636 //
2637 // (Last changed on 18/ 2/93.)
2638 //-----------------------------------------------------------------------
2639
2640 std::complex<double> zi, zj;
2641
2642 // Loop over all wires.
2643 for (int i = 0; i < nWires; ++i) {
2644 // Set a complex wire-coordinate to make things a little easier.
2645 zi = std::complex<double>(w[i].x, w[i].y);
2646 if (abs(zi) < w[i].d / 2.) {
2647 // Case of a wire near the centre.
2648 // Inner loop over the wires.
2649 for (int j = 0; j < nWires; ++j) {
2650 if (i == j) {
2651 // Set the diagonal terms.
2652 a[i][i] =
2653 -log(0.5 * w[i].d /
2654 (cotube - (w[i].x * w[i].x + w[i].y * w[i].y) / cotube));
2655 } else {
2656 // Off-diagonal terms.
2657 zj = std::complex<double>(w[j].x, w[j].y);
2658 a[j][i] = -log(abs((1. / cotube) * (zi - zj) /
2659 (1. - conj(zi) * zj / (cotube * cotube))));
2660 }
2661 }
2662 } else {
2663 // Normal case.
2664 // Inner wire loop.
2665 for (int j = 0; j < nWires; ++j) {
2666 if (i == j) {
2667 // Diagonal elements.
2668 a[i][i] = -log(abs(0.5 * w[i].d * mtube * pow(zi, mtube - 1) /
2669 (pow(cotube, mtube) *
2670 (1. - pow((abs(zi) / cotube), 2 * mtube)))));
2671 } else {
2672 // Off-diagonal terms.
2673 zj = std::complex<double>(w[j].x, w[j].y);
2674 a[j][i] =
2675 -log(abs((1 / pow(cotube, mtube)) *
2676 (pow(zj, mtube) - pow(zi, mtube)) /
2677 (1. - pow(zj * conj(zi) / (cotube * cotube), mtube))));
2678 }
2679 }
2680 }
2681 }
2682 // Call CHARGE to calculate the charges really.
2683 return Charge();
2684}
2685
2686bool ComponentAnalyticField::SetupD30() {
2687
2688 //-----------------------------------------------------------------------
2689 // SETD30 - Subroutine preparing the field calculations by calculating
2690 // the charges on the wires, for cells with wires inside a
2691 // polygon.
2692 //
2693 // (Last changed on 21/ 2/94.)
2694 //-----------------------------------------------------------------------
2695
2696 wmap.assign(nWires, std::complex<double>(0., 0.));
2697
2698 std::complex<double> wd = std::complex<double>(0., 0.);
2699
2700 InitializeCoefficientTables();
2701
2702 // Evaluate kappa, a constant needed by ConformalMap.
2703 kappa = tgamma((ntube + 1.) / ntube) * tgamma((ntube - 2.) / ntube) /
2704 tgamma((ntube - 1.) / ntube);
2705 // Loop over all wire combinations.
2706 for (int i = 0; i < nWires; ++i) {
2707 // Compute wire mappings only once.
2708 ConformalMap(std::complex<double>(w[i].x, w[i].y) / cotube, wmap[i], wd);
2709 // Diagonal elements.
2710 a[i][i] =
2711 -log(abs((0.5 * w[i].d / cotube) * wd / (1. - pow(abs(wmap[i]), 2))));
2712 // Loop over all other wires for the off-diagonal elements.
2713 for (int j = 0; j < i; ++j) {
2714 a[i][j] = -log(abs((wmap[i] - wmap[j]) / (1. - conj(wmap[i]) * wmap[j])));
2715 // Copy this to a[j][i] since the capacitance matrix is symmetric.
2716 a[j][i] = a[i][j];
2717 }
2718 }
2719 // Call CHARGE to calculate the charges really.
2720 return Charge();
2721}
2722
2723bool ComponentAnalyticField::Charge() {
2724
2725 //-----------------------------------------------------------------------
2726 // CHARGE - Routine actually inverting the capacitance matrix filled in
2727 // the SET... routines thereby providing the charges.
2728 // (Last changed on 30/ 1/93.)
2729 //-----------------------------------------------------------------------
2730
2731 // Transfer the voltages to rhs vector,
2732 // correcting for the equipotential planes.
2733 std::vector<double> b;
2734 b.resize(nWires);
2735 for (int i = 0; i < nWires; ++i) {
2736 b[i] = w[i].v - (corvta * w[i].x + corvtb * w[i].y + corvtc);
2737 }
2738
2739 bool ok = true;
2740 int ifail = 0;
2741
2742 // Force sum charges = 0 in case of absence of equipotential planes.
2743 if (!(ynplan[0] || ynplan[1] || ynplan[2] || ynplan[3] || tube)) {
2744 // Add extra elements to A, acting as constraints.
2745 b.push_back(0.);
2746 a.resize(nWires + 1);
2747 a[nWires].clear();
2748 for (int i = 0; i < nWires; ++i) {
2749 a[i].push_back(1.);
2750 a[nWires].push_back(1.);
2751 }
2752 a[nWires].push_back(0.);
2753 // Solve equations to yield charges, using KERNLIB (scalar).
2754 Numerics::Deqinv(nWires + 1, a, ifail, b);
2755 if (ifail != 0) {
2756 std::cerr << m_className << "::Charge:\n";
2757 std::cerr << " Matrix inversion failed.\n";
2758 return false;
2759 }
2760 // Modify A to give true inverse of capacitance matrix.
2761 if (a[nWires][nWires] != 0.) {
2762 const double t = 1. / a[nWires][nWires];
2763 for (int i = 0; i < nWires; ++i) {
2764 for (int j = 0; j < nWires; ++j) {
2765 a[i][j] -= t * a[i][nWires] * a[nWires][j];
2766 }
2767 }
2768 } else {
2769 std::cerr << m_className << "::Charge:\n";
2770 std::cerr << " True inverse of the capacitance matrix"
2771 << " could not be calculated.\n";
2772 std::cerr << " Use of the FACTOR instruction should be avoided.\n";
2773 ok = false;
2774 }
2775 // Store reference potential.
2776 v0 = b[nWires];
2777 } else {
2778 // Handle the case when the sum of the charges is zero automatically.
2779 Numerics::Deqinv(nWires, a, ifail, b);
2780 // Reference potential chosen to be zero.
2781 v0 = 0.;
2782 }
2783
2784 // Check the error condition flag.
2785 if (!ok) {
2786 std::cerr << m_className << "::Charge:\n";
2787 std::cerr << " Failure to solve the capacitance equations.\n";
2788 std::cerr << " No charges are available.\n";
2789 return false;
2790 }
2791
2792 // Copy the charges to E.
2793 for (int i = 0; i < nWires; ++i) w[i].e = b[i];
2794
2795 // If debugging is on, print the capacitance matrix.
2796 if (debug) {
2797 std::cout << m_className << "::Charge:\n";
2798 std::cout << " Dump of the capacitance matrix after inversion:\n";
2799 for (int i = 0; i < nWires; i += 10) {
2800 for (int j = 0; j < nWires; j += 10) {
2801 std::cout << " (Block " << i / 10 << ", " << j / 10 << ")\n";
2802 for (int ii = 0; ii < 10; ++ii) {
2803 if (i + ii >= nWires) break;
2804 for (int jj = 0; jj < 10; ++jj) {
2805 if (j + jj >= nWires) break;
2806 std::cout << std::setw(6) << a[i + ii][j + jj] << " ";
2807 }
2808 std::cout << "\n";
2809 }
2810 std::cout << "\n";
2811 }
2812 }
2813 std::cout << m_className << "::Charge:\n";
2814 std::cout << " End of the inverted capacitance matrix.\n";
2815 }
2816
2817 // And also check the quality of the matrix inversion.
2818 if (chargeCheck) {
2819 std::cout << m_className << "::Charge:\n";
2820 std::cout << " Quality check of the charge calculation.\n";
2821 std::cout << " Wire E as obtained E reconstructed\n";
2822 for (int i = 0; i < nWires; ++i) {
2823 b[i] = 0.;
2824 for (int j = 0; j < nWires; ++j) {
2825 b[i] += a[i][j] *
2826 (w[j].v - v0 - (corvta * w[j].x + corvtb * w[j].y + corvtc));
2827 }
2828 std::cout << " " << i << " " << w[i].e << " " << b[i] << "\n";
2829 }
2830 }
2831 return true;
2832}
2833
2834double ComponentAnalyticField::Ph2(const double xpos, const double ypos) {
2835
2836 //-----------------------------------------------------------------------
2837 // PH2 - Logarithmic contribution to real single-wire potential,
2838 // for a doubly priodic wire array.
2839 // PH2LIM - Entry, PH2LIM(r) corresponds to z on the surface of a wire
2840 // of (small) radius r.
2841 //
2842 // Clenshaw's algorithm is used for the evaluation of the sum
2843 // ZTERM = SIN(zeta) - P1*SIN(3*zeta) + P2*SIN(5*zeta).
2844 //
2845 // (G.A.Erskine/DD, 14.8.1984; some minor modifications (i) common block
2846 // /EV2COM/ incorporated in /CELDAT/ (ii) large imag(zeta) corrected)
2847 //-----------------------------------------------------------------------
2848
2849 // Start of the main subroutine, off diagonal elements.
2850 std::complex<double> zeta = zmult * std::complex<double>(xpos, ypos);
2851 if (fabs(imag(zeta)) < 10.) {
2852 std::complex<double> zsin = sin(zeta);
2853 std::complex<double> zcof = 4. * zsin * zsin - 2.;
2854 std::complex<double> zu = -p1 - zcof * p2;
2855 std::complex<double> zunew = 1. - zcof * zu - p2;
2856 std::complex<double> zterm = (zunew + zu) * zsin;
2857 return -log(abs(zterm));
2858 }
2859
2860 return -fabs(imag(zeta)) + CLog2;
2861}
2862
2863void ComponentAnalyticField::ConformalMap(std::complex<double> z,
2864 std::complex<double>& ww,
2865 std::complex<double>& wd) {
2866
2867 //-----------------------------------------------------------------------
2868 // EFCMAP - Maps a the interior part of a regular in the unit circle.
2869 // Variables: Z - point to be mapped
2870 // W - the image of Z
2871 // WD - derivative of the mapping at Z
2872 // CC1 - coefficients for expansion around centre
2873 // CC2 - coefficients for expansion around corner
2874 // (Last changed on 19/ 2/94.)
2875 //-----------------------------------------------------------------------
2876
2877 const int nterm = 15;
2878
2879 if (z == 0.) {
2880 // Z coincides with the centre.
2881 // Results are trivial.
2882 ww = 0;
2883 wd = kappa;
2884 } else if (abs(z) < 0.75) {
2885 // Z is close to the centre.
2886 // Series expansion.
2887 std::complex<double> zterm = pow(kappa * z, ntube);
2888 std::complex<double> wdsum = 0.;
2889 std::complex<double> wsum = cc1[ntube - 3][nterm];
2890 for (int i = nterm; i--;) {
2891 wdsum = wsum + zterm * wdsum;
2892 wsum = cc1[ntube - 3][i] + zterm * wsum;
2893 }
2894 // Return the results.
2895 ww = kappa * z * wsum;
2896 wd = kappa * (wsum + double(ntube) * zterm * wdsum);
2897 } else {
2898 // Z is close to the edge.
2899 // First rotate Z nearest to 1.
2900 double arot =
2901 -TwoPi * int(round(atan2(imag(z), real(z)) * ntube / TwoPi)) / ntube;
2902 std::complex<double> zz = z * std::complex<double>(cos(arot), sin(arot));
2903 // Expand in a series.
2904 std::complex<double> zterm = pow(kappa * (1. - zz), ntube / (ntube - 2.));
2905 std::complex<double> wdsum = 0.;
2906 std::complex<double> wsum = cc2[ntube - 3][nterm];
2907 for (int i = nterm; i--;) {
2908 wdsum = wsum + zterm * wdsum;
2909 wsum = cc2[ntube - 3][i] + zterm * wsum;
2910 }
2911 // And return the results.
2912 ww = std::complex<double>(cos(arot), -sin(arot)) * (1. - zterm * wsum);
2913 wd = ntube * kappa * pow(kappa * (1. - zz), 2. / (ntube - 2.)) *
2914 (wsum + zterm * wdsum) / (ntube - 2.);
2915 }
2916}
2917
2918void ComponentAnalyticField::E2Sum(const double xpos, const double ypos,
2919 double& ex, double& ey) {
2920
2921 //-----------------------------------------------------------------------
2922 // E2SUM - Components of the elecrostatic field intensity in a doubly
2923 // periodic wire array.
2924 // Clenshaw's algorithm is used for the evaluation of the sums
2925 // ZTERM1 = SIN(ZETA) - P1*SIN(3*ZETA) + P2*SIN(5*ZETA),
2926 // ZTERM2 = COS(ZETA)- 3 P1*COS(3*ZETA)+ 5P2*COS(5*ZETA)
2927 // VARIABLES : (XPOS,YPOS): Position in the basic cell at which the
2928 // field is to be computed.
2929 // (Essentially by G.A.Erskine/DD, 14.8.1984)
2930 //-----------------------------------------------------------------------
2931
2932 const std::complex<double> icons = std::complex<double>(0., 1.);
2933
2934 std::complex<double> wsum = 0.;
2935 std::complex<double> zsin, zcof, zu, zunew;
2936 std::complex<double> zterm1, zterm2, zeta;
2937
2938 for (int j = 0; j < nWires; ++j) {
2939 zeta = zmult * std::complex<double>(xpos - w[j].x, ypos - w[j].y);
2940 if (imag(zeta) > 15.) {
2941 wsum -= w[j].e * icons;
2942 } else if (imag(zeta) < -15.) {
2943 wsum += w[j].e * icons;
2944 } else {
2945 zsin = sin(zeta);
2946 zcof = 4. * zsin * zsin - 2.;
2947 zu = -p1 - zcof * p2;
2948 zunew = 1. - zcof * zu - p2;
2949 zterm1 = (zunew + zu) * zsin;
2950 zu = -3. * p1 - zcof * 5. * p2;
2951 zunew = 1. - zcof * zu - 5. * p2;
2952 zterm2 = (zunew - zu) * cos(zeta);
2953 wsum += w[j].e * (zterm2 / zterm1);
2954 }
2955 }
2956 ex = -real(-zmult * wsum);
2957 ey = imag(-zmult * wsum);
2958}
2959
2960void ComponentAnalyticField::FieldA00(const double xpos, const double ypos,
2961 double& ex, double& ey, double& volt,
2962 const bool opt) {
2963
2964 //-----------------------------------------------------------------------
2965 // EFCA00 - Subroutine performing the actual field calculations in case
2966 // only one charge and not more than 1 mirror-charge in either
2967 // x or y is present.
2968 // The potential used is 1/2*pi*eps0 log(r).
2969 // VARIABLES : R2 : Potential before taking -log(sqrt(...))
2970 // EX, EY : x,y-component of the electric field.
2971 // ETOT : Magnitude of electric field.
2972 // VOLT : Potential.
2973 // EXHELP etc : One term in the series to be summed.
2974 // (XPOS,YPOS): The position where the field is calculated.
2975 // (Last changed on 25/ 1/96.)
2976 //-----------------------------------------------------------------------
2977
2978 // Initialise the electric field and potential.
2979 ex = ey = 0.;
2980 volt = v0;
2981
2982 double xxmirr = 0., yymirr = 0.;
2983 // Loop over all wires.
2984 for (int i = nWires; i--;) {
2985 const double xx = xpos - w[i].x;
2986 const double yy = ypos - w[i].y;
2987 double r2 = xx * xx + yy * yy;
2988 // Calculate the field in case there are no planes.
2989 double exhelp = xx / r2;
2990 double eyhelp = yy / r2;
2991 // Take care of a plane at constant x.
2992 if (ynplax) {
2993 xxmirr = w[i].x + (xpos - 2. * coplax);
2994 const double r2plan = xxmirr * xxmirr + yy * yy;
2995 exhelp -= xxmirr / r2plan;
2996 eyhelp -= yy / r2plan;
2997 r2 /= r2plan;
2998 }
2999 // Take care of a plane at constant y.
3000 if (ynplay) {
3001 yymirr = w[i].y + (ypos - 2. * coplay);
3002 const double r2plan = xx * xx + yymirr * yymirr;
3003 exhelp -= xx / r2plan;
3004 eyhelp -= yymirr / r2plan;
3005 r2 /= r2plan;
3006 }
3007 // Take care of pairs of planes.
3008 if (ynplax && ynplay) {
3009 const double r2plan = xxmirr * xxmirr + yymirr * yymirr;
3010 exhelp += xxmirr / r2plan;
3011 eyhelp += yymirr / r2plan;
3012 r2 *= r2plan;
3013 }
3014 // Calculate the electric field and potential.
3015 if (opt) volt -= 0.5 * w[i].e * log(r2);
3016 ex += w[i].e * exhelp;
3017 ey += w[i].e * eyhelp;
3018 }
3019}
3020
3021void ComponentAnalyticField::FieldB1X(const double xpos, const double ypos,
3022 double& ex, double& ey, double& volt,
3023 const bool opt) {
3024
3025 //-----------------------------------------------------------------------
3026 // EFCB1X - Routine calculating the potential for a row of positive
3027 // charges. The potential used is Re(Log(sin pi/s (z-z0))).
3028 // VARIABLES : See routine EFCA00 for most of the variables.
3029 // Z,ZZMIRR : X + I*Y , XXMIRR + I*YYMIRR ; I**2=-1
3030 // ECOMPL : EX + I*EY ; I**2=-1
3031 //-----------------------------------------------------------------------
3032
3033 const std::complex<double> icons = std::complex<double>(0., 1.);
3034
3035 std::complex<double> zz, ecompl, zzmirr;
3036
3037 double r2 = 0.;
3038
3039 // Initialise the electric field and potential.
3040 ex = ey = 0.;
3041 volt = v0;
3042
3043 // Loop over all wires.
3044 for (int i = nWires; i--;) {
3045 const double xx = (Pi / sx) * (xpos - w[i].x);
3046 const double yy = (Pi / sx) * (ypos - w[i].y);
3047 zz = std::complex<double>(xx, yy);
3048 // Calculate the field in case there are no equipotential planes.
3049 if (yy > 20.) ecompl = -icons;
3050 if (fabs(yy) <= 20.)
3051 ecompl =
3052 icons * (exp(2. * icons * zz) + 1.) / (exp(2. * icons * zz) - 1.);
3053 if (yy < -20.) ecompl = icons;
3054 if (opt) {
3055 if (fabs(yy) > 20.) r2 = -fabs(yy) + CLog2;
3056 if (fabs(yy) <= 20.) r2 = -0.5 * log(pow(sinh(yy), 2) + pow(sin(xx), 2));
3057 }
3058 // Take care of a plane at constant y.
3059 if (ynplay) {
3060 const double yymirr = (Pi / sx) * (ypos + w[i].y - 2. * coplay);
3061 zzmirr = std::complex<double>(xx, yymirr);
3062 if (yymirr > 20.) ecompl += icons;
3063 if (fabs(yymirr) <= 20.)
3064 ecompl += -icons * (exp(2. * icons * zzmirr) + 1.) /
3065 (exp(2. * icons * zzmirr) - 1.);
3066 if (yymirr < -20.) ecompl += -icons;
3067
3068 if (opt && fabs(yymirr) > 20.) r2 += fabs(yymirr) - CLog2;
3069 if (opt && fabs(yymirr) <= 20.)
3070 r2 += 0.5 * log(pow(sinh(yymirr), 2) + pow(sin(xx), 2));
3071 }
3072 // Calculate the electric field and potential.
3073 ex += w[i].e * real(ecompl);
3074 ey -= w[i].e * imag(ecompl);
3075 if (opt) volt += w[i].e * r2;
3076 }
3077 ex *= Pi / sx;
3078 ey *= Pi / sx;
3079}
3080
3081void ComponentAnalyticField::FieldB1Y(const double xpos, const double ypos,
3082 double& ex, double& ey, double& volt,
3083 const bool opt) {
3084
3085 //-----------------------------------------------------------------------
3086 // EFCB1Y - Routine calculating the potential for a row of positive
3087 // charges. The potential used is Re(Log(sinh pi/sy(z-z0)).
3088 // VARIABLES : See routine EFCA00 for most of the variables.
3089 // Z,ZZMIRR : X + I*Y , XXMIRR + I*YYMIRR ; I**2=-1
3090 // ECOMPL : EX + I*EY ; I**2=-1
3091 //-----------------------------------------------------------------------
3092
3093 std::complex<double> zz, ecompl, zzmirr;
3094
3095 double r2 = 0.;
3096
3097 // Initialise the electric field and potential.
3098 ex = ey = 0.;
3099 volt = v0;
3100
3101 // Loop over all wires.
3102 for (int i = nWires; i--;) {
3103 const double xx = (Pi / sy) * (xpos - w[i].x);
3104 const double yy = (Pi / sy) * (ypos - w[i].y);
3105 zz = std::complex<double>(xx, yy);
3106 // Calculate the field in case there are no equipotential planes.
3107 if (xx > 20.) ecompl = 1.;
3108 if (fabs(xx) <= 20.) ecompl = (exp(2. * zz) + 1.) / (exp(2. * zz) - 1.);
3109 if (xx < -20.) ecompl = -1.;
3110 if (opt) {
3111 if (fabs(xx) > 20.) r2 = -fabs(xx) + CLog2;
3112 if (fabs(xx) <= 20.) r2 = -0.5 * log(pow(sinh(xx), 2) + pow(sin(yy), 2));
3113 }
3114 // Take care of a plane at constant x.
3115 if (ynplax) {
3116 const double xxmirr = (Pi / sy) * (xpos + w[i].x - 2. * coplax);
3117 zzmirr = std::complex<double>(xxmirr, yy);
3118 if (xxmirr > 20.) ecompl -= 1.;
3119 if (xxmirr < -20.) ecompl += 1.;
3120 if (fabs(xxmirr) <= 20.)
3121 ecompl -= (exp(2. * zzmirr) + 1.) / (exp(2. * zzmirr) - 1.);
3122 if (opt && fabs(xxmirr) > 20.) r2 += fabs(xxmirr) - CLog2;
3123 if (opt && fabs(xxmirr) <= 20.)
3124 r2 += 0.5 * log(pow(sinh(xxmirr), 2) + pow(sin(yy), 2));
3125 }
3126 // Calculate the electric field and potential.
3127 ex += w[i].e * real(ecompl);
3128 ey -= w[i].e * imag(ecompl);
3129 if (opt) volt += w[i].e * r2;
3130 }
3131 ex *= Pi / sy;
3132 ey *= Pi / sy;
3133}
3134
3135void ComponentAnalyticField::FieldB2X(const double xpos, const double ypos,
3136 double& ex, double& ey, double& volt,
3137 const bool opt) {
3138
3139 //-----------------------------------------------------------------------
3140 // EFCB2X - Routine calculating the potential for a row of alternating
3141 // + - charges. The potential used is re log(sin pi/sx (z-z0))
3142 // VARIABLES : See routine EFCA00 for most of the variables.
3143 // Z, ZZMRR : X + i*Y , XXMIRR + i*YYMIRR ; i**2=-1
3144 // ECOMPL : EX + i*EY ; i**2=-1
3145 // (Cray vectorisable)
3146 //-----------------------------------------------------------------------
3147
3148 std::complex<double> zz, ecompl, zzmirr, zzneg, zznmirr;
3149
3150 // Initialise the electric field and potential.
3151 ex = ey = 0.;
3152 volt = v0;
3153
3154 // Loop over all wires.
3155 for (int i = nWires; i--;) {
3156 const double xx = HalfPi * (xpos - w[i].x) / sx;
3157 const double yy = HalfPi * (ypos - w[i].y) / sx;
3158 const double xxneg = HalfPi * (xpos - w[i].x - 2 * coplax) / sx;
3159 zz = std::complex<double>(xx, yy);
3160 zzneg = std::complex<double>(xxneg, yy);
3161 // Calculate the field in case there are no equipotential planes.
3162 ecompl = 0.;
3163 double r2 = 1.;
3164 if (fabs(yy) <= 20.) {
3165 ecompl = -b2sin[i] / (sin(zz) * sin(zzneg));
3166 if (opt) {
3167 r2 = (pow(sinh(yy), 2) + pow(sin(xx), 2)) /
3168 (pow(sinh(yy), 2) + pow(sin(xxneg), 2));
3169 }
3170 }
3171 // Take care of a planes at constant y.
3172 if (ynplay) {
3173 const double yymirr = HalfPi * (ypos + w[i].y - 2 * coplay) / sx;
3174 zzmirr = std::complex<double>(xx, yymirr);
3175 zznmirr = std::complex<double>(xxneg, yymirr);
3176 if (fabs(yymirr) <= 20.) {
3177 ecompl += b2sin[i] / (sin(zzmirr) * sin(zznmirr));
3178 if (opt) {
3179 const double r2plan = (pow(sinh(yymirr), 2) + pow(sin(xx), 2)) /
3180 (pow(sinh(yymirr), 2) + pow(sin(xxneg), 2));
3181 r2 /= r2plan;
3182 }
3183 }
3184 }
3185 // Calculate the electric field and potential.
3186 ex += w[i].e * real(ecompl);
3187 ey -= w[i].e * imag(ecompl);
3188 if (opt) volt -= 0.5 * w[i].e * log(r2);
3189 }
3190 ex *= (HalfPi / sx);
3191 ey *= (HalfPi / sx);
3192}
3193
3194void ComponentAnalyticField::FieldB2Y(const double xpos, const double ypos,
3195 double& ex, double& ey, double& volt,
3196 const bool opt) {
3197
3198 //-----------------------------------------------------------------------
3199 // EFCB2Y - Routine calculating the potential for a row of alternating
3200 // + - charges. The potential used is re log(sin pi/sx (z-z0))
3201 // VARIABLES : See routine EFCA00 for most of the variables.
3202 // Z, ZMIRR : X + i*Y , XXMIRR + i*YYMIRR ; i**2=-1
3203 // ECOMPL : EX + i*EY ; i**2=-1
3204 // (Cray vectorisable)
3205 //-----------------------------------------------------------------------
3206
3207 const std::complex<double> icons(0., 1.);
3208
3209 std::complex<double> zz, zzneg, zzmirr, zznmirr, ecompl;
3210
3211 // Initialise the electric field and potential.
3212 ex = ey = 0.;
3213 volt = v0;
3214
3215 // Loop over all wires.
3216 for (int i = nWires; i--;) {
3217 const double xx = HalfPi * (xpos - w[i].x) / sy;
3218 const double yy = HalfPi * (ypos - w[i].y) / sy;
3219 const double yyneg = HalfPi * (ypos + w[i].y - 2. * coplay) / sy;
3220 zz = std::complex<double>(xx, yy);
3221 zzneg = std::complex<double>(xx, yyneg);
3222 // Calculate the field in case there are no equipotential planes.
3223 ecompl = 0.;
3224 double r2 = 1.;
3225 if (fabs(xx) <= 20.) {
3226 ecompl = icons * b2sin[i] / (sin(icons * zz) * sin(icons * zzneg));
3227 if (opt) {
3228 r2 = (pow(sinh(xx), 2) + pow(sin(yy), 2)) /
3229 (pow(sinh(xx), 2) + pow(sin(yyneg), 2));
3230 }
3231 }
3232 // Take care of a plane at constant x.
3233 if (ynplax) {
3234 const double xxmirr = HalfPi * (xpos + w[i].x - 2. * coplax) / sy;
3235 zzmirr = std::complex<double>(xxmirr, yy);
3236 zznmirr = std::complex<double>(xxmirr, yyneg);
3237 if (fabs(xxmirr) <= 20.) {
3238 ecompl -=
3239 icons * b2sin[i] / (sin(icons * zzmirr) * sin(icons * zznmirr));
3240 if (opt) {
3241 const double r2plan = (pow(sinh(xxmirr), 2) + pow(sin(yy), 2)) /
3242 (pow(sinh(xxmirr), 2) + pow(sin(yyneg), 2));
3243 r2 /= r2plan;
3244 }
3245 }
3246 }
3247 // Calculate the electric field and potential.
3248 ex += w[i].e * real(ecompl);
3249 ey -= w[i].e * imag(ecompl);
3250 if (opt) volt -= 0.5 * w[i].e * log(r2);
3251 }
3252 ex *= (HalfPi / sy);
3253 ey *= (HalfPi / sy);
3254}
3255
3256void ComponentAnalyticField::FieldC10(const double xpos, const double ypos,
3257 double& ex, double& ey, double& volt,
3258 const bool opt) {
3259
3260 //-----------------------------------------------------------------------
3261 // EFCC10 - Routine returning the potential and electric field. It
3262 // calls the routines PH2 and E2SUM written by G.A.Erskine.
3263 // VARIABLES : No local variables.
3264 //-----------------------------------------------------------------------
3265
3266 // Calculate voltage first, if needed.
3267 if (opt) {
3268 if (mode == 0) volt = v0 + c1 * xpos;
3269 if (mode == 1) volt = v0 + c1 * ypos;
3270 for (int i = 0; i < nWires; ++i) {
3271 volt += w[i].e * Ph2(xpos - w[i].x, ypos - w[i].y);
3272 }
3273 }
3274
3275 // And finally the electric field.
3276 E2Sum(xpos, ypos, ex, ey);
3277 if (mode == 0) ex -= c1;
3278 if (mode == 1) ey -= c1;
3279}
3280
3281void ComponentAnalyticField::FieldC2X(const double xpos, const double ypos,
3282 double& ex, double& ey, double& volt,
3283 const bool opt) {
3284
3285 //-----------------------------------------------------------------------
3286 // EFCC2X - Routine returning the potential and electric field in a
3287 // configuration with 2 x planes and y periodicity.
3288 // VARIABLES : see the writeup
3289 //-----------------------------------------------------------------------
3290
3291 const std::complex<double> icons(0., 1.);
3292
3293 std::complex<double> zsin, zcof, zu, zunew;
3294 std::complex<double> zterm1, zterm2, zeta;
3295
3296 // Initial values.
3297 std::complex<double> wsum1 = 0.;
3298 std::complex<double> wsum2 = 0.;
3299 volt = 0.;
3300
3301 // Wire loop.
3302 for (int i = nWires; i--;) {
3303 // Compute the direct contribution.
3304 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
3305 if (imag(zeta) > 15.) {
3306 wsum1 -= w[i].e * icons;
3307 if (opt) volt -= w[i].e * (fabs(imag(zeta)) - CLog2);
3308 } else if (imag(zeta) < -15.) {
3309 wsum1 += w[i].e * icons;
3310 if (opt) volt -= w[i].e * (fabs(imag(zeta)) - CLog2);
3311 } else {
3312 zsin = sin(zeta);
3313 zcof = 4. * zsin * zsin - 2.;
3314 zu = -p1 - zcof * p2;
3315 zunew = 1. - zcof * zu - p2;
3316 zterm1 = (zunew + zu) * zsin;
3317 zu = -3. * p1 - zcof * 5. * p2;
3318 zunew = 1. - zcof * zu - 5. * p2;
3319 zterm2 = (zunew - zu) * cos(zeta);
3320 wsum1 += w[i].e * (zterm2 / zterm1);
3321 if (opt) volt -= w[i].e * log(abs(zterm1));
3322 }
3323 // Find the plane nearest to the wire.
3324 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
3325 // Mirror contribution.
3326 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x, ypos - w[i].y);
3327 if (imag(zeta) > 15.) {
3328 wsum2 -= w[i].e * icons;
3329 if (opt) volt += w[i].e * (fabs(imag(zeta)) - CLog2);
3330 } else if (imag(zeta) < -15.) {
3331 wsum2 += w[i].e * icons;
3332 if (opt) volt += w[i].e * (fabs(imag(zeta)) - CLog2);
3333 } else {
3334 zsin = sin(zeta);
3335 zcof = 4. * zsin * zsin - 2.;
3336 zu = -p1 - zcof * p2;
3337 zunew = 1. - zcof * zu - p2;
3338 zterm1 = (zunew + zu) * zsin;
3339 zu = -3. * p1 - zcof * 5. * p2;
3340 zunew = 1. - zcof * zu - 5. * p2;
3341 zterm2 = (zunew - zu) * cos(zeta);
3342 wsum2 += w[i].e * (zterm2 / zterm1);
3343 if (opt) volt += w[i].e * log(abs(zterm1));
3344 }
3345 // Correct the voltage, if needed (MODE).
3346 if (opt && mode == 0) {
3347 volt -= TwoPi * w[i].e * (xpos - cx) * (w[i].x - cx) / (sx * sy);
3348 }
3349 }
3350 // Convert the two contributions to a real field.
3351 ex = real(zmult * (wsum1 + wsum2));
3352 ey = -imag(zmult * (wsum1 - wsum2));
3353 // Constant correction terms.
3354 if (mode == 0) ex -= c1;
3355}
3356
3357void ComponentAnalyticField::FieldC2Y(const double xpos, const double ypos,
3358 double& ex, double& ey, double& volt,
3359 const bool opt) {
3360
3361 //-----------------------------------------------------------------------
3362 // EFCC2Y - Routine returning the potential and electric field in a
3363 // configuration with 2 y planes and x periodicity.
3364 // VARIABLES : see the writeup
3365 //-----------------------------------------------------------------------
3366
3367 const std::complex<double> icons(0., 1.);
3368
3369 std::complex<double> zsin, zcof, zu, zunew;
3370 std::complex<double> zterm1, zterm2, zeta;
3371
3372 // Initial values.
3373 volt = 0.;
3374 std::complex<double> wsum1 = 0.;
3375 std::complex<double> wsum2 = 0.;
3376
3377 // Wire loop.
3378 for (int i = nWires; i--;) {
3379 // Compute the direct contribution.
3380 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
3381 if (imag(zeta) > 15.) {
3382 wsum1 -= w[i].e * icons;
3383 if (opt) volt -= w[i].e * (fabs(imag(zeta)) - CLog2);
3384 } else if (imag(zeta) < -15.) {
3385 wsum1 += w[i].e * icons;
3386 if (opt) volt -= w[i].e * (fabs(imag(zeta)) - CLog2);
3387 } else {
3388 zsin = sin(zeta);
3389 zcof = 4. * zsin * zsin - 2.;
3390 zu = -p1 - zcof * p2;
3391 zunew = 1. - zcof * zu - p2;
3392 zterm1 = (zunew + zu) * zsin;
3393 zu = -3. * p1 - zcof * 5. * p2;
3394 zunew = 1. - zcof * zu - 5. * p2;
3395 zterm2 = (zunew - zu) * cos(zeta);
3396 wsum1 += w[i].e * (zterm2 / zterm1);
3397 if (opt) volt -= w[i].e * log(abs(zterm1));
3398 }
3399 // Find the plane nearest to the wire.
3400 const double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
3401 // Mirror contribution from the y plane.
3402 zeta = zmult * std::complex<double>(xpos - w[i].x, 2 * cy - ypos - w[i].y);
3403 if (imag(zeta) > 15.) {
3404 wsum2 -= w[i].e * icons;
3405 if (opt) volt += w[i].e * (fabs(imag(zeta)) - CLog2);
3406 } else if (imag(zeta) < -15.) {
3407 wsum2 += w[i].e * icons;
3408 if (opt) volt += w[i].e * (fabs(imag(zeta)) - CLog2);
3409 } else {
3410 zsin = sin(zeta);
3411 zcof = 4. * zsin * zsin - 2.;
3412 zu = -p1 - zcof * p2;
3413 zunew = 1. - zcof * zu - p2;
3414 zterm1 = (zunew + zu) * zsin;
3415 zu = -3. * p1 - zcof * 5. * p2;
3416 zunew = 1. - zcof * zu - 5. * p2;
3417 zterm2 = (zunew - zu) * cos(zeta);
3418 wsum2 += w[i].e * (zterm2 / zterm1);
3419 if (opt) volt += w[i].e * log(abs(zterm1));
3420 }
3421 // Correct the voltage, if needed (MODE).
3422 if (opt && mode == 1) {
3423 volt -= TwoPi * w[i].e * (ypos - cy) * (w[i].y - cy) / (sx * sy);
3424 }
3425 }
3426 // Convert the two contributions to a real field.
3427 ex = real(zmult * (wsum1 - wsum2));
3428 ey = -imag(zmult * (wsum1 + wsum2));
3429 // Constant correction terms.
3430 if (mode == 1) ey -= c1;
3431}
3432
3433void ComponentAnalyticField::FieldC30(const double xpos, const double ypos,
3434 double& ex, double& ey, double& volt,
3435 const bool opt) {
3436
3437 //-----------------------------------------------------------------------
3438 // EFCC30 - Routine returning the potential and electric field in a
3439 // configuration with 2 y and 2 x planes.
3440 // VARIABLES : see the writeup
3441 //-----------------------------------------------------------------------
3442
3443 const std::complex<double> icons(0., 1.);
3444
3445 std::complex<double> zsin, zcof, zu, zunew;
3446 std::complex<double> zterm1, zterm2, zeta;
3447
3448 // Initial values.
3449 std::complex<double> wsum1 = 0.;
3450 std::complex<double> wsum2 = 0.;
3451 std::complex<double> wsum3 = 0.;
3452 std::complex<double> wsum4 = 0.;
3453 volt = 0.;
3454
3455 // Wire loop.
3456 for (int i = 0; i < nWires; ++i) {
3457 // Compute the direct contribution.
3458 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
3459 if (imag(zeta) > 15.) {
3460 wsum1 -= w[i].e * icons;
3461 if (opt) volt -= w[i].e * (fabs(imag(zeta)) - CLog2);
3462 } else if (imag(zeta) < -15.) {
3463 wsum1 += w[i].e * icons;
3464 if (opt) volt -= w[i].e * (fabs(imag(zeta)) - CLog2);
3465 } else {
3466 zsin = sin(zeta);
3467 zcof = 4. * zsin * zsin - 2.;
3468 zu = -p1 - zcof * p2;
3469 zunew = 1. - zcof * zu - p2;
3470 zterm1 = (zunew + zu) * zsin;
3471 zu = -3. * p1 - zcof * 5. * p2;
3472 zunew = 1. - zcof * zu - 5. * p2;
3473 zterm2 = (zunew - zu) * cos(zeta);
3474 wsum1 += w[i].e * (zterm2 / zterm1);
3475 if (opt) volt -= w[i].e * log(abs(zterm1));
3476 }
3477 // Find the plane nearest to the wire.
3478 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
3479 // Mirror contribution from the x plane.
3480 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x, ypos - w[i].y);
3481 if (imag(zeta) > 15.) {
3482 wsum2 -= w[i].e * icons;
3483 if (opt) volt += w[i].e * (fabs(imag(zeta)) - CLog2);
3484 } else if (imag(zeta) < -15.) {
3485 wsum2 += w[i].e * icons;
3486 if (opt) volt += w[i].e * (fabs(imag(zeta)) - CLog2);
3487 } else {
3488 zsin = sin(zeta);
3489 zcof = 4. * zsin * zsin - 2.;
3490 zu = -p1 - zcof * p2;
3491 zunew = 1. - zcof * zu - p2;
3492 zterm1 = (zunew + zu) * zsin;
3493 zu = -3. * p1 - zcof * 5. * p2;
3494 zunew = 1. - zcof * zu - 5. * p2;
3495 zterm2 = (zunew - zu) * cos(zeta);
3496 wsum2 += w[i].e * (zterm2 / zterm1);
3497 if (opt) volt += w[i].e * log(abs(zterm1));
3498 }
3499 // Find the plane nearest to the wire.
3500 double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
3501 // Mirror contribution from the x plane.
3502 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x, ypos - w[i].y);
3503 if (imag(zeta) > 15.) {
3504 wsum3 -= w[i].e * icons;
3505 if (opt) volt += w[i].e * (fabs(imag(zeta)) - CLog2);
3506 } else if (imag(zeta) < -15.) {
3507 wsum3 += w[i].e * icons;
3508 if (opt) volt += w[i].e * (fabs(imag(zeta)) - CLog2);
3509 } else {
3510 zsin = sin(zeta);
3511 zcof = 4. * zsin * zsin - 2.;
3512 zu = -p1 - zcof * p2;
3513 zunew = 1. - zcof * zu - p2;
3514 zterm1 = (zunew + zu) * zsin;
3515 zu = -3. * p1 - zcof * 5. * p2;
3516 zunew = 1. - zcof * zu - 5. * p2;
3517 zterm2 = (zunew - zu) * cos(zeta);
3518 wsum3 += w[i].e * (zterm2 / zterm1);
3519 if (opt) volt += w[i].e * log(abs(zterm1));
3520 }
3521 // Mirror contribution from both the x and the y plane.
3522 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x,
3523 2. * cy - ypos - w[i].y);
3524 if (imag(zeta) > 15.) {
3525 wsum4 -= w[i].e * icons;
3526 if (opt) volt -= w[i].e * (fabs(imag(zeta)) - CLog2);
3527 } else if (imag(zeta) < -15.) {
3528 wsum4 += w[i].e * icons;
3529 if (opt) volt -= w[i].e * (fabs(imag(zeta)) - CLog2);
3530 } else {
3531 zsin = sin(zeta);
3532 zcof = 4. * zsin * zsin - 2.;
3533 zu = -p1 - zcof * p2;
3534 zunew = 1. - zcof * zu - p2;
3535 zterm1 = (zunew + zu) * zsin;
3536 zu = -3. * p1 - zcof * 5. * p2;
3537 zunew = 1. - zcof * zu - 5. * p2;
3538 zterm2 = (zunew - zu) * cos(zeta);
3539 wsum4 += w[i].e * (zterm2 / zterm1);
3540 if (opt) volt -= w[i].e * log(abs(zterm1));
3541 }
3542 }
3543 // Convert the two contributions to a real field.
3544 ex = real(zmult * (wsum1 + wsum2 - wsum3 - wsum4));
3545 ey = -imag(zmult * (wsum1 - wsum2 + wsum3 - wsum4));
3546}
3547
3548void ComponentAnalyticField::FieldD10(const double xpos, const double ypos,
3549 double& ex, double& ey, double& volt,
3550 const bool opt) {
3551
3552 //-----------------------------------------------------------------------
3553 // EFCD10 - Subroutine performing the actual field calculations for a
3554 // cell which has a one circular plane and some wires.
3555 // VARIABLES : EX, EY, VOLT:Electric field and potential.
3556 // ETOT, VOLT : Magnitude of electric field, potential.
3557 // (XPOS,YPOS): The position where the field is calculated.
3558 // ZI, ZPOS : Shorthand complex notations.
3559 // (Last changed on 4/ 9/95.)
3560 //-----------------------------------------------------------------------
3561
3562 // Initialise the electric field and potential.
3563 ex = ey = 0.;
3564 volt = v0;
3565
3566 // Set the complex position coordinates.
3567 std::complex<double> zpos = std::complex<double>(xpos, ypos);
3568 std::complex<double> zi;
3569 std::complex<double> wi;
3570
3571 // Loop over all wires.
3572 for (int i = nWires; i--;) {
3573 // Set the complex version of the wire-coordinate for simplicity.
3574 zi = std::complex<double>(w[i].x, w[i].y);
3575 // Compute the contribution to the potential, if needed.
3576 if (opt)
3577 volt -=
3578 w[i].e *
3579 log(abs(cotube * (zpos - zi) / (cotube * cotube - zpos * conj(zi))));
3580 // Compute the contribution to the electric field, always.
3581 wi = 1. / conj(zpos - zi) + zi / (cotube * cotube - conj(zpos) * zi);
3582 ex += w[i].e * real(wi);
3583 ey += w[i].e * imag(wi);
3584 }
3585}
3586
3587void ComponentAnalyticField::FieldD20(const double xpos, const double ypos,
3588 double& ex, double& ey, double& volt,
3589 const bool opt) {
3590
3591 //-----------------------------------------------------------------------
3592 // EFCD20 - Subroutine performing the actual field calculations for a
3593 // cell which has a tube and phi periodicity.
3594 // VARIABLES : EX, EY, VOLT:Electric field and potential.
3595 // ETOT, VOLT : Magnitude of electric field, potential.
3596 // (XPOS,YPOS): The position where the field is calculated.
3597 // ZI, ZPOS : Shorthand complex notations.
3598 // (Last changed on 10/ 2/93.)
3599 //-----------------------------------------------------------------------
3600
3601 // Initialise the electric field and potential.
3602 ex = ey = 0.;
3603 volt = v0;
3604
3605 // Set the complex position coordinates.
3606 std::complex<double> zpos = std::complex<double>(xpos, ypos);
3607 std::complex<double> zi;
3608 std::complex<double> wi;
3609
3610 // Loop over all wires.
3611 for (int i = nWires; i--;) {
3612 // Set the complex version of the wire-coordinate for simplicity.
3613 zi = std::complex<double>(w[i].x, w[i].y);
3614 // Case of the wire which is not in the centre.
3615 if (abs(zi) > w[i].d / 2.) {
3616 // Compute the contribution to the potential, if needed.
3617 if (opt) {
3618 volt -=
3619 w[i].e *
3620 log(abs((1. / pow(cotube, mtube)) *
3621 (pow(zpos, mtube) - pow(zi, mtube)) /
3622 (1. - pow(zpos * conj(zi) / (cotube * cotube), mtube))));
3623 }
3624 // Compute the contribution to the electric field, always.
3625 wi = double(mtube) * pow(conj(zpos), mtube - 1) *
3626 (1. / conj(pow(zpos, mtube) - pow(zi, mtube)) +
3627 pow(zi, mtube) /
3628 (pow(cotube, 2 * mtube) - pow(conj(zpos) * zi, mtube)));
3629 ex += w[i].e * real(wi);
3630 ey += w[i].e * imag(wi);
3631 } else {
3632 // Case of the central wire.
3633 if (opt) {
3634 volt -= w[i].e * log(abs((1. / cotube) * (zpos - zi) /
3635 (1. - zpos * conj(zi) / (cotube * cotube))));
3636 }
3637 wi = 1. / conj(zpos - zi) + zi / (cotube * cotube - conj(zpos) * zi);
3638 // Compute the contribution to the electric field, always.
3639 ex += w[i].e * real(wi);
3640 ey += w[i].e * imag(wi);
3641 }
3642 }
3643}
3644
3645void ComponentAnalyticField::FieldD30(const double xpos, const double ypos,
3646 double& ex, double& ey, double& volt,
3647 const bool opt) {
3648
3649 //-----------------------------------------------------------------------
3650 // EFCD30 - Subroutine performing the actual field calculations for a
3651 // cell which has a polygon as tube and some wires.
3652 // VARIABLES : EX, EY, VOLT:Electric field and potential.
3653 // ETOT, VOLT : Magnitude of electric field, potential.
3654 // (XPOS,YPOS): The position where the field is calculated.
3655 // ZI, ZPOS : Shorthand complex notations.
3656 // (Last changed on 19/ 2/94.)
3657 //-----------------------------------------------------------------------
3658
3659 // Initialise the electric field and potential.
3660 ex = ey = 0.;
3661 volt = v0;
3662
3663 std::complex<double> whelp;
3664
3665 // Get the mapping of the position.
3666 std::complex<double> wpos, wdpos;
3667 ConformalMap(std::complex<double>(xpos, ypos) / cotube, wpos, wdpos);
3668 // Loop over all wires.
3669 for (int i = nWires; i--;) {
3670 // Compute the contribution to the potential, if needed.
3671 if (opt) {
3672 volt -= w[i].e * log(abs((wpos - wmap[i]) / (1. - wpos * conj(wmap[i]))));
3673 }
3674 whelp = wdpos * (1. - pow(abs(wmap[i]), 2)) /
3675 ((wpos - wmap[i]) * (1. - conj(wmap[i]) * wpos));
3676 // Compute the contribution to the electric field, always.
3677 ex += w[i].e * real(whelp);
3678 ey -= w[i].e * imag(whelp);
3679 }
3680 ex /= cotube;
3681 ey /= cotube;
3682}
3683
3684bool ComponentAnalyticField::InTube(const double x0, const double y0,
3685 const double a, const int n) {
3686
3687 //-----------------------------------------------------------------------
3688 // INTUBE - Determines whether a point is located inside a polygon.
3689 // ILOC is set to +1 if outside, 0 if inside and -1 if the
3690 // arguments are not valid.
3691 // (Last changed on 18/ 3/01.)
3692 //-----------------------------------------------------------------------
3693
3694 // Special case: x = y = 0
3695 if (x0 == 0. && y0 == 0.) return true;
3696
3697 // Special case: round tube.
3698 if (n == 0) {
3699 if (x0 * x0 + y0 * y0 > a * a) return false;
3700 return true;
3701 }
3702
3703 if (n < 0 || n == 1 || n == 2) {
3704 std::cerr << m_className << "::InTube:\n";
3705 std::cerr << " Invalid number of edges (n = " << n << ")\n";
3706 return false;
3707 }
3708
3709 // Truly polygonal tubes.
3710 // Reduce angle to the first sector.
3711 double phi = atan2(y0, x0);
3712 if (phi < 0.) phi += TwoPi;
3713 phi -= TwoPi * int(0.5 * n * phi / Pi) / n;
3714 // Compare the length to the local radius.
3715 if ((x0 * x0 + y0 * y0) * pow(cos(Pi / n - phi), 2) >
3716 a * a * pow(cos(Pi / n), 2))
3717 return false;
3718
3719 return true;
3720}
3721
3722void ComponentAnalyticField::InitializeCoefficientTables() {
3723
3724 const int nterms = 16;
3725
3726 // Tables of coefficients used by ConformalMap
3727 cc1.resize(6);
3728 cc2.resize(6);
3729 for (int i = 0; i < 6; ++i) {
3730 cc1[i].clear();
3731 cc1[i].resize(nterms);
3732 cc1[i].clear();
3733 cc2[i].resize(nterms);
3734 }
3735
3736 // Triangle: coefficients for centre and corner expansion.
3737 const double cc13[nterms] = {
3738 0.1000000000e+01, -.1666666865e+00, 0.3174602985e-01, -.5731921643e-02,
3739 0.1040112227e-02, -.1886279933e-03, 0.3421107249e-04, -.6204730198e-05,
3740 0.1125329618e-05, -.2040969207e-06, 0.3701631357e-07, -.6713513301e-08,
3741 0.1217605794e-08, -.2208327132e-09, 0.4005162868e-10, -.7264017512e-11};
3742 const double cc23[nterms] = {
3743 0.3333333135e+00, -.5555555597e-01, 0.1014109328e-01, -.1837154618e-02,
3744 0.3332451452e-03, -.6043842586e-04, 0.1096152027e-04, -.1988050826e-05,
3745 0.3605655365e-06, -.6539443120e-07, 0.1186035448e-07, -.2151069323e-08,
3746 0.3901317047e-09, -.7075676156e-10, 0.1283289534e-10, -.2327455936e-11};
3747
3748 // Square: coefficients for centre and corner expansion.
3749 const double cc14[nterms] = {
3750 0.1000000000e+01, -.1000000238e+00, 0.8333332837e-02, -.7051283028e-03,
3751 0.5967194738e-04, -.5049648280e-05, 0.4273189802e-06, -.3616123934e-07,
3752 0.3060091514e-08, -.2589557457e-09, 0.2191374859e-10, -.1854418528e-11,
3753 0.1569274224e-12, -.1327975205e-13, 0.1123779363e-14, -.9509817570e-16};
3754 const double cc24[nterms] = {
3755 0.1000000000e+01, -.5000000000e+00, 0.3000000119e+00, -.1750000119e+00,
3756 0.1016666889e+00, -.5916666612e-01, 0.3442307562e-01, -.2002724260e-01,
3757 0.1165192947e-01, -.6779119372e-02, 0.3944106400e-02, -.2294691978e-02,
3758 0.1335057430e-02, -.7767395582e-03, 0.4519091453e-03, -.2629216760e-03};
3759
3760 // Pentagon: coefficients for centre and corner expansion.
3761 const double cc15[nterms] = {
3762 0.1000000000e+01, -.6666666269e-01, 0.1212121220e-02, -.2626262140e-03,
3763 -.3322110570e-04, -.9413293810e-05, -.2570029210e-05, -.7695705904e-06,
3764 -.2422486887e-06, -.7945993730e-07, -.2691839640e-07, -.9361642128e-08,
3765 -.3327319087e-08, -.1204430555e-08, -.4428404310e-09, -.1650302672e-09};
3766 const double cc25[nterms] = {
3767 0.1248050690e+01, -.7788147926e+00, 0.6355384588e+00, -.4899077415e+00,
3768 0.3713272810e+00, -.2838423252e+00, 0.2174729109e+00, -.1663445234e+00,
3769 0.1271933913e+00, -.9728997946e-01, 0.7442557812e-01, -.5692918226e-01,
3770 0.4354400188e-01, -.3330700099e-01, 0.2547712997e-01, -.1948769018e-01};
3771
3772 // Hexagon: coefficients for centre and corner expansion.
3773 const double cc16[nterms] = {
3774 0.1000000000e+01, -.4761904851e-01, -.1221001148e-02, -.3753788769e-03,
3775 -.9415557724e-04, -.2862767724e-04, -.9587882232e-05, -.3441659828e-05,
3776 -.1299798896e-05, -.5103651119e-06, -.2066504408e-06, -.8578405186e-07,
3777 -.3635090096e-07, -.1567239494e-07, -.6857355572e-08, -.3038770346e-08};
3778 const double cc26[nterms] = {
3779 0.1333333015e+01, -.8888888955e+00, 0.8395061493e+00, -.7242798209e+00,
3780 0.6016069055e+00, -.5107235312e+00, 0.4393203855e+00, -.3745460510e+00,
3781 0.3175755739e+00, -.2703750730e+00, 0.2308617830e+00, -.1966916919e+00,
3782 0.1672732830e+00, -.1424439549e+00, 0.1214511395e+00, -.1034612656e+00};
3783
3784 // Heptagon: coefficients for centre and corner expansion.
3785 const double cc17[nterms] = {
3786 0.1000000000e+01, -.3571428731e-01, -.2040816238e-02, -.4936389159e-03,
3787 -.1446709794e-03, -.4963850370e-04, -.1877940667e-04, -.7600909157e-05,
3788 -.3232265954e-05, -.1427365532e-05, -.6493634714e-06, -.3026190711e-06,
3789 -.1438593245e-06, -.6953911225e-07, -.3409525462e-07, -.1692310647e-07};
3790 const double cc27[nterms] = {
3791 0.1359752655e+01, -.9244638681e+00, 0.9593217969e+00, -.8771237731e+00,
3792 0.7490229011e+00, -.6677658558e+00, 0.6196745634e+00, -.5591596961e+00,
3793 0.4905325770e+00, -.4393517375e+00, 0.4029803872e+00, -.3631100059e+00,
3794 0.3199430704e+00, -.2866140604e+00, 0.2627358437e+00, -.2368256450e+00};
3795
3796 // Octagon: coefficients for centre and corner expansion.
3797 const double cc18[nterms] = {
3798 0.1000000000e+01, -.2777777612e-01, -.2246732125e-02, -.5571441725e-03,
3799 -.1790652314e-03, -.6708275760e-04, -.2766949183e-04, -.1219387286e-04,
3800 -.5640039490e-05, -.2706697160e-05, -.1337270078e-05, -.6763995657e-06,
3801 -.3488264610e-06, -.1828456675e-06, -.9718036154e-07, -.5227070332e-07};
3802 const double cc28[nterms] = {
3803 0.1362840652e+01, -.9286670089e+00, 0.1035511017e+01, -.9800255299e+00,
3804 0.8315343261e+00, -.7592730522e+00, 0.7612683773e+00, -.7132136226e+00,
3805 0.6074471474e+00, -.5554352999e+00, 0.5699443221e+00, -.5357525349e+00,
3806 0.4329345822e+00, -.3916820884e+00, 0.4401986003e+00, -.4197303057e+00};
3807
3808 for (int i = 0; i < nterms; ++i) {
3809 cc1[0][i] = cc13[i];
3810 cc2[0][i] = cc23[i];
3811 cc1[1][i] = cc14[i];
3812 cc2[1][i] = cc24[i];
3813 cc1[2][i] = cc15[i];
3814 cc2[2][i] = cc25[i];
3815 cc1[3][i] = cc16[i];
3816 cc2[3][i] = cc26[i];
3817 cc1[4][i] = cc17[i];
3818 cc2[4][i] = cc27[i];
3819 cc1[5][i] = cc18[i];
3820 cc2[5][i] = cc28[i];
3821 }
3822}
3823
3824void ComponentAnalyticField::Field3dA00(const double xpos, const double ypos,
3825 const double zpos, double& ex,
3826 double& ey, double& ez, double& volt) {
3827
3828 //-----------------------------------------------------------------------
3829 // E3DA00 - Subroutine adding 3-dimensional charges for A cells.
3830 // The potential used is 1/2*pi*eps0 1/r
3831 // VARIABLES : EX, EY : x,y-component of the electric field.
3832 // ETOT : Magnitude of electric field.
3833 // VOLT : Potential.
3834 // EXHELP etc : One term in the series to be summed.
3835 // (XPOS,YPOS): The position where the field is calculated.
3836 // (Last changed on 5/12/94.)
3837 //-----------------------------------------------------------------------
3838
3839 double exhelp, eyhelp, ezhelp, vhelp;
3840
3841 // Initialise the electric field and potential.
3842 ex = ey = ez = volt = 0.;
3843
3844 // Loop over all charges.
3845 for (int i = 0; i < n3d; ++i) {
3846 // Calculate the field in case there are no planes.
3847 const double dx = xpos - ch3d[i].x;
3848 const double dy = ypos - ch3d[i].y;
3849 const double dz = zpos - ch3d[i].z;
3850 const double r = sqrt(dx * dx + dy * dy + dz * dz);
3851 if (fabs(r) < Small) continue;
3852 const double r3 = pow(r, 3);
3853 exhelp = -dx / r3;
3854 eyhelp = -dy / r3;
3855 ezhelp = -dz / r3;
3856 vhelp = 1. / r;
3857 // Take care of a plane at constant x.
3858 double dxm = 0., dym = 0.;
3859 if (ynplax) {
3860 dxm = ch3d[i].x + xpos - 2 * coplax;
3861 const double rplan = sqrt(dxm * dxm + dy * dy);
3862 if (fabs(rplan) < Small) continue;
3863 const double rplan3 = pow(rplan, 3);
3864 exhelp += dxm / rplan3;
3865 eyhelp += dy / rplan3;
3866 ezhelp += dz / rplan3;
3867 vhelp -= 1. / rplan;
3868 }
3869 // Take care of a plane at constant y.
3870 if (ynplay) {
3871 dym = ch3d[i].y + ypos - 2. * coplay;
3872 const double rplan = sqrt(dx * dx + dym * dym);
3873 if (fabs(rplan) < Small) continue;
3874 const double rplan3 = pow(rplan, 3);
3875 exhelp += dx / rplan3;
3876 eyhelp += dym / rplan3;
3877 ezhelp += dz / rplan3;
3878 vhelp -= 1. / rplan;
3879 }
3880 // Take care of pairs of planes.
3881 if (ynplax && ynplay) {
3882 const double rplan = sqrt(dxm * dxm + dym * dym);
3883 if (fabs(rplan) < Small) continue;
3884 const double rplan3 = pow(rplan, 3);
3885 exhelp -= dxm / rplan3;
3886 eyhelp -= dym / rplan3;
3887 ezhelp -= dz / rplan3;
3888 vhelp += 1. / rplan;
3889 }
3890 // Add the terms to the electric field and the potential.
3891 ex -= ch3d[i].e * exhelp;
3892 ey -= ch3d[i].e * eyhelp;
3893 ez -= ch3d[i].e * ezhelp;
3894 volt += ch3d[i].e * vhelp;
3895 }
3896}
3897
3898void ComponentAnalyticField::Field3dB2X(const double xpos, const double ypos,
3899 const double zpos, double& ex,
3900 double& ey, double& ez, double& volt) {
3901
3902 //-----------------------------------------------------------------------
3903 // E3DB2X - Routine calculating the potential for a 3 dimensional point
3904 // charge between two plates at constant x.
3905 // The series expansions for the modified Bessel functions
3906 // have been taken from Abramowitz and Stegun.
3907 // VARIABLES : See routine E3DA00 for most of the variables.
3908 // (Last changed on 5/12/94.)
3909 //-----------------------------------------------------------------------
3910
3911 const double rcut = 1.;
3912
3913 double rr1, rr2, rm1, rm2, err, ezz;
3914 double exsum = 0., eysum = 0., ezsum = 0., vsum = 0.;
3915 double k0r, k1r, k0rm, k1rm;
3916
3917 // Initialise the sums for the field components.
3918 ex = ey = ez = volt = 0.;
3919
3920 // Loop over all charges.
3921 for (int i = 0; i < n3d; ++i) {
3922 // Skip wires that are on the charge.
3923 if (xpos == ch3d[i].x && ypos == ch3d[i].y && zpos == ch3d[i].z) continue;
3924 const double dx = xpos - ch3d[i].x;
3925 const double dy = ypos - ch3d[i].y;
3926 const double dz = zpos - ch3d[i].z;
3927 const double dxm = xpos + ch3d[i].x - 2 * coplax;
3928 // In the far away zone, sum the modified Bessel function series.
3929 if (dy * dy + dz * dz > pow(rcut * 2 * sx, 2)) {
3930 // Initialise the per-wire sum.
3931 exsum = eysum = ezsum = vsum = 0.;
3932 // Loop over the terms in the series.
3933 for (int j = 1; j <= nTermBessel; ++j) {
3934 // Obtain reduced coordinates.
3935 const double rr = Pi * j * sqrt(dy * dy + dz * dz) / sx;
3936 const double zzp = Pi * j * dx / sx;
3937 const double zzn = Pi * j * dxm / sx;
3938 // Evaluate the Bessel functions for this R.
3939 if (rr < 2.) {
3940 k0r = Numerics::BesselK0S(rr);
3941 k1r = Numerics::BesselK1S(rr);
3942 } else {
3943 k0r = Numerics::BesselK0L(rr);
3944 k1r = Numerics::BesselK1L(rr);
3945 }
3946 // Get the field components.
3947 const double czzp = cos(zzp);
3948 const double czzn = cos(zzn);
3949 vsum += (1. / sx) * k0r * (czzp - czzn);
3950 err = (TwoPi * j / (sx * sx)) * k1r * (czzp - czzn);
3951 ezz = (TwoPi * j / (sx * sx)) * k0r * (sin(zzp) - sin(zzn));
3952 exsum += ezz;
3953 eysum += err * dy / sqrt(dy * dy + dz * dz);
3954 ezsum += err * dz / sqrt(dy * dy + dz * dz);
3955 continue;
3956 }
3957 } else {
3958 // Direct polynomial summing, obtain reduced coordinates.
3959 // Loop over the terms.
3960 for (int j = 0; j <= nTermPoly; ++j) {
3961 // Simplify the references to the distances.
3962 rr1 = sqrt(pow(dx + j * 2 * sx, 2) + dy * dy + dz * dz);
3963 rr2 = sqrt(pow(dx - j * 2 * sx, 2) + dy * dy + dz * dz);
3964 rm1 = sqrt(pow(dxm - j * 2 * sx, 2) + dy * dy + dz * dz);
3965 rm2 = sqrt(pow(dxm + j * 2 * sx, 2) + dy * dy + dz * dz);
3966 const double rr13 = pow(rr1, 3);
3967 const double rm13 = pow(rm1, 3);
3968 // Initialisation of the sum: only a charge and a mirror charge.
3969 if (j == 0) {
3970 vsum = 1. / rr1 - 1. / rm1;
3971 exsum = dx / rr13 - dxm / rm13;
3972 eysum = dy * (1. / rr13 - 1. / rm13);
3973 ezsum = dz * (1. / rr13 - 1. / rm13);
3974 continue;
3975 }
3976 const double rr23 = pow(rr2, 3);
3977 const double rm23 = pow(rm2, 3);
3978 // Further terms in the series: 2 charges and 2 mirror charges.
3979 vsum += 1. / rr1 + 1. / rr2 - 1. / rm1 - 1. / rm2;
3980 exsum += (dx + j * 2 * sx) / rr13 + (dx - j * 2 * sx) / rr23 -
3981 (dxm - j * 2 * sx) / rm13 - (dxm + j * 2 * sx) / rm23;
3982 eysum += dy * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
3983 ezsum += dz * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
3984 }
3985 }
3986 // Take care of a plane at constant y.
3987 if (ynplay) {
3988 const double dym = ypos + ch3d[i].y - 2. * coplay;
3989 if (dym * dym + dz * dz > pow(rcut * 2 * sx, 2)) {
3990 // Bessel function series.
3991 // Loop over the terms in the series.
3992 for (int j = 1; j <= nTermBessel; ++j) {
3993 // Obtain reduced coordinates.
3994 const double rrm = Pi * j * sqrt(dym * dym + dz * dz) / sx;
3995 const double zzp = Pi * j * dx / sx;
3996 const double zzn = Pi * j * dxm / sx;
3997 // Evaluate the Bessel functions for this R.
3998 if (rrm < 2.) {
3999 k0rm = Numerics::BesselK0S(rrm);
4000 k1rm = Numerics::BesselK1S(rrm);
4001 } else {
4002 k0rm = Numerics::BesselK0L(rrm);
4003 k1rm = Numerics::BesselK1L(rrm);
4004 }
4005 // Get the field components.
4006 const double czzp = cos(zzp);
4007 const double czzn = cos(zzn);
4008 vsum += (1. / sx) * k0rm * (czzp - czzn);
4009 err = (TwoPi / (sx * sx)) * k1rm * (czzp - czzn);
4010 ezz = (TwoPi / (sx * sx)) * k0rm * (sin(zzp) - sin(zzn));
4011 exsum += ezz;
4012 eysum += err * dym / sqrt(dym * dym + dz * dz);
4013 ezsum += err * dz / sqrt(dym * dym + dz * dz);
4014 }
4015 } else {
4016 // Polynomial sum.
4017 // Loop over the terms.
4018 for (int j = 0; j <= nTermPoly; ++j) {
4019 // Simplify the references to the distances.
4020 rr1 = sqrt(pow(dx + j * 2 * sx, 2) + dym * dym + dz * dz);
4021 rr2 = sqrt(pow(dx - j * 2 * sx, 2) + dym * dym + dz * dz);
4022 rm1 = sqrt(pow(dxm - j * 2 * sx, 2) + dym * dym + dz * dz);
4023 rm2 = sqrt(pow(dxm + j * 2 * sx, 2) + dym * dym + dz * dz);
4024 const double rr13 = pow(rr1, 3);
4025 const double rm13 = pow(rm1, 3);
4026 // Initialisation of the sum: only a charge and a mirror charge.
4027 if (j == 0) {
4028 vsum += -1. / rr1 + 1. / rm1;
4029 exsum += -dx / rr13 + dxm / rm13;
4030 eysum += -dym * (1. / rr13 - 1. / rm13);
4031 ezsum += -dz * (1. / rr13 - 1. / rm13);
4032 continue;
4033 }
4034 const double rr23 = pow(rr2, 3);
4035 const double rm23 = pow(rm2, 3);
4036 // Further terms in the series: 2 charges and 2 mirror charges.
4037 vsum += -1. / rr1 - 1. / rr2 + 1. / rm1 + 1. / rm2;
4038 exsum += -(dx + j * 2 * sx) / rr13 - (dx - j * 2 * sx) / rr23 +
4039 (dxm - j * 2 * sx) / rm13 + (dxm + j * 2 * sx) / rm23;
4040 eysum += -dym * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
4041 ezsum += -dz * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
4042 }
4043 }
4044 }
4045 ex += ch3d[i].e * exsum;
4046 ey += ch3d[i].e * eysum;
4047 ez += ch3d[i].e * ezsum;
4048 volt += ch3d[i].e * vsum;
4049 }
4050}
4051
4052void ComponentAnalyticField::Field3dB2Y(const double xpos, const double ypos,
4053 const double zpos, double& ex,
4054 double& ey, double& ez, double& volt) {
4055
4056 //-----------------------------------------------------------------------
4057 // E3DB2Y - Routine calculating the potential for a 3 dimensional point
4058 // charge between two plates at constant y.
4059 // The series expansions for the modified Bessel functions
4060 // have been taken from Abramowitz and Stegun.
4061 // VARIABLES : See routine E3DA00 for most of the variables.
4062 // (Last changed on 5/12/94.)
4063 //-----------------------------------------------------------------------
4064
4065 const double rcut = 1.;
4066
4067 double rr1, rr2, rm1, rm2, err, ezz;
4068 double exsum = 0., eysum = 0., ezsum = 0., vsum = 0.;
4069 double k0r, k1r, k0rm, k1rm;
4070
4071 // Initialise the sums for the field components.
4072 ex = ey = ez = volt = 0.;
4073
4074 // Loop over all charges.
4075 for (int i = n3d; i--;) {
4076 // Skip wires that are on the charge.
4077 if (xpos == ch3d[i].x && ypos == ch3d[i].y && zpos == ch3d[i].z) continue;
4078 const double dx = xpos - ch3d[i].x;
4079 const double dy = ypos - ch3d[i].y;
4080 const double dz = zpos - ch3d[i].z;
4081 const double dym = ypos + ch3d[i].y - 2 * coplay;
4082 // In the far away zone, sum the modified Bessel function series.
4083 if (dx * dx + dz * dz > pow(rcut * 2 * sy, 2)) {
4084 // Initialise the per-wire sum.
4085 exsum = eysum = ezsum = vsum = 0.;
4086 // Loop over the terms in the series.
4087 for (int j = 1; j <= nTermBessel; ++j) {
4088 // Obtain reduced coordinates.
4089 const double rr = Pi * j * sqrt(dx * dx + dz * dz) / sy;
4090 const double zzp = Pi * j * dy / sy;
4091 const double zzn = Pi * j * dym / sy;
4092 // Evaluate the Bessel functions for this R.
4093 if (rr < 2.) {
4094 k0r = Numerics::BesselK0S(rr);
4095 k1r = Numerics::BesselK1S(rr);
4096 } else {
4097 k0r = Numerics::BesselK0L(rr);
4098 k1r = Numerics::BesselK1L(rr);
4099 }
4100 // Get the field components.
4101 const double czzp = cos(zzp);
4102 const double czzn = cos(zzn);
4103 vsum += (1. / sy) * k0r * (czzp - czzn);
4104 err = (TwoPi * j / (sy * sy)) * k1r * (czzp - czzn);
4105 ezz = (TwoPi * j / (sy * sy)) * k0r * (sin(zzp) - sin(zzn));
4106 exsum += err * dx / sqrt(dx * dx + dz * dz);
4107 ezsum += err * dz / sqrt(dx * dx + dz * dz);
4108 eysum += ezz;
4109 continue;
4110 }
4111 } else {
4112 // Direct polynomial summing, obtain reduced coordinates.
4113 // Loop over the terms.
4114 for (int j = 0; j <= nTermPoly; ++j) {
4115 // Simplify the references to the distances.
4116 rr1 = sqrt(dx * dx + dz * dz + pow(dy + j * 2 * sy, 2));
4117 rr2 = sqrt(dx * dx + dz * dz + pow(dy - j * 2 * sy, 2));
4118 rm1 = sqrt(dx * dx + dz * dz + pow(dym - j * 2 * sy, 2));
4119 rm2 = sqrt(dx * dx + dz * dz + pow(dym + j * 2 * sy, 2));
4120 const double rr13 = pow(rr1, 3);
4121 const double rm13 = pow(rm1, 3);
4122 // Initialisation of the sum: only a charge and a mirror charge.
4123 if (j == 0) {
4124 vsum = 1. / rr1 - 1. / rm1;
4125 exsum = dx * (1. / rr13 - 1. / rm13);
4126 ezsum = dz * (1. / rr13 - 1. / rm13);
4127 eysum = dy / rr13 - dym / rm13;
4128 continue;
4129 }
4130 // Further terms in the series: 2 charges and 2 mirror charges.
4131 const double rr23 = pow(rr2, 3);
4132 const double rm23 = pow(rm2, 3);
4133 vsum += 1. / rr1 + 1. / rr2 - 1. / rm1 - 1. / rm2;
4134 exsum += dx * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
4135 ezsum += dz * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
4136 eysum += (dy + j * 2 * sy) / rr13 + (dy - j * 2 * sy) / rr23 -
4137 (dym - j * 2 * sy) / rm13 - (dym + j * 2 * sy) / rm23;
4138 }
4139 }
4140 // Take care of a plane at constant x.
4141 if (ynplax) {
4142 const double dxm = xpos + ch3d[i].x - 2. * coplax;
4143 if (dxm * dxm + dz * dz > pow(rcut * 2 * sy, 2)) {
4144 // Bessel function series.
4145 // Loop over the terms in the series.
4146 for (int j = 1; j <= nTermBessel; ++j) {
4147 // Obtain reduced coordinates.
4148 const double rrm = Pi * j * sqrt(dxm * dxm + dz * dz) / sy;
4149 const double zzp = Pi * j * dy / sy;
4150 const double zzn = Pi * j * dym / sy;
4151 // Evaluate the Bessel functions for this R.
4152 if (rrm < 2.) {
4153 k0rm = Numerics::BesselK0S(rrm);
4154 k1rm = Numerics::BesselK1S(rrm);
4155 } else {
4156 k0rm = Numerics::BesselK0L(rrm);
4157 k1rm = Numerics::BesselK1L(rrm);
4158 }
4159 // Get the field components.
4160 const double czzp = cos(zzp);
4161 const double czzn = cos(zzn);
4162 vsum += (1. / sy) * k0rm * (czzp - czzn);
4163 err = (TwoPi / (sy * sy)) * k1rm * (czzp - czzn);
4164 ezz = (TwoPi / (sy * sy)) * k0rm * (sin(zzp) - sin(zzn));
4165 exsum += err * dxm / sqrt(dxm * dxm + dz * dz);
4166 ezsum += err * dz / sqrt(dxm * dxm + dz * dz);
4167 eysum += ezz;
4168 }
4169 } else {
4170 // Polynomial sum.
4171 // Loop over the terms.
4172 for (int j = 0; j <= nTermPoly; ++j) {
4173 // Simplify the references to the distances.
4174 rr1 = sqrt(pow(dy + j * 2 * sy, 2) + dxm * dxm + dz * dz);
4175 rr2 = sqrt(pow(dy - j * 2 * sy, 2) + dxm * dxm + dz * dz);
4176 rm1 = sqrt(pow(dym - j * 2 * sy, 2) + dxm * dxm + dz * dz);
4177 rm2 = sqrt(pow(dym + j * 2 * sy, 2) + dxm * dxm + dz * dz);
4178 const double rr13 = pow(rr1, 3);
4179 const double rm13 = pow(rm1, 3);
4180 // Initialisation of the sum: only a charge and a mirror charge.
4181 if (j == 0) {
4182 vsum += -1. / rr1 + 1. / rm1;
4183 exsum += -dxm * (1. / rr13 - 1. / rm13);
4184 ezsum += -dz * (1. / rr13 - 1. / rm13);
4185 eysum += -dy / rr13 + dym / rm13;
4186 continue;
4187 }
4188 const double rr23 = pow(rr2, 3);
4189 const double rm23 = pow(rm2, 3);
4190 // Further terms in the series: 2 charges and 2 mirror charges.
4191 vsum += -1. / rr1 - 1. / rr2 + 1. / rm1 + 1. / rm2;
4192 exsum += -dxm * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
4193 ezsum += -dz * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
4194 eysum += -(dy + j * 2 * sy) / rr13 - (dy - j * 2 * sy) / rr23 +
4195 (dym - j * 2 * sy) / rm13 + (dym + j * 2 * sy) / rm23;
4196 }
4197 }
4198 }
4199 ex += ch3d[i].e * exsum;
4200 ey += ch3d[i].e * eysum;
4201 ez += ch3d[i].e * ezsum;
4202 volt += ch3d[i].e * vsum;
4203 }
4204}
4205
4206void ComponentAnalyticField::Field3dD10(const double xxpos, const double yypos,
4207 const double zzpos, double& eex,
4208 double& eey, double& eez,
4209 double& volt) {
4210
4211 //-----------------------------------------------------------------------
4212 // E3DD10 - Subroutine adding 3-dimensional charges to tubes with one
4213 // wire running down the centre.
4214 // The series expansions for the modified Bessel functions
4215 // have been taken from Abramowitz and Stegun.
4216 // VARIABLES : See routine E3DA00 for most of the variables.
4217 // (Last changed on 25/11/95.)
4218 //-----------------------------------------------------------------------
4219
4220 const double rcut = 1.;
4221
4222 double x3d, y3d, z3d;
4223 double exsum = 0., eysum = 0., ezsum = 0., vsum = 0.;
4224 double rr1, rr2, rm1, rm2, err, ezz;
4225 double k0r, k1r;
4226
4227 // Initialise the sums for the field components.
4228 eex = eey = eez = volt = 0.;
4229 double ex = 0., ey = 0., ez = 0.;
4230
4231 // Ensure that the routine can actually work.
4232 if (nWires < 1) {
4233 std::cerr << m_className << "::Field3dD10:\n";
4234 std::cerr << " Inappropriate potential function.\n";
4235 return;
4236 }
4237
4238 // Define a periodicity and one plane in the mapped frame.
4239 const double ssx = log(2. * cotube / w[0].d);
4240 const double cpl = log(w[0].d / 2.);
4241
4242 // Transform the coordinates to the mapped frame.
4243 const double xpos = 0.5 * log(xxpos * xxpos + yypos * yypos);
4244 const double ypos = atan2(yypos, xxpos);
4245 const double zpos = zzpos;
4246
4247 // Loop over all point charges.
4248 for (int i = 0; i < n3d; ++i) {
4249 for (int ii = -1; ii <= 1; ++ii) {
4250 x3d = 0.5 * log(ch3d[i].x * ch3d[i].x + ch3d[i].y * ch3d[i].y);
4251 y3d = atan2(ch3d[i].y, ch3d[i].x + ii * TwoPi);
4252 z3d = ch3d[i].z;
4253 const double dx = xpos - x3d;
4254 const double dy = ypos - y3d;
4255 const double dz = zpos - z3d;
4256 const double dxm = xpos + x3d - 2 * cpl;
4257 // Skip wires that are on the charge.
4258 if (xpos == x3d && ypos == y3d && zpos == z3d) continue;
4259 // In the far away zone, sum the modified Bessel function series.
4260 if (dy * dy + dz * dz > pow(rcut * 2 * ssx, 2)) {
4261 // Initialise the per-wire sum.
4262 exsum = eysum = ezsum = vsum = 0.;
4263 // Loop over the terms in the series.
4264 for (int j = 1; j <= nTermBessel; ++j) {
4265 // Obtain reduced coordinates.
4266 const double rr = Pi * j * sqrt(dy * dy + dz * dz) / ssx;
4267 const double zzp = Pi * j * dx / ssx;
4268 const double zzn = Pi * j * dxm / ssx;
4269 // Evaluate the Bessel functions for this R.
4270 if (rr < 2.) {
4271 k0r = Numerics::BesselK0S(rr);
4272 k1r = Numerics::BesselK1S(rr);
4273 } else {
4274 k0r = Numerics::BesselK0L(rr);
4275 k1r = Numerics::BesselK1L(rr);
4276 }
4277 // Get the field components.
4278 const double czzp = cos(zzp);
4279 const double czzn = cos(zzn);
4280 vsum += (1. / ssx) * k0r * (czzp - czzn);
4281 err = (j * TwoPi / (ssx * ssx)) * k1r * (czzp - czzn);
4282 ezz = (j * TwoPi / (ssx * ssx)) * k0r * (sin(zzp) - sin(zzn));
4283 exsum += ezz;
4284 eysum += err * dy / sqrt(dy * dy + dz * dz);
4285 ezsum += err * dz / sqrt(dy * dy + dz * dz);
4286 }
4287 } else {
4288 // Direct polynomial summing, obtain reduced coordinates.
4289 // Loop over the terms.
4290 for (int j = 0; j < nTermPoly; ++j) {
4291 // Simplify the references to the distances.
4292 rr1 = sqrt(pow(dx + j * 2 * ssx, 2) + dy * dy + dz * dz);
4293 rr2 = sqrt(pow(dx - j * 2 * ssx, 2) + dy * dy + dz * dz);
4294 rm1 = sqrt(pow(dxm - j * 2 * ssx, 2) + dy * dy + dz * dz);
4295 rm2 = sqrt(pow(dxm + j * 2 * ssx, 2) + dy * dy + dz * dz);
4296 const double rr13 = pow(rr1, 3);
4297 const double rm13 = pow(rm1, 3);
4298 // Initialisation of the sum: only a charge and a mirror charge.
4299 if (j == 0) {
4300 vsum = 1. / rr1 - 1. / rm1;
4301 exsum = dxm / rr13 - dxm / rm13;
4302 eysum = dy * (1. / rr13 - 1. / rm13);
4303 ezsum = dz * (1. / rr13 - 1. / rm13);
4304 continue;
4305 }
4306 const double rr23 = pow(rr2, 3);
4307 const double rm23 = pow(rm2, 3);
4308 // Further terms in the series: 2 charges and 2 mirror charges.
4309 vsum += 1. / rr1 + 1. / rr2 - 1. / rm1 - 1. / rm2;
4310 exsum += (dx + j * 2 * ssx) / rr13 + (dx - j * 2 * ssx) / rr23 -
4311 (dxm - j * 2 * ssx) / rm13 - (dxm + j * 2 * ssx) / rm23;
4312 eysum += dy * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
4313 ezsum += dz * (1. / rr13 + 1. / rr23 - 1. / rm13 - 1. / rm23);
4314 }
4315 }
4316 ex += ch3d[i].e * exsum;
4317 ey += ch3d[i].e * eysum;
4318 ez += ch3d[i].e * ezsum;
4319 // Finish the loop over the charges.
4320 }
4321 }
4322
4323 // Transform the field vectors back to Cartesian coordinates.
4324 eex = exp(-xpos) * (ex * cos(ypos) - ey * sin(ypos));
4325 eey = exp(-ypos) * (ex * sin(ypos) + ey * cos(ypos));
4326 eez = ez;
4327}
4328
4329bool ComponentAnalyticField::PrepareSignals() {
4330
4331 //-----------------------------------------------------------------------
4332 // SIGINI - Initialises signal calculations.
4333 // (Last changed on 11/10/06.)
4334 //-----------------------------------------------------------------------
4335
4336 if (nReadout <= 0) {
4337 std::cerr << m_className << "::PrepareSignals:\n";
4338 std::cerr << " There are no readout groups defined.\n";
4339 std::cerr << " Calculation of weighting fields makes no sense.\n";
4340 return false;
4341 }
4342
4343 if (!cellset) {
4344 if (!Prepare()) {
4345 std::cerr << m_className << "::PrepareSignals:\n";
4346 std::cerr << " Cell could not be set up.\n";
4347 std::cerr << " No calculation of weighting fields possible.\n";
4348 return false;
4349 }
4350 }
4351
4352 // If using natural periodicity, copy the cell type.
4353 // Otherwise, eliminate true periodicities.
4354 if (nFourier == 0) {
4355 cellTypeFourier = cellType;
4356 } else if (cellType == "A " || cellType == "B1X" || cellType == "B1Y" ||
4357 cellType == "C1 ") {
4358 cellTypeFourier = "A ";
4359 } else if (cellType == "B2X" || cellType == "C2X") {
4360 cellTypeFourier = "B2X";
4361 } else if (cellType == "B2Y" || cellType == "C2Y") {
4362 cellTypeFourier = "B2Y";
4363 } else if (cellType == "C3 ") {
4364 cellTypeFourier = "C3 ";
4365 } else if (cellType == "D1 ") {
4366 cellTypeFourier = "D1 ";
4367 } else if (cellType == "D3 ") {
4368 cellTypeFourier = "D3 ";
4369 } else {
4370 // Other cases.
4371 std::cerr << m_className << "::PrepareSignals:\n";
4372 std::cerr << " No potentials available to handle cell type " << cellType
4373 << ".\n";
4374 return false;
4375 }
4376
4377 // Establish the directions in which convolutions occur.
4378 fperx = fpery = false;
4379 if (nFourier == 0) {
4380 mfexp = 0;
4381 } else {
4382 if (cellType == "B1X" || cellType == "C1 " || cellType == "C2Y") {
4383 fperx = true;
4384 }
4385 if (cellType == "B1Y" || cellType == "C1 " || cellType == "C2X") {
4386 fpery = true;
4387 }
4388 mfexp = int(0.1 + log(1. * nFourier) / log(2.));
4389 if (mfexp == 0) {
4390 fperx = fpery = false;
4391 }
4392 }
4393 // Set maximum and minimum Fourier terms.
4394 mxmin = mymin = mxmax = mymax = 0;
4395 if (fperx) {
4396 mxmin = std::min(0, 1 - nFourier / 2);
4397 mxmax = nFourier / 2;
4398 }
4399 if (fpery) {
4400 mymin = std::min(0, 1 - nFourier / 2);
4401 mymax = nFourier / 2;
4402 }
4403
4404 // Print some debugging output if requested.
4405 if (debug) {
4406 std::cout << m_className << "::PrepareSignals:\n";
4407 std::cout << " Cell type: " << cellType << "\n";
4408 std::cout << " Fourier cell type: " << cellTypeFourier << "\n";
4409 std::cout << " x convolutions: " << fperx << "\n";
4410 std::cout << " y convolutions: " << fpery << "\n";
4411 std::cout << " No of Fourier terms: " << nFourier << " (= 2**" << mfexp
4412 << ")\n";
4413 }
4414
4415 // Prepare the signal matrices.
4416 if (!SetupWireSignals()) {
4417 std::cerr << m_className << "::PrepareSignals:\n";
4418 std::cerr << " Preparing wire signal capacitance matrices failed.\n";
4419 sigmat.clear();
4420 return false;
4421 }
4422 if (!SetupPlaneSignals()) {
4423 std::cerr << m_className << "::PrepareSignals:\n";
4424 std::cerr << " Preparing plane charges failed.\n";
4425 sigmat.clear();
4426 qplane.clear();
4427 return false;
4428 }
4429
4430 // And open the signal file.
4431 // CALL SIGIST('OPEN',0,DUMMY,DUMMY,0,0,0,0,IFAIL1)
4432
4433 // Associate wires, planes and strips with readout groups
4434 for (int i = 0; i < nReadout; ++i) {
4435 for (int j = 0; j < nWires; ++j) {
4436 if (w[j].type == readout[i]) w[j].ind = i;
4437 }
4438 for (int j = 0; j < 5; ++j) {
4439 if (planes[j].type == readout[i]) planes[j].ind = i;
4440 for (int k = 0; k < planes[j].nStrips1; ++k) {
4441 if (planes[j].strips1[k].type == readout[i]) {
4442 planes[j].strips1[i].ind = i;
4443 }
4444 }
4445 for (int k = 0; k < planes[j].nStrips2; ++k) {
4446 if (planes[j].strips2[k].type == readout[i]) {
4447 planes[j].strips2[i].ind = i;
4448 }
4449 }
4450 }
4451 }
4452
4453 // Seems to have worked.
4454 sigset = true;
4455 return true;
4456}
4457
4458bool ComponentAnalyticField::SetupWireSignals() {
4459
4460 //-----------------------------------------------------------------------
4461 // SIGIPR - Prepares the ion tail calculation by filling the signal
4462 // matrices (ie non-periodic capacitance matrices),
4463 // Fourier transforming them if necessary, inverting them and
4464 // Fourier transforming them back. Because of the large number
4465 // of terms involved, a (scratch) external file on unit 13 is
4466 // used to store the intermediate and final results. This file
4467 // is handled by the routines IONBGN and IONIO.
4468 // VARIABLES : FFTMAT : Matrix used for Fourier transforms.
4469 // (Last changed on 4/10/06.)
4470 //-----------------------------------------------------------------------
4471
4472 sigmat.resize(nWires);
4473 for (int i = 0; i < nWires; ++i) {
4474 sigmat[i].clear();
4475 sigmat[i].resize(nWires);
4476 }
4477
4478 std::vector<std::vector<std::complex<double> > > fftmat;
4479 fftmat.clear();
4480
4481 if (fperx || fpery) {
4482 fftmat.resize(nFourier);
4483 for (int i = 0; i < nFourier; ++i) {
4484 fftmat[i].resize(nWires);
4485 }
4486 }
4487
4488 if (fperx || fpery) {
4489 // CALL IONBGN(IFAIL)
4490 // IF(IFAIL.EQ.1)THEN
4491 // PRINT *,' !!!!!! SIGIPR WARNING : No storage'
4492 // ' available for the signal matrices; no'
4493 // ' induced currents.'
4494 // RETURN
4495 // ENDIF
4496 }
4497
4498 // Have the matrix/matrices filled (and stored).
4499 for (int mx = mxmin; mx <= mxmax; ++mx) {
4500 for (int my = mymin; my <= mymax; ++my) {
4501 // Select layer to be produced.
4502 if (cellTypeFourier == "A ") {
4503 IprA00(mx, my);
4504 } else if (cellTypeFourier == "B2X") {
4505 IprB2X(my);
4506 } else if (cellTypeFourier == "B2Y") {
4507 IprB2Y(mx);
4508 } else if (cellTypeFourier == "C2X") {
4509 IprC2X();
4510 } else if (cellTypeFourier == "C2Y") {
4511 IprC2Y();
4512 } else if (cellTypeFourier == "D1 ") {
4513 IprD10();
4514 } else if (cellTypeFourier == "D3 ") {
4515 IprD30();
4516 } else {
4517 std::cerr << m_className << "::SetupWireSignals:\n";
4518 std::cerr << " Unknown signal cell type " << cellTypeFourier << "\n";
4519 return false;
4520 }
4521 if (debug) {
4522 std::cout << m_className << "::SetupWireSignals:\n";
4523 std::cout << " Signal matrix MX = " << mx << ", MY = " << my
4524 << " has been calculated.\n";
4525 }
4526 if (fperx || fpery) {
4527 // Store the matrix.
4528 // CALL IONIO(MX,MY,1,0,IFAIL)
4529 // Quit if storing failed.
4530 // IF(IFAIL.NE.0)GOTO 2010
4531 }
4532 // Dump the signal matrix before inversion, if DEBUG is requested.
4533 if (debug) {
4534 std::cout << m_className << "::SetupWireSignals:\n";
4535 std::cout << " Dump of signal matrix (" << mx << ", " << my
4536 << ") before inversion:\n";
4537 for (int i = 0; i < nWires; i += 10) {
4538 for (int j = 0; j < nWires; j += 10) {
4539 std::cout << " (Re-Block " << i / 10 << ", " << j / 10 << ")\n";
4540 for (int ii = 0; ii < 10; ++ii) {
4541 if (i + ii >= nWires) break;
4542 for (int jj = 0; jj < 10; ++jj) {
4543 if (j + jj >= nWires) break;
4544 std::cout << real(sigmat[i + ii][j + jj]) << " ";
4545 }
4546 std::cout << "\n";
4547 }
4548 std::cout << "\n";
4549 std::cout << " (Im-Block " << i / 10 << ", " << j / 10 << ")\n";
4550 for (int ii = 0; ii < 10; ++ii) {
4551 if (i + ii >= nWires) break;
4552 for (int jj = 0; jj < 10; ++jj) {
4553 if (j + jj >= nWires) break;
4554 std::cout << imag(sigmat[i + ii][j + jj]) << " ";
4555 }
4556 std::cout << "\n";
4557 }
4558 std::cout << "\n";
4559 }
4560 }
4561 std::cout << m_className << "::SetupWireSignals:\n";
4562 std::cout << " End of the uninverted capacitance matrix dump.\n";
4563 }
4564 // Next layer.
4565 }
4566 }
4567
4568 // Have them fourier transformed (singly periodic case).
4569 if ((fperx && !fpery) || (fpery && !fperx)) {
4570 for (int i = 0; i < nWires; ++i) {
4571 for (int m = -nFourier / 2; m < nFourier / 2; ++m) {
4572 // CALL IONIO(M,M,2,I,IFAIL)
4573 // IF(IFAIL.NE.0)GOTO 2010
4574 for (int j = 0; j < nWires; ++j) {
4575 fftmat[m + nFourier / 2][j] = sigmat[i][j];
4576 }
4577 }
4578 for (int j = 0; j < nWires; ++j) {
4579 // CALL CFFT(FFTMAT(1,J),MFEXP)
4580 }
4581 for (int m = -nFourier / 2; m < nFourier / 2; ++m) {
4582 // CALL IONIO(M,M,2,I,IFAIL)
4583 // IF(IFAIL.NE.0)GOTO 2010
4584 for (int j = 0; j < nWires; ++j) {
4585 sigmat[i][j] = fftmat[m + nFourier / 2][j];
4586 }
4587 // CALL IONIO(M,M,1,I,IFAIL)
4588 // IF(IFAIL.NE.0)GOTO 2010
4589 }
4590 }
4591 }
4592 // Have them fourier transformed (doubly periodic case).
4593 if (fperx || fpery) {
4594 for (int i = 0; i < nWires; ++i) {
4595 for (int mx = mxmin; mx <= mxmax; ++mx) {
4596 for (int my = mymin; my <= mymax; ++my) {
4597 // CALL IONIO(MX,MY,2,I,IFAIL)
4598 // IF(IFAIL.NE.0)GOTO 2010
4599 for (int j = 0; j < nWires; ++j) {
4600 fftmat[my + nFourier / 2 - 1][j] = sigmat[i][j];
4601 }
4602 }
4603 for (int j = 0; j < nWires; ++j) {
4604 // CALL CFFT(FFTMAT(1,J),MFEXP)
4605 }
4606 for (int my = mymin; my <= mymax; ++my) {
4607 // CALL IONIO(MX,MY,2,I,IFAIL)
4608 // IF(IFAIL.NE.0)GOTO 2010
4609 for (int j = 0; j < nWires; ++j) {
4610 sigmat[i][j] = fftmat[my + nFourier / 2 - 1][j];
4611 }
4612 // CALL IONIO(MX,MY,1,I,IFAIL)
4613 // IF(IFAIL.NE.0)GOTO 2010
4614 }
4615 }
4616 for (int my = mymin; my <= mymax; ++my) {
4617 for (int mx = mxmin; mx <= mxmax; ++mx) {
4618 // CALL IONIO(MX,MY,2,I,IFAIL)
4619 // IF(IFAIL.NE.0)GOTO 2010
4620 for (int j = 0; j < nWires; ++j) {
4621 fftmat[mx + nFourier / 2 - 1][j] = sigmat[i][j];
4622 }
4623 }
4624 for (int j = 0; j < nWires; ++j) {
4625 // CALL CFFT(FFTMAT(1,J),MFEXP)
4626 }
4627 for (int mx = mxmin; mx <= mxmax; ++mx) {
4628 // CALL IONIO(MX,MY,2,I,IFAIL)
4629 // IF(IFAIL.NE.0)GOTO 2010
4630 for (int j = 0; j < nWires; ++j) {
4631 sigmat[i][j] = fftmat[mx + nFourier / 2 - 1][j];
4632 }
4633 // CALL IONIO(MX,MY,1,I,IFAIL)
4634 // IF(IFAIL.NE.0)GOTO 2010
4635 }
4636 }
4637 }
4638 }
4639
4640 // Invert the matrices.
4641 for (int mx = mxmin; mx <= mxmax; ++mx) {
4642 for (int my = mymin; my <= mymax; ++my) {
4643 // Retrieve the layer.
4644 if (fperx || fpery) {
4645 // CALL IONIO(MX,MY,2,0,IFAIL)
4646 // IF(IFAIL.NE.0)GOTO 2010
4647 }
4648 // Invert.
4649 if (nWires >= 1) {
4650 int ifail = 0;
4651 Numerics::Cinv(nWires, sigmat, ifail);
4652 if (ifail != 0) {
4653 std::cerr << m_className << "::PrepareWireSignals:\n";
4654 std::cerr << " Inversion of signal matrix (" << mx << ", " << my
4655 << ") failed.\n";
4656 std::cerr << " No reliable results.\n";
4657 std::cerr << " Preparation of weighting fields is abandoned.\n";
4658 return false;
4659 }
4660 }
4661 // Store the matrix back.
4662 if (fperx || fpery) {
4663 // CALL IONIO(MX,MY,1,0,IFAIL)
4664 // IF(IFAIL.NE.0)GOTO 2010
4665 }
4666 // Next layer.
4667 }
4668 }
4669
4670 // And transform the matrices back to the original domain.
4671 if ((fperx && !fpery) || (fpery && !fperx)) {
4672 for (int i = 0; i < nWires; ++i) {
4673 for (int m = -nFourier / 2; m < nFourier / 2; ++m) {
4674 // CALL IONIO(M,M,2,I,IFAIL)
4675 // IF(IFAIL.NE.0)GOTO 2010
4676 for (int j = 0; j < nWires; ++j) {
4677 fftmat[m + nFourier / 2][j] = sigmat[i][j];
4678 }
4679 }
4680 for (int j = 0; j < nWires; ++j) {
4681 // CALL CFFT(FFTMAT(1,J),-MFEXP)
4682 }
4683 for (int m = -nFourier / 2; m < nFourier / 2; ++m) {
4684 // CALL IONIO(M,M,2,I,IFAIL)
4685 // IF(IFAIL.NE.0)GOTO 2010
4686 for (int j = 0; j < nWires; ++j) {
4687 sigmat[i][j] = fftmat[m + nFourier / 2][j] / double(nFourier);
4688 }
4689 // CALL IONIO(M,M,1,I,IFAIL)
4690 // IF(IFAIL.NE.0)GOTO 2010
4691 }
4692 }
4693 }
4694 // Have them transformed to the original domain (doubly periodic).
4695 if (fperx && fpery) {
4696 for (int i = 0; i < nWires; ++i) {
4697 for (int mx = mxmin; mx <= mxmax; ++mx) {
4698 for (int my = mymin; my <= mymax; ++my) {
4699 // CALL IONIO(MX,MY,2,I,IFAIL)
4700 // IF(IFAIL.NE.0)GOTO 2010
4701 for (int j = 0; j < nWires; ++j) {
4702 fftmat[my + nFourier / 2 - 1][j] = sigmat[i][j];
4703 }
4704 }
4705 for (int j = 0; j < nWires; ++j) {
4706 // CFFT(FFTMAT(1,J),-MFEXP)
4707 }
4708 for (int my = mymin; my <= mymax; ++my) {
4709 // CALL IONIO(MX,MY,2,I,IFAIL)
4710 // IF(IFAIL.NE.0)GOTO 2010
4711 for (int j = 0; j < nWires; ++j) {
4712 sigmat[i][j] = fftmat[my + nFourier / 2 - 1][j] / double(nFourier);
4713 }
4714 // CALL IONIO(MX,MY,1,I,IFAIL)
4715 // IF(IFAIL.NE.0)GOTO 2010
4716 }
4717 }
4718 for (int my = mymin; my <= mymax; ++my) {
4719 for (int mx = mxmin; mx <= mxmax; ++mx) {
4720 // CALL IONIO(MX,MY,2,I,IFAIL)
4721 // IF(IFAIL.NE.0)GOTO 2010
4722 for (int j = 0; j < nWires; ++j) {
4723 fftmat[mx + nFourier / 2 - 1][j] = sigmat[i][j];
4724 }
4725 }
4726 for (int j = 0; j < nWires; ++j) {
4727 // CALL CFFT(FFTMAT(1,J),-MFEXP)
4728 }
4729 for (int mx = mxmin; mx <= mxmax; ++mx) {
4730 // CALL IONIO(MX,MY,2,I,IFAIL)
4731 // IF(IFAIL.NE.0)GOTO 2010
4732 for (int j = 0; j < nWires; ++j) {
4733 sigmat[i][j] = fftmat[mx + nFourier / 2 - 1][j] / double(nFourier);
4734 }
4735 // CALL IONIO(MX,MY,1,I,IFAIL)
4736 // IF(IFAIL.NE.0)GOTO 2010
4737 }
4738 }
4739 }
4740 }
4741
4742 // Dump the signal matrix after inversion, if DEBUG is requested.
4743 if (debug) {
4744 for (int mx = mxmin; mx <= mxmax; ++mx) {
4745 for (int my = mymin; my <= mymax; ++my) {
4746 std::cout << m_className << "::SetupWireSignals:\n";
4747 std::cout << " Dump of signal matrix (" << mx << ", " << my
4748 << ") after inversion:\n";
4749 for (int i = 0; i < nWires; i += 10) {
4750 for (int j = 0; j < nWires; j += 10) {
4751 std::cout << " (Re-Block " << i / 10 << ", " << j / 10 << ")\n";
4752 for (int ii = 0; ii < 10; ++ii) {
4753 if (i + ii >= nWires) break;
4754 for (int jj = 0; jj < 10; ++jj) {
4755 if (j + jj >= nWires) break;
4756 std::cout << real(sigmat[i + ii][j + jj]) << " ";
4757 }
4758 std::cout << "\n";
4759 }
4760 std::cout << "\n";
4761 std::cout << " (Im-Block " << i / 10 << ", " << j / 10 << ")\n";
4762 for (int ii = 0; ii < 10; ++ii) {
4763 if (i + ii >= nWires) break;
4764 for (int jj = 0; jj < 10; ++jj) {
4765 if (j + jj >= nWires) break;
4766 std::cout << imag(sigmat[i + ii][j + jj]) << " ";
4767 }
4768 std::cout << "\n";
4769 }
4770 std::cout << "\n";
4771 }
4772 }
4773 std::cout << m_className << "::SetupWireSignals:\n";
4774 std::cout << " End of the inverted capacitance matrix dump.\n";
4775 }
4776 }
4777 }
4778 return true;
4779}
4780
4781bool ComponentAnalyticField::SetupPlaneSignals() {
4782
4783 //-----------------------------------------------------------------------
4784 // SIGPLP - Computes the weighting field charges for the planes and
4785 // the tube.
4786 // (Last changed on 14/10/99.)
4787 //-----------------------------------------------------------------------
4788
4789 const int nPlanes = 5;
4790 qplane.resize(nPlanes);
4791 for (int i = 0; i < nPlanes; ++i) {
4792 qplane[i].resize(nWires);
4793 }
4794
4795 double vw;
4796
4797 // Loop over the signal layers.
4798 for (int mx = mxmin; mx <= mxmax; ++mx) {
4799 for (int my = mymin; my <= mymax; ++my) {
4800 // Load the layers of the signal matrices.
4801 // CALL IONIO(MX,MY,2,0,IFAIL1)
4802 // IF(IFAIL1.NE.0)THEN
4803 // PRINT *,' !!!!!! SIGPLP WARNING : Signal matrix'//
4804 // ' store error; field for planes not prepared.'
4805 // RETURN
4806 // ENDIF
4807 // Initialise the plane matrices.
4808 for (int i = 0; i < 5; ++i) {
4809 for (int j = 0; j < nWires; ++j) qplane[i][j] = 0.;
4810 }
4811 // Charges for plane 1, if present.
4812 if (ynplan[0]) {
4813 // Set the weighting field voltages.
4814 for (int i = 0; i < nWires; ++i) {
4815 if (ynplan[1]) {
4816 vw = -(coplan[1] - w[i].x) / (coplan[1] - coplan[0]);
4817 } else if (perx) {
4818 vw = -(coplan[0] + sx - w[i].x) / sx;
4819 } else {
4820 vw = -1;
4821 }
4822 // Multiply with the matrix.
4823 for (int j = 0; j < nWires; ++j) {
4824 qplane[0][j] += real(sigmat[i][j]) * vw;
4825 }
4826 }
4827 }
4828 // Charges for plane 2, if present.
4829 if (ynplan[1]) {
4830 // Set the weighting field voltages.
4831 for (int i = 0; i < nWires; ++i) {
4832 if (ynplan[0]) {
4833 vw = -(coplan[0] - w[i].x) / (coplan[0] - coplan[1]);
4834 } else if (perx) {
4835 vw = -(w[i].x - coplan[1] + sx) / sx;
4836 } else {
4837 vw = -1.;
4838 }
4839 // Multiply with the matrix.
4840 for (int j = 0; j < nWires; ++j) {
4841 qplane[1][j] += real(sigmat[i][j]) * vw;
4842 }
4843 }
4844 }
4845 // Charges for plane 3, if present.
4846 if (ynplan[2]) {
4847 // Set the weighting field voltages.
4848 for (int i = 0; i < nWires; ++i) {
4849 if (ynplan[3]) {
4850 vw = -(coplan[3] - w[i].y) / (coplan[3] - coplan[2]);
4851 } else if (pery) {
4852 vw = -(coplan[2] + sy - w[i].y) / sy;
4853 } else {
4854 vw = -1.;
4855 }
4856 // Multiply with the matrix.
4857 for (int j = 0; j < nWires; ++j) {
4858 qplane[2][i] += real(sigmat[i][j]) * vw;
4859 }
4860 }
4861 }
4862 // Charges for plane 4, if present.
4863 if (ynplan[3]) {
4864 // Set the weighting field voltages.
4865 for (int i = 0; i < nWires; ++i) {
4866 if (ynplan[2]) {
4867 vw = -(coplan[2] - w[i].y) / (coplan[2] - coplan[3]);
4868 } else if (pery) {
4869 vw = -(w[i].y - coplan[3] + sy) / sy;
4870 } else {
4871 vw = -1.;
4872 }
4873 // Multiply with the matrix.
4874 for (int j = 0; j < nWires; ++j) {
4875 qplane[3][i] += real(sigmat[i][j]) * vw;
4876 }
4877 }
4878 }
4879 // Charges for the tube, if present.
4880 if (tube) {
4881 for (int i = 0; i < nWires; ++i) {
4882 for (int j = 0; j < nWires; ++j) {
4883 qplane[4][i] -= real(sigmat[i][j]);
4884 }
4885 }
4886 }
4887 // Store the plane charges.
4888 // CALL IPLIO(MX,MY,1,IFAIL1)
4889 // IF(IFAIL1.NE.0)THEN
4890 // PRINT *,' !!!!!! SIGPLP WARNING : Plane matrix'//
4891 // ' store error; field for planes not prepared.'
4892 // RETURN
4893 // ENDIF
4894 // Next layer.
4895 }
4896 }
4897 // Compute the background weighting fields, first in x.
4898 if (ynplan[0] && ynplan[1]) {
4899 planes[0].ewxcor = 1. / (coplan[1] - coplan[0]);
4900 planes[1].ewxcor = 1. / (coplan[0] - coplan[1]);
4901 } else if (ynplan[0] && perx) {
4902 planes[0].ewxcor = 1. / sx;
4903 planes[1].ewxcor = 0.;
4904 } else if (ynplan[1] && perx) {
4905 planes[0].ewxcor = 0.;
4906 planes[1].ewxcor = -1. / sx;
4907 } else {
4908 planes[0].ewxcor = planes[1].ewxcor = 0.;
4909 }
4910 planes[2].ewxcor = planes[3].ewxcor = planes[4].ewxcor = 0.;
4911 // Next also in y.
4912 planes[0].ewycor = planes[1].ewycor = 0.;
4913 if (ynplan[2] && ynplan[3]) {
4914 planes[2].ewycor = 1. / (coplan[3] - coplan[2]);
4915 planes[3].ewycor = 1. / (coplan[2] - coplan[3]);
4916 } else if (ynplan[2] && pery) {
4917 planes[2].ewycor = 1. / sy;
4918 planes[3].ewycor = 0.;
4919 } else if (ynplan[3] && pery) {
4920 planes[2].ewycor = 0.;
4921 planes[3].ewycor = -1. / sy;
4922 } else {
4923 planes[2].ewycor = planes[3].ewycor = 0.;
4924 }
4925 // The tube has no correction field.
4926 planes[4].ewycor = 0.;
4927
4928 // Debugging output.
4929 if (debug) {
4930 std::cout << m_className << "::SetupPlaneSignals:\n";
4931 std::cout << " Charges for currents induced in the planes:\n";
4932 std::cout << " Wire x-Plane 1 x-Plane 2"
4933 << " y-Plane 1 y-Plane 2"
4934 << " Tube\n";
4935 for (int i = 0; i < nWires; ++i) {
4936 std::cout << " " << i << " " << qplane[0][i] << " " << qplane[1][i]
4937 << " " << qplane[2][i] << " " << qplane[3][i] << " "
4938 << qplane[4][i] << "\n";
4939 }
4940 std::cout << m_className << "::SetupPlaneSignals:\n";
4941 std::cout << " Bias fields:\n";
4942 std::cout << " Plane x-Bias [1/cm] y-Bias [1/cm]\n";
4943 for (int i = 0; i < 4; ++i) {
4944 std::cout << " " << i << " " << planes[i].ewxcor << " "
4945 << planes[i].ewycor << "\n";
4946 }
4947 }
4948
4949 return true;
4950}
4951
4952bool ComponentAnalyticField::IprA00(const int mx, const int my) {
4953
4954 //-----------------------------------------------------------------------
4955 // IPRA00 - Routine filling the (MX,MY) th layer of the signal matrix
4956 // for cells with non-periodic type A (see SIGIPR).
4957 //-----------------------------------------------------------------------
4958
4959 const double dx = mx * sx;
4960 const double dy = my * sy;
4961 double aa = 0.;
4962
4963 for (int i = 0; i < nWires; ++i) {
4964 // Diagonal terms.
4965 if (dx != 0. || dy != 0.) {
4966 aa = dx * dx + dy * dy;
4967 } else {
4968 aa = 0.25 * w[i].d * w[i].d;
4969 }
4970 // Take care of single equipotential planes.
4971 if (ynplax) aa /= 2. * pow(w[i].x - coplax, 2) + dy * dy;
4972 if (ynplay) aa /= 2. * pow(w[i].y - coplay, 2) + dx * dx;
4973 // Take care of pairs of equipotential planes.
4974 if (ynplax && ynplay)
4975 aa *= 4. * (pow(w[i].x - coplax, 2) + pow(w[i].y - coplay, 2));
4976 // Define the final version of a[i][i].
4977 sigmat[i][i] = -0.5 * log(aa);
4978 for (int j = i + 1; j < nWires; ++j) {
4979 aa = pow(w[i].x + dx - w[j].x, 2) + pow(w[i].y + dy - w[j].y, 2);
4980 // Take care of single planes.
4981 if (ynplax)
4982 aa /= pow(2. * coplax - w[i].x - dx - w[j].x, 2) +
4983 pow(w[i].y + dy - w[j].y, 2);
4984 if (ynplay)
4985 aa /= pow(w[i].x + dx - w[j].x, 2) +
4986 pow(2. * coplay - w[i].y - dy - w[j].y, 2);
4987 // Take care of pairs of planes.
4988 if (ynplax && ynplay) {
4989 aa *= pow(2. * coplax - w[i].x - dx - w[j].x, 2) +
4990 pow(2. * coplay - w[i].y - dy - w[j].y, 2);
4991 }
4992 // Store the true versions after taking LOGs and SQRT's.
4993 sigmat[i][j] = -0.5 * log(aa);
4994 sigmat[j][i] = sigmat[i][j];
4995 }
4996 }
4997 return true;
4998}
4999
5000bool ComponentAnalyticField::IprB2X(const int my) {
5001
5002 //-----------------------------------------------------------------------
5003 // IPRB2X - Routine filling the MY th layer of the signal matrix
5004 // for cells with non-periodic type B2X (see SIGIPR).
5005 // (Last changed on 26/ 4/92.)
5006 //-----------------------------------------------------------------------
5007
5008 b2sin.resize(nWires);
5009
5010 const double dy = my * sy;
5011 double aa = 0.;
5012 double xx, yy, xxneg, yymirr;
5013
5014 // Loop over all wires and calculate the diagonal elements first.
5015 for (int i = 0; i < nWires; ++i) {
5016 xx = (Pi / sx) * (w[i].x - coplan[0]);
5017 if (dy != 0.) {
5018 aa = pow(sinh(Pi * dy / sx) / sin(xx), 2);
5019 } else {
5020 aa = pow((0.25 * w[i].d * Pi / sx) / sin(xx), 2);
5021 }
5022 // Take care of a planes at constant y (no dy in this case).
5023 if (ynplay) {
5024 yymirr = (Pi / sx) * (w[i].y - coplay);
5025 if (fabs(yymirr) <= 20.) {
5026 aa *= (pow(sinh(yymirr), 2) + pow(sin(xx), 2)) / pow(sinh(yymirr), 2);
5027 }
5028 }
5029 // Store the true value of A[i][i].
5030 sigmat[i][i] = -0.5 * log(aa);
5031 // Loop over all other wires to obtain off-diagonal elements.
5032 for (int j = i + 1; j < nWires; ++j) {
5033 yy = HalfPi * (w[i].y + dy - w[j].y) / sx;
5034 xx = HalfPi * (w[i].x - w[j].x) / sx;
5035 xxneg = HalfPi * (w[i].x + w[j].x - 2. * coplan[0]) / sx;
5036 if (fabs(yy) <= 20.) {
5037 aa = (pow(sinh(yy), 2) + pow(sin(xx), 2)) /
5038 (pow(sinh(yy), 2) + pow(sin(xxneg), 2));
5039 } else {
5040 aa = 1.;
5041 }
5042 // Take equipotential planes into account (no dy anyhow).
5043 if (ynplay) {
5044 yymirr = HalfPi * (w[i].y + w[j].y - 2. * coplay) / sx;
5045 if (fabs(yymirr) <= 20.) {
5046 aa *= (pow(sinh(yymirr), 2) + pow(sin(xxneg), 2)) /
5047 (pow(sinh(yymirr), 2) + pow(sin(xx), 2));
5048 }
5049 }
5050 // Store the true value of a[i][j] in both a[i][j] and a[j][i].
5051 sigmat[i][j] = -0.5 * log(aa);
5052 sigmat[j][i] = sigmat[i][j];
5053 }
5054 // Fill the B2SIN vector.
5055 b2sin[i] = sin(Pi * (coplan[0] - w[i].x) / sx);
5056 }
5057
5058 return true;
5059}
5060
5061bool ComponentAnalyticField::IprB2Y(const int mx) {
5062
5063 //-----------------------------------------------------------------------
5064 // IPRB2Y - Routine filling the MX th layer of the signal matrix
5065 // for cells with non-periodic type B2Y (see SIGIPR).
5066 // (Last changed on 26/ 4/92.)
5067 //-----------------------------------------------------------------------
5068
5069 b2sin.resize(nWires);
5070
5071 const double dx = mx * sx;
5072 double aa = 0.;
5073 double xx, yy, xxmirr, yyneg;
5074
5075 // Loop over all wires and calculate the diagonal elements first.
5076 for (int i = 0; i < nWires; ++i) {
5077 yy = (Pi / sy) * (w[i].y - coplan[2]);
5078 if (dx != 0.) {
5079 aa = pow(sinh(Pi * dx / sy) / sin(yy), 2);
5080 } else {
5081 aa = pow((0.25 * w[i].d * Pi / sy) / sin(yy), 2);
5082 }
5083 // Take care of a planes at constant x (no dx in this case).
5084 if (ynplax) {
5085 xxmirr = (Pi / sy) * (w[i].x - coplax);
5086 if (fabs(xxmirr) <= 20.) {
5087 aa *= (pow(sinh(xxmirr), 2) + pow(sin(yy), 2)) / pow(sinh(xxmirr), 2);
5088 }
5089 }
5090 // Store the true value of A[i][i].
5091 sigmat[i][i] = -0.5 * log(aa);
5092 // Loop over all other wires to obtain off-diagonal elements.
5093 for (int j = i + 1; j < nWires; ++j) {
5094 xx = HalfPi * (w[i].x + dx - w[j].x) / sy;
5095 yy = HalfPi * (w[i].y - w[j].y) / sy;
5096 yyneg = HalfPi * (w[i].y + w[j].y - 2. * coplan[2]) / sy;
5097 if (fabs(xx) <= 20.) {
5098 aa = (pow(sinh(xx), 2) + pow(sin(yy), 2)) /
5099 (pow(sinh(xx), 2) + pow(sin(yyneg), 2));
5100 } else {
5101 aa = 1.;
5102 }
5103 // Take equipotential planes into account (no dx anyhow).
5104 if (ynplax) {
5105 xxmirr = HalfPi * (w[i].x + w[j].x - 2. * coplax) / sy;
5106 if (fabs(xxmirr) <= 20.) {
5107 aa *= (pow(sinh(xxmirr), 2) + pow(sin(yyneg), 2)) /
5108 (pow(sinh(xxmirr), 2) + pow(sin(yy), 2));
5109 }
5110 }
5111 // Store the true value of a[i][j] in both a[i][j] and a[j][i].
5112 sigmat[i][j] = -0.5 * log(aa);
5113 sigmat[j][i] = sigmat[i][j];
5114 }
5115 // Fill the B2SIN vector.
5116 b2sin[i] = sin(Pi * (coplan[2] - w[i].y) / sy);
5117 }
5118 return true;
5119}
5120
5121bool ComponentAnalyticField::IprC2X() {
5122
5123 //-----------------------------------------------------------------------
5124 // IPRC2X - This initializing subroutine stores the capacitance matrix
5125 // for the configuration:
5126 // wires at zw(j)+cmplx(lx*2*sx,ly*sy),
5127 // j=1(1)n, lx=-infinity(1)infinity, ly=-infinity(1)infinity.
5128 // but the signs of the charges alternate in the x-direction
5129 // (Last changed on 4/10/06.)
5130 //-----------------------------------------------------------------------
5131
5132 // Fill the capacitance matrix.
5133 for (int i = 0; i < nWires; ++i) {
5134 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
5135 for (int j = 0; j < nWires; ++j) {
5136 double temp = 0.;
5137 if (mode == 0) {
5138 temp = (w[i].x - cx) * (w[j].x - cx) * TwoPi / (sx * sy);
5139 }
5140 if (i == j) {
5141 sigmat[i][j] =
5142 Ph2Lim(0.5 * w[i].d) - Ph2(2. * (w[j].x - cx), 0.) - temp;
5143 } else {
5144 sigmat[i][j] = Ph2(w[i].x - w[j].x, w[i].y - w[j].y) -
5145 Ph2(w[i].x + w[j].x - 2. * cx, w[i].y - w[j].y) - temp;
5146 }
5147 }
5148 }
5149 return true;
5150}
5151
5152bool ComponentAnalyticField::IprC2Y() {
5153
5154 //-----------------------------------------------------------------------
5155 // IPRC2Y - This initializing subroutine stores the capacitance matrix
5156 // for the configuration:
5157 // wires at zw(j)+cmplx(lx*sx,ly*2*sy),
5158 // j=1(1)n, lx=-infinity(1)infinity, ly=-infinity(1)infinity.
5159 // but the signs of the charges alternate in the y-direction
5160 // (Last changed on 4/10/06.)
5161 //-----------------------------------------------------------------------
5162
5163 // Fill the capacitance matrix.
5164 for (int i = 0; i < nWires; ++i) {
5165 double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
5166 for (int j = 0; j < nWires; ++j) {
5167 double temp = 0.;
5168 if (mode == 1) {
5169 temp = (w[i].y - cy) * (w[j].y - cy) * TwoPi / (sx * sy);
5170 }
5171 if (i == j) {
5172 sigmat[i][j] =
5173 Ph2Lim(0.5 * w[i].d) - Ph2(0., 2. * (w[j].y - cy)) - temp;
5174 } else {
5175 sigmat[i][j] = Ph2(w[i].x - w[j].x, w[i].y - w[j].y) -
5176 Ph2(w[i].x - w[j].x, w[i].y + w[j].y - 2. * cy) - temp;
5177 }
5178 }
5179 }
5180 return true;
5181}
5182
5183bool ComponentAnalyticField::IprC30() {
5184
5185 //-----------------------------------------------------------------------
5186 // IPRC30 - Routine filling the signal matrix for cells of type C30.
5187 // Since the signal matrix equals the capacitance matrix for
5188 // this potential, the routine is identical to SETC30 except
5189 // for the C and P parameters.
5190 // (Last changed on 11/11/97.)
5191 //-----------------------------------------------------------------------
5192
5193 // Fill the capacitance matrix.
5194 for (int i = 0; i < nWires; ++i) {
5195 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
5196 double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
5197 for (int j = 0; j < nWires; ++j) {
5198 if (i == j) {
5199 sigmat[i][i] = Ph2Lim(0.5 * w[i].d) - Ph2(0., 2. * (w[i].y - cy)) -
5200 Ph2(2. * (w[i].x - cx), 0.) +
5201 Ph2(2. * (w[i].x - cx), 2. * (w[i].y - cy));
5202 } else {
5203 sigmat[i][j] =
5204 Ph2(w[i].x - w[j].x, w[i].y - w[j].y) -
5205 Ph2(w[i].x - w[j].x, w[i].y + w[j].y - 2. * cy) -
5206 Ph2(w[i].x + w[j].x - 2. * cx, w[i].y - w[j].y) +
5207 Ph2(w[i].x + w[j].x - 2. * cx, w[i].y + w[j].y - 2. * cy);
5208 }
5209 }
5210 }
5211 return true;
5212}
5213
5214bool ComponentAnalyticField::IprD10() {
5215
5216 //-----------------------------------------------------------------------
5217 // IPRD10 - Signal matrix preparation for D1 cells.
5218 // VARIABLES :
5219 // (Last changed on 2/ 2/93.)
5220 //-----------------------------------------------------------------------
5221
5222 std::complex<double> zi, zj;
5223
5224 // Loop over all wires.
5225 for (int i = 0; i < nWires; ++i) {
5226 // Set the diagonal terms.
5227 sigmat[i][i] =
5228 -log(0.5 * w[i].d /
5229 (cotube - (w[i].x * w[i].x + w[i].y * w[i].y) / cotube));
5230 // Set a complex wire-coordinate to make things a little easier.
5231 zi = std::complex<double>(w[i].x, w[i].y);
5232 // Loop over all other wires for the off-diagonal elements.
5233 for (int j = i + 1; j < nWires; ++j) {
5234 // Set a complex wire-coordinate to make things a little easier.
5235 zj = std::complex<double>(w[j].x, w[j].y);
5236 sigmat[i][j] = -log(abs((1. / cotube) * (zi - zj) /
5237 (1. - conj(zi) * zj / (cotube * cotube))));
5238 // Copy this to a[j][i] since the capacitance matrix is symmetric.
5239 sigmat[j][i] = sigmat[i][j];
5240 }
5241 }
5242 return true;
5243}
5244
5245bool ComponentAnalyticField::IprD30() {
5246
5247 //-----------------------------------------------------------------------
5248 // IPRD30 - Signal matrix preparation for polygonal cells (type D3).
5249 // Variables :
5250 // (Last changed on 19/ 6/97.)
5251 //-----------------------------------------------------------------------
5252
5253 wmap.resize(nWires);
5254
5255 std::complex<double> wd;
5256 InitializeCoefficientTables();
5257
5258 // Loop over all wire combinations.
5259 for (int i = 0; i < nWires; ++i) {
5260 // We need to compute the wire mapping again to obtain WD.
5261 ConformalMap(std::complex<double>(w[i].x, w[i].y) / cotube, wmap[i], wd);
5262 // Diagonal elements.
5263 sigmat[i][i] =
5264 -log(abs((0.5 * w[i].d / cotube) * wd / (1. - pow(abs(wmap[i]), 2))));
5265 // Loop over all other wires for the off-diagonal elements.
5266 for (int j = 0; j < i - 1; ++j) {
5267 sigmat[i][j] =
5268 -log(abs((wmap[i] - wmap[j]) / (1. - conj(wmap[i]) * wmap[j])));
5269 // Copy this to a[j][i] since the capacitance matrix is symmetric.
5270 sigmat[j][i] = sigmat[i][j];
5271 }
5272 }
5273 return true;
5274}
5275
5276bool ComponentAnalyticField::Wfield(const double xpos, const double ypos,
5277 const double zpos, double& exsum,
5278 double& eysum, double& ezsum, double& vsum,
5279 const int isw, const bool opt) {
5280
5281 //-----------------------------------------------------------------------
5282 // SIGFLS - Sums the weighting field components at (XPOS,YPOS,ZPOS).
5283 // (Last changed on 11/10/06.)
5284 //-----------------------------------------------------------------------
5285
5286 // Initialise the sums.
5287 exsum = eysum = ezsum = vsum = 0.;
5288 double ex = 0., ey = 0., ez = 0.;
5289 double volt = 0.;
5290
5291 if (!sigset) return false;
5292
5293 // Loop over the signal layers.
5294 for (int mx = mxmin; mx <= mxmax; ++mx) {
5295 for (int my = mymin; my <= mymax; ++my) {
5296 // Load the layers of the wire matrices.
5297 // CALL IONIO(MX,MY,2,0,IFAIL)
5298 // if (!LoadWireLayers(mx, my)) {
5299 // std::cerr << m_className << "::LoadWireLayers:\n";
5300 // std::cerr << " Wire matrix store error.\n";
5301 // std::cerr << " No weighting field returned.\n";
5302 // exsum = eysum = ezsum = 0.;
5303 // return false;
5304 //}
5305 // Loop over all wires.
5306 for (int iw = nWires; iw--;) {
5307 // Pick out those wires that are part of this read out group.
5308 if (w[iw].ind == isw) {
5309 ex = ey = ez = 0.;
5310 if (cellTypeFourier == "A ") {
5311 WfieldWireA00(xpos, ypos, ex, ey, volt, mx, my, iw, opt);
5312 } else if (cellTypeFourier == "B2X") {
5313 WfieldWireB2X(xpos, ypos, ex, ey, volt, my, iw, opt);
5314 } else if (cellTypeFourier == "B2Y") {
5315 WfieldWireB2Y(xpos, ypos, ex, ey, volt, mx, iw, opt);
5316 } else if (cellTypeFourier == "C2X") {
5317 WfieldWireC2X(xpos, ypos, ex, ey, volt, iw, opt);
5318 } else if (cellTypeFourier == "C2Y") {
5319 WfieldWireC2Y(xpos, ypos, ex, ey, volt, iw, opt);
5320 } else if (cellTypeFourier == "C3 ") {
5321 WfieldWireC30(xpos, ypos, ex, ey, volt, iw, opt);
5322 } else if (cellTypeFourier == "D1 ") {
5323 WfieldWireD10(xpos, ypos, ex, ey, volt, iw, opt);
5324 } else if (cellTypeFourier == "D3 ") {
5325 WfieldWireD30(xpos, ypos, ex, ey, volt, iw, opt);
5326 } else {
5327 std::cerr << m_className << "::Wfield:\n";
5328 std::cerr << " Unkown signal field type " << cellTypeFourier
5329 << " received. Program error!\n";
5330 std::cerr << " Encountered for wire " << iw
5331 << ", readout group = " << w[iw].ind << "\n";
5332 exsum = eysum = ezsum = vsum = 0.;
5333 return false;
5334 }
5335 exsum += ex;
5336 eysum += ey;
5337 ezsum += ez;
5338 if (opt) vsum += volt;
5339 }
5340 }
5341 // Load the layers of the plane matrices.
5342 // CALL IPLIO(MX,MY,2,IFAIL)
5343 // if (!LoadPlaneLayers(mx, my)) {
5344 // std::cerr << m_className << "::Wfield:\n";
5345 // std::cerr << " Plane matrix store error.\n";
5346 // std::cerr << " No weighting field returned.\n";
5347 // exsum = eysum = ezsum = 0.;
5348 // return;
5349 //}
5350 // Loop over all planes.
5351 for (int ip = 0; ip < 5; ++ip) {
5352 // Pick out those that are part of this read out group.
5353 if (planes[ip].ind == isw) {
5354 ex = ey = ez = 0.;
5355 if (cellTypeFourier == "A ") {
5356 WfieldPlaneA00(xpos, ypos, ex, ey, volt, mx, my, ip, opt);
5357 } else if (cellTypeFourier == "B2X") {
5358 WfieldPlaneB2X(xpos, ypos, ex, ey, volt, my, ip, opt);
5359 } else if (cellTypeFourier == "B2Y") {
5360 WfieldPlaneB2Y(xpos, ypos, ex, ey, volt, mx, ip, opt);
5361 } else if (cellTypeFourier == "C2X") {
5362 WfieldPlaneC2X(xpos, ypos, ex, ey, volt, ip, opt);
5363 } else if (cellTypeFourier == "C2Y") {
5364 WfieldPlaneC2Y(xpos, ypos, ex, ey, volt, ip, opt);
5365 } else if (cellTypeFourier == "D1 ") {
5366 WfieldPlaneD10(xpos, ypos, ex, ey, volt, ip, opt);
5367 } else if (cellTypeFourier == "D3 ") {
5368 WfieldPlaneD30(xpos, ypos, ex, ey, volt, ip, opt);
5369 } else {
5370 std::cerr << m_className << "::Wfield:\n";
5371 std::cerr << " Unkown field type " << cellTypeFourier
5372 << " received. Program error!\n";
5373 std::cerr << " Encountered for plane " << ip
5374 << ", readout group = " << planes[ip].ind << "\n";
5375 exsum = eysum = ezsum = 0.;
5376 return false;
5377 }
5378 exsum += ex;
5379 eysum += ey;
5380 ezsum += ez;
5381 if (opt) vsum += volt;
5382 }
5383 }
5384 // Next signal layer.
5385 }
5386 }
5387 // Add the field due to the planes themselves.
5388 for (int ip = 0; ip < 5; ++ip) {
5389 if (planes[ip].ind == isw) {
5390 exsum += planes[ip].ewxcor;
5391 eysum += planes[ip].ewycor;
5392 if (opt) {
5393 if (ip == 0 || ip == 1) {
5394 double xx = xpos;
5395 if (perx) {
5396 xx -= sx * int(round(xpos / sx));
5397 if (ynplan[0] && xx <= coplan[0]) xx += sx;
5398 if (ynplan[1] && xx >= coplan[1]) xx -= sx;
5399 }
5400 vsum += 1. - planes[ip].ewxcor * (xx - coplan[ip]);
5401 } else if (ip == 2 || ip == 3) {
5402 double yy = ypos;
5403 if (pery) {
5404 yy -= sy * int(round(ypos / sy));
5405 if (ynplan[2] && yy <= coplan[2]) yy += sy;
5406 if (ynplan[3] && yy >= coplan[3]) yy -= sy;
5407 }
5408 vsum += 1. - planes[ip].ewycor * (yy - coplan[ip]);
5409 }
5410 }
5411 }
5412 }
5413
5414 // Add strips, if there are any.
5415 for (int ip = 0; ip < 5; ++ip) {
5416 for (int istrip = 0; istrip < planes[ip].nStrips1; ++istrip) {
5417 if (planes[ip].strips1[istrip].ind == isw) {
5418 WfieldStripXy(xpos, ypos, zpos, ex, ey, ez, volt, ip, istrip, opt);
5419 exsum += ex;
5420 eysum += ey;
5421 ezsum += ez;
5422 if (opt) vsum += volt;
5423 }
5424 }
5425 for (int istrip = 0; istrip < planes[ip].nStrips2; ++istrip) {
5426 if (planes[ip].strips2[istrip].ind == isw) {
5427 WfieldStripZ(xpos, ypos, ex, ey, volt, ip, istrip, opt);
5428 exsum += ex;
5429 eysum += ey;
5430 if (opt) vsum += volt;
5431 }
5432 }
5433 }
5434 return true;
5435}
5436
5437void ComponentAnalyticField::WfieldWireA00(const double xpos, const double ypos,
5438 double& ex, double& ey, double& volt,
5439 const int mx, const int my,
5440 const int isw, const bool opt) {
5441
5442 //-----------------------------------------------------------------------
5443 // IONA00 - Routine returning the A I,J [MX,MY] * E terms for A cells.
5444 // VARIABLES : R2 : Potential before taking -Log(Sqrt(...))
5445 // EX,EY : x,y-Component of the electric field.
5446 // ETOT : Magnitude of the electric field.
5447 // VOLT : Potential.
5448 // EXHELP ETC : One term in the summing series.
5449 // (XPOS,YPOS): Position where the field is needed.
5450 // (Last changed on 14/ 8/98.)
5451 //-----------------------------------------------------------------------
5452
5453 // Initialise the electric field and potential.
5454 ex = ey = volt = 0.;
5455
5456 double xxmirr = 0., yymirr = 0.;
5457 // Loop over all wires.
5458 for (int i = nWires; i--;) {
5459 // Define a few reduced variables.
5460 const double xx = xpos - w[i].x - mx * sx;
5461 const double yy = ypos - w[i].y - my * sy;
5462 // Calculate the field in case there are no planes.
5463 double r2 = xx * xx + yy * yy;
5464 if (r2 <= 0.) continue;
5465 double exhelp = xx / r2;
5466 double eyhelp = yy / r2;
5467 // Take care of a plane at constant x.
5468 if (ynplax) {
5469 xxmirr = xpos + w[i].x - 2. * coplax;
5470 const double r2plan = xxmirr * xxmirr + yy * yy;
5471 if (r2plan <= 0.) continue;
5472 exhelp -= xxmirr / r2plan;
5473 eyhelp -= yy / r2plan;
5474 r2 /= r2plan;
5475 }
5476 // Take care of a plane at constant y.
5477 if (ynplay) {
5478 yymirr = ypos + w[i].y - 2. * coplay;
5479 const double r2plan = xx * xx + yymirr * yymirr;
5480 if (r2plan <= 0.) continue;
5481 exhelp -= xx / r2plan;
5482 eyhelp -= yymirr / r2plan;
5483 r2 /= r2plan;
5484 }
5485 // Take care of pairs of planes.
5486 if (ynplax && ynplay) {
5487 const double r2plan = xxmirr * xxmirr + yymirr * yymirr;
5488 if (r2plan <= 0.) continue;
5489 exhelp += xxmirr / r2plan;
5490 eyhelp += yymirr / r2plan;
5491 r2 *= r2plan;
5492 }
5493 // Calculate the electric field and the potential.
5494 if (opt) volt -= 0.5 * real(sigmat[isw][i]) * log(r2);
5495 ex += real(sigmat[isw][i]) * exhelp;
5496 ey += real(sigmat[isw][i]) * eyhelp;
5497 }
5498}
5499
5500void ComponentAnalyticField::WfieldWireB2X(const double xpos, const double ypos,
5501 double& ex, double& ey, double& volt,
5502 const int my, const int isw,
5503 const bool opt) {
5504
5505 //-----------------------------------------------------------------------
5506 // IONB2X - Routine calculating the MY contribution to the signal on
5507 // wire ISW due to a charge at (XPOS,YPOS) for F-B2Y cells.
5508 // VARIABLES : See routine EFCA00 for most of the variables.
5509 // Z,ZZMIRR : X + I*Y , XXMIRR + I*YYMIRR ; I**2=-1
5510 // ECOMPL : EX + I*EY ; I**2=-1
5511 // (Last changed on 20/ 2/90.)
5512 //-----------------------------------------------------------------------
5513
5514 std::complex<double> zz, ecompl, zzmirr, zzneg, zznmirr;
5515
5516 // Initialise the electric field and potential.
5517 ex = ey = volt = 0.;
5518
5519 // Loop over all wires.
5520 for (int i = nWires; i--;) {
5521 const double xx = HalfPi * (xpos - w[i].x) / sx;
5522 const double yy = HalfPi * (ypos - w[i].y - my * sy) / sx;
5523 const double xxneg = HalfPi * (xpos + w[i].x - 2. * coplan[0]) / sx;
5524 zz = std::complex<double>(xx, yy);
5525 zzneg = std::complex<double>(xxneg, yy);
5526 // Calculate the field in case there are no equipotential planes.
5527 ecompl = 0.;
5528 double r2 = 1.;
5529 if (fabs(yy) <= 20.) {
5530 ecompl = -b2sin[i] / (sin(zz) * sin(zzneg));
5531 if (opt) {
5532 r2 = (pow(sinh(yy), 2) + pow(sin(xx), 2)) /
5533 (pow(sinh(yy), 2) + pow(sin(xxneg), 2));
5534 }
5535 }
5536 // Take care of a plane at constant y.
5537 if (ynplay) {
5538 const double yymirr = (HalfPi / sx) * (ypos + w[i].y - 2. * coplay);
5539 zzmirr = std::complex<double>(xx, yymirr);
5540 zznmirr = std::complex<double>(xxneg, yymirr);
5541 if (fabs(yymirr) <= 20.) {
5542 ecompl += b2sin[i] / (sin(zzmirr) * sin(zznmirr));
5543 if (opt) {
5544 const double r2plan = (pow(sinh(yymirr), 2) + pow(sin(xx), 2)) /
5545 (pow(sinh(yymirr), 2) + pow(sin(xxneg), 2));
5546 r2 /= r2plan;
5547 }
5548 }
5549 }
5550 // Calculate the electric field and potential.
5551 ex += real(sigmat[isw][i]) * real(ecompl);
5552 ey -= real(sigmat[isw][i]) * imag(ecompl);
5553 if (opt) volt -= 0.5 * real(sigmat[isw][i]) * log(r2);
5554 }
5555 ex *= HalfPi / sx;
5556 ey *= HalfPi / sx;
5557}
5558
5559void ComponentAnalyticField::WfieldWireB2Y(const double xpos, const double ypos,
5560 double& ex, double& ey, double& volt,
5561 const int mx, const int isw,
5562 const bool opt) {
5563
5564 //-----------------------------------------------------------------------
5565 // IONB2Y - Routine calculating the MX contribution to the signal on
5566 // wire ISW due to a charge at (XPOS,YPOS) for F-B2X cells.
5567 // VARIABLES : See routine EFCA00 for most of the variables.
5568 // Z,ZZMIRR : X + I*Y , XXMIRR + I*YYMIRR ; I**2=-1
5569 // ECOMPL : EX + I*EY ; I**2=-1
5570 // (Last changed on 20/ 2/90.)
5571 //-----------------------------------------------------------------------
5572
5573 const std::complex<double> icons = std::complex<double>(0., 1.);
5574
5575 std::complex<double> zz, ecompl, zzmirr, zzneg, zznmirr;
5576
5577 // Initialise the electric field and potential.
5578 ex = ey = volt = 0.;
5579
5580 // Loop over all wires.
5581 for (int i = 0; i < nWires; ++i) {
5582 const double xx = HalfPi * (xpos - w[i].x - mx * sx) / sy;
5583 const double yy = HalfPi * (ypos - w[i].y) / sy;
5584 const double yyneg = HalfPi * (ypos + w[i].y - 2. * coplan[2]) / sy;
5585 zz = std::complex<double>(xx, yy);
5586 zzneg = std::complex<double>(xx, yyneg);
5587 // Calculate the field in case there are no equipotential planes.
5588 ecompl = 0.;
5589 double r2 = 1.;
5590 if (fabs(xx) <= 20.) {
5591 ecompl = icons * b2sin[i] / (sin(icons * zz) * sin(icons * zzneg));
5592 if (opt) {
5593 r2 = (pow(sinh(xx), 2) + pow(sin(yy), 2)) /
5594 (pow(sinh(xx), 2) + pow(sin(yyneg), 2));
5595 }
5596 }
5597 // Take care of a plane at constant x.
5598 if (ynplax) {
5599 const double xxmirr = (HalfPi / sy) * (xpos + w[i].x - 2. * coplax);
5600 zzmirr = std::complex<double>(xxmirr, yy);
5601 zznmirr = std::complex<double>(xxmirr, yyneg);
5602 if (fabs(xxmirr) <= 20.) {
5603 ecompl -=
5604 icons * b2sin[i] / (sin(icons * zzmirr) * sin(icons * zznmirr));
5605 if (opt) {
5606 const double r2plan = (pow(sinh(xxmirr), 2) + pow(sin(yy), 2)) /
5607 (pow(sinh(xxmirr), 2) + pow(sin(yyneg), 2));
5608 r2 /= r2plan;
5609 }
5610 }
5611 }
5612 // Calculate the electric field and potential.
5613 ex += real(sigmat[isw][i]) * real(ecompl);
5614 ey -= real(sigmat[isw][i]) * imag(ecompl);
5615 if (opt) volt -= 0.5 * real(sigmat[isw][i]) * log(r2);
5616 }
5617 ex *= HalfPi / sy;
5618 ey *= HalfPi / sy;
5619}
5620
5621void ComponentAnalyticField::WfieldWireC2X(const double xpos, const double ypos,
5622 double& ex, double& ey, double& volt,
5623 const int isw, const bool opt) {
5624
5625 //-----------------------------------------------------------------------
5626 // IONC2X - Routine returning the potential and electric field in a
5627 // configuration with 2 x planes and y periodicity.
5628 // VARIABLES : see the writeup
5629 // (Last changed on 12/10/06.)
5630 //-----------------------------------------------------------------------
5631
5632 const std::complex<double> icons = std::complex<double>(0., 1.);
5633 std::complex<double> zsin, zcof, zu, zunew, zterm1, zterm2, zeta;
5634
5635 // Initial values.
5636 std::complex<double> wsum1 = 0.;
5637 std::complex<double> wsum2 = 0.;
5638 double s = 0.;
5639 volt = 0.;
5640
5641 // Wire loop.
5642 for (int i = 0; i < nWires; ++i) {
5643 // Compute the direct contribution.
5644 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
5645 if (imag(zeta) > 15.) {
5646 wsum1 -= real(sigmat[isw][i]) * icons;
5647 if (opt) volt -= real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5648 } else if (imag(zeta) < -15.) {
5649 wsum1 += real(sigmat[isw][i]) * icons;
5650 if (opt) volt -= real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5651 } else {
5652 zsin = sin(zeta);
5653 zcof = 4. * zsin * zsin - 2.;
5654 zu = -p1 - zcof * p2;
5655 zunew = 1. - zcof * zu - p2;
5656 zterm1 = (zunew + zu) * zsin;
5657 zu = -3. * p1 - zcof * 5. * p2;
5658 zunew = 1. - zcof * zu - 5. * p2;
5659 zterm2 = (zunew - zu) * cos(zeta);
5660 wsum1 += real(sigmat[isw][i]) * (zterm2 / zterm1);
5661 if (opt) volt -= real(sigmat[isw][i]) * log(abs(zterm1));
5662 }
5663 // Find the plane nearest to the wire.
5664 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
5665 // Constant terms sum
5666 s += real(sigmat[isw][i]) * (w[i].x - cx);
5667 // Mirror contribution.
5668 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x, ypos - w[i].y);
5669 if (imag(zeta) > +15.) {
5670 wsum2 -= real(sigmat[isw][i]) * icons;
5671 if (opt) volt += real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5672 } else if (imag(zeta) < -15.) {
5673 wsum2 += real(sigmat[isw][i]) * icons;
5674 if (opt) volt += real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5675 } else {
5676 zsin = sin(zeta);
5677 zcof = 4. * zsin * zsin - 2.;
5678 zu = -p1 - zcof * p2;
5679 zunew = 1. - zcof * zu - p2;
5680 zterm1 = (zunew + zu) * zsin;
5681 zu = -3. * p1 - zcof * 5. * p2;
5682 zunew = 1. - zcof * zu - 5. * p2;
5683 zterm2 = (zunew - zu) * cos(zeta);
5684 wsum2 += real(sigmat[isw][i]) * (zterm2 / zterm1);
5685 if (opt) volt += real(sigmat[isw][i]) * log(abs(zterm1));
5686 }
5687 // Correct the voltage, if needed (MODE).
5688 if (opt && mode == 0) {
5689 volt -= TwoPi * real(sigmat[isw][i]) * (xpos - cx) * (w[i].x - cx) /
5690 (sx * sy);
5691 }
5692 }
5693 // Convert the two contributions to a real field.
5694 ex = real(zmult * (wsum1 + wsum2));
5695 ey = -imag(zmult * (wsum1 - wsum2));
5696 // Constant correction terms.
5697 if (mode == 0) ex += s * TwoPi / (sx * sy);
5698}
5699
5700void ComponentAnalyticField::WfieldWireC2Y(const double xpos, const double ypos,
5701 double& ex, double& ey, double& volt,
5702 const int isw, const bool opt) {
5703
5704 //-----------------------------------------------------------------------
5705 // IONC2Y - Routine returning the potential and electric field in a
5706 // configuration with 2 y planes and x periodicity.
5707 // VARIABLES : see the writeup
5708 // (Last changed on 12/10/06.)
5709 //-----------------------------------------------------------------------
5710
5711 const std::complex<double> icons = std::complex<double>(0., 1.);
5712 std::complex<double> zsin, zcof, zu, zunew, zterm1, zterm2, zeta;
5713
5714 // Initial values.
5715 std::complex<double> wsum1 = 0.;
5716 std::complex<double> wsum2 = 0.;
5717 double s = 0.;
5718 volt = 0.;
5719
5720 // Wire loop.
5721 for (int i = 0; i < nWires; ++i) {
5722 // Compute the direct contribution.
5723 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
5724 if (imag(zeta) > +15.) {
5725 wsum1 -= real(sigmat[isw][i]) * icons;
5726 if (opt) volt -= real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5727 } else if (imag(zeta) < -15.) {
5728 wsum1 += real(sigmat[isw][i]) * icons;
5729 if (opt) volt -= real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5730 } else {
5731 zsin = sin(zeta);
5732 zcof = 4. * zsin * zsin - 2.;
5733 zu = -p1 - zcof * p2;
5734 zunew = 1. - zcof * zu - p2;
5735 zterm1 = (zunew + zu) * zsin;
5736 zu = -3. * p1 - zcof * 5. * p2;
5737 zunew = 1. - zcof * zu - 5. * p2;
5738 zterm2 = (zunew - zu) * cos(zeta);
5739 wsum1 += real(sigmat[isw][i]) * (zterm2 / zterm1);
5740 if (opt) volt -= real(sigmat[isw][i]) * log(abs(zterm1));
5741 }
5742 // Find the plane nearest to the wire.
5743 double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
5744 // Constant terms sum
5745 s += real(sigmat[isw][i]) * (w[i].y - cy);
5746 // Mirror contribution.
5747 zeta = zmult * std::complex<double>(xpos - w[i].x, 2. * cy - ypos - w[i].y);
5748 if (imag(zeta) > +15.) {
5749 wsum2 -= real(sigmat[isw][i]) * icons;
5750 if (opt) volt += real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5751 } else if (imag(zeta) < -15.) {
5752 wsum2 += real(sigmat[isw][i]) * icons;
5753 if (opt) volt += real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5754 } else {
5755 zsin = sin(zeta);
5756 zcof = 4. * zsin * zsin - 2.;
5757 zu = -p1 - zcof * p2;
5758 zunew = 1. - zcof * zu - p2;
5759 zterm1 = (zunew + zu) * zsin;
5760 zu = -3. * p1 - zcof * 5. * p2;
5761 zunew = 1. - zcof * zu - 5. * p2;
5762 zterm2 = (zunew - zu) * cos(zeta);
5763 wsum2 += real(sigmat[isw][i]) * (zterm2 / zterm1);
5764 if (opt) volt += real(sigmat[isw][i]) * log(abs(zterm1));
5765 }
5766 // Correct the voltage, if needed (MODE).
5767 if (opt && mode == 1) {
5768 volt -= TwoPi * real(sigmat[isw][i]) * (ypos - cy) * (w[i].y - cy) /
5769 (sx * sy);
5770 }
5771 }
5772 // Convert the two contributions to a real field.
5773 ex = real(zmult * (wsum1 - wsum2));
5774 ey = -imag(zmult * (wsum1 + wsum2));
5775 // Constant correction terms.
5776 if (mode == 1) ey += s * TwoPi / (sx * sy);
5777}
5778
5779void ComponentAnalyticField::WfieldWireC30(const double xpos, const double ypos,
5780 double& ex, double& ey, double& volt,
5781 const int isw, const bool opt) {
5782
5783 //-----------------------------------------------------------------------
5784 // IONC30 - Routine returning the weighting field field in a
5785 // configuration with 2 y and 2 x planes. This routine is
5786 // basically the same as EFCC30.
5787 // (Last changed on 11/11/97.)
5788 //-----------------------------------------------------------------------
5789
5790 const std::complex<double> icons = std::complex<double>(0., 1.);
5791 std::complex<double> zsin, zcof, zu, zunew, zterm1, zterm2, zeta;
5792
5793 // Initial values.
5794 std::complex<double> wsum1 = 0.;
5795 std::complex<double> wsum2 = 0.;
5796 std::complex<double> wsum3 = 0.;
5797 std::complex<double> wsum4 = 0.;
5798 volt = 0.;
5799
5800 // Wire loop.
5801 for (int i = 0; i < nWires; ++i) {
5802 // Compute the direct contribution.
5803 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
5804 if (imag(zeta) > +15.) {
5805 wsum1 -= real(sigmat[isw][i]) * icons;
5806 if (opt) volt -= real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5807 } else if (imag(zeta) < -15.) {
5808 wsum1 += real(sigmat[isw][i]) * icons;
5809 if (opt) volt -= real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5810 } else {
5811 zsin = sin(zeta);
5812 zcof = 4. * zsin * zsin - 2.;
5813 zu = -p1 - zcof * p2;
5814 zunew = 1. - zcof * zu - p2;
5815 zterm1 = (zunew + zu) * zsin;
5816 zu = -3. * p1 - zcof * 5. * p2;
5817 zunew = 1. - zcof * zu - 5. * p2;
5818 zterm2 = (zunew - zu) * cos(zeta);
5819 wsum1 += real(sigmat[isw][i]) * (zterm2 / zterm1);
5820 if (opt) volt -= real(sigmat[isw][i]) * log(abs(zterm1));
5821 }
5822 // Find the plane nearest to the wire.
5823 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
5824 // Mirror contribution from the x plane.
5825 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x, ypos - w[i].y);
5826 if (imag(zeta) > +15.) {
5827 wsum2 -= real(sigmat[isw][i]) * icons;
5828 if (opt) volt += real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5829 } else if (imag(zeta) < -15.) {
5830 wsum2 += real(sigmat[isw][i]) * icons;
5831 if (opt) volt += real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5832 } else {
5833 zsin = sin(zeta);
5834 zcof = 4. * zsin * zsin - 2.;
5835 zu = -p1 - zcof * p2;
5836 zunew = 1. - zcof * zu - p2;
5837 zterm1 = (zunew + zu) * zsin;
5838 zu = -3. * p1 - zcof * 5. * p2;
5839 zunew = 1. - zcof * zu - 5. * p2;
5840 zterm2 = (zunew - zu) * cos(zeta);
5841 wsum2 += real(sigmat[isw][i]) * (zterm2 / zterm1);
5842 if (opt) volt += real(sigmat[isw][i]) * log(abs(zterm1));
5843 }
5844 // Find the plane nearest to the wire.
5845 double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
5846 // Mirror contribution from the y plane.
5847 zeta = zmult * std::complex<double>(xpos - w[i].x, 2. * cy - ypos - w[i].y);
5848 if (imag(zeta) > +15.) {
5849 wsum3 -= real(sigmat[isw][i]) * icons;
5850 if (opt) volt += real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5851 } else if (imag(zeta) < -15.) {
5852 wsum3 += real(sigmat[isw][i]) * icons;
5853 if (opt) volt += real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5854 } else {
5855 zsin = sin(zeta);
5856 zcof = 4. * zsin * zsin - 2.;
5857 zu = -p1 - zcof * p2;
5858 zunew = 1. - zcof * zu - p2;
5859 zterm1 = (zunew + zu) * zsin;
5860 zu = -3. * p1 - zcof * 5. * p2;
5861 zunew = 1. - zcof * zu - 5. * p2;
5862 zterm2 = (zunew - zu) * cos(zeta);
5863 wsum3 += real(sigmat[isw][i]) * (zterm2 / zterm1);
5864 if (opt) volt += real(sigmat[isw][i]) * log(abs(zterm1));
5865 }
5866 // Mirror contribution from both the x and the y plane.
5867 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x,
5868 2. * cy - ypos - w[i].y);
5869 if (imag(zeta) > +15.) {
5870 wsum4 -= real(sigmat[isw][i]) * icons;
5871 if (opt) volt -= real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5872 } else if (imag(zeta) < -15.) {
5873 wsum4 += real(sigmat[isw][i]) * icons;
5874 if (opt) volt -= real(sigmat[isw][i]) * (fabs(imag(zeta)) - CLog2);
5875 } else {
5876 zsin = sin(zeta);
5877 zcof = 4. * zsin * zsin - 2.;
5878 zu = -p1 - zcof * p2;
5879 zunew = 1. - zcof * zu - p2;
5880 zterm1 = (zunew + zu) * zsin;
5881 zu = -3. * p1 - zcof * 5. * p2;
5882 zunew = 1. - zcof * zu - 5. * p2;
5883 zterm2 = (zunew - zu) * cos(zeta);
5884 wsum4 += real(sigmat[isw][i]) * (zterm2 / zterm1);
5885 if (opt) volt -= real(sigmat[isw][i]) * log(abs(zterm1));
5886 }
5887 }
5888 // Convert the two contributions to a real field.
5889 ex = real(zmult * (wsum1 + wsum2 - wsum3 - wsum4));
5890 ey = -imag(zmult * (wsum1 - wsum2 + wsum3 - wsum4));
5891}
5892
5893void ComponentAnalyticField::WfieldWireD10(const double xpos, const double ypos,
5894 double& ex, double& ey, double& volt,
5895 const int isw, const bool opt) {
5896
5897 //-----------------------------------------------------------------------
5898 // IOND10 - Subroutine computing the signal on wire ISW due to a charge
5899 // at (XPOS,YPOS). This is effectively routine EFCD10.
5900 // VARIABLES : EX, EY, VOLT:Electric field and potential.
5901 // ETOT, VOLT : Magnitude of electric field, potential.
5902 // (XPOS,YPOS): The position where the field is calculated.
5903 // ZI, ZPOS : Shorthand complex notations.
5904 // (Last changed on 2/ 2/93.)
5905 //-----------------------------------------------------------------------
5906
5907 // Initialise the electric field and potential.
5908 ex = ey = volt = 0.;
5909
5910 // Set the complex position coordinates.
5911 std::complex<double> zpos = std::complex<double>(xpos, ypos);
5912 std::complex<double> zi;
5913 std::complex<double> wi;
5914
5915 // Loop over all wires.
5916 for (int i = nWires; i--;) {
5917 // Set the complex version of the wire-coordinate for simplicity.
5918 zi = std::complex<double>(w[i].x, w[i].y);
5919 // Compute the contribution to the potential, if needed.
5920 if (opt) {
5921 volt -=
5922 real(sigmat[isw][i]) *
5923 log(abs(cotube * (zpos - zi) / (cotube * cotube - zpos * conj(zi))));
5924 }
5925 // Compute the contribution to the electric field.
5926 wi = 1. / conj(zpos - zi) + zi / (cotube * cotube - conj(zpos) * zi);
5927 ex += real(sigmat[isw][i]) * real(wi);
5928 ey += real(sigmat[isw][i]) * imag(wi);
5929 }
5930}
5931
5932void ComponentAnalyticField::WfieldWireD30(const double xpos, const double ypos,
5933 double& ex, double& ey, double& volt,
5934 const int isw, const bool opt) {
5935
5936 //-----------------------------------------------------------------------
5937 // IOND30 - Subroutine computing the weighting field for a polygonal
5938 // cells without periodicities, type D3.
5939 // VARIABLES : EX, EY :Electric field
5940 // (XPOS,YPOS): The position where the field is calculated.
5941 // ZI, ZPOS : Shorthand complex notations.
5942 // (Last changed on 19/ 6/97.)
5943 //-----------------------------------------------------------------------
5944
5945 // Initialise the electric field and potential.
5946 ex = ey = volt = 0.;
5947
5948 std::complex<double> whelp;
5949
5950 // Get the mapping of the position.
5951 std::complex<double> wpos, wdpos;
5952 ConformalMap(std::complex<double>(xpos, ypos) / cotube, wpos, wdpos);
5953 // Loop over all wires.
5954 for (int i = nWires; i--;) {
5955 // Compute the contribution to the potential, if needed.
5956 if (opt) {
5957 volt -= real(sigmat[isw][i]) *
5958 log(abs((wpos - wmap[i]) / (1. - wpos * conj(wmap[i]))));
5959 }
5960 // Compute the contribution to the electric field.
5961 whelp = wdpos * (1. - pow(abs(wmap[i]), 2)) /
5962 ((wpos - wmap[i]) * (1. - conj(wmap[i]) * wpos));
5963 ex += real(sigmat[isw][i]) * real(whelp);
5964 ey -= real(sigmat[isw][i]) * imag(whelp);
5965 }
5966 ex /= cotube;
5967 ey /= cotube;
5968}
5969
5970void ComponentAnalyticField::WfieldPlaneA00(const double xpos,
5971 const double ypos, double& ex,
5972 double& ey, double& volt,
5973 const int mx, const int my,
5974 const int iplane, const bool opt) {
5975
5976 //-----------------------------------------------------------------------
5977 // IPLA00 - Routine returning the A I,J [MX,MY] * E terms for A cells.
5978 // VARIABLES : R2 : Potential before taking -Log(Sqrt(...))
5979 // EX,EY : x,y-Component of the electric field.
5980 // EXHELP ETC : One term in the summing series.
5981 // (XPOS,YPOS): Position where the field is needed.
5982 // (Last changed on 9/11/98.)
5983 //-----------------------------------------------------------------------
5984
5985 // Initialise the electric field and potential.
5986 ex = ey = volt = 0.;
5987
5988 double xxmirr = 0., yymirr = 0.;
5989 // Loop over all wires.
5990 for (int i = nWires; i--;) {
5991 // Define a few reduced variables.
5992 const double xx = xpos - w[i].x - mx * sx;
5993 const double yy = ypos - w[i].y - my * sy;
5994 // Calculate the field in case there are no planes.
5995 double r2 = xx * xx + yy * yy;
5996 if (r2 <= 0.) continue;
5997 double exhelp = xx / r2;
5998 double eyhelp = yy / r2;
5999 // Take care of a planes at constant x.
6000 if (ynplax) {
6001 xxmirr = xpos + w[i].x - 2 * coplax;
6002 const double r2plan = xxmirr * xxmirr + yy * yy;
6003 if (r2plan <= 0.) continue;
6004 exhelp -= xxmirr / r2plan;
6005 eyhelp -= yy / r2plan;
6006 r2 /= r2plan;
6007 }
6008 // Take care of a plane at constant y.
6009 if (ynplay) {
6010 yymirr = ypos + w[i].y - 2 * coplay;
6011 const double r2plan = xx * xx + yymirr * yymirr;
6012 if (r2plan <= 0.) continue;
6013 exhelp -= xx / r2plan;
6014 eyhelp -= yymirr / r2plan;
6015 r2 /= r2plan;
6016 }
6017 // Take care of pairs of planes.
6018 if (ynplax && ynplay) {
6019 const double r2plan = xxmirr * xxmirr + yymirr * yymirr;
6020 if (r2plan <= 0.) continue;
6021 exhelp += xxmirr / r2plan;
6022 eyhelp += yymirr / r2plan;
6023 r2 *= r2plan;
6024 }
6025 // Calculate the electric field and potential.
6026 if (opt) volt -= 0.5 * qplane[iplane][i] * log(r2);
6027 ex += qplane[iplane][i] * exhelp;
6028 ey += qplane[iplane][i] * eyhelp;
6029 }
6030}
6031
6032void ComponentAnalyticField::WfieldPlaneB2X(const double xpos,
6033 const double ypos, double& ex,
6034 double& ey, double& volt,
6035 const int my, const int iplane,
6036 const bool opt) {
6037
6038 //-----------------------------------------------------------------------
6039 // IPLB2X - Routine calculating the MY contribution to the signal on
6040 // wire IPLANE due to a charge at (XPOS,YPOS) for F-B2Y cells.
6041 // VARIABLES : See routine EFCA00 for most of the variables.
6042 // Z,ZZMIRR : X + I*Y , XXMIRR + I*YYMIRR ; I**2=-1
6043 // ECOMPL : EX + I*EY ; I**2=-1
6044 // (Last changed on 12/11/98.)
6045 //-----------------------------------------------------------------------
6046
6047 std::complex<double> zz, ecompl, zzmirr, zzneg, zznmirr;
6048
6049 // Initialise ex, ey and volt.
6050 ex = ey = volt = 0.;
6051 // Loop over all wires.
6052 for (int i = nWires; i--;) {
6053 const double xx = HalfPi * (xpos - w[i].x) / sx;
6054 const double yy = HalfPi * (ypos - w[i].y - my * sy) / sx;
6055 const double xxneg = HalfPi * (xpos + w[i].x - 2 * coplan[0]) / sx;
6056 zz = std::complex<double>(xx, yy);
6057 zzneg = std::complex<double>(xxneg, yy);
6058 // Calculate the field in case there are no equipotential planes.
6059 ecompl = 0.;
6060 double r2 = 1.;
6061 if (fabs(yy) <= 20.) {
6062 ecompl = -b2sin[i] / (sin(zz) * sin(zzneg));
6063 if (opt) {
6064 r2 = (pow(sinh(yy), 2) + pow(sin(xx), 2)) /
6065 (pow(sinh(yy), 2) + pow(sin(xxneg), 2));
6066 }
6067 }
6068 // Take care of a plane at constant y.
6069 if (ynplay) {
6070 const double yymirr = (HalfPi / sx) * (ypos + w[i].y - 2 * coplay);
6071 zzmirr = std::complex<double>(yy, yymirr);
6072 zznmirr = std::complex<double>(xxneg, yymirr);
6073 if (fabs(yymirr) <= 20.) {
6074 ecompl += b2sin[i] / (sin(zzmirr) * sin(zznmirr));
6075 if (opt) {
6076 const double r2plan = (pow(sinh(yymirr), 2) + pow(sin(xx), 2)) /
6077 (pow(sinh(yymirr), 2) + pow(sin(xxneg), 2));
6078 r2 /= r2plan;
6079 }
6080 }
6081 }
6082 // Calculate the electric field.
6083 ex += qplane[iplane][i] * real(ecompl);
6084 ey -= qplane[iplane][i] * imag(ecompl);
6085 if (opt) volt -= 0.5 * qplane[iplane][i] * log(r2);
6086 }
6087 ex *= (HalfPi / sx);
6088 ey *= (HalfPi / sx);
6089}
6090
6091void ComponentAnalyticField::WfieldPlaneB2Y(const double xpos,
6092 const double ypos, double& ex,
6093 double& ey, double& volt,
6094 const int mx, const int iplane,
6095 const bool opt) {
6096
6097 //-----------------------------------------------------------------------
6098 // IPLB2Y - Routine calculating the MX contribution to the signal on
6099 // wire IPLANE due to a charge at (XPOS,YPOS) for F-B2X cells.
6100 // VARIABLES : See routine EFCA00 for most of the variables.
6101 // Z,ZZMIRR : X + I*Y , XXMIRR + I*YYMIRR ; I**2=-1
6102 // ECOMPL : EX + I*EY ; I**2=-1
6103 // (Last changed on 12/11/98.)
6104 //-----------------------------------------------------------------------
6105
6106 const std::complex<double> icons = std::complex<double>(0., 1.);
6107
6108 std::complex<double> zz, ecompl, zzmirr, zzneg, zznmirr;
6109
6110 // Initialise ex, ey and volt.
6111 ex = ey = volt = 0.;
6112 // Loop over all wires.
6113 for (int i = nWires; i--;) {
6114 const double xx = HalfPi * (xpos - w[i].x - mx * sx) / sy;
6115 const double yy = HalfPi * (ypos - w[i].y) / sy;
6116 const double yyneg = HalfPi * (ypos + w[i].y - 2 * coplan[2]) / sy;
6117 zz = std::complex<double>(xx, yy);
6118 zzneg = std::complex<double>(xx, yyneg);
6119 // Calculate the field in case there are no equipotential planes.
6120 ecompl = 0.;
6121 double r2 = 1.;
6122 if (fabs(xx) <= 20.) {
6123 ecompl = icons * b2sin[i] / (sin(icons * zz) * sin(icons * zzneg));
6124 if (opt) {
6125 r2 = (pow(sinh(xx), 2) + pow(sin(yy), 2)) /
6126 (pow(sinh(xx), 2) + pow(sin(yyneg), 2));
6127 }
6128 }
6129 // Take care of a plane at constant y.
6130 if (ynplax) {
6131 const double xxmirr = (HalfPi / sy) * (xpos + w[i].x - 2 * coplax);
6132 zzmirr = std::complex<double>(xxmirr, yy);
6133 zznmirr = std::complex<double>(xxmirr, yyneg);
6134 if (fabs(xxmirr) <= 20.) {
6135 ecompl -= b2sin[i] / (sin(icons * zzmirr) * sin(icons * zznmirr));
6136 if (opt) {
6137 const double r2plan = (pow(sinh(xxmirr), 2) + pow(sin(yy), 2)) /
6138 (pow(sinh(xxmirr), 2) + pow(sin(yyneg), 2));
6139 r2 /= r2plan;
6140 }
6141 }
6142 }
6143 // Calculate the electric field and potential.
6144 ex += qplane[iplane][i] * real(ecompl);
6145 ey -= qplane[iplane][i] * imag(ecompl);
6146 if (opt) volt -= 0.5 * qplane[iplane][i] * log(r2);
6147 }
6148 ex *= HalfPi / sy;
6149 ey *= HalfPi / sy;
6150}
6151
6152void ComponentAnalyticField::WfieldPlaneC2X(const double xpos,
6153 const double ypos, double& ex,
6154 double& ey, double& volt,
6155 const int iplane, const bool opt) {
6156
6157 //-----------------------------------------------------------------------
6158 // IPLC2X - Routine returning the potential and electric field in a
6159 // configuration with 2 x planes and y periodicity.
6160 // VARIABLES : see the writeup
6161 // (Last changed on 12/10/06.)
6162 //-----------------------------------------------------------------------
6163
6164 const std::complex<double> icons = std::complex<double>(0., 1.);
6165
6166 std::complex<double> zsin, zcof, zu, zunew, zterm1, zterm2, zeta;
6167 // Initial values.
6168 std::complex<double> wsum1 = 0.;
6169 std::complex<double> wsum2 = 0.;
6170 double s = 0.;
6171 volt = 0.;
6172
6173 // Wire loop.
6174 for (int i = 0; i < nWires; ++i) {
6175 // Compute the direct contribution.
6176 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
6177 if (imag(zeta) > +15.) {
6178 wsum1 -= qplane[iplane][i] * icons;
6179 if (opt) volt -= qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6180 } else if (imag(zeta) < -15.) {
6181 wsum1 += qplane[iplane][i] * icons;
6182 if (opt) volt -= qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6183 } else {
6184 zsin = sin(zeta);
6185 zcof = 4. * zsin * zsin - 2.;
6186 zu = -p1 - zcof * p2;
6187 zunew = 1. - zcof * zu - p2;
6188 zterm1 = (zunew + zu) * zsin;
6189 zu = -3. * p1 - zcof * 5. * p2;
6190 zunew = 1. - zcof * zu - 5. * p2;
6191 zterm2 = (zunew - zu) * cos(zeta);
6192 wsum1 += qplane[iplane][i] * (zterm2 / zterm1);
6193 if (opt) volt -= qplane[iplane][i] * log(abs(zterm1));
6194 }
6195 // Find the plane nearest to the wire.
6196 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
6197 // Constant terms sum
6198 s += qplane[iplane][i] * (w[i].x - cx);
6199 // Mirror contribution.
6200 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x, ypos - w[i].y);
6201 if (imag(zeta) > 15.) {
6202 wsum2 -= qplane[iplane][i] * icons;
6203 if (opt) volt += qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6204 } else if (imag(zeta) < -15.) {
6205 wsum2 += qplane[iplane][i] * icons;
6206 if (opt) volt += qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6207 } else {
6208 zsin = sin(zeta);
6209 zcof = 4. * zsin * zsin - 2.;
6210 zu = -p1 - zcof * p2;
6211 zunew = 1. - zcof * zu - p2;
6212 zterm1 = (zunew + zu) * zsin;
6213 zu = -3. * p1 - zcof * 5. * p2;
6214 zunew = 1. - zcof * zu - 5. * p2;
6215 zterm2 = (zunew - zu) * cos(zeta);
6216 wsum2 += qplane[iplane][i] * (zterm2 / zterm1);
6217 if (opt) volt += qplane[iplane][i] * log(abs(zterm1));
6218 }
6219 if (opt && mode == 0) {
6220 volt -=
6221 TwoPi * qplane[iplane][i] * (xpos - cx) * (w[i].x - cx) / (sx * sy);
6222 }
6223 }
6224 // Convert the two contributions to a real field.
6225 ex = real(zmult * (wsum1 + wsum2));
6226 ey = -imag(zmult * (wsum1 - wsum2));
6227 // Constant correction terms.
6228 if (mode == 0) ex += s * TwoPi / (sx * sy);
6229}
6230
6231void ComponentAnalyticField::WfieldPlaneC2Y(const double xpos,
6232 const double ypos, double& ex,
6233 double& ey, double& volt,
6234 const int iplane, const bool opt) {
6235
6236 //-----------------------------------------------------------------------
6237 // IPLC2Y - Routine returning the potential and electric field in a
6238 // configuration with 2 y planes and x periodicity.
6239 // VARIABLES : see the writeup
6240 // (Last changed on 12/10/06.)
6241 //-----------------------------------------------------------------------
6242
6243 const std::complex<double> icons = std::complex<double>(0., 1.);
6244
6245 std::complex<double> zsin, zcof, zu, zunew, zterm1, zterm2, zeta;
6246 // Initial values.
6247 std::complex<double> wsum1 = 0.;
6248 std::complex<double> wsum2 = 0.;
6249 double s = 0.;
6250 volt = 0.;
6251
6252 // Wire loop.
6253 for (int i = 0; i < nWires; ++i) {
6254 // Compute the direct contribution.
6255 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
6256 if (imag(zeta) > +15.) {
6257 wsum1 -= qplane[iplane][i] * icons;
6258 if (opt) volt -= qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6259 } else if (imag(zeta) < -15.) {
6260 wsum1 += qplane[iplane][i] * icons;
6261 if (opt) volt -= qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6262 } else {
6263 zsin = sin(zeta);
6264 zcof = 4. * zsin * zsin - 2.;
6265 zu = -p1 - zcof * p2;
6266 zunew = 1. - zcof * zu - p2;
6267 zterm1 = (zunew + zu) * zsin;
6268 zu = -3. * p1 - zcof * 5. * p2;
6269 zunew = 1. - zcof * zu - 5. * p2;
6270 zterm2 = (zunew - zu) * cos(zeta);
6271 wsum1 += qplane[iplane][i] * (zterm2 / zterm1);
6272 if (opt) volt -= qplane[iplane][i] * log(abs(zterm1));
6273 }
6274 // Find the plane nearest to the wire.
6275 double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
6276 // Constant terms sum
6277 s += qplane[iplane][i] * (w[i].y - cy);
6278 // Mirror contribution.
6279 zeta = zmult * std::complex<double>(xpos - w[i].x, 2. * cy - ypos - w[i].y);
6280 if (imag(zeta) > 15.) {
6281 wsum2 -= qplane[iplane][i] * icons;
6282 if (opt) volt += qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6283 } else if (imag(zeta) < -15.) {
6284 wsum2 += qplane[iplane][i] * icons;
6285 if (opt) volt += qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6286 } else {
6287 zsin = sin(zeta);
6288 zcof = 4. * zsin * zsin - 2.;
6289 zu = -p1 - zcof * p2;
6290 zunew = 1. - zcof * zu - p2;
6291 zterm1 = (zunew + zu) * zsin;
6292 zu = -3. * p1 - zcof * 5. * p2;
6293 zunew = 1. - zcof * zu - 5. * p2;
6294 zterm2 = (zunew - zu) * cos(zeta);
6295 wsum2 += qplane[iplane][i] * (zterm2 / zterm1);
6296 if (opt) volt += qplane[iplane][i] * log(abs(zterm1));
6297 }
6298 // Correct the voltage, if needed (MODE).
6299 if (opt && mode == 1) {
6300 volt -=
6301 TwoPi * qplane[iplane][i] * (ypos - cy) * (w[i].y - cy) / (sx * sy);
6302 }
6303 }
6304 // Convert the two contributions to a real field.
6305 ex = real(zmult * (wsum1 - wsum2));
6306 ey = -imag(zmult * (wsum1 + wsum2));
6307 // Constant correction terms.
6308 if (mode == 1) ey += s * TwoPi / (sx * sy);
6309}
6310
6311void ComponentAnalyticField::WfieldPlaneC30(const double xpos,
6312 const double ypos, double& ex,
6313 double& ey, double& volt,
6314 const int iplane, const bool opt) {
6315
6316 //-----------------------------------------------------------------------
6317 // IPLC30 - Routine returning the weighting field field in a
6318 // configuration with 2 y and 2 x planes. This routine is
6319 // basically the same as EFCC30.
6320 // (Last changed on 9/11/98.)
6321 //-----------------------------------------------------------------------
6322
6323 const std::complex<double> icons = std::complex<double>(0., 1.);
6324
6325 std::complex<double> zsin, zcof, zu, zunew, zterm1, zterm2, zeta;
6326 // Initial values.
6327 std::complex<double> wsum1 = 0.;
6328 std::complex<double> wsum2 = 0.;
6329 std::complex<double> wsum3 = 0.;
6330 std::complex<double> wsum4 = 0.;
6331 volt = 0.;
6332
6333 // Wire loop.
6334 for (int i = 0; i < nWires; ++i) {
6335 // Compute the direct contribution.
6336 zeta = zmult * std::complex<double>(xpos - w[i].x, ypos - w[i].y);
6337 if (imag(zeta) > +15.) {
6338 wsum1 -= qplane[iplane][i] * icons;
6339 if (opt) volt -= qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6340 } else if (imag(zeta) < -15.) {
6341 wsum1 += qplane[iplane][i] * icons;
6342 if (opt) volt -= qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6343 } else {
6344 zsin = sin(zeta);
6345 zcof = 4. * zsin * zsin - 2.;
6346 zu = -p1 - zcof * p2;
6347 zunew = 1. - zcof * zu - p2;
6348 zterm1 = (zunew + zu) * zsin;
6349 zu = -3. * p1 - zcof * 5. * p2;
6350 zunew = 1. - zcof * zu - 5. * p2;
6351 zterm2 = (zunew - zu) * cos(zeta);
6352 wsum1 += qplane[iplane][i] * zterm2 / zterm1;
6353 if (opt) volt -= qplane[iplane][i] * log(abs(zterm1));
6354 }
6355 // Find the plane nearest to the wire.
6356 double cx = coplax - sx * int(round((coplax - w[i].x) / sx));
6357 // Mirror contribution from the x plane.
6358 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x, ypos - w[i].y);
6359 if (imag(zeta) > 15.) {
6360 wsum2 -= qplane[iplane][i] * icons;
6361 if (opt) volt += qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6362 } else if (imag(zeta) < -15.) {
6363 wsum2 += qplane[iplane][i] * icons;
6364 if (opt) volt += qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6365 } else {
6366 zsin = sin(zeta);
6367 zcof = 4. * zsin * zsin - 2.;
6368 zu = -p1 - zcof * p2;
6369 zunew = 1. - zcof * zu - p2;
6370 zterm1 = (zunew + zu) * zsin;
6371 zu = -3. * p1 - zcof * 5. * p2;
6372 zunew = 1. - zcof * zu - 5. * p2;
6373 zterm2 = (zunew - zu) * cos(zeta);
6374 wsum2 += qplane[iplane][i] * zterm2 / zterm1;
6375 if (opt) volt += qplane[iplane][i] * log(abs(zterm1));
6376 }
6377 // Find the plane nearest to the wire.
6378 double cy = coplay - sy * int(round((coplay - w[i].y) / sy));
6379 // Mirror contribution from the y plane.
6380 zeta = zmult * std::complex<double>(xpos - w[i].x, 2. * cy - ypos - w[i].y);
6381 if (imag(zeta) > 15.) {
6382 wsum3 -= qplane[iplane][i] * icons;
6383 if (opt) volt += qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6384 } else if (imag(zeta) < -15.) {
6385 wsum3 += qplane[iplane][i] * icons;
6386 if (opt) volt += qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6387 } else {
6388 zsin = sin(zeta);
6389 zcof = 4. * zsin * zsin - 2.;
6390 zu = -p1 - zcof * p2;
6391 zunew = 1. - zcof * zu - p2;
6392 zterm1 = (zunew + zu) * zsin;
6393 zu = -3. * p1 - zcof * 5. * p2;
6394 zunew = 1. - zcof * zu - 5. * p2;
6395 zterm2 = (zunew - zu) * cos(zeta);
6396 wsum3 += qplane[iplane][i] * zterm2 / zterm1;
6397 if (opt) volt += qplane[iplane][i] * log(abs(zterm1));
6398 }
6399 // Mirror contribution from both the x and the y plane.
6400 zeta = zmult * std::complex<double>(2. * cx - xpos - w[i].x,
6401 2. * cy - ypos - w[i].y);
6402 if (imag(zeta) > 15.) {
6403 wsum4 -= qplane[iplane][i] * icons;
6404 if (opt) volt -= qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6405 } else if (imag(zeta) < -15.) {
6406 wsum4 += qplane[iplane][i] * icons;
6407 if (opt) volt -= qplane[iplane][i] * (fabs(imag(zeta)) - CLog2);
6408 } else {
6409 zsin = sin(zeta);
6410 zcof = 4. * zsin * zsin - 2.;
6411 zu = -p1 - zcof * p2;
6412 zunew = 1. - zcof * zu - p2;
6413 zterm1 = (zunew + zu) * zsin;
6414 zu = -3. * p1 - zcof * 5. * p2;
6415 zunew = 1. - zcof * zu - 5. * p2;
6416 zterm2 = (zunew - zu) * cos(zeta);
6417 wsum4 += qplane[iplane][i] * zterm2 / zterm1;
6418 if (opt) volt -= qplane[iplane][i] * log(abs(zterm1));
6419 }
6420 }
6421 ex = real(zmult * (wsum1 + wsum2 - wsum3 - wsum4));
6422 ey = -imag(zmult * (wsum1 - wsum2 + wsum3 - wsum4));
6423}
6424
6425void ComponentAnalyticField::WfieldPlaneD10(const double xpos,
6426 const double ypos, double& ex,
6427 double& ey, double& volt,
6428 const int iplane, const bool opt) {
6429
6430 //-----------------------------------------------------------------------
6431 // IPLD10 - Subroutine computing the signal on wire IPLANE due to a
6432 // charge at (XPOS,YPOS). This is effectively routine EFCD10.
6433 // VARIABLES : EX, EY : Electric field.
6434 // (XPOS,YPOS): The position where the field is calculated.
6435 // ZI, ZPOS : Shorthand complex notations.
6436 // (Last changed on 9/11/98.)
6437 //-----------------------------------------------------------------------
6438
6439 // Initialise the electric field and potential.
6440 ex = ey = volt = 0.;
6441
6442 // Set the complex position coordinates.
6443 std::complex<double> zpos = std::complex<double>(xpos, ypos);
6444 std::complex<double> zi;
6445 std::complex<double> wi;
6446
6447 // Loop over all wires.
6448 for (int i = nWires; i--;) {
6449 // Set the complex version of the wire-coordinate for simplicity.
6450 zi = std::complex<double>(w[i].x, w[i].y);
6451 // Compute the contribution to the potential, if needed.
6452 if (opt) {
6453 volt -=
6454 qplane[iplane][i] *
6455 log(abs(cotube * (zpos - zi) / (cotube * cotube - zpos * conj(zi))));
6456 }
6457 // Compute the contribution to the electric field.
6458 wi = 1. / conj(zpos - zi) + zi / (cotube * cotube - conj(zpos) * zi);
6459 ex += qplane[iplane][i] * real(wi);
6460 ey += qplane[iplane][i] * imag(wi);
6461 }
6462}
6463
6464void ComponentAnalyticField::WfieldPlaneD30(const double xpos,
6465 const double ypos, double& ex,
6466 double& ey, double& volt,
6467 const int iplane, const bool opt) {
6468
6469 //-----------------------------------------------------------------------
6470 // IPLD30 - Subroutine computing the weighting field for a polygonal
6471 // cells without periodicities, type D3.
6472 // VARIABLES : EX, EY : Electric field
6473 // (XPOS,YPOS): The position where the field is calculated.
6474 // ZI, ZPOS : Shorthand complex notations.
6475 // (Last changed on 9/11/98.)
6476 //-----------------------------------------------------------------------
6477
6478 // Initialise the weighting field and potential.
6479 ex = ey = volt = 0.;
6480
6481 std::complex<double> whelp;
6482
6483 // Get the mapping of the position.
6484 std::complex<double> wpos, wdpos;
6485 ConformalMap(std::complex<double>(xpos, ypos) / cotube, wpos, wdpos);
6486 // Loop over all wires.
6487 for (int i = 0; i < nWires; ++i) {
6488 // Compute the contribution to the potential, if needed.
6489 if (opt) {
6490 volt -= qplane[iplane][i] *
6491 log(abs((wpos - wmap[i]) / (1. - wpos * conj(wmap[i]))));
6492 }
6493 // Compute the contribution to the electric field.
6494 whelp = wdpos * (1. - pow(abs(wmap[i]), 2)) /
6495 ((wpos - wmap[i]) * (1. - conj(wmap[i]) * wpos));
6496 ex += qplane[iplane][i] * real(whelp);
6497 ey -= qplane[iplane][i] * imag(whelp);
6498 }
6499 ex /= cotube;
6500 ey /= cotube;
6501}
6502
6503void ComponentAnalyticField::WfieldStripZ(const double xpos, const double ypos,
6504 double& ex, double& ey, double& volt,
6505 const int ip, const int is,
6506 const bool opt) {
6507
6508 //-----------------------------------------------------------------------
6509 // IONEST - Weighting field for strips.
6510 // (Last changed on 6/12/00.)
6511 //-----------------------------------------------------------------------
6512
6513 // Initialise the weighting field and potential.
6514 ex = ey = volt = 0.;
6515
6516 strip theStrip = planes[ip].strips2[is];
6517 // Transform to normalised coordinates.
6518 double xw = 0., yw = 0.;
6519 switch (ip) {
6520 case 0:
6521 xw = -ypos + (theStrip.smin + theStrip.smax) / 2.;
6522 yw = xpos - coplan[ip];
6523 break;
6524 case 1:
6525 xw = ypos - (theStrip.smin + theStrip.smax) / 2.;
6526 yw = coplan[ip] - xpos;
6527 break;
6528 case 2:
6529 xw = xpos - (theStrip.smin + theStrip.smax) / 2.;
6530 yw = ypos - coplan[ip];
6531 break;
6532 case 3:
6533 xw = -xpos + (theStrip.smin + theStrip.smax) / 2.;
6534 yw = coplan[ip] - ypos;
6535 break;
6536 default:
6537 return;
6538 }
6539 // Store the gap and strip halfwidth.
6540 const double w = fabs(theStrip.smax - theStrip.smin) / 2.;
6541 const double g = theStrip.gap;
6542
6543 // Make sure we are in the fiducial part of the weighting map.
6544 if (yw <= 0. || yw > g) return;
6545
6546 // Define shorthand notations.
6547 const double s = sin(Pi * yw / g);
6548 const double c = cos(Pi * yw / g);
6549 const double e1 = exp(Pi * (w - xw) / g);
6550 const double e2 = exp(-Pi * (w + xw) / g);
6551 const double ce12 = pow(c - e1, 2);
6552 const double ce22 = pow(c - e2, 2);
6553 // Check for singularities.
6554 if (c == e1 || c == e2) return;
6555 // Evaluate the potential, if requested.
6556 if (opt) {
6557 volt = atan((c - e2) / s) - atan((c - e1) / s);
6558 volt /= Pi;
6559 }
6560 // Evaluate the field.
6561 const double ewx = (s / g) * (e1 / (ce12 + s * s) - e2 / (ce22 + s * s));
6562 const double ewy = ((c / (c - e2) + s * s / ce22) / (1. + s * s / ce22) -
6563 (c / (c - e1) + s * s / ce12) / (1. + s * s / ce12)) /
6564 g;
6565
6566 // Rotate the field back to the original coordinates.
6567 switch (ip) {
6568 case 0:
6569 ex = ewy;
6570 ey = -ewx;
6571 break;
6572 case 1:
6573 ex = -ewy;
6574 ey = ewx;
6575 break;
6576 case 2:
6577 ex = ewx;
6578 ey = ewy;
6579 break;
6580 case 3:
6581 ex = -ewx;
6582 ey = -ewy;
6583 break;
6584 }
6585}
6586
6587void ComponentAnalyticField::WfieldStripXy(const double xpos, const double ypos,
6588 const double zpos, double& ex,
6589 double& ey, double& ez, double& volt,
6590 const int ip, const int is,
6591 const bool opt) {
6592
6593 //-----------------------------------------------------------------------
6594 // IONEST - Weighting field for strips.
6595 // (Last changed on 6/12/00.)
6596 //-----------------------------------------------------------------------
6597
6598 // Initialise the weighting field and potential.
6599 ex = ey = ez = volt = 0.;
6600
6601 strip theStrip = planes[ip].strips1[is];
6602 // Transform to normalised coordinates.
6603 double xw = 0., yw = 0.;
6604 switch (ip) {
6605 case 0:
6606 xw = -zpos + (theStrip.smin + theStrip.smax) / 2.;
6607 yw = xpos - coplan[ip];
6608 break;
6609 case 1:
6610 xw = zpos - (theStrip.smin + theStrip.smax) / 2.;
6611 yw = coplan[ip] - xpos;
6612 break;
6613 case 2:
6614 xw = zpos - (theStrip.smin + theStrip.smax) / 2.;
6615 yw = ypos - coplan[ip];
6616 break;
6617 case 3:
6618 xw = -zpos + (theStrip.smin + theStrip.smax) / 2.;
6619 yw = coplan[ip] - ypos;
6620 break;
6621 default:
6622 return;
6623 }
6624
6625 // Store the gap and strip halfwidth.
6626 const double w = fabs(theStrip.smax - theStrip.smin) / 2.;
6627 const double g = theStrip.gap;
6628
6629 // Make sure we are in the fiducial part of the weighting map.
6630 if (yw <= 0. || yw > g) return;
6631
6632 // Define shorthand notations.
6633 const double s = sin(Pi * yw / g);
6634 const double c = cos(Pi * yw / g);
6635 const double e1 = exp(Pi * (w - xw) / g);
6636 const double e2 = exp(-Pi * (w + xw) / g);
6637 const double ce12 = pow(c - e1, 2);
6638 const double ce22 = pow(c - e2, 2);
6639 // Check for singularities.
6640 if (c == e1 || c == e2) return;
6641 // Evaluate the potential, if requested.
6642 if (opt) {
6643 volt = atan((c - e2) / s) - atan((c - e1) / s);
6644 volt /= Pi;
6645 }
6646 // Evaluate the field.
6647 const double ewx = (s / g) * (e1 / (ce12 + s * s) - e2 / (ce22 + s * s));
6648 const double ewy = ((c / (c - e2) + s * s / ce22) / (1. + s * s / ce22) -
6649 (c / (c - e1) + s * s / ce12) / (1. + s * s / ce12)) /
6650 g;
6651
6652 // Rotate the field back to the original coordinates.
6653 switch (ip) {
6654 case 0:
6655 ex = ewy;
6656 ey = 0.;
6657 ez = -ewx;
6658 break;
6659 case 1:
6660 ex = -ewy;
6661 ey = 0.;
6662 ez = ewx;
6663 break;
6664 case 2:
6665 ex = 0.;
6666 ey = ewy;
6667 ez = ewx;
6668 break;
6669 case 3:
6670 ex = 0.;
6671 ey = -ewy;
6672 ez = -ewx;
6673 break;
6674 }
6675}
6676}
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 exp(const DoubleAc &f)
Definition: DoubleAc.cpp:376
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:616
bool GetVoltageRange(double &pmin, double &pmax)
void AddPlaneX(const double x, const double voltage, const std::string label)
void AddWire(const double x, const double y, const double diameter, const double voltage, const std::string label, const double length=100., const double tension=50., const double rho=19.3, const int ntrap=5)
bool IsInTrapRadius(double x0, double y0, double z0, double &xw, double &yx, double &rw)
void AddCharge(const double x, const double y, const double z, const double q)
void AddReadout(const std::string label)
void AddStripOnPlaneX(const char direction, const double x, const double smin, const double smax, const std::string label, const double gap=-1.)
void AddTube(const double radius, const double voltage, const int nEdges, const std::string label)
bool IsWireCrossed(double x0, double y0, double z0, double x1, double y1, double z1, double &xc, double &yc, double &zc)
bool GetPlaneX(const int i, double &x, double &voltage, std::string &label)
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string label)
void AddStripOnPlaneY(const char direction, const double y, const double smin, const double smax, const std::string label, const double gap=-1.)
bool GetBoundingBox(double &x0, double &y0, double &z0, double &x1, double &y1, double &z1)
void AddPlaneY(const double y, const double voltage, const std::string label)
double WeightingPotential(const double x, const double y, const double z, const std::string label)
bool GetWire(const int i, double &x, double &y, double &diameter, double &voltage, std::string &label, double &length, double &charge, int &ntrap)
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status)
bool GetPlaneY(const int i, double &y, double &voltage, std::string &label)
bool GetTube(double &r, double &voltage, int &nEdges, std::string &label)
GeometryBase * theGeometry
virtual Medium * GetMedium(const double &x, const double &y, const double &z)
virtual bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)=0
bool IsDriftable() const
Definition: Medium.hh:57
double BesselK0L(const double xx)
Definition: Numerics.hh:58
double BesselK1S(const double xx)
Definition: Numerics.hh:65
void Cinv(const int n, std::vector< std::vector< std::complex< double > > > &a, int &ifail)
Definition: Numerics.cc:494
double BesselK0S(const double xx)
Definition: Numerics.hh:51
void Deqinv(const int n, std::vector< std::vector< double > > &a, int &ifail, std::vector< double > &b)
Definition: Numerics.cc:216
double BesselK1L(const double xx)
Definition: Numerics.hh:73