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