Garfield++ v2r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentTcad2d.cc
Go to the documentation of this file.
1#include <iostream>
2#include <fstream>
3#include <sstream>
4#include <string>
5#include <algorithm>
6#include <cmath>
7
8#include "ComponentTcad2d.hh"
9
10namespace {
11
12void ltrim(std::string& line) {
13 line.erase(line.begin(), find_if(line.begin(), line.end(),
14 not1(std::ptr_fun<int, int>(isspace))));
15}
16
17}
18
19namespace Garfield {
20
22 m_hasPotential(false),
23 m_hasField(false),
24 m_hasElectronMobility(false),
25 m_hasHoleMobility(false),
26 m_hasElectronVelocity(false),
27 m_hasHoleVelocity(false),
28 m_hasElectronLifetime(false),
29 m_hasHoleLifetime(false),
30 m_validTraps(false),
31 m_pMin(0.),
32 m_pMax(0.),
33 m_hasRangeZ(false),
34 m_lastElement(0) {
35
36 m_className = "ComponentTcad2d";
37
38 m_regions.reserve(10);
39 m_vertices.reserve(10000);
40 m_elements.reserve(10000);
41}
42
43
44bool ComponentTcad2d::SetDonor(const unsigned int donorNumber,
45 const double eXsec, const double hXsec,
46 const double conc) {
47
48
49 if (donorNumber >= m_donors.size()) {
50 std::cerr << m_className << "::SetDonor:\n"
51 << " Index (" << donorNumber << ") out of range.\n";
52 return false;
53 }
54 m_donors[donorNumber].xsece = eXsec;
55 m_donors[donorNumber].xsech = hXsec;
56 m_donors[donorNumber].conc = conc;
57
58 m_validTraps = CheckTraps();
59 return true;
60}
61
62bool ComponentTcad2d::SetAcceptor(const unsigned int acceptorNumber,
63 const double eXsec, const double hXsec,
64 const double conc) {
65
66 if (acceptorNumber >= m_acceptors.size()) {
67 std::cerr << m_className << "::SetAcceptor:\n"
68 << " Index (" << acceptorNumber << ") out of range.\n";
69 return false;
70 }
71 m_acceptors[acceptorNumber].xsece = eXsec;
72 m_acceptors[acceptorNumber].xsech = hXsec;
73 m_acceptors[acceptorNumber].conc = conc;
74
75 m_validTraps = CheckTraps();
76 return true;
77}
78
79bool ComponentTcad2d::ElectronAttachment(const double x, const double y,
80 const double z, double& eta) {
81 eta = 0.;
82 if (!m_validTraps) {
83 std::cerr << m_className << "::ElectronAttachment:\n"
84 << " Trap cross-sections or concentrations are invalid.\n";
85 return false;
86 }
87
88 if (m_donors.empty() && m_acceptors.empty()) {
89 if (m_debug) {
90 std::cerr << m_className << "::ElectronAttachment:\n"
91 << " There are no traps defined.\n";
92 }
93 return true;
94 }
95
96 const unsigned int nAcceptors = m_acceptors.size();
97 for (unsigned int i = 0; i < nAcceptors; ++i) {
98 const Defect& acceptor = m_acceptors[i];
99 double f = 0.;
100 GetAcceptorOccupation(x, y, z, i, f);
101 eta += acceptor.conc * acceptor.xsece * (1. - f);
102 }
103 const unsigned int nDonors = m_donors.size();
104 for (unsigned int i = 0; i < nDonors; ++i) {
105 const Defect& donor = m_donors[i];
106 double f = 0.;
107 GetDonorOccupation(x, y, z, i, f);
108 eta += donor.conc * donor.xsece * f;
109 }
110 return true;
111}
112
113bool ComponentTcad2d::HoleAttachment(const double x, const double y,
114 const double z, double& eta) {
115
116 eta = 0.;
117 if (!m_validTraps) {
118 std::cerr << m_className << "::HoleAttachment:\n"
119 << " Trap cross-sections or concentrations are invalid.\n";
120 return false;
121 }
122
123 if (m_donors.empty() && m_acceptors.empty()) {
124 if (m_debug) {
125 std::cerr << m_className << "::HoleAttachment:\n"
126 << " There are no traps defined.\n";
127 }
128 return true;
129 }
130
131 const unsigned int nAcceptors = m_acceptors.size();
132 for (unsigned int i = 0; i < nAcceptors; ++i) {
133 const Defect& acceptor = m_acceptors[i];
134 double f = 0.;
135 GetAcceptorOccupation(x, y, z, i, f);
136 eta += acceptor.conc * acceptor.xsech * f;
137 }
138 const unsigned int nDonors = m_donors.size();
139 for (unsigned int i = 0; i < nDonors; ++i) {
140 const Defect& donor = m_donors[i];
141 double f = 0.;
142 GetDonorOccupation(x, y, z, i, f);
143 eta += donor.conc * donor.xsech * (1. - f);
144 }
145 return true;
146}
147
148void ComponentTcad2d::WeightingField(const double x, const double y, const double z,
149 double& wx, double& wy, double& wz,
150 const std::string& /*label*/) {
151 int status = 0;
152 Medium* med = NULL;
153 double v = 0.;
154 ElectricField(x, y, z, wx, wy, wz, v, med, status);
155}
156
157void ComponentTcad2d::ElectricField(const double xin, const double yin,
158 const double zin, double& ex, double& ey,
159 double& ez, double& p, Medium*& m,
160 int& status) {
161
162 // Initialise.
163 ex = ey = ez = p = 0.;
164 m = NULL;
165
166 // Make sure the field map has been loaded.
167 if (!m_ready) {
168 std::cerr << m_className << "::ElectricField:\n"
169 << " Field map is not available for interpolation.\n";
170 status = -10;
171 return;
172 }
173
174 // In case of periodicity, reduce to the cell volume.
175 double x = xin, y = yin, z = zin;
176 bool xmirr = false, ymirr = false;
177 MapCoordinates(x, y, xmirr, ymirr);
178 // Check if the point is inside the bounding box.
179 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
180 status = -11;
181 return;
182 }
183 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) {
184 status = -11;
185 return;
186 }
187
188 // Assume this will work.
189 status = 0;
190 double w[nMaxVertices] = {0};
191 if (m_lastElement >= 0) {
192 // Check if the point is still located in the previously found element.
193 const Element& last = m_elements[m_lastElement];
194 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax) {
195 if (CheckElement(x, y, last, w)) {
196 const unsigned int nVertices = last.type + 1;
197 for (unsigned int j = 0; j < nVertices; ++j) {
198 const Vertex& vj = m_vertices[last.vertex[j]];
199 ex += w[j] * vj.ex;
200 ey += w[j] * vj.ey;
201 p += w[j] * vj.p;
202 }
203 if (xmirr) ex = -ex;
204 if (ymirr) ey = -ey;
205 m = m_regions[last.region].medium;
206 if (!m_regions[last.region].drift || !m) status = -5;
207 return;
208 }
209 }
210 // The point is not in the previous element.
211 // Check the adjacent elements.
212 const unsigned int nNeighbours = last.neighbours.size();
213 for (unsigned int i = 0; i < nNeighbours; ++i) {
214 const Element& element = m_elements[last.neighbours[i]];
215 if (x < element.xmin || x > element.xmax ||
216 y < element.ymin || y > element.ymax) continue;
217 if (!CheckElement(x, y, element, w)) continue;
218 const unsigned int nVertices = element.type + 1;
219 for (unsigned int j = 0; j < nVertices; ++j) {
220 const Vertex& vj = m_vertices[element.vertex[j]];
221 ex += w[j] * vj.ex;
222 ey += w[j] * vj.ey;
223 p += w[j] * vj.p;
224 }
225 if (xmirr) ex = -ex;
226 if (ymirr) ey = -ey;
227 m = m_regions[element.region].medium;
228 if (!m_regions[element.region].drift || !m) status = -5;
229 m_lastElement = last.neighbours[i];
230 return;
231 }
232 }
233
234 // The point is not in the previous element nor in the adjacent ones.
235 // We have to loop over all elements.
236 const unsigned int nElements = m_elements.size();
237 for (unsigned int i = 0; i < nElements; ++i) {
238 const Element& element = m_elements[i];
239 if (x < element.xmin || x > element.xmax ||
240 y < element.ymin || y > element.ymax) continue;
241 if (!CheckElement(x, y, element, w)) continue;
242 const unsigned int nVertices = element.type + 1;
243 for (unsigned int j = 0; j < nVertices; ++j) {
244 const Vertex& vj = m_vertices[element.vertex[j]];
245 ex += w[j] * vj.ex;
246 ey += w[j] * vj.ey;
247 p += w[j] * vj.p;
248 }
249 if (xmirr) ex = -ex;
250 if (ymirr) ey = -ey;
251 m = m_regions[element.region].medium;
252 if (!m_regions[element.region].drift || !m) status = -5;
253 m_lastElement = i;
254 return;
255 }
256 // Point is outside the mesh.
257 if (m_debug) {
258 std::cerr << m_className << "::ElectricField:\n"
259 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
260 }
261 status = -6;
262}
263
264void ComponentTcad2d::ElectronVelocity(const double xin, const double yin,
265 const double zin,
266 double& vx, double& vy, double& vz,
267 Medium*& m, int& status) {
268
269 // Initialise.
270 vx = vy = vz = 0.;
271 m = NULL;
272 // Make sure the field map has been loaded.
273 if (!m_ready) {
274 std::cerr << m_className << "::ElectronVelocity:\n";
275 std::cerr << " Field map is not available for interpolation.\n";
276 status = -10;
277 return;
278 }
279
280 double x = xin, y = yin, z = zin;
281 // In case of periodicity, reduce to the cell volume.
282 bool xmirr = false, ymirr = false;
283 MapCoordinates(x, y, xmirr, ymirr);
284 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
285 status = -11;
286 return;
287 }
288 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) {
289 status = -11;
290 return;
291 }
292
293 // Assume this will work.
294 status = 0;
295 double w[nMaxVertices] = {0};
296 if (m_lastElement >= 0) {
297 // Check if the point is still located in the previously found element.
298 const Element& last = m_elements[m_lastElement];
299 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax) {
300 if (CheckElement(x, y, last, w)) {
301 const unsigned int nVertices = last.type + 1;
302 for (unsigned int j = 0; j < nVertices; ++j) {
303 const Vertex& vj = m_vertices[last.vertex[j]];
304 vx += w[j] * vj.eVx;
305 vy += w[j] * vj.eVy;
306 }
307 if (xmirr) vx = -vx;
308 if (ymirr) vy = -vy;
309 m = m_regions[last.region].medium;
310 if (!m_regions[last.region].drift || !m) status = -5;
311 return;
312 }
313 }
314 // The point is not in the previous element.
315 // Check the adjacent elements.
316 const unsigned int nNeighbours = last.neighbours.size();
317 for (unsigned int i = 0; i < nNeighbours; ++i) {
318 const Element& element = m_elements[last.neighbours[i]];
319 if (x < element.xmin || x > element.xmax ||
320 y < element.ymin || y > element.ymax) continue;
321 if (!CheckElement(x, y, element, w)) continue;
322 const unsigned int nVertices = element.type + 1;
323 for (unsigned int j = 0; j < nVertices; ++j) {
324 const Vertex& vj = m_vertices[element.vertex[j]];
325 vx += w[j] * vj.eVx;
326 vy += w[j] * vj.eVy;
327 }
328 if (xmirr) vx = -vx;
329 if (ymirr) vy = -vy;
330 m = m_regions[element.region].medium;
331 if (!m_regions[element.region].drift || !m) status = -5;
332 m_lastElement = last.neighbours[i];
333 return;
334 }
335 }
336
337 // The point is not in the previous element nor in the adjacent ones.
338 // We have to loop over all elements.
339 const unsigned int nElements = m_elements.size();
340 for (unsigned int i = 0; i < nElements; ++i) {
341 const Element& element = m_elements[i];
342 if (x < element.xmin || x > element.xmax ||
343 y < element.ymin || y > element.ymax) continue;
344 if (!CheckElement(x, y, element, w)) continue;
345 const unsigned int nVertices = element.type + 1;
346 for (unsigned int j = 0; j < nVertices; ++j) {
347 const Vertex& vj = m_vertices[element.vertex[j]];
348 vx += w[j] * vj.eVx;
349 vy += w[j] * vj.eVy;
350 }
351 if (xmirr) vx = -vx;
352 if (ymirr) vy = -vy;
353 m = m_regions[element.region].medium;
354 if (!m_regions[element.region].drift || !m) status = -5;
355 m_lastElement = i;
356 return;
357 }
358 // Point is outside the mesh.
359 if (m_debug) {
360 std::cerr << m_className << "::ElectronVelocity:\n"
361 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
362 }
363 status = -6;
364}
365
366void ComponentTcad2d::HoleVelocity(const double xin, const double yin,
367 const double zin,
368 double& vx, double& vy, double& vz,
369 Medium*& m, int& status) {
370
371 // Initialise.
372 vx = vy = vz = 0.;
373 m = NULL;
374
375 // Make sure the field map has been loaded.
376 if (!m_ready) {
377 std::cerr << m_className << "::HoleVelocity:\n"
378 << " Field map is not available for interpolation.\n";
379 status = -10;
380 return;
381 }
382
383 double x = xin, y = yin, z = zin;
384 // In case of periodicity, reduce to the cell volume.
385 bool xmirr = false, ymirr = false;
386 MapCoordinates(x, y, xmirr, ymirr);
387 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
388 status = -11;
389 return;
390 }
391 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) {
392 status = -11;
393 return;
394 }
395
396 // Assume this will work.
397 status = 0;
398
399 double w[nMaxVertices] = {0};
400 if (m_lastElement >= 0) {
401 // Check if the point is still located in the previously found element.
402 const Element& last = m_elements[m_lastElement];
403 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax) {
404 if (CheckElement(x, y, last, w)) {
405 const unsigned int nVertices = last.type + 1;
406 for (unsigned int j = 0; j < nVertices; ++j) {
407 const Vertex& vj = m_vertices[last.vertex[j]];
408 vx += w[j] * vj.hVx;
409 vy += w[j] * vj.hVy;
410 }
411 if (xmirr) vx = -vx;
412 if (ymirr) vy = -vy;
413 m = m_regions[last.region].medium;
414 if (!m_regions[last.region].drift || !m) status = -5;
415 return;
416 }
417 }
418 // The point is not in the previous element.
419 // Check the adjacent elements.
420 const unsigned int nNeighbours = last.neighbours.size();
421 for (unsigned int i = 0; i < nNeighbours; ++i) {
422 const Element& element = m_elements[last.neighbours[i]];
423 if (x < element.xmin || x > element.xmax ||
424 y < element.ymin || y > element.ymax) continue;
425 if (!CheckElement(x, y, element, w)) continue;
426 const unsigned int nVertices = element.type + 1;
427 for (unsigned int j = 0; j < nVertices; ++j) {
428 const Vertex& vj = m_vertices[element.vertex[j]];
429 vx += w[j] * vj.hVx;
430 vy += w[j] * vj.hVy;
431 }
432 if (xmirr) vx = -vx;
433 if (ymirr) vy = -vy;
434 m = m_regions[element.region].medium;
435 if (!m_regions[element.region].drift || !m) status = -5;
436 m_lastElement = last.neighbours[i];
437 return;
438 }
439 }
440
441 // The point is not in the previous element nor in the adjacent ones.
442 // We have to loop over all elements.
443 const unsigned int nElements = m_elements.size();
444 for (unsigned int i = 0; i < nElements; ++i) {
445 const Element& element = m_elements[i];
446 if (x < element.xmin || x > element.xmax ||
447 y < element.ymin || y > element.ymax) continue;
448 if (!CheckElement(x, y, element, w)) continue;
449 const unsigned int nVertices = element.type + 1;
450 for (unsigned int j = 0; j < nVertices; ++j) {
451 const Vertex& vj = m_vertices[element.vertex[j]];
452 vx += w[j] * vj.hVx;
453 vy += w[j] * vj.hVy;
454 }
455 if (xmirr) vx = -vx;
456 if (ymirr) vy = -vy;
457 m = m_regions[element.region].medium;
458 if (!m_regions[element.region].drift || !m) status = -5;
459 m_lastElement = i;
460 return;
461 }
462
463 // Point is outside the mesh.
464 if (m_debug) {
465 std::cerr << m_className << "::HoleVelocity:\n"
466 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
467 }
468 status = -6;
469}
470
471Medium* ComponentTcad2d::GetMedium(const double xin, const double yin,
472 const double zin) {
473
474 // Make sure the field map has been loaded.
475 if (!m_ready) {
476 std::cerr << m_className << "::GetMedium:\n"
477 << " Field map not available for interpolation.\n";
478 return NULL;
479 }
480
481 double x = xin, y = yin, z = zin;
482 // In case of periodicity, reduce to the cell volume.
483 bool xmirr = false, ymirr = false;
484 MapCoordinates(x, y, xmirr, ymirr);
485 // Check if the point is inside the bounding box.
486 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
487 return NULL;
488 }
489 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) return NULL;
490
491 // Shape functions
492 double w[nMaxVertices] = {0};
493 if (m_lastElement >= 0) {
494 // Check if the point is still located in the previously found element.
495 const Element& last = m_elements[m_lastElement];
496 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax &&
497 CheckElement(x, y, last, w)) {
498 return m_regions[last.region].medium;
499 }
500
501 // The point is not in the previous element.
502 // Check the adjacent elements.
503 const unsigned int nNeighbours = last.neighbours.size();
504 for (unsigned int i = 0; i < nNeighbours; ++i) {
505 const Element& element = m_elements[last.neighbours[i]];
506 if (x < element.xmin || x > element.xmax ||
507 y < element.ymin || y > element.ymax) continue;
508 if (!CheckElement(x, y, element, w)) continue;
509 m_lastElement = last.neighbours[i];
510 return m_regions[element.region].medium;
511 }
512 }
513
514 // The point is not in the previous element nor in the adjacent ones.
515 // We have to loop over all elements.
516 const unsigned int nElements = m_elements.size();
517 for (unsigned int i = 0; i < nElements; ++i) {
518 const Element& element = m_elements[i];
519 if (x < element.xmin || x > element.xmax ||
520 y < element.ymin || y > element.ymax) continue;
521 if (!CheckElement(x, y, element, w)) continue;
522 m_lastElement = i;
523 return m_regions[element.region].medium;
524 }
525
526 // Point is outside the mesh.
527 return NULL;
528}
529
530bool ComponentTcad2d::GetElectronLifetime(const double xin, const double yin,
531 const double zin, double& tau) {
532
533 tau = 0.;
534 // Make sure the field map has been loaded.
535 if (!m_ready) {
536 std::cerr << m_className << "::GetElectronLifetime:\n"
537 << " Field map is not available for interpolation.\n";
538 return false;
539 }
540
541 double x = xin, y = yin, z = zin;
542 // In case of periodicity, reduce to the cell volume.
543 bool xmirr = false, ymirr = false;
544 MapCoordinates(x, y, xmirr, ymirr);
545 // Check if the point is inside the bounding box.
546 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
547 return false;
548 }
549 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) return false;
550
551 double w[nMaxVertices] = {0};
552 if (m_lastElement >= 0) {
553 // Check if the point is still located in the previously found element.
554 const Element& last = m_elements[m_lastElement];
555 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax) {
556 if (CheckElement(x, y, last, w)) {
557 const unsigned int nVertices = last.type + 1;
558 for (unsigned int j = 0; j < nVertices; ++j) {
559 const Vertex& vj = m_vertices[last.vertex[j]];
560 tau += w[j] * vj.eTau;
561 }
562 return true;
563 }
564 }
565 // The point is not in the previous element.
566 // Check the adjacent elements.
567 const unsigned int nNeighbours = last.neighbours.size();
568 for (unsigned int i = 0; i < nNeighbours; ++i) {
569 const Element& element = m_elements[last.neighbours[i]];
570 if (x < element.xmin || x > element.xmax ||
571 y < element.ymin || y > element.ymax) continue;
572 if (!CheckElement(x, y, element, w)) continue;
573 const unsigned int nVertices = element.type + 1;
574 for (unsigned int j = 0; j < nVertices; ++j) {
575 const Vertex& vj = m_vertices[element.vertex[j]];
576 tau += w[j] * vj.eTau;
577 }
578 m_lastElement = last.neighbours[i];
579 return true;
580 }
581 }
582
583 // The point is not in the previous element nor in the adjacent ones.
584 // We have to loop over all elements.
585 const unsigned int nElements = m_elements.size();
586 for (unsigned int i = 0; i < nElements; ++i) {
587 const Element& element = m_elements[i];
588 if (x < element.xmin || x > element.xmax ||
589 y < element.ymin || y > element.ymax) continue;
590 if (!CheckElement(x, y, element, w)) continue;
591 const unsigned int nVertices = element.type + 1;
592 for (unsigned int j = 0; j < nVertices; ++j) {
593 const Vertex& vj = m_vertices[element.vertex[j]];
594 tau += w[j] * vj.eTau;
595 }
596 m_lastElement = i;
597 return true;
598 }
599
600 // Point is outside the mesh.
601 if (m_debug) {
602 std::cerr << m_className << "::GetElectronLifetime:\n"
603 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
604 }
605 return false;
606
607}
608bool ComponentTcad2d::GetHoleLifetime(const double xin, const double yin,
609 const double zin, double& tau) {
610
611 tau = 0.;
612 // Make sure the field map has been loaded.
613 if (!m_ready) {
614 std::cerr << m_className << "::GetHoleLifetime:\n"
615 << " Field map is not available for interpolation.\n";
616 return false;
617 }
618
619 double x = xin, y = yin, z = zin;
620 // In case of periodicity, reduce to the cell volume.
621 bool xmirr = false, ymirr = false;
622 MapCoordinates(x, y, xmirr, ymirr);
623 // Check if the point is inside the bounding box.
624 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
625 return false;
626 }
627 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) return false;
628
629 double w[nMaxVertices] = {0};
630 if (m_lastElement >= 0) {
631 // Check if the point is still located in the previously found element.
632 const Element& last = m_elements[m_lastElement];
633 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax) {
634 if (CheckElement(x, y, last, w)) {
635 const unsigned int nVertices = last.type + 1;
636 for (unsigned int j = 0; j < nVertices; ++j) {
637 const Vertex& vj = m_vertices[last.vertex[j]];
638 tau += w[j] * vj.hTau;
639 }
640 return true;
641 }
642 }
643 // The point is not in the previous element.
644 // Check the adjacent elements.
645 const unsigned int nNeighbours = last.neighbours.size();
646 for (unsigned int i = 0; i < nNeighbours; ++i) {
647 const Element& element = m_elements[last.neighbours[i]];
648 if (x < element.xmin || x > element.xmax ||
649 y < element.ymin || y > element.ymax) continue;
650 if (!CheckElement(x, y, element, w)) continue;
651 const unsigned int nVertices = element.type + 1;
652 for (unsigned int j = 0; j < nVertices; ++j) {
653 const Vertex& vj = m_vertices[element.vertex[j]];
654 tau += w[j] * vj.hTau;
655 }
656 m_lastElement = last.neighbours[i];
657 return true;
658 }
659 }
660
661 // The point is not in the previous element nor in the adjacent ones.
662 // We have to loop over all elements.
663 const unsigned int nElements = m_elements.size();
664 for (unsigned int i = 0; i < nElements; ++i) {
665 const Element& element = m_elements[i];
666 if (x < element.xmin || x > element.xmax ||
667 y < element.ymin || y > element.ymax) continue;
668 if (!CheckElement(x, y, element, w)) continue;
669 const unsigned int nVertices = element.type + 1;
670 for (unsigned int j = 0; j < nVertices; ++j) {
671 const Vertex& vj = m_vertices[element.vertex[j]];
672 tau += w[j] * vj.hTau;
673 }
674 m_lastElement = i;
675 return true;
676 }
677
678 // Point is outside the mesh.
679 if (m_debug) {
680 std::cerr << m_className << "::GetHoleLifetime:\n"
681 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
682 }
683 return false;
684
685}
686
687bool ComponentTcad2d::GetMobility(const double xin, const double yin,
688 const double zin,
689 double& emob, double& hmob) {
690
691 emob = hmob = 0.;
692
693 // Make sure the field map has been loaded.
694 if (!m_ready) {
695 std::cerr << m_className << "::GetMobility:\n"
696 << " Field map is not available for interpolation.\n";
697 return false;
698 }
699
700 double x = xin, y = yin, z = zin;
701 // In case of periodicity, reduce to the cell volume.
702 bool xmirr = false, ymirr = false;
703 MapCoordinates(x, y, xmirr, ymirr);
704 // Check if the point is inside the bounding box.
705 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
706 return false;
707 }
708 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) {
709 return false;
710 }
711
712 double w[nMaxVertices] = {0};
713 if (m_lastElement >= 0) {
714 // Check if the point is still located in the previously found element.
715 const Element& last = m_elements[m_lastElement];
716 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax) {
717 if (CheckElement(x, y, last, w)) {
718 const unsigned int nVertices = last.type + 1;
719 for (unsigned int j = 0; j < nVertices; ++j) {
720 const Vertex& vj = m_vertices[last.vertex[j]];
721 emob += w[j] * vj.emob;
722 hmob += w[j] * vj.hmob;
723 }
724 return true;
725 }
726 }
727 // The point is not in the previous element.
728 // Check the adjacent elements.
729 const unsigned int nNeighbours = last.neighbours.size();
730 for (unsigned int i = 0; i < nNeighbours; ++i) {
731 const Element& element = m_elements[last.neighbours[i]];
732 if (x < element.xmin || x > element.xmax ||
733 y < element.ymin || y > element.ymax) continue;
734 if (!CheckElement(x, y, element, w)) continue;
735 const unsigned int nVertices = element.type + 1;
736 for (unsigned int j = 0; j < nVertices; ++j) {
737 const Vertex& vj = m_vertices[element.vertex[j]];
738 emob += w[j] * vj.emob;
739 hmob += w[j] * vj.hmob;
740 }
741 m_lastElement = last.neighbours[i];
742 return true;
743 }
744 }
745
746 // The point is not in the previous element nor in the adjacent ones.
747 // We have to loop over all elements.
748 const unsigned int nElements = m_elements.size();
749 for (unsigned int i = 0; i < nElements; ++i) {
750 const Element& element = m_elements[i];
751 if (x < element.xmin || x > element.xmax ||
752 y < element.ymin || y > element.ymax) continue;
753 if (!CheckElement(x, y, element, w)) continue;
754 const unsigned int nVertices = element.type + 1;
755 for (unsigned int j = 0; j < nVertices; ++j) {
756 const Vertex& vj = m_vertices[element.vertex[j]];
757 emob += w[j] * vj.emob;
758 hmob += w[j] * vj.hmob;
759 }
760 m_lastElement = i;
761 return true;
762 }
763
764 // Point is outside the mesh.
765 if (m_debug) {
766 std::cerr << m_className << "::GetMobility:\n"
767 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
768 }
769 return false;
770}
771
772bool ComponentTcad2d::GetDonorOccupation(const double xin, const double yin,
773 const double zin,
774 const unsigned int donorNumber,
775 double& f) {
776 f = 0.;
777 if (donorNumber >= m_donors.size()) {
778 std::cerr << m_className << "::GetDonorOccupation:\n"
779 << " Donor " << donorNumber << " does not exist.\n";
780 return false;
781 }
782
783 // Make sure the field map has been loaded.
784 if (!m_ready) {
785 std::cerr << m_className << "::GetDonorOccupation:\n"
786 << " Field map is not available for interpolation.\n";
787 return false;
788 }
789
790 double x = xin, y = yin, z = zin;
791 // In case of periodicity, reduce to the cell volume.
792 bool xmirr = false, ymirr = false;
793 MapCoordinates(x, y, xmirr, ymirr);
794 // Check if the point is inside the bounding box.
795 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
796 return false;
797 }
798 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) {
799 return false;
800 }
801
802 double w[nMaxVertices] = {0};
803 if (m_lastElement >= 0) {
804 // Check if the point is still located in the previously found element.
805 const Element& last = m_elements[m_lastElement];
806 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax) {
807 if (CheckElement(x, y, last, w)) {
808 const unsigned int nVertices = last.type + 1;
809 for (unsigned int j = 0; j < nVertices; ++j) {
810 const Vertex& vj = m_vertices[last.vertex[j]];
811 f += w[j] * vj.donorOcc[donorNumber];
812 }
813 return true;
814 }
815 }
816 // The point is not in the previous element.
817 // Check the adjacent elements.
818 const unsigned int nNeighbours = last.neighbours.size();
819 for (unsigned int i = 0; i < nNeighbours; ++i) {
820 const Element& element = m_elements[last.neighbours[i]];
821 if (x < element.xmin || x > element.xmax ||
822 y < element.ymin || y > element.ymax) continue;
823 if (!CheckElement(x, y, element, w)) continue;
824 const unsigned int nVertices = element.type + 1;
825 for (unsigned int j = 0; j < nVertices; ++j) {
826 const Vertex& vj = m_vertices[element.vertex[j]];
827 f += w[j] * vj.donorOcc[donorNumber];
828 }
829 m_lastElement = last.neighbours[i];
830 return true;
831 }
832 }
833
834 // The point is not in the previous element nor in the adjacent ones.
835 // We have to loop over all elements.
836 const unsigned int nElements = m_elements.size();
837 for (unsigned int i = 0; i < nElements; ++i) {
838 const Element& element = m_elements[i];
839 if (x < element.xmin || x > element.xmax ||
840 y < element.ymin || y > element.ymax) continue;
841 if (!CheckElement(x, y, element, w)) continue;
842 const unsigned int nVertices = element.type + 1;
843 for (unsigned int j = 0; j < nVertices; ++j) {
844 const Vertex& vj = m_vertices[element.vertex[j]];
845 f += w[j] * vj.donorOcc[donorNumber];
846 }
847 m_lastElement = i;
848 return true;
849 }
850
851 // Point is outside the mesh.
852 if (m_debug) {
853 std::cerr << m_className << "::GetDonorOccupation:\n"
854 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
855 }
856 return false;
857}
858
859bool ComponentTcad2d::GetAcceptorOccupation(const double xin, const double yin,
860 const double zin,
861 const unsigned int acceptorNumber,
862 double& f) {
863 f = 0.;
864 if (acceptorNumber >= m_acceptors.size()) {
865 std::cerr << m_className << "::GetAcceptorOccupation:\n"
866 << " Acceptor " << acceptorNumber << " does not exist.\n";
867 return false;
868 }
869
870 // Make sure the field map has been loaded.
871 if (!m_ready) {
872 std::cerr << m_className << "::GetAcceptorOccupation:\n"
873 << " Field map is not available for interpolation.\n";
874 return false;
875 }
876
877 double x = xin, y = yin, z = zin;
878 // In case of periodicity, reduce to the cell volume.
879 bool xmirr = false, ymirr = false;
880 MapCoordinates(x, y, xmirr, ymirr);
881 // Check if the point is inside the bounding box.
882 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB) {
883 return false;
884 }
885 if (m_hasRangeZ && (z < m_zMinBB || z > m_zMaxBB)) {
886 return false;
887 }
888
889
890 double w[nMaxVertices] = {0};
891 if (m_lastElement >= 0) {
892 // Check if the point is still located in the previously found element.
893 const Element& last = m_elements[m_lastElement];
894 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax) {
895 if (CheckElement(x, y, last, w)) {
896 const unsigned int nVertices = last.type + 1;
897 for (unsigned int j = 0; j < nVertices; ++j) {
898 const Vertex& vj = m_vertices[last.vertex[j]];
899 f += w[j] * vj.acceptorOcc[acceptorNumber];
900 }
901 return true;
902 }
903 }
904 // The point is not in the previous element.
905 // Check the adjacent elements.
906 const unsigned int nNeighbours = last.neighbours.size();
907 for (unsigned int i = 0; i < nNeighbours; ++i) {
908 const Element& element = m_elements[last.neighbours[i]];
909 if (x < element.xmin || x > element.xmax ||
910 y < element.ymin || y > element.ymax) continue;
911 if (!CheckElement(x, y, element, w)) continue;
912 const unsigned int nVertices = element.type + 1;
913 for (unsigned int j = 0; j < nVertices; ++j) {
914 const Vertex& vj = m_vertices[element.vertex[j]];
915 f += w[j] * vj.acceptorOcc[acceptorNumber];
916 }
917 m_lastElement = last.neighbours[i];
918 return true;
919 }
920 }
921
922 // The point is not in the previous element nor in the adjacent ones.
923 // We have to loop over all elements.
924 const unsigned int nElements = m_elements.size();
925 for (unsigned int i = 0; i < nElements; ++i) {
926 const Element& element = m_elements[i];
927 if (x < element.xmin || x > element.xmax ||
928 y < element.ymin || y > element.ymax) continue;
929 if (!CheckElement(x, y, element, w)) continue;
930 const unsigned int nVertices = element.type + 1;
931 for (unsigned int j = 0; j < nVertices; ++j) {
932 const Vertex& vj = m_vertices[element.vertex[j]];
933 f += w[j] * vj.acceptorOcc[acceptorNumber];
934 }
935 m_lastElement = i;
936 return true;
937 }
938
939 // Point is outside the mesh.
940 if (m_debug) {
941 std::cerr << m_className << "::GetAcceptorOccupation:\n"
942 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
943 }
944 return false;
945}
946
947bool ComponentTcad2d::Initialise(const std::string& gridfilename,
948 const std::string& datafilename) {
949
950 m_ready = false;
951
952 m_hasPotential = m_hasField = false;
953 m_hasElectronMobility = m_hasHoleMobility = false;
954 m_validTraps = false;
955 m_donors.clear();
956 m_acceptors.clear();
957
958 // Import mesh data from .grd file.
959 if (!LoadGrid(gridfilename)) {
960 std::cerr << m_className << "::Initialise:\n"
961 << " Importing mesh data failed.\n";
962 return false;
963 }
964
965 // Import electric field, potential, mobilities and
966 // trap occupation values from .dat file.
967 if (!LoadData(datafilename)) {
968 std::cerr << m_className << "::Initialise:\n"
969 << " Importing electric field and potential failed.\n";
970 return false;
971 }
972
973 // Find min./max. coordinates and potentials.
974 m_xMaxBB = m_xMinBB = m_vertices[m_elements[0].vertex[0]].x;
975 m_yMaxBB = m_yMinBB = m_vertices[m_elements[0].vertex[0]].y;
976 m_pMax = m_pMin = m_vertices[m_elements[0].vertex[0]].p;
977 const unsigned int nElements = m_elements.size();
978 for (unsigned int i = 0; i < nElements; ++i) {
979 const Vertex& v0 = m_vertices[m_elements[i].vertex[0]];
980 const Vertex& v1 = m_vertices[m_elements[i].vertex[1]];
981 double xmin = std::min(v0.x, v1.x);
982 double xmax = std::max(v0.x, v1.x);
983 double ymin = std::min(v0.y, v1.y);
984 double ymax = std::max(v0.y, v1.y);
985 m_pMin = std::min(m_pMin, std::min(v0.p, v1.p));
986 m_pMax = std::max(m_pMax, std::max(v0.p, v1.p));
987 if (m_elements[i].type > 1) {
988 const Vertex& v2 = m_vertices[m_elements[i].vertex[2]];
989 xmin = std::min(xmin, v2.x);
990 xmax = std::max(xmax, v2.x);
991 ymin = std::min(ymin, v2.y);
992 ymax = std::max(ymax, v2.y);
993 m_pMin = std::min(m_pMin, v2.p);
994 m_pMax = std::max(m_pMax, v2.p);
995 }
996 if (m_elements[i].type > 2) {
997 const Vertex& v3 = m_vertices[m_elements[i].vertex[3]];
998 xmin = std::min(xmin, v3.x);
999 xmax = std::max(xmax, v3.x);
1000 ymin = std::min(ymin, v3.y);
1001 ymax = std::max(ymax, v3.y);
1002 m_pMin = std::min(m_pMin, v3.p);
1003 m_pMax = std::max(m_pMax, v3.p);
1004 }
1005 const double tol = 1.e-6;
1006 m_elements[i].xmin = xmin - tol;
1007 m_elements[i].xmax = xmax + tol;
1008 m_elements[i].ymin = ymin - tol;
1009 m_elements[i].ymax = ymax + tol;
1010 m_xMinBB = std::min(m_xMinBB, xmin);
1011 m_xMaxBB = std::max(m_xMaxBB, xmax);
1012 m_yMinBB = std::min(m_yMinBB, ymin);
1013 m_yMaxBB = std::max(m_yMaxBB, ymax);
1014 }
1015
1016 std::cout << m_className << "::Initialise:\n"
1017 << " Available data:\n";
1018 if (m_hasPotential) {
1019 std::cout << " Electrostatic potential\n";
1020 }
1021 if (m_hasField) {
1022 std::cout << " Electric field\n";
1023 }
1024 if (m_hasElectronMobility) {
1025 std::cout << " Electron mobility\n";
1026 }
1027 if (m_hasHoleMobility) {
1028 std::cout << " Hole mobility\n";
1029 }
1030 if (m_hasElectronVelocity) {
1031 std::cout << " Electron velocity\n";
1032 }
1033 if (m_hasHoleVelocity) {
1034 std::cout << " Hole velocity\n";
1035 }
1036 if (m_hasElectronLifetime) {
1037 std::cout << " Electron lifetimes\n";
1038 }
1039 if (m_hasHoleLifetime) {
1040 std::cout << " Hole lifetimes\n";
1041 }
1042 if (!m_donors.empty()) {
1043 std::cout << " " << m_donors.size() << " donor-type traps\n";
1044 }
1045 if (!m_acceptors.empty()) {
1046 std::cout << " " << m_acceptors.size() << " acceptor-type traps\n";
1047 }
1048 std::cout << " Bounding box:\n"
1049 << " " << m_xMinBB << " < x [cm] < " << m_xMaxBB << "\n"
1050 << " " << m_yMinBB << " < y [cm] < " << m_yMaxBB << "\n"
1051 << " Voltage range:\n"
1052 << " " << m_pMin << " < V < " << m_pMax << "\n";
1053
1054 bool ok = true;
1055
1056 // Count the number of elements belonging to a region.
1057 const int nRegions = m_regions.size();
1058 std::vector<int> nElementsRegion(nRegions, 0);
1059
1060 // Count the different element shapes.
1061 unsigned int nLines = 0;
1062 unsigned int nTriangles = 0;
1063 unsigned int nRectangles = 0;
1064 unsigned int nOtherShapes = 0;
1065
1066 // Check if there are elements which are not part of any region.
1067 unsigned int nLoose = 0;
1068 std::vector<int> looseElements;
1069
1070 // Check if there are degenerate elements.
1071 unsigned int nDegenerate = 0;
1072 std::vector<int> degenerateElements;
1073
1074 for (unsigned int i = 0; i < nElements; ++i) {
1075 const Element& element = m_elements[i];
1076 if (element.type == 1) {
1077 ++nLines;
1078 if (element.vertex[0] == element.vertex[1]) {
1079 degenerateElements.push_back(i);
1080 ++nDegenerate;
1081 }
1082 } else if (element.type == 2) {
1083 ++nTriangles;
1084 if (element.vertex[0] == element.vertex[1] ||
1085 element.vertex[1] == element.vertex[2] ||
1086 element.vertex[2] == element.vertex[0]) {
1087 degenerateElements.push_back(i);
1088 ++nDegenerate;
1089 }
1090 } else if (element.type == 3) {
1091 ++nRectangles;
1092 if (element.vertex[0] == element.vertex[1] ||
1093 element.vertex[0] == element.vertex[2] ||
1094 element.vertex[0] == element.vertex[3] ||
1095 element.vertex[1] == element.vertex[2] ||
1096 element.vertex[1] == element.vertex[3] ||
1097 element.vertex[2] == element.vertex[3]) {
1098 degenerateElements.push_back(i);
1099 ++nDegenerate;
1100 }
1101 } else {
1102 // Other shapes should not occur, since they were excluded in LoadGrid.
1103 ++nOtherShapes;
1104 }
1105 if (element.region >= 0 && element.region < nRegions) {
1106 ++nElementsRegion[element.region];
1107 } else {
1108 looseElements.push_back(i);
1109 ++nLoose;
1110 }
1111 }
1112
1113 if (nDegenerate > 0) {
1114 std::cerr << m_className << "::Initialise:\n"
1115 << " The following elements are degenerate:\n";
1116 for (unsigned int i = 0; i < nDegenerate; ++i) {
1117 std::cerr << " " << degenerateElements[i] << "\n";
1118 }
1119 ok = false;
1120 }
1121
1122 if (nLoose > 0) {
1123 std::cerr << m_className << "::Initialise:\n"
1124 << " The following elements are not part of any region:\n";
1125 for (unsigned int i = 0; i < nLoose; ++i) {
1126 std::cerr << " " << looseElements[i] << "\n";
1127 }
1128 ok = false;
1129 }
1130
1131 std::cout << m_className << "::Initialise:\n"
1132 << " Number of regions: " << nRegions << "\n";
1133 for (int i = 0; i < nRegions; ++i) {
1134 std::cout << " " << i << ": " << m_regions[i].name << ", "
1135 << nElementsRegion[i] << " elements\n";
1136 }
1137
1138 std::cout << " Number of elements: " << nElements << "\n";
1139 if (nLines > 0) {
1140 std::cout << " " << nLines << " lines\n";
1141 }
1142 if (nTriangles > 0) {
1143 std::cout << " " << nTriangles << " triangles\n";
1144 }
1145 if (nRectangles > 0) {
1146 std::cout << " " << nRectangles << " rectangles\n";
1147 }
1148 if (nOtherShapes > 0) {
1149 std::cerr << " " << nOtherShapes << " elements of unknown type.\n"
1150 << " Program bug!\n";
1151 m_ready = false;
1152 Cleanup();
1153 return false;
1154 }
1155
1156 std::cout << " Number of vertices: " << m_vertices.size() << "\n";
1157
1158 // Find adjacent elements.
1159 std::cout << m_className << "::Initialise:\n"
1160 << " Looking for neighbouring elements. Be patient...\n";
1161 FindNeighbours();
1162
1163 if (!ok) {
1164 m_ready = false;
1165 Cleanup();
1166 return false;
1167 }
1168
1169 m_ready = true;
1170 UpdatePeriodicity();
1171 std::cout << m_className << "::Initialise:\n"
1172 << " Initialisation finished.\n";
1173 return true;
1174}
1175
1176bool ComponentTcad2d::GetBoundingBox(double& xmin, double& ymin, double& zmin,
1177 double& xmax, double& ymax, double& zmax) {
1178
1179 if (!m_ready) return false;
1181 xmin = -INFINITY;
1182 xmax = +INFINITY;
1183 } else {
1184 xmin = m_xMinBB;
1185 xmax = m_xMaxBB;
1186 }
1187
1189 ymin = -INFINITY;
1190 ymax = +INFINITY;
1191 } else {
1192 ymin = m_yMinBB;
1193 ymax = m_yMaxBB;
1194 }
1195
1196 if (m_hasRangeZ) {
1197 zmin = m_zMinBB;
1198 zmax = m_zMaxBB;
1199 }
1200 return true;
1201}
1202
1203void ComponentTcad2d::SetRangeZ(const double zmin, const double zmax) {
1204
1205 if (fabs(zmax - zmin) <= 0.) {
1206 std::cerr << m_className << "::SetRangeZ:\n"
1207 << " Zero range is not permitted.\n";
1208 return;
1209 }
1210 m_zMinBB = std::min(zmin, zmax);
1211 m_zMaxBB = std::max(zmin, zmax);
1212 m_hasRangeZ = true;
1213}
1214
1215bool ComponentTcad2d::GetVoltageRange(double& vmin, double& vmax) {
1216
1217 if (!m_ready) return false;
1218 vmin = m_pMin;
1219 vmax = m_pMax;
1220 return true;
1221}
1222
1224
1225 // Do not proceed if not properly initialised.
1226 if (!m_ready) {
1227 std::cerr << m_className << "::PrintRegions:\n"
1228 << " Field map not yet initialised.\n";
1229 return;
1230 }
1231
1232 if (m_regions.empty()) {
1233 std::cerr << m_className << "::PrintRegions:\n"
1234 << " No regions are currently defined.\n";
1235 return;
1236 }
1237
1238 const unsigned int nRegions = m_regions.size();
1239 std::cout << m_className << "::PrintRegions:\n"
1240 << " Currently " << nRegions << " regions are defined.\n"
1241 << " Index Name Medium\n";
1242 for (unsigned int i = 0; i < nRegions; ++i) {
1243 std::cout << " " << i << " " << m_regions[i].name;
1244 if (!m_regions[i].medium) {
1245 std::cout << " none ";
1246 } else {
1247 std::cout << " " << m_regions[i].medium->GetName();
1248 }
1249 if (m_regions[i].drift) {
1250 std::cout << " (active region)\n";
1251 } else {
1252 std::cout << "\n";
1253 }
1254 }
1255}
1256
1257void ComponentTcad2d::GetRegion(const unsigned int i,
1258 std::string& name, bool& active) const {
1259
1260 if (i >= m_regions.size()) {
1261 std::cerr << m_className << "::GetRegion:\n"
1262 << " Region " << i << " does not exist.\n";
1263 return;
1264 }
1265 name = m_regions[i].name;
1266 active = m_regions[i].drift;
1267}
1268
1269void ComponentTcad2d::SetDriftRegion(const unsigned int i) {
1270
1271 if (i >= m_regions.size()) {
1272 std::cerr << m_className << "::SetDriftRegion:\n"
1273 << " Region " << i << " does not exist.\n";
1274 return;
1275 }
1276 m_regions[i].drift = true;
1277}
1278
1279void ComponentTcad2d::UnsetDriftRegion(const unsigned int i) {
1280
1281 if (i >= m_regions.size()) {
1282 std::cerr << m_className << "::UnsetDriftRegion:\n"
1283 << " Region " << i << " does not exist.\n";
1284 return;
1285 }
1286 m_regions[i].drift = false;
1287}
1288
1289void ComponentTcad2d::SetMedium(const unsigned int i, Medium* medium) {
1290
1291 if (i >= m_regions.size()) {
1292 std::cerr << m_className << "::SetMedium:\n"
1293 << " Region " << i << " does not exist.\n";
1294 return;
1295 }
1296
1297 if (!medium) {
1298 std::cerr << m_className << "::SetMedium:\n Null pointer.\n";
1299 return;
1300 }
1301
1302 m_regions[i].medium = medium;
1303}
1304
1305Medium* ComponentTcad2d::GetMedium(const unsigned int i) const {
1306
1307 if (i >= m_regions.size()) {
1308 std::cerr << m_className << "::GetMedium:\n"
1309 << " Region " << i << " does not exist.\n";
1310 return NULL;
1311 }
1312
1313 return m_regions[i].medium;
1314}
1315
1316bool ComponentTcad2d::GetElement(const unsigned int i, double& vol, double& dmin,
1317 double& dmax, int& type) const {
1318
1319 if (i >= m_elements.size()) {
1320 std::cerr << m_className << "::GetElement:\n"
1321 << " Element index (" << i << ") out of range.\n";
1322 return false;
1323 }
1324
1325 const Element& element = m_elements[i];
1326 if (element.type == 1) {
1327 const Vertex& v0 = m_vertices[element.vertex[0]];
1328 const Vertex& v1 = m_vertices[element.vertex[1]];
1329 const double dx = v1.x - v0.x;
1330 const double dy = v1.y - v0.y;
1331 const double d = sqrt(dx * dx + dy * dy);
1332 dmin = dmax = vol = d;
1333 } else if (m_elements[i].type == 2) {
1334 const Vertex& v0 = m_vertices[element.vertex[0]];
1335 const Vertex& v1 = m_vertices[element.vertex[1]];
1336 const Vertex& v2 = m_vertices[element.vertex[2]];
1337 vol = 0.5 * fabs((v2.x - v0.x) * (v1.y - v0.y) -
1338 (v2.y - v0.y) * (v1.x - v0.x));
1339 const double a = sqrt(pow(v1.x - v0.x, 2) + pow(v1.y - v0.y, 2));
1340 const double b = sqrt(pow(v2.x - v0.x, 2) + pow(v2.y - v0.y, 2));
1341 const double c = sqrt(pow(v1.x - v2.x, 2) + pow(v1.y - v2.y, 2));
1342 dmin = std::min(std::min(a, b), c);
1343 dmax = std::max(std::max(a, b), c);
1344 } else if (m_elements[i].type == 3) {
1345 const Vertex& v0 = m_vertices[element.vertex[0]];
1346 const Vertex& v1 = m_vertices[element.vertex[1]];
1347 const Vertex& v3 = m_vertices[element.vertex[3]];
1348 const double a = sqrt(pow(v1.x - v0.x, 2) + pow(v1.y - v0.y, 2));
1349 const double b = sqrt(pow(v3.x - v0.x, 2) + pow(v3.y - v0.y, 2));
1350 vol = a * b;
1351 dmin = std::min(a, b);
1352 dmax = sqrt(a * a + b * b);
1353 } else {
1354 std::cerr << m_className << "::GetElement:\n"
1355 << " Unexpected element type (" << type << ")\n";
1356 return false;
1357 }
1358 return true;
1359}
1360
1361bool ComponentTcad2d::GetElement(const unsigned int i, double& vol,
1362 double& dmin, double& dmax, int& type,
1363 int& node1, int& node2,
1364 int& node3, int& node4, int& reg) const {
1365
1366 if (!GetElement(i, vol, dmin, dmax, type)) return false;
1367 const Element& element = m_elements[i];
1368 node1 = element.vertex[0];
1369 node2 = element.vertex[1];
1370 node3 = element.vertex[2];
1371 node4 = element.vertex[3];
1372 reg = element.region;
1373 return true;
1374}
1375
1376bool ComponentTcad2d::GetNode(const unsigned int i,
1377 double& x, double& y, double& v,
1378 double& ex, double& ey) const {
1379
1380 if (i >= m_vertices.size()) {
1381 std::cerr << m_className << "::GetNode:\n"
1382 << " Node index (" << i << ") out of range.\n";
1383 return false;
1384 }
1385
1386 x = m_vertices[i].x;
1387 y = m_vertices[i].y;
1388 v = m_vertices[i].p;
1389 ex = m_vertices[i].ex;
1390 ey = m_vertices[i].ey;
1391 return true;
1392}
1393
1394bool ComponentTcad2d::LoadData(const std::string& datafilename) {
1395
1396 std::ifstream datafile;
1397 datafile.open(datafilename.c_str(), std::ios::in);
1398 if (!datafile) {
1399 std::cerr << m_className << "::LoadData:\n"
1400 << " Could not open file " << datafilename << ".\n";
1401 return false;
1402 }
1403
1404 const unsigned int nVertices = m_vertices.size();
1405 std::vector<unsigned int> fillCount(nVertices, 0);
1406 for (unsigned int i = 0; i < nVertices; ++i) {
1407 m_vertices[i].p = 0.;
1408 m_vertices[i].ex = 0.;
1409 m_vertices[i].ey = 0.;
1410 m_vertices[i].emob = 0.;
1411 m_vertices[i].hmob = 0.;
1412 m_vertices[i].donorOcc.clear();
1413 m_vertices[i].acceptorOcc.clear();
1414 }
1415 m_donors.clear();
1416 m_acceptors.clear();
1417
1418 while (!datafile.fail()) {
1419 // Read one line and strip white space from the beginning of the line.
1420 std::string line;
1421 std::getline(datafile, line);
1422 ltrim(line);
1423 // Find data section.
1424 if (line.substr(0, 8) != "function") continue;
1425 // Read type of data set.
1426 const std::string::size_type pEq = line.find('=');
1427 if (pEq == std::string::npos) {
1428 // No "=" found.
1429 std::cerr << m_className << "::LoadData:\n"
1430 << " Error reading file " << datafilename << ".\n"
1431 << " Line:\n " << line << "\n";
1432 datafile.close();
1433 Cleanup();
1434 return false;
1435 }
1436 line = line.substr(pEq + 1);
1437 std::string dataset;
1438 std::istringstream data;
1439 data.str(line);
1440 data >> dataset;
1441 if (dataset == "ElectrostaticPotential") {
1442 if (!ReadDataset(datafile, dataset)) return false;
1443 m_hasPotential = true;
1444 } else if (dataset == "ElectricField") {
1445 if (!ReadDataset(datafile, dataset)) return false;
1446 m_hasField = true;
1447 } else if (dataset == "eDriftVelocity") {
1448 if (!ReadDataset(datafile, dataset)) return false;
1449 m_hasElectronVelocity= true;
1450 } else if (dataset == "hDriftVelocity") {
1451 if (!ReadDataset(datafile, dataset)) return false;
1452 m_hasHoleVelocity= true;
1453 } else if (dataset == "eMobility") {
1454 if (!ReadDataset(datafile, dataset)) return false;
1455 m_hasElectronMobility = true;
1456 } else if (dataset == "hMobility") {
1457 if (!ReadDataset(datafile, dataset)) return false;
1458 m_hasHoleMobility = true;
1459 } else if (dataset == "eLifetime") {
1460 if (!ReadDataset(datafile, dataset)) return false;
1461 m_hasElectronLifetime = true;
1462 } else if (dataset == "hLifetime") {
1463 if (!ReadDataset(datafile, dataset)) return false;
1464 m_hasHoleLifetime = true;
1465 } else if (dataset.substr(0,14) == "TrapOccupation" &&
1466 dataset.substr(17,2) == "Do") {
1467 if (!ReadDataset(datafile, dataset)) return false;
1468 Defect donor;
1469 donor.xsece = -1.;
1470 donor.xsech = -1.;
1471 donor.conc = -1.;
1472 m_donors.push_back(donor);
1473 } else if (dataset.substr(0,14) == "TrapOccupation" &&
1474 dataset.substr(17, 2) == "Ac") {
1475 if (!ReadDataset(datafile, dataset)) return false;
1476 Defect acceptor;
1477 acceptor.xsece = -1.;
1478 acceptor.xsech = -1.;
1479 acceptor.conc = -1.;
1480 m_acceptors.push_back(acceptor);
1481 }
1482 }
1483 if (datafile.fail() && !datafile.eof()) {
1484 std::cerr << m_className << "::LoadData\n"
1485 << " Error reading file " << datafilename << "\n";
1486 datafile.close();
1487 Cleanup();
1488 return false;
1489 }
1490
1491 datafile.close();
1492
1493 return true;
1494}
1495
1496bool ComponentTcad2d::ReadDataset(std::ifstream& datafile,
1497 const std::string& dataset) {
1498
1499 enum DataSet {
1500 ElectrostaticPotential,
1501 EField,
1502 eDriftVelocity,
1503 hDriftVelocity,
1504 eMobility,
1505 hMobility,
1506 eLifetime,
1507 hLifetime,
1508 DonorTrapOccupation,
1509 AcceptorTrapOccupation,
1510 Unknown
1511 };
1512 DataSet ds = Unknown;
1513 if (dataset == "ElectrostaticPotential") {
1514 ds = ElectrostaticPotential;
1515 } else if (dataset == "ElectricField") {
1516 ds = EField;
1517 } else if (dataset == "eDriftVelocity") {
1518 ds = eDriftVelocity;
1519 } else if (dataset == "hDriftVelocity") {
1520 ds = hDriftVelocity;
1521 } else if (dataset == "eMobility") {
1522 ds = eMobility;
1523 } else if (dataset == "hMobility") {
1524 ds = hMobility;
1525 } else if (dataset == "eLifetime") {
1526 ds = eLifetime;
1527 } else if (dataset == "hLifetime") {
1528 ds = hLifetime;
1529 } else if (dataset.substr(0,14) == "TrapOccupation") {
1530 if (dataset.substr(17,2) == "Do") {
1531 ds = DonorTrapOccupation;
1532 } else if (dataset.substr(17,2) == "Ac") {
1533 ds = AcceptorTrapOccupation;
1534 }
1535 } else {
1536 std::cerr << m_className << "::ReadDataset:\n"
1537 << " Unexpected dataset " << dataset << ".\n";
1538 return false;
1539 }
1540
1541 bool isVector = false;
1542 if (ds == EField ||
1543 ds == eDriftVelocity || ds == hDriftVelocity) {
1544 isVector = true;
1545 }
1546
1547 if (!datafile.is_open()) return false;
1548 std::string line;
1549 std::getline(datafile, line);
1550 std::getline(datafile, line);
1551 std::getline(datafile, line);
1552 std::getline(datafile, line);
1553 // Get the region name (given in brackets).
1554 std::string::size_type bra = line.find('[');
1555 std::string::size_type ket = line.find(']');
1556 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1557 std::cerr << m_className << "::ReadDataset:\n"
1558 << " Cannot extract region name.\n"
1559 << " Line:\n " << line << "\n";
1560 datafile.close();
1561 Cleanup();
1562 return false;
1563 }
1564 line = line.substr(bra + 1, ket - bra - 1);
1565 std::string name;
1566 std::istringstream data;
1567 data.str(line);
1568 data >> name;
1569 data.clear();
1570 // Check if the region name matches one from the mesh file.
1571 const int index = FindRegion(name);
1572 if (index == -1) {
1573 std::cerr << m_className << "::ReadDataset:\n"
1574 << " Unknown region " << name << ".\n";
1575 datafile.close();
1576 Cleanup();
1577 return false;
1578 }
1579 // Get the number of values.
1580 std::getline(datafile, line);
1581 bra = line.find('(');
1582 ket = line.find(')');
1583 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1584 std::cerr << m_className << "::LoadData:\n"
1585 << " Cannot extract number of values to be read.\n"
1586 << " Line:\n " << line << "\n";
1587 datafile.close();
1588 Cleanup();
1589 return false;
1590 }
1591 line = line.substr(bra + 1, ket - bra - 1);
1592 int nValues;
1593 data.str(line);
1594 data >> nValues;
1595 data.clear();
1596 if (isVector) nValues = nValues / 2;
1597 // Mark the vertices belonging to this region.
1598 const unsigned int nVertices = m_vertices.size();
1599 std::vector<bool> isInRegion(nVertices, false);
1600 const unsigned int nElements = m_elements.size();
1601 for (unsigned int j = 0; j < nElements; ++j) {
1602 if (m_elements[j].region != index) continue;
1603 for (int k = 0; k <= m_elements[j].type; ++k) {
1604 isInRegion[m_elements[j].vertex[k]] = true;
1605 }
1606 }
1607
1608 unsigned int ivertex = 0;
1609 for (int j = 0; j < nValues; ++j) {
1610 // Read the next value.
1611 double val1, val2;
1612 if (isVector) {
1613 datafile >> val1 >> val2;
1614 } else {
1615 datafile >> val1;
1616 }
1617 // Find the next vertex belonging to the region.
1618 while (ivertex < nVertices) {
1619 if (isInRegion[ivertex]) break;
1620 ++ivertex;
1621 }
1622 // Check if there is a mismatch between the number of vertices
1623 // and the number of values.
1624 if (ivertex >= nVertices) {
1625 std::cerr << m_className << "::ReadDataset:\n"
1626 << " Dataset " << dataset
1627 << " has more values than vertices in region " << name << "\n";
1628 datafile.close();
1629 Cleanup();
1630 return false;
1631 }
1632 switch (ds) {
1633 case ElectrostaticPotential:
1634 m_vertices[ivertex].p = val1;
1635 break;
1636 case EField:
1637 m_vertices[ivertex].ex = val1;
1638 m_vertices[ivertex].ey = val2;
1639 break;
1640 case eDriftVelocity:
1641 // Scale from cm/s to cm/ns.
1642 m_vertices[ivertex].eVx = val1 * 1.e-9;
1643 m_vertices[ivertex].eVy = val2 * 1.e-9;
1644 break;
1645 case hDriftVelocity:
1646 // Scale from cm/s to cm/ns.
1647 m_vertices[ivertex].hVx = val1 * 1.e-9;
1648 m_vertices[ivertex].hVy = val2 * 1.e-9;
1649 break;
1650 case eMobility:
1651 // Convert from cm2 / (V s) to cm2 / (V ns).
1652 m_vertices[ivertex].emob = val1 * 1.e-9;
1653 break;
1654 case hMobility:
1655 // Convert from cm2 / (V s) to cm2 / (V ns).
1656 m_vertices[ivertex].hmob = val1 * 1.e-9;
1657 break;
1658 case eLifetime:
1659 // Convert from s to ns.
1660 m_vertices[ivertex].eTau = val1 * 1.e9;
1661 break;
1662 case hLifetime:
1663 // Convert from s to ns.
1664 m_vertices[ivertex].hTau = val1 * 1.e9;
1665 break;
1666 case DonorTrapOccupation:
1667 m_vertices[ivertex].donorOcc.push_back(val1);
1668 break;
1669 case AcceptorTrapOccupation:
1670 m_vertices[ivertex].acceptorOcc.push_back(val1);
1671 break;
1672 default:
1673 std::cerr << m_className << "::ReadDataset:\n"
1674 << " Unexpected dataset (" << ds << "). Program bug!\n";
1675 datafile.close();
1676 Cleanup();
1677 return false;
1678 }
1679 ++ivertex;
1680 }
1681 return true;
1682
1683}
1684
1685bool ComponentTcad2d::LoadGrid(const std::string& gridfilename) {
1686
1687 // Open the file containing the mesh description.
1688 std::ifstream gridfile;
1689 gridfile.open(gridfilename.c_str(), std::ios::in);
1690 if (!gridfile) {
1691 std::cerr << m_className << "::LoadGrid:\n"
1692 << " Could not open file " << gridfilename << ".\n";
1693 return false;
1694 }
1695
1696 // Delete existing mesh information.
1697 Cleanup();
1698 // Count line numbers.
1699 unsigned int iLine = 0;
1700 // Get the number of regions.
1701 unsigned int nRegions = 0;
1702 while (!gridfile.fail()) {
1703 // Read one line and strip white space from the beginning of the line.
1704 std::string line;
1705 std::getline(gridfile, line);
1706 ++iLine;
1707 ltrim(line);
1708 // Find entry 'nb_regions'.
1709 if (line.substr(0, 10) != "nb_regions") continue;
1710 const std::string::size_type pEq = line.find('=');
1711 if (pEq == std::string::npos) {
1712 // No "=" sign found.
1713 std::cerr << m_className << "::LoadGrid:\n"
1714 << " Could not read number of regions.\n";
1715 Cleanup();
1716 gridfile.close();
1717 return false;
1718 }
1719 line = line.substr(pEq + 1);
1720 std::istringstream data;
1721 data.str(line);
1722 data >> nRegions;
1723 break;
1724 }
1725 if (gridfile.eof()) {
1726 // Reached end of file.
1727 std::cerr << m_className << "::LoadGrid:\n"
1728 << " Could not find entry 'nb_regions' in file\n"
1729 << " " << gridfilename << ".\n";
1730 Cleanup();
1731 gridfile.close();
1732 return false;
1733 } else if (gridfile.fail()) {
1734 // Error reading from the file.
1735 std::cerr << m_className << "::LoadGrid:\n"
1736 << " Error reading file " << gridfilename << " (line "
1737 << iLine << ").\n";
1738 Cleanup();
1739 gridfile.close();
1740 return false;
1741 }
1742 m_regions.resize(nRegions);
1743 for (unsigned int j = 0; j < nRegions; ++j) {
1744 m_regions[j].name = "";
1745 m_regions[j].drift = false;
1746 m_regions[j].medium = NULL;
1747 }
1748
1749 if (m_debug) {
1750 std::cout << m_className << "::LoadGrid:\n"
1751 << " Found " << nRegions << " regions.\n";
1752 }
1753
1754 // Get the region names.
1755 while (!gridfile.fail()) {
1756 std::string line;
1757 std::getline(gridfile, line);
1758 ++iLine;
1759 ltrim(line);
1760 // Find entry 'regions'.
1761 if (line.substr(0, 7) != "regions") continue;
1762 // Get region names (given in brackets).
1763 const std::string::size_type bra = line.find('[');
1764 const std::string::size_type ket = line.find(']');
1765 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1766 // No closed brackets [].
1767 std::cerr << m_className << "::LoadGrid:\n"
1768 << " Could not read region names.\n";
1769 Cleanup();
1770 gridfile.close();
1771 return false;
1772 }
1773 line = line.substr(bra + 1, ket - bra - 1);
1774 std::istringstream data;
1775 data.str(line);
1776 for (unsigned int j = 0; j < nRegions; ++j) {
1777 data >> m_regions[j].name;
1778 data.clear();
1779 // Assume by default that all regions are active.
1780 m_regions[j].drift = true;
1781 m_regions[j].medium = NULL;
1782 }
1783 break;
1784 }
1785 if (gridfile.eof()) {
1786 // Reached end of file.
1787 std::cerr << m_className << "::LoadGrid:\n"
1788 << " Could not find entry 'regions' in file\n"
1789 << " " << gridfilename << ".\n";
1790 Cleanup();
1791 gridfile.close();
1792 return false;
1793 } else if (gridfile.fail()) {
1794 // Error reading from the file.
1795 std::cerr << m_className << "::LoadGrid:\n"
1796 << " Error reading file " << gridfilename << " (line "
1797 << iLine << ").\n";
1798 Cleanup();
1799 gridfile.close();
1800 return false;
1801 }
1802
1803 // Get the vertices.
1804 unsigned int nVertices = 0;
1805 while (!gridfile.fail()) {
1806 std::string line;
1807 std::getline(gridfile, line);
1808 ++iLine;
1809 ltrim(line);
1810 // Find section 'Vertices'.
1811 if (line.substr(0, 8) != "Vertices") continue;
1812 // Get number of vertices (given in brackets).
1813 const std::string::size_type bra = line.find('(');
1814 const std::string::size_type ket = line.find(')');
1815 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1816 // No closed brackets [].
1817 std::cerr << m_className << "::LoadGrid:\n"
1818 << " Could not read number of vertices.\n";
1819 Cleanup();
1820 gridfile.close();
1821 return false;
1822 }
1823 line = line.substr(bra + 1, ket - bra - 1);
1824 std::istringstream data;
1825 data.str(line);
1826 data >> nVertices;
1827 m_vertices.resize(nVertices);
1828 // Get the coordinates of this vertex.
1829 for (unsigned int j = 0; j < nVertices; ++j) {
1830 gridfile >> m_vertices[j].x >> m_vertices[j].y;
1831 // Change units from micron to cm.
1832 m_vertices[j].x *= 1.e-4;
1833 m_vertices[j].y *= 1.e-4;
1834 ++iLine;
1835 }
1836 break;
1837 }
1838 if (gridfile.eof()) {
1839 std::cerr << m_className << "::LoadGrid:\n"
1840 << " Could not find section 'Vertices' in file\n"
1841 << " " << gridfilename << ".\n";
1842 Cleanup();
1843 gridfile.close();
1844 return false;
1845 } else if (gridfile.fail()) {
1846 std::cerr << m_className << "::LoadGrid:\n"
1847 << " Error reading file " << gridfilename << " (line " << iLine
1848 << ").\n";
1849 Cleanup();
1850 gridfile.close();
1851 return false;
1852 }
1853
1854 // Get the "edges" (lines connecting two vertices).
1855 int nEdges = 0;
1856 // Temporary arrays for storing edge points.
1857 std::vector<int> edgeP1;
1858 std::vector<int> edgeP2;
1859 while (!gridfile.fail()) {
1860 std::string line;
1861 std::getline(gridfile, line);
1862 ++iLine;
1863 ltrim(line);
1864 // Find section 'Edges'.
1865 if (line.substr(0, 5) != "Edges") continue;
1866 // Get the number of edges (given in brackets).
1867 const std::string::size_type bra = line.find('(');
1868 const std::string::size_type ket = line.find(')');
1869 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1870 // No closed brackets ()
1871 std::cerr << m_className << "::LoadGrid:\n"
1872 << " Could not read number of edges.\n";
1873 Cleanup();
1874 gridfile.close();
1875 return false;
1876 }
1877 line = line.substr(bra + 1, ket - bra - 1);
1878 std::istringstream data;
1879 data.str(line);
1880 data >> nEdges;
1881 edgeP1.resize(nEdges);
1882 edgeP2.resize(nEdges);
1883 // Get the indices of the two endpoints.
1884 for (int j = 0; j < nEdges; ++j) {
1885 gridfile >> edgeP1[j] >> edgeP2[j];
1886 ++iLine;
1887 }
1888 break;
1889 }
1890 if (gridfile.eof()) {
1891 std::cerr << m_className << "::LoadGrid:\n"
1892 << " Could not find section 'Edges' in file\n"
1893 << " " << gridfilename << ".\n";
1894 Cleanup();
1895 gridfile.close();
1896 return false;
1897 } else if (gridfile.fail()) {
1898 std::cerr << m_className << "::LoadGrid:\n"
1899 << " Error reading file " << gridfilename << " (line "
1900 << iLine << ").\n";
1901 Cleanup();
1902 gridfile.close();
1903 return false;
1904 }
1905
1906 for (int i = 0; i < nEdges; ++i) {
1907 // Make sure the indices of the edge endpoints are not out of range.
1908 if (edgeP1[i] < 0 || edgeP1[i] >= (int)nVertices ||
1909 edgeP2[i] < 0 || edgeP2[i] >= (int)nVertices) {
1910 std::cerr << m_className << "::LoadGrid:\n"
1911 << " Vertex index of edge " << i << " out of range.\n";
1912 Cleanup();
1913 gridfile.close();
1914 return false;
1915 }
1916 // Make sure the edge is non-degenerate.
1917 if (edgeP1[i] == edgeP2[i]) {
1918 std::cerr << m_className << "::LoadGrid:\n"
1919 << " Edge " << i << " is degenerate.\n";
1920 Cleanup();
1921 gridfile.close();
1922 return false;
1923 }
1924 }
1925
1926 // Get the elements.
1927 int edge0, edge1, edge2, edge3;
1928 int type;
1929 unsigned int nElements = 0;
1930 while (!gridfile.fail()) {
1931 std::string line;
1932 std::getline(gridfile, line);
1933 ++iLine;
1934 ltrim(line);
1935 // Find section 'Elements'.
1936 if (line.substr(0, 8) != "Elements") continue;
1937 // Get number of elements (given in brackets).
1938 const std::string::size_type bra = line.find('(');
1939 const std::string::size_type ket = line.find(')');
1940 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1941 // No closed brackets ().
1942 std::cerr << m_className << "::LoadGrid:\n"
1943 << " Could not read number of elements.\n";
1944 Cleanup();
1945 gridfile.close();
1946 return false;
1947 }
1948 line = line.substr(bra + 1, ket - bra - 1);
1949 std::istringstream data;
1950 data.str(line);
1951 data >> nElements;
1952 // Resize array of elements.
1953 m_elements.resize(nElements);
1954 // Get type and constituting edges of each element.
1955 for (unsigned int j = 0; j < nElements; ++j) {
1956 for (int k = nMaxVertices; k--;) m_elements[j].vertex[k] = -1;
1957 m_elements[j].neighbours.clear();
1958 ++iLine;
1959 gridfile >> type;
1960 switch (type) {
1961 case 1:
1962 // Line
1963 gridfile >> edge0 >> edge1;
1964 if (edge0 < 0) edge0 = -edge0 - 1;
1965 if (edge1 < 0) edge1 = -edge1 - 1;
1966 // Make sure the indices are not out of range.
1967 if (edge0 >= nEdges || edge1 >= nEdges) {
1968 std::cerr << m_className << "::LoadGrid:\n"
1969 << " Error reading file " << gridfilename
1970 << " (line " << iLine << ").\n"
1971 << " Edge index out of range.\n";
1972 Cleanup();
1973 gridfile.close();
1974 return false;
1975 }
1976 // Get the vertices of this element.
1977 // Negative edge index means that the sequence of the two points
1978 // is supposed to be inverted.
1979 // The actual index is then given by "-index - 1".
1980 // Orientt the line such that the first point is on the left.
1981 if (m_vertices[edgeP1[edge0]].x > m_vertices[edgeP2[edge0]].x) {
1982 m_elements[j].vertex[0] = edgeP2[edge0];
1983 m_elements[j].vertex[1] = edgeP1[edge0];
1984 } else {
1985 m_elements[j].vertex[0] = edgeP1[edge0];
1986 m_elements[j].vertex[1] = edgeP2[edge0];
1987 }
1988 break;
1989 case 2:
1990 // Triangle
1991 gridfile >> edge0 >> edge1 >> edge2;
1992 // Make sure the indices are not out of range.
1993 if (edge0 < 0) edge0 = -edge0 - 1;
1994 if (edge1 < 0) edge1 = -edge1 - 1;
1995 if (edge2 < 0) edge2 = -edge2 - 1;
1996 if (edge0 >= nEdges || edge1 >= nEdges || edge2 >= nEdges) {
1997 std::cerr << m_className << "::LoadGrid:\n"
1998 << " Error reading file " << gridfilename
1999 << " (line " << iLine << ").\n"
2000 << " Edge index out of range.\n";
2001 Cleanup();
2002 gridfile.close();
2003 return false;
2004 }
2005 m_elements[j].vertex[0] = edgeP1[edge0];
2006 m_elements[j].vertex[1] = edgeP2[edge0];
2007 if (edgeP1[edge1] != m_elements[j].vertex[0] &&
2008 edgeP1[edge1] != m_elements[j].vertex[1]) {
2009 m_elements[j].vertex[2] = edgeP1[edge1];
2010 } else {
2011 m_elements[j].vertex[2] = edgeP2[edge1];
2012 }
2013 // Rearrange vertices such that point 0 is on the left.
2014 while (m_vertices[m_elements[j].vertex[0]].x >
2015 m_vertices[m_elements[j].vertex[1]].x ||
2016 m_vertices[m_elements[j].vertex[0]].x >
2017 m_vertices[m_elements[j].vertex[2]].x) {
2018 const int tmp = m_elements[j].vertex[0];
2019 m_elements[j].vertex[0] = m_elements[j].vertex[1];
2020 m_elements[j].vertex[1] = m_elements[j].vertex[2];
2021 m_elements[j].vertex[2] = tmp;
2022 }
2023 break;
2024 case 3:
2025 // Rectangle
2026 gridfile >> edge0 >> edge1 >> edge2 >> edge3;
2027 // Make sure the indices are not out of range.
2028 if (edge0 >= nEdges || -edge0 - 1 >= nEdges ||
2029 edge1 >= nEdges || -edge1 - 1 >= nEdges ||
2030 edge2 >= nEdges || -edge2 - 1 >= nEdges ||
2031 edge3 >= nEdges || -edge3 - 1 >= nEdges) {
2032 std::cerr << m_className << "::LoadGrid:\n"
2033 << " Error reading file " << gridfilename
2034 << " (line " << iLine << ").\n"
2035 << " Edge index out of range.\n";
2036 Cleanup();
2037 gridfile.close();
2038 return false;
2039 }
2040 if (edge0 >= 0)
2041 m_elements[j].vertex[0] = edgeP1[edge0];
2042 else
2043 m_elements[j].vertex[0] = edgeP2[-edge0 - 1];
2044 if (edge1 >= 0)
2045 m_elements[j].vertex[1] = edgeP1[edge1];
2046 else
2047 m_elements[j].vertex[1] = edgeP2[-edge1 - 1];
2048 if (edge2 >= 0)
2049 m_elements[j].vertex[2] = edgeP1[edge2];
2050 else
2051 m_elements[j].vertex[2] = edgeP2[-edge2 - 1];
2052 if (edge3 >= 0)
2053 m_elements[j].vertex[3] = edgeP1[edge3];
2054 else
2055 m_elements[j].vertex[3] = edgeP2[-edge3 - 1];
2056
2057 // Rearrange vertices such that point 0 is on the left.
2058 while (m_vertices[m_elements[j].vertex[0]].x >
2059 m_vertices[m_elements[j].vertex[1]].x ||
2060 m_vertices[m_elements[j].vertex[0]].x >
2061 m_vertices[m_elements[j].vertex[2]].x ||
2062 m_vertices[m_elements[j].vertex[0]].x >
2063 m_vertices[m_elements[j].vertex[3]].x) {
2064 const int tmp = m_elements[j].vertex[0];
2065 m_elements[j].vertex[0] = m_elements[j].vertex[1];
2066 m_elements[j].vertex[1] = m_elements[j].vertex[2];
2067 m_elements[j].vertex[2] = m_elements[j].vertex[3];
2068 m_elements[j].vertex[3] = tmp;
2069 }
2070 break;
2071 default:
2072 // Other element types are not permitted for 2d grids.
2073 std::cerr << m_className << "::LoadGrid:\n"
2074 << " Error reading file " << gridfilename << " (line "
2075 << iLine << ").\n";
2076 std::cerr << " Invalid element type (" << type
2077 << ") for 2d mesh.\n";
2078 Cleanup();
2079 gridfile.close();
2080 return false;
2081 break;
2082 }
2083 m_elements[j].type = type;
2084 m_elements[j].region = -1;
2085 }
2086 break;
2087 }
2088 if (gridfile.eof()) {
2089 std::cerr << m_className << "::LoadGrid:\n";
2090 std::cerr << " Could not find section 'Elements' in file\n";
2091 std::cerr << " " << gridfilename << ".\n";
2092 Cleanup();
2093 gridfile.close();
2094 return false;
2095 } else if (gridfile.fail()) {
2096 std::cerr << m_className << "::LoadGrid:\n";
2097 std::cerr << " Error reading file " << gridfilename << " (line " << iLine
2098 << ").\n";
2099 Cleanup();
2100 gridfile.close();
2101 return false;
2102 }
2103
2104 // Assign regions to elements.
2105 std::string name;
2106 while (!gridfile.fail()) {
2107 std::string line;
2108 std::getline(gridfile, line);
2109 ltrim(line);
2110 // Find section 'Region'.
2111 if (line.substr(0, 6) != "Region") continue;
2112 // Get region name (given in brackets).
2113 std::string::size_type bra = line.find('(');
2114 std::string::size_type ket = line.find(')');
2115 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
2116 std::cerr << m_className << "::LoadGrid:\n"
2117 << " Could not read region name.\n";
2118 Cleanup();
2119 gridfile.close();
2120 return false;
2121 }
2122 line = line.substr(bra + 1, ket - bra - 1);
2123 std::istringstream data;
2124 data.str(line);
2125 data >> name;
2126 data.clear();
2127 const int index = FindRegion(name);
2128 if (index == -1) {
2129 // Specified region name is not in the list.
2130 std::cerr << m_className << "::LoadGrid:\n";
2131 std::cerr << " Error reading file " << gridfilename << ".\n";
2132 std::cerr << " Unknown region " << name << ".\n";
2133 continue;
2134 }
2135 std::getline(gridfile, line);
2136 std::getline(gridfile, line);
2137 bra = line.find('(');
2138 ket = line.find(')');
2139 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
2140 // No closed brackets ().
2141 std::cerr << m_className << "::LoadGrid:\n";
2142 std::cerr << " Error reading file " << gridfilename << ".\n";
2143 std::cerr << " Could not read number of elements in region " << name
2144 << ".\n";
2145 Cleanup();
2146 gridfile.close();
2147 return false;
2148 }
2149 line = line.substr(bra + 1, ket - bra - 1);
2150 int nElementsRegion;
2151 int iElement;
2152 data.str(line);
2153 data >> nElementsRegion;
2154 data.clear();
2155 for (int j = 0; j < nElementsRegion; ++j) {
2156 gridfile >> iElement;
2157 m_elements[iElement].region = index;
2158 }
2159 }
2160
2161 gridfile.close();
2162 if (gridfile.fail() && !gridfile.eof()) {
2163 std::cerr << m_className << "::LoadGrid:\n";
2164 std::cerr << " Error reading file " << gridfilename << ".\n";
2165 Cleanup();
2166 return false;
2167 }
2168
2169 return true;
2170}
2171
2172void ComponentTcad2d::FindNeighbours() {
2173
2174 const unsigned int nElements = m_elements.size();
2175 std::vector<std::vector<bool> > adjacent(nElements, std::vector<bool>(nElements, false));
2176
2177 const double tol = 5.e-4;
2178 for (unsigned int i = 0; i < nElements; ++i) {
2179 const Element& ei = m_elements[i];
2180 for (unsigned int j = 0; j < nElements; ++j) {
2181 if (i == j || adjacent[i][j]) continue;
2182 const Element& ej = m_elements[j];
2183 if (ei.xmin > ej.xmax + tol || ei.xmax < ej.xmin - tol) continue;
2184 if (ei.ymin > ej.ymax + tol || ei.ymax < ej.ymin - tol) continue;
2185 for (unsigned int m = 0; m < nMaxVertices; ++m) {
2186 if (ei.vertex[m] < 0) break;
2187 for (unsigned int n = 0; n < nMaxVertices; ++n) {
2188 if (ei.vertex[n] < 0) break;
2189 if (ei.vertex[m] == ej.vertex[n]) {
2190 adjacent[i][j] = adjacent[j][i] = true;
2191 break;
2192 }
2193 }
2194 if (adjacent[i][j]) break;
2195 }
2196 }
2197 }
2198
2199 for (unsigned int i = 0; i < nElements; ++i) {
2200 m_elements[i].neighbours.clear();
2201 for (unsigned int j = 0; j < nElements; ++j) {
2202 if (adjacent[i][j]) {
2203 m_elements[i].neighbours.push_back(j);
2204 }
2205 }
2206 }
2207}
2208
2209void ComponentTcad2d::Cleanup() {
2210
2211 // Vertices
2212 m_vertices.clear();
2213
2214 // Elements
2215 m_elements.clear();
2216
2217 // Regions
2218 m_regions.clear();
2219}
2220
2221bool ComponentTcad2d::CheckElement(const double x, const double y,
2222 const Element& element,
2223 double w[nMaxVertices]) const {
2224
2225 switch (element.type) {
2226 case 1:
2227 return CheckLine(x, y, element, w);
2228 break;
2229 case 2:
2230 return CheckTriangle(x, y, element, w);
2231 break;
2232 case 3:
2233 return CheckRectangle(x, y, element, w);
2234 break;
2235 default:
2236 std::cerr << m_className << "::CheckElement:\n"
2237 << " Unknown element type. Program bug!\n";
2238 break;
2239 }
2240 return false;
2241}
2242
2243bool ComponentTcad2d::CheckRectangle(const double x, const double y,
2244 const Element& element,
2245 double w[nMaxVertices]) const {
2246
2247 const Vertex& v0 = m_vertices[element.vertex[0]];
2248 const Vertex& v1 = m_vertices[element.vertex[1]];
2249 const Vertex& v3 = m_vertices[element.vertex[3]];
2250 if (y < v0.y || x > v3.x || y > v1.y) return false;
2251
2252 // Map (x, y) to local variables (u, v) in [-1, 1].
2253 const double u = (x - 0.5 * (v0.x + v3.x)) / (v3.x - v0.x);
2254 const double v = (y - 0.5 * (v0.y + v1.y)) / (v1.y - v0.y);
2255 // Compute weighting factors for each corner.
2256 w[0] = (0.5 - u) * (0.5 - v);
2257 w[1] = (0.5 - u) * (0.5 + v);
2258 w[2] = (0.5 + u) * (0.5 + v);
2259 w[3] = (0.5 + u) * (0.5 - v);
2260 return true;
2261}
2262
2263bool ComponentTcad2d::CheckTriangle(const double x, const double y,
2264 const Element& element,
2265 double w[nMaxVertices]) const {
2266
2267 const Vertex& v0 = m_vertices[element.vertex[0]];
2268 const Vertex& v1 = m_vertices[element.vertex[1]];
2269 const Vertex& v2 = m_vertices[element.vertex[2]];
2270 if (x > v1.x && x > v2.x) return false;
2271 if (y < v0.y && y < v1.y && y < v2.y) return false;
2272 if (y > v0.y && y > v1.y && y > v2.y) return false;
2273
2274 // Map (x, y) onto local variables (b, c) such that
2275 // P = A + b * (B - A) + c * (C - A)
2276 // A point P is inside the triangle ABC if b, c > 0 and b + c < 1;
2277 // b, c are also weighting factors for points B, C
2278 const double v1x = v1.x - v0.x;
2279 const double v1y = v1.y - v0.y;
2280 const double v2x = v2.x - v0.x;
2281 const double v2y = v2.y - v0.y;
2282
2283 w[1] = ((x - v0.x) * v2y - (y - v0.y) * v2x) / (v1x * v2y - v1y * v2x);
2284 if (w[1] < 0. || w[1] > 1.) return false;
2285 w[2] = ((v0.x - x) * v1y - (v0.y - y) * v1x) / (v1x * v2y - v1y * v2x);
2286 if (w[2] < 0. || w[1] + w[2] > 1.) return false;
2287
2288 // Weighting factor for point A
2289 w[0] = 1. - w[1] - w[2];
2290
2291 return true;
2292}
2293
2294bool ComponentTcad2d::CheckLine(const double x, const double y,
2295 const Element& element,
2296 double w[nMaxVertices]) const {
2297
2298 const Vertex& v0 = m_vertices[element.vertex[0]];
2299 const Vertex& v1 = m_vertices[element.vertex[1]];
2300 if (x > v1.x) return false;
2301 if (y < v0.y && y < v1.y) return false;
2302 if (y > v0.y && y > v1.y) return false;
2303 const double tx = (x - v0.x) / (v1.x - v0.x);
2304 if (tx < 0. || tx > 1.) return false;
2305 const double ty = (y - v0.y) / (v1.y - v0.y);
2306 if (ty < 0. || ty > 1.) return false;
2307 if (tx == ty) {
2308 // Compute weighting factors for endpoints A, B
2309 w[0] = tx;
2310 w[1] = 1. - w[0];
2311 return true;
2312 }
2313 return false;
2314}
2315
2316void ComponentTcad2d::Reset() {
2317
2318 Cleanup();
2319 m_hasRangeZ = false;
2320 m_ready = false;
2321}
2322
2323void ComponentTcad2d::UpdatePeriodicity() {
2324
2325 if (!m_ready) {
2326 std::cerr << m_className << "::UpdatePeriodicity:\n"
2327 << " Field map not available.\n";
2328 return;
2329 }
2330
2331 // Check for conflicts.
2333 std::cerr << m_className << "::UpdatePeriodicity:\n"
2334 << " Both simple and mirror periodicity\n"
2335 << " along x requested; reset.\n";
2337 }
2338
2340 std::cerr << m_className << "::UpdatePeriodicity:\n"
2341 << " Both simple and mirror periodicity\n"
2342 << " along y requested; reset.\n";
2344 }
2345
2347 std::cerr << m_className << "::UpdatePeriodicity:\n"
2348 << " Periodicity along z requested; reset.\n";
2350 }
2351
2353 std::cerr << m_className << "::UpdatePeriodicity:\n"
2354 << " Axial symmetry is not supported; reset.\n";
2356 }
2357
2359 std::cerr << m_className << "::UpdatePeriodicity:\n"
2360 << " Rotation symmetry is not supported; reset.\n";
2362 }
2363}
2364
2365int ComponentTcad2d::FindRegion(const std::string& name) const {
2366
2367 const unsigned int nRegions = m_regions.size();
2368 for (unsigned int j = 0; j < nRegions; ++j) {
2369 if (name == m_regions[j].name) return j;
2370 }
2371 return -1;
2372}
2373
2374void ComponentTcad2d::MapCoordinates(double& x, double& y,
2375 bool& xmirr, bool& ymirr) const {
2376
2377 // In case of periodicity, reduce to the cell volume.
2378 xmirr = false;
2379 const double cellsx = m_xMaxBB - m_xMinBB;
2380 if (m_xPeriodic) {
2381 x = m_xMinBB + fmod(x - m_xMinBB, cellsx);
2382 if (x < m_xMinBB) x += cellsx;
2383 } else if (m_xMirrorPeriodic) {
2384 double xNew = m_xMinBB + fmod(x - m_xMinBB, cellsx);
2385 if (xNew < m_xMinBB) xNew += cellsx;
2386 const int nx = int(floor(0.5 + (xNew - x) / cellsx));
2387 if (nx != 2 * (nx / 2)) {
2388 xNew = m_xMinBB + m_xMaxBB - xNew;
2389 xmirr = true;
2390 }
2391 x = xNew;
2392 }
2393 ymirr = false;
2394 const double cellsy = m_yMaxBB - m_yMinBB;
2395 if (m_yPeriodic) {
2396 y = m_yMinBB + fmod(y - m_yMinBB, cellsy);
2397 if (y < m_yMinBB) y += cellsy;
2398 } else if (m_yMirrorPeriodic) {
2399 double yNew = m_yMinBB + fmod(y - m_yMinBB, cellsy);
2400 if (yNew < m_yMinBB) yNew += cellsy;
2401 const int ny = int(floor(0.5 + (yNew - y) / cellsy));
2402 if (ny != 2 * (ny / 2)) {
2403 yNew = m_yMinBB + m_yMaxBB - yNew;
2404 ymirr = true;
2405 }
2406 y = yNew;
2407 }
2408}
2409
2410bool ComponentTcad2d::CheckTraps() const {
2411
2412 const unsigned int nDonors = m_donors.size();
2413 for (unsigned int i = 0; i < nDonors; ++i) {
2414 if (m_donors[i].xsece < 0. || m_donors[i].xsech < 0.) return false;
2415 if (m_donors[i].conc < 0.) return false;
2416 }
2417
2418 const unsigned int nAcceptors = m_acceptors.size();
2419 for (unsigned int i = 0; i < nAcceptors; ++i) {
2420 if (m_acceptors[i].xsece < 0. || m_acceptors[i].xsech < 0.) return false;
2421 if (m_acceptors[i].conc < 0.) return false;
2422 }
2423 return true;
2424}
2425
2426}
Abstract base class for components.
bool m_zMirrorPeriodic
Mirror periodicity in z.
bool m_yAxiallyPeriodic
Axial periodicity in y.
bool m_zRotationSymmetry
Rotation symmetry around z-axis.
bool m_yRotationSymmetry
Rotation symmetry around y-axis.
bool m_ready
Ready for use?
bool m_zAxiallyPeriodic
Axial periodicity in z.
bool m_xRotationSymmetry
Rotation symmetry around x-axis.
bool m_yPeriodic
Simple periodicity in y.
bool m_yMirrorPeriodic
Mirror periodicity in y.
bool m_xPeriodic
Simple periodicity in x.
bool m_zPeriodic
Simple periodicity in z.
std::string m_className
Class name.
bool m_xAxiallyPeriodic
Axial periodicity in x.
bool m_debug
Switch on/off debugging messages.
bool m_xMirrorPeriodic
Mirror periodicity in x.
bool GetVoltageRange(double &vmin, double &vmax)
Calculate the voltage range [V].
bool GetDonorOccupation(const double x, const double y, const double z, const unsigned int donorNumber, double &occupationFraction)
void GetRegion(const unsigned int i, std::string &name, bool &active) const
bool GetNode(const unsigned int i, double &x, double &y, double &v, double &ex, double &ey) const
bool SetDonor(const unsigned int donorNumber, const double eXsec, const double hxSec, const double concentration)
bool Initialise(const std::string &gridfilename, const std::string &datafilename)
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Get the bounding box coordinates.
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&m, int &status)
void SetRangeZ(const double zmin, const double zmax)
bool GetHoleLifetime(const double x, const double y, const double z, double &htau)
void ElectronVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz, Medium *&m, int &status)
Get the electron drift velocity.
bool ElectronAttachment(const double x, const double y, const double z, double &eta)
Get the electron attachment coefficient.
bool GetElectronLifetime(const double x, const double y, const double z, double &etau)
bool GetAcceptorOccupation(const double x, const double y, const double z, const unsigned int acceptorNumber, double &occupationFraction)
bool GetElement(const unsigned int i, double &vol, double &dmin, double &dmax, int &type) const
void UnsetDriftRegion(const unsigned int ireg)
void SetMedium(const unsigned int ireg, Medium *m)
void HoleVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz, Medium *&m, int &status)
Get the hole drift velocity.
bool HoleAttachment(const double x, const double y, const double z, double &eta)
Get the hole attachment coefficient.
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
void SetDriftRegion(const unsigned int ireg)
Medium * GetMedium(const double x, const double y, const double z)
Get the medium at a given location (x, y, z).
bool GetMobility(const double x, const double y, const double z, double &emob, double &hmob)
bool SetAcceptor(const unsigned int acceptorNumber, const double eXsec, const double hxSec, const double concentration)
Abstract base class for media.
Definition: Medium.hh:11