15bool ExtractFromSquareBrackets(std::string& line) {
17 const auto bra = line.find(
'[');
18 const auto ket = line.find(
']');
19 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
22 line = line.substr(bra + 1, ket - bra - 1);
26bool ExtractFromBrackets(std::string& line) {
28 const auto bra = line.find(
'(');
29 const auto ket = line.find(
')');
30 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
33 line = line.substr(bra + 1, ket - bra - 1);
37void PrintError(
const std::string& fcn,
const std::string& filename,
38 const unsigned int line) {
39 std::cerr << fcn <<
":\n"
40 <<
" Error reading file " << filename
41 <<
" (line " << line <<
").\n";
50 const double x,
const double y,
const double z,
51 double& wx,
double& wy,
double& wz,
const std::string& label) {
54 std::cerr <<
m_className <<
"::WeightingField: Not available.\n";
57 double dx = 0., dy = 0., dz = 0.;
58 if (!
GetOffset(label, dx, dy, dz))
return;
64 const double x,
const double y,
const double z,
65 const std::string& label) {
68 std::cerr <<
m_className <<
"::WeightingPotential: Not available.\n";
71 double dx = 0., dy = 0., dz = 0.;
80 const double x,
const double y,
const double z,
const double t,
81 double& wx,
double& wy,
double& wz,
const std::string& label) {
84 std::cerr <<
m_className <<
"::DelayedWeightingField: Not available.\n";
87 if (m_dwtf.empty())
return;
88 if (t < m_dwtf.front() || t > m_dwtf.back())
return;
90 double dx = 0., dy = 0., dz = 0.;
91 if (!GetOffset(label, dx, dy, dz))
return;
93 const auto it1 = std::upper_bound(m_dwtf.cbegin(), m_dwtf.cend(), t);
94 const auto it0 = std::prev(it1);
95 const double dt = t - *it0;
96 const auto i0 = std::distance(
m_dwtf.cbegin(), it0);
97 double wx0 = 0., wy0 = 0., wz0 = 0.;
99 if (dt < Small || it1 ==
m_dwtf.cend()) {
105 const auto i1 = std::distance(m_dwtf.cbegin(), it1);
106 double wx1 = 0., wy1 = 0., wz1 = 0.;
107 Interpolate(x - dx, y - dy, z - dz, m_dwf[i1], wx1, wy1, wz1);
108 const double f1 = dt / (*it1 - *it0);
109 const double f0 = 1. - f1;
110 wx = f0 * wx0 + f1 * wx1;
111 wy = f0 * wy0 + f1 * wy1;
112 wz = f0 * wz0 + f1 * wz1;
117 const double x,
const double y,
const double z,
const double t,
118 const std::string& label) {
121 std::cerr <<
m_className <<
"::DelayedWeightingPotential: Not available.\n";
124 if (m_dwtp.empty())
return 0.;
125 if (t < m_dwtp.front() || t > m_dwtp.back())
return 0.;
127 double dx = 0., dy = 0., dz = 0.;
128 if (!
GetOffset(label, dx, dy, dz))
return 0.;
130 const auto it1 = std::upper_bound(
m_dwtp.cbegin(),
m_dwtp.cend(), t);
131 const auto it0 = std::prev(it1);
132 const double dt = t - *it0;
133 const auto i0 = std::distance(
m_dwtp.cbegin(), it0);
136 if (dt < Small || it1 ==
m_dwtp.cend())
return v0;
138 const auto i1 = std::distance(
m_dwtp.cbegin(), it1);
141 const double f1 = dt / (*it1 - *it0);
142 const double f0 = 1. - f1;
143 return f0 * v0 + f1 * v1;
148 const std::string& label,
double& dx,
double& dy,
double& dz)
const {
151 if (it ==
m_wlabel.end())
return false;
152 const auto i = std::distance(
m_wlabel.begin(), it);
161 const std::string& datafilename) {
168 <<
" Importing mesh data failed.\n";
175 <<
" Importing electric field and potential failed.\n";
181 for (
size_t i = 0; i < N; ++i) {
186 for (
size_t i = 0; i < nElements; ++i) {
191 for (
unsigned int j = 0; j < nV; ++j) {
193 for (
size_t k = 0; k < N; ++k) {
194 xmin[k] = std::min(xmin[k], v[k]);
195 xmax[k] = std::max(xmax[k], v[k]);
198 constexpr double tol = 1.e-6;
199 for (
size_t k = 0; k < N; ++k) {
210 <<
" Available data:\n";
211 if (!
m_epot.empty()) std::cout <<
" Electrostatic potential\n";
212 if (!
m_efield.empty()) std::cout <<
" Electric field\n";
213 if (!
m_eMobility.empty()) std::cout <<
" Electron mobility\n";
214 if (!
m_hMobility.empty()) std::cout <<
" Hole mobility\n";
215 if (!
m_eVelocity.empty()) std::cout <<
" Electron velocity\n";
216 if (!
m_hVelocity.empty()) std::cout <<
" Hole velocity\n";
217 if (!
m_eAlpha.empty()) std::cout <<
" Electron impact ionisation\n";
218 if (!
m_hAlpha.empty()) std::cout <<
" Hole impact ionisation\n";
219 if (!
m_eLifetime.empty()) std::cout <<
" Electron lifetime\n";
220 if (!
m_hLifetime.empty()) std::cout <<
" Hole lifetime\n";
222 std::cout <<
" " <<
m_donors.size() <<
" donor-type traps\n";
225 std::cout <<
" " <<
m_acceptors.size() <<
" acceptor-type traps\n";
227 const std::array<std::string, 3> axes = {
"x",
"y",
"z"};
228 std::cout <<
" Bounding box:\n";
229 for (
size_t i = 0; i < N; ++i) {
230 std::cout <<
" " <<
m_bbMin[i] <<
" < " << axes[i] <<
" [cm] < "
233 std::cout <<
" Voltage range:\n"
240 std::vector<size_t> nElementsByRegion(nRegions, 0);
242 std::vector<size_t> looseElements;
245 std::map<int, unsigned int> nElementsByShape;
247 nElementsByShape = {{0, 0}, {1, 0}, {2, 0}, {3, 0}};
249 nElementsByShape = {{2, 0}, {5, 0}};
251 unsigned int nElementsOther = 0;
254 std::vector<size_t> degenerateElements;
256 for (
size_t i = 0; i < nElements; ++i) {
258 if (element.
region < nRegions) {
259 ++nElementsByRegion[element.
region];
261 looseElements.push_back(i);
263 if (nElementsByShape.count(element.
type) == 0) {
267 nElementsByShape[element.
type] += 1;
268 bool degenerate =
false;
270 for (
unsigned int j = 0; j < nV; ++j) {
271 for (
unsigned int k = j + 1; k < nV; ++k) {
277 if (degenerate)
break;
280 degenerateElements.push_back(i);
284 if (!degenerateElements.empty()) {
286 <<
" The following elements are degenerate:\n";
287 for (
size_t i : degenerateElements) std::cerr <<
" " << i <<
"\n";
291 if (!looseElements.empty()) {
293 <<
" The following elements are not part of any region:\n";
294 for (
size_t i : looseElements) std::cerr <<
" " << i <<
"\n";
299 <<
" Number of regions: " << nRegions <<
"\n";
300 for (
size_t i = 0; i < nRegions; ++i) {
303 std::cout <<
" (" <<
m_regions[i].material <<
")";
305 std::cout <<
", " << nElementsByRegion[i] <<
" elements\n";
308 std::map<int, std::string> shapes = {
309 {0,
"points"}, {1,
"lines"}, {2,
"triangles"}, {3,
"rectangles"},
312 std::cout <<
" Number of elements: " << nElements <<
"\n";
313 for (
const auto& n : nElementsByShape) {
315 std::cout <<
" " << n.second <<
" " << shapes[n.first] <<
"\n";
318 if (nElementsOther > 0) {
319 std::cerr <<
" " << nElementsOther <<
" elements of unknown type.\n"
320 <<
" Program bug!\n";
326 std::cout <<
" Number of vertices: " << m_vertices.size() <<
"\n";
337 std::cout << m_className <<
"::Initialise: Initialisation finished.\n";
351 const std::string& datfile2,
353 const std::string& label) {
356 std::cerr <<
m_className <<
"::SetWeightingField:\n"
357 <<
" Mesh is not available. Call Initialise first.\n";
361 std::cerr <<
m_className <<
"::SetWeightingField:\n"
362 <<
" Voltage difference must be > 0.\n";
365 const double s = 1. / dv;
372 std::vector<std::array<double, N> > wf1;
373 std::vector<double> wp1;
375 std::cerr <<
m_className <<
"::SetWeightingField:\n"
376 <<
" Could not import data from " << datfile1 <<
".\n";
382 std::vector<std::array<double, N> > wf2;
383 std::vector<double> wp2;
385 std::cerr <<
m_className <<
"::SetWeightingField:\n"
386 <<
" Could not import data from " << datfile2 <<
".\n";
390 bool foundField =
true;
391 if (wf1.size() != nVertices || wf2.size() != nVertices) {
393 std::cerr <<
m_className <<
"::SetWeightingField:\n"
394 <<
" Could not load electric field values.\n";
396 bool foundPotential =
true;
397 if (wp1.size() != nVertices || wp2.size() != nVertices) {
398 foundPotential =
false;
399 std::cerr <<
m_className <<
"::SetWeightingField:\n"
400 <<
" Could not load electrostatic potentials.\n";
402 if (!foundField && !foundPotential)
return false;
405 for (
size_t i = 0; i < nVertices; ++i) {
406 for (
size_t j = 0; j < N; ++j) {
407 m_wfield[i][j] = (wf2[i][j] - wf1[i][j]) * s;
411 if (foundPotential) {
412 m_wpot.assign(nVertices, 0.);
413 for (
size_t i = 0; i < nVertices; ++i) {
414 m_wpot[i] = (wp2[i] - wp1[i]) * s;
424 const std::string& datfile1,
const std::string& datfile2,
425 const double dv,
const double t,
const std::string& label) {
428 std::cerr <<
m_className <<
"::SetWeightingPotential:\n"
429 <<
" Mesh is not available. Call Initialise first.\n";
433 std::cerr <<
m_className <<
"::SetWeightingPotential:\n"
434 <<
" Voltage difference must be > 0.\n";
437 const double s = 1. / dv;
440 std::cerr <<
m_className <<
"::SetWeightingPotential:\n"
441 <<
" Prompt component not present.\n"
442 <<
" Import the map for t = 0 first.\n";
446 std::cerr <<
m_className <<
"::SetWeightingPotential:\n"
447 <<
" Label does not match the existing prompt component.\n";
452 std::vector<std::array<double, N> > wf1;
453 std::vector<double> wp1;
455 std::cerr <<
m_className <<
"::SetWeightingPotential:\n"
456 <<
" Could not import data from " << datfile1 <<
".\n";
460 std::vector<std::array<double, N> > wf2;
461 std::vector<double> wp2;
463 std::cerr <<
m_className <<
"::SetWeightingPotential:\n"
464 <<
" Could not import data from " << datfile2 <<
".\n";
468 if (wp1.size() != nVertices || wp2.size() != nVertices) {
469 std::cerr <<
m_className <<
"::SetWeightingPotential:\n"
470 <<
" Could not load electrostatic potentials.\n";
473 if (
m_wpot.size() != nVertices) {
474 std::cerr <<
m_className <<
"::SetWeightingPotential:\n"
475 <<
" Prompt weighting potential not present.\n";
478 std::vector<double> wp(nVertices, 0.);
479 for (
size_t i = 0; i < nVertices; ++i) {
480 wp[i] = (wp2[i] - wp1[i]) * s;
486 m_dwp.push_back(std::move(wp));
488 const auto it = std::upper_bound(
m_dwtp.begin(),
m_dwtp.end(), t);
489 const auto n = std::distance(
m_dwtp.begin(), it);
491 m_dwp.insert(
m_dwp.begin() + n, std::move(wp));
498 const std::string& datfile1,
const std::string& datfile2,
499 const double dv,
const double t,
const std::string& label) {
502 std::cerr <<
m_className <<
"::SetWeightingField:\n"
503 <<
" Mesh is not available. Call Initialise first.\n";
507 std::cerr <<
m_className <<
"::SetWeightingField:\n"
508 <<
" Voltage difference must be > 0.\n";
511 const double s = 1. / dv;
514 std::cerr <<
m_className <<
"::SetWeightingField:\n"
515 <<
" Prompt component not present.\n"
516 <<
" Import the map for t = 0 first.\n";
520 std::cerr <<
m_className <<
"::SetWeightingField:\n"
521 <<
" Label does not match the existing prompt component.\n";
526 std::vector<std::array<double, N> > wf1;
527 std::vector<double> wp1;
529 std::cerr <<
m_className <<
"::SetWeightingField:\n"
530 <<
" Could not import data from " << datfile1 <<
".\n";
534 std::vector<std::array<double, N> > wf2;
535 std::vector<double> wp2;
537 std::cerr <<
m_className <<
"::SetWeightingField:\n"
538 <<
" Could not import data from " << datfile2 <<
".\n";
542 if (wf1.size() != nVertices || wf2.size() != nVertices) {
543 std::cerr <<
m_className <<
"::SetWeightingField:\n"
544 <<
" Could not load electric field values.\n";
548 std::cerr <<
m_className <<
"::SetWeightingField:\n"
549 <<
" Prompt weighting field not present.\n";
552 std::vector<std::array<double, N> > wf;
553 wf.resize(nVertices);
554 for (
size_t i = 0; i < nVertices; ++i) {
555 for (
size_t j = 0; j < N; ++j) {
556 wf[i][j] = (wf2[i][j] - wf1[i][j]) * s;
561 m_dwf.push_back(std::move(wf));
563 const auto it = std::upper_bound(
m_dwtf.begin(),
m_dwtf.end(), t);
564 const auto n = std::distance(
m_dwtf.begin(), it);
566 m_dwf.insert(
m_dwf.begin() + n, std::move(wf));
573 const std::string& label,
const double x,
const double y,
const double z) {
575 std::cerr <<
m_className <<
"::SetWeightingFieldShift:\n"
576 <<
" No map of weighting potentials/fields loaded.\n";
580 for (
size_t i = 0; i < n; ++i) {
583 std::cout <<
m_className <<
"::SetWeightingFieldShift:\n"
584 <<
" Changing offset of electrode \'" << label
585 <<
"\' to (" << x <<
", " << y <<
", " << z <<
") cm.\n";
591 std::cout <<
m_className <<
"::SetWeightingFieldShift:\n"
592 <<
" Adding electrode \'" << label <<
"\' with offset ("
593 << x <<
", " << y <<
", " << z <<
") cm.\n";
601 std::cout <<
m_className <<
"::EnableVelocityMap:\n"
602 <<
" Warning: current map does not include velocity data.\n";
609 std::ifstream gridfile(filename);
612 <<
" Could not open file " << filename <<
".\n";
618 unsigned int iLine = 0;
623 while (std::getline(gridfile, line)) {
627 if (line.empty())
continue;
629 if (line.substr(0, 10) !=
"nb_regions")
continue;
630 const auto pEq = line.find(
'=');
631 if (pEq == std::string::npos) {
634 <<
" Could not read number of regions.\n";
637 line = line.substr(pEq + 1);
638 std::istringstream data(line);
642 if (gridfile.eof()) {
645 <<
" Could not find entry 'nb_regions' in file\n"
646 <<
" " << filename <<
".\n";
648 }
else if (gridfile.fail()) {
650 PrintError(
m_className +
"::LoadGrid", filename, iLine);
654 for (
size_t j = 0; j < nRegions; ++j) {
662 <<
" Found " << nRegions <<
" regions.\n";
665 while (std::getline(gridfile, line)) {
668 if (line.empty())
continue;
670 if (line.substr(0, 7) !=
"regions")
continue;
672 if (!ExtractFromSquareBrackets(line)) {
674 <<
" Could not read region names.\n";
677 std::istringstream data(line);
678 for (
size_t j = 0; j < nRegions; ++j) {
687 if (gridfile.eof()) {
690 <<
" Could not find entry 'regions' in file\n"
691 <<
" " << filename <<
".\n";
693 }
else if (gridfile.fail()) {
695 PrintError(
m_className +
"::LoadGrid", filename, iLine);
700 while (std::getline(gridfile, line)) {
703 if (line.empty())
continue;
705 if (line.substr(0, 9) !=
"materials")
continue;
707 if (!ExtractFromSquareBrackets(line)) {
709 <<
" Could not read materials.\n";
712 std::istringstream data(line);
713 for (
size_t j = 0; j < nRegions; ++j) {
719 if (gridfile.eof()) {
722 <<
" Could not find entry 'materials' in file\n"
723 <<
" " << filename <<
".\n";
724 }
else if (gridfile.fail()) {
726 PrintError(
m_className +
"::LoadGrid", filename, iLine);
730 size_t nVertices = 0;
731 while (std::getline(gridfile, line)) {
734 if (line.empty())
continue;
736 if (line.substr(0, 8) !=
"Vertices")
continue;
738 if (!ExtractFromBrackets(line)) {
740 <<
" Could not read number of vertices.\n";
743 std::istringstream data(line);
747 for (
size_t j = 0; j < nVertices; ++j) {
748 for (
size_t k = 0; k < N; ++k) {
757 if (gridfile.eof()) {
759 <<
" Could not find section 'Vertices' in file\n"
760 <<
" " << filename <<
".\n";
762 }
else if (gridfile.fail()) {
763 PrintError(
m_className +
"::LoadGrid", filename, iLine);
770 std::vector<unsigned int> edgeP1;
771 std::vector<unsigned int> edgeP2;
772 while (std::getline(gridfile, line)) {
775 if (line.empty())
continue;
777 if (line.substr(0, 5) !=
"Edges")
continue;
779 if (!ExtractFromBrackets(line)) {
781 <<
" Could not read number of edges.\n";
784 std::istringstream data(line);
786 edgeP1.resize(nEdges);
787 edgeP2.resize(nEdges);
789 for (
size_t j = 0; j < nEdges; ++j) {
790 gridfile >> edgeP1[j] >> edgeP2[j];
795 if (gridfile.eof()) {
797 <<
" Could not find section 'Edges' in file\n"
798 <<
" " << filename <<
".\n";
800 }
else if (gridfile.fail()) {
801 PrintError(
m_className +
"::LoadGrid", filename, iLine);
805 for (
size_t i = 0; i < nEdges; ++i) {
807 if (edgeP1[i] >= nVertices || edgeP2[i] >= nVertices) {
809 <<
" Vertex index of edge " << i <<
" out of range.\n";
813 if (edgeP1[i] == edgeP2[i]) {
815 <<
" Edge " << i <<
" is degenerate.\n";
827 std::vector<Face> faces;
829 while (std::getline(gridfile, line)) {
832 if (line.empty())
continue;
834 if (line.substr(0, 5) !=
"Faces")
continue;
836 if (!ExtractFromBrackets(line)) {
838 <<
" Could not read number of faces.\n";
841 std::istringstream data(line);
843 faces.resize(nFaces);
845 for (
size_t j = 0; j < nFaces; ++j) {
846 gridfile >> faces[j].type;
847 if (faces[j].type != 3 && faces[j].type != 4) {
849 <<
" Face with index " << j
850 <<
" has invalid number of edges, " << faces[j].type <<
".\n";
853 for (
int k = 0; k < faces[j].type; ++k) {
854 gridfile >> faces[j].edge[k];
860 if (gridfile.eof()) {
862 <<
" Could not find section 'Faces' in file\n"
863 <<
" " << filename <<
".\n";
865 }
else if (gridfile.fail()) {
866 PrintError(
m_className +
"::LoadGrid", filename, iLine);
872 size_t nElements = 0;
873 while (std::getline(gridfile, line)) {
876 if (line.empty())
continue;
878 if (line.substr(0, 8) !=
"Elements")
continue;
880 if (!ExtractFromBrackets(line)) {
882 <<
" Could not read number of elements.\n";
885 std::istringstream data(line);
891 for (
size_t j = 0; j < nElements; ++j) {
893 unsigned int type = 0;
901 if (p >= nVertices) {
902 PrintError(
m_className +
"::LoadGrid", filename, iLine);
903 std::cerr <<
" Vertex index out of range.\n";
907 }
else if (type == 1) {
909 for (
size_t k = 0; k < 2; ++k) {
912 if (p < 0) p = -p - 1;
914 if (p >= (
int)nVertices) {
915 PrintError(
m_className +
"::LoadGrid", filename, iLine);
916 std::cerr <<
" Vertex index out of range.\n";
921 }
else if (type == 2) {
923 int p0 = 0, p1 = 0, p2 = 0;
924 gridfile >> p0 >> p1 >> p2;
928 if (p0 < 0) p0 = -p0 - 1;
929 if (p1 < 0) p1 = -p1 - 1;
930 if (p2 < 0) p2 = -p2 - 1;
932 if (p0 >= (
int)nEdges || p1 >= (
int)nEdges || p2 >= (
int)nEdges) {
933 PrintError(
m_className +
"::LoadGrid", filename, iLine);
934 std::cerr <<
" Edge index out of range.\n";
955 }
else if (type == 3) {
957 for (
size_t k = 0; k < 4; ++k) {
961 if (p >= (
int)nEdges || -p - 1 >= (
int)nEdges) {
962 PrintError(
m_className +
"::LoadGrid", filename, iLine);
963 std::cerr <<
" Edge index out of range.\n";
987 PrintError(
m_className +
"::LoadGrid", filename, iLine);
988 std::cerr <<
" Invalid element type (" << type
989 <<
") for 2d mesh.\n";
995 int edge0, edge1, edge2;
996 gridfile >> edge0 >> edge1 >> edge2;
1002 if (edge0 < 0) edge0 = -edge0 - 1;
1003 if (edge1 < 0) edge1 = -edge1 - 1;
1004 if (edge2 < 0) edge2 = -edge2 - 1;
1006 if (edge0 >= (
int)nEdges || edge1 >= (
int)nEdges ||
1007 edge2 >= (
int)nEdges) {
1008 PrintError(
m_className +
"::LoadGrid", filename, iLine);
1009 std::cerr <<
" Edge index out of range.\n";
1014 if (edgeP1[edge1] !=
m_elements[j].vertex[0] &&
1020 }
else if (type == 5) {
1026 int face0, face1, face2, face3;
1027 gridfile >> face0 >> face1 >> face2 >> face3;
1028 if (face0 < 0) face0 = -face0 - 1;
1029 if (face1 < 0) face1 = -face1 - 1;
1030 if (face2 < 0) face2 = -face2 - 1;
1031 if (face3 < 0) face3 = -face3 - 1;
1033 if (face0 >= (
int)nFaces || face1 >= (
int)nFaces ||
1034 face2 >= (
int)nFaces || face3 >= (
int)nFaces) {
1035 PrintError(
m_className +
"::LoadGrid", filename, iLine);
1036 std::cerr <<
" Face index out of range.\n";
1040 int edge0 = faces[face0].edge[0];
1041 int edge1 = faces[face0].edge[1];
1042 int edge2 = faces[face0].edge[2];
1043 if (edge0 < 0) edge0 = -edge0 - 1;
1044 if (edge1 < 0) edge1 = -edge1 - 1;
1045 if (edge2 < 0) edge2 = -edge2 - 1;
1047 if (edge0 >= (
int)nEdges || edge1 >= (
int)nEdges ||
1048 edge2 >= (
int)nEdges) {
1049 PrintError(
m_className +
"::LoadGrid", filename, iLine);
1050 std::cerr <<
" Edge index out of range.\n";
1056 if (edgeP1[edge1] !=
m_elements[j].vertex[0] &&
1063 edge0 = faces[face1].edge[0];
1064 edge1 = faces[face1].edge[1];
1065 edge2 = faces[face1].edge[2];
1066 if (edge0 < 0) edge0 = -edge0 - 1;
1067 if (edge1 < 0) edge1 = -edge1 - 1;
1068 if (edge2 < 0) edge2 = -edge2 - 1;
1072 if (edgeP1[edge0] != v0 && edgeP1[edge0] != v1 && edgeP1[edge0] != v2) {
1074 }
else if (edgeP2[edge0] != v0 && edgeP2[edge0] != v1 &&
1075 edgeP2[edge0] != v2) {
1077 }
else if (edgeP1[edge1] != v0 &&
1078 edgeP1[edge1] != v1 &&
1079 edgeP1[edge1] != v2) {
1081 }
else if (edgeP2[edge1] != v0 &&
1082 edgeP2[edge1] != v1 &&
1083 edgeP2[edge1] != v2) {
1086 PrintError(
m_className +
"::LoadGrid", filename, iLine);
1087 std::cerr <<
" Face 1 of element " << j <<
" is degenerate.\n";
1092 PrintError(
m_className +
"::LoadGrid", filename, iLine);
1093 if (type == 0 || type == 1) {
1094 std::cerr <<
" Invalid element type (" << type
1095 <<
") for 3d mesh.\n";
1097 std::cerr <<
" Element type " << type <<
" is not supported.\n"
1098 <<
" Remesh with option -t to create only"
1099 <<
" triangles and tetrahedra.\n";
1109 if (gridfile.eof()) {
1111 <<
" Could not find section 'Elements' in file\n"
1112 <<
" " << filename <<
".\n";
1114 }
else if (gridfile.fail()) {
1115 PrintError(
m_className +
"::LoadGrid", filename, iLine);
1120 while (std::getline(gridfile, line)) {
1122 if (line.empty())
continue;
1124 if (line.substr(0, 6) !=
"Region")
continue;
1126 if (!ExtractFromBrackets(line)) {
1128 <<
" Could not read region name.\n";
1131 std::istringstream data(line);
1139 <<
" Unknown region " << name <<
".\n";
1142 std::getline(gridfile, line);
1143 std::getline(gridfile, line);
1144 if (!ExtractFromBrackets(line)) {
1146 <<
" Could not read number of elements in region "
1150 int nElementsRegion;
1152 data >> nElementsRegion;
1154 for (
int j = 0; j < nElementsRegion; ++j) {
1155 size_t iElement = 0;
1156 gridfile >> iElement;
1159 <<
" Error reading element indices for region "
1166 if (gridfile.fail() && !gridfile.eof()) {
1168 <<
" Error reading file " << filename <<
".\n";
1177 std::ifstream datafile(filename);
1180 <<
" Could not open file " << filename <<
".\n";
1184 std::vector<unsigned int> fillCount(nVertices, 0);
1186 std::array<double, N> zeroes;
1190 while (std::getline(datafile, line)) {
1194 if (line.empty())
continue;
1196 if (line.substr(0, 8) !=
"function")
continue;
1198 const auto pEq = line.find(
'=');
1199 if (pEq == std::string::npos) {
1202 <<
" Error reading file " << filename <<
".\n"
1203 <<
" Line:\n " << line <<
"\n";
1206 line = line.substr(pEq + 1);
1207 std::string dataset;
1208 std::istringstream data(line);
1211 if (
m_debug && dataset !=
"[") {
1212 std::cout <<
m_className <<
"::LoadData: Found dataset "
1213 << dataset <<
".\n";
1215 if (dataset ==
"ElectrostaticPotential") {
1221 }
else if (dataset ==
"ElectricField") {
1227 }
else if (dataset ==
"eDriftVelocity") {
1233 }
else if (dataset ==
"hDriftVelocity") {
1239 }
else if (dataset ==
"eMobility") {
1245 }
else if (dataset ==
"hMobility") {
1251 }
else if (dataset ==
"eAlphaAvalanche") {
1257 }
else if (dataset ==
"hAlphaAvalanche") {
1263 }
else if (dataset ==
"eLifetime") {
1269 }
else if (dataset ==
"hLifetime") {
1275 }
else if (dataset.substr(0, 14) ==
"TrapOccupation" &&
1276 dataset.substr(17, 2) ==
"Do") {
1277 if (!
ReadDataset(datafile, dataset))
return false;
1283 }
else if (dataset.substr(0, 14) ==
"TrapOccupation" &&
1284 dataset.substr(17, 2) ==
"Ac") {
1285 if (!
ReadDataset(datafile, dataset))
return false;
1287 acceptor.
xsece = -1.;
1288 acceptor.
xsech = -1.;
1289 acceptor.
conc = -1.;
1293 if (datafile.fail() && !datafile.eof()) {
1295 <<
" Error reading file " << filename <<
"\n";
1303 const std::string& dataset) {
1305 if (!datafile.is_open())
return false;
1307 ElectrostaticPotential,
1317 DonorTrapOccupation,
1318 AcceptorTrapOccupation,
1321 DataSet ds = Unknown;
1322 if (dataset ==
"ElectrostaticPotential") {
1323 ds = ElectrostaticPotential;
1324 }
else if (dataset ==
"ElectricField") {
1326 }
else if (dataset ==
"eDriftVelocity") {
1327 ds = eDriftVelocity;
1328 }
else if (dataset ==
"hDriftVelocity") {
1329 ds = hDriftVelocity;
1330 }
else if (dataset ==
"eMobility") {
1332 }
else if (dataset ==
"hMobility") {
1334 }
else if (dataset ==
"eAlphaAvalanche") {
1336 }
else if (dataset ==
"hAlphaAvalanche") {
1338 }
else if (dataset ==
"eLifetime") {
1340 }
else if (dataset ==
"hLifetime") {
1342 }
else if (dataset.substr(0, 14) ==
"TrapOccupation") {
1343 if (dataset.substr(17, 2) ==
"Do") {
1344 ds = DonorTrapOccupation;
1345 }
else if (dataset.substr(17, 2) ==
"Ac") {
1346 ds = AcceptorTrapOccupation;
1350 <<
" Unexpected dataset " << dataset <<
".\n";
1353 bool isVector =
false;
1354 if (ds == EField || ds == eDriftVelocity || ds == hDriftVelocity) {
1358 std::getline(datafile, line);
1359 std::getline(datafile, line);
1360 std::getline(datafile, line);
1361 std::getline(datafile, line);
1363 if (!ExtractFromSquareBrackets(line)) {
1365 <<
" Cannot extract region name.\n"
1366 <<
" Line:\n " << line <<
"\n";
1370 std::istringstream data(line);
1377 <<
" Unknown region " << name <<
".\n";
1382 <<
" Reading dataset " << dataset <<
" for region "
1386 std::getline(datafile, line);
1387 if (!ExtractFromBrackets(line)) {
1389 <<
" Cannot extract number of values to be read.\n"
1390 <<
" Line:\n " << line <<
"\n";
1396 if (isVector) nValues /= N;
1397 if (
m_debug) std::cout <<
" Expecting " << nValues <<
" values.\n";
1400 std::vector<bool> isInRegion(nVertices,
false);
1401 size_t nVerticesInRegion = 0;
1402 size_t nElementsInRegion = 0;
1404 if (element.
region != index)
continue;
1405 ++nElementsInRegion;
1407 for (
unsigned int k = 0; k < nV; ++k) {
1408 if (isInRegion[element.
vertex[k]])
continue;
1409 isInRegion[element.
vertex[k]] =
true;
1410 ++nVerticesInRegion;
1414 std::cout <<
" Region has " << nElementsInRegion <<
" elements and "
1415 << nVerticesInRegion <<
" vertices.\n";
1417 unsigned int ivertex = 0;
1418 for (
int j = 0; j < nValues; ++j) {
1420 std::array<long double, N> val;
1422 for (
size_t k = 0; k < N; ++k) datafile >> val[k];
1427 while (ivertex < nVertices) {
1428 if (isInRegion[ivertex])
break;
1433 if (ivertex >= nVertices) {
1435 <<
" Dataset " << dataset <<
" has more values than "
1436 <<
"there are vertices in region " << name <<
"\n";
1441 case ElectrostaticPotential:
1442 m_epot[ivertex] = val[0];
1445 for (
size_t k = 0; k < N; ++k)
m_efield[ivertex][k] = val[k];
1447 case eDriftVelocity:
1449 for (
size_t k = 0; k < N; ++k) {
1453 case hDriftVelocity:
1455 for (
size_t k = 0; k < N; ++k) {
1481 case DonorTrapOccupation:
1484 case AcceptorTrapOccupation:
1489 <<
" Unexpected dataset (" << ds <<
"). Program bug!\n";
1499 const std::string& filename,
1500 std::vector<std::array<double, N> >& wf, std::vector<double>& wp) {
1502 std::ifstream datafile(filename, std::ios::in);
1504 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
1505 <<
" Could not open file " << filename <<
".\n";
1509 std::array<double, N> zeroes;
1514 while (std::getline(datafile, line)) {
1517 if (line.empty())
continue;
1519 if (line.substr(0, 8) !=
"function")
continue;
1521 const auto pEq = line.find(
'=');
1522 if (pEq == std::string::npos) {
1524 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
1525 <<
" Error reading file " << filename <<
".\n"
1526 <<
" Line:\n " << line <<
"\n";
1529 line = line.substr(pEq + 1);
1530 std::string dataset;
1531 std::istringstream data(line);
1534 if (dataset !=
"ElectrostaticPotential" && dataset !=
"ElectricField") {
1538 if (dataset ==
"ElectricField") {
1539 if (wf.empty()) wf.assign(nVertices, zeroes);
1542 if (wp.empty()) wp.assign(nVertices, 0.);
1544 std::getline(datafile, line);
1545 std::getline(datafile, line);
1546 std::getline(datafile, line);
1547 std::getline(datafile, line);
1549 if (!ExtractFromSquareBrackets(line)) {
1550 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
1551 <<
" Cannot extract region name.\n"
1552 <<
" Line:\n " << line <<
"\n";
1563 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
1564 <<
" Unknown region " << name <<
".\n";
1569 std::getline(datafile, line);
1570 if (!ExtractFromBrackets(line)) {
1571 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
1572 <<
" Cannot extract number of values to be read.\n"
1573 <<
" Line:\n " << line <<
"\n";
1581 if (field) nValues /= N;
1583 std::vector<bool> isInRegion(nVertices,
false);
1585 for (
size_t j = 0; j < nElements; ++j) {
1588 for (
unsigned int k = 0; k < nV; ++k) {
1592 unsigned int ivertex = 0;
1593 for (
int j = 0; j < nValues; ++j) {
1595 std::array<double, N> val;
1597 for (
size_t k = 0; k < N; ++k) datafile >> val[k];
1602 while (ivertex < nVertices) {
1603 if (isInRegion[ivertex])
break;
1608 if (ivertex >= nVertices) {
1609 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
1610 <<
" Dataset " << dataset
1611 <<
" has more values than vertices in region " << name <<
"\n";
1618 wp[ivertex] = val[0];
1624 if (!ok || (datafile.fail() && !datafile.eof())) {
1625 std::cerr <<
m_className <<
"::LoadWeightingField:\n"
1626 <<
" Error reading file " << filename <<
"\n";
1637 <<
" No regions are currently defined.\n";
1641 const size_t nRegions =
m_regions.size();
1643 <<
" Currently " << nRegions <<
" regions are defined.\n"
1644 <<
" Index Name Material Medium\n";
1645 for (
size_t i = 0; i < nRegions; ++i) {
1646 std::cout << std::setw(8) << std::right << i <<
" "
1647 << std::setw(20) << std::left <<
m_regions[i].name <<
" "
1648 << std::setw(18) << std::left <<
m_regions[i].material <<
" ";
1650 std::cout << std::setw(18) <<
"none";
1652 std::cout << std::setw(18) <<
m_regions[i].medium->GetName();
1655 std::cout <<
" (active)\n";
1664 bool& active)
const {
1666 std::cerr <<
m_className <<
"::GetRegion: Index out of range.\n";
1676 std::cerr <<
m_className <<
"::SetDriftRegion: Index out of range.\n";
1685 std::cerr <<
m_className <<
"::UnsetDriftRegion: Index out of range.\n";
1694 std::cerr <<
m_className <<
"::SetMedium: Index out of range.\n";
1698 std::cerr <<
m_className <<
"::SetMedium: Null pointer.\n";
1708 std::cerr <<
m_className <<
"::SetMedium: Null pointer.\n";
1713 for (
size_t i = 0; i < nRegions; ++i) {
1714 if (material !=
m_regions[i].material)
continue;
1716 std::cout <<
m_className <<
"::SetMedium: Associating region " << i
1717 <<
" (" <<
m_regions[i].name <<
") with "
1718 << medium->
GetName() <<
".\n";
1722 std::cerr <<
m_className <<
"::SetMedium: Found no region with material "
1723 << material <<
".\n";
1729 const double eXsec,
const double hXsec,
1730 const double conc) {
1731 if (donorNumber >=
m_donors.size()) {
1732 std::cerr <<
m_className <<
"::SetDonor: Index out of range.\n";
1735 m_donors[donorNumber].xsece = eXsec;
1736 m_donors[donorNumber].xsech = hXsec;
1745 const double eXsec,
const double hXsec,
1746 const double conc) {
1748 std::cerr <<
m_className <<
"::SetAcceptor: Index out of range.\n";
1761 const double z,
double& eta) {
1768 const double z,
double& eta) {
1775 const double z,
double& alpha) {
1782 const double z,
double& alpha) {
1789 const double z,
double& vx,
1790 double& vy,
double& vz) {
1796 const double z,
double& vx,
1797 double& vy,
double& vz) {
1803 const double z,
double& tau) {
1809 const double z,
double& tau) {
1815 const double z,
double& mob) {
1821 const double z,
double& mob) {
1828 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
1829 <<
" Field map not available.\n";
1834 for (
size_t i = 0; i < 3; ++i) {
1836 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
1837 <<
" Both simple and mirror periodicity requested. Reset.\n";
1841 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
1842 <<
" Axial symmetry is not supported. Reset.\n";
1846 std::cerr <<
m_className <<
"::UpdatePeriodicity:\n"
1847 <<
" Rotation symmetry is not supported. Reset.\n";
1893 std::array<bool, N>& mirr)
const {
1895 for (
size_t i = 0; i < N; ++i) {
1900 if (x[i] <
m_bbMin[i]) x[i] += cellsx;
1903 if (xNew <
m_bbMin[i]) xNew += cellsx;
1904 const int nx = int(floor(0.5 + (xNew - x[i]) / cellsx));
1905 if (nx != 2 * (nx / 2)) {
1917 for (
size_t j = 0; j < nRegions; ++j) {
1918 if (name ==
m_regions[j].name)
return j;
1932 for (
size_t i = 0; i < nAcceptors; ++i) {
1934 if (defect.conc < 0.)
continue;
1935 for (
size_t j = 0; j < nVertices; ++j) {
1938 if (defect.xsece > 0.) {
1941 if (defect.xsech > 0.) {
1946 const size_t nDonors =
m_donors.size();
1947 for (
size_t i = 0; i < nDonors; ++i) {
1949 if (defect.conc < 0.)
continue;
1950 for (
size_t j = 0; j < nVertices; ++j) {
1952 if (defect.xsece > 0.) {
1955 if (defect.xsech > 0.) {
Interpolation in a field map created by Sentaurus Device.
bool GetElectronLifetime(const double x, const double y, const double z, double &etau) override
std::vector< double > m_hMobility
bool LoadGrid(const std::string &gridfilename)
bool SetWeightingPotential(const std::string &datfile1, const std::string &datfile2, const double dv, const double t, const std::string &label)
Import time-dependent weighting potentials at t > 0.
void GetRegion(const size_t ireg, std::string &name, bool &active) const
Get the name and "active volume" flag of a region.
void UnsetDriftRegion(const size_t ireg)
Make a region inactive.
std::vector< Defect > m_acceptors
std::vector< double > m_dwtf
std::vector< std::vector< float > > m_acceptorOcc
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
std::vector< std::string > m_wlabel
size_t FindRegion(const std::string &name) const
std::vector< std::array< double, 3 > > m_wshift
virtual bool Interpolate(const double x, const double y, const double z, const std::vector< double > &field, double &f)=0
bool GetElectronMobility(const double x, const double y, const double z, double &mob)
Get the electron mobility at a given point in the mesh.
std::vector< std::vector< float > > m_donorOcc
std::vector< std::array< double, N > > m_vertices
bool GetHoleMobility(const double x, const double y, const double z, double &mob)
Get the hole mobility at a given point in the mesh.
bool SetWeightingField(const std::string &datfile1, const std::string &datfile2, const double dv, const std::string &label)
std::vector< Element > m_elements
void EnableVelocityMap(const bool on)
Switch use of the imported velocity map on/off.
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
bool HoleAttachment(const double x, const double y, const double z, double &eta) override
Get the hole attachment coefficient.
std::vector< std::vector< double > > m_dwp
std::vector< double > m_eAttachment
std::vector< double > m_hAlpha
std::vector< Region > m_regions
bool HoleTownsend(const double x, const double y, const double z, double &alpha) override
Get the hole Townsend coefficient.
std::vector< double > m_eAlpha
bool LoadWeightingField(const std::string &datafilename, std::vector< std::array< double, N > > &wf, std::vector< double > &wp)
std::vector< std::array< double, N > > m_hVelocity
static unsigned int ElementVertices(const Element &element)
std::array< double, 3 > m_bbMax
bool ReadDataset(std::ifstream &datafile, const std::string &dataset)
std::vector< double > m_epot
bool LoadData(const std::string &datafilename)
std::vector< std::array< double, N > > m_efield
std::vector< std::array< double, N > > m_eVelocity
std::vector< double > m_hLifetime
bool GetHoleLifetime(const double x, const double y, const double z, double &htau) override
bool GetOffset(const std::string &label, double &dx, double &dy, double &dz) const
void MapCoordinates(std::array< double, N > &x, std::array< bool, N > &mirr) const
void PrintRegions() const
List all currently defined regions.
std::vector< double > m_wpot
bool SetAcceptor(const size_t acceptorNumber, const double exsec, const double hxsec, const double concentration)
Set the properties of an acceptor-type defect state.
bool ElectronAttachment(const double x, const double y, const double z, double &eta) override
Get the electron attachment coefficient.
std::vector< std::vector< std::array< double, N > > > m_dwf
std::vector< Defect > m_donors
bool SetWeightingFieldShift(const std::string &label, const double x, const double y, const double z)
bool Initialise(const std::string &gridfilename, const std::string &datafilename)
std::vector< double > m_eMobility
void UpdatePeriodicity() override
Verify periodicities.
void DelayedWeightingField(const double x, const double y, const double z, const double t, double &wx, double &wy, double &wz, const std::string &label) override
bool ElectronVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz) override
Get the electron drift velocity.
double DelayedWeightingPotential(const double x, const double y, const double z, const double t, const std::string &label) override
void SetDriftRegion(const size_t ireg)
Make a region active ("driftable").
bool HoleVelocity(const double x, const double y, const double z, double &vx, double &vy, double &vz) override
Get the hole drift velocity.
std::vector< std::array< double, N > > m_wfield
std::vector< double > m_dwtp
bool SetDonor(const size_t donorNumber, const double exsec, const double hxsec, const double concentration)
void SetMedium(const size_t ireg, Medium *m)
Set the medium to be associated to a given region.
bool ElectronTownsend(const double x, const double y, const double z, double &alpha) override
Get the electron Townsend coefficient.
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
std::vector< double > m_hAttachment
std::array< double, 3 > m_bbMin
std::vector< double > m_eLifetime
std::array< bool, 3 > m_rotationSymmetric
Rotation symmetry around x-axis, y-axis, z-axis.
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
bool m_debug
Switch on/off debugging messages.
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
std::string m_className
Class name.
bool m_ready
Ready for use?
std::array< bool, 3 > m_axiallyPeriodic
Axial periodicity in x, y, z.
Abstract base class for media.
const std::string & GetName() const
Get the medium name/identifier.
void ltrim(std::string &line)
unsigned int vertex[nMaxVertices]