Garfield++ 4.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_ready) {
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
158void ComponentGrid::SetWeightingFieldOffset(const double x, const double y,
159 const double z) {
160 m_wFieldOffset = {x, y, z};
161}
162
163void ComponentGrid::MagneticField(const double x, const double y,
164 const double z, double& bx, double& by,
165 double& bz, int& status) {
166 status = 0;
167 if (m_bfields.empty()) {
168 return Component::MagneticField(x, y, z, bx, by, bz, status);
169 }
170
171 double p = 0.;
172 bool active = true;
173 if (!GetField(x, y, z, m_bfields, bx, by, bz, p, active)) {
174 status = -11;
175 }
176}
177
178Medium* ComponentGrid::GetMedium(const double x, const double y,
179 const double z) {
180 // Make sure the field map has been loaded.
181 if (!m_ready) {
182 PrintNotReady(m_className + "::GetMedium");
183 return nullptr;
184 }
185
186 std::array<double, 3> xx = {x, y, z};
187 for (size_t i = 0; i < 3; ++i) {
188 if (m_periodic[i] || m_mirrorPeriodic[i]) continue;
189 if (xx[i] < m_xMin[i] || xx[i] > m_xMax[i]) {
190 return nullptr;
191 }
192 }
193 if (m_active.empty()) return m_medium;
194 for (size_t i = 0; i < 3; ++i) {
195 bool mirrored = false;
196 xx[i] = Reduce(xx[i], m_xMin[i], m_xMax[i],
197 m_periodic[i], m_mirrorPeriodic[i], mirrored);
198 }
199 // Get the indices.
200 const double sx = (xx[0] - m_xMin[0]) * m_sX[0];
201 const double sy = (xx[1] - m_xMin[1]) * m_sX[1];
202 const double sz = (xx[2] - m_xMin[2]) * m_sX[2];
203 const unsigned int i0 = static_cast<unsigned int>(std::floor(sx));
204 const unsigned int j0 = static_cast<unsigned int>(std::floor(sy));
205 const unsigned int k0 = static_cast<unsigned int>(std::floor(sz));
206 const unsigned int i1 = std::min(i0 + 1, m_nX[0] - 1);
207 const unsigned int j1 = std::min(j0 + 1, m_nX[1] - 1);
208 const unsigned int k1 = std::min(k0 + 1, m_nX[2] - 1);
209 if (m_active[i0][j0][k0] && m_active[i0][j0][k1] && m_active[i0][j1][k0] &&
210 m_active[i0][j1][k1] && m_active[i1][j0][k0] && m_active[i1][j0][k1] &&
211 m_active[i1][j1][k0] && m_active[i1][j1][k1]) {
212 return m_medium;
213 }
214 return nullptr;
215}
216
217bool ComponentGrid::SetMesh(const unsigned int nx, const unsigned int ny,
218 const unsigned int nz, const double xmin,
219 const double xmax, const double ymin,
220 const double ymax, const double zmin,
221 const double zmax) {
222 Reset();
223 if (nx == 0 || ny == 0 || nz == 0) {
224 std::cerr << m_className << "::SetMesh:\n"
225 << " Number of mesh elements must be positive.\n";
226 return false;
227 }
228 if (xmin >= xmax) {
229 std::cerr << m_className << "::SetMesh: Invalid x range.\n";
230 return false;
231 } else if (ymin >= ymax) {
232 std::cerr << m_className << "::SetMesh: Invalid y range.\n";
233 return false;
234 } else if (zmin >= zmax) {
235 std::cerr << m_className << "::SetMesh: Invalid z range.\n";
236 return false;
237 }
238 m_nX[0] = nx;
239 m_nX[1] = ny;
240 m_nX[2] = nz;
241 m_xMin[0] = xmin;
242 m_xMin[1] = ymin;
243 m_xMin[2] = zmin;
244 m_xMax[0] = xmax;
245 m_xMax[1] = ymax;
246 m_xMax[2] = zmax;
247 constexpr double tol = 1.e-10;
248 for (size_t i = 0; i < 3; ++i) {
249 if (m_xMax[i] - m_xMin[i] > tol) {
250 m_sX[i] = std::max(m_nX[i] - 1., 1.) / (m_xMax[i] - m_xMin[i]);
251 } else {
252 m_sX[i] = 0.;
253 }
254 }
255 m_hasMesh = true;
256 return true;
257}
258
259bool ComponentGrid::GetMesh(unsigned int& nx, unsigned int& ny,
260 unsigned int& nz, double& xmin, double& xmax,
261 double& ymin, double& ymax, double& zmin,
262 double& zmax) const {
263 if (!m_hasMesh) return false;
264 nx = m_nX[0];
265 ny = m_nX[1];
266 nz = m_nX[2];
267 xmin = m_xMin[0];
268 ymin = m_xMin[1];
269 zmin = m_xMin[2];
270 xmax = m_xMax[0];
271 ymax = m_xMax[1];
272 zmax = m_xMax[2];
273 return true;
274}
275
276bool ComponentGrid::LoadElectricField(const std::string& fname,
277 const std::string& fmt, const bool withP,
278 const bool withFlag, const double scaleX,
279 const double scaleE,
280 const double scaleP) {
281 m_ready = false;
282 m_hasPotential = false;
283 m_active.assign(m_nX[0], std::vector<std::vector<bool> >(
284 m_nX[1], std::vector<bool>(m_nX[2], true)));
285 // Read the file.
286 m_pMin = withP ? +1. : 0.;
287 m_pMax = withP ? -1. : 0.;
288 if (!LoadData(fname, fmt, withP, withFlag, scaleX, scaleE, scaleP,
289 m_efields)) {
290 m_efields.clear();
291 return false;
292 }
293 m_ready = true;
294 if (withP) m_hasPotential = true;
295 return true;
296}
297
298bool ComponentGrid::LoadWeightingField(const std::string& fname,
299 const std::string& fmt, const bool withP,
300 const double scaleX, const double scaleE,
301 const double scaleP) {
302 // Read the file.
303 if (!LoadData(fname, fmt, withP, false, scaleX, scaleE, scaleP, m_wfields)) {
304 m_wfields.clear();
305 return false;
306 }
307 return true;
308}
309
310bool ComponentGrid::LoadWeightingField(const std::string& fname,
311 const std::string& fmt, const double t,
312 const bool withP, const double scaleX,
313 const double scaleE,
314 const double scaleP) {
315 std::vector<std::vector<std::vector<Node> > > wfield;
316 // Read the file.
317 if (!LoadData(fname, fmt, withP, false, scaleX, scaleE, scaleP, wfield)) {
318 return false;
319 }
320 if (m_wdtimes.empty() || t > m_wdtimes.back()) {
321 m_wdtimes.push_back(t);
322 m_wdfields.push_back(std::move(wfield));
323 } else {
324 const auto it = std::upper_bound(m_wdtimes.begin(), m_wdtimes.end(), t);
325 const auto n = std::distance(m_wdtimes.begin(), it);
326 m_wdtimes.insert(it, t);
327 m_wdfields.insert( m_wdfields.begin() + n, std::move(wfield));
328 }
329 return true;
330}
331
332bool ComponentGrid::LoadMagneticField(const std::string& fname,
333 const std::string& fmt,
334 const double scaleX,
335 const double scaleB) {
336 // Read the file.
337 if (!LoadData(fname, fmt, false, false, scaleX, scaleB, 1., m_bfields)) {
338 m_bfields.clear();
339 return false;
340 }
341 return true;
342}
343
345 const std::string& filename,
346 const std::string& format) {
347 if (!cmp) {
348 std::cerr << m_className << "::SaveElectricField: Null pointer.\n";
349 return false;
350 }
351 if (!m_hasMesh) {
352 std::cerr << m_className << "::SaveElectricField: Mesh not set.\n";
353 return false;
354 }
355 const auto fmt = GetFormat(format);
356 if (fmt == Format::Unknown) {
357 std::cerr << m_className << "::SaveElectricField:\n"
358 << " Unknown format (" << format << ").\n";
359 return false;
360 }
361 std::ofstream outfile;
362 outfile.open(filename.c_str(), std::ios::out);
363 if (!outfile) {
364 std::cerr << m_className << "::SaveElectricField:\n"
365 << " Could not open file " << filename << ".\n";
366 return false;
367 }
368 std::cout << m_className << "::SaveElectricField:\n"
369 << " Exporting field/potential to " << filename << ".\n"
370 << " Be patient...\n";
371 PrintProgress(0.);
372 outfile << "# XMIN = " << m_xMin[0] << ", XMAX = " << m_xMax[0]
373 << ", NX = " << m_nX[0] << "\n";
374 outfile << "# YMIN = " << m_xMin[1] << ", YMAX = " << m_xMax[1]
375 << ", NY = " << m_nX[1] << "\n";
376 outfile << "# ZMIN = " << m_xMin[2] << ", ZMAX = " << m_xMax[2]
377 << ", NZ = " << m_nX[2] << "\n";
378
379 const unsigned int nValues = m_nX[0] * m_nX[1] * m_nX[2];
380 const unsigned int nPrint =
381 std::pow(10, static_cast<unsigned int>(
382 std::max(std::floor(std::log10(nValues)) - 1, 1.)));
383 unsigned int nLines = 0;
384 Medium* medium = nullptr;
385 int status = 0;
386 const double dx = (m_xMax[0] - m_xMin[0]) / std::max(m_nX[0] - 1., 1.);
387 const double dy = (m_xMax[1] - m_xMin[1]) / std::max(m_nX[1] - 1., 1.);
388 const double dz = (m_xMax[2] - m_xMin[2]) / std::max(m_nX[2] - 1., 1.);
389 for (unsigned int i = 0; i < m_nX[0]; ++i) {
390 const double x = m_xMin[0] + i * dx;
391 for (unsigned int j = 0; j < m_nX[1]; ++j) {
392 const double y = m_xMin[1] + j * dy;
393 for (unsigned int k = 0; k < m_nX[2]; ++k) {
394 const double z = m_xMin[2] + k * dz;
395 if (fmt == Format::XY) {
396 outfile << x << " " << y << " ";
397 } else if (fmt == Format::XYZ) {
398 outfile << x << " " << y << " " << z << " ";
399 } else if (fmt == Format::IJ) {
400 outfile << i << " " << j << " ";
401 } else if (fmt == Format::IJK) {
402 outfile << i << " " << j << " " << k << " ";
403 } else if (fmt == Format::YXZ) {
404 outfile << y << " " << x << " " << z << " ";
405 }
406 double ex = 0., ey = 0., ez = 0., v = 0.;
407 cmp->ElectricField(x, y, z, ex, ey, ez, v, medium, status);
408 outfile << ex << " " << ey << " " << ez << " " << v << "\n";
409 ++nLines;
410 if (nLines % nPrint == 0) PrintProgress(double(nLines) / nValues);
411 }
412 }
413 }
414 outfile.close();
415 std::cout << std::endl << m_className << "::SaveElectricField: Done.\n";
416 return true;
417}
418
420 const std::string& id,
421 const std::string& filename,
422 const std::string& format) {
423 if (!cmp) {
424 std::cerr << m_className << "::SaveWeightingField: Null pointer.\n";
425 return false;
426 }
427 if (!m_hasMesh) {
428 std::cerr << m_className << "::SaveWeightingField: Mesh not set.\n";
429 return false;
430 }
431 const auto fmt = GetFormat(format);
432 if (fmt == Format::Unknown) {
433 std::cerr << m_className << "::SaveWeightingField:\n"
434 << " Unknown format (" << format << ").\n";
435 return false;
436 }
437 std::ofstream outfile;
438 outfile.open(filename.c_str(), std::ios::out);
439 if (!outfile) {
440 std::cerr << m_className << "::SaveWeightingField:\n"
441 << " Could not open file " << filename << ".\n";
442 return false;
443 }
444 std::cout << m_className << "::SaveWeightingField:\n"
445 << " Exporting field/potential to " << filename << ".\n"
446 << " Be patient...\n";
447 PrintProgress(0.);
448 outfile << "# XMIN = " << m_xMin[0] << ", XMAX = " << m_xMax[0]
449 << ", NX = " << m_nX[0] << "\n";
450 outfile << "# YMIN = " << m_xMin[1] << ", YMAX = " << m_xMax[1]
451 << ", NY = " << m_nX[1] << "\n";
452 outfile << "# ZMIN = " << m_xMin[2] << ", ZMAX = " << m_xMax[2]
453 << ", NZ = " << m_nX[2] << "\n";
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 const double dx = (m_xMax[0] - m_xMin[0]) / std::max(m_nX[0] - 1., 1.);
460 const double dy = (m_xMax[1] - m_xMin[1]) / std::max(m_nX[1] - 1., 1.);
461 const double dz = (m_xMax[2] - m_xMin[2]) / std::max(m_nX[2] - 1., 1.);
462 for (unsigned int i = 0; i < m_nX[0]; ++i) {
463 const double x = m_xMin[0] + i * dx;
464 for (unsigned int j = 0; j < m_nX[1]; ++j) {
465 const double y = m_xMin[1] + j * dy;
466 for (unsigned int k = 0; k < m_nX[2]; ++k) {
467 const double z = m_xMin[2] + k * dz;
468 if (fmt == Format::XY) {
469 outfile << x << " " << y << " ";
470 } else if (fmt == Format::XYZ) {
471 outfile << x << " " << y << " " << z << " ";
472 } else if (fmt == Format::IJ) {
473 outfile << i << " " << j << " ";
474 } else if (fmt == Format::IJK) {
475 outfile << i << " " << j << " " << k << " ";
476 } else if (fmt == Format::YXZ) {
477 outfile << y << " " << x << " " << z << " ";
478 }
479 double wx = 0., wy = 0., wz = 0.;
480 cmp->WeightingField(x, y, z, wx, wy, wz, id);
481 const double v = cmp->WeightingPotential(x, y, z, id);
482 outfile << wx << " " << wy << " " << wz << " " << v << "\n";
483 ++nLines;
484 if (nLines % nPrint == 0) PrintProgress(double(nLines) / nValues);
485 }
486 }
487 }
488 outfile.close();
489 std::cout << std::endl << m_className << "::SaveWeightingField: Done.\n";
490 return true;
491}
492
493bool ComponentGrid::LoadMesh(const std::string& filename, std::string format,
494 const double scaleX) {
495 const auto fmt = GetFormat(format);
496 if (fmt == Format::Unknown) {
497 std::cerr << m_className << "::LoadMesh:\n"
498 << " Unknown format (" << format << ").\n";
499 return false;
500 }
501
502 // Keep track of which mesh parameters we have found.
503 std::bitset<9> found;
504 found.reset();
505 double xmin = 0., ymin = 0., zmin = 0.;
506 double xmax = 0., ymax = 0., zmax = 0.;
507 unsigned int nx = 0, ny = 0, nz = 0;
508
509 // Parse the comment lines in the file.
510 std::ifstream infile;
511 infile.open(filename.c_str(), std::ios::in);
512 if (!infile) {
513 std::cerr << m_className << "::LoadMesh:\n"
514 << " Could not open file " << filename << ".\n";
515 return false;
516 }
517 std::string line;
518 unsigned int nLines = 0;
519 // Read the file line by line.
520 while (std::getline(infile, line)) {
521 ++nLines;
522 // Strip white space from the beginning of the line.
523 ltrim(line);
524 // Skip empty lines.
525 if (line.empty()) continue;
526 // Skip lines that are not comments.
527 if (!IsComment(line)) continue;
528 std::size_t pos0 = 0;
529 std::size_t pos1 = line.find("=", pos0);
530 while (pos1 != std::string::npos) {
531 std::string key = line.substr(pos0, pos1 - pos0);
532 std::transform(key.begin(), key.end(), key.begin(), toupper);
533 const std::size_t pos2 = line.find_first_of(",;", pos1 + 1);
534 std::istringstream val(line.substr(pos1 + 1, pos2 - pos1 - 1));
535 if (key.find("XMIN") != std::string::npos) {
536 val >> xmin;
537 found.set(0);
538 } else if (key.find("YMIN") != std::string::npos) {
539 val >> ymin;
540 found.set(1);
541 } else if (key.find("ZMIN") != std::string::npos) {
542 val >> zmin;
543 found.set(2);
544 } else if (key.find("XMAX") != std::string::npos) {
545 val >> xmax;
546 found.set(3);
547 } else if (key.find("YMAX") != std::string::npos) {
548 val >> ymax;
549 found.set(4);
550 } else if (key.find("ZMAX") != std::string::npos) {
551 val >> zmax;
552 found.set(5);
553 } else if (key.find("NX") != std::string::npos) {
554 val >> nx;
555 found.set(6);
556 } else if (key.find("NY") != std::string::npos) {
557 val >> ny;
558 found.set(7);
559 } else if (key.find("NZ") != std::string::npos) {
560 val >> nz;
561 found.set(8);
562 }
563 if (pos2 == std::string::npos) break;
564 pos0 = pos2 + 1;
565 pos1 = line.find("=", pos0);
566 }
567 }
568 infile.close();
569
570 if (fmt == Format::XY || fmt == Format::IJ) {
571 // Try to complement missing information on the z-range.
572 if (!found[8]) {
573 nz = 1;
574 found.set(8);
575 }
576 if (!found[2]) {
577 if (found[0] || found[1] || found[3] || found[4] || found[5]) {
578 zmin = -std::max(
579 {fabs(xmin), fabs(xmax), fabs(ymin), fabs(ymax), fabs(zmax)});
580 } else {
581 zmin = -100.;
582 }
583 found.set(2);
584 }
585 if (!found[5]) {
586 zmax = std::max(
587 {fabs(xmin), fabs(xmax), fabs(ymin), fabs(ymax), fabs(zmin)});
588 found.set(5);
589 }
590 }
591 if (found.all()) {
592 std::cout << m_className << "::LoadMesh:\n";
593 std::printf("%12.6f < x [cm] < %12.6f, %5u points\n", xmin, xmax, nx);
594 std::printf("%12.6f < y [cm] < %12.6f, %5u points\n", ymin, ymax, ny);
595 std::printf("%12.6f < z [cm] < %12.6f, %5u points\n", zmin, zmax, nz);
596 return SetMesh(nx, ny, nz, xmin, xmax, ymin, ymax, zmin, zmax);
597 }
598
599 if ((fmt == Format::IJ || fmt == Format::IJK) && !(found[0] && found[3])) {
600 std::cerr << m_className << "::LoadMesh: x-limits not found.\n";
601 return false;
602 } else if ((fmt == Format::IJ || fmt == Format::IJK) && !(found[1] && found[4])) {
603 std::cerr << m_className << "::LoadMesh: y-limits not found.\n";
604 return false;
605 } else if (fmt == Format::IJK && !(found[2] && found[5])) {
606 std::cerr << m_className << "::LoadMesh: z-limits not found.\n";
607 return false;
608 }
609
610 unsigned int nValues = 0;
611 infile.open(filename.c_str(), std::ios::in);
612 if (!infile) {
613 std::cerr << m_className << "::LoadMesh:\n"
614 << " Could not open file " << filename << ".\n";
615 return false;
616 }
617
618 if (!found[0]) xmin = std::numeric_limits<double>::max();
619 if (!found[1]) ymin = std::numeric_limits<double>::max();
620 if (!found[2]) zmin = std::numeric_limits<double>::max();
621 if (!found[3]) xmax = std::numeric_limits<double>::min();
622 if (!found[4]) ymax = std::numeric_limits<double>::min();
623 if (!found[5]) zmax = std::numeric_limits<double>::min();
624 constexpr double tol = 1.e-10;
625 auto cmp = [](double x, double y) {
626 return x < y - tol * (std::fabs(x) + std::fabs(y));
627 };
628 std::set<double, decltype(cmp)> xLines(cmp);
629 std::set<double, decltype(cmp)> yLines(cmp);
630 std::set<double, decltype(cmp)> zLines(cmp);
631 nLines = 0;
632 bool bad = false;
633 // Read the file line by line.
634 while (std::getline(infile, line)) {
635 ++nLines;
636 // Strip white space from the beginning of the line.
637 ltrim(line);
638 // Skip empty lines.
639 if (line.empty()) continue;
640 // Skip comments.
641 if (IsComment(line)) continue;
642 std::istringstream data;
643 data.str(line);
644 if (fmt == Format::XY) {
645 double x, y;
646 data >> x >> y;
647 if (data.fail()) {
648 PrintError(m_className + "::LoadMesh", nLines, "coordinates");
649 bad = true;
650 break;
651 }
652 x *= scaleX;
653 y *= scaleX;
654 if (!found[0]) xmin = std::min(x, xmin);
655 if (!found[1]) ymin = std::min(y, ymin);
656 if (!found[3]) xmax = std::max(x, xmax);
657 if (!found[4]) ymax = std::max(y, ymax);
658 xLines.insert(x);
659 yLines.insert(y);
660 } else if (fmt == Format::XYZ) {
661 double x, y, z;
662 data >> x >> y >> z;
663 if (data.fail()) {
664 PrintError(m_className + "::LoadMesh", nLines, "coordinates");
665 bad = true;
666 break;
667 }
668 x *= scaleX;
669 y *= scaleX;
670 z *= scaleX;
671 if (!found[0]) xmin = std::min(x, xmin);
672 if (!found[1]) ymin = std::min(y, ymin);
673 if (!found[2]) zmin = std::min(z, zmin);
674 if (!found[3]) xmax = std::max(x, xmax);
675 if (!found[4]) ymax = std::max(y, ymax);
676 if (!found[5]) zmax = std::max(z, zmax);
677 xLines.insert(x);
678 yLines.insert(y);
679 zLines.insert(z);
680 } else if (fmt == Format::IJ) {
681 unsigned int i = 0, j = 0;
682 data >> i >> j;
683 if (data.fail()) {
684 PrintError(m_className + "::LoadMesh", nLines, "indices");
685 bad = true;
686 break;
687 }
688 if (!found[6]) nx = std::max(nx, i);
689 if (!found[7]) ny = std::max(ny, j);
690 } else if (fmt == Format::IJK) {
691 unsigned int i = 0, j = 0, k = 0;
692 data >> i >> j >> k;
693 if (data.fail()) {
694 PrintError(m_className + "::LoadMesh", nLines, "indices");
695 bad = true;
696 break;
697 }
698 if (!found[6]) nx = std::max(nx, i);
699 if (!found[7]) ny = std::max(ny, j);
700 if (!found[8]) nz = std::max(nz, k);
701 } else if (fmt == Format::YXZ) {
702 double x, y, z;
703 data >> y >> x >> z;
704 if (data.fail()) {
705 PrintError(m_className + "::LoadMesh", nLines, "coordinates");
706 bad = true;
707 break;
708 }
709 x *= scaleX;
710 y *= scaleX;
711 z *= scaleX;
712 if (!found[0]) xmin = std::min(x, xmin);
713 if (!found[1]) ymin = std::min(y, ymin);
714 if (!found[2]) zmin = std::min(z, zmin);
715 if (!found[3]) xmax = std::max(x, xmax);
716 if (!found[4]) ymax = std::max(y, ymax);
717 if (!found[5]) zmax = std::max(z, zmax);
718 xLines.insert(x);
719 yLines.insert(y);
720 zLines.insert(z);
721 }
722 ++nValues;
723 }
724 infile.close();
725 if (bad) return false;
726
727 if (fmt == Format::XY || fmt == Format::XYZ || fmt == Format::YXZ) {
728 if (!found[6]) nx = xLines.size();
729 if (!found[7]) ny = yLines.size();
730 if (!found[8]) nz = zLines.size();
731 }
732
733 std::cout << m_className << "::LoadMesh:\n";
734 std::printf("%12.6f < x [cm] < %12.6f, %5u points\n", xmin, xmax, nx);
735 std::printf("%12.6f < y [cm] < %12.6f, %5u points\n", ymin, ymax, ny);
736 std::printf("%12.6f < z [cm] < %12.6f, %5u points\n", zmin, zmax, nz);
737 unsigned int nExpected = nx * ny;
738 if (fmt == Format::XYZ || fmt == Format::IJK || fmt == Format::YXZ) {
739 nExpected *= nz;
740 }
741 if (nExpected != nValues) {
742 std::cerr << m_className << "::LoadMesh:\n"
743 << " Warning: Expected " << nExpected << " lines, read "
744 << nValues << ".\n";
745 }
746 return SetMesh(nx, ny, nz, xmin, xmax, ymin, ymax, zmin, zmax);
747}
748
749bool ComponentGrid::LoadData(
750 const std::string& filename, std::string format, const bool withPotential,
751 const bool withFlag, const double scaleX, const double scaleF,
752 const double scaleP,
753 std::vector<std::vector<std::vector<Node> > >& fields) {
754 if (!m_hasMesh) {
755 if (!LoadMesh(filename, format, scaleX)) {
756 std::cerr << m_className << "::LoadData: Mesh not set.\n";
757 return false;
758 }
759 }
760
761 const auto fmt = GetFormat(format);
762 if (fmt == Format::Unknown) {
763 std::cerr << m_className << "::LoadData:\n"
764 << " Unknown format (" << format << ").\n";
765 return false;
766 }
767
768 // Set up the grid.
769 Initialise(fields);
770
771 unsigned int nValues = 0;
772 // Keep track of which elements have been read.
773 std::vector<std::vector<std::vector<bool> > > isSet(
774 m_nX[0],
775 std::vector<std::vector<bool> >(m_nX[1], std::vector<bool>(m_nX[2], false)));
776
777 std::ifstream infile;
778 infile.open(filename.c_str(), std::ios::in);
779 if (!infile) {
780 std::cerr << m_className << "::LoadData:\n"
781 << " Could not open file " << filename << ".\n";
782 return false;
783 }
784
785 std::string line;
786 unsigned int nLines = 0;
787 bool bad = false;
788 // Read the file line by line.
789 while (std::getline(infile, line)) {
790 ++nLines;
791 // Strip white space from beginning of line.
792 ltrim(line);
793 // Skip empty lines.
794 if (line.empty()) continue;
795 // Skip comments.
796 if (IsComment(line)) continue;
797 unsigned int i = 0;
798 unsigned int j = 0;
799 unsigned int k = 0;
800 double fx = 0.;
801 double fy = 0.;
802 double fz = 0.;
803 double p = 0.;
804 std::istringstream data;
805 data.str(line);
806 if (fmt == Format::XY) {
807 double x, y;
808 data >> x >> y;
809 if (data.fail()) {
810 PrintError(m_className + "::LoadData", nLines, "coordinates");
811 bad = true;
812 break;
813 }
814 x *= scaleX;
815 y *= scaleX;
816 if (m_nX[0] > 1) {
817 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
818 i = u < 0. ? 0 : static_cast<unsigned int>(u);
819 if (i >= m_nX[0]) i = m_nX[0] - 1;
820 }
821 if (m_nX[1] > 1) {
822 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
823 j = v < 0. ? 0 : static_cast<unsigned int>(v);
824 if (j >= m_nX[1]) j = m_nX[1] - 1;
825 }
826 } else if (fmt == Format::XYZ) {
827 double x, y, z;
828 data >> x >> y >> z;
829 if (data.fail()) {
830 PrintError(m_className + "::LoadData", nLines, "coordinates");
831 bad = true;
832 break;
833 }
834 x *= scaleX;
835 y *= scaleX;
836 z *= scaleX;
837 if (m_nX[0] > 1) {
838 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
839 i = u < 0. ? 0 : static_cast<unsigned int>(u);
840 if (i >= m_nX[0]) i = m_nX[0] - 1;
841 }
842 if (m_nX[1] > 1) {
843 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
844 j = v < 0. ? 0 : static_cast<unsigned int>(v);
845 if (j >= m_nX[1]) j = m_nX[1] - 1;
846 }
847 if (m_nX[2] > 1) {
848 const double w = std::round((z - m_xMin[2]) * m_sX[2]);
849 k = w < 0. ? 0 : static_cast<unsigned int>(w);
850 if (k >= m_nX[2]) k = m_nX[2] - 1;
851 }
852 } else if (fmt == Format::IJ) {
853 data >> i >> j;
854 if (data.fail()) {
855 PrintError(m_className + "::LoadData", nLines, "indices");
856 bad = true;
857 break;
858 }
859 } else if (fmt == Format::IJK) {
860 data >> i >> j >> k;
861 if (data.fail()) {
862 PrintError(m_className + "::LoadData", nLines, "indices");
863 bad = true;
864 break;
865 }
866 } else if (fmt == Format::YXZ) {
867 double x, y, z;
868 data >> y >> x >> z;
869 if (data.fail()) {
870 PrintError(m_className + "::LoadData", nLines, "coordinates");
871 bad = true;
872 break;
873 }
874 x *= scaleX;
875 y *= scaleX;
876 z *= scaleX;
877 if (m_nX[0] > 1) {
878 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
879 i = u < 0. ? 0 : static_cast<unsigned int>(u);
880 if (i >= m_nX[0]) i = m_nX[0] - 1;
881 }
882 if (m_nX[1] > 1) {
883 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
884 j = v < 0. ? 0 : static_cast<unsigned int>(v);
885 if (j >= m_nX[1]) j = m_nX[1] - 1;
886 }
887 if (m_nX[2] > 1) {
888 const double w = std::round((z - m_xMin[2]) * m_sX[2]);
889 k = w < 0. ? 0 : static_cast<unsigned int>(w);
890 if (k >= m_nX[2]) k = m_nX[2] - 1;
891 }
892 }
893 // Check the indices.
894 if (i >= m_nX[0] || j >= m_nX[1] || k >= m_nX[2]) {
895 std::cerr << m_className << "::LoadData:\n"
896 << " Error reading line " << nLines << ".\n"
897 << " Index (" << i << ", " << j << ", " << k
898 << ") out of range.\n";
899 continue;
900 }
901 if (isSet[i][j][k]) {
902 std::cerr << m_className << "::LoadData:\n"
903 << " Error reading line " << nLines << ".\n"
904 << " Node (" << i << ", " << j << ", " << k
905 << ") has already been set.\n";
906 continue;
907 }
908 // Get the field values.
909 if (fmt == Format::XY || fmt == Format::IJ) {
910 // Two-dimensional field-map
911 fz = 0.;
912 data >> fx >> fy;
913 } else if (fmt == Format::YXZ) {
914 data >> fy >> fx >> fz;
915 } else {
916 data >> fx >> fy >> fz;
917 }
918 if (data.fail()) {
919 PrintError(m_className + "::LoadData", nLines, "field components");
920 bad = true;
921 break;
922 }
923 fx *= scaleF;
924 fy *= scaleF;
925 fz *= scaleF;
926 if (withPotential) {
927 data >> p;
928 if (data.fail()) {
929 PrintError(m_className + "::LoadData", nLines, "potential");
930 bad = true;
931 break;
932 }
933 p *= scaleP;
934 if (m_pMin > m_pMax) {
935 // First value.
936 m_pMin = p;
937 m_pMax = p;
938 } else {
939 if (p < m_pMin) m_pMin = p;
940 if (p > m_pMax) m_pMax = p;
941 }
942 }
943 int flag = 0;
944 if (withFlag) {
945 data >> flag;
946 if (data.fail()) {
947 PrintError(m_className + "::LoadData", nLines, "region");
948 bad = true;
949 break;
950 }
951 }
952 const bool isActive = flag == 0 ? false : true;
953 if (fmt == Format::XY || fmt == Format::IJ) {
954 // Two-dimensional field-map
955 for (unsigned int kk = 0; kk < m_nX[2]; ++kk) {
956 fields[i][j][kk].fx = fx;
957 fields[i][j][kk].fy = fy;
958 fields[i][j][kk].fz = fz;
959 fields[i][j][kk].v = p;
960 if (withFlag) m_active[i][j][kk] = isActive;
961 isSet[i][j][kk] = true;
962 }
963 } else {
964 fields[i][j][k].fx = fx;
965 fields[i][j][k].fy = fy;
966 fields[i][j][k].fz = fz;
967 fields[i][j][k].v = p;
968 isSet[i][j][k] = true;
969 }
970 ++nValues;
971 }
972 infile.close();
973 if (bad) return false;
974 std::cout << m_className << "::LoadData:\n"
975 << " Read " << nValues << " values from " << filename << ".\n";
976 unsigned int nExpected = m_nX[0] * m_nX[1];
977 if (fmt == Format::XYZ || fmt == Format::IJK || fmt == Format::YXZ) {
978 nExpected *= m_nX[2];
979 }
980 if (nExpected != nValues) {
981 std::cerr << m_className << "::LoadData:\n"
982 << " Expected " << nExpected << " values.\n";
983 }
984 return true;
985}
986
987bool ComponentGrid::GetBoundingBox(double& xmin, double& ymin, double& zmin,
988 double& xmax, double& ymax, double& zmax) {
989 if (!m_ready) return false;
990 if (m_periodic[0] || m_mirrorPeriodic[0]) {
991 xmin = -INFINITY;
992 xmax = +INFINITY;
993 } else {
994 xmin = m_xMin[0];
995 xmax = m_xMax[0];
996 }
997
998 if (m_periodic[1] || m_mirrorPeriodic[1]) {
999 ymin = -INFINITY;
1000 ymax = +INFINITY;
1001 } else {
1002 ymin = m_xMin[1];
1003 ymax = m_xMax[1];
1004 }
1005
1006 if (m_periodic[2] || m_mirrorPeriodic[2]) {
1007 zmin = -INFINITY;
1008 zmax = +INFINITY;
1009 } else {
1010 zmin = m_xMin[2];
1011 zmax = m_xMax[2];
1012 }
1013 return true;
1014}
1015
1017 double& xmin, double& ymin, double& zmin,
1018 double& xmax, double& ymax, double& zmax) {
1019
1020 if (!m_ready) return false;
1021 xmin = m_xMin[0];
1022 xmax = m_xMax[0];
1023 ymin = m_xMin[1];
1024 ymax = m_xMax[1];
1025 zmin = m_xMin[2];
1026 zmax = m_xMax[2];
1027 return true;
1028}
1029
1030bool ComponentGrid::GetVoltageRange(double& vmin, double& vmax) {
1031 if (!m_ready) return false;
1032 vmin = m_pMin;
1033 vmax = m_pMax;
1034 return true;
1035}
1036
1037bool ComponentGrid::GetElectricFieldRange(double& exmin, double& exmax,
1038 double& eymin, double& eymax,
1039 double& ezmin, double& ezmax) {
1040 if (!m_ready) {
1041 PrintNotReady(m_className + "::GetElectricFieldRange");
1042 return false;
1043 }
1044
1045 exmin = exmax = m_efields[0][0][0].fx;
1046 eymin = eymax = m_efields[0][0][0].fy;
1047 ezmin = ezmax = m_efields[0][0][0].fz;
1048 for (unsigned int i = 0; i < m_nX[0]; ++i) {
1049 for (unsigned int j = 0; j < m_nX[1]; ++j) {
1050 for (unsigned int k = 0; k < m_nX[2]; ++k) {
1051 const Node& node = m_efields[i][j][k];
1052 if (node.fx < exmin) exmin = node.fx;
1053 if (node.fx > exmax) exmax = node.fx;
1054 if (node.fy < eymin) eymin = node.fy;
1055 if (node.fy > eymax) eymax = node.fy;
1056 if (node.fz < ezmin) ezmin = node.fz;
1057 if (node.fz > ezmax) ezmax = node.fz;
1058 }
1059 }
1060 }
1061 return true;
1062}
1063
1065 if (!m) {
1066 std::cerr << m_className << "::SetMedium: Null pointer.\n";
1067 }
1068 m_medium = m;
1069}
1070
1071bool ComponentGrid::GetField(
1072 const double xi, const double yi, const double zi,
1073 const std::vector<std::vector<std::vector<Node> > >& field, double& fx,
1074 double& fy, double& fz, double& p, bool& active) {
1075 if (!m_hasMesh) {
1076 std::cerr << m_className << "::GetField: Mesh is not set.\n";
1077 return false;
1078 }
1079
1080 // Reduce the point to the basic cell (in case of periodicity) and
1081 // check if it is inside the mesh.
1082 std::array<bool, 3> mirrored = {false, false, false};
1083 std::array<double, 3> xx = {xi, yi, zi};
1084 for (size_t i = 0; i < 3; ++i) {
1085 xx[i] = Reduce(xx[i], m_xMin[i], m_xMax[i],
1086 m_periodic[i], m_mirrorPeriodic[i], mirrored[i]);
1087 if (xx[i] < m_xMin[i] || xx[i] > m_xMax[i]) return false;
1088 }
1089 // Get the indices.
1090 const double sx = (xx[0] - m_xMin[0]) * m_sX[0];
1091 const double sy = (xx[1] - m_xMin[1]) * m_sX[1];
1092 const double sz = (xx[2] - m_xMin[2]) * m_sX[2];
1093 const unsigned int i0 = static_cast<unsigned int>(std::floor(sx));
1094 const unsigned int j0 = static_cast<unsigned int>(std::floor(sy));
1095 const unsigned int k0 = static_cast<unsigned int>(std::floor(sz));
1096 const double ux = sx - i0;
1097 const double uy = sy - j0;
1098 const double uz = sz - k0;
1099 const unsigned int i1 = std::min(i0 + 1, m_nX[0] - 1);
1100 const unsigned int j1 = std::min(j0 + 1, m_nX[1] - 1);
1101 const unsigned int k1 = std::min(k0 + 1, m_nX[2] - 1);
1102 const double vx = 1. - ux;
1103 const double vy = 1. - uy;
1104 const double vz = 1. - uz;
1105 if (!m_active.empty()) {
1106 active = m_active[i0][j0][k0] && m_active[i0][j0][k1] &&
1107 m_active[i0][j1][k0] && m_active[i0][j1][k1] &&
1108 m_active[i1][j0][k0] && m_active[i1][j0][k1] &&
1109 m_active[i1][j1][k0] && m_active[i1][j1][k1];
1110 }
1111 const Node& n000 = field[i0][j0][k0];
1112 const Node& n100 = field[i1][j0][k0];
1113 const Node& n010 = field[i0][j1][k0];
1114 const Node& n110 = field[i1][j1][k0];
1115 const Node& n001 = field[i0][j0][k1];
1116 const Node& n101 = field[i1][j0][k1];
1117 const Node& n011 = field[i0][j1][k1];
1118 const Node& n111 = field[i1][j1][k1];
1119
1120 if (m_debug) {
1121 std::cout << m_className << "::GetField: Determining field at (" << xi
1122 << ", " << yi << ", " << zi << ").\n"
1123 << " X: " << i0 << " (" << ux << ") - " << i1 << " (" << vx
1124 << ").\n"
1125 << " Y: " << j0 << " (" << uy << ") - " << j1 << " (" << vy
1126 << ").\n"
1127 << " Z: " << k0 << " (" << uz << ") - " << k1 << " (" << vz
1128 << ").\n";
1129 }
1130 fx = ((n000.fx * vx + n100.fx * ux) * vy +
1131 (n010.fx * vx + n110.fx * ux) * uy) *
1132 vz +
1133 ((n001.fx * vx + n101.fx * ux) * vy +
1134 (n011.fx * vx + n111.fx * ux) * uy) *
1135 uz;
1136 fy = ((n000.fy * vx + n100.fy * ux) * vy +
1137 (n010.fy * vx + n110.fy * ux) * uy) *
1138 vz +
1139 ((n001.fy * vx + n101.fy * ux) * vy +
1140 (n011.fy * vx + n111.fy * ux) * uy) *
1141 uz;
1142 fz = ((n000.fz * vx + n100.fz * ux) * vy +
1143 (n010.fz * vx + n110.fz * ux) * uy) *
1144 vz +
1145 ((n001.fz * vx + n101.fz * ux) * vy +
1146 (n011.fz * vx + n111.fz * ux) * uy) *
1147 uz;
1148 p = ((n000.v * vx + n100.v * ux) * vy + (n010.v * vx + n110.v * ux) * uy) *
1149 vz +
1150 ((n001.v * vx + n101.v * ux) * vy + (n011.v * vx + n111.v * ux) * uy) *
1151 uz;
1152 if (mirrored[0]) fx = -fx;
1153 if (mirrored[1]) fy = -fy;
1154 if (mirrored[2]) fz = -fz;
1155 return true;
1156}
1157
1158bool ComponentGrid::GetElectricField(const unsigned int i, const unsigned int j,
1159 const unsigned int k, double& v,
1160 double& ex, double& ey, double& ez) const {
1161 v = ex = ey = ez = 0.;
1162 if (!m_ready) {
1163 if (!m_hasMesh) {
1164 std::cerr << m_className << "::GetElectricField: Mesh not set.\n";
1165 return false;
1166 }
1167 PrintNotReady(m_className + "::GetElectricField");
1168 return false;
1169 }
1170 if (i >= m_nX[0] || j >= m_nX[1] || k >= m_nX[2]) {
1171 std::cerr << m_className << "::GetElectricField: Index out of range.\n";
1172 return false;
1173 }
1174 const Node& node = m_efields[i][j][k];
1175 v = node.v;
1176 ex = node.fx;
1177 ey = node.fy;
1178 ez = node.fz;
1179 return true;
1180}
1181
1183
1184 std::cout << m_className << "::Print:\n";
1185 if (!m_hasMesh) {
1186 std::cout << " Mesh not set.\n";
1187 return;
1188 }
1189 std::printf(" %15.8f < x [cm] < %15.8f, %10u nodes\n",
1190 m_xMin[0], m_xMax[0], m_nX[0]);
1191 std::printf(" %15.8f < y [cm] < %15.8f, %10u nodes\n",
1192 m_xMin[1], m_xMax[1], m_nX[1]);
1193 std::printf(" %15.8f < z [cm] < %15.8f, %10u nodes\n",
1194 m_xMin[2], m_xMax[2], m_nX[2]);
1195 if (m_efields.empty() && m_bfields.empty() &&
1196 m_wfields.empty() && m_wdfields.empty() &&
1197 m_eAttachment.empty() && m_hAttachment.empty() &&
1198 m_eVelocity.empty() && m_hVelocity.empty()) {
1199 std::cout << " Available data: None.\n";
1200 return;
1201 }
1202 std::cout << " Available data:\n";
1203 if (!m_efields.empty()) std::cout << " Electric field.\n";
1204 if (!m_bfields.empty()) std::cout << " Magnetic field.\n";
1205 if (!m_wfields.empty()) std::cout << " Weighting field.\n";
1206 if (!m_wdfields.empty()) {
1207 std::cout << " Delayed weighting field.\n";
1208 }
1209 if (!m_eVelocity.empty()) {
1210 std::cout << " Electron drift velocity.\n";
1211 }
1212 if (!m_hVelocity.empty()) {
1213 std::cout << " Hole drift velocity.\n";
1214 }
1215 if (!m_eAttachment.empty()) {
1216 std::cout << " Electron attachment coefficient.\n";
1217 }
1218 if (!m_hAttachment.empty()) {
1219 std::cout << " Hole attachment coefficient.\n";
1220 }
1221}
1222
1223void ComponentGrid::Reset() {
1224 m_efields.clear();
1225 m_bfields.clear();
1226 m_wfields.clear();
1227 m_eAttachment.clear();
1228 m_hAttachment.clear();
1229 m_eVelocity.clear();
1230 m_hVelocity.clear();
1231
1232 m_wdfields.clear();
1233 m_wdtimes.clear();
1234
1235 m_active.clear();
1236
1237 m_nX.fill(1);
1238 m_xMin.fill(0.);
1239 m_xMax.fill(0.);
1240 m_sX[0] = m_sX[1] = m_sX[2] = 0.;
1241 m_pMin = m_pMax = 0.;
1242 m_medium = nullptr;
1243
1244 m_hasMesh = false;
1245 m_hasPotential = false;
1246 m_ready = false;
1247
1248 m_wFieldOffset.fill(0.);
1249}
1250
1251void ComponentGrid::UpdatePeriodicity() {
1252 if (!m_ready) {
1253 PrintNotReady(m_className + "::UpdatePeriodicity");
1254 return;
1255 }
1256
1257 // Check for conflicts.
1258 for (size_t i = 0; i < 3; ++i) {
1259 if (m_periodic[i] && m_mirrorPeriodic[i]) {
1260 std::cerr << m_className << "::UpdatePeriodicity:\n"
1261 << " Both simple and mirror periodicity requested. Reset.\n";
1262 m_periodic[i] = m_mirrorPeriodic[i] = false;
1263 }
1264 }
1265
1267 std::cerr << m_className << "::UpdatePeriodicity:\n"
1268 << " Axial symmetry is not supported. Reset.\n";
1269 m_axiallyPeriodic.fill(false);
1270 }
1271
1274 std::cerr << m_className << "::UpdatePeriodicity:\n"
1275 << " Rotation symmetry is not supported. Reset.\n";
1276 m_rotationSymmetric.fill(false);
1277 }
1278}
1279
1280double ComponentGrid::Reduce(const double xin, const double xmin,
1281 const double xmax, const bool simplePeriodic,
1282 const bool mirrorPeriodic, bool& mirrored) const {
1283 // In case of periodicity, reduce the coordinate to the basic cell.
1284 double x = xin;
1285 const double lx = xmax - xmin;
1286 if (simplePeriodic) {
1287 x = xmin + fmod(x - xmin, lx);
1288 if (x < xmin) x += lx;
1289 } else if (mirrorPeriodic) {
1290 double xNew = xmin + fmod(x - xmin, lx);
1291 if (xNew < xmin) xNew += lx;
1292 const int nx = int(floor(0.5 + (xNew - x) / lx));
1293 if (nx != 2 * (nx / 2)) {
1294 xNew = xmin + xmax - xNew;
1295 mirrored = true;
1296 }
1297 x = xNew;
1298 }
1299 return x;
1300}
1301
1302void ComponentGrid::Initialise(
1303 std::vector<std::vector<std::vector<Node> > >& fields) {
1304 fields.resize(m_nX[0]);
1305 for (unsigned int i = 0; i < m_nX[0]; ++i) {
1306 fields[i].resize(m_nX[1]);
1307 for (unsigned int j = 0; j < m_nX[1]; ++j) {
1308 fields[i][j].resize(m_nX[2]);
1309 for (unsigned int k = 0; k < m_nX[2]; ++k) {
1310 fields[i][j][k].fx = 0.;
1311 fields[i][j][k].fy = 0.;
1312 fields[i][j][k].fz = 0.;
1313 fields[i][j][k].v = 0.;
1314 }
1315 }
1316 }
1317}
1318
1319bool ComponentGrid::LoadElectronVelocity(const std::string& fname,
1320 const std::string& fmt,
1321 const double scaleX,
1322 const double scaleV) {
1323 // Read the file.
1324 if (!LoadData(fname, fmt, false, false, scaleX, scaleV, 1., m_eVelocity)) {
1325 return false;
1326 }
1327 return true;
1328}
1329
1330bool ComponentGrid::LoadHoleVelocity(const std::string& fname,
1331 const std::string& fmt,
1332 const double scaleX,
1333 const double scaleV) {
1334 // Read the file.
1335 if (!LoadData(fname, fmt, false, false, scaleX, scaleV, 1., m_hVelocity)) {
1336 return false;
1337 }
1338 return true;
1339}
1340
1341bool ComponentGrid::ElectronVelocity(const double x, const double y,
1342 const double z,
1343 double& vx, double& vy, double& vz) {
1344 if (m_eVelocity.empty()) {
1345 PrintNotReady(m_className + "::ElectronVelocity");
1346 return false;
1347 }
1348 double p = 0.;
1349 bool active = true;
1350 return GetField(x, y, z, m_eVelocity, vx, vy, vz, p, active);
1351}
1352
1353bool ComponentGrid::HoleVelocity(const double x, const double y,
1354 const double z,
1355 double& vx, double& vy, double& vz) {
1356 if (m_hVelocity.empty()) {
1357 PrintNotReady(m_className + "::HoleVelocity");
1358 return false;
1359 }
1360 double p = 0.;
1361 bool active = true;
1362 return GetField(x, y, z, m_hVelocity, vx, vy, vz, p, active);
1363}
1364
1365bool ComponentGrid::LoadElectronAttachment(const std::string& fname,
1366 const std::string& fmt,
1367 const unsigned int col,
1368 const double scaleX) {
1369 // Read the file.
1370 return LoadData(fname, fmt, scaleX, m_eAttachment, col);
1371}
1372
1373bool ComponentGrid::LoadHoleAttachment(const std::string& fname,
1374 const std::string& fmt,
1375 const unsigned int col,
1376 const double scaleX) {
1377 // Read the file.
1378 return LoadData(fname, fmt, scaleX, m_hAttachment, col);
1379}
1380
1381bool ComponentGrid::LoadData(
1382 const std::string& filename, std::string format, const double scaleX,
1383 std::vector<std::vector<std::vector<double> > >& tab,
1384 const unsigned int col) {
1385 if (!m_hasMesh) {
1386 if (!LoadMesh(filename, format, scaleX)) {
1387 std::cerr << m_className << "::LoadData: Mesh not set.\n";
1388 return false;
1389 }
1390 }
1391
1392 const auto fmt = GetFormat(format);
1393 if (fmt == Format::Unknown) {
1394 std::cerr << m_className << "::LoadData:\n"
1395 << " Unknown format (" << format << ").\n";
1396 return false;
1397 }
1398 // Check the column index.
1399 unsigned int offset = 0;
1400 if (fmt == Format::XY || fmt == Format::IJ) {
1401 if (col < 2) {
1402 std::cerr << m_className << "::LoadData:\n"
1403 << " Unexpected column index (" << col << ").\n";
1404 return false;
1405 }
1406 offset = 2;
1407 } else {
1408 if (col < 3) {
1409 std::cerr << m_className << "::LoadData:\n"
1410 << " Unexpected column index (" << col << ").\n";
1411 return false;
1412 }
1413 offset = 3;
1414 }
1415
1416 // Set up the grid.
1417 tab.assign(
1418 m_nX[0],
1419 std::vector<std::vector<double> >(m_nX[1], std::vector<double>(m_nX[2], 0.)));
1420
1421 unsigned int nValues = 0;
1422 // Keep track of which elements have been read.
1423 std::vector<std::vector<std::vector<bool> > > isSet(
1424 m_nX[0],
1425 std::vector<std::vector<bool> >(m_nX[1], std::vector<bool>(m_nX[2], false)));
1426
1427 std::ifstream infile;
1428 infile.open(filename.c_str(), std::ios::in);
1429 if (!infile) {
1430 std::cerr << m_className << "::LoadData:\n"
1431 << " Could not open file " << filename << ".\n";
1432 return false;
1433 }
1434
1435 std::string line;
1436 unsigned int nLines = 0;
1437 bool bad = false;
1438 // Read the file line by line.
1439 while (std::getline(infile, line)) {
1440 ++nLines;
1441 // Strip white space from beginning of line.
1442 ltrim(line);
1443 // Skip empty lines.
1444 if (line.empty()) continue;
1445 // Skip comments.
1446 if (IsComment(line)) continue;
1447 unsigned int i = 0;
1448 unsigned int j = 0;
1449 unsigned int k = 0;
1450 double val = 0;
1451 std::istringstream data;
1452 data.str(line);
1453 if (fmt == Format::XY) {
1454 double x, y;
1455 data >> x >> y;
1456 if (data.fail()) {
1457 PrintError(m_className + "::LoadData", nLines, "coordinates");
1458 bad = true;
1459 break;
1460 }
1461 x *= scaleX;
1462 y *= scaleX;
1463 if (m_nX[0] > 1) {
1464 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1465 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1466 if (i >= m_nX[0]) i = m_nX[0] - 1;
1467 }
1468 if (m_nX[1] > 1) {
1469 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1470 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1471 if (j >= m_nX[1]) j = m_nX[1] - 1;
1472 }
1473 } else if (fmt == Format::XYZ) {
1474 double x, y, z;
1475 data >> x >> y >> z;
1476 if (data.fail()) {
1477 PrintError(m_className + "::LoadData", nLines, "coordinates");
1478 bad = true;
1479 break;
1480 }
1481 x *= scaleX;
1482 y *= scaleX;
1483 z *= scaleX;
1484 if (m_nX[0] > 1) {
1485 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1486 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1487 if (i >= m_nX[0]) i = m_nX[0] - 1;
1488 }
1489 if (m_nX[1] > 1) {
1490 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1491 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1492 if (j >= m_nX[1]) j = m_nX[1] - 1;
1493 }
1494 if (m_nX[2] > 1) {
1495 const double w = std::round((z - m_xMin[2]) * m_sX[2]);
1496 k = w < 0. ? 0 : static_cast<unsigned int>(w);
1497 if (k >= m_nX[2]) k = m_nX[2] - 1;
1498 }
1499 } else if (fmt == Format::IJ) {
1500 data >> i >> j;
1501 if (data.fail()) {
1502 PrintError(m_className + "::LoadData", nLines, "indices");
1503 bad = true;
1504 break;
1505 }
1506 } else if (fmt == Format::IJK) {
1507 data >> i >> j >> k;
1508 if (data.fail()) {
1509 PrintError(m_className + "::LoadData", nLines, "indices");
1510 bad = true;
1511 break;
1512 }
1513 } else if (fmt == Format::YXZ) {
1514 double x, y, z;
1515 data >> y >> x >> z;
1516 if (data.fail()) {
1517 PrintError(m_className + "::LoadData", nLines, "coordinates");
1518 bad = true;
1519 break;
1520 }
1521 x *= scaleX;
1522 y *= scaleX;
1523 z *= scaleX;
1524 if (m_nX[0] > 1) {
1525 const double u = std::round((x - m_xMin[0]) * m_sX[0]);
1526 i = u < 0. ? 0 : static_cast<unsigned int>(u);
1527 if (i >= m_nX[0]) i = m_nX[0] - 1;
1528 }
1529 if (m_nX[1] > 1) {
1530 const double v = std::round((y - m_xMin[1]) * m_sX[1]);
1531 j = v < 0. ? 0 : static_cast<unsigned int>(v);
1532 if (j >= m_nX[1]) j = m_nX[1] - 1;
1533 }
1534 if (m_nX[2] > 1) {
1535 const double w = std::round((z - m_xMin[2]) * m_sX[2]);
1536 k = w < 0. ? 0 : static_cast<unsigned int>(w);
1537 if (k >= m_nX[2]) k = m_nX[2] - 1;
1538 }
1539 }
1540 // Check the indices.
1541 if (i >= m_nX[0] || j >= m_nX[1] || k >= m_nX[2]) {
1542 std::cerr << m_className << "::LoadData:\n"
1543 << " Error reading line " << nLines << ".\n"
1544 << " Index (" << i << ", " << j << ", " << k
1545 << ") out of range.\n";
1546 continue;
1547 }
1548 if (isSet[i][j][k]) {
1549 std::cerr << m_className << "::LoadData:\n"
1550 << " Error reading line " << nLines << ".\n"
1551 << " Node (" << i << ", " << j << ", " << k
1552 << ") has already been set.\n";
1553 continue;
1554 }
1555
1556 // Skip to the requested column.
1557 for (unsigned int ii = 0; ii < col - offset; ++ii) {
1558 double dummy = 0.;
1559 data >> dummy;
1560 if (data.fail()) {
1561 PrintError(m_className + "::LoadData", nLines,
1562 "column " + std::to_string(offset + ii));
1563 break;
1564 }
1565 }
1566 if (data.fail()) {
1567 bad = true;
1568 break;
1569 }
1570 data >> val;
1571
1572 if (data.fail()) {
1573 PrintError(m_className + "::LoadData", nLines,
1574 "column " + std::to_string(col));
1575 bad = true;
1576 break;
1577 }
1578
1579 if (fmt == Format::XY || fmt == Format::IJ) {
1580 // Two-dimensional map
1581 for (unsigned int kk = 0; kk < m_nX[2]; ++kk) {
1582 tab[i][j][kk] = val;
1583 isSet[i][j][kk] = true;
1584 }
1585 } else {
1586 tab[i][j][k] = val;
1587 isSet[i][j][k] = true;
1588 }
1589 ++nValues;
1590 }
1591 infile.close();
1592 if (bad) return false;
1593 std::cout << m_className << "::LoadData:\n"
1594 << " Read " << nValues << " values from " << filename << ".\n";
1595 unsigned int nExpected = m_nX[0] * m_nX[1];
1596 if (fmt == Format::XYZ || fmt == Format::IJK || fmt == Format::YXZ) {
1597 nExpected *= m_nX[2];
1598 }
1599 if (nExpected != nValues) {
1600 std::cerr << m_className << "::LoadData:\n"
1601 << " Expected " << nExpected << " values.\n";
1602 }
1603 return true;
1604}
1605
1606bool ComponentGrid::GetData(
1607 const double xi, const double yi, const double zi,
1608 const std::vector<std::vector<std::vector<double> > >& tab, double& val) {
1609 if (!m_hasMesh) {
1610 std::cerr << m_className << "::GetData: Mesh is not set.\n";
1611 return false;
1612 }
1613
1614 // Reduce the point to the basic cell (in case of periodicity) and
1615 // check if it is inside the mesh.
1616 bool xMirrored = false;
1617 const double x =
1618 Reduce(xi, m_xMin[0], m_xMax[0], m_periodic[0], m_mirrorPeriodic[0], xMirrored);
1619 if (x < m_xMin[0] || x > m_xMax[0]) return false;
1620 bool yMirrored = false;
1621 const double y =
1622 Reduce(yi, m_xMin[1], m_xMax[1], m_periodic[1], m_mirrorPeriodic[1], yMirrored);
1623 if (y < m_xMin[1] || y > m_xMax[1]) return false;
1624 bool zMirrored = false;
1625 const double z =
1626 Reduce(zi, m_xMin[2], m_xMax[2], m_periodic[2], m_mirrorPeriodic[2], zMirrored);
1627 if (z < m_xMin[2] || z > m_xMax[2]) return false;
1628
1629 // Get the indices.
1630 const double sx = (x - m_xMin[0]) * m_sX[0];
1631 const double sy = (y - m_xMin[1]) * m_sX[1];
1632 const double sz = (z - m_xMin[2]) * m_sX[2];
1633 const unsigned int i0 = static_cast<unsigned int>(std::floor(sx));
1634 const unsigned int j0 = static_cast<unsigned int>(std::floor(sy));
1635 const unsigned int k0 = static_cast<unsigned int>(std::floor(sz));
1636 const double ux = sx - i0;
1637 const double uy = sy - j0;
1638 const double uz = sz - k0;
1639 const unsigned int i1 = std::min(i0 + 1, m_nX[0] - 1);
1640 const unsigned int j1 = std::min(j0 + 1, m_nX[1] - 1);
1641 const unsigned int k1 = std::min(k0 + 1, m_nX[2] - 1);
1642 const double vx = 1. - ux;
1643 const double vy = 1. - uy;
1644 const double vz = 1. - uz;
1645 const double n000 = tab[i0][j0][k0];
1646 const double n100 = tab[i1][j0][k0];
1647 const double n010 = tab[i0][j1][k0];
1648 const double n110 = tab[i1][j1][k0];
1649 const double n001 = tab[i0][j0][k1];
1650 const double n101 = tab[i1][j0][k1];
1651 const double n011 = tab[i0][j1][k1];
1652 const double n111 = tab[i1][j1][k1];
1653
1654 if (m_debug) {
1655 std::cout << m_className << "::GetData: Interpolating at (" << xi
1656 << ", " << yi << ", " << zi << ").\n"
1657 << " X: " << i0 << " (" << ux << ") - " << i1 << " (" << vx
1658 << ").\n"
1659 << " Y: " << j0 << " (" << uy << ") - " << j1 << " (" << vy
1660 << ").\n"
1661 << " Z: " << k0 << " (" << uz << ") - " << k1 << " (" << vz
1662 << ").\n";
1663 }
1664 val = ((n000 * vx + n100 * ux) * vy + (n010 * vx + n110 * ux) * uy) * vz +
1665 ((n001 * vx + n101 * ux) * vy + (n011 * vx + n111 * ux) * uy) * uz;
1666
1667 return true;
1668}
1669
1670bool ComponentGrid::ElectronAttachment(const double x, const double y,
1671 const double z, double& att) {
1672 // Make sure the map has been loaded.
1673 if (m_eAttachment.empty()) {
1674 PrintNotReady(m_className + "::ElectronAttachment");
1675 return false;
1676 }
1677 return GetData(x, y, z, m_eAttachment, att);
1678}
1679
1680bool ComponentGrid::HoleAttachment(const double x, const double y,
1681 const double z, double& att) {
1682 // Make sure the map has been loaded.
1683 if (m_hAttachment.empty()) {
1684 PrintNotReady(m_className + "::HoleAttachment");
1685 return false;
1686 }
1687 return GetData(x, y, z, m_hAttachment, att);
1688}
1689
1690ComponentGrid::Format ComponentGrid::GetFormat(std::string format) {
1691 std::transform(format.begin(), format.end(), format.begin(), toupper);
1692 if (format == "XY") {
1693 return Format::XY;
1694 } else if (format == "XYZ") {
1695 return Format::XYZ;
1696 } else if (format == "IJ") {
1697 return Format::IJ;
1698 } else if (format == "IJK") {
1699 return Format::IJK;
1700 } else if (format == "YXZ") {
1701 return Format::YXZ;
1702 }
1703 return Format::Unknown;
1704}
1705
1706} // 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)
bool SaveElectricField(Component *cmp, const std::string &filename, const std::string &format)
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)
bool GetElementaryCell(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the coordinates of the elementary cell.
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.
bool SaveWeightingField(Component *cmp, const std::string &id, const std::string &filename, const std::string &format)
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.)
Abstract base class for components.
Definition: Component.hh:13
virtual double WeightingPotential(const double x, const double y, const double z, const std::string &label)
Definition: Component.cc:59
std::array< bool, 3 > m_rotationSymmetric
Rotation symmetry around x-axis, y-axis, z-axis.
Definition: Component.hh:350
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
Definition: Component.hh:346
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:40
bool m_debug
Switch on/off debugging messages.
Definition: Component.hh:341
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition: Component.hh:344
std::string m_className
Class name.
Definition: Component.hh:329
virtual void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
Definition: Component.cc:81
bool m_ready
Ready for use?
Definition: Component.hh:338
std::array< bool, 3 > m_axiallyPeriodic
Axial periodicity in x, y, z.
Definition: Component.hh:348
Abstract base class for media.
Definition: Medium.hh:13
void ltrim(std::string &line)
Definition: Utilities.hh:10
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615