14bool ExtractFromSquareBrackets(std::string& line) {
16 const auto bra = line.find(
'[');
17 const auto ket = line.find(
']');
18 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
21 line = line.substr(bra + 1, ket - bra - 1);
25bool ExtractFromBrackets(std::string& line) {
27 const auto bra = line.find(
'(');
28 const auto ket = line.find(
')');
29 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
32 line = line.substr(bra + 1, ket - bra - 1);
36void PrintError(
const std::string& fcn,
const std::string& filename,
37 const unsigned int line) {
38 std::cerr << fcn <<
":\n"
39 <<
" Error reading file " << filename
40 <<
" (line " << line <<
").\n";
49 const double xin,
const double yin,
const double zin,
50 double& wx,
double& wy,
double& wz,
const std::string& label) {
52 if (m_wfield.empty()) {
53 std::cerr << m_className <<
"::WeightingField: Not available.\n";
56 const size_t n = m_wlabel.size();
57 for (
size_t i = 0; i < n; ++i) {
58 if (label == m_wlabel[i]) {
59 const double x = xin - m_wshift[i][0];
60 const double y = yin - m_wshift[i][1];
61 const double z = zin - m_wshift[i][2];
62 Interpolate(x, y, z, m_wfield, wx, wy, wz);
70 const double xin,
const double yin,
const double zin,
71 const std::string& label) {
74 std::cerr << m_className <<
"::WeightingPotential: Not available.\n";
77 const size_t n = m_wlabel.size();
78 for (
size_t i = 0; i < n; ++i) {
79 if (label == m_wlabel[i]) {
80 const double x = xin - m_wshift[i][0];
81 const double y = yin - m_wshift[i][1];
82 const double z = zin - m_wshift[i][2];
84 Interpolate(x, y, z, m_wpot, v);
93 const std::string& datafilename) {
98 if (!LoadGrid(gridfilename)) {
99 std::cerr << m_className <<
"::Initialise:\n"
100 <<
" Importing mesh data failed.\n";
105 if (!LoadData(datafilename)) {
106 std::cerr << m_className <<
"::Initialise:\n"
107 <<
" Importing electric field and potential failed.\n";
113 for (
size_t i = 0; i < N; ++i) {
114 m_bbMax[i] = m_vertices[m_elements[0].vertex[0]][i];
115 m_bbMin[i] = m_bbMax[i];
117 const size_t nElements = m_elements.size();
118 for (
size_t i = 0; i < nElements; ++i) {
119 Element& element = m_elements[i];
120 std::array<double, N> xmin = m_vertices[element.vertex[0]];
121 std::array<double, N> xmax = m_vertices[element.vertex[0]];
122 const auto nV = ElementVertices(m_elements[i]);
123 for (
unsigned int j = 0; j < nV; ++j) {
124 const auto& v = m_vertices[m_elements[i].vertex[j]];
125 for (
size_t k = 0; k < N; ++k) {
126 xmin[k] = std::min(xmin[k], v[k]);
127 xmax[k] = std::max(xmax[k], v[k]);
130 constexpr double tol = 1.e-6;
131 for (
size_t k = 0; k < N; ++k) {
132 m_elements[i].bbMin[k] = xmin[k] - tol;
133 m_elements[i].bbMax[k] = xmax[k] + tol;
134 m_bbMin[k] = std::min(m_bbMin[k], xmin[k]);
135 m_bbMax[k] = std::max(m_bbMax[k], xmax[k]);
138 m_pMin = *std::min_element(m_epot.begin(), m_epot.end());
139 m_pMax = *std::max_element(m_epot.begin(), m_epot.end());
141 std::cout << m_className <<
"::Initialise:\n"
142 <<
" Available data:\n";
143 if (!m_epot.empty()) std::cout <<
" Electrostatic potential\n";
144 if (!m_efield.empty()) std::cout <<
" Electric field\n";
145 if (!m_eMobility.empty()) std::cout <<
" Electron mobility\n";
146 if (!m_hMobility.empty()) std::cout <<
" Hole mobility\n";
147 if (!m_eVelocity.empty()) std::cout <<
" Electron velocity\n";
148 if (!m_hVelocity.empty()) std::cout <<
" Hole velocity\n";
149 if (!m_eLifetime.empty()) std::cout <<
" Electron lifetime\n";
150 if (!m_hLifetime.empty()) std::cout <<
" Hole lifetime\n";
151 if (!m_donors.empty()) {
152 std::cout <<
" " << m_donors.size() <<
" donor-type traps\n";
154 if (!m_acceptors.empty()) {
155 std::cout <<
" " << m_acceptors.size() <<
" acceptor-type traps\n";
157 const std::array<std::string, 3> axes = {
"x",
"y",
"z"};
158 std::cout <<
" Bounding box:\n";
159 for (
size_t i = 0; i < N; ++i) {
160 std::cout <<
" " << m_bbMin[i] <<
" < " << axes[i] <<
" [cm] < "
161 << m_bbMax[i] <<
"\n";
163 std::cout <<
" Voltage range:\n"
164 <<
" " << m_pMin <<
" < V < " << m_pMax <<
"\n";
169 const auto nRegions = m_regions.size();
170 std::vector<size_t> nElementsByRegion(nRegions, 0);
172 std::vector<size_t> looseElements;
175 std::map<int, unsigned int> nElementsByShape;
177 nElementsByShape = {{0, 0}, {1, 0}, {2, 0}, {3, 0}};
179 nElementsByShape = {{2, 0}, {5, 0}};
181 unsigned int nElementsOther = 0;
184 std::vector<size_t> degenerateElements;
186 for (
size_t i = 0; i < nElements; ++i) {
187 const Element& element = m_elements[i];
188 if (element.region < nRegions) {
189 ++nElementsByRegion[element.region];
191 looseElements.push_back(i);
193 if (nElementsByShape.count(element.type) == 0) {
197 nElementsByShape[element.type] += 1;
198 bool degenerate =
false;
199 const auto nV = ElementVertices(m_elements[i]);
200 for (
unsigned int j = 0; j < nV; ++j) {
201 for (
unsigned int k = j + 1; k < nV; ++k) {
202 if (element.vertex[j] == element.vertex[k]) {
207 if (degenerate)
break;
210 degenerateElements.push_back(i);
214 if (!degenerateElements.empty()) {
215 std::cerr << m_className <<
"::Initialise:\n"
216 <<
" The following elements are degenerate:\n";
217 for (
size_t i : degenerateElements) std::cerr <<
" " << i <<
"\n";
221 if (!looseElements.empty()) {
222 std::cerr << m_className <<
"::Initialise:\n"
223 <<
" The following elements are not part of any region:\n";
224 for (
size_t i : looseElements) std::cerr <<
" " << i <<
"\n";
228 std::cout << m_className <<
"::Initialise:\n"
229 <<
" Number of regions: " << nRegions <<
"\n";
230 for (
size_t i = 0; i < nRegions; ++i) {
231 std::cout <<
" " << i <<
": " << m_regions[i].name <<
", "
232 << nElementsByRegion[i] <<
" elements\n";
235 std::map<int, std::string> shapes = {
236 {0,
"points"}, {1,
"lines"}, {2,
"triangles"}, {3,
"rectangles"},
239 std::cout <<
" Number of elements: " << nElements <<
"\n";
240 for (
const auto& n : nElementsByShape) {
242 std::cout <<
" " << n.second <<
" " << shapes[n.first] <<
"\n";
245 if (nElementsOther > 0) {
246 std::cerr <<
" " << nElementsOther <<
" elements of unknown type.\n"
247 <<
" Program bug!\n";
253 std::cout <<
" Number of vertices: " << m_vertices.size() <<
"\n";
264 std::cout << m_className <<
"::Initialise: Initialisation finished.\n";
270 if (!m_ready)
return false;
278 const std::string& datfile2,
280 const std::string& label) {
283 std::cerr << m_className <<
"::SetWeightingField:\n"
284 <<
" Mesh is not available. Call Initialise first.\n";
288 std::cerr << m_className <<
"::SetWeightingField:\n"
289 <<
" Voltage difference must be > 0.\n";
292 const double s = 1. / dv;
299 std::vector<std::array<double, N> > wf1;
300 std::vector<double> wp1;
301 if (!LoadWeightingField(datfile1, wf1, wp1)) {
302 std::cerr << m_className <<
"::SetWeightingField:\n"
303 <<
" Could not import data from " << datfile1 <<
".\n";
309 std::vector<std::array<double, N> > wf2;
310 std::vector<double> wp2;
311 if (!LoadWeightingField(datfile2, wf2, wp2)) {
312 std::cerr << m_className <<
"::SetWeightingField:\n"
313 <<
" Could not import data from " << datfile2 <<
".\n";
316 const size_t nVertices = m_vertices.size();
317 bool foundField =
true;
318 if (wf1.size() != nVertices || wf2.size() != nVertices) {
320 std::cerr << m_className <<
"::SetWeightingField:\n"
321 <<
" Could not load electric field values.\n";
323 bool foundPotential =
true;
324 if (wp1.size() != nVertices || wp2.size() != nVertices) {
325 foundPotential =
false;
326 std::cerr << m_className <<
"::SetWeightingField:\n"
327 <<
" Could not load electrostatic potentials.\n";
329 if (!foundField && !foundPotential)
return false;
331 m_wfield.resize(nVertices);
332 for (
size_t i = 0; i < nVertices; ++i) {
333 for (
size_t j = 0; j < N; ++j) {
334 m_wfield[i][j] = (wf2[i][j] - wf1[i][j]) * s;
338 if (foundPotential) {
339 m_wpot.assign(nVertices, 0.);
340 for (
size_t i = 0; i < nVertices; ++i) {
341 m_wpot[i] = (wp2[i] - wp1[i]) * s;
344 m_wlabel.push_back(label);
345 m_wshift.push_back({0., 0., 0.});
351 const std::string& label,
const double x,
const double y,
const double z) {
352 if (m_wlabel.empty()) {
353 std::cerr << m_className <<
"::SetWeightingFieldShift:\n"
354 <<
" No map of weighting potentials/fields loaded.\n";
357 const size_t n = m_wlabel.size();
358 for (
size_t i = 0; i < n; ++i) {
359 if (m_wlabel[i] == label) {
360 m_wshift[i] = {x, y, z};
361 std::cout << m_className <<
"::SetWeightingFieldShift:\n"
362 <<
" Changing offset of electrode \'" << label
363 <<
"\' to (" << x <<
", " << y <<
", " << z <<
") cm.\n";
367 m_wlabel.push_back(label);
368 m_wshift.push_back({x, y, z});
369 std::cout << m_className <<
"::SetWeightingFieldShift:\n"
370 <<
" Adding electrode \'" << label <<
"\' with offset ("
371 << x <<
", " << y <<
", " << z <<
") cm.\n";
377 m_useVelocityMap = on;
378 if (m_ready && (m_eVelocity.empty() && m_hVelocity.empty())) {
379 std::cout << m_className <<
"::EnableVelocityMap:\n"
380 <<
" Warning: current map does not include velocity data.\n";
387 std::ifstream gridfile;
388 gridfile.open(filename, std::ios::in);
390 std::cerr << m_className <<
"::LoadGrid:\n"
391 <<
" Could not open file " << filename <<
".\n";
397 unsigned int iLine = 0;
402 while (std::getline(gridfile, line)) {
407 if (line.substr(0, 10) !=
"nb_regions")
continue;
408 const auto pEq = line.find(
'=');
409 if (pEq == std::string::npos) {
411 std::cerr << m_className <<
"::LoadGrid:\n"
412 <<
" Could not read number of regions.\n";
415 line = line.substr(pEq + 1);
416 std::istringstream data;
421 if (gridfile.eof()) {
423 std::cerr << m_className <<
"::LoadGrid:\n"
424 <<
" Could not find entry 'nb_regions' in file\n"
425 <<
" " << filename <<
".\n";
427 }
else if (gridfile.fail()) {
429 PrintError(m_className +
"::LoadGrid", filename, iLine);
432 m_regions.resize(nRegions);
433 for (
size_t j = 0; j < nRegions; ++j) {
434 m_regions[j].name =
"";
435 m_regions[j].drift =
false;
436 m_regions[j].medium =
nullptr;
439 std::cout << m_className <<
"::LoadGrid:\n"
440 <<
" Found " << nRegions <<
" regions.\n";
443 while (std::getline(gridfile, line)) {
447 if (line.substr(0, 7) !=
"regions")
continue;
449 if (!ExtractFromSquareBrackets(line)) {
450 std::cerr << m_className <<
"::LoadGrid:\n"
451 <<
" Could not read region names.\n";
454 std::istringstream data;
456 for (
size_t j = 0; j < nRegions; ++j) {
457 data >> m_regions[j].name;
460 m_regions[j].drift =
true;
461 m_regions[j].medium =
nullptr;
465 if (gridfile.eof()) {
467 std::cerr << m_className <<
"::LoadGrid:\n"
468 <<
" Could not find entry 'regions' in file\n"
469 <<
" " << filename <<
".\n";
471 }
else if (gridfile.fail()) {
473 PrintError(m_className +
"::LoadGrid", filename, iLine);
478 size_t nVertices = 0;
479 while (std::getline(gridfile, line)) {
483 if (line.substr(0, 8) !=
"Vertices")
continue;
485 if (!ExtractFromBrackets(line)) {
486 std::cerr << m_className <<
"::LoadGrid:\n"
487 <<
" Could not read number of vertices.\n";
490 std::istringstream data;
493 m_vertices.resize(nVertices);
495 for (
size_t j = 0; j < nVertices; ++j) {
496 for (
size_t k = 0; k < N; ++k) {
497 gridfile >> m_vertices[j][k];
499 m_vertices[j][k] *= 1.e-4;
505 if (gridfile.eof()) {
506 std::cerr << m_className <<
"::LoadGrid:\n"
507 <<
" Could not find section 'Vertices' in file\n"
508 <<
" " << filename <<
".\n";
510 }
else if (gridfile.fail()) {
511 PrintError(m_className +
"::LoadGrid", filename, iLine);
518 std::vector<unsigned > edgeP1;
519 std::vector<unsigned > edgeP2;
520 while (std::getline(gridfile, line)) {
524 if (line.substr(0, 5) !=
"Edges")
continue;
526 if (!ExtractFromBrackets(line)) {
527 std::cerr << m_className <<
"::LoadGrid:\n"
528 <<
" Could not read number of edges.\n";
531 std::istringstream data;
534 edgeP1.resize(nEdges);
535 edgeP2.resize(nEdges);
537 for (
size_t j = 0; j < nEdges; ++j) {
538 gridfile >> edgeP1[j] >> edgeP2[j];
543 if (gridfile.eof()) {
544 std::cerr << m_className <<
"::LoadGrid:\n"
545 <<
" Could not find section 'Edges' in file\n"
546 <<
" " << filename <<
".\n";
548 }
else if (gridfile.fail()) {
549 PrintError(m_className +
"::LoadGrid", filename, iLine);
553 for (
size_t i = 0; i < nEdges; ++i) {
555 if (edgeP1[i] >= nVertices || edgeP2[i] >= nVertices) {
556 std::cerr << m_className <<
"::LoadGrid:\n"
557 <<
" Vertex index of edge " << i <<
" out of range.\n";
561 if (edgeP1[i] == edgeP2[i]) {
562 std::cerr << m_className <<
"::LoadGrid:\n"
563 <<
" Edge " << i <<
" is degenerate.\n";
575 std::vector<Face> faces;
577 while (std::getline(gridfile, line)) {
581 if (line.substr(0, 5) !=
"Faces")
continue;
583 if (!ExtractFromBrackets(line)) {
584 std::cerr << m_className <<
"::LoadGrid:\n"
585 <<
" Could not read number of faces.\n";
588 std::istringstream data;
591 faces.resize(nFaces);
593 for (
size_t j = 0; j < nFaces; ++j) {
594 gridfile >> faces[j].type;
595 if (faces[j].type != 3 && faces[j].type != 4) {
596 std::cerr << m_className <<
"::LoadGrid:\n"
597 <<
" Face with index " << j
598 <<
" has invalid number of edges, " << faces[j].type <<
".\n";
601 for (
int k = 0; k < faces[j].type; ++k) {
602 gridfile >> faces[j].edge[k];
608 if (gridfile.eof()) {
609 std::cerr << m_className <<
"::LoadGrid:\n"
610 <<
" Could not find section 'Faces' in file\n"
611 <<
" " << filename <<
".\n";
613 }
else if (gridfile.fail()) {
614 PrintError(m_className +
"::LoadGrid", filename, iLine);
620 size_t nElements = 0;
621 while (std::getline(gridfile, line)) {
625 if (line.substr(0, 8) !=
"Elements")
continue;
627 if (!ExtractFromBrackets(line)) {
628 std::cerr << m_className <<
"::LoadGrid:\n"
629 <<
" Could not read number of elements.\n";
632 std::istringstream data;
637 m_elements.resize(nElements);
639 for (
size_t j = 0; j < nElements; ++j) {
641 unsigned int type = 0;
649 if (p >= nVertices) {
650 PrintError(m_className +
"::LoadGrid", filename, iLine);
651 std::cerr <<
" Vertex index out of range.\n";
654 m_elements[j].vertex[0] = p;
655 }
else if (type == 1) {
657 for (
size_t k = 0; k < 2; ++k) {
660 if (p < 0) p = -p - 1;
662 if (p >= (
int)nVertices) {
663 PrintError(m_className +
"::LoadGrid", filename, iLine);
664 std::cerr <<
" Vertex index out of range.\n";
667 m_elements[j].vertex[k] = p;
669 }
else if (type == 2) {
671 int p0 = 0, p1 = 0, p2 = 0;
672 gridfile >> p0 >> p1 >> p2;
676 if (p0 < 0) p0 = -p0 - 1;
677 if (p1 < 0) p1 = -p1 - 1;
678 if (p2 < 0) p2 = -p2 - 1;
680 if (p0 >= (
int)nEdges || p1 >= (
int)nEdges || p2 >= (
int)nEdges) {
681 PrintError(m_className +
"::LoadGrid", filename, iLine);
682 std::cerr <<
" Edge index out of range.\n";
685 m_elements[j].vertex[0] = edgeP1[p0];
686 m_elements[j].vertex[1] = edgeP2[p0];
687 if (edgeP1[p1] != m_elements[j].vertex[0] &&
688 edgeP1[p1] != m_elements[j].vertex[1]) {
689 m_elements[j].vertex[2] = edgeP1[p1];
691 m_elements[j].vertex[2] = edgeP2[p1];
694 while (m_vertices[m_elements[j].vertex[0]][0] >
695 m_vertices[m_elements[j].vertex[1]][0] ||
696 m_vertices[m_elements[j].vertex[0]][0] >
697 m_vertices[m_elements[j].vertex[2]][0]) {
698 const int tmp = m_elements[j].vertex[0];
699 m_elements[j].vertex[0] = m_elements[j].vertex[1];
700 m_elements[j].vertex[1] = m_elements[j].vertex[2];
701 m_elements[j].vertex[2] = tmp;
703 }
else if (type == 3) {
705 for (
size_t k = 0; k < 4; ++k) {
709 if (p >= (
int)nEdges || -p - 1 >= (
int)nEdges) {
710 PrintError(m_className +
"::LoadGrid", filename, iLine);
711 std::cerr <<
" Edge index out of range.\n";
715 m_elements[j].vertex[k] = edgeP1[p];
717 m_elements[j].vertex[k] = edgeP2[-p - 1];
721 while (m_vertices[m_elements[j].vertex[0]][0] >
722 m_vertices[m_elements[j].vertex[1]][0] ||
723 m_vertices[m_elements[j].vertex[0]][0] >
724 m_vertices[m_elements[j].vertex[2]][0] ||
725 m_vertices[m_elements[j].vertex[0]][0] >
726 m_vertices[m_elements[j].vertex[3]][0]) {
727 const int tmp = m_elements[j].vertex[0];
728 m_elements[j].vertex[0] = m_elements[j].vertex[1];
729 m_elements[j].vertex[1] = m_elements[j].vertex[2];
730 m_elements[j].vertex[2] = m_elements[j].vertex[3];
731 m_elements[j].vertex[3] = tmp;
735 PrintError(m_className +
"::LoadGrid", filename, iLine);
736 std::cerr <<
" Invalid element type (" << type
737 <<
") for 2d mesh.\n";
743 int edge0, edge1, edge2;
744 gridfile >> edge0 >> edge1 >> edge2;
750 if (edge0 < 0) edge0 = -edge0 - 1;
751 if (edge1 < 0) edge1 = -edge1 - 1;
752 if (edge2 < 0) edge2 = -edge2 - 1;
754 if (edge0 >= (
int)nEdges || edge1 >= (
int)nEdges ||
755 edge2 >= (
int)nEdges) {
756 PrintError(m_className +
"::LoadGrid", filename, iLine);
757 std::cerr <<
" Edge index out of range.\n";
760 m_elements[j].vertex[0] = edgeP1[edge0];
761 m_elements[j].vertex[1] = edgeP2[edge0];
762 if (edgeP1[edge1] != m_elements[j].vertex[0] &&
763 edgeP1[edge1] != m_elements[j].vertex[1]) {
764 m_elements[j].vertex[2] = edgeP1[edge1];
766 m_elements[j].vertex[2] = edgeP2[edge1];
768 }
else if (type == 5) {
774 int face0, face1, face2, face3;
775 gridfile >> face0 >> face1 >> face2 >> face3;
776 if (face0 < 0) face0 = -face0 - 1;
777 if (face1 < 0) face1 = -face1 - 1;
778 if (face2 < 0) face2 = -face2 - 1;
779 if (face3 < 0) face3 = -face3 - 1;
781 if (face0 >= (
int)nFaces || face1 >= (
int)nFaces ||
782 face2 >= (
int)nFaces || face3 >= (
int)nFaces) {
783 PrintError(m_className +
"::LoadGrid", filename, iLine);
784 std::cerr <<
" Face index out of range.\n";
788 int edge0 = faces[face0].edge[0];
789 int edge1 = faces[face0].edge[1];
790 int edge2 = faces[face0].edge[2];
791 if (edge0 < 0) edge0 = -edge0 - 1;
792 if (edge1 < 0) edge1 = -edge1 - 1;
793 if (edge2 < 0) edge2 = -edge2 - 1;
795 if (edge0 >= (
int)nEdges || edge1 >= (
int)nEdges ||
796 edge2 >= (
int)nEdges) {
797 PrintError(m_className +
"::LoadGrid", filename, iLine);
798 std::cerr <<
" Edge index out of range.\n";
802 m_elements[j].vertex[0] = edgeP1[edge0];
803 m_elements[j].vertex[1] = edgeP2[edge0];
804 if (edgeP1[edge1] != m_elements[j].vertex[0] &&
805 edgeP1[edge1] != m_elements[j].vertex[1]) {
806 m_elements[j].vertex[2] = edgeP1[edge1];
808 m_elements[j].vertex[2] = edgeP2[edge1];
811 edge0 = faces[face1].edge[0];
812 edge1 = faces[face1].edge[1];
813 edge2 = faces[face1].edge[2];
814 if (edge0 < 0) edge0 = -edge0 - 1;
815 if (edge1 < 0) edge1 = -edge1 - 1;
816 if (edge2 < 0) edge2 = -edge2 - 1;
817 const auto v0 = m_elements[j].vertex[0];
818 const auto v1 = m_elements[j].vertex[1];
819 const auto v2 = m_elements[j].vertex[2];
820 if (edgeP1[edge0] != v0 && edgeP1[edge0] != v1 && edgeP1[edge0] != v2) {
821 m_elements[j].vertex[3] = edgeP1[edge0];
822 }
else if (edgeP2[edge0] != v0 && edgeP2[edge0] != v1 &&
823 edgeP2[edge0] != v2) {
824 m_elements[j].vertex[3] = edgeP2[edge0];
825 }
else if (edgeP1[edge1] != v0 &&
826 edgeP1[edge1] != v1 &&
827 edgeP1[edge1] != v2) {
828 m_elements[j].vertex[3] = edgeP1[edge1];
829 }
else if (edgeP2[edge1] != v0 &&
830 edgeP2[edge1] != v1 &&
831 edgeP2[edge1] != v2) {
832 m_elements[j].vertex[3] = edgeP2[edge1];
834 PrintError(m_className +
"::LoadGrid", filename, iLine);
835 std::cerr <<
" Face 1 of element " << j <<
" is degenerate.\n";
840 PrintError(m_className +
"::LoadGrid", filename, iLine);
841 if (type == 0 || type == 1) {
842 std::cerr <<
" Invalid element type (" << type
843 <<
") for 3d mesh.\n";
845 std::cerr <<
" Element type " << type <<
" is not supported.\n"
846 <<
" Remesh with option -t to create only"
847 <<
" triangles and tetrahedra.\n";
852 m_elements[j].type = type;
853 m_elements[j].region = m_regions.size();
857 if (gridfile.eof()) {
858 std::cerr << m_className <<
"::LoadGrid:\n"
859 <<
" Could not find section 'Elements' in file\n"
860 <<
" " << filename <<
".\n";
862 }
else if (gridfile.fail()) {
863 PrintError(m_className +
"::LoadGrid", filename, iLine);
868 while (std::getline(gridfile, line)) {
871 if (line.substr(0, 6) !=
"Region")
continue;
873 if (!ExtractFromBrackets(line)) {
874 std::cerr << m_className <<
"::LoadGrid:\n"
875 <<
" Could not read region name.\n";
878 std::istringstream data;
883 const size_t index = FindRegion(name);
884 if (index >= m_regions.size()) {
886 std::cerr << m_className <<
"::LoadGrid:\n"
887 <<
" Unknown region " << name <<
".\n";
890 std::getline(gridfile, line);
891 std::getline(gridfile, line);
892 if (!ExtractFromBrackets(line)) {
893 std::cerr << m_className <<
"::LoadGrid:\n"
894 <<
" Could not read number of elements in region "
901 data >> nElementsRegion;
903 for (
int j = 0; j < nElementsRegion; ++j) {
904 gridfile >> iElement;
905 m_elements[iElement].region = index;
908 if (gridfile.fail() && !gridfile.eof()) {
909 std::cerr << m_className <<
"::LoadGrid:\n"
910 <<
" Error reading file " << filename <<
".\n";
919 std::ifstream datafile;
920 datafile.open(filename, std::ios::in);
922 std::cerr << m_className <<
"::LoadData:\n"
923 <<
" Could not open file " << filename <<
".\n";
926 const size_t nVertices = m_vertices.size();
927 std::vector<unsigned int> fillCount(nVertices, 0);
929 std::array<double, N> zeroVector{};
932 while (std::getline(datafile, line)) {
936 if (line.substr(0, 8) !=
"function")
continue;
938 const auto pEq = line.find(
'=');
939 if (pEq == std::string::npos) {
941 std::cerr << m_className <<
"::LoadData:\n"
942 <<
" Error reading file " << filename <<
".\n"
943 <<
" Line:\n " << line <<
"\n";
946 line = line.substr(pEq + 1);
948 std::istringstream data;
952 if (dataset ==
"ElectrostaticPotential") {
953 m_epot.assign(nVertices, 0.);
954 if (!ReadDataset(datafile, dataset)) {
958 }
else if (dataset ==
"ElectricField") {
959 m_efield.assign(nVertices, zeroVector);
960 if (!ReadDataset(datafile, dataset)) {
964 }
else if (dataset ==
"eDriftVelocity") {
965 m_eVelocity.assign(nVertices, zeroVector);
966 if (!ReadDataset(datafile, dataset)) {
970 }
else if (dataset ==
"hDriftVelocity") {
971 m_hVelocity.assign(nVertices, zeroVector);
972 if (!ReadDataset(datafile, dataset)) {
976 }
else if (dataset ==
"eMobility") {
977 m_eMobility.assign(nVertices, 0.);
978 if (!ReadDataset(datafile, dataset)) {
982 }
else if (dataset ==
"hMobility") {
983 m_hMobility.assign(nVertices, 0.);
984 if (!ReadDataset(datafile, dataset)) {
988 }
else if (dataset ==
"eLifetime") {
989 m_eLifetime.assign(nVertices, 0.);
990 if (!ReadDataset(datafile, dataset)) {
994 }
else if (dataset ==
"hLifetime") {
995 m_hLifetime.assign(nVertices, 0.);
996 if (!ReadDataset(datafile, dataset)) {
1000 }
else if (dataset.substr(0, 14) ==
"TrapOccupation" &&
1001 dataset.substr(17, 2) ==
"Do") {
1002 if (!ReadDataset(datafile, dataset))
return false;
1007 m_donors.push_back(donor);
1008 }
else if (dataset.substr(0, 14) ==
"TrapOccupation" &&
1009 dataset.substr(17, 2) ==
"Ac") {
1010 if (!ReadDataset(datafile, dataset))
return false;
1012 acceptor.
xsece = -1.;
1013 acceptor.
xsech = -1.;
1014 acceptor.
conc = -1.;
1015 m_acceptors.push_back(acceptor);
1018 if (datafile.fail() && !datafile.eof()) {
1019 std::cerr << m_className <<
"::LoadData:\n"
1020 <<
" Error reading file " << filename <<
"\n";
1028 const std::string& dataset) {
1030 if (!datafile.is_open())
return false;
1032 ElectrostaticPotential,
1040 DonorTrapOccupation,
1041 AcceptorTrapOccupation,
1044 DataSet ds = Unknown;
1045 if (dataset ==
"ElectrostaticPotential") {
1046 ds = ElectrostaticPotential;
1047 }
else if (dataset ==
"ElectricField") {
1049 }
else if (dataset ==
"eDriftVelocity") {
1050 ds = eDriftVelocity;
1051 }
else if (dataset ==
"hDriftVelocity") {
1052 ds = hDriftVelocity;
1053 }
else if (dataset ==
"eMobility") {
1055 }
else if (dataset ==
"hMobility") {
1057 }
else if (dataset ==
"eLifetime") {
1059 }
else if (dataset ==
"hLifetime") {
1061 }
else if (dataset.substr(0, 14) ==
"TrapOccupation") {
1062 if (dataset.substr(17, 2) ==
"Do") {
1063 ds = DonorTrapOccupation;
1064 }
else if (dataset.substr(17, 2) ==
"Ac") {
1065 ds = AcceptorTrapOccupation;
1068 std::cerr << m_className <<
"::ReadDataset:\n"
1069 <<
" Unexpected dataset " << dataset <<
".\n";
1072 bool isVector =
false;
1073 if (ds == EField || ds == eDriftVelocity || ds == hDriftVelocity) {
1078 std::getline(datafile, line);
1079 std::getline(datafile, line);
1080 std::getline(datafile, line);
1081 std::getline(datafile, line);
1083 if (!ExtractFromSquareBrackets(line)) {
1084 std::cerr << m_className <<
"::ReadDataset:\n"
1085 <<
" Cannot extract region name.\n"
1086 <<
" Line:\n " << line <<
"\n";
1090 std::istringstream data;
1095 const size_t index = FindRegion(name);
1096 if (index >= m_regions.size()) {
1097 std::cerr << m_className <<
"::ReadDataset:\n"
1098 <<
" Unknown region " << name <<
".\n";
1102 std::getline(datafile, line);
1103 if (!ExtractFromBrackets(line)) {
1104 std::cerr << m_className <<
"::ReadDataset:\n"
1105 <<
" Cannot extract number of values to be read.\n"
1106 <<
" Line:\n " << line <<
"\n";
1112 if (isVector) nValues /= N;
1114 const size_t nVertices = m_vertices.size();
1115 std::vector<bool> isInRegion(nVertices,
false);
1116 const size_t nElements = m_elements.size();
1117 for (
size_t j = 0; j < nElements; ++j) {
1118 if (m_elements[j].region != index)
continue;
1119 const unsigned int nV = ElementVertices(m_elements[j]);
1120 for (
unsigned int k = 0; k < nV; ++k) {
1121 isInRegion[m_elements[j].vertex[k]] =
true;
1125 unsigned int ivertex = 0;
1126 for (
int j = 0; j < nValues; ++j) {
1128 std::array<double, N> val;
1130 for (
size_t k = 0; k < N; ++k) datafile >> val[k];
1135 while (ivertex < nVertices) {
1136 if (isInRegion[ivertex])
break;
1141 if (ivertex >= nVertices) {
1142 std::cerr << m_className <<
"::ReadDataset:\n"
1143 <<
" Dataset " << dataset <<
" has more values than "
1144 <<
"there are vertices in region " << name <<
"\n";
1149 case ElectrostaticPotential:
1150 m_epot[ivertex] = val[0];
1153 for (
size_t k = 0; k < N; ++k) m_efield[ivertex][k] = val[k];
1155 case eDriftVelocity:
1157 for (
size_t k = 0; k < N; ++k) {
1158 m_eVelocity[ivertex][k] = val[k] * 1.e-9;
1161 case hDriftVelocity:
1163 for (
size_t k = 0; k < N; ++k) {
1164 m_hVelocity[ivertex][k] = val[k] * 1.e-9;
1169 m_eMobility[ivertex] = val[0] * 1.e-9;
1173 m_hMobility[ivertex] = val[0] * 1.e-9;
1177 m_eLifetime[ivertex] = val[0] * 1.e9;
1181 m_hLifetime[ivertex] = val[0] * 1.e9;
1183 case DonorTrapOccupation:
1184 m_donorOcc[ivertex].push_back(val[0]);
1186 case AcceptorTrapOccupation:
1187 m_acceptorOcc[ivertex].push_back(val[0]);
1190 std::cerr << m_className <<
"::ReadDataset:\n"
1191 <<
" Unexpected dataset (" << ds <<
"). Program bug!\n";
1201 const std::string& filename,
1202 std::vector<std::array<double, N> >& wf, std::vector<double>& wp) {
1204 std::ifstream datafile;
1205 datafile.open(filename.c_str(), std::ios::in);
1207 std::cerr << m_className <<
"::LoadWeightingField:\n"
1208 <<
" Could not open file " << filename <<
".\n";
1211 const size_t nVertices = m_vertices.size();
1212 const std::array<double, N> zeroVector{};
1216 while (std::getline(datafile, line)) {
1220 if (line.substr(0, 8) !=
"function")
continue;
1222 const auto pEq = line.find(
'=');
1223 if (pEq == std::string::npos) {
1225 std::cerr << m_className <<
"::LoadWeightingField:\n"
1226 <<
" Error reading file " << filename <<
".\n"
1227 <<
" Line:\n " << line <<
"\n";
1230 line = line.substr(pEq + 1);
1231 std::string dataset;
1232 std::istringstream data;
1236 if (dataset !=
"ElectrostaticPotential" && dataset !=
"ElectricField") {
1240 if (dataset ==
"ElectricField") {
1241 wf.assign(nVertices, zeroVector);
1244 wp.assign(nVertices, 0.);
1246 std::getline(datafile, line);
1247 std::getline(datafile, line);
1248 std::getline(datafile, line);
1249 std::getline(datafile, line);
1251 if (!ExtractFromSquareBrackets(line)) {
1252 std::cerr << m_className <<
"::LoadWeightingField:\n"
1253 <<
" Cannot extract region name.\n"
1254 <<
" Line:\n " << line <<
"\n";
1263 const auto index = FindRegion(name);
1264 if (index >= m_regions.size()) {
1265 std::cerr << m_className <<
"::LoadWeightingField:\n"
1266 <<
" Unknown region " << name <<
".\n";
1271 std::getline(datafile, line);
1272 if (!ExtractFromBrackets(line)) {
1273 std::cerr << m_className <<
"::LoadWeightingField:\n"
1274 <<
" Cannot extract number of values to be read.\n"
1275 <<
" Line:\n " << line <<
"\n";
1283 if (field) nValues /= N;
1285 std::vector<bool> isInRegion(nVertices,
false);
1286 const size_t nElements = m_elements.size();
1287 for (
size_t j = 0; j < nElements; ++j) {
1288 if (m_elements[j].region != index)
continue;
1289 const unsigned int nV = ElementVertices(m_elements[j]);
1290 for (
unsigned int k = 0; k < nV; ++k) {
1291 isInRegion[m_elements[j].vertex[k]] =
true;
1294 unsigned int ivertex = 0;
1295 for (
int j = 0; j < nValues; ++j) {
1297 std::array<double, N> val;
1299 for (
size_t k = 0; k < N; ++k) datafile >> val[k];
1304 while (ivertex < nVertices) {
1305 if (isInRegion[ivertex])
break;
1310 if (ivertex >= nVertices) {
1311 std::cerr << m_className <<
"::LoadWeightingField:\n"
1312 <<
" Dataset " << dataset
1313 <<
" has more values than vertices in region " << name <<
"\n";
1320 wp[ivertex] = val[0];
1326 if (!ok || (datafile.fail() && !datafile.eof())) {
1327 std::cerr << m_className <<
"::LoadWeightingField:\n"
1328 <<
" Error reading file " << filename <<
"\n";
1337 if (m_regions.empty()) {
1338 std::cerr << m_className <<
"::PrintRegions:\n"
1339 <<
" No regions are currently defined.\n";
1343 const size_t nRegions = m_regions.size();
1344 std::cout << m_className <<
"::PrintRegions:\n"
1345 <<
" Currently " << nRegions <<
" regions are defined.\n"
1346 <<
" Index Name Medium\n";
1347 for (
size_t i = 0; i < nRegions; ++i) {
1348 std::cout <<
" " << i <<
" " << m_regions[i].name;
1349 if (!m_regions[i].medium) {
1350 std::cout <<
" none ";
1352 std::cout <<
" " << m_regions[i].medium->GetName();
1354 if (m_regions[i].drift) {
1355 std::cout <<
" (active region)\n";
1364 bool& active)
const {
1365 if (i >= m_regions.size()) {
1366 std::cerr << m_className <<
"::GetRegion: Index out of range.\n";
1369 name = m_regions[i].name;
1370 active = m_regions[i].drift;
1375 if (i >= m_regions.size()) {
1376 std::cerr << m_className <<
"::SetDriftRegion: Index out of range.\n";
1379 m_regions[i].drift =
true;
1384 if (i >= m_regions.size()) {
1385 std::cerr << m_className <<
"::UnsetDriftRegion: Index out of range.\n";
1388 m_regions[i].drift =
false;
1393 if (i >= m_regions.size()) {
1394 std::cerr << m_className <<
"::SetMedium: Index out of range.\n";
1399 std::cerr << m_className <<
"::SetMedium: Null pointer.\n";
1402 m_regions[i].medium = medium;
1407 const double eXsec,
const double hXsec,
1408 const double conc) {
1409 if (donorNumber >= m_donors.size()) {
1410 std::cerr << m_className <<
"::SetDonor: Index out of range.\n";
1413 m_donors[donorNumber].xsece = eXsec;
1414 m_donors[donorNumber].xsech = hXsec;
1415 m_donors[donorNumber].conc = conc;
1423 const double eXsec,
const double hXsec,
1424 const double conc) {
1425 if (acceptorNumber >= m_acceptors.size()) {
1426 std::cerr << m_className <<
"::SetAcceptor: Index out of range.\n";
1429 m_acceptors[acceptorNumber].xsece = eXsec;
1430 m_acceptors[acceptorNumber].xsech = hXsec;
1431 m_acceptors[acceptorNumber].conc = conc;
1439 const double z,
double& eta) {
1440 Interpolate(x, y, z, m_eAttachment, eta);
1446 const double z,
double& eta) {
1447 Interpolate(x, y, z, m_hAttachment, eta);
1453 const double z,
double& vx,
1454 double& vy,
double& vz) {
1455 return Interpolate(x, y, z, m_eVelocity, vx, vy, vz);
1460 const double z,
double& vx,
1461 double& vy,
double& vz) {
1462 return Interpolate(x, y, z, m_hVelocity, vx, vy, vz);
1467 const double z,
double& tau) {
1468 return Interpolate(x, y, z, m_eLifetime, tau);
1473 const double z,
double& tau) {
1474 return Interpolate(x, y, z, m_hLifetime, tau);
1479 const double z,
double& mob) {
1480 return Interpolate(x, y, z, m_eMobility, mob);
1485 const double z,
double& mob) {
1486 return Interpolate(x, y, z, m_hMobility, mob);
1492 std::cerr << m_className <<
"::UpdatePeriodicity:\n"
1493 <<
" Field map not available.\n";
1498 for (
size_t i = 0; i < 3; ++i) {
1499 if (m_periodic[i] && m_mirrorPeriodic[i]) {
1500 std::cerr << m_className <<
"::UpdatePeriodicity:\n"
1501 <<
" Both simple and mirror periodicity requested. Reset.\n";
1502 m_periodic[i] = m_mirrorPeriodic[i] =
false;
1504 if (m_axiallyPeriodic[i]) {
1505 std::cerr << m_className <<
"::UpdatePeriodicity:\n"
1506 <<
" Axial symmetry is not supported. Reset.\n";
1507 m_axiallyPeriodic.fill(
false);
1509 if (m_rotationSymmetric[i]) {
1510 std::cerr << m_className <<
"::UpdatePeriodicity:\n"
1511 <<
" Rotation symmetry is not supported. Reset.\n";
1512 m_rotationSymmetric.fill(
false);
1535 m_eVelocity.clear();
1536 m_hVelocity.clear();
1537 m_eMobility.clear();
1538 m_hMobility.clear();
1539 m_eLifetime.clear();
1540 m_hLifetime.clear();
1542 m_acceptors.clear();
1544 m_acceptorOcc.clear();
1545 m_eAttachment.clear();
1546 m_hAttachment.clear();
1551 std::array<bool, N>& mirr)
const {
1553 for (
size_t i = 0; i < N; ++i) {
1555 const double cellsx = m_bbMax[i] - m_bbMin[i];
1556 if (m_periodic[i]) {
1557 x[i] = m_bbMin[i] + fmod(x[i] - m_bbMin[i], cellsx);
1558 if (x[i] < m_bbMin[i]) x[i] += cellsx;
1559 }
else if (m_mirrorPeriodic[i]) {
1560 double xNew = m_bbMin[i] + fmod(x[i] - m_bbMin[i], cellsx);
1561 if (xNew < m_bbMin[i]) xNew += cellsx;
1562 const int nx = int(floor(0.5 + (xNew - x[i]) / cellsx));
1563 if (nx != 2 * (nx / 2)) {
1564 xNew = m_bbMin[i] + m_bbMax[i] - xNew;
1574 const auto nRegions = m_regions.size();
1575 for (
size_t j = 0; j < nRegions; ++j) {
1576 if (name == m_regions[j].name)
return j;
1578 return m_regions.size();
1584 if (m_vertices.empty())
return;
1585 const size_t nVertices = m_vertices.size();
1586 m_eAttachment.assign(nVertices, 0.);
1587 m_hAttachment.assign(nVertices, 0.);
1589 const size_t nAcceptors = m_acceptors.size();
1590 for (
size_t i = 0; i < nAcceptors; ++i) {
1591 const auto& defect = m_acceptors[i];
1592 if (defect.conc < 0.)
continue;
1593 for (
size_t j = 0; j < nVertices; ++j) {
1595 const double f = m_acceptorOcc[j][i];
1596 if (defect.xsece > 0.) {
1597 m_eAttachment[j] += defect.conc * defect.xsece * (1. - f);
1599 if (defect.xsech > 0.) {
1600 m_hAttachment[j] += defect.conc * defect.xsech * f;
1604 const size_t nDonors = m_donors.size();
1605 for (
size_t i = 0; i < nDonors; ++i) {
1606 const auto& defect = m_donors[i];
1607 if (defect.conc < 0.)
continue;
1608 for (
size_t j = 0; j < nVertices; ++j) {
1609 const double f = m_donorOcc[j][i];
1610 if (defect.xsece > 0.) {
1611 m_eAttachment[j] += defect.conc * defect.xsece * f;
1613 if (defect.xsech > 0.) {
1614 m_hAttachment[j] += defect.conc * defect.xsech * (1. - f);
Interpolation in a field map created by Sentaurus Device.
bool GetElectronLifetime(const double x, const double y, const double z, double &etau) override
bool LoadGrid(const std::string &gridfilename)
void GetRegion(const size_t ireg, std::string &name, bool &active) const
Get the name and "active volume" flag of a region.
void UnsetDriftRegion(const size_t ireg)
Make a region inactive.
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
size_t FindRegion(const std::string &name) const
bool GetElectronMobility(const double x, const double y, const double z, double &mob)
bool GetHoleMobility(const double x, const double y, const double z, double &mob)
bool SetWeightingField(const std::string &datfile1, const std::string &datfile2, const double dv, const std::string &label)
void EnableVelocityMap(const bool on)
Switch use of the imported velocity map on/off.
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
bool HoleAttachment(const double x, const double y, const double z, double &eta) override
Get the hole attachment coefficient.
bool LoadWeightingField(const std::string &datafilename, std::vector< std::array< double, N > > &wf, std::vector< double > &wp)
bool ReadDataset(std::ifstream &datafile, const std::string &dataset)
bool LoadData(const std::string &datafilename)
bool GetHoleLifetime(const double x, const double y, const double z, double &htau) override
void MapCoordinates(std::array< double, N > &x, std::array< bool, N > &mirr) const
void PrintRegions() const
List all currently defined regions.
bool SetAcceptor(const size_t acceptorNumber, const double exsec, const double hxsec, const double concentration)
Set the properties of an acceptor-type defect state.
bool ElectronAttachment(const double x, const double y, const double z, double &eta) override
Get the electron attachment coefficient.
bool SetWeightingFieldShift(const std::string &label, const double x, const double y, const double z)
bool Initialise(const std::string &gridfilename, const std::string &datafilename)
void UpdatePeriodicity() override
Verify periodicities.
bool ElectronVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz) override
Get the electron drift velocity.
void SetDriftRegion(const size_t ireg)
Make a region active ("driftable").
bool HoleVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz) override
Get the hole drift velocity.
bool SetDonor(const size_t donorNumber, const double exsec, const double hxsec, const double concentration)
void SetMedium(const size_t ireg, Medium *m)
Set the medium to be associated to a given region.
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
Abstract base class for media.
void ltrim(std::string &line)