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