Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentTcadBase.cc
Go to the documentation of this file.
1#include <cmath>
2#include <fstream>
3#include <iostream>
4#include <iomanip>
5#include <map>
6#include <sstream>
7#include <string>
8
11#include "Garfield/Utilities.hh"
12
13namespace {
14
15bool ExtractFromSquareBrackets(std::string& line) {
16
17 const auto bra = line.find('[');
18 const auto ket = line.find(']');
19 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
20 return false;
21 }
22 line = line.substr(bra + 1, ket - bra - 1);
23 return true;
24}
25
26bool ExtractFromBrackets(std::string& line) {
27
28 const auto bra = line.find('(');
29 const auto ket = line.find(')');
30 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
31 return false;
32 }
33 line = line.substr(bra + 1, ket - bra - 1);
34 return true;
35}
36
37void PrintError(const std::string& fcn, const std::string& filename,
38 const unsigned int line) {
39 std::cerr << fcn << ":\n"
40 << " Error reading file " << filename
41 << " (line " << line << ").\n";
42}
43
45
46namespace Garfield {
47
48template<size_t N>
50 const double x, const double y, const double z,
51 double& wx, double& wy, double& wz, const std::string& label) {
52 wx = wy = wz = 0.;
53 if (m_wfield.empty()) {
54 std::cerr << m_className << "::WeightingField: Not available.\n";
55 return;
56 }
57 double dx = 0., dy = 0., dz = 0.;
58 if (!GetOffset(label, dx, dy, dz)) return;
59 Interpolate(x - dx, y - dy, z - dz, m_wfield, wx, wy, wz);
60}
61
62template<size_t N>
64 const double x, const double y, const double z,
65 const std::string& label) {
67 if (m_wpot.empty()) {
68 std::cerr << m_className << "::WeightingPotential: Not available.\n";
69 return 0.;
70 }
71 double dx = 0., dy = 0., dz = 0.;
72 if (!GetOffset(label, dx, dy, dz)) return 0.;
73 double v = 0.;
74 Interpolate(x - dx, y - dy, z - dz, m_wpot, v);
75 return v;
76}
77
78template<size_t N>
80 const double x, const double y, const double z, const double t,
81 double& wx, double& wy, double& wz, const std::string& label) {
82 wx = wy = wz = 0.;
83 if (m_dwf.empty()) {
84 std::cerr << m_className << "::DelayedWeightingField: Not available.\n";
85 return;
86 }
87 if (m_dwtf.empty()) return;
88 if (t < m_dwtf.front() || t > m_dwtf.back()) return;
89
90 double dx = 0., dy = 0., dz = 0.;
91 if (!GetOffset(label, dx, dy, dz)) return;
92
93 const auto it1 = std::upper_bound(m_dwtf.cbegin(), m_dwtf.cend(), t);
94 const auto it0 = std::prev(it1);
95 const double dt = t - *it0;
96 const auto i0 = std::distance(m_dwtf.cbegin(), it0);
97 double wx0 = 0., wy0 = 0., wz0 = 0.;
98 Interpolate(x - dx, y - dy, z - dz, m_dwf[i0], wx0, wy0, wz0);
99 if (dt < Small || it1 == m_dwtf.cend()) {
100 wx = wx0;
101 wy = wy0;
102 wz = wz0;
103 return;
104 }
105 const auto i1 = std::distance(m_dwtf.cbegin(), it1);
106 double wx1 = 0., wy1 = 0., wz1 = 0.;
107 Interpolate(x - dx, y - dy, z - dz, m_dwf[i1], wx1, wy1, wz1);
108 const double f1 = dt / (*it1 - *it0);
109 const double f0 = 1. - f1;
110 wx = f0 * wx0 + f1 * wx1;
111 wy = f0 * wy0 + f1 * wy1;
112 wz = f0 * wz0 + f1 * wz1;
113}
115template<size_t N>
117 const double x, const double y, const double z, const double t,
118 const std::string& label) {
120 if (m_dwp.empty()) {
121 std::cerr << m_className << "::DelayedWeightingPotential: Not available.\n";
122 return 0.;
124 if (m_dwtp.empty()) return 0.;
125 if (t < m_dwtp.front() || t > m_dwtp.back()) return 0.;
126
127 double dx = 0., dy = 0., dz = 0.;
128 if (!GetOffset(label, dx, dy, dz)) return 0.;
129
130 const auto it1 = std::upper_bound(m_dwtp.cbegin(), m_dwtp.cend(), t);
131 const auto it0 = std::prev(it1);
132 const double dt = t - *it0;
133 const auto i0 = std::distance(m_dwtp.cbegin(), it0);
134 double v0 = 0.;
135 Interpolate(x - dx, y - dy, z - dz, m_dwp[i0], v0);
136 if (dt < Small || it1 == m_dwtp.cend()) return v0;
137
138 const auto i1 = std::distance(m_dwtp.cbegin(), it1);
139 double v1 = 0.;
140 Interpolate(x - dx, y - dy, z - dz, m_dwp[i1], v1);
141 const double f1 = dt / (*it1 - *it0);
142 const double f0 = 1. - f1;
143 return f0 * v0 + f1 * v1;
144}
146template<size_t N>
148 const std::string& label, double& dx, double& dy, double& dz) const {
149
150 const auto it = std::find(m_wlabel.cbegin(), m_wlabel.cend(), label);
151 if (it == m_wlabel.end()) return false;
152 const auto i = std::distance(m_wlabel.begin(), it);
153 dx = m_wshift[i][0];
154 dy = m_wshift[i][1];
155 dz = m_wshift[i][2];
156 return true;
157}
158
159template<size_t N>
160bool ComponentTcadBase<N>::Initialise(const std::string& gridfilename,
161 const std::string& datafilename) {
162
163 m_ready = false;
164 Cleanup();
165 // Import mesh data from .grd file.
166 if (!LoadGrid(gridfilename)) {
167 std::cerr << m_className << "::Initialise:\n"
168 << " Importing mesh data failed.\n";
169 Cleanup();
170 return false;
171 }
172 // Import electric field, potential and other data from .dat file.
173 if (!LoadData(datafilename)) {
174 std::cerr << m_className << "::Initialise:\n"
175 << " Importing electric field and potential failed.\n";
176 Cleanup();
177 return false;
178 }
179
180 // Find min./max. coordinates and potentials.
181 for (size_t i = 0; i < N; ++i) {
182 m_bbMax[i] = m_vertices[m_elements[0].vertex[0]][i];
183 m_bbMin[i] = m_bbMax[i];
184 }
185 const size_t nElements = m_elements.size();
186 for (size_t i = 0; i < nElements; ++i) {
187 Element& element = m_elements[i];
188 std::array<double, N> xmin = m_vertices[element.vertex[0]];
189 std::array<double, N> xmax = m_vertices[element.vertex[0]];
190 const auto nV = ElementVertices(m_elements[i]);
191 for (unsigned int j = 0; j < nV; ++j) {
192 const auto& v = m_vertices[m_elements[i].vertex[j]];
193 for (size_t k = 0; k < N; ++k) {
194 xmin[k] = std::min(xmin[k], v[k]);
195 xmax[k] = std::max(xmax[k], v[k]);
196 }
197 }
198 constexpr double tol = 1.e-6;
199 for (size_t k = 0; k < N; ++k) {
200 m_elements[i].bbMin[k] = xmin[k] - tol;
201 m_elements[i].bbMax[k] = xmax[k] + tol;
202 m_bbMin[k] = std::min(m_bbMin[k], xmin[k]);
203 m_bbMax[k] = std::max(m_bbMax[k], xmax[k]);
204 }
205 }
206 m_pMin = *std::min_element(m_epot.begin(), m_epot.end());
207 m_pMax = *std::max_element(m_epot.begin(), m_epot.end());
208
209 std::cout << m_className << "::Initialise:\n"
210 << " Available data:\n";
211 if (!m_epot.empty()) std::cout << " Electrostatic potential\n";
212 if (!m_efield.empty()) std::cout << " Electric field\n";
213 if (!m_eMobility.empty()) std::cout << " Electron mobility\n";
214 if (!m_hMobility.empty()) std::cout << " Hole mobility\n";
215 if (!m_eVelocity.empty()) std::cout << " Electron velocity\n";
216 if (!m_hVelocity.empty()) std::cout << " Hole velocity\n";
217 if (!m_eAlpha.empty()) std::cout << " Electron impact ionisation\n";
218 if (!m_hAlpha.empty()) std::cout << " Hole impact ionisation\n";
219 if (!m_eLifetime.empty()) std::cout << " Electron lifetime\n";
220 if (!m_hLifetime.empty()) std::cout << " Hole lifetime\n";
221 if (!m_donors.empty()) {
222 std::cout << " " << m_donors.size() << " donor-type traps\n";
223 }
224 if (!m_acceptors.empty()) {
225 std::cout << " " << m_acceptors.size() << " acceptor-type traps\n";
226 }
227 const std::array<std::string, 3> axes = {"x", "y", "z"};
228 std::cout << " Bounding box:\n";
229 for (size_t i = 0; i < N; ++i) {
230 std::cout << " " << m_bbMin[i] << " < " << axes[i] << " [cm] < "
231 << m_bbMax[i] << "\n";
232 }
233 std::cout << " Voltage range:\n"
234 << " " << m_pMin << " < V < " << m_pMax << "\n";
235
236 bool ok = true;
237
238 // Count the number of elements belonging to a region.
239 const auto nRegions = m_regions.size();
240 std::vector<size_t> nElementsByRegion(nRegions, 0);
241 // Keep track of elements that are not part of any region.
242 std::vector<size_t> looseElements;
243
244 // Count the different element shapes.
245 std::map<int, unsigned int> nElementsByShape;
246 if (N == 2) {
247 nElementsByShape = {{0, 0}, {1, 0}, {2, 0}, {3, 0}};
248 } else {
249 nElementsByShape = {{2, 0}, {5, 0}};
250 }
251 unsigned int nElementsOther = 0;
252
253 // Keep track of degenerate elements.
254 std::vector<size_t> degenerateElements;
255
256 for (size_t i = 0; i < nElements; ++i) {
257 const Element& element = m_elements[i];
258 if (element.region < nRegions) {
259 ++nElementsByRegion[element.region];
260 } else {
261 looseElements.push_back(i);
262 }
263 if (nElementsByShape.count(element.type) == 0) {
264 ++nElementsOther;
265 continue;
266 }
267 nElementsByShape[element.type] += 1;
268 bool degenerate = false;
269 const auto nV = ElementVertices(m_elements[i]);
270 for (unsigned int j = 0; j < nV; ++j) {
271 for (unsigned int k = j + 1; k < nV; ++k) {
272 if (element.vertex[j] == element.vertex[k]) {
273 degenerate = true;
274 break;
275 }
276 }
277 if (degenerate) break;
278 }
279 if (degenerate) {
280 degenerateElements.push_back(i);
281 }
282 }
284 if (!degenerateElements.empty()) {
285 std::cerr << m_className << "::Initialise:\n"
286 << " The following elements are degenerate:\n";
287 for (size_t i : degenerateElements) std::cerr << " " << i << "\n";
288 ok = false;
289 }
290
291 if (!looseElements.empty()) {
292 std::cerr << m_className << "::Initialise:\n"
293 << " The following elements are not part of any region:\n";
294 for (size_t i : looseElements) std::cerr << " " << i << "\n";
295 ok = false;
298 std::cout << m_className << "::Initialise:\n"
299 << " Number of regions: " << nRegions << "\n";
300 for (size_t i = 0; i < nRegions; ++i) {
301 std::cout << " " << i << ": " << m_regions[i].name;
302 if (!m_regions[i].material.empty()) {
303 std::cout << " (" << m_regions[i].material << ")";
304 }
305 std::cout << ", " << nElementsByRegion[i] << " elements\n";
306 }
307
308 std::map<int, std::string> shapes = {
309 {0, "points"}, {1, "lines"}, {2, "triangles"}, {3, "rectangles"},
310 {5, "tetrahedra"}};
311
312 std::cout << " Number of elements: " << nElements << "\n";
313 for (const auto& n : nElementsByShape) {
314 if (n.second > 0) {
315 std::cout << " " << n.second << " " << shapes[n.first] << "\n";
316 }
317 }
318 if (nElementsOther > 0) {
319 std::cerr << " " << nElementsOther << " elements of unknown type.\n"
320 << " Program bug!\n";
321 m_ready = false;
322 Cleanup();
323 return false;
324 }
325
326 std::cout << " Number of vertices: " << m_vertices.size() << "\n";
327 if (!ok) {
328 m_ready = false;
329 Cleanup();
330 return false;
331 }
332
333 FillTree();
334
335 m_ready = true;
336 UpdatePeriodicity();
337 std::cout << m_className << "::Initialise: Initialisation finished.\n";
338 return true;
339}
340
341template<size_t N>
342bool ComponentTcadBase<N>::GetVoltageRange(double& vmin, double& vmax) {
343 if (!m_ready) return false;
344 vmin = m_pMin;
345 vmax = m_pMax;
346 return true;
347}
348
349template<size_t N>
350bool ComponentTcadBase<N>::SetWeightingField(const std::string& datfile1,
351 const std::string& datfile2,
352 const double dv,
353 const std::string& label) {
354
355 if (!m_ready) {
356 std::cerr << m_className << "::SetWeightingField:\n"
357 << " Mesh is not available. Call Initialise first.\n";
358 return false;
359 }
360 if (dv < Small) {
361 std::cerr << m_className << "::SetWeightingField:\n"
362 << " Voltage difference must be > 0.\n";
363 return false;
364 }
365 const double s = 1. / dv;
366 m_wfield.clear();
367 m_wpot.clear();
368 m_wlabel.clear();
369 m_wshift.clear();
370
371 // Load first the field/potential at nominal bias.
372 std::vector<std::array<double, N> > wf1;
373 std::vector<double> wp1;
374 if (!LoadWeightingField(datfile1, wf1, wp1)) {
375 std::cerr << m_className << "::SetWeightingField:\n"
376 << " Could not import data from " << datfile1 << ".\n";
377 return false;
378 }
379
380 // Then load the field/potential for the configuration with the potential
381 // at the electrode to be read out increased by small voltage dv.
382 std::vector<std::array<double, N> > wf2;
383 std::vector<double> wp2;
384 if (!LoadWeightingField(datfile2, wf2, wp2)) {
385 std::cerr << m_className << "::SetWeightingField:\n"
386 << " Could not import data from " << datfile2 << ".\n";
387 return false;
388 }
389 const size_t nVertices = m_vertices.size();
390 bool foundField = true;
391 if (wf1.size() != nVertices || wf2.size() != nVertices) {
392 foundField = false;
393 std::cerr << m_className << "::SetWeightingField:\n"
394 << " Could not load electric field values.\n";
395 }
396 bool foundPotential = true;
397 if (wp1.size() != nVertices || wp2.size() != nVertices) {
398 foundPotential = false;
399 std::cerr << m_className << "::SetWeightingField:\n"
400 << " Could not load electrostatic potentials.\n";
401 }
402 if (!foundField && !foundPotential) return false;
403 if (foundField) {
404 m_wfield.resize(nVertices);
405 for (size_t i = 0; i < nVertices; ++i) {
406 for (size_t j = 0; j < N; ++j) {
407 m_wfield[i][j] = (wf2[i][j] - wf1[i][j]) * s;
408 }
409 }
410 }
411 if (foundPotential) {
412 m_wpot.assign(nVertices, 0.);
413 for (size_t i = 0; i < nVertices; ++i) {
414 m_wpot[i] = (wp2[i] - wp1[i]) * s;
415 }
416 }
417 m_wlabel.push_back(label);
418 m_wshift.push_back({0., 0., 0.});
419 return true;
420}
421
422template<size_t N>
424 const std::string& datfile1, const std::string& datfile2,
425 const double dv, const double t, const std::string& label) {
426
427 if (!m_ready) {
428 std::cerr << m_className << "::SetWeightingPotential:\n"
429 << " Mesh is not available. Call Initialise first.\n";
430 return false;
431 }
432 if (dv < Small) {
433 std::cerr << m_className << "::SetWeightingPotential:\n"
434 << " Voltage difference must be > 0.\n";
435 return false;
436 }
437 const double s = 1. / dv;
438
439 if (m_wlabel.empty()) {
440 std::cerr << m_className << "::SetWeightingPotential:\n"
441 << " Prompt component not present.\n"
442 << " Import the map for t = 0 first.\n";
443 return false;
444 }
445 if (label != m_wlabel.front()) {
446 std::cerr << m_className << "::SetWeightingPotential:\n"
447 << " Label does not match the existing prompt component.\n";
448 return false;
449 }
450
451 // Load the first map.
452 std::vector<std::array<double, N> > wf1;
453 std::vector<double> wp1;
454 if (!LoadWeightingField(datfile1, wf1, wp1)) {
455 std::cerr << m_className << "::SetWeightingPotential:\n"
456 << " Could not import data from " << datfile1 << ".\n";
457 return false;
458 }
459 // Load the second map.
460 std::vector<std::array<double, N> > wf2;
461 std::vector<double> wp2;
462 if (!LoadWeightingField(datfile2, wf2, wp2)) {
463 std::cerr << m_className << "::SetWeightingPotential:\n"
464 << " Could not import data from " << datfile2 << ".\n";
465 return false;
466 }
467 const size_t nVertices = m_vertices.size();
468 if (wp1.size() != nVertices || wp2.size() != nVertices) {
469 std::cerr << m_className << "::SetWeightingPotential:\n"
470 << " Could not load electrostatic potentials.\n";
471 return false;
472 }
473 if (m_wpot.size() != nVertices) {
474 std::cerr << m_className << "::SetWeightingPotential:\n"
475 << " Prompt weighting potential not present.\n";
476 return false;
477 }
478 std::vector<double> wp(nVertices, 0.);
479 for (size_t i = 0; i < nVertices; ++i) {
480 wp[i] = (wp2[i] - wp1[i]) * s;
481 // Subtract the prompt component.
482 wp[i] -= m_wpot[i];
483 }
484 if (m_dwtp.empty() || t > m_dwtp.back()) {
485 m_dwtp.push_back(t);
486 m_dwp.push_back(std::move(wp));
487 } else {
488 const auto it = std::upper_bound(m_dwtp.begin(), m_dwtp.end(), t);
489 const auto n = std::distance(m_dwtp.begin(), it);
490 m_dwtp.insert(it, t);
491 m_dwp.insert(m_dwp.begin() + n, std::move(wp));
492 }
493 return true;
494}
495
496template<size_t N>
498 const std::string& datfile1, const std::string& datfile2,
499 const double dv, const double t, const std::string& label) {
500
501 if (!m_ready) {
502 std::cerr << m_className << "::SetWeightingField:\n"
503 << " Mesh is not available. Call Initialise first.\n";
504 return false;
505 }
506 if (dv < Small) {
507 std::cerr << m_className << "::SetWeightingField:\n"
508 << " Voltage difference must be > 0.\n";
509 return false;
510 }
511 const double s = 1. / dv;
512
513 if (m_wlabel.empty()) {
514 std::cerr << m_className << "::SetWeightingField:\n"
515 << " Prompt component not present.\n"
516 << " Import the map for t = 0 first.\n";
517 return false;
518 }
519 if (label != m_wlabel.front()) {
520 std::cerr << m_className << "::SetWeightingField:\n"
521 << " Label does not match the existing prompt component.\n";
522 return false;
523 }
524
525 // Load the first map.
526 std::vector<std::array<double, N> > wf1;
527 std::vector<double> wp1;
528 if (!LoadWeightingField(datfile1, wf1, wp1)) {
529 std::cerr << m_className << "::SetWeightingField:\n"
530 << " Could not import data from " << datfile1 << ".\n";
531 return false;
532 }
533 // Load the second map.
534 std::vector<std::array<double, N> > wf2;
535 std::vector<double> wp2;
536 if (!LoadWeightingField(datfile2, wf2, wp2)) {
537 std::cerr << m_className << "::SetWeightingField:\n"
538 << " Could not import data from " << datfile2 << ".\n";
539 return false;
540 }
541 const size_t nVertices = m_vertices.size();
542 if (wf1.size() != nVertices || wf2.size() != nVertices) {
543 std::cerr << m_className << "::SetWeightingField:\n"
544 << " Could not load electric field values.\n";
545 return false;
546 }
547 if (m_wfield.size() != nVertices) {
548 std::cerr << m_className << "::SetWeightingField:\n"
549 << " Prompt weighting field not present.\n";
550 return false;
551 }
552 std::vector<std::array<double, N> > wf;
553 wf.resize(nVertices);
554 for (size_t i = 0; i < nVertices; ++i) {
555 for (size_t j = 0; j < N; ++j) {
556 wf[i][j] = (wf2[i][j] - wf1[i][j]) * s;
557 }
558 }
559 if (m_dwtf.empty() || t > m_dwtf.back()) {
560 m_dwtf.push_back(t);
561 m_dwf.push_back(std::move(wf));
562 } else {
563 const auto it = std::upper_bound(m_dwtf.begin(), m_dwtf.end(), t);
564 const auto n = std::distance(m_dwtf.begin(), it);
565 m_dwtf.insert(it, t);
566 m_dwf.insert(m_dwf.begin() + n, std::move(wf));
567 }
568 return true;
569}
570
571template<size_t N>
573 const std::string& label, const double x, const double y, const double z) {
574 if (m_wlabel.empty()) {
575 std::cerr << m_className << "::SetWeightingFieldShift:\n"
576 << " No map of weighting potentials/fields loaded.\n";
577 return false;
578 }
579 const size_t n = m_wlabel.size();
580 for (size_t i = 0; i < n; ++i) {
581 if (m_wlabel[i] == label) {
582 m_wshift[i] = {x, y, z};
583 std::cout << m_className << "::SetWeightingFieldShift:\n"
584 << " Changing offset of electrode \'" << label
585 << "\' to (" << x << ", " << y << ", " << z << ") cm.\n";
586 return true;
587 }
588 }
589 m_wlabel.push_back(label);
590 m_wshift.push_back({x, y, z});
591 std::cout << m_className << "::SetWeightingFieldShift:\n"
592 << " Adding electrode \'" << label << "\' with offset ("
593 << x << ", " << y << ", " << z << ") cm.\n";
594 return true;
595}
596
597template<size_t N>
599 m_useVelocityMap = on;
600 if (m_ready && (m_eVelocity.empty() && m_hVelocity.empty())) {
601 std::cout << m_className << "::EnableVelocityMap:\n"
602 << " Warning: current map does not include velocity data.\n";
603 }
604}
605
606template<size_t N>
607bool ComponentTcadBase<N>::LoadGrid(const std::string& filename) {
608 // Open the file containing the mesh description.
609 std::ifstream gridfile(filename);
610 if (!gridfile) {
611 std::cerr << m_className << "::LoadGrid:\n"
612 << " Could not open file " << filename << ".\n";
613 return false;
614 }
615 // Delete existing mesh information.
616 Cleanup();
617 // Count line numbers.
618 unsigned int iLine = 0;
619 // Get the number of regions.
620 size_t nRegions = 0;
621 // Read the file line by line.
622 std::string line;
623 while (std::getline(gridfile, line)) {
624 ++iLine;
625 // Strip white space from the beginning of the line.
626 ltrim(line);
627 if (line.empty()) continue;
628 // Find entry 'nb_regions'.
629 if (line.substr(0, 10) != "nb_regions") continue;
630 const auto pEq = line.find('=');
631 if (pEq == std::string::npos) {
632 // No "=" sign found.
633 std::cerr << m_className << "::LoadGrid:\n"
634 << " Could not read number of regions.\n";
635 return false;
636 }
637 line = line.substr(pEq + 1);
638 std::istringstream data(line);
639 data >> nRegions;
640 break;
641 }
642 if (gridfile.eof()) {
643 // Reached end of file.
644 std::cerr << m_className << "::LoadGrid:\n"
645 << " Could not find entry 'nb_regions' in file\n"
646 << " " << filename << ".\n";
647 return false;
648 } else if (gridfile.fail()) {
649 // Error reading from the file.
650 PrintError(m_className + "::LoadGrid", filename, iLine);
651 return false;
652 }
653 m_regions.resize(nRegions);
654 for (size_t j = 0; j < nRegions; ++j) {
655 m_regions[j].name = "";
656 m_regions[j].material = "";
657 m_regions[j].drift = false;
658 m_regions[j].medium = nullptr;
659 }
660 if (m_debug) {
661 std::cout << m_className << "::LoadGrid:\n"
662 << " Found " << nRegions << " regions.\n";
663 }
664 // Get the region names.
665 while (std::getline(gridfile, line)) {
666 ++iLine;
667 ltrim(line);
668 if (line.empty()) continue;
669 // Find entry 'regions'.
670 if (line.substr(0, 7) != "regions") continue;
671 // Get region names (given in brackets).
672 if (!ExtractFromSquareBrackets(line)) {
673 std::cerr << m_className << "::LoadGrid:\n"
674 << " Could not read region names.\n";
675 return false;
676 }
677 std::istringstream data(line);
678 for (size_t j = 0; j < nRegions; ++j) {
679 data >> m_regions[j].name;
680 data.clear();
681 // Assume by default that all regions are active.
682 m_regions[j].drift = true;
683 m_regions[j].medium = nullptr;
684 }
685 break;
686 }
687 if (gridfile.eof()) {
688 // Reached end of file.
689 std::cerr << m_className << "::LoadGrid:\n"
690 << " Could not find entry 'regions' in file\n"
691 << " " << filename << ".\n";
692 return false;
693 } else if (gridfile.fail()) {
694 // Error reading from the file.
695 PrintError(m_className + "::LoadGrid", filename, iLine);
696 return false;
697 }
698
699 // Get the materials.
700 while (std::getline(gridfile, line)) {
701 ++iLine;
702 ltrim(line);
703 if (line.empty()) continue;
704 // Find entry 'materials'.
705 if (line.substr(0, 9) != "materials") continue;
706 // Get region names (given in brackets).
707 if (!ExtractFromSquareBrackets(line)) {
708 std::cerr << m_className << "::LoadGrid:\n"
709 << " Could not read materials.\n";
710 return false;
711 }
712 std::istringstream data(line);
713 for (size_t j = 0; j < nRegions; ++j) {
714 data >> m_regions[j].material;
715 data.clear();
716 }
717 break;
718 }
719 if (gridfile.eof()) {
720 // Reached end of file.
721 std::cerr << m_className << "::LoadGrid:\n"
722 << " Could not find entry 'materials' in file\n"
723 << " " << filename << ".\n";
724 } else if (gridfile.fail()) {
725 // Error reading from the file.
726 PrintError(m_className + "::LoadGrid", filename, iLine);
727 }
728
729 // Get the vertices.
730 size_t nVertices = 0;
731 while (std::getline(gridfile, line)) {
732 ++iLine;
733 ltrim(line);
734 if (line.empty()) continue;
735 // Find section 'Vertices'.
736 if (line.substr(0, 8) != "Vertices") continue;
737 // Get number of vertices (given in brackets).
738 if (!ExtractFromBrackets(line)) {
739 std::cerr << m_className << "::LoadGrid:\n"
740 << " Could not read number of vertices.\n";
741 return false;
742 }
743 std::istringstream data(line);
744 data >> nVertices;
745 m_vertices.resize(nVertices);
746 // Get the coordinates of every vertex.
747 for (size_t j = 0; j < nVertices; ++j) {
748 for (size_t k = 0; k < N; ++k) {
749 gridfile >> m_vertices[j][k];
750 // Change units from micron to cm.
751 m_vertices[j][k] *= 1.e-4;
752 }
753 ++iLine;
754 }
755 break;
756 }
757 if (gridfile.eof()) {
758 std::cerr << m_className << "::LoadGrid:\n"
759 << " Could not find section 'Vertices' in file\n"
760 << " " << filename << ".\n";
761 return false;
762 } else if (gridfile.fail()) {
763 PrintError(m_className + "::LoadGrid", filename, iLine);
764 return false;
765 }
766
767 // Get the "edges" (lines connecting two vertices).
768 size_t nEdges = 0;
769 // Temporary arrays for storing edge points.
770 std::vector<unsigned int> edgeP1;
771 std::vector<unsigned int> edgeP2;
772 while (std::getline(gridfile, line)) {
773 ++iLine;
774 ltrim(line);
775 if (line.empty()) continue;
776 // Find section 'Edges'.
777 if (line.substr(0, 5) != "Edges") continue;
778 // Get the number of edges (given in brackets).
779 if (!ExtractFromBrackets(line)) {
780 std::cerr << m_className << "::LoadGrid:\n"
781 << " Could not read number of edges.\n";
782 return false;
783 }
784 std::istringstream data(line);
785 data >> nEdges;
786 edgeP1.resize(nEdges);
787 edgeP2.resize(nEdges);
788 // Get the indices of the two endpoints.
789 for (size_t j = 0; j < nEdges; ++j) {
790 gridfile >> edgeP1[j] >> edgeP2[j];
791 ++iLine;
792 }
793 break;
794 }
795 if (gridfile.eof()) {
796 std::cerr << m_className << "::LoadGrid:\n"
797 << " Could not find section 'Edges' in file\n"
798 << " " << filename << ".\n";
799 return false;
800 } else if (gridfile.fail()) {
801 PrintError(m_className + "::LoadGrid", filename, iLine);
802 return false;
803 }
804
805 for (size_t i = 0; i < nEdges; ++i) {
806 // Make sure the indices of the edge endpoints are not out of range.
807 if (edgeP1[i] >= nVertices || edgeP2[i] >= nVertices) {
808 std::cerr << m_className << "::LoadGrid:\n"
809 << " Vertex index of edge " << i << " out of range.\n";
810 return false;
811 }
812 // Make sure the edge is non-degenerate.
813 if (edgeP1[i] == edgeP2[i]) {
814 std::cerr << m_className << "::LoadGrid:\n"
815 << " Edge " << i << " is degenerate.\n";
816 return false;
817 }
818 }
819
820 // Get the "faces" (only for 3D maps).
821 struct Face {
822 // Indices of edges
823 int edge[4];
824 int type;
825 };
826 size_t nFaces = 0;
827 std::vector<Face> faces;
828 if (N == 3) {
829 while (std::getline(gridfile, line)) {
830 ++iLine;
831 ltrim(line);
832 if (line.empty()) continue;
833 // Find section 'Faces'.
834 if (line.substr(0, 5) != "Faces") continue;
835 // Get the number of faces (given in brackets).
836 if (!ExtractFromBrackets(line)) {
837 std::cerr << m_className << "::LoadGrid:\n"
838 << " Could not read number of faces.\n";
839 return false;
840 }
841 std::istringstream data(line);
842 data >> nFaces;
843 faces.resize(nFaces);
844 // Get the indices of the edges constituting this face.
845 for (size_t j = 0; j < nFaces; ++j) {
846 gridfile >> faces[j].type;
847 if (faces[j].type != 3 && faces[j].type != 4) {
848 std::cerr << m_className << "::LoadGrid:\n"
849 << " Face with index " << j
850 << " has invalid number of edges, " << faces[j].type << ".\n";
851 return false;
852 }
853 for (int k = 0; k < faces[j].type; ++k) {
854 gridfile >> faces[j].edge[k];
855 }
856 }
857 iLine += nFaces - 1;
858 break;
859 }
860 if (gridfile.eof()) {
861 std::cerr << m_className << "::LoadGrid:\n"
862 << " Could not find section 'Faces' in file\n"
863 << " " << filename << ".\n";
864 return false;
865 } else if (gridfile.fail()) {
866 PrintError(m_className + "::LoadGrid", filename, iLine);
867 return false;
868 }
869 }
870
871 // Get the elements.
872 size_t nElements = 0;
873 while (std::getline(gridfile, line)) {
874 ++iLine;
875 ltrim(line);
876 if (line.empty()) continue;
877 // Find section 'Elements'.
878 if (line.substr(0, 8) != "Elements") continue;
879 // Get number of elements (given in brackets).
880 if (!ExtractFromBrackets(line)) {
881 std::cerr << m_className << "::LoadGrid:\n"
882 << " Could not read number of elements.\n";
883 return false;
884 }
885 std::istringstream data(line);
886 data >> nElements;
887 data.clear();
888 // Resize the list of elements.
889 m_elements.resize(nElements);
890 // Get type and constituting edges of each element.
891 for (size_t j = 0; j < nElements; ++j) {
892 ++iLine;
893 unsigned int type = 0;
894 gridfile >> type;
895 if (N == 2) {
896 if (type == 0) {
897 // Point
898 unsigned int p = 0;
899 gridfile >> p;
900 // Make sure the index is not out of range.
901 if (p >= nVertices) {
902 PrintError(m_className + "::LoadGrid", filename, iLine);
903 std::cerr << " Vertex index out of range.\n";
904 return false;
905 }
906 m_elements[j].vertex[0] = p;
907 } else if (type == 1) {
908 // Line
909 for (size_t k = 0; k < 2; ++k) {
910 int p = 0;
911 gridfile >> p;
912 if (p < 0) p = -p - 1;
913 // Make sure the index is not out of range.
914 if (p >= (int)nVertices) {
915 PrintError(m_className + "::LoadGrid", filename, iLine);
916 std::cerr << " Vertex index out of range.\n";
917 return false;
918 }
919 m_elements[j].vertex[k] = p;
920 }
921 } else if (type == 2) {
922 // Triangle
923 int p0 = 0, p1 = 0, p2 = 0;
924 gridfile >> p0 >> p1 >> p2;
925 // Negative edge index means that the sequence of the two points
926 // is supposed to be inverted.
927 // The actual index is then given by "-index - 1".
928 if (p0 < 0) p0 = -p0 - 1;
929 if (p1 < 0) p1 = -p1 - 1;
930 if (p2 < 0) p2 = -p2 - 1;
931 // Make sure the indices are not out of range.
932 if (p0 >= (int)nEdges || p1 >= (int)nEdges || p2 >= (int)nEdges) {
933 PrintError(m_className + "::LoadGrid", filename, iLine);
934 std::cerr << " Edge index out of range.\n";
935 return false;
936 }
937 m_elements[j].vertex[0] = edgeP1[p0];
938 m_elements[j].vertex[1] = edgeP2[p0];
939 if (edgeP1[p1] != m_elements[j].vertex[0] &&
940 edgeP1[p1] != m_elements[j].vertex[1]) {
941 m_elements[j].vertex[2] = edgeP1[p1];
942 } else {
943 m_elements[j].vertex[2] = edgeP2[p1];
944 }
945 // Rearrange vertices such that point 0 is on the left.
946 while (m_vertices[m_elements[j].vertex[0]][0] >
947 m_vertices[m_elements[j].vertex[1]][0] ||
948 m_vertices[m_elements[j].vertex[0]][0] >
949 m_vertices[m_elements[j].vertex[2]][0]) {
950 const int tmp = m_elements[j].vertex[0];
951 m_elements[j].vertex[0] = m_elements[j].vertex[1];
952 m_elements[j].vertex[1] = m_elements[j].vertex[2];
953 m_elements[j].vertex[2] = tmp;
954 }
955 } else if (type == 3) {
956 // Rectangle
957 for (size_t k = 0; k < 4; ++k) {
958 int p = 0;
959 gridfile >> p;
960 // Make sure the index is not out of range.
961 if (p >= (int)nEdges || -p - 1 >= (int)nEdges) {
962 PrintError(m_className + "::LoadGrid", filename, iLine);
963 std::cerr << " Edge index out of range.\n";
964 return false;
965 }
966 if (p >= 0) {
967 m_elements[j].vertex[k] = edgeP1[p];
968 } else {
969 m_elements[j].vertex[k] = edgeP2[-p - 1];
970 }
971 }
972 // Rearrange vertices such that point 0 is on the left.
973 while (m_vertices[m_elements[j].vertex[0]][0] >
974 m_vertices[m_elements[j].vertex[1]][0] ||
975 m_vertices[m_elements[j].vertex[0]][0] >
976 m_vertices[m_elements[j].vertex[2]][0] ||
977 m_vertices[m_elements[j].vertex[0]][0] >
978 m_vertices[m_elements[j].vertex[3]][0]) {
979 const int tmp = m_elements[j].vertex[0];
980 m_elements[j].vertex[0] = m_elements[j].vertex[1];
981 m_elements[j].vertex[1] = m_elements[j].vertex[2];
982 m_elements[j].vertex[2] = m_elements[j].vertex[3];
983 m_elements[j].vertex[3] = tmp;
984 }
985 } else {
986 // Other element types are not permitted for 2d grids.
987 PrintError(m_className + "::LoadGrid", filename, iLine);
988 std::cerr << " Invalid element type (" << type
989 << ") for 2d mesh.\n";
990 return false;
991 }
992 } else if (N == 3) {
993 if (type == 2) {
994 // Triangle
995 int edge0, edge1, edge2;
996 gridfile >> edge0 >> edge1 >> edge2;
997 // Get the vertices.
998 // Negative edge index means that the sequence of the two points
999 // is supposed to be inverted.
1000 // The actual index is then given by "-index - 1".
1001 // For our purposes, the orientation does not matter.
1002 if (edge0 < 0) edge0 = -edge0 - 1;
1003 if (edge1 < 0) edge1 = -edge1 - 1;
1004 if (edge2 < 0) edge2 = -edge2 - 1;
1005 // Make sure the indices are not out of range.
1006 if (edge0 >= (int)nEdges || edge1 >= (int)nEdges ||
1007 edge2 >= (int)nEdges) {
1008 PrintError(m_className + "::LoadGrid", filename, iLine);
1009 std::cerr << " Edge index out of range.\n";
1010 return false;
1011 }
1012 m_elements[j].vertex[0] = edgeP1[edge0];
1013 m_elements[j].vertex[1] = edgeP2[edge0];
1014 if (edgeP1[edge1] != m_elements[j].vertex[0] &&
1015 edgeP1[edge1] != m_elements[j].vertex[1]) {
1016 m_elements[j].vertex[2] = edgeP1[edge1];
1017 } else {
1018 m_elements[j].vertex[2] = edgeP2[edge1];
1019 }
1020 } else if (type == 5) {
1021 // Tetrahedron
1022 // Get the faces.
1023 // Negative face index means that the sequence of the edges
1024 // is supposed to be inverted.
1025 // For our purposes, the orientation does not matter.
1026 int face0, face1, face2, face3;
1027 gridfile >> face0 >> face1 >> face2 >> face3;
1028 if (face0 < 0) face0 = -face0 - 1;
1029 if (face1 < 0) face1 = -face1 - 1;
1030 if (face2 < 0) face2 = -face2 - 1;
1031 if (face3 < 0) face3 = -face3 - 1;
1032 // Make sure the face indices are not out of range.
1033 if (face0 >= (int)nFaces || face1 >= (int)nFaces ||
1034 face2 >= (int)nFaces || face3 >= (int)nFaces) {
1035 PrintError(m_className + "::LoadGrid", filename, iLine);
1036 std::cerr << " Face index out of range.\n";
1037 return false;
1038 }
1039 // Get the edges of the first face.
1040 int edge0 = faces[face0].edge[0];
1041 int edge1 = faces[face0].edge[1];
1042 int edge2 = faces[face0].edge[2];
1043 if (edge0 < 0) edge0 = -edge0 - 1;
1044 if (edge1 < 0) edge1 = -edge1 - 1;
1045 if (edge2 < 0) edge2 = -edge2 - 1;
1046 // Make sure the edge indices are not out of range.
1047 if (edge0 >= (int)nEdges || edge1 >= (int)nEdges ||
1048 edge2 >= (int)nEdges) {
1049 PrintError(m_className + "::LoadGrid", filename, iLine);
1050 std::cerr << " Edge index out of range.\n";
1051 return false;
1052 }
1053 // Get the first three vertices.
1054 m_elements[j].vertex[0] = edgeP1[edge0];
1055 m_elements[j].vertex[1] = edgeP2[edge0];
1056 if (edgeP1[edge1] != m_elements[j].vertex[0] &&
1057 edgeP1[edge1] != m_elements[j].vertex[1]) {
1058 m_elements[j].vertex[2] = edgeP1[edge1];
1059 } else {
1060 m_elements[j].vertex[2] = edgeP2[edge1];
1061 }
1062 // Get the fourth vertex from face 1.
1063 edge0 = faces[face1].edge[0];
1064 edge1 = faces[face1].edge[1];
1065 edge2 = faces[face1].edge[2];
1066 if (edge0 < 0) edge0 = -edge0 - 1;
1067 if (edge1 < 0) edge1 = -edge1 - 1;
1068 if (edge2 < 0) edge2 = -edge2 - 1;
1069 const auto v0 = m_elements[j].vertex[0];
1070 const auto v1 = m_elements[j].vertex[1];
1071 const auto v2 = m_elements[j].vertex[2];
1072 if (edgeP1[edge0] != v0 && edgeP1[edge0] != v1 && edgeP1[edge0] != v2) {
1073 m_elements[j].vertex[3] = edgeP1[edge0];
1074 } else if (edgeP2[edge0] != v0 && edgeP2[edge0] != v1 &&
1075 edgeP2[edge0] != v2) {
1076 m_elements[j].vertex[3] = edgeP2[edge0];
1077 } else if (edgeP1[edge1] != v0 &&
1078 edgeP1[edge1] != v1 &&
1079 edgeP1[edge1] != v2) {
1080 m_elements[j].vertex[3] = edgeP1[edge1];
1081 } else if (edgeP2[edge1] != v0 &&
1082 edgeP2[edge1] != v1 &&
1083 edgeP2[edge1] != v2) {
1084 m_elements[j].vertex[3] = edgeP2[edge1];
1085 } else {
1086 PrintError(m_className + "::LoadGrid", filename, iLine);
1087 std::cerr << " Face 1 of element " << j << " is degenerate.\n";
1088 return false;
1089 }
1090 } else {
1091 // Other element types are not allowed.
1092 PrintError(m_className + "::LoadGrid", filename, iLine);
1093 if (type == 0 || type == 1) {
1094 std::cerr << " Invalid element type (" << type
1095 << ") for 3d mesh.\n";
1096 } else {
1097 std::cerr << " Element type " << type << " is not supported.\n"
1098 << " Remesh with option -t to create only"
1099 << " triangles and tetrahedra.\n";
1100 }
1101 return false;
1102 }
1103 }
1104 m_elements[j].type = type;
1105 m_elements[j].region = m_regions.size();
1106 }
1107 break;
1108 }
1109 if (gridfile.eof()) {
1110 std::cerr << m_className << "::LoadGrid:\n"
1111 << " Could not find section 'Elements' in file\n"
1112 << " " << filename << ".\n";
1113 return false;
1114 } else if (gridfile.fail()) {
1115 PrintError(m_className + "::LoadGrid", filename, iLine);
1116 return false;
1117 }
1118
1119 // Assign regions to elements.
1120 while (std::getline(gridfile, line)) {
1121 ltrim(line);
1122 if (line.empty()) continue;
1123 // Find section 'Region'.
1124 if (line.substr(0, 6) != "Region") continue;
1125 // Get region name (given in brackets).
1126 if (!ExtractFromBrackets(line)) {
1127 std::cerr << m_className << "::LoadGrid:\n"
1128 << " Could not read region name.\n";
1129 return false;
1130 }
1131 std::istringstream data(line);
1132 std::string name;
1133 data >> name;
1134 data.clear();
1135 const size_t index = FindRegion(name);
1136 if (index >= m_regions.size()) {
1137 // Specified region name is not in the list.
1138 std::cerr << m_className << "::LoadGrid:\n"
1139 << " Unknown region " << name << ".\n";
1140 continue;
1141 }
1142 std::getline(gridfile, line);
1143 std::getline(gridfile, line);
1144 if (!ExtractFromBrackets(line)) {
1145 std::cerr << m_className << "::LoadGrid:\n"
1146 << " Could not read number of elements in region "
1147 << name << ".\n";
1148 return false;
1149 }
1150 int nElementsRegion;
1151 data.str(line);
1152 data >> nElementsRegion;
1153 data.clear();
1154 for (int j = 0; j < nElementsRegion; ++j) {
1155 size_t iElement = 0;
1156 gridfile >> iElement;
1157 if (iElement >= m_elements.size()) {
1158 std::cerr << m_className << "::LoadGrid:\n"
1159 << " Error reading element indices for region "
1160 << name << ".\n";
1161 return false;
1162 }
1163 m_elements[iElement].region = index;
1164 }
1165 }
1166 if (gridfile.fail() && !gridfile.eof()) {
1167 std::cerr << m_className << "::LoadGrid:\n"
1168 << " Error reading file " << filename << ".\n";
1169 return false;
1170 }
1171 return true;
1172}
1173
1174template<size_t N>
1175bool ComponentTcadBase<N>::LoadData(const std::string& filename) {
1176
1177 std::ifstream datafile(filename);
1178 if (!datafile) {
1179 std::cerr << m_className << "::LoadData:\n"
1180 << " Could not open file " << filename << ".\n";
1181 return false;
1182 }
1183 const size_t nVertices = m_vertices.size();
1184 std::vector<unsigned int> fillCount(nVertices, 0);
1185
1186 std::array<double, N> zeroes;
1187 zeroes.fill(0.);
1188 // Read the file line by line.
1189 std::string line;
1190 while (std::getline(datafile, line)) {
1191 // Strip white space from the beginning of the line.
1192 ltrim(line);
1193 // Skip empty lines.
1194 if (line.empty()) continue;
1195 // Find data section.
1196 if (line.substr(0, 8) != "function") continue;
1197 // Read type of data set.
1198 const auto pEq = line.find('=');
1199 if (pEq == std::string::npos) {
1200 // No "=" found.
1201 std::cerr << m_className << "::LoadData:\n"
1202 << " Error reading file " << filename << ".\n"
1203 << " Line:\n " << line << "\n";
1204 return false;
1205 }
1206 line = line.substr(pEq + 1);
1207 std::string dataset;
1208 std::istringstream data(line);
1209 data >> dataset;
1210 data.clear();
1211 if (m_debug && dataset != "[") {
1212 std::cout << m_className << "::LoadData: Found dataset "
1213 << dataset << ".\n";
1214 }
1215 if (dataset == "ElectrostaticPotential") {
1216 if (m_epot.empty()) m_epot.assign(nVertices, 0.);
1217 if (!ReadDataset(datafile, dataset)) {
1218 m_epot.clear();
1219 return false;
1220 }
1221 } else if (dataset == "ElectricField") {
1222 if (m_efield.empty()) m_efield.assign(nVertices, zeroes);
1223 if (!ReadDataset(datafile, dataset)) {
1224 m_efield.clear();
1225 return false;
1226 }
1227 } else if (dataset == "eDriftVelocity") {
1228 if (m_eVelocity.empty()) m_eVelocity.assign(nVertices, zeroes);
1229 if (!ReadDataset(datafile, dataset)) {
1230 m_eVelocity.clear();
1231 return false;
1232 }
1233 } else if (dataset == "hDriftVelocity") {
1234 if (m_hVelocity.empty()) m_hVelocity.assign(nVertices, zeroes);
1235 if (!ReadDataset(datafile, dataset)) {
1236 m_hVelocity.clear();
1237 return false;
1238 }
1239 } else if (dataset == "eMobility") {
1240 if (m_eMobility.empty()) m_eMobility.assign(nVertices, 0.);
1241 if (!ReadDataset(datafile, dataset)) {
1242 m_eMobility.clear();
1243 return false;
1244 }
1245 } else if (dataset == "hMobility") {
1246 if (m_hMobility.empty()) m_hMobility.assign(nVertices, 0.);
1247 if (!ReadDataset(datafile, dataset)) {
1248 m_hMobility.clear();
1249 return false;
1250 }
1251 } else if (dataset == "eAlphaAvalanche") {
1252 if (m_eAlpha.empty()) m_eAlpha.assign(nVertices, 0.);
1253 if (!ReadDataset(datafile, dataset)) {
1254 m_eAlpha.clear();
1255 return false;
1256 }
1257 } else if (dataset == "hAlphaAvalanche") {
1258 if (m_hAlpha.empty()) m_hAlpha.assign(nVertices, 0.);
1259 if (!ReadDataset(datafile, dataset)) {
1260 m_hAlpha.clear();
1261 return false;
1262 }
1263 } else if (dataset == "eLifetime") {
1264 if (m_eLifetime.empty()) m_eLifetime.assign(nVertices, 0.);
1265 if (!ReadDataset(datafile, dataset)) {
1266 m_eLifetime.clear();
1267 return false;
1268 }
1269 } else if (dataset == "hLifetime") {
1270 if (m_hLifetime.empty()) m_hLifetime.assign(nVertices, 0.);
1271 if (!ReadDataset(datafile, dataset)) {
1272 m_hLifetime.clear();
1273 return false;
1274 }
1275 } else if (dataset.substr(0, 14) == "TrapOccupation" &&
1276 dataset.substr(17, 2) == "Do") {
1277 if (!ReadDataset(datafile, dataset)) return false;
1278 Defect donor;
1279 donor.xsece = -1.;
1280 donor.xsech = -1.;
1281 donor.conc = -1.;
1282 m_donors.push_back(donor);
1283 } else if (dataset.substr(0, 14) == "TrapOccupation" &&
1284 dataset.substr(17, 2) == "Ac") {
1285 if (!ReadDataset(datafile, dataset)) return false;
1286 Defect acceptor;
1287 acceptor.xsece = -1.;
1288 acceptor.xsech = -1.;
1289 acceptor.conc = -1.;
1290 m_acceptors.push_back(acceptor);
1291 }
1292 }
1293 if (datafile.fail() && !datafile.eof()) {
1294 std::cerr << m_className << "::LoadData:\n"
1295 << " Error reading file " << filename << "\n";
1296 return false;
1297 }
1298 return true;
1299}
1300
1301template<size_t N>
1302bool ComponentTcadBase<N>::ReadDataset(std::ifstream& datafile,
1303 const std::string& dataset) {
1304
1305 if (!datafile.is_open()) return false;
1306 enum DataSet {
1307 ElectrostaticPotential,
1308 EField,
1309 eDriftVelocity,
1310 hDriftVelocity,
1311 eMobility,
1312 hMobility,
1313 eAlpha,
1314 hAlpha,
1315 eLifetime,
1316 hLifetime,
1317 DonorTrapOccupation,
1318 AcceptorTrapOccupation,
1319 Unknown
1320 };
1321 DataSet ds = Unknown;
1322 if (dataset == "ElectrostaticPotential") {
1323 ds = ElectrostaticPotential;
1324 } else if (dataset == "ElectricField") {
1325 ds = EField;
1326 } else if (dataset == "eDriftVelocity") {
1327 ds = eDriftVelocity;
1328 } else if (dataset == "hDriftVelocity") {
1329 ds = hDriftVelocity;
1330 } else if (dataset == "eMobility") {
1331 ds = eMobility;
1332 } else if (dataset == "hMobility") {
1333 ds = hMobility;
1334 } else if (dataset == "eAlphaAvalanche") {
1335 ds = eAlpha;
1336 } else if (dataset == "hAlphaAvalanche") {
1337 ds = hAlpha;
1338 } else if (dataset == "eLifetime") {
1339 ds = eLifetime;
1340 } else if (dataset == "hLifetime") {
1341 ds = hLifetime;
1342 } else if (dataset.substr(0, 14) == "TrapOccupation") {
1343 if (dataset.substr(17, 2) == "Do") {
1344 ds = DonorTrapOccupation;
1345 } else if (dataset.substr(17, 2) == "Ac") {
1346 ds = AcceptorTrapOccupation;
1347 }
1348 } else {
1349 std::cerr << m_className << "::ReadDataset:\n"
1350 << " Unexpected dataset " << dataset << ".\n";
1351 return false;
1352 }
1353 bool isVector = false;
1354 if (ds == EField || ds == eDriftVelocity || ds == hDriftVelocity) {
1355 isVector = true;
1356 }
1357 std::string line;
1358 std::getline(datafile, line);
1359 std::getline(datafile, line);
1360 std::getline(datafile, line);
1361 std::getline(datafile, line);
1362 // Get the region name (given in brackets).
1363 if (!ExtractFromSquareBrackets(line)) {
1364 std::cerr << m_className << "::ReadDataset:\n"
1365 << " Cannot extract region name.\n"
1366 << " Line:\n " << line << "\n";
1367 return false;
1368 }
1369 std::string name;
1370 std::istringstream data(line);
1371 data >> name;
1372 data.clear();
1373 // Check if the region name matches one from the mesh file.
1374 const size_t index = FindRegion(name);
1375 if (index >= m_regions.size()) {
1376 std::cerr << m_className << "::ReadDataset:\n"
1377 << " Unknown region " << name << ".\n";
1378 return false;
1379 }
1380 if (m_debug) {
1381 std::cout << m_className << "::ReadDataset:\n"
1382 << " Reading dataset " << dataset << " for region "
1383 << name << ".\n";
1384 }
1385 // Get the number of values.
1386 std::getline(datafile, line);
1387 if (!ExtractFromBrackets(line)) {
1388 std::cerr << m_className << "::ReadDataset:\n"
1389 << " Cannot extract number of values to be read.\n"
1390 << " Line:\n " << line << "\n";
1391 return false;
1392 }
1393 int nValues;
1394 data.str(line);
1395 data >> nValues;
1396 if (isVector) nValues /= N;
1397 if (m_debug) std::cout << " Expecting " << nValues << " values.\n";
1398 // Mark the vertices belonging to this region.
1399 const size_t nVertices = m_vertices.size();
1400 std::vector<bool> isInRegion(nVertices, false);
1401 size_t nVerticesInRegion = 0;
1402 size_t nElementsInRegion = 0;
1403 for (const auto& element : m_elements) {
1404 if (element.region != index) continue;
1405 ++nElementsInRegion;
1406 const unsigned int nV = ElementVertices(element);
1407 for (unsigned int k = 0; k < nV; ++k) {
1408 if (isInRegion[element.vertex[k]]) continue;
1409 isInRegion[element.vertex[k]] = true;
1410 ++nVerticesInRegion;
1411 }
1412 }
1413 if (m_debug) {
1414 std::cout << " Region has " << nElementsInRegion << " elements and "
1415 << nVerticesInRegion << " vertices.\n";
1416 }
1417 unsigned int ivertex = 0;
1418 for (int j = 0; j < nValues; ++j) {
1419 // Read the next value.
1420 std::array<long double, N> val;
1421 if (isVector) {
1422 for (size_t k = 0; k < N; ++k) datafile >> val[k];
1423 } else {
1424 datafile >> val[0];
1425 }
1426 // Find the next vertex belonging to the region.
1427 while (ivertex < nVertices) {
1428 if (isInRegion[ivertex]) break;
1429 ++ivertex;
1430 }
1431 // Check if there is a mismatch between the number of m_vertices
1432 // and the number of potential values.
1433 if (ivertex >= nVertices) {
1434 std::cerr << m_className << "::ReadDataset:\n"
1435 << " Dataset " << dataset << " has more values than "
1436 << "there are vertices in region " << name << "\n";
1437 return false;
1438 }
1439
1440 switch (ds) {
1441 case ElectrostaticPotential:
1442 m_epot[ivertex] = val[0];
1443 break;
1444 case EField:
1445 for (size_t k = 0; k < N; ++k) m_efield[ivertex][k] = val[k];
1446 break;
1447 case eDriftVelocity:
1448 // Scale from cm/s to cm/ns.
1449 for (size_t k = 0; k < N; ++k) {
1450 m_eVelocity[ivertex][k] = val[k] * 1.e-9;
1451 }
1452 break;
1453 case hDriftVelocity:
1454 // Scale from cm/s to cm/ns.
1455 for (size_t k = 0; k < N; ++k) {
1456 m_hVelocity[ivertex][k] = val[k] * 1.e-9;
1457 }
1458 break;
1459 case eMobility:
1460 // Convert from cm2 / (V s) to cm2 / (V ns).
1461 m_eMobility[ivertex] = val[0] * 1.e-9;
1462 break;
1463 case hMobility:
1464 // Convert from cm2 / (V s) to cm2 / (V ns).
1465 m_hMobility[ivertex] = val[0] * 1.e-9;
1466 break;
1467 case eAlpha:
1468 m_eAlpha[ivertex] = val[0];
1469 break;
1470 case hAlpha:
1471 m_hAlpha[ivertex] = val[0];
1472 break;
1473 case eLifetime:
1474 // Convert from s to ns.
1475 m_eLifetime[ivertex] = val[0] * 1.e9;
1476 break;
1477 case hLifetime:
1478 // Convert from s to ns.
1479 m_hLifetime[ivertex] = val[0] * 1.e9;
1480 break;
1481 case DonorTrapOccupation:
1482 m_donorOcc[ivertex].push_back(val[0]);
1483 break;
1484 case AcceptorTrapOccupation:
1485 m_acceptorOcc[ivertex].push_back(val[0]);
1486 break;
1487 default:
1488 std::cerr << m_className << "::ReadDataset:\n"
1489 << " Unexpected dataset (" << ds << "). Program bug!\n";
1490 return false;
1491 }
1492 ++ivertex;
1493 }
1494 return true;
1495}
1496
1497template<size_t N>
1499 const std::string& filename,
1500 std::vector<std::array<double, N> >& wf, std::vector<double>& wp) {
1501
1502 std::ifstream datafile(filename, std::ios::in);
1503 if (!datafile) {
1504 std::cerr << m_className << "::LoadWeightingField:\n"
1505 << " Could not open file " << filename << ".\n";
1506 return false;
1507 }
1508 const size_t nVertices = m_vertices.size();
1509 std::array<double, N> zeroes;
1510 zeroes.fill(0.);
1511 bool ok = true;
1512 // Read the file line by line.
1513 std::string line;
1514 while (std::getline(datafile, line)) {
1515 // Strip white space from the beginning of the line.
1516 ltrim(line);
1517 if (line.empty()) continue;
1518 // Find data section.
1519 if (line.substr(0, 8) != "function") continue;
1520 // Read type of data set.
1521 const auto pEq = line.find('=');
1522 if (pEq == std::string::npos) {
1523 // No "=" found.
1524 std::cerr << m_className << "::LoadWeightingField:\n"
1525 << " Error reading file " << filename << ".\n"
1526 << " Line:\n " << line << "\n";
1527 return false;
1528 }
1529 line = line.substr(pEq + 1);
1530 std::string dataset;
1531 std::istringstream data(line);
1532 data >> dataset;
1533 data.clear();
1534 if (dataset != "ElectrostaticPotential" && dataset != "ElectricField") {
1535 continue;
1536 }
1537 bool field = false;
1538 if (dataset == "ElectricField") {
1539 if (wf.empty()) wf.assign(nVertices, zeroes);
1540 field = true;
1541 } else {
1542 if (wp.empty()) wp.assign(nVertices, 0.);
1543 }
1544 std::getline(datafile, line);
1545 std::getline(datafile, line);
1546 std::getline(datafile, line);
1547 std::getline(datafile, line);
1548 // Get the region name (given in brackets).
1549 if (!ExtractFromSquareBrackets(line)) {
1550 std::cerr << m_className << "::LoadWeightingField:\n"
1551 << " Cannot extract region name.\n"
1552 << " Line:\n " << line << "\n";
1553 ok = false;
1554 break;
1555 }
1556 std::string name;
1557 data.str(line);
1558 data >> name;
1559 data.clear();
1560 // Check if the region name matches one from the mesh file.
1561 const auto index = FindRegion(name);
1562 if (index >= m_regions.size()) {
1563 std::cerr << m_className << "::LoadWeightingField:\n"
1564 << " Unknown region " << name << ".\n";
1565 ok = false;
1566 break;
1567 }
1568 // Get the number of values.
1569 std::getline(datafile, line);
1570 if (!ExtractFromBrackets(line)) {
1571 std::cerr << m_className << "::LoadWeightingField:\n"
1572 << " Cannot extract number of values to be read.\n"
1573 << " Line:\n " << line << "\n";
1574 ok = false;
1575 break;
1576 }
1577 int nValues;
1578 data.str(line);
1579 data >> nValues;
1580 data.clear();
1581 if (field) nValues /= N;
1582 // Mark the vertices belonging to this region.
1583 std::vector<bool> isInRegion(nVertices, false);
1584 const size_t nElements = m_elements.size();
1585 for (size_t j = 0; j < nElements; ++j) {
1586 if (m_elements[j].region != index) continue;
1587 const unsigned int nV = ElementVertices(m_elements[j]);
1588 for (unsigned int k = 0; k < nV; ++k) {
1589 isInRegion[m_elements[j].vertex[k]] = true;
1590 }
1591 }
1592 unsigned int ivertex = 0;
1593 for (int j = 0; j < nValues; ++j) {
1594 // Read the next value.
1595 std::array<double, N> val;
1596 if (field) {
1597 for (size_t k = 0; k < N; ++k) datafile >> val[k];
1598 } else {
1599 datafile >> val[0];
1600 }
1601 // Find the next vertex belonging to the region.
1602 while (ivertex < nVertices) {
1603 if (isInRegion[ivertex]) break;
1604 ++ivertex;
1605 }
1606 // Check if there is a mismatch between the number of vertices
1607 // and the number of values.
1608 if (ivertex >= nVertices) {
1609 std::cerr << m_className << "::LoadWeightingField:\n"
1610 << " Dataset " << dataset
1611 << " has more values than vertices in region " << name << "\n";
1612 ok = false;
1613 break;
1614 }
1615 if (field) {
1616 wf[ivertex] = val;
1617 } else {
1618 wp[ivertex] = val[0];
1619 }
1620 ++ivertex;
1621 }
1622 }
1623
1624 if (!ok || (datafile.fail() && !datafile.eof())) {
1625 std::cerr << m_className << "::LoadWeightingField:\n"
1626 << " Error reading file " << filename << "\n";
1627 return false;
1628 }
1629 return true;
1630}
1631
1632template<size_t N>
1634
1635 if (m_regions.empty()) {
1636 std::cerr << m_className << "::PrintRegions:\n"
1637 << " No regions are currently defined.\n";
1638 return;
1639 }
1640
1641 const size_t nRegions = m_regions.size();
1642 std::cout << m_className << "::PrintRegions:\n"
1643 << " Currently " << nRegions << " regions are defined.\n"
1644 << " Index Name Material Medium\n";
1645 for (size_t i = 0; i < nRegions; ++i) {
1646 std::cout << std::setw(8) << std::right << i << " "
1647 << std::setw(20) << std::left << m_regions[i].name << " "
1648 << std::setw(18) << std::left << m_regions[i].material << " ";
1649 if (!m_regions[i].medium) {
1650 std::cout << std::setw(18) << "none";
1651 } else {
1652 std::cout << std::setw(18) << m_regions[i].medium->GetName();
1653 }
1654 if (m_regions[i].drift) {
1655 std::cout << " (active)\n";
1656 } else {
1657 std::cout << "\n";
1658 }
1659 }
1660}
1661
1662template<size_t N>
1663void ComponentTcadBase<N>::GetRegion(const size_t i, std::string& name,
1664 bool& active) const {
1665 if (i >= m_regions.size()) {
1666 std::cerr << m_className << "::GetRegion: Index out of range.\n";
1667 return;
1668 }
1669 name = m_regions[i].name;
1670 active = m_regions[i].drift;
1671}
1672
1673template<size_t N>
1675 if (i >= m_regions.size()) {
1676 std::cerr << m_className << "::SetDriftRegion: Index out of range.\n";
1677 return;
1678 }
1679 m_regions[i].drift = true;
1680}
1681
1682template<size_t N>
1684 if (i >= m_regions.size()) {
1685 std::cerr << m_className << "::UnsetDriftRegion: Index out of range.\n";
1686 return;
1687 }
1688 m_regions[i].drift = false;
1689}
1690
1691template<size_t N>
1692void ComponentTcadBase<N>::SetMedium(const size_t i, Medium* medium) {
1693 if (i >= m_regions.size()) {
1694 std::cerr << m_className << "::SetMedium: Index out of range.\n";
1695 return;
1696 }
1697 if (!medium) {
1698 std::cerr << m_className << "::SetMedium: Null pointer.\n";
1699 return;
1700 }
1701 m_regions[i].medium = medium;
1702}
1703
1704template<size_t N>
1705void ComponentTcadBase<N>::SetMedium(const std::string& material,
1706 Medium* medium) {
1707 if (!medium) {
1708 std::cerr << m_className << "::SetMedium: Null pointer.\n";
1709 return;
1710 }
1711 size_t nMatch = 0;
1712 const auto nRegions = m_regions.size();
1713 for (size_t i = 0; i < nRegions; ++i) {
1714 if (material != m_regions[i].material) continue;
1715 m_regions[i].medium = medium;
1716 std::cout << m_className << "::SetMedium: Associating region " << i
1717 << " (" << m_regions[i].name << ") with "
1718 << medium->GetName() << ".\n";
1719 ++nMatch;
1720 }
1721 if (nMatch == 0) {
1722 std::cerr << m_className << "::SetMedium: Found no region with material "
1723 << material << ".\n";
1724 }
1725}
1726
1727template<size_t N>
1728bool ComponentTcadBase<N>::SetDonor(const size_t donorNumber,
1729 const double eXsec, const double hXsec,
1730 const double conc) {
1731 if (donorNumber >= m_donors.size()) {
1732 std::cerr << m_className << "::SetDonor: Index out of range.\n";
1733 return false;
1734 }
1735 m_donors[donorNumber].xsece = eXsec;
1736 m_donors[donorNumber].xsech = hXsec;
1737 m_donors[donorNumber].conc = conc;
1738
1740 return true;
1741}
1742
1743template<size_t N>
1744bool ComponentTcadBase<N>::SetAcceptor(const size_t acceptorNumber,
1745 const double eXsec, const double hXsec,
1746 const double conc) {
1747 if (acceptorNumber >= m_acceptors.size()) {
1748 std::cerr << m_className << "::SetAcceptor: Index out of range.\n";
1749 return false;
1750 }
1751 m_acceptors[acceptorNumber].xsece = eXsec;
1752 m_acceptors[acceptorNumber].xsech = hXsec;
1753 m_acceptors[acceptorNumber].conc = conc;
1754
1756 return true;
1757}
1758
1759template<size_t N>
1760bool ComponentTcadBase<N>::ElectronAttachment(const double x, const double y,
1761 const double z, double& eta) {
1762 Interpolate(x, y, z, m_eAttachment, eta);
1763 return true;
1764}
1765
1766template<size_t N>
1767bool ComponentTcadBase<N>::HoleAttachment(const double x, const double y,
1768 const double z, double& eta) {
1769 Interpolate(x, y, z, m_hAttachment, eta);
1770 return true;
1771}
1772
1773template<size_t N>
1774bool ComponentTcadBase<N>::ElectronTownsend(const double x, const double y,
1775 const double z, double& alpha) {
1776 Interpolate(x, y, z, m_eAlpha, alpha);
1777 return true;
1778}
1779
1780template<size_t N>
1781bool ComponentTcadBase<N>::HoleTownsend(const double x, const double y,
1782 const double z, double& alpha) {
1783 Interpolate(x, y, z, m_hAlpha, alpha);
1784 return true;
1785}
1786
1787template<size_t N>
1788bool ComponentTcadBase<N>::ElectronVelocity(const double x, const double y,
1789 const double z, double& vx,
1790 double& vy, double& vz) {
1791 return Interpolate(x, y, z, m_eVelocity, vx, vy, vz);
1792}
1793
1794template<size_t N>
1795bool ComponentTcadBase<N>::HoleVelocity(const double x, const double y,
1796 const double z, double& vx,
1797 double& vy, double& vz) {
1798 return Interpolate(x, y, z, m_hVelocity, vx, vy, vz);
1799}
1800
1801template<size_t N>
1802bool ComponentTcadBase<N>::GetElectronLifetime(const double x, const double y,
1803 const double z, double& tau) {
1804 return Interpolate(x, y, z, m_eLifetime, tau);
1805}
1806
1807template<size_t N>
1808bool ComponentTcadBase<N>::GetHoleLifetime(const double x, const double y,
1809 const double z, double& tau) {
1810 return Interpolate(x, y, z, m_hLifetime, tau);
1811}
1812
1813template<size_t N>
1814bool ComponentTcadBase<N>::GetElectronMobility(const double x, const double y,
1815 const double z, double& mob) {
1816 return Interpolate(x, y, z, m_eMobility, mob);
1817}
1818
1819template<size_t N>
1820bool ComponentTcadBase<N>::GetHoleMobility(const double x, const double y,
1821 const double z, double& mob) {
1822 return Interpolate(x, y, z, m_hMobility, mob);
1823}
1824
1825template<size_t N>
1827 if (!m_ready) {
1828 std::cerr << m_className << "::UpdatePeriodicity:\n"
1829 << " Field map not available.\n";
1830 return;
1831 }
1832
1833 // Check for conflicts.
1834 for (size_t i = 0; i < 3; ++i) {
1835 if (m_periodic[i] && m_mirrorPeriodic[i]) {
1836 std::cerr << m_className << "::UpdatePeriodicity:\n"
1837 << " Both simple and mirror periodicity requested. Reset.\n";
1838 m_periodic[i] = m_mirrorPeriodic[i] = false;
1839 }
1840 if (m_axiallyPeriodic[i]) {
1841 std::cerr << m_className << "::UpdatePeriodicity:\n"
1842 << " Axial symmetry is not supported. Reset.\n";
1843 m_axiallyPeriodic.fill(false);
1844 }
1845 if (m_rotationSymmetric[i]) {
1846 std::cerr << m_className << "::UpdatePeriodicity:\n"
1847 << " Rotation symmetry is not supported. Reset.\n";
1848 m_rotationSymmetric.fill(false);
1849 }
1850 }
1851}
1852
1853template<size_t N>
1855 // Vertices
1856 m_vertices.clear();
1857 // Elements
1858 m_elements.clear();
1859 // Regions
1860 m_regions.clear();
1861 // Potential and electric field.
1862 m_epot.clear();
1863 m_efield.clear();
1864 // Weighting potential and field.
1865 m_wpot.clear();
1866 m_wfield.clear();
1867 m_wlabel.clear();
1868 m_wshift.clear();
1869 m_dwf.clear();
1870 m_dwp.clear();
1871 m_dwtp.clear();
1872 m_dwtf.clear();
1873
1874 // Other data.
1875 m_eVelocity.clear();
1876 m_hVelocity.clear();
1877 m_eMobility.clear();
1878 m_hMobility.clear();
1879 m_eAlpha.clear();
1880 m_hAlpha.clear();
1881 m_eLifetime.clear();
1882 m_hLifetime.clear();
1883 m_donors.clear();
1884 m_acceptors.clear();
1885 m_donorOcc.clear();
1886 m_acceptorOcc.clear();
1887 m_eAttachment.clear();
1888 m_hAttachment.clear();
1889}
1890
1891template<size_t N>
1892void ComponentTcadBase<N>::MapCoordinates(std::array<double, N>& x,
1893 std::array<bool, N>& mirr) const {
1894 mirr.fill(false);
1895 for (size_t i = 0; i < N; ++i) {
1896 // In case of periodicity, reduce to the cell volume.
1897 const double cellsx = m_bbMax[i] - m_bbMin[i];
1898 if (m_periodic[i]) {
1899 x[i] = m_bbMin[i] + fmod(x[i] - m_bbMin[i], cellsx);
1900 if (x[i] < m_bbMin[i]) x[i] += cellsx;
1901 } else if (m_mirrorPeriodic[i]) {
1902 double xNew = m_bbMin[i] + fmod(x[i] - m_bbMin[i], cellsx);
1903 if (xNew < m_bbMin[i]) xNew += cellsx;
1904 const int nx = int(floor(0.5 + (xNew - x[i]) / cellsx));
1905 if (nx != 2 * (nx / 2)) {
1906 xNew = m_bbMin[i] + m_bbMax[i] - xNew;
1907 mirr[i] = true;
1908 }
1909 x[i] = xNew;
1910 }
1911 }
1912}
1913
1914template<size_t N>
1915size_t ComponentTcadBase<N>::FindRegion(const std::string& name) const {
1916 const auto nRegions = m_regions.size();
1917 for (size_t j = 0; j < nRegions; ++j) {
1918 if (name == m_regions[j].name) return j;
1919 }
1920 return m_regions.size();
1921}
1922
1923template<size_t N>
1925
1926 if (m_vertices.empty()) return;
1927 const size_t nVertices = m_vertices.size();
1928 m_eAttachment.assign(nVertices, 0.);
1929 m_hAttachment.assign(nVertices, 0.);
1930
1931 const size_t nAcceptors = m_acceptors.size();
1932 for (size_t i = 0; i < nAcceptors; ++i) {
1933 const auto& defect = m_acceptors[i];
1934 if (defect.conc < 0.) continue;
1935 for (size_t j = 0; j < nVertices; ++j) {
1936 // Get the occupation probability.
1937 const double f = m_acceptorOcc[j][i];
1938 if (defect.xsece > 0.) {
1939 m_eAttachment[j] += defect.conc * defect.xsece * (1. - f);
1940 }
1941 if (defect.xsech > 0.) {
1942 m_hAttachment[j] += defect.conc * defect.xsech * f;
1943 }
1944 }
1945 }
1946 const size_t nDonors = m_donors.size();
1947 for (size_t i = 0; i < nDonors; ++i) {
1948 const auto& defect = m_donors[i];
1949 if (defect.conc < 0.) continue;
1950 for (size_t j = 0; j < nVertices; ++j) {
1951 const double f = m_donorOcc[j][i];
1952 if (defect.xsece > 0.) {
1953 m_eAttachment[j] += defect.conc * defect.xsece * f;
1954 }
1955 if (defect.xsech > 0.) {
1956 m_hAttachment[j] += defect.conc * defect.xsech * (1. - f);
1957 }
1958 }
1959 }
1960}
1961
1962template class ComponentTcadBase<2>;
1963template class ComponentTcadBase<3>;
1964}
Interpolation in a field map created by Sentaurus Device.
bool GetElectronLifetime(const double x, const double y, const double z, double &etau) override
std::vector< double > m_hMobility
bool LoadGrid(const std::string &gridfilename)
bool SetWeightingPotential(const std::string &datfile1, const std::string &datfile2, const double dv, const double t, const std::string &label)
Import time-dependent weighting potentials at t > 0.
void GetRegion(const size_t ireg, std::string &name, bool &active) const
Get the name and "active volume" flag of a region.
void UnsetDriftRegion(const size_t ireg)
Make a region inactive.
std::vector< Defect > m_acceptors
std::vector< std::vector< float > > m_acceptorOcc
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
std::vector< std::string > m_wlabel
size_t FindRegion(const std::string &name) const
std::vector< std::array< double, 3 > > m_wshift
virtual bool Interpolate(const double x, const double y, const double z, const std::vector< double > &field, double &f)=0
bool GetElectronMobility(const double x, const double y, const double z, double &mob)
Get the electron mobility at a given point in the mesh.
std::vector< std::vector< float > > m_donorOcc
std::vector< std::array< double, N > > m_vertices
bool GetHoleMobility(const double x, const double y, const double z, double &mob)
Get the hole mobility at a given point in the mesh.
bool SetWeightingField(const std::string &datfile1, const std::string &datfile2, const double dv, const std::string &label)
std::vector< Element > m_elements
void EnableVelocityMap(const bool on)
Switch use of the imported velocity map on/off.
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
bool HoleAttachment(const double x, const double y, const double z, double &eta) override
Get the hole attachment coefficient.
std::vector< std::vector< double > > m_dwp
std::vector< double > m_eAttachment
std::vector< double > m_hAlpha
std::vector< Region > m_regions
bool HoleTownsend(const double x, const double y, const double z, double &alpha) override
Get the hole Townsend coefficient.
std::vector< double > m_eAlpha
bool LoadWeightingField(const std::string &datafilename, std::vector< std::array< double, N > > &wf, std::vector< double > &wp)
std::vector< std::array< double, N > > m_hVelocity
static unsigned int ElementVertices(const Element &element)
std::array< double, 3 > m_bbMax
bool ReadDataset(std::ifstream &datafile, const std::string &dataset)
bool LoadData(const std::string &datafilename)
std::vector< std::array< double, N > > m_efield
std::vector< std::array< double, N > > m_eVelocity
std::vector< double > m_hLifetime
bool GetHoleLifetime(const double x, const double y, const double z, double &htau) override
bool GetOffset(const std::string &label, double &dx, double &dy, double &dz) const
void MapCoordinates(std::array< double, N > &x, std::array< bool, N > &mirr) const
void PrintRegions() const
List all currently defined regions.
bool SetAcceptor(const size_t acceptorNumber, const double exsec, const double hxsec, const double concentration)
Set the properties of an acceptor-type defect state.
bool ElectronAttachment(const double x, const double y, const double z, double &eta) override
Get the electron attachment coefficient.
std::vector< std::vector< std::array< double, N > > > m_dwf
std::vector< Defect > m_donors
bool SetWeightingFieldShift(const std::string &label, const double x, const double y, const double z)
bool Initialise(const std::string &gridfilename, const std::string &datafilename)
std::vector< double > m_eMobility
void UpdatePeriodicity() override
Verify periodicities.
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 ElectronVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz) override
Get the electron drift velocity.
double DelayedWeightingPotential(const double x, const double y, const double z, const double t, const std::string &label) override
void SetDriftRegion(const size_t ireg)
Make a region active ("driftable").
bool HoleVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz) override
Get the hole drift velocity.
std::vector< std::array< double, N > > m_wfield
bool SetDonor(const size_t donorNumber, const double exsec, const double hxsec, const double concentration)
void SetMedium(const size_t ireg, Medium *m)
Set the medium to be associated to a given region.
bool ElectronTownsend(const double x, const double y, const double z, double &alpha) override
Get the electron Townsend coefficient.
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
std::vector< double > m_hAttachment
std::array< double, 3 > m_bbMin
std::vector< double > m_eLifetime
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
bool m_debug
Switch on/off debugging messages.
Definition Component.hh:371
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition Component.hh:374
std::string m_className
Class name.
Definition Component.hh:359
bool m_ready
Ready for use?
Definition Component.hh:368
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
const std::string & GetName() const
Get the medium name/identifier.
Definition Medium.hh:26
void ltrim(std::string &line)
Definition Utilities.hh:12