Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentParallelPlate.cc
Go to the documentation of this file.
2
3#include <TF1.h>
4#include <TF2.h>
5
6#include <algorithm>
7#include <cmath>
8#include <iostream>
9#include <limits>
10
12
13namespace Garfield {
14
16
17void ComponentParallelPlate::Setup(const int N, std::vector<double> eps,
18 std::vector<double> d, const double V,
19 std::vector<int> sigmaIndex) {
20
21 // Here I switch conventions with the z-axis the direction of drift.
22 std::vector<double> placeHolder(N + 1, 0);
23
24 const int Nholder1 = eps.size();
25 const int Nholder2 = d.size();
26 if (N != Nholder1 || N != Nholder2) {
27 std::cout << m_className
28 << "::Inconsistency between the number of layers, permittivities "
29 "and thicknesses given.\n";
30 return;
31 } else if (N < 2) {
32
33 std::cout << m_className
34 << "::Setup:: Number of layers must be larger then 1.\n";
35 return;
36 }
37
38 if (m_debug) std::cout << m_className << "::Setup:: Loading parameters.\n";
39 m_epsHolder = eps;
40 m_eps = placeHolder;
41
42 m_dHolder = d;
43 m_d = placeHolder;
44 m_N = N + 1;
45 m_V = V;
46
47 if (sigmaIndex.size() == 0) {
48 for (int i = 0; i < N; i++) {
49 if (eps[i] != 1) sigmaIndex.push_back(i + 1);
50 }
51 }
52
53 m_sigmaIndex = sigmaIndex;
54
55 std::vector<double> m_zHolder(N + 1);
56 m_zHolder[0] = 0;
57 for (int i = 1; i <= N; i++) {
58 m_zHolder[i] = m_zHolder[i - 1] + m_dHolder[i - 1];
59
60 if (m_debug)
61 std::cout << m_className << "Setup:: layer " << i
62 << ":: z = " << m_zHolder[i]
63 << ", epsr = " << m_epsHolder[i - 1] << ".\n";
64 }
65 m_z = m_zHolder;
66
67 if (m_debug) std::cout << m_className << "Setup:: Constructing matrices.\n";
68 constructGeometryMatrices(m_N);
69
70 if (m_debug)
71 std::cout << m_className
72 << "Setup:: Computing weighting potential functions.\n";
73 setHIntegrand();
74 setwpStripIntegrand();
75 setwpPixelIntegrand();
76
77 std::cout << m_className << "Setup:: Geometry with N = " << N
78 << " layers set.\n";
79}
80
81bool ComponentParallelPlate::GetBoundingBox(double &x0, double &y0, double &z0,
82 double &x1, double &y1,
83 double &z1) {
84 // If a geometry is present, try to get the bounding box from there.
85
86 // Here I switch conventions back with the y-axis the direction of drift.
87 if (m_geometry) {
88 if (m_geometry->GetBoundingBox(x0, y0, z0, x1, y1, z1)) return true;
89 }
90 z0 = -std::numeric_limits<double>::infinity();
91 x0 = -std::numeric_limits<double>::infinity();
92 z1 = +std::numeric_limits<double>::infinity();
93 x1 = +std::numeric_limits<double>::infinity();
94
95 y0 = 0.;
96 y1 = m_z.back();
97 return true;
98}
99
100double ComponentParallelPlate::IntegratePromptPotential(const Electrode &el,
101 const double x,
102 const double y,
103 const double z) {
104 switch (el.ind) {
105 case structureelectrode::Plane: {
106 return wpPlane(z);
107 break;
108 }
109 case structureelectrode::Pixel: {
110 m_wpPixelIntegral.SetParameters(x, y, el.xpos, el.ypos, el.lx, el.ly,
111 z); //(x,y,x0,y0,lx,ly,z)
112 int im;
113 double epsm = 0.;
114 getLayer(z, im, epsm);
115 if (!m_getPotentialInPlate && epsm != 1) return 0.;
116 double upLim = m_upperBoundIntegration;
117 if (z == 0 || m_upperBoundIntegration / z > 200) {
118 upLim = 200;
119 } else {
120 upLim *= 1 / z;
121 }
122 return m_wpPixelIntegral.Integral(0, upLim, 0, upLim, m_precision);
123 break;
124 }
125 case structureelectrode::Strip: {
126 m_wpStripIntegral.SetParameters(x, el.xpos, el.lx, z); //(x,x0,lx,z)
127 int im;
128 double epsm = 0.;
129 getLayer(z, im, epsm);
130 if (!m_getPotentialInPlate && epsm != 1) return 0.;
131 double upLim = m_upperBoundIntegration;
132 if (z == 0 || m_upperBoundIntegration / z > 200) {
133 upLim = 200;
134 } else {
135 upLim *= 1 / z;
136 }
137 return m_wpStripIntegral.Integral(0, upLim, m_precision);
138 break;
139 }
140 default: {
141 std::cerr << m_className << "::IntegratePromptPotential:\n"
142 << " Unknown electrode type.\n";
143 return 0.;
144 }
145 }
146}
147
148void ComponentParallelPlate::ElectricField(const double x, const double y,
149 const double z, double &ex,
150 double &ey, double &ez, Medium *&m,
151 int &status) {
152 // Here I switch conventions back with the y-axis the direction of drift.
153
154 ex = ey = ez = 0;
155
156 int im = -1;
157 double epsM = -1;
158 if (!getLayer(y, im, epsM)) {
159 if (m_debug)
160 std::cout << m_className << "::ElectricField: Not inside geometry.\n";
161 status = -6;
162 return;
163 }
164
165 ey = constEFieldLayer(im);
166
167 m = m_geometry ? m_geometry->GetMedium(x, y, z) : m_medium;
168
169 if (!m) {
170 if (m_debug) {
171 std::cout << m_className << "::ElectricField: No medium at (" << x << ", "
172 << y << ", " << z << ").\n";
173 }
174 status = -6;
175 return;
176 }
177
178 if (epsM == 1) {
179 status = 0;
180 } else {
181 status = -5;
182 }
183}
184
185void ComponentParallelPlate::ElectricField(const double x, const double y,
186 const double z, double &ex,
187 double &ey, double &ez, double &v,
188 Medium *&m, int &status) {
189 // Here I switch conventions back with the y-axis the direction of drift.
190
191 ex = ey = ez = v = 0;
192
193 int im = -1;
194 double epsM = -1;
195 if (!getLayer(y, im, epsM)) {
196 if (m_debug)
197 std::cout << m_className << "::ElectricField: Not inside geometry.\n";
198 status = -6;
199 return;
200 }
201
202 ey = constEFieldLayer(im);
203
204 v = -m_V - (y - m_z[im - 1]) * constEFieldLayer(im);
205 for (int i = 1; i <= im - 1; i++) {
206 v -= (m_z[i] - m_z[i - 1]) * constEFieldLayer(i);
207 }
208
209 m = m_geometry ? m_geometry->GetMedium(x, y, z) : m_medium;
210
211 if (!m) {
212 if (m_debug) {
213 std::cout << m_className << "::ElectricField: No medium at (" << x << ", "
214 << y << ", " << z << ").\n";
215 }
216 status = -6;
217 return;
218 }
219
220 if (epsM == 1) {
221 status = 0;
222 } else {
223 status = -5;
224 }
225}
226
227bool ComponentParallelPlate::GetVoltageRange(double &vmin, double &vmax) {
228 if (m_V == 0) return false;
229
230 if (m_V < 0) {
231 vmin = m_V;
232 vmax = 0;
233 } else {
234 vmin = 0;
235 vmax = m_V;
236 }
237 return true;
238}
239
241 const double y,
242 const double z,
243 const std::string &label) {
244
245 // Here I switch conventions back with the y-axis the direction of drift.
246
247 double ret = 0.;
248
249 for (auto &electrode : m_readout_p) {
250 if (electrode.label == label) {
251 double yin = y;
252 if (!electrode.formAnode) yin = m_z.back() - y;
253 if (!electrode.m_usegrid) {
254 ret += IntegratePromptPotential(electrode, z, x, yin);
255 } else {
256 ret += FindWeightingPotentialInGrid(electrode, z, x, yin);
257 }
258 }
259 }
260 return ret;
261}
262
263void ComponentParallelPlate::Reset() {
264 m_readout.clear();
265 m_readout_p.clear();
266
267 m_cMatrix.clear();
268 m_vMatrix.clear();
269 m_gMatrix.clear();
270 m_wMatrix.clear();
271
272 m_sigmaIndex.clear();
273 m_eps.clear();
274 m_d.clear();
275 m_z.clear();
276
277 m_N = 0;
278 m_V = 0;
279
280 m_medium = nullptr;
281}
282
283void ComponentParallelPlate::UpdatePeriodicity() {
284 if (m_debug) {
285 std::cerr << m_className << "::UpdatePeriodicity:\n"
286 << " Periodicities are not supported.\n";
287 }
288}
289
290void ComponentParallelPlate::AddPixel(double x, double z, double lx_input,
291 double lz_input, const std::string &label,
292 bool anode) {
293
294 // Here I switch conventions back with the y-axis the direction of drift.
295
296 const auto it = std::find(m_readout.cbegin(), m_readout.cend(), label);
297 if (it != m_readout.end() && m_readout.size() > 0) {
298 std::cerr << m_className << "::AddPixel:\n"
299 << "Note that the label " << label << " is already in use.\n";
300 }
301 Electrode pixel;
302 pixel.label = label;
303 pixel.ind = structureelectrode::Pixel;
304 pixel.xpos = z;
305 pixel.ypos = x;
306 pixel.lx = lz_input;
307 pixel.ly = lx_input;
308
309 pixel.formAnode = anode;
310
311 m_readout.push_back(label);
312 m_readout_p.push_back(std::move(pixel));
313 std::cout << m_className << "::AddPixel: Added pixel electrode.\n";
314}
315
316void ComponentParallelPlate::AddStrip(double z, double lz_input,
317 const std::string &label, bool anode) {
318 const auto it = std::find(m_readout.cbegin(), m_readout.cend(), label);
319 if (it != m_readout.end() && m_readout.size() > 0) {
320 std::cerr << m_className << "::AddStrip:\n"
321 << "Note that the label " << label << " is already in use.\n";
322 }
323 Electrode strip;
324 strip.label = label;
325 strip.ind = structureelectrode::Strip;
326 strip.xpos = z;
327 strip.lx = lz_input;
328
329 strip.formAnode = anode;
330
331 m_readout.push_back(label);
332 m_readout_p.push_back(std::move(strip));
333
334 std::cout << m_className << "::AddStrip: Added strip electrode.\n";
335}
336
337void ComponentParallelPlate::AddPlane(const std::string &label, bool anode) {
338 const auto it = std::find(m_readout.cbegin(), m_readout.cend(), label);
339 if (it != m_readout.end() && m_readout.size() > 0) {
340 std::cerr << m_className << "::AddPlane:\n"
341 << "Note that the label " << label << " is already in use.\n";
342 }
343 Electrode plate;
344 plate.label = label;
345 plate.ind = structureelectrode::Plane;
346
347 plate.formAnode = anode;
348
349 m_readout.push_back(label);
350 m_readout_p.push_back(std::move(plate));
351
352 std::cout << m_className << "::AddPlane: Added plane electrode.\n";
353}
354
355Medium *ComponentParallelPlate::GetMedium(const double x, const double y,
356 const double z) {
357 if (m_geometry) {
358 return m_geometry->GetMedium(x, y, z);
359 } else if (m_medium) {
360 return m_medium;
361 }
362 return nullptr;
363}
364
365bool ComponentParallelPlate::Nsigma(
366 int N, std::vector<std::vector<int>> &sigmaMatrix) {
367 int nCol = N - 1;
368 int nRow = pow(2, N - 1);
369 // array to store binary number
370 std::vector<int> binaryNum(nCol, 0);
371
372 for (int i = 0; i < nRow; i++) {
373 if (decToBinary(i, binaryNum)) {
374 sigmaMatrix.push_back(binaryNum);
375 std::reverse(sigmaMatrix[i].begin(), sigmaMatrix[i].end());
376 std::for_each(sigmaMatrix[i].begin(), sigmaMatrix[i].end(),
377 [](int &n) { n = 1 - 2 * n; });
378 }
379 }
380 return true;
381}
382
383bool ComponentParallelPlate::Ntheta(
384 int N, std::vector<std::vector<int>> &thetaMatrix,
385 std::vector<std::vector<int>> &sigmaMatrix) {
386 int nCol = N - 1;
387 int nRow = pow(2, N - 1);
388
389 std::vector<int> thetaRow(nCol, 1);
390 std::vector<int> thetaRowReset(nCol, 1);
391
392 for (int i = 0; i < nRow; i++) {
393 for (int j = 0; j < nCol; j++) {
394 for (int l = j; l < nCol; l++) thetaRow[j] *= sigmaMatrix[i][l];
395 }
396 thetaMatrix.push_back(thetaRow);
397 thetaRow = thetaRowReset;
398 }
399 return true;
400}
401
402void ComponentParallelPlate::constructGeometryMatrices(const int N) {
403
404 int nRow = N;
405
406 std::vector<std::vector<int>> sigmaMatrix;
407 std::vector<std::vector<int>> thetaMatrix;
408
409 for (int n = 1; n <= nRow; n++) {
410 // building sigma and theta matrices
411 Nsigma(n, sigmaMatrix);
412 Ntheta(n, thetaMatrix, sigmaMatrix);
413 // store solution for row n-1
414 m_sigmaMatrix.push_back(sigmaMatrix);
415 m_thetaMatrix.push_back(thetaMatrix);
416 // reset
417 sigmaMatrix.clear();
418 thetaMatrix.clear();
419 }
420}
421
422void ComponentParallelPlate::constructGeometryFunction(const int N) {
423
424 int nRow = N;
425 int nCol = pow(2, N - 1);
426 // reset
427 m_cMatrix.clear();
428 m_vMatrix.clear();
429 m_gMatrix.clear();
430 m_wMatrix.clear();
431
432 std::vector<double> cHold(nCol, 1);
433 std::vector<double> vHold(nCol, 0);
434 std::vector<double> gHold(nCol, 1);
435 std::vector<double> wHold(nCol, 0);
436
437 for (int n = 1; n <= nRow; n++) {
438 int ix1 = 0;
439 int ix2 = 0;
440
441 for (int i = 0; i < nCol; i++) {
442 // cyclic permutation over the rows of sigma
443 if (ix1 == pow(2, n - 1)) ix1 = 0;
444 if (ix2 == pow(2, N - n)) ix2 = 0;
445 // normalization
446 cHold[i] *= 1 / pow(2, n - 1);
447 gHold[i] *= 1 / pow(2, N - n);
448 // summation for c and v
449 if (n > 1) {
450 for (int j = 0; j < n - 1; j++) {
451 cHold[i] *= (m_eps[j] + m_sigmaMatrix[n - 1][ix1][j] * m_eps[j + 1]) /
452 m_eps[j + 1];
453 vHold[i] += (m_thetaMatrix[n - 1][ix1][j] - 1) * m_d[j];
454 }
455 }
456 // summation for g and w
457 for (int j = 0; j < N - n; j++) {
458 gHold[i] *= (m_eps[N - j - 1] +
459 m_sigmaMatrix[N - n][ix2][j] * m_eps[N - j - 2]) /
460 m_eps[N - j - 2];
461 wHold[i] += (m_thetaMatrix[N - n][ix2][j] - 1) * m_d[N - 1 - j];
462 }
463 ix1++;
464 ix2++;
465 }
466
467 // store solution for row n
468 m_cMatrix.push_back(cHold);
469 m_vMatrix.push_back(vHold);
470 m_gMatrix.push_back(gHold);
471 m_wMatrix.push_back(wHold);
472
473 // reset
474 std::fill(cHold.begin(), cHold.end(), 1);
475 std::fill(vHold.begin(), vHold.end(), 0);
476 std::fill(gHold.begin(), gHold.end(), 1);
477 std::fill(wHold.begin(), wHold.end(), 0);
478 }
479}
480
481void ComponentParallelPlate::setHIntegrand() {
482#if __cplusplus > 201703L // C++20 or newer
483 auto hFunction = [=,this](double *k, double * /*p*/) {
484#else
485 auto hFunction = [=](double *k, double * /*p*/) {
486#endif
487 double kk = k[0];
488 double z = k[1];
489
490 double hNorm = 0;
491 double h = 0;
492
493 int im = -1;
494 double epsM = -1;
495 if (!getLayer(z, im, epsM)) return 0.;
496 LayerUpdate(z, im, epsM);
497
498 for (int i = 0; i < pow(2, m_N - im - 1); i++) {
499 h += m_gMatrix[im][i] * sinh(kk * (m_wMatrix[im][i] + m_z.back() - z));
500 }
501 for (int i = 0; i < pow(2, m_N - 1); i++) {
502 hNorm += m_cMatrix[m_N - 1][i] *
503 sinh(kk * (m_vMatrix[m_N - 1][i] + m_z.back()));
504 }
505 return h * m_eps[0] / (m_eps[m_N - 1] * hNorm);
506 };
507 TF2 *hF = new TF2("hFunction", hFunction, 0, m_upperBoundIntegration, 0,
508 m_z.back(), 0);
509
510 hF->Copy(m_hIntegrand);
511
512 delete hF;
513}
514
515void ComponentParallelPlate::setwpPixelIntegrand() {
516
517#if __cplusplus > 201703L // C++20 or newer
518 auto intFunction = [=,this](double *k, double *p) {
519#else
520 auto intFunction = [=](double *k, double *p) {
521#endif
522 double kx = k[0];
523 double ky = k[1];
524
525 double K = sqrt(kx * kx + ky * ky);
526
527 double x = p[0];
528 double y = p[1];
529 double x0 = p[2];
530 double y0 = p[3];
531 double wx = p[4];
532 double wy = p[5];
533 double z = p[6];
534
535 double sol = cos(kx * (x - x0)) * sin(kx * wx / 2) * cos(ky * (y - y0)) *
536 sin(ky * wy / 2) * m_hIntegrand.Eval(K, z) / (kx * ky);
537
538 return 4 * sol / (Pi * Pi);
539 };
540
541 TF2 *wpPixelIntegrand =
542 new TF2("wpPixelIntegrand", intFunction, 0, m_upperBoundIntegration, 0,
543 m_upperBoundIntegration, 7);
544 wpPixelIntegrand->SetNpx(
545 10000); // increasing number of points the function is evaluated on
546 wpPixelIntegrand->SetNpy(10000);
547 wpPixelIntegrand->Copy(m_wpPixelIntegral);
548
549 delete wpPixelIntegrand;
550}
551
552void ComponentParallelPlate::setwpStripIntegrand() {
553
554#if __cplusplus > 201703L // C++20 or newer
555 auto intFunction = [=,this](double *k, double *p) {
556#else
557 auto intFunction = [=](double *k, double *p) {
558#endif
559 double kk = k[0];
560 double x = p[0];
561 double x0 = p[1];
562 double wx = p[2];
563 double z = p[3];
564 double sol =
565 cos(kk * (x - x0)) * sin(kk * wx / 2) * m_hIntegrand.Eval(kk, z) / kk;
566 return 2 * sol / Pi;
567 };
568 TF1 *wpStripIntegrand =
569 new TF1("wpStripIntegrand", intFunction, 0, m_upperBoundIntegration, 4);
570 wpStripIntegrand->SetNpx(
571 1000); // increasing number of points the function is evaluated on
572 wpStripIntegrand->Copy(m_wpStripIntegral);
573
574 delete wpStripIntegrand;
575}
576
577bool ComponentParallelPlate::decToBinary(int n, std::vector<int> &binaryNum) {
578 int L = binaryNum.size();
579 // counter for binary array
580 int i = 0;
581 while (n > 0) {
582 if (i + 1 > L) {
583 std::cerr << m_className
584 << "::decToBinary: Size of binary exceeds amount of colomb.\n";
585 return false; // Triggered if binary expression is larger then n.
586 }
587 // storing remainder in binary array
588 binaryNum[i] = n % 2;
589 n = n / 2;
590 i++;
591 }
592 return true; // Succesfully
593}
594
596 const double xmin, const double xmax, const double xsteps,
597 const double ymin, const double ymax, const double ysteps,
598 const double zmin, const double zmax, const double zsteps,
599 const std::string &label) {
600
601 for (auto &electrode : m_readout_p) {
602 if (electrode.label == label) {
603 if (electrode.m_usegrid) {
604 std::cerr << m_className
605 << "::SetWeightingPotentialGrid: Overwriting grid.\n";
606 }
607
608 if (electrode.grid.SetMesh(xsteps, ysteps, zsteps, xmin, xmax, ymin, ymax,
609 zmin, zmax)) {
610 std::cerr << m_className << "::SetWeightingPotentialGrid: Mesh set for "
611 << label << " (for " << xsteps *ysteps *zsteps
612 << " points).\n";
613 }
614
615 electrode.grid.SaveWeightingField(this, label, label + "map", "xyz");
616
618 }
619 }
620}
621
623 const double xmin, const double xmax, const double xsteps,
624 const double ymin, const double ymax, const double ysteps,
625 const double zmin, const double zmax, const double zsteps) {
626
627 for (auto &electrode : m_readout_p) {
628 SetWeightingPotentialGrid(xmin, xmax, xsteps, ymin, ymax, ysteps, zmin,
629 zmax, zsteps, electrode.label);
630 }
631}
632
633double ComponentParallelPlate::FindWeightingPotentialInGrid(Electrode &el,
634 const double x,
635 const double y,
636 const double z) {
637 return el.grid.WeightingPotential(y, z, x, el.label);
638}
639
640} // namespace Garfield
void AddPlane(const std::string &label, bool fromAnode=true)
void AddStrip(double z, double lz, const std::string &label, bool fromAnode=true)
Add strip electrode.
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
void LoadWeightingPotentialGrid(const std::string &label)
void SetWeightingPotentialGrids(const double xmin, const double xmax, const double xsteps, const double ymin, const double ymax, const double ysteps, const double zmin, const double zmax, const double zsteps)
void Setup(const int N, std::vector< double > eps, std::vector< double > d, const double V, std::vector< int > sigmaIndex={})
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status) override
void SetWeightingPotentialGrid(const double xmin, const double xmax, const double xsteps, const double ymin, const double ymax, const double ysteps, const double zmin, const double zmax, const double zsteps, const std::string &label)
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
bool getLayer(const double y, int &m, double &epsM)
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
void AddPixel(double x, double z, double lx, double lz, const std::string &label, bool fromAnode=true)
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
bool m_debug
Switch on/off debugging messages.
Definition Component.hh:371
Component()=delete
Default constructor.
std::string m_className
Class name.
Definition Component.hh:359
Geometry * m_geometry
Pointer to the geometry.
Definition Component.hh:362
Abstract base class for media.
Definition Medium.hh:16
DoubleAc cos(const DoubleAc &f)
Definition DoubleAc.cpp:432
DoubleAc pow(const DoubleAc &f, double p)
Definition DoubleAc.cpp:337
DoubleAc sin(const DoubleAc &f)
Definition DoubleAc.cpp:384
DoubleAc sqrt(const DoubleAc &f)
Definition DoubleAc.cpp:314