Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentGrid.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <bitset>
3#include <cmath>
4#include <cstdio>
5#include <fstream>
6#include <iostream>
7#include <limits>
8#include <set>
9#include <sstream>
10
13#include "Garfield/Utilities.hh"
14
15namespace {
16
17void PrintError(const std::string& fcn, const unsigned int line,
18 const std::string& par) {
19 std::cerr << fcn << ": Error reading line " << line << ".\n"
20 << " Could not read " << par << ".\n";
21}
22
23void PrintNotReady(const std::string& fcn) {
24 std::cerr << fcn << ": Map not available.\n";
25}
26
27void PrintProgress(const double f) {
28 if (f < 0.) return;
29 constexpr unsigned int width = 70;
30 const unsigned int n = static_cast<unsigned int>(std::floor(width * f));
31 std::string bar = "[";
32 if (n < 1) {
33 bar += std::string(width, ' ');
34 } else if (n >= width) {
35 bar += std::string(width, '=');
36 } else {
37 bar += std::string(n, '=') + ">" + std::string(width - n - 1, ' ');
38 }
39 bar += "]";
40 std::cout << bar << "\r" << std::flush;
41}
42
43bool IsComment(const std::string& line) {
44 if (line.empty()) return false;
45 if (line[0] == '#') return true;
46 if (line.size() > 1 && (line[0] == '/' && line[1] == '/')) return true;
47 return false;
48}
49
50} // namespace
51
52namespace Garfield {
53
55
56void ComponentGrid::ElectricField(const double x, const double y,
57 const double z, double& ex, double& ey,
58 double& ez, double& p, Medium*& m,
59 int& status) {
60 m = nullptr;
61 status = 0;
62
63 // Make sure the field map has been loaded.
64 if (m_efields.empty()) {
65 PrintNotReady(m_className + "::ElectricField");
66 status = -10;
67 return;
68 }
69
70 status = 0;
71 bool active = true;
72 if (!GetField(x, y, z, m_efields, ex, ey, ez, p, active)) {
73 status = -11;
74 return;
75 }
76 if (!active) {
77 status = -5;
78 return;
79 }
80 m = m_medium;
81 if (!m) status = -5;
82}
83
84void ComponentGrid::ElectricField(const double x, const double y,
85 const double z, double& ex, double& ey,
86 double& ez, Medium*& m, int& status) {
87 double v = 0.;
88 ElectricField(x, y, z, ex, ey, ez, v, m, status);
89}
90
91void ComponentGrid::WeightingField(const double x, const double y,
92 const double z, double& wx, double& wy,
93 double& wz, const std::string& /*label*/) {
94 wx = wy = wz = 0.;
95 if (m_wfields.empty()) return;
96 const double xx = x - m_wFieldOffset[0];
97 const double yy = y - m_wFieldOffset[1];
98 const double zz = z - m_wFieldOffset[2];
99 double wp = 0.;
100 bool active = true;
101 GetField(xx, yy, zz, m_wfields, wx, wy, wz, wp, active);
102}
103
104double ComponentGrid::WeightingPotential(const double x, const double y,
105 const double z,
106 const std::string& /*label*/) {
107 if (m_wfields.empty()) return 0.;
108 const double xx = x - m_wFieldOffset[0];
109 const double yy = y - m_wFieldOffset[1];
110 const double zz = z - m_wFieldOffset[2];
111 double wx = 0., wy = 0., wz = 0.;
112 double wp = 0.;
113 bool active = true;
114 if (!GetField(xx, yy, zz, m_wfields, wx, wy, wz, wp, active)) return 0.;
115 return wp;
116}
117
118void ComponentGrid::DelayedWeightingField(const double x, const double y,
119 const double z, const double t,
120 double& wx, double& wy, double& wz,
121 const std::string& /*label*/) {
122 wx = wy = wz = 0.;
123 if (m_wdtimes.empty()) return;
124 // Assume no weighting field for times outside the range of available maps.
125 if (t < m_wdtimes.front() || t > m_wdtimes.back()) return;
126
127 const double xx = x - m_wFieldOffset[0];
128 const double yy = y - m_wFieldOffset[1];
129 const double zz = z - m_wFieldOffset[2];
130
131 const auto it1 = std::upper_bound(m_wdtimes.cbegin(), m_wdtimes.cend(), t);
132 const auto it0 = std::prev(it1);
133
134 const double dt = t - *it0;
135 double wp = 0.;
136 const unsigned int i0 = it0 - m_wdtimes.cbegin();
137 double wx0 = 0., wy0 = 0., wz0 = 0.;
138 bool active = true;
139 if (!GetField(xx, yy, zz, m_wdfields[i0], wx0, wy0, wz0, wp, active)) return;
140
141 if (dt < Small || it1 == m_wdtimes.cend()) {
142 wx = wx0;
143 wy = wy0;
144 wz = wz0;
145 return;
146 }
147 const unsigned int i1 = it1 - m_wdtimes.cbegin();
148 double wx1 = 0., wy1 = 0., wz1 = 0.;
149 if (!GetField(xx, yy, zz, m_wdfields[i1], wx1, wy1, wz1, wp, active)) return;
150
151 const double f1 = dt / (*it1 - *it0);
152 const double f0 = 1. - f1;
153 wx = f0 * wx0 + f1 * wx1;
154 wy = f0 * wy0 + f1 * wy1;
155 wz = f0 * wz0 + f1 * wz1;
156}
157
159 const double x, const double y, const double z, const double t,
160 const std::string& /*label*/) {
161
162 if (m_wdtimes.empty()) return 0.;
163 // Outside the range of the available maps?
164 if (t < m_wdtimes.front() || t > m_wdtimes.back()) return 0.;
165
166 const double xx = x - m_wFieldOffset[0];
167 const double yy = y - m_wFieldOffset[1];
168 const double zz = z - m_wFieldOffset[2];
169
170 const auto it1 = std::upper_bound(m_wdtimes.cbegin(), m_wdtimes.cend(), t);
171 const auto it0 = std::prev(it1);
172
173 const double dt = t - *it0;
174 const unsigned int i0 = it0 - m_wdtimes.cbegin();
175 double wp0 = 0., wx0 = 0., wy0 = 0., wz0 = 0.;
176 bool active = true;
177 if (!GetField(xx, yy, zz, m_wdfields[i0], wx0, wy0, wz0, wp0, active)) return 0.;
178
179 if (dt < Small || it1 == m_wdtimes.cend()) return wp0;
180
181 const unsigned int i1 = it1 - m_wdtimes.cbegin();
182 double wp1 = 0., wx1 = 0., wy1 = 0., wz1 = 0.;
183 if (!GetField(xx, yy, zz, m_wdfields[i1], wx1, wy1, wz1, wp1, active)) return 0.;
184
185 const double f1 = dt / (*it1 - *it0);
186 const double f0 = 1. - f1;
187 return f0 * wp0 + f1 * wp1;
188}
189
190void ComponentGrid::SetWeightingFieldOffset(const double x, const double y,
191 const double z) {
192 m_wFieldOffset = {x, y, z};
193}
194
195void ComponentGrid::MagneticField(const double x, const double y,
196 const double z, double& bx, double& by,
197 double& bz, int& status) {
198 status = 0;
199 if (m_bfields.empty()) {
200 return Component::MagneticField(x, y, z, bx, by, bz, status);
201 }
202
203 double p = 0.;
204 bool active = true;
205 if (!GetField(x, y, z, m_bfields, bx, by, bz, p, active)) {
206 status = -11;
207 }
208}
209
211 return m_bfields.empty() ? Component::HasMagneticField() : true;
212}
213
214Medium* ComponentGrid::GetMedium(const double x, const double y,
215 const double z) {
216 // Make sure the field map has been loaded.
217 if (m_efields.empty()) {
218 PrintNotReady(m_className + "::GetMedium");
219 return nullptr;
220 }
221
222 std::array<double, 3> xx = {x, y, z};
223 if (m_coordinates == Coordinates::Cylindrical) {
224 if (fabs(x) > Small || fabs(y) > Small) {
225 xx[0] = sqrt(x * x + y * y);
226 xx[1] = atan2(y, x);
227 }
228 }
229 for (size_t i = 0; i < 3; ++i) {
230 if (m_periodic[i] || m_mirrorPeriodic[i]) continue;
231 if (xx[i] < m_xMin[i] || xx[i] > m_xMax[i]) return nullptr;
232 }
233 if (m_active.empty()) return m_medium;
234 for (size_t i = 0; i < 3; ++i) {
235 bool mirrored = false;
236 xx[i] = Reduce(xx[i], m_xMin[i], m_xMax[i],
237 m_periodic[i], m_mirrorPeriodic[i], mirrored);
238 }
239 // Get the indices.
240 const double sx = (xx[0] - m_xMin[0]) * m_sX[0];
241 const double sy = (xx[1] - m_xMin[1]) * m_sX[1];
242 const double sz = (xx[2] - m_xMin[2]) * m_sX[2];
243 const unsigned int i0 = static_cast<unsigned int>(std::floor(sx));
244 const unsigned int j0 = static_cast<unsigned int>(std::floor(sy));
245 const unsigned int k0 = static_cast<unsigned int>(std::floor(sz));
246 const unsigned int i1 = std::min(i0 + 1, m_nX[0] - 1);
247 const unsigned int j1 = std::min(j0 + 1, m_nX[1] - 1);
248 const unsigned int k1 = std::min(k0 + 1, m_nX[2] - 1);
249 if (m_active[i0][j0][k0] && m_active[i0][j0][k1] && m_active[i0][j1][k0] &&
250 m_active[i0][j1][k1] && m_active[i1][j0][k0] && m_active[i1][j0][k1] &&
251 m_active[i1][j1][k0] && m_active[i1][j1][k1]) {
252 return m_medium;
253 }
254 return nullptr;
255}
256
257bool ComponentGrid::SetMesh(const unsigned int nx, const unsigned int ny,
258 const unsigned int nz, const double xmin,
259 const double xmax, const double ymin,
260 const double ymax, const double zmin,
261 const double zmax) {
262 Reset();
263 if (nx == 0 || ny == 0 || nz == 0) {
264 std::cerr << m_className << "::SetMesh:\n"
265 << " Number of mesh elements must be positive.\n";
266 return false;
267 }
268 if (xmin >= xmax) {
269 std::cerr << m_className << "::SetMesh: Invalid x range.\n";
270 return false;
271 } else if (ymin >= ymax) {
272 std::cerr << m_className << "::SetMesh: Invalid y range.\n";
273 return false;
274 } else if (zmin >= zmax) {
275 std::cerr << m_className << "::SetMesh: Invalid z range.\n";
276 return false;
277 }
278 if (m_coordinates == Coordinates::Cylindrical) {
279 if (xmin < 0.) {
280 std::cerr << m_className << "::SetMesh: Invalid range.\n"
281 << " Radial coordinates must be >= 0.\n";
282 return false;
283 }
284 }
285 m_nX[0] = nx;
286 m_nX[1] = ny;
287 m_nX[2] = nz;
288 m_xMin[0] = xmin;
289 m_xMin[1] = ymin;
290 m_xMin[2] = zmin;
291 m_xMax[0] = xmax;
292 m_xMax[1] = ymax;
293 m_xMax[2] = zmax;
294 constexpr double tol = 1.e-10;
295 for (size_t i = 0; i < 3; ++i) {
296 if (m_xMax[i] - m_xMin[i] > tol) {
297 m_sX[i] = std::max(m_nX[i] - 1., 1.) / (m_xMax[i] - m_xMin[i]);
298 } else {
299 m_sX[i] = 0.;
300 }
301 }
302 if (m_coordinates == Coordinates::Cylindrical) {
303 if (fabs(m_xMax[1] - m_xMin[1] - TwoPi) < tol) {
304 if (!m_periodic[1]) {
305 std::cerr << m_className << "::SetMesh: Enabling theta periodicity.\n";
306 }
307 m_periodic[1] = true;
308 m_mirrorPeriodic[1] = false;
309 }
310 }
311 m_hasMesh = true;
312 return true;
313}
314
315bool ComponentGrid::GetMesh(unsigned int& nx, unsigned int& ny,
316 unsigned int& nz, double& xmin, double& xmax,
317 double& ymin, double& ymax, double& zmin,
318 double& zmax) const {
319 if (!m_hasMesh) return false;
320 nx = m_nX[0];
321 ny = m_nX[1];
322 nz = m_nX[2];
323 xmin = m_xMin[0];
324 ymin = m_xMin[1];
325 zmin = m_xMin[2];
326 xmax = m_xMax[0];
327 ymax = m_xMax[1];
328 zmax = m_xMax[2];
329 return true;
330}
331
333
334 if (m_xMin[0] < 0. || m_xMax[0] < 0.) {
335 std::cerr << m_className << "::SetCylindricalCoordinates:\n"
336 << " Invalid mesh limits. Radial coordinate must be >= 0.\n";
337 return;
338 }
339 if (m_periodic[1] || m_mirrorPeriodic[1]) {
340 const double s = m_xMax[1] - m_xMin[1];
341 if (std::abs(TwoPi - s * int(TwoPi / s)) > 1.e-4) {
342 std::cerr << m_className << "::SetCylindricalCoordinates:\n"
343 << " Angular range does not divide 2 pi.\n"
344 << " Switching off periodicity.\n";
345 m_periodic[1] = false;
346 m_mirrorPeriodic[1] = false;
347 }
348 }
349 m_coordinates = Coordinates::Cylindrical;
350}
351
352bool ComponentGrid::LoadElectricField(const std::string& fname,
353 const std::string& fmt, const bool withP,
354 const bool withFlag, const double scaleX,
355 const double scaleE,
356 const double scaleP) {
357 m_efields.clear();
358 m_hasPotential = false;
359 m_active.assign(m_nX[0], std::vector<std::vector<bool> >(
360 m_nX[1], std::vector<bool>(m_nX[2], true)));
361 // Read the file.
362 m_pMin = withP ? +1. : 0.;
363 m_pMax = withP ? -1. : 0.;
364 if (!LoadData(fname, fmt, withP, withFlag, scaleX, scaleE, scaleP,
365 m_efields)) {
366 m_efields.clear();
367 return false;
368 }
369 if (withP) m_hasPotential = true;
370 return true;
371}
372
373bool ComponentGrid::LoadWeightingField(const std::string& fname,
374 const std::string& fmt, const bool withP,
375 const double scaleX, const double scaleE,
376 const double scaleP) {
377 // Read the file.
378 if (!LoadData(fname, fmt, withP, false, scaleX, scaleE, scaleP, m_wfields)) {
379 m_wfields.clear();
380 return false;
381 }
382 return true;
383}
384
385bool ComponentGrid::LoadWeightingField(const std::string& fname,
386 const std::string& fmt, const double t,
387 const bool withP, const double scaleX,
388 const double scaleE,
389 const double scaleP) {
390 std::vector<std::vector<std::vector<Node> > > wfield;
391 // Read the file.
392 if (!LoadData(fname, fmt, withP, false, scaleX, scaleE, scaleP, wfield)) {
393 return false;
394 }
395 if (m_wdtimes.empty() || t > m_wdtimes.back()) {
396 m_wdtimes.push_back(t);
397 m_wdfields.push_back(std::move(wfield));
398 } else {
399 const auto it = std::upper_bound(m_wdtimes.begin(), m_wdtimes.end(), t);
400 const auto n = std::distance(m_wdtimes.begin(), it);
401 m_wdtimes.insert(it, t);
402 m_wdfields.insert( m_wdfields.begin() + n, std::move(wfield));
403 }
404 return true;
405}
406
407bool ComponentGrid::LoadMagneticField(const std::string& fname,
408 const std::string& fmt,
409 const double scaleX,
410 const double scaleB) {
411 // Read the file.
412 if (!LoadData(fname, fmt, false, false, scaleX, scaleB, 1., m_bfields)) {
413 m_bfields.clear();
414 return false;
415 }
416 return true;
417}
418
420 const std::string& filename,
421 const std::string& format) {
422 if (!cmp) {
423 std::cerr << m_className << "::SaveElectricField: Null pointer.\n";
424 return false;
425 }
426 if (!m_hasMesh) {
427 std::cerr << m_className << "::SaveElectricField: Mesh not set.\n";
428 return false;
429 }
430 const auto fmt = GetFormat(format);
431 if (fmt == Format::Unknown) {
432 std::cerr << m_className << "::SaveElectricField:\n"
433 << " Unknown format (" << format << ").\n";
434 return false;
435 }
436 std::ofstream outfile;
437 outfile.open(filename, std::ios::out);
438 if (!outfile) {
439 std::cerr << m_className << "::SaveElectricField:\n"
440 << " Could not open file " << filename << ".\n";
441 return false;
442 }
443 std::cout << m_className << "::SaveElectricField:\n"
444 << " Exporting field/potential to " << filename << ".\n"
445 << " Be patient...\n";
446 PrintProgress(0.);
447 outfile << "# XMIN = " << m_xMin[0] << ", XMAX = " << m_xMax[0]
448 << ", NX = " << m_nX[0] << "\n";
449 outfile << "# YMIN = " << m_xMin[1] << ", YMAX = " << m_xMax[1]
450 << ", NY = " << m_nX[1] << "\n";
451 outfile << "# ZMIN = " << m_xMin[2] << ", ZMAX = " << m_xMax[2]
452 << ", NZ = " << m_nX[2] << "\n";
453
454 const unsigned int nValues = m_nX[0] * m_nX[1] * m_nX[2];
455 const unsigned int nPrint =
456 std::pow(10, static_cast<unsigned int>(
457 std::max(std::floor(std::log10(nValues)) - 1, 1.)));
458 unsigned int nLines = 0;
459 Medium* medium = nullptr;
460 int status = 0;
461 const double dx = (m_xMax[0] - m_xMin[0]) / std::max(m_nX[0] - 1., 1.);
462 const double dy = (m_xMax[1] - m_xMin[1]) / std::max(m_nX[1] - 1., 1.);
463 const double dz = (m_xMax[2] - m_xMin[2]) / std::max(m_nX[2] - 1., 1.);
464 for (unsigned int i = 0; i < m_nX[0]; ++i) {
465 const double x = m_xMin[0] + i * dx;
466 for (unsigned int j = 0; j < m_nX[1]; ++j) {
467 const double y = m_xMin[1] + j * dy;
468 for (unsigned int k = 0; k < m_nX[2]; ++k) {
469 const double z = m_xMin[2] + k * dz;
470 if (fmt == Format::XY) {
471 outfile << x << " " << y << " ";
472 } else if (fmt == Format::XZ) {
473 outfile << x << " " << z << " ";
474 } else if (fmt == Format::XYZ) {
475 outfile << x << " " << y << " " << z << " ";
476 } else if (fmt == Format::IJ) {
477 outfile << i << " " << j << " ";
478 } else if (fmt == Format::IK) {
479 outfile << i << " " << k << " ";
480 } else if (fmt == Format::IJK) {
481 outfile << i << " " << j << " " << k << " ";
482 } else if (fmt == Format::YXZ) {
483 outfile << y << " " << x << " " << z << " ";
484 }
485 if (m_coordinates == Coordinates::Cylindrical) {
486 const double ct = cos(y);
487 const double st = sin(y);
488 double ex = 0., ey = 0., ez = 0., v = 0.;
489 cmp->ElectricField(x * ct, x * st, z, ex, ey, ez, v, medium, status);
490 const double er = +ex * ct + ey * st;
491 const double et = -ex * st + ey * ct;
492 outfile << er << " " << et << " " << ez << " " << v << "\n";
493 } else {
494 double ex = 0., ey = 0., ez = 0., v = 0.;
495 cmp->ElectricField(x, y, z, ex, ey, ez, v, medium, status);
496 outfile << ex << " " << ey << " " << ez << " " << v << "\n";
497 }
498 ++nLines;
499 if (nLines % nPrint == 0) PrintProgress(double(nLines) / nValues);
500 }
501 }
502 }
503 outfile.close();
504 std::cout << std::endl << m_className << "::SaveElectricField: Done.\n";
505 return true;
506}
507
509 const std::string& id,
510 const std::string& filename,
511 const std::string& format) {
512 if (!cmp) {
513 std::cerr << m_className << "::SaveWeightingField: Null pointer.\n";
514 return false;
515 }
516 if (!m_hasMesh) {
517 std::cerr << m_className << "::SaveWeightingField: Mesh not set.\n";
518 return false;
519 }
520 const auto fmt = GetFormat(format);
521 if (fmt == Format::Unknown) {
522 std::cerr << m_className << "::SaveWeightingField:\n"
523 << " Unknown format (" << format << ").\n";
524 return false;
525 }
526 std::ofstream outfile;
527 outfile.open(filename, std::ios::out);
528 if (!outfile) {
529 std::cerr << m_className << "::SaveWeightingField:\n"
530 << " Could not open file " << filename << ".\n";
531 return false;
532 }
533 std::cout << m_className << "::SaveWeightingField:\n"
534 << " Exporting field/potential to " << filename << ".\n"
535 << " Be patient...\n";
536 PrintProgress(0.);
537 outfile << "# XMIN = " << m_xMin[0] << ", XMAX = " << m_xMax[0]
538 << ", NX = " << m_nX[0] << "\n";
539 outfile << "# YMIN = " << m_xMin[1] << ", YMAX = " << m_xMax[1]
540 << ", NY = " << m_nX[1] << "\n";
541 outfile << "# ZMIN = " << m_xMin[2] << ", ZMAX = " << m_xMax[2]
542 << ", NZ = " << m_nX[2] << "\n";
543 const unsigned int nValues = m_nX[0] * m_nX[1] * m_nX[2];
544 const unsigned int nPrint =
545 std::pow(10, static_cast<unsigned int>(
546 std::max(std::floor(std::log10(nValues)) - 1, 1.)));
547 unsigned int nLines = 0;
548 const double dx = (m_xMax[0] - m_xMin[0]) / std::max(m_nX[0] - 1., 1.);
549 const double dy = (m_xMax[1] - m_xMin[1]) / std::max(m_nX[1] - 1., 1.);
550 const double dz = (m_xMax[2] - m_xMin[2]) / std::max(m_nX[2] - 1., 1.);
551 for (unsigned int i = 0; i < m_nX[0]; ++i) {
552 const double x = m_xMin[0] + i * dx;
553 for (unsigned int j = 0; j < m_nX[1]; ++j) {
554 const double y = m_xMin[1] + j * dy;
555 for (unsigned int k = 0; k < m_nX[2]; ++k) {
556 const double z = m_xMin[2] + k * dz;
557 if (fmt == Format::XY) {
558 outfile << x << " " << y << " ";
559 } else if (fmt == Format::XZ) {
560 outfile << x << " " << z << " ";
561 } else if (fmt == Format::XYZ) {
562 outfile << x << " " << y << " " << z << " ";
563 } else if (fmt == Format::IJ) {
564 outfile << i << " " << j << " ";
565 } else if (fmt == Format::IK) {
566 outfile << i << " " << k << " ";
567 } else if (fmt == Format::IJK) {
568 outfile << i << " " << j << " " << k << " ";
569 } else if (fmt == Format::YXZ) {
570 outfile << y << " " << x << " " << z << " ";
571 }
572 if (m_coordinates == Coordinates::Cylindrical) {
573 const double ct = cos(y);
574 const double st = sin(y);
575 double wx = 0., wy = 0., wz = 0.;
576 cmp->WeightingField(x * ct, x * st, z, wx, wy, wz, id);
577 const double v = cmp->WeightingPotential(x * ct, x * st, z, id);
578 const double wr = +wx * ct + wy * st;
579 const double wt = -wx * st + wy * ct;
580 outfile << wr << " " << wt << " " << wz << " " << v << "\n";
581 } else {
582 double wx = 0., wy = 0., wz = 0.;
583 cmp->WeightingField(x, y, z, wx, wy, wz, id);
584 const double v = cmp->WeightingPotential(x, y, z, id);
585 outfile << wx << " " << wy << " " << wz << " " << v << "\n";
586 }
587 ++nLines;
588 if (nLines % nPrint == 0) PrintProgress(double(nLines) / nValues);
589 }
590 }
591 }
592 outfile.close();
593 std::cout << std::endl << m_className << "::SaveWeightingField: Done.\n";
594 return true;
595}
596
597bool ComponentGrid::LoadMesh(const std::string& filename, std::string format,
598 const double scaleX) {
599 const auto fmt = GetFormat(format);
600 if (fmt == Format::Unknown) {
601 std::cerr << m_className << "::LoadMesh:\n"
602 << " Unknown format (" << format << ").\n";
603 return false;
604 }
605
606 // Keep track of which mesh parameters we have found.
607 std::bitset<9> found;
608 found.reset();
609 double xmin = 0., ymin = 0., zmin = 0.;
610 double xmax = 0., ymax = 0., zmax = 0.;
611 unsigned int nx = 0, ny = 0, nz = 0;
612 bool cylindrical = (m_coordinates == Coordinates::Cylindrical);
613 // Parse the comment lines in the file.
614 std::ifstream infile(filename);
615 if (!infile) {
616 std::cerr << m_className << "::LoadMesh:\n"
617 << " Could not open file " << filename << ".\n";
618 return false;
619 }
620 std::string line;
621 unsigned int nLines = 0;
622 // Read the file line by line.
623 while (std::getline(infile, line)) {
624 ++nLines;
625 // Strip white space from the beginning of the line.
626 ltrim(line);
627 // Skip empty lines.
628 if (line.empty()) continue;
629 // Skip lines that are not comments.
630 if (!IsComment(line)) continue;
631 std::size_t pos0 = 0;
632 std::size_t pos1 = line.find("=", pos0);
633 while (pos1 != std::string::npos) {
634 std::string key = line.substr(pos0, pos1 - pos0);
635 std::transform(key.begin(), key.end(), key.begin(), toupper);
636 const std::size_t pos2 = line.find_first_of(",;", pos1 + 1);
637 std::istringstream val(line.substr(pos1 + 1, pos2 - pos1 - 1));
638 if (key.find("XMIN") != std::string::npos) {
639 val >> xmin;
640 found.set(0);
641 } else if (key.find("YMIN") != std::string::npos) {
642 val >> ymin;
643 found.set(1);
644 } else if (key.find("ZMIN") != std::string::npos) {
645 val >> zmin;
646 found.set(2);
647 } else if (key.find("XMAX") != std::string::npos) {
648 val >> xmax;
649 found.set(3);
650 } else if (key.find("YMAX") != std::string::npos) {
651 val >> ymax;
652 found.set(4);
653 } else if (key.find("ZMAX") != std::string::npos) {
654 val >> zmax;
655 found.set(5);
656 } else if (key.find("NX") != std::string::npos) {
657 val >> nx;
658 found.set(6);
659 } else if (key.find("NY") != std::string::npos) {
660 val >> ny;
661 found.set(7);
662 } else if (key.find("NZ") != std::string::npos) {
663 val >> nz;
664 found.set(8);
665 } else if (key.find("CYLINDRICAL") != std::string::npos) {
666 cylindrical = true;
667 }
668 if (pos2 == std::string::npos) break;
669 pos0 = pos2 + 1;
670 pos1 = line.find("=", pos0);
671 }
672 }
673 infile.close();
674
675 if (fmt == Format::XY || fmt == Format::IJ) {
676 // Try to complement missing information on the z-range.
677 if (!found[8]) {
678 nz = 1;
679 found.set(8);
680 }
681 if (!found[2]) {
682 if (found[0] || found[1] || found[3] || found[4] || found[5]) {
683 zmin = -std::max(
684 {fabs(xmin), fabs(xmax), fabs(ymin), fabs(ymax), fabs(zmax)});
685 } else {
686 zmin = -100.;
687 }
688 found.set(2);
689 }
690 if (!found[5]) {
691 zmax = std::max(
692 {fabs(xmin), fabs(xmax), fabs(ymin), fabs(ymax), fabs(zmin)});
693 found.set(5);
694 }
695 } else if (fmt == Format::XZ || fmt == Format::IK) {
696 // Try to complement missing information on the y/theta-range.
697 if (!found[7]) {
698 ny = 1;
699 found.set(7);
700 }
701 if (cylindrical) {
702 if (!found[1]) ymin = -Pi;
703 if (!found[4]) ymax = +Pi;
704 found.set(1);
705 found.set(4);
706 } else {
707 if (!found[1]) {
708 if (found[0] || found[2] || found[3] || found[4] || found[5]) {
709 ymin = -std::max(
710 {fabs(xmin), fabs(xmax), fabs(zmin), fabs(zmax), fabs(ymax)});
711 } else {
712 ymin = -100.;
713 }
714 found.set(1);
715 }
716 if (!found[4]) {
717 ymax = std::max(
718 {fabs(xmin), fabs(xmax), fabs(zmin), fabs(zmax), fabs(ymin)});
719 found.set(4);
720 }
721 }
722 }
723 if (found.all()) {
724 if (cylindrical && xmin < 0.) {
725 std::cerr << m_className << "::LoadMesh:\n"
726 << " Radial coordinate must be >= 0.\n";
727 return false;
728 }
729 std::cout << m_className << "::LoadMesh:\n";
730 std::printf("%12.6f < x [cm] < %12.6f, %5u points\n", xmin, xmax, nx);
731 std::printf("%12.6f < y [cm] < %12.6f, %5u points\n", ymin, ymax, ny);
732 std::printf("%12.6f < z [cm] < %12.6f, %5u points\n", zmin, zmax, nz);
733 if (cylindrical) {
734 m_coordinates = Coordinates::Cylindrical;
735 } else {
736 m_coordinates = Coordinates::Cartesian;
737 }
738 return SetMesh(nx, ny, nz, xmin, xmax, ymin, ymax, zmin, zmax);
739 }
740 if ((fmt == Format::IJ || fmt == Format::IJK || fmt == Format::IK) &&
741 !(found[0] && found[3])) {
742 std::cerr << m_className << "::LoadMesh: x-limits not found.\n";
743 return false;
744 } else if ((fmt == Format::IJ || fmt == Format::IJK) &&
745 !(found[1] && found[4])) {
746 std::cerr << m_className << "::LoadMesh: y-limits not found.\n";
747 return false;
748 } else if ((fmt == Format::IK || fmt == Format::IJK) &&
749 !(found[2] && found[5])) {
750 std::cerr << m_className << "::LoadMesh: z-limits not found.\n";
751 return false;
752 }
753
754 unsigned int nValues = 0;
755 infile.open(filename, std::ios::in);
756 if (!infile) {
757 std::cerr << m_className << "::LoadMesh:\n"
758 << " Could not open file " << filename << ".\n";
759 return false;
760 }
761
762 if (!found[0]) xmin = std::numeric_limits<double>::max();
763 if (!found[1]) ymin = std::numeric_limits<double>::max();
764 if (!found[2]) zmin = std::numeric_limits<double>::max();
765 if (!found[3]) xmax = std::numeric_limits<double>::min();
766 if (!found[4]) ymax = std::numeric_limits<double>::min();
767 if (!found[5]) zmax = std::numeric_limits<double>::min();
768 constexpr double tol = 1.e-10;
769 auto cmp = [](double x, double y) {
770 return x < y - tol * (std::fabs(x) + std::fabs(y));
771 };
772 std::set<double, decltype(cmp)> xLines(cmp);
773 std::set<double, decltype(cmp)> yLines(cmp);
774 std::set<double, decltype(cmp)> zLines(cmp);
775 nLines = 0;
776 bool bad = false;
777 // Read the file line by line.
778 while (std::getline(infile, line)) {
779 ++nLines;
780 // Strip white space from the beginning of the line.
781 ltrim(line);
782 // Skip empty lines.
783 if (line.empty()) continue;
784 // Skip comments.
785 if (IsComment(line)) continue;
786 std::istringstream data(line);
787 if (fmt == Format::XY) {
788 double x, y;
789 data >> x >> y;
790 if (data.fail()) {
791 PrintError(m_className + "::LoadMesh", nLines, "coordinates");
792 bad = true;
793 break;
794 }
795 x *= scaleX;
796 y *= scaleX;
797 if (!found[0]) xmin = std::min(x, xmin);
798 if (!found[1]) ymin = std::min(y, ymin);
799 if (!found[3]) xmax = std::max(x, xmax);
800 if (!found[4]) ymax = std::max(y, ymax);
801 xLines.insert(x);
802 yLines.insert(y);
803 } else if (fmt == Format::XZ) {
804 double x, z;
805 data >> x >> z;
806 if (data.fail()) {
807 PrintError(m_className + "::LoadMesh", nLines, "coordinates");
808 bad = true;
809 break;
810 }
811 x *= scaleX;
812 z *= scaleX;
813 if (!found[0]) xmin = std::min(x, xmin);
814 if (!found[2]) zmin = std::min(z, zmin);
815 if (!found[3]) xmax = std::max(x, xmax);
816 if (!found[5]) zmax = std::max(z, zmax);
817 xLines.insert(x);
818 zLines.insert(z);
819 } else if (fmt == Format::XYZ) {
820 double x, y, z;
821 data >> x >> y >> z;
822 if (data.fail()) {
823 PrintError(m_className + "::LoadMesh", nLines, "coordinates");
824 bad = true;
825 break;
826 }
827 x *= scaleX;
828 y *= scaleX;
829 z *= scaleX;
830 if (!found[0]) xmin = std::min(x, xmin);
831 if (!found[1]) ymin = std::min(y, ymin);
832 if (!found[2]) zmin = std::min(z, zmin);
833 if (!found[3]) xmax = std::max(x, xmax);
834 if (!found[4]) ymax = std::max(y, ymax);
835 if (!found[5]) zmax = std::max(z, zmax);
836 xLines.insert(x);
837 yLines.insert(y);
838 zLines.insert(z);
839 } else if (fmt == Format::IJ) {
840 unsigned int i = 0, j = 0;
841 data >> i >> j;
842 if (data.fail()) {
843 PrintError(m_className + "::LoadMesh", nLines, "indices");
844 bad = true;
845 break;
846 }
847 if (!found[6]) nx = std::max(nx, i);
848 if (!found[7]) ny = std::max(ny, j);
849 } else if (fmt == Format::IK) {
850 unsigned int i = 0, k = 0;
851 data >> i >> k;
852 if (data.fail()) {
853 PrintError(m_className + "::LoadMesh", nLines, "indices");
854 bad = true;
855 break;
856 }
857 if (!found[6]) nx = std::max(nx, i);
858 if (!found[8]) nz = std::max(nz, k);
859 } else if (fmt == Format::IJK) {
860 unsigned int i = 0, j = 0, k = 0;
861 data >> i >> j >> k;
862 if (data.fail()) {
863 PrintError(m_className + "::LoadMesh", nLines, "indices");
864 bad = true;
865 break;
866 }
867 if (!found[6]) nx = std::max(nx, i);
868 if (!found[7]) ny = std::max(ny, j);
869 if (!found[8]) nz = std::max(nz, k);
870 } else if (fmt == Format::YXZ) {
871 double x, y, z;
872 data >> y >> x >> z;
873 if (data.fail()) {
874 PrintError(m_className + "::LoadMesh", nLines, "coordinates");
875 bad = true;
876 break;
877 }
878 x *= scaleX;
879 y *= scaleX;
880 z *= scaleX;
881 if (!found[0]) xmin = std::min(x, xmin);
882 if (!found[1]) ymin = std::min(y, ymin);
883 if (!found[2]) zmin = std::min(z, zmin);
884 if (!found[3]) xmax = std::max(x, xmax);
885 if (!found[4]) ymax = std::max(y, ymax);
886 if (!found[5]) zmax = std::max(z, zmax);
887 xLines.insert(x);
888 yLines.insert(y);
889 zLines.insert(z);
890 }
891 ++nValues;
892 }
893 infile.close();
894 if (bad) return false;
895
896 if (fmt == Format::XY || fmt == Format::XYZ ||
897 fmt == Format::XZ || fmt == Format::YXZ) {
898 if (!found[6]) nx = xLines.size();
899 if (!found[7]) ny = yLines.size();
900 if (!found[8]) nz = zLines.size();
901 }
902
903 if (cylindrical && xmin < 0.) {
904 std::cerr << m_className << "::LoadMesh:\n"
905 << " Radial coordinate must be >= 0.\n";
906 return false;
907 }
908 std::cout << m_className << "::LoadMesh:\n";
909 if (cylindrical) {
910 std::printf("%12.6f < r [cm] < %12.6f, %5u points\n", xmin, xmax, nx);
911 std::printf("%12.6f < theta < %12.6f, %5u points\n", ymin, ymax, ny);
912 } else {
913 std::printf("%12.6f < x [cm] < %12.6f, %5u points\n", xmin, xmax, nx);
914 std::printf("%12.6f < y [cm] < %12.6f, %5u points\n", ymin, ymax, ny);
915 }
916 std::printf("%12.6f < z [cm] < %12.6f, %5u points\n", zmin, zmax, nz);
917 unsigned int nExpected = nx;
918 if (fmt == Format::XZ || fmt == Format::IK) {
919 nExpected *= nz;
920 } else {
921 nExpected *= ny;
922 }
923 if (fmt == Format::XYZ || fmt == Format::IJK || fmt == Format::YXZ) {
924 nExpected *= nz;
925 }
926 if (nExpected != nValues) {
927 std::cerr << m_className << "::LoadMesh:\n"
928 << " Warning: Expected " << nExpected << " lines, read "
929 << nValues << ".\n";
930 }
931 if (cylindrical) {
932 m_coordinates = Coordinates::Cylindrical;
933 } else {
934 m_coordinates = Coordinates::Cartesian;
935 }
936 return SetMesh(nx, ny, nz, xmin, xmax, ymin, ymax, zmin, zmax);
937}
938
939bool ComponentGrid::LoadData(
940 const std::string& filename, std::string format, const bool withPotential,
941 const bool withFlag, const double scaleX, const double scaleF,
942 const double scaleP,
943 std::vector<std::vector<std::vector<Node> > >& fields) {
944 if (!m_hasMesh) {
945 if (!LoadMesh(filename, format, scaleX)) {
946 std::cerr << m_className << "::LoadData: Mesh not set.\n";
947 return false;
948 }
949 }
950
951 const auto fmt = GetFormat(format);
952 if (fmt == Format::Unknown) {
953 std::cerr << m_className << "::LoadData:\n"
954 << " Unknown format (" << format << ").\n";
955 return false;
956 }
957
958 // Set up the grid.
959 Initialise(fields);
960
961 unsigned int nValues = 0;
962 // Keep track of which elements have been read.
963 std::vector<std::vector<std::vector<bool> > > isSet(
964 m_nX[0],
965 std::vector<std::vector<bool> >(m_nX[1], std::vector<bool>(m_nX[2], false)));
966
967 std::ifstream infile(filename);
968 if (!infile) {
969 std::cerr << m_className << "::LoadData:\n"
970 << " Could not open file " << filename << ".\n";
971 return false;
972 }
973
974 std::string line;
975 unsigned int nLines = 0;
976 bool bad = false;
977 // Read the file line by line.
978 while (std::getline(infile, line)) {
979 ++nLines;
980 // Strip white space from beginning of line.
981 ltrim(line);
982 // Skip empty lines.
983 if (line.empty()) continue;
984 // Skip comments.
985 if (IsComment(line)) continue;
986 unsigned int i = 0;
987 unsigned int j = 0;
988 unsigned int k = 0;
989 double fx = 0.;
990 double fy = 0.;
991 double fz = 0.;
992 double p = 0.;
993 std::istringstream data(line);
994 if (fmt == Format::XY) {
995 double x, y;
996 data >> x >> y;
997 if (data.fail()) {
998 PrintError(m_className + "::LoadData", nLines, "coordinates");
999 bad = true;
1000 break;
1001 }
1002 x *= scaleX;
1003 y *= scaleX;
1004 if (m_nX[0] > 1) {
1005 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1006 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1007 if (i >= m_nX[0]) i = m_nX[0] - 1;
1008 }
1009 if (m_nX[1] > 1) {
1010 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1011 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1012 if (j >= m_nX[1]) j = m_nX[1] - 1;
1013 }
1014 } else if (fmt == Format::XZ) {
1015 double x, z;
1016 data >> x >> z;
1017 if (data.fail()) {
1018 PrintError(m_className + "::LoadData", nLines, "coordinates");
1019 bad = true;
1020 break;
1021 }
1022 x *= scaleX;
1023 z *= scaleX;
1024 if (m_nX[0] > 1) {
1025 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1026 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1027 if (i >= m_nX[0]) i = m_nX[0] - 1;
1028 }
1029 if (m_nX[2] > 1) {
1030 const double v = std::round((z - m_xMin[2]) * m_sX[2]);
1031 k = v < 0. ? 0 : static_cast<unsigned int>(v);
1032 if (j >= m_nX[1]) j = m_nX[1] - 1;
1033 }
1034 } else if (fmt == Format::XYZ) {
1035 double x, y, z;
1036 data >> x >> y >> z;
1037 if (data.fail()) {
1038 PrintError(m_className + "::LoadData", nLines, "coordinates");
1039 bad = true;
1040 break;
1041 }
1042 x *= scaleX;
1043 y *= scaleX;
1044 z *= scaleX;
1045 if (m_nX[0] > 1) {
1046 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1047 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1048 if (i >= m_nX[0]) i = m_nX[0] - 1;
1049 }
1050 if (m_nX[1] > 1) {
1051 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1052 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1053 if (j >= m_nX[1]) j = m_nX[1] - 1;
1054 }
1055 if (m_nX[2] > 1) {
1056 const double w = std::round((z - m_xMin[2]) * m_sX[2]);
1057 k = w < 0. ? 0 : static_cast<unsigned int>(w);
1058 if (k >= m_nX[2]) k = m_nX[2] - 1;
1059 }
1060 } else if (fmt == Format::IJ) {
1061 data >> i >> j;
1062 if (data.fail()) {
1063 PrintError(m_className + "::LoadData", nLines, "indices");
1064 bad = true;
1065 break;
1066 }
1067 } else if (fmt == Format::IJK) {
1068 data >> i >> j >> k;
1069 if (data.fail()) {
1070 PrintError(m_className + "::LoadData", nLines, "indices");
1071 bad = true;
1072 break;
1073 }
1074 } else if (fmt == Format::YXZ) {
1075 double x, y, z;
1076 data >> y >> x >> z;
1077 if (data.fail()) {
1078 PrintError(m_className + "::LoadData", nLines, "coordinates");
1079 bad = true;
1080 break;
1081 }
1082 x *= scaleX;
1083 y *= scaleX;
1084 z *= scaleX;
1085 if (m_nX[0] > 1) {
1086 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1087 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1088 if (i >= m_nX[0]) i = m_nX[0] - 1;
1089 }
1090 if (m_nX[1] > 1) {
1091 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1092 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1093 if (j >= m_nX[1]) j = m_nX[1] - 1;
1094 }
1095 if (m_nX[2] > 1) {
1096 const double w = std::round((z - m_xMin[2]) * m_sX[2]);
1097 k = w < 0. ? 0 : static_cast<unsigned int>(w);
1098 if (k >= m_nX[2]) k = m_nX[2] - 1;
1099 }
1100 }
1101 // Check the indices.
1102 if (i >= m_nX[0] || j >= m_nX[1] || k >= m_nX[2]) {
1103 std::cerr << m_className << "::LoadData:\n"
1104 << " Error reading line " << nLines << ".\n"
1105 << " Index (" << i << ", " << j << ", " << k
1106 << ") out of range.\n";
1107 continue;
1108 }
1109 if (isSet[i][j][k]) {
1110 std::cerr << m_className << "::LoadData:\n"
1111 << " Error reading line " << nLines << ".\n"
1112 << " Node (" << i << ", " << j << ", " << k
1113 << ") has already been set.\n";
1114 continue;
1115 }
1116 // Get the field values.
1117 if (fmt == Format::XY || fmt == Format::IJ) {
1118 // Two-dimensional map.
1119 fz = 0.;
1120 data >> fx >> fy;
1121 } else if (fmt == Format::XZ || fmt == Format::IK) {
1122 // Two-dimensional map.
1123 fy = 0.;
1124 data >> fx >> fz;
1125 } else if (fmt == Format::YXZ) {
1126 data >> fy >> fx >> fz;
1127 } else {
1128 data >> fx >> fy >> fz;
1129 }
1130 if (data.fail()) {
1131 PrintError(m_className + "::LoadData", nLines, "field components");
1132 bad = true;
1133 break;
1134 }
1135 fx *= scaleF;
1136 fy *= scaleF;
1137 fz *= scaleF;
1138 if (withPotential) {
1139 data >> p;
1140 if (data.fail()) {
1141 PrintError(m_className + "::LoadData", nLines, "potential");
1142 bad = true;
1143 break;
1144 }
1145 p *= scaleP;
1146 if (m_pMin > m_pMax) {
1147 // First value.
1148 m_pMin = p;
1149 m_pMax = p;
1150 } else {
1151 if (p < m_pMin) m_pMin = p;
1152 if (p > m_pMax) m_pMax = p;
1153 }
1154 }
1155 int flag = 0;
1156 if (withFlag) {
1157 data >> flag;
1158 if (data.fail()) {
1159 PrintError(m_className + "::LoadData", nLines, "region");
1160 bad = true;
1161 break;
1162 }
1163 }
1164 const bool isActive = flag == 0 ? false : true;
1165 if (fmt == Format::XY || fmt == Format::IJ) {
1166 // Two-dimensional map.
1167 for (unsigned int kk = 0; kk < m_nX[2]; ++kk) {
1168 fields[i][j][kk].fx = fx;
1169 fields[i][j][kk].fy = fy;
1170 fields[i][j][kk].fz = fz;
1171 fields[i][j][kk].v = p;
1172 if (withFlag) m_active[i][j][kk] = isActive;
1173 isSet[i][j][kk] = true;
1174 }
1175 } else if (fmt == Format::XZ || fmt == Format::IK) {
1176 // Two-dimensional map.
1177 for (unsigned int jj = 0; jj < m_nX[1]; ++jj) {
1178 fields[i][jj][k].fx = fx;
1179 fields[i][jj][k].fy = fy;
1180 fields[i][jj][k].fz = fz;
1181 fields[i][jj][k].v = p;
1182 if (withFlag) m_active[i][jj][k] = isActive;
1183 isSet[i][jj][k] = true;
1184 }
1185 } else {
1186 fields[i][j][k].fx = fx;
1187 fields[i][j][k].fy = fy;
1188 fields[i][j][k].fz = fz;
1189 fields[i][j][k].v = p;
1190 isSet[i][j][k] = true;
1191 }
1192 ++nValues;
1193 }
1194 infile.close();
1195 if (bad) return false;
1196 std::cout << m_className << "::LoadData:\n"
1197 << " Read " << nValues << " values from " << filename << ".\n";
1198 unsigned int nExpected = m_nX[0];
1199 if (fmt == Format::XY || fmt == Format::IJ) {
1200 nExpected *= m_nX[1];
1201 } else if (fmt == Format::XZ || fmt == Format::IK) {
1202 nExpected *= m_nX[2];
1203 } else {
1204 nExpected *= m_nX[1] * m_nX[2];
1205 }
1206 if (nExpected != nValues) {
1207 std::cerr << m_className << "::LoadData:\n"
1208 << " Expected " << nExpected << " values.\n";
1209 }
1210 return true;
1211}
1212
1213bool ComponentGrid::GetBoundingBox(double& xmin, double& ymin, double& zmin,
1214 double& xmax, double& ymax, double& zmax) {
1215 if (m_efields.empty() && m_wfields.empty() && m_bfields.empty()) {
1216 return false;
1217 }
1218 if (m_coordinates == Coordinates::Cylindrical) {
1219 const double rmax = m_xMax[0];
1220 xmin = -rmax;
1221 ymin = -rmax;
1222 xmax = +rmax;
1223 ymax = +rmax;
1224 zmin = (m_periodic[2] || m_mirrorPeriodic[2]) ? -INFINITY : m_xMin[2];
1225 zmax = (m_periodic[2] || m_mirrorPeriodic[2]) ? +INFINITY : m_xMax[2];
1226 return true;
1227 }
1228 if (m_periodic[0] || m_mirrorPeriodic[0]) {
1229 xmin = -INFINITY;
1230 xmax = +INFINITY;
1231 } else {
1232 xmin = m_xMin[0];
1233 xmax = m_xMax[0];
1234 }
1235
1236 if (m_periodic[1] || m_mirrorPeriodic[1]) {
1237 ymin = -INFINITY;
1238 ymax = +INFINITY;
1239 } else {
1240 ymin = m_xMin[1];
1241 ymax = m_xMax[1];
1242 }
1243
1244 if (m_periodic[2] || m_mirrorPeriodic[2]) {
1245 zmin = -INFINITY;
1246 zmax = +INFINITY;
1247 } else {
1248 zmin = m_xMin[2];
1249 zmax = m_xMax[2];
1250 }
1251 return true;
1252}
1253
1255 double& xmin, double& ymin, double& zmin,
1256 double& xmax, double& ymax, double& zmax) {
1257
1258 if (m_efields.empty() && m_wfields.empty() && m_bfields.empty()) {
1259 return false;
1260 }
1261 if (m_coordinates == Coordinates::Cylindrical) {
1262 const double rmax = m_xMax[0];
1263 xmin = -rmax;
1264 ymin = -rmax;
1265 xmax = +rmax;
1266 ymax = +rmax;
1267 } else {
1268 xmin = m_xMin[0];
1269 xmax = m_xMax[0];
1270 ymin = m_xMin[1];
1271 ymax = m_xMax[1];
1272 }
1273 zmin = m_xMin[2];
1274 zmax = m_xMax[2];
1275 return true;
1276}
1277
1278bool ComponentGrid::GetVoltageRange(double& vmin, double& vmax) {
1279 if (m_efields.empty()) return false;
1280 vmin = m_pMin;
1281 vmax = m_pMax;
1282 return true;
1283}
1284
1285bool ComponentGrid::GetElectricFieldRange(double& exmin, double& exmax,
1286 double& eymin, double& eymax,
1287 double& ezmin, double& ezmax) {
1288 if (m_efields.empty()) {
1289 PrintNotReady(m_className + "::GetElectricFieldRange");
1290 return false;
1291 }
1292
1293 exmin = exmax = m_efields[0][0][0].fx;
1294 eymin = eymax = m_efields[0][0][0].fy;
1295 ezmin = ezmax = m_efields[0][0][0].fz;
1296 for (unsigned int i = 0; i < m_nX[0]; ++i) {
1297 for (unsigned int j = 0; j < m_nX[1]; ++j) {
1298 for (unsigned int k = 0; k < m_nX[2]; ++k) {
1299 const Node& node = m_efields[i][j][k];
1300 if (node.fx < exmin) exmin = node.fx;
1301 if (node.fx > exmax) exmax = node.fx;
1302 if (node.fy < eymin) eymin = node.fy;
1303 if (node.fy > eymax) eymax = node.fy;
1304 if (node.fz < ezmin) ezmin = node.fz;
1305 if (node.fz > ezmax) ezmax = node.fz;
1306 }
1307 }
1308 }
1309 return true;
1310}
1311
1313 if (!m) {
1314 std::cerr << m_className << "::SetMedium: Null pointer.\n";
1315 }
1316 m_medium = m;
1317}
1318
1319bool ComponentGrid::GetField(
1320 const double xi, const double yi, const double zi,
1321 const std::vector<std::vector<std::vector<Node> > >& field, double& fx,
1322 double& fy, double& fz, double& p, bool& active) {
1323 if (!m_hasMesh) {
1324 std::cerr << m_className << "::GetField: Mesh is not set.\n";
1325 return false;
1326 }
1327
1328 // Reduce the point to the basic cell (in case of periodicity) and
1329 // check if it is inside the mesh.
1330 std::array<bool, 3> mirrored = {false, false, false};
1331 std::array<double, 3> xx = {xi, yi, zi};
1332 double theta = 0.;
1333 if (m_coordinates == Coordinates::Cylindrical) {
1334 if (fabs(xi) > Small || fabs(yi) > Small) {
1335 theta = atan2(yi, xi);
1336 xx[0] = sqrt(xi * xi + yi * yi);
1337 xx[1] = theta;
1338 }
1339 }
1340 for (size_t i = 0; i < 3; ++i) {
1341 xx[i] = Reduce(xx[i], m_xMin[i], m_xMax[i],
1342 m_periodic[i], m_mirrorPeriodic[i], mirrored[i]);
1343 if (xx[i] < m_xMin[i] || xx[i] > m_xMax[i]) return false;
1344 }
1345 // Get the indices.
1346 const double sx = (xx[0] - m_xMin[0]) * m_sX[0];
1347 const double sy = (xx[1] - m_xMin[1]) * m_sX[1];
1348 const double sz = (xx[2] - m_xMin[2]) * m_sX[2];
1349 // Get the indices.
1350 const unsigned int i0 = static_cast<unsigned int>(std::floor(sx));
1351 const unsigned int j0 = static_cast<unsigned int>(std::floor(sy));
1352 const unsigned int k0 = static_cast<unsigned int>(std::floor(sz));
1353 const double ux = sx - i0;
1354 const double uy = sy - j0;
1355 const double uz = sz - k0;
1356 const unsigned int i1 = std::min(i0 + 1, m_nX[0] - 1);
1357 const unsigned int j1 = std::min(j0 + 1, m_nX[1] - 1);
1358 const unsigned int k1 = std::min(k0 + 1, m_nX[2] - 1);
1359 const double vx = 1. - ux;
1360 const double vy = 1. - uy;
1361 const double vz = 1. - uz;
1362 if (!m_active.empty()) {
1363 active = m_active[i0][j0][k0] && m_active[i0][j0][k1] &&
1364 m_active[i0][j1][k0] && m_active[i0][j1][k1] &&
1365 m_active[i1][j0][k0] && m_active[i1][j0][k1] &&
1366 m_active[i1][j1][k0] && m_active[i1][j1][k1];
1367 }
1368 const Node& n000 = field[i0][j0][k0];
1369 const Node& n100 = field[i1][j0][k0];
1370 const Node& n010 = field[i0][j1][k0];
1371 const Node& n110 = field[i1][j1][k0];
1372 const Node& n001 = field[i0][j0][k1];
1373 const Node& n101 = field[i1][j0][k1];
1374 const Node& n011 = field[i0][j1][k1];
1375 const Node& n111 = field[i1][j1][k1];
1376
1377 if (m_debug) {
1378 std::cout << m_className << "::GetField: Determining field at (" << xi
1379 << ", " << yi << ", " << zi << ").\n"
1380 << " X: " << i0 << " (" << ux << ") - " << i1 << " (" << vx
1381 << ").\n"
1382 << " Y: " << j0 << " (" << uy << ") - " << j1 << " (" << vy
1383 << ").\n"
1384 << " Z: " << k0 << " (" << uz << ") - " << k1 << " (" << vz
1385 << ").\n";
1386 }
1387 fx = ((n000.fx * vx + n100.fx * ux) * vy +
1388 (n010.fx * vx + n110.fx * ux) * uy) *
1389 vz +
1390 ((n001.fx * vx + n101.fx * ux) * vy +
1391 (n011.fx * vx + n111.fx * ux) * uy) *
1392 uz;
1393 fy = ((n000.fy * vx + n100.fy * ux) * vy +
1394 (n010.fy * vx + n110.fy * ux) * uy) *
1395 vz +
1396 ((n001.fy * vx + n101.fy * ux) * vy +
1397 (n011.fy * vx + n111.fy * ux) * uy) *
1398 uz;
1399 fz = ((n000.fz * vx + n100.fz * ux) * vy +
1400 (n010.fz * vx + n110.fz * ux) * uy) *
1401 vz +
1402 ((n001.fz * vx + n101.fz * ux) * vy +
1403 (n011.fz * vx + n111.fz * ux) * uy) *
1404 uz;
1405 p = ((n000.v * vx + n100.v * ux) * vy + (n010.v * vx + n110.v * ux) * uy) *
1406 vz +
1407 ((n001.v * vx + n101.v * ux) * vy + (n011.v * vx + n111.v * ux) * uy) *
1408 uz;
1409 if (mirrored[0]) fx = -fx;
1410 if (mirrored[1]) fy = -fy;
1411 if (mirrored[2]) fz = -fz;
1412 if (m_coordinates == Coordinates::Cylindrical) {
1413 const double ct = cos(theta);
1414 const double st = sin(theta);
1415 const double fr = fx;
1416 const double ft = fy;
1417 fx = ct * fr - st * ft;
1418 fy = st * fr + ct * ft;
1419 }
1420 return true;
1421}
1422
1423bool ComponentGrid::GetElectricField(const unsigned int i, const unsigned int j,
1424 const unsigned int k, double& v,
1425 double& ex, double& ey, double& ez) const {
1426 v = ex = ey = ez = 0.;
1427 if (m_efields.empty()) {
1428 if (!m_hasMesh) {
1429 std::cerr << m_className << "::GetElectricField: Mesh not set.\n";
1430 return false;
1431 }
1432 PrintNotReady(m_className + "::GetElectricField");
1433 return false;
1434 }
1435 if (i >= m_nX[0] || j >= m_nX[1] || k >= m_nX[2]) {
1436 std::cerr << m_className << "::GetElectricField: Index out of range.\n";
1437 return false;
1438 }
1439 const Node& node = m_efields[i][j][k];
1440 v = node.v;
1441 ex = node.fx;
1442 ey = node.fy;
1443 ez = node.fz;
1444 return true;
1445}
1446
1448
1449 std::cout << m_className << "::Print:\n";
1450 if (!m_hasMesh) {
1451 std::cout << " Mesh not set.\n";
1452 return;
1453 }
1454 std::printf(" %15.8f < x [cm] < %15.8f, %10u nodes\n",
1455 m_xMin[0], m_xMax[0], m_nX[0]);
1456 std::printf(" %15.8f < y [cm] < %15.8f, %10u nodes\n",
1457 m_xMin[1], m_xMax[1], m_nX[1]);
1458 std::printf(" %15.8f < z [cm] < %15.8f, %10u nodes\n",
1459 m_xMin[2], m_xMax[2], m_nX[2]);
1460 if (m_efields.empty() && m_bfields.empty() &&
1461 m_wfields.empty() && m_wdfields.empty() &&
1462 m_eAttachment.empty() && m_hAttachment.empty() &&
1463 m_eVelocity.empty() && m_hVelocity.empty()) {
1464 std::cout << " Available data: None.\n";
1465 return;
1466 }
1467 std::cout << " Available data:\n";
1468 if (!m_efields.empty()) std::cout << " Electric field.\n";
1469 if (!m_bfields.empty()) std::cout << " Magnetic field.\n";
1470 if (!m_wfields.empty()) std::cout << " Weighting field.\n";
1471 if (!m_wdfields.empty()) {
1472 std::cout << " Delayed weighting field.\n";
1473 }
1474 if (!m_eVelocity.empty()) {
1475 std::cout << " Electron drift velocity.\n";
1476 }
1477 if (!m_hVelocity.empty()) {
1478 std::cout << " Hole drift velocity.\n";
1479 }
1480 if (!m_eAttachment.empty()) {
1481 std::cout << " Electron attachment coefficient.\n";
1482 }
1483 if (!m_hAttachment.empty()) {
1484 std::cout << " Hole attachment coefficient.\n";
1485 }
1486}
1487
1488void ComponentGrid::Reset() {
1489 m_efields.clear();
1490 m_bfields.clear();
1491 m_wfields.clear();
1492 m_eAttachment.clear();
1493 m_hAttachment.clear();
1494 m_eVelocity.clear();
1495 m_hVelocity.clear();
1496
1497 m_wdfields.clear();
1498 m_wdtimes.clear();
1499
1500 m_active.clear();
1501
1502 m_nX.fill(1);
1503 m_xMin.fill(0.);
1504 m_xMax.fill(0.);
1505 m_sX[0] = m_sX[1] = m_sX[2] = 0.;
1506 m_pMin = m_pMax = 0.;
1507 m_medium = nullptr;
1508
1509 m_hasMesh = false;
1510 m_hasPotential = false;
1511
1512 m_wFieldOffset.fill(0.);
1513}
1514
1515void ComponentGrid::UpdatePeriodicity() {
1516
1517 // Check for conflicts.
1518 for (size_t i = 0; i < 3; ++i) {
1519 if (m_periodic[i] && m_mirrorPeriodic[i]) {
1520 std::cerr << m_className << "::UpdatePeriodicity:\n"
1521 << " Both simple and mirror periodicity requested. Reset.\n";
1522 m_periodic[i] = m_mirrorPeriodic[i] = false;
1523 }
1524 }
1525
1527 std::cerr << m_className << "::UpdatePeriodicity:\n"
1528 << " Axial symmetry is not supported. Reset.\n";
1529 m_axiallyPeriodic.fill(false);
1530 }
1531
1534 std::cerr << m_className << "::UpdatePeriodicity:\n"
1535 << " Rotation symmetry is not supported. Reset.\n";
1536 m_rotationSymmetric.fill(false);
1537 }
1538}
1539
1540double ComponentGrid::Reduce(const double xin, const double xmin,
1541 const double xmax, const bool simplePeriodic,
1542 const bool mirrorPeriodic, bool& mirrored) const {
1543 // In case of periodicity, reduce the coordinate to the basic cell.
1544 double x = xin;
1545 const double lx = xmax - xmin;
1546 if (simplePeriodic) {
1547 x = xmin + fmod(x - xmin, lx);
1548 if (x < xmin) x += lx;
1549 } else if (mirrorPeriodic) {
1550 double xNew = xmin + fmod(x - xmin, lx);
1551 if (xNew < xmin) xNew += lx;
1552 const int nx = int(floor(0.5 + (xNew - x) / lx));
1553 if (nx != 2 * (nx / 2)) {
1554 xNew = xmin + xmax - xNew;
1555 mirrored = true;
1556 }
1557 x = xNew;
1558 }
1559 return x;
1560}
1561
1562void ComponentGrid::Initialise(
1563 std::vector<std::vector<std::vector<Node> > >& fields) {
1564 fields.resize(m_nX[0]);
1565 for (unsigned int i = 0; i < m_nX[0]; ++i) {
1566 fields[i].resize(m_nX[1]);
1567 for (unsigned int j = 0; j < m_nX[1]; ++j) {
1568 fields[i][j].resize(m_nX[2]);
1569 for (unsigned int k = 0; k < m_nX[2]; ++k) {
1570 fields[i][j][k].fx = 0.;
1571 fields[i][j][k].fy = 0.;
1572 fields[i][j][k].fz = 0.;
1573 fields[i][j][k].v = 0.;
1574 }
1575 }
1576 }
1577}
1578
1579bool ComponentGrid::LoadElectronVelocity(const std::string& fname,
1580 const std::string& fmt,
1581 const double scaleX,
1582 const double scaleV) {
1583 // Read the file.
1584 if (!LoadData(fname, fmt, false, false, scaleX, scaleV, 1., m_eVelocity)) {
1585 return false;
1586 }
1587 return true;
1588}
1589
1590bool ComponentGrid::LoadHoleVelocity(const std::string& fname,
1591 const std::string& fmt,
1592 const double scaleX,
1593 const double scaleV) {
1594 // Read the file.
1595 if (!LoadData(fname, fmt, false, false, scaleX, scaleV, 1., m_hVelocity)) {
1596 return false;
1597 }
1598 return true;
1599}
1600
1601bool ComponentGrid::ElectronVelocity(const double x, const double y,
1602 const double z,
1603 double& vx, double& vy, double& vz) {
1604 if (m_eVelocity.empty()) {
1605 PrintNotReady(m_className + "::ElectronVelocity");
1606 return false;
1607 }
1608 double p = 0.;
1609 bool active = true;
1610 return GetField(x, y, z, m_eVelocity, vx, vy, vz, p, active);
1611}
1612
1613bool ComponentGrid::HoleVelocity(const double x, const double y,
1614 const double z,
1615 double& vx, double& vy, double& vz) {
1616 if (m_hVelocity.empty()) {
1617 PrintNotReady(m_className + "::HoleVelocity");
1618 return false;
1619 }
1620 double p = 0.;
1621 bool active = true;
1622 return GetField(x, y, z, m_hVelocity, vx, vy, vz, p, active);
1623}
1624
1625bool ComponentGrid::LoadElectronAttachment(const std::string& fname,
1626 const std::string& fmt,
1627 const unsigned int col,
1628 const double scaleX) {
1629 // Read the file.
1630 return LoadData(fname, fmt, scaleX, m_eAttachment, col);
1631}
1632
1633bool ComponentGrid::LoadHoleAttachment(const std::string& fname,
1634 const std::string& fmt,
1635 const unsigned int col,
1636 const double scaleX) {
1637 // Read the file.
1638 return LoadData(fname, fmt, scaleX, m_hAttachment, col);
1639}
1640
1641bool ComponentGrid::LoadData(
1642 const std::string& filename, std::string format, const double scaleX,
1643 std::vector<std::vector<std::vector<double> > >& tab,
1644 const unsigned int col) {
1645 if (!m_hasMesh) {
1646 if (!LoadMesh(filename, format, scaleX)) {
1647 std::cerr << m_className << "::LoadData: Mesh not set.\n";
1648 return false;
1649 }
1650 }
1651
1652 const auto fmt = GetFormat(format);
1653 if (fmt == Format::Unknown) {
1654 std::cerr << m_className << "::LoadData:\n"
1655 << " Unknown format (" << format << ").\n";
1656 return false;
1657 }
1658 // Check the column index.
1659 unsigned int offset = 0;
1660 if (fmt == Format::XY || fmt == Format::IJ) {
1661 if (col < 2) {
1662 std::cerr << m_className << "::LoadData:\n"
1663 << " Unexpected column index (" << col << ").\n";
1664 return false;
1665 }
1666 offset = 2;
1667 } else {
1668 if (col < 3) {
1669 std::cerr << m_className << "::LoadData:\n"
1670 << " Unexpected column index (" << col << ").\n";
1671 return false;
1672 }
1673 offset = 3;
1674 }
1675
1676 // Set up the grid.
1677 tab.assign(
1678 m_nX[0],
1679 std::vector<std::vector<double> >(m_nX[1], std::vector<double>(m_nX[2], 0.)));
1680
1681 unsigned int nValues = 0;
1682 // Keep track of which elements have been read.
1683 std::vector<std::vector<std::vector<bool> > > isSet(
1684 m_nX[0],
1685 std::vector<std::vector<bool> >(m_nX[1], std::vector<bool>(m_nX[2], false)));
1686
1687 std::ifstream infile(filename);
1688 if (!infile) {
1689 std::cerr << m_className << "::LoadData:\n"
1690 << " Could not open file " << filename << ".\n";
1691 return false;
1692 }
1693
1694 std::string line;
1695 unsigned int nLines = 0;
1696 bool bad = false;
1697 // Read the file line by line.
1698 while (std::getline(infile, line)) {
1699 ++nLines;
1700 // Strip white space from beginning of line.
1701 ltrim(line);
1702 // Skip empty lines.
1703 if (line.empty()) continue;
1704 // Skip comments.
1705 if (IsComment(line)) continue;
1706 unsigned int i = 0;
1707 unsigned int j = 0;
1708 unsigned int k = 0;
1709 double val = 0;
1710 std::istringstream data(line);
1711 if (fmt == Format::XY) {
1712 double x, y;
1713 data >> x >> y;
1714 if (data.fail()) {
1715 PrintError(m_className + "::LoadData", nLines, "coordinates");
1716 bad = true;
1717 break;
1718 }
1719 x *= scaleX;
1720 y *= scaleX;
1721 if (m_nX[0] > 1) {
1722 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1723 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1724 if (i >= m_nX[0]) i = m_nX[0] - 1;
1725 }
1726 if (m_nX[1] > 1) {
1727 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1728 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1729 if (j >= m_nX[1]) j = m_nX[1] - 1;
1730 }
1731 } else if (fmt == Format::XYZ) {
1732 double x, y, z;
1733 data >> x >> y >> z;
1734 if (data.fail()) {
1735 PrintError(m_className + "::LoadData", nLines, "coordinates");
1736 bad = true;
1737 break;
1738 }
1739 x *= scaleX;
1740 y *= scaleX;
1741 z *= scaleX;
1742 if (m_nX[0] > 1) {
1743 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1744 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1745 if (i >= m_nX[0]) i = m_nX[0] - 1;
1746 }
1747 if (m_nX[1] > 1) {
1748 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1749 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1750 if (j >= m_nX[1]) j = m_nX[1] - 1;
1751 }
1752 if (m_nX[2] > 1) {
1753 const double w = std::round((z - m_xMin[2]) * m_sX[2]);
1754 k = w < 0. ? 0 : static_cast<unsigned int>(w);
1755 if (k >= m_nX[2]) k = m_nX[2] - 1;
1756 }
1757 } else if (fmt == Format::IJ) {
1758 data >> i >> j;
1759 if (data.fail()) {
1760 PrintError(m_className + "::LoadData", nLines, "indices");
1761 bad = true;
1762 break;
1763 }
1764 } else if (fmt == Format::IJK) {
1765 data >> i >> j >> k;
1766 if (data.fail()) {
1767 PrintError(m_className + "::LoadData", nLines, "indices");
1768 bad = true;
1769 break;
1770 }
1771 } else if (fmt == Format::YXZ) {
1772 double x, y, z;
1773 data >> y >> x >> z;
1774 if (data.fail()) {
1775 PrintError(m_className + "::LoadData", nLines, "coordinates");
1776 bad = true;
1777 break;
1778 }
1779 x *= scaleX;
1780 y *= scaleX;
1781 z *= scaleX;
1782 if (m_nX[0] > 1) {
1783 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1784 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1785 if (i >= m_nX[0]) i = m_nX[0] - 1;
1786 }
1787 if (m_nX[1] > 1) {
1788 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1789 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1790 if (j >= m_nX[1]) j = m_nX[1] - 1;
1791 }
1792 if (m_nX[2] > 1) {
1793 const double w = std::round((z - m_xMin[2]) * m_sX[2]);
1794 k = w < 0. ? 0 : static_cast<unsigned int>(w);
1795 if (k >= m_nX[2]) k = m_nX[2] - 1;
1796 }
1797 }
1798 // Check the indices.
1799 if (i >= m_nX[0] || j >= m_nX[1] || k >= m_nX[2]) {
1800 std::cerr << m_className << "::LoadData:\n"
1801 << " Error reading line " << nLines << ".\n"
1802 << " Index (" << i << ", " << j << ", " << k
1803 << ") out of range.\n";
1804 continue;
1805 }
1806 if (isSet[i][j][k]) {
1807 std::cerr << m_className << "::LoadData:\n"
1808 << " Error reading line " << nLines << ".\n"
1809 << " Node (" << i << ", " << j << ", " << k
1810 << ") has already been set.\n";
1811 continue;
1812 }
1813
1814 // Skip to the requested column.
1815 for (unsigned int ii = 0; ii < col - offset; ++ii) {
1816 double dummy = 0.;
1817 data >> dummy;
1818 if (data.fail()) {
1819 PrintError(m_className + "::LoadData", nLines,
1820 "column " + std::to_string(offset + ii));
1821 break;
1822 }
1823 }
1824 if (data.fail()) {
1825 bad = true;
1826 break;
1827 }
1828 data >> val;
1829
1830 if (data.fail()) {
1831 PrintError(m_className + "::LoadData", nLines,
1832 "column " + std::to_string(col));
1833 bad = true;
1834 break;
1835 }
1836
1837 if (fmt == Format::XY || fmt == Format::IJ) {
1838 // Two-dimensional map
1839 for (unsigned int kk = 0; kk < m_nX[2]; ++kk) {
1840 tab[i][j][kk] = val;
1841 isSet[i][j][kk] = true;
1842 }
1843 } else {
1844 tab[i][j][k] = val;
1845 isSet[i][j][k] = true;
1846 }
1847 ++nValues;
1848 }
1849 infile.close();
1850 if (bad) return false;
1851 std::cout << m_className << "::LoadData:\n"
1852 << " Read " << nValues << " values from " << filename << ".\n";
1853 unsigned int nExpected = m_nX[0] * m_nX[1];
1854 if (fmt == Format::XYZ || fmt == Format::IJK || fmt == Format::YXZ) {
1855 nExpected *= m_nX[2];
1856 }
1857 if (nExpected != nValues) {
1858 std::cerr << m_className << "::LoadData:\n"
1859 << " Expected " << nExpected << " values.\n";
1860 }
1861 return true;
1862}
1863
1864bool ComponentGrid::GetData(
1865 const double xi, const double yi, const double zi,
1866 const std::vector<std::vector<std::vector<double> > >& tab, double& val) {
1867 if (!m_hasMesh) {
1868 std::cerr << m_className << "::GetData: Mesh is not set.\n";
1869 return false;
1870 }
1871
1872 // Reduce the point to the basic cell (in case of periodicity) and
1873 // check if it is inside the mesh.
1874 bool xMirrored = false;
1875 const double x =
1876 Reduce(xi, m_xMin[0], m_xMax[0], m_periodic[0], m_mirrorPeriodic[0], xMirrored);
1877 if (x < m_xMin[0] || x > m_xMax[0]) return false;
1878 bool yMirrored = false;
1879 const double y =
1880 Reduce(yi, m_xMin[1], m_xMax[1], m_periodic[1], m_mirrorPeriodic[1], yMirrored);
1881 if (y < m_xMin[1] || y > m_xMax[1]) return false;
1882 bool zMirrored = false;
1883 const double z =
1884 Reduce(zi, m_xMin[2], m_xMax[2], m_periodic[2], m_mirrorPeriodic[2], zMirrored);
1885 if (z < m_xMin[2] || z > m_xMax[2]) return false;
1886
1887 // Get the indices.
1888 const double sx = (x - m_xMin[0]) * m_sX[0];
1889 const double sy = (y - m_xMin[1]) * m_sX[1];
1890 const double sz = (z - m_xMin[2]) * m_sX[2];
1891 const unsigned int i0 = static_cast<unsigned int>(std::floor(sx));
1892 const unsigned int j0 = static_cast<unsigned int>(std::floor(sy));
1893 const unsigned int k0 = static_cast<unsigned int>(std::floor(sz));
1894 const double ux = sx - i0;
1895 const double uy = sy - j0;
1896 const double uz = sz - k0;
1897 const unsigned int i1 = std::min(i0 + 1, m_nX[0] - 1);
1898 const unsigned int j1 = std::min(j0 + 1, m_nX[1] - 1);
1899 const unsigned int k1 = std::min(k0 + 1, m_nX[2] - 1);
1900 const double vx = 1. - ux;
1901 const double vy = 1. - uy;
1902 const double vz = 1. - uz;
1903 const double n000 = tab[i0][j0][k0];
1904 const double n100 = tab[i1][j0][k0];
1905 const double n010 = tab[i0][j1][k0];
1906 const double n110 = tab[i1][j1][k0];
1907 const double n001 = tab[i0][j0][k1];
1908 const double n101 = tab[i1][j0][k1];
1909 const double n011 = tab[i0][j1][k1];
1910 const double n111 = tab[i1][j1][k1];
1911
1912 if (m_debug) {
1913 std::cout << m_className << "::GetData: Interpolating at (" << xi
1914 << ", " << yi << ", " << zi << ").\n"
1915 << " X: " << i0 << " (" << ux << ") - " << i1 << " (" << vx
1916 << ").\n"
1917 << " Y: " << j0 << " (" << uy << ") - " << j1 << " (" << vy
1918 << ").\n"
1919 << " Z: " << k0 << " (" << uz << ") - " << k1 << " (" << vz
1920 << ").\n";
1921 }
1922 val = ((n000 * vx + n100 * ux) * vy + (n010 * vx + n110 * ux) * uy) * vz +
1923 ((n001 * vx + n101 * ux) * vy + (n011 * vx + n111 * ux) * uy) * uz;
1924
1925 return true;
1926}
1927
1928bool ComponentGrid::ElectronAttachment(const double x, const double y,
1929 const double z, double& att) {
1930 // Make sure the map has been loaded.
1931 if (m_eAttachment.empty()) {
1932 PrintNotReady(m_className + "::ElectronAttachment");
1933 return false;
1934 }
1935 return GetData(x, y, z, m_eAttachment, att);
1936}
1937
1938bool ComponentGrid::HoleAttachment(const double x, const double y,
1939 const double z, double& att) {
1940 // Make sure the map has been loaded.
1941 if (m_hAttachment.empty()) {
1942 PrintNotReady(m_className + "::HoleAttachment");
1943 return false;
1944 }
1945 return GetData(x, y, z, m_hAttachment, att);
1946}
1947
1948ComponentGrid::Format ComponentGrid::GetFormat(std::string format) {
1949 std::transform(format.begin(), format.end(), format.begin(), toupper);
1950 if (format == "XY") {
1951 return Format::XY;
1952 } else if (format == "XZ") {
1953 return Format::XZ;
1954 } else if (format == "XYZ") {
1955 return Format::XYZ;
1956 } else if (format == "IJ") {
1957 return Format::IJ;
1958 } else if (format == "IK") {
1959 return Format::IK;
1960 } else if (format == "IJK") {
1961 return Format::IJK;
1962 } else if (format == "YXZ") {
1963 return Format::YXZ;
1964 }
1965 return Format::Unknown;
1966}
1967
1968} // namespace Garfield
bool ElectronVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz) override
Get the electron drift velocity.
bool LoadElectronVelocity(const std::string &fname, const std::string &fmt, const double scaleX=1., const double scaleV=1.e-9)
void SetCylindricalCoordinates()
Use cylindrical coordinates.
bool SetMesh(const unsigned int nx, const unsigned int ny, const unsigned int nz, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax)
double DelayedWeightingPotential(const double x, const double y, const double z, const double t, const std::string &label) override
bool HasMagneticField() const override
Does the component have a non-zero magnetic field?
bool GetElementaryCell(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the coordinates of the elementary cell.
bool SaveElectricField(Component *cmp, const std::string &filename, const std::string &fmt)
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
ComponentGrid()
Constructor.
bool LoadElectricField(const std::string &filename, const std::string &format, const bool withPotential, const bool withFlag, const double scaleX=1., const double scaleE=1., const double scaleP=1.)
void DelayedWeightingField(const double x, const double y, const double z, const double t, double &wx, double &wy, double &wz, const std::string &label) override
bool GetMesh(unsigned int &nx, unsigned int &ny, unsigned int &nz, double &xmin, double &xmax, double &ymin, double &ymax, double &zmin, double &zmax) const
Retrieve the parameters of the grid.
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
void Print()
Print information about the mesh and the available data.
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
void SetMedium(Medium *m)
Set the medium.
Medium * GetMedium() const
Get the medium.
bool LoadMagneticField(const std::string &filename, const std::string &format, const double scaleX=1., const double scaleB=1.)
Import magnetic field values from a file.
bool GetElectricField(const unsigned int i, const unsigned int j, const unsigned int k, double &v, double &ex, double &ey, double &ez) const
Return the field at a given node.
bool HoleVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz) override
Get the hole drift velocity.
bool LoadWeightingField(const std::string &filename, const std::string &format, const bool withPotential, const double scaleX=1., const double scaleE=1., const double scaleP=1.)
Import (prompt) weighting field from file.
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&m, int &status) override
Calculate the drift field [V/cm] and potential [V] at (x, y, z).
bool LoadHoleVelocity(const std::string &fname, const std::string &fmt, const double scaleX=1., const double scaleV=1.e-9)
Import a map of hole drift velocities from a file.
bool HoleAttachment(const double x, const double y, const double z, double &att) override
Get the hole attachment coefficient.
bool GetElectricFieldRange(double &exmin, double &exmax, double &eymin, double &eymax, double &ezmin, double &ezmax)
void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status) override
bool ElectronAttachment(const double x, const double y, const double z, double &att) override
Get the electron attachment coefficient.
void SetWeightingFieldOffset(const double x, const double y, const double z)
bool LoadHoleAttachment(const std::string &fname, const std::string &fmt, const unsigned int col, const double scaleX=1.)
Import hole attachment coefficients from a file.
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
bool LoadElectronAttachment(const std::string &fname, const std::string &fmt, const unsigned int col, const double scaleX=1.)
bool SaveWeightingField(Component *cmp, const std::string &id, const std::string &filename, const std::string &fmt)
virtual double WeightingPotential(const double x, const double y, const double z, const std::string &label)
Definition Component.cc:80
std::array< bool, 3 > m_rotationSymmetric
Rotation symmetry around x-axis, y-axis, z-axis.
Definition Component.hh:380
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
Definition Component.hh:376
virtual void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status)=0
virtual void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
Definition Component.cc:61
bool m_debug
Switch on/off debugging messages.
Definition Component.hh:371
Component()=delete
Default constructor.
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition Component.hh:374
virtual bool HasMagneticField() const
Does the component have a non-zero magnetic field?
Definition Component.cc:155
std::string m_className
Class name.
Definition Component.hh:359
virtual void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
Definition Component.cc:102
std::array< bool, 3 > m_axiallyPeriodic
Axial periodicity in x, y, z.
Definition Component.hh:378
Abstract base class for media.
Definition Medium.hh:16
void ltrim(std::string &line)
Definition Utilities.hh:12
DoubleAc cos(const DoubleAc &f)
Definition DoubleAc.cpp:432
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