Garfield++ 3.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentTcad3d.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 = "ComponentTcad3d";
16
17 m_regions.reserve(10);
18 m_vertices.reserve(10000);
19 m_elements.reserve(10000);
20}
21
22void ComponentTcad3d::ElectricField(const double xin, const double yin,
23 const double zin, double& ex, double& ey,
24 double& ez, double& p, Medium*& m,
25 int& status) {
26 ex = ey = ez = p = 0.;
27 m = nullptr;
28 // Make sure the field map has been loaded.
29 if (!m_ready) {
30 std::cerr << m_className << "::ElectricField:\n"
31 << " Field map is not available for interpolation.\n";
32 status = -10;
33 return;
34 }
35
36 double x = xin, y = yin, z = zin;
37 // In case of periodicity, reduce to the cell volume.
38 bool xmirr = false, ymirr = false, zmirr = false;
39 MapCoordinates(x, y, z, xmirr, ymirr, zmirr);
40
41 // Check if the point is inside the bounding box.
42 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB ||
43 z < m_zMinBB || z > m_zMaxBB) {
44 status = -6;
45 return;
46 }
47
48 // Assume this will work.
49 status = 0;
50 std::array<double, nMaxVertices> w;
51 const unsigned int i = FindElement(x, y, z, w);
52 if (i >= m_elements.size()) {
53 // Point is outside the mesh.
54 status = -6;
55 return;
56 }
57 const Element& element = m_elements[i];
58 const unsigned int nVertices = element.type == 2 ? 3 : 4;
59 for (unsigned int j = 0; j < nVertices; ++j) {
60 const Vertex& vj = m_vertices[element.vertex[j]];
61 ex += w[j] * vj.ex;
62 ey += w[j] * vj.ey;
63 ez += w[j] * vj.ez;
64 p += w[j] * vj.p;
65 }
66 if (xmirr) ex = -ex;
67 if (ymirr) ey = -ey;
68 if (zmirr) ez = -ez;
69 m = m_regions[element.region].medium;
70 if (!m_regions[element.region].drift || !m) status = -5;
71 m_lastElement = i;
72}
73
74void ComponentTcad3d::ElectricField(const double x, const double y,
75 const double z, double& ex, double& ey,
76 double& ez, Medium*& m, int& status) {
77 double v = 0.;
78 ElectricField(x, y, z, ex, ey, ez, v, m, status);
79}
80
81void ComponentTcad3d::WeightingField(const double xin, const double yin,
82 const double zin, double& wx, double& wy,
83 double& wz, const std::string& /*label*/) {
84 wx = wy = wz = 0.;
85 if (m_wf.empty()) {
86 std::cerr << m_className << "::WeightingField: Not available.\n";
87 return;
88 }
89 double x = xin, y = yin, z = zin;
90 // In case of periodicity, reduce to the cell volume.
91 bool xmirr = false, ymirr = false, zmirr = false;
92 MapCoordinates(x, y, z, xmirr, ymirr, zmirr);
93
94 // Check if the point is inside the bounding box.
95 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB ||
96 z < m_zMinBB || z > m_zMaxBB) return;
97
98 std::array<double, nMaxVertices> w;
99 const unsigned int i = FindElement(x, y, z, w);
100 if (i >= m_elements.size()) return;
101
102 const Element& element = m_elements[i];
103 const unsigned int nVertices = element.type == 2 ? 3 : 4;
104 for (unsigned int j = 0; j < nVertices; ++j) {
105 const auto& f = m_wf[element.vertex[j]];
106 wx += w[j] * f[0];
107 wy += w[j] * f[1];
108 wz += w[j] * f[2];
109 }
110 if (xmirr) wx = -wx;
111 if (ymirr) wy = -wy;
112 if (zmirr) wz = -wz;
113}
114
115double ComponentTcad3d::WeightingPotential(const double xin, const double yin,
116 const double zin,
117 const std::string& /*label*/) {
118
119 if (m_wp.empty()) {
120 std::cerr << m_className << "::WeightingPotential: Not available.\n";
121 return 0.;
122 }
123 // In case of periodicity, reduce to the cell volume.
124 double x = xin, y = yin, z = zin;
125 bool xmirr = false, ymirr = false, zmirr = false;
126 MapCoordinates(x, y, z, xmirr, ymirr, zmirr);
127
128 // Check if the point is inside the bounding box.
129 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB ||
130 z < m_zMinBB || z > m_zMaxBB) return 0.;
131
132 std::array<double, nMaxVertices> w;
133 const unsigned int i = FindElement(x, y, z, w);
134 if (i >= m_elements.size()) return 0.;
135
136 double v = 0.;
137 const Element& element = m_elements[i];
138 const unsigned int nVertices = element.type == 2 ? 3 : 4;
139 for (unsigned int j = 0; j < nVertices; ++j) {
140 v += w[j] * m_wp[element.vertex[j]];
141 }
142 return v;
143}
144
145Medium* ComponentTcad3d::GetMedium(const double xin, const double yin,
146 const double zin) {
147 // Make sure the field map has been loaded.
148 if (!m_ready) {
149 std::cerr << m_className << "::GetMedium:\n"
150 << " Field map not available for interpolation.\n";
151 return nullptr;
152 }
153
154 double x = xin, y = yin, z = zin;
155 bool xmirr = false, ymirr = false, zmirr = false;
156 MapCoordinates(x, y, z, xmirr, ymirr, zmirr);
157
158 // Check if the point is inside the bounding box.
159 if (x < m_xMinBB || x > m_xMaxBB || y < m_yMinBB || y > m_yMaxBB ||
160 z < m_zMinBB || z > m_zMaxBB) {
161 return nullptr;
162 }
163
164 std::array<double, nMaxVertices> w;
165 const unsigned int i = FindElement(x, y, z, w);
166 if (i >= m_elements.size()) {
167 // Point is outside the mesh.
168 return nullptr;
169 }
170 m_lastElement = i;
171 const Element& element = m_elements[i];
172 return m_regions[element.region].medium;
173}
174
175bool ComponentTcad3d::Initialise(const std::string& gridfilename,
176 const std::string& datafilename) {
177 m_ready = false;
178 // Import mesh data from .grd file.
179 if (!LoadGrid(gridfilename)) {
180 std::cerr << m_className << "::Initialise:\n"
181 << " Importing mesh data failed.\n";
182 return false;
183 }
184
185 // Import electric field and potential from .dat file.
186 if (!LoadData(datafilename)) {
187 std::cerr << m_className << "::Initialise:\n"
188 << " Importing electric field and potential failed.\n";
189 return false;
190 }
191
192 // Find min./max. coordinates and potentials.
193 m_xMaxBB = m_vertices[m_elements[0].vertex[0]].x;
194 m_yMaxBB = m_vertices[m_elements[0].vertex[0]].y;
195 m_zMaxBB = m_vertices[m_elements[0].vertex[0]].z;
196 m_xMinBB = m_xMaxBB;
197 m_yMinBB = m_yMaxBB;
198 m_zMinBB = m_zMaxBB;
199 m_pMax = m_pMin = m_vertices[m_elements[0].vertex[0]].p;
200 const unsigned int nElements = m_elements.size();
201 for (unsigned int i = 0; i < nElements; ++i) {
202 Element& element = m_elements[i];
203 double xmin = m_vertices[element.vertex[0]].x;
204 double ymin = m_vertices[element.vertex[0]].y;
205 double zmin = m_vertices[element.vertex[0]].z;
206 double xmax = xmin;
207 double ymax = ymin;
208 double zmax = zmin;
209 const unsigned int nVertices = element.type == 2 ? 3 : 4;
210 for (unsigned int j = 0; j < nVertices; ++j) {
211 const Vertex& vj = m_vertices[element.vertex[j]];
212 if (vj.x < xmin) xmin = vj.x;
213 if (vj.x > xmax) xmax = vj.x;
214 if (vj.y < ymin) ymin = vj.y;
215 if (vj.y > ymax) ymax = vj.y;
216 if (vj.z < zmin) zmin = vj.z;
217 if (vj.z > zmax) zmax = vj.z;
218 if (vj.p < m_pMin) m_pMin = vj.p;
219 if (vj.p > m_pMax) m_pMax = vj.p;
220 }
221 constexpr double tol = 1.e-6;
222 element.xmin = xmin - tol;
223 element.xmax = xmax + tol;
224 element.ymin = ymin - tol;
225 element.ymax = ymax + tol;
226 element.zmin = zmin - tol;
227 element.zmax = zmax + tol;
228 m_xMinBB = std::min(m_xMinBB, xmin);
229 m_xMaxBB = std::max(m_xMaxBB, xmax);
230 m_yMinBB = std::min(m_yMinBB, ymin);
231 m_yMaxBB = std::max(m_yMaxBB, ymax);
232 m_zMinBB = std::min(m_zMinBB, zmin);
233 m_zMaxBB = std::max(m_zMaxBB, zmax);
234 }
235
236 std::cout << m_className << "::Initialise:\n"
237 << " Bounding box:\n"
238 << " " << m_xMinBB << " < x [cm] < " << m_xMaxBB << "\n"
239 << " " << m_yMinBB << " < y [cm] < " << m_yMaxBB << "\n"
240 << " " << m_zMinBB << " < z [cm] < " << m_zMaxBB << "\n"
241 << " Voltage range:\n"
242 << " " << m_pMin << " < V < " << m_pMax << "\n";
243
244 bool ok = true;
245
246 // Count the number of elements belonging to a region.
247 const int nRegions = m_regions.size();
248 std::vector<unsigned int> nElementsRegion(nRegions, 0);
249
250 // Count the different element shapes.
251 unsigned int nTriangles = 0;
252 unsigned int nTetrahedra = 0;
253 unsigned int nOtherShapes = 0;
254
255 // Check if there are elements which are not part of any region.
256 unsigned int nLoose = 0;
257 std::vector<int> looseElements;
258
259 // Check if there are degenerate elements.
260 unsigned int nDegenerate = 0;
261 std::vector<int> degenerateElements;
262
263 for (unsigned int i = 0; i < nElements; ++i) {
264 const Element& element = m_elements[i];
265 if (element.type == 2) {
266 ++nTriangles;
267 if (element.vertex[0] == element.vertex[1] ||
268 element.vertex[1] == element.vertex[2] ||
269 element.vertex[2] == element.vertex[0]) {
270 degenerateElements.push_back(i);
271 ++nDegenerate;
272 }
273 } else if (element.type == 5) {
274 if (element.vertex[0] == element.vertex[1] ||
275 element.vertex[0] == element.vertex[2] ||
276 element.vertex[0] == element.vertex[3] ||
277 element.vertex[1] == element.vertex[2] ||
278 element.vertex[1] == element.vertex[3] ||
279 element.vertex[2] == element.vertex[3]) {
280 degenerateElements.push_back(i);
281 ++nDegenerate;
282 }
283 ++nTetrahedra;
284 } else {
285 // Other shapes should not occur, since they were excluded in LoadGrid.
286 ++nOtherShapes;
287 }
288 if (element.region >= 0 && element.region < nRegions) {
289 ++nElementsRegion[element.region];
290 } else {
291 looseElements.push_back(i);
292 ++nLoose;
293 }
294 }
295
296 if (nDegenerate > 0) {
297 std::cerr << m_className << "::Initialise:\n"
298 << " The following elements are degenerate:\n";
299 for (unsigned int i = 0; i < nDegenerate; ++i) {
300 std::cerr << " " << degenerateElements[i] << "\n";
301 }
302 ok = false;
303 }
304
305 if (nLoose > 0) {
306 std::cerr << m_className << "::Initialise:\n"
307 << " The following elements are not part of any region:\n";
308 for (unsigned int i = 0; i < nLoose; ++i) {
309 std::cerr << " " << looseElements[i] << "\n";
310 }
311 ok = false;
312 }
313
314 std::cout << m_className << "::Initialise:\n"
315 << " Number of regions: " << nRegions << "\n";
316 for (int i = 0; i < nRegions; ++i) {
317 std::cout << " " << i << ": " << m_regions[i].name << ", "
318 << nElementsRegion[i] << " elements\n";
319 }
320
321 std::cout << " Number of elements: " << nElements << "\n";
322 if (nTriangles > 0) {
323 std::cout << " " << nTriangles << " triangles\n";
324 }
325 if (nTetrahedra > 0) {
326 std::cout << " " << nTetrahedra << " tetrahedra\n";
327 }
328 if (nOtherShapes > 0) {
329 std::cerr << " " << nOtherShapes << " elements of unknown type\n"
330 << " Program bug!\n";
331 m_ready = false;
332 Cleanup();
333 return false;
334 }
335 if (m_debug) {
336 // For each element, print the indices of the constituting vertices.
337 for (unsigned int i = 0; i < nElements; ++i) {
338 const Element& element = m_elements[i];
339 if (element.type == 2) {
340 std::cout << " " << i << ": " << element.vertex[0] << " "
341 << element.vertex[1] << " " << element.vertex[2]
342 << " (triangle, region " << element.region << ")\n";
343 } else if (element.type == 5) {
344 std::cout << " " << i << ": " << element.vertex[0] << " "
345 << element.vertex[1] << " " << element.vertex[2] << " "
346 << element.vertex[3] << " (tetrahedron, region "
347 << element.region << ")\n";
348 }
349 }
350 }
351
352 const unsigned int nVertices = m_vertices.size();
353 std::cout << " Number of vertices: " << nVertices << "\n";
354 if (m_debug) {
355 for (unsigned int i = 0; i < nVertices; ++i) {
356 const Vertex& vi = m_vertices[i];
357 std::cout << " " << i << ": (x, y, z) = (" << vi.x << ", " << vi.y
358 << ", " << vi.z << "), V = " << vi.p << "\n";
359 }
360 }
361
362 // Find adjacent elements.
363 std::cout << m_className << "::Initialise:\n"
364 << " Looking for neighbouring elements. Be patient...\n";
365 FindNeighbours();
366
367 if (!ok) {
368 m_ready = false;
369 Cleanup();
370 return false;
371 }
372
373 m_ready = true;
374 UpdatePeriodicity();
375 std::cout << m_className << "::Initialise: Initialisation finished.\n";
376 return true;
377}
378
379bool ComponentTcad3d::SetWeightingField(const std::string& datfile1,
380 const std::string& datfile2,
381 const double dv) {
382
383 if (!m_ready) {
384 std::cerr << m_className << "::SetWeightingField:\n"
385 << " Mesh is not available. Call Initialise first.\n";
386 return false;
387 }
388 if (dv < Small) {
389 std::cerr << m_className << "::SetWeightingField:\n"
390 << " Voltage difference must be > 0.\n";
391 return false;
392 }
393 const double s = 1. / dv;
394
395 m_wf.clear();
396 m_wp.clear();
397 // Load first the field/potential at nominal bias.
398 std::vector<std::array<double, 3> > wf1;
399 std::vector<double> wp1;
400 if (!LoadWeightingField(datfile1, wf1, wp1)) {
401 std::cerr << m_className << "::SetWeightingField:\n"
402 << " Could not import data from " << datfile1 << ".\n";
403 return false;
404 }
405 // Then load the field/potential for the configuration with the potential
406 // at the electrode to be read out increased by small voltage dv.
407 std::vector<std::array<double, 3> > wf2;
408 std::vector<double> wp2;
409 if (!LoadWeightingField(datfile2, wf2, wp2)) {
410 std::cerr << m_className << "::SetWeightingField:\n"
411 << " Could not import data from " << datfile2 << ".\n";
412 return false;
413 }
414 const unsigned int nVertices = m_vertices.size();
415 bool foundField = true;
416 if (wf1.size() != nVertices || wf2.size() != nVertices) {
417 foundField = false;
418 std::cerr << m_className << "::SetWeightingField:\n"
419 << " Could not load electric field values.\n";
420 }
421 bool foundPotential = true;
422 if (wp1.size() != nVertices || wp2.size() != nVertices) {
423 foundPotential = false;
424 std::cerr << m_className << "::SetWeightingField:\n"
425 << " Could not load electrostatic potentials.\n";
426 }
427 if (!foundField && !foundPotential) return false;
428 if (foundField) {
429 m_wf.assign(nVertices, {0., 0., 0.});
430 for (unsigned int i = 0; i < nVertices; ++i) {
431 for (unsigned int j = 0; j < 3; ++j) {
432 m_wf[i][j] = (wf2[i][j] - wf1[i][j]) * s;
433 }
434 }
435 }
436 if (foundPotential) {
437 m_wp.assign(nVertices, 0.);
438 for (unsigned int i = 0; i < nVertices; ++i) {
439 m_wp[i] = (wp2[i] - wp1[i]) * s;
440 }
441 }
442 return true;
443}
444
445void ComponentTcad3d::FindNeighbours() {
446 const unsigned int nElements = m_elements.size();
447 std::vector<std::vector<bool> > adjacent(nElements,
448 std::vector<bool>(nElements, false));
449
450 constexpr double tol = 5.e-4;
451 for (unsigned int i = 0; i < nElements; ++i) {
452 const Element& ei = m_elements[i];
453 for (unsigned int j = 0; j < nElements; ++j) {
454 if (i == j || adjacent[i][j]) continue;
455 const Element& ej = m_elements[j];
456 if (ei.xmin > ej.xmax + tol || ei.xmax < ej.xmin - tol) continue;
457 if (ei.ymin > ej.ymax + tol || ei.ymax < ej.ymin - tol) continue;
458 if (ei.zmin > ej.zmax + tol || ei.zmax < ej.zmin - tol) continue;
459 for (unsigned int m = 0; m < nMaxVertices; ++m) {
460 if (ei.vertex[m] < 0) break;
461 for (unsigned int n = 0; n < nMaxVertices; ++n) {
462 if (ei.vertex[n] < 0) break;
463 if (ei.vertex[m] == ej.vertex[n]) {
464 adjacent[i][j] = adjacent[j][i] = true;
465 break;
466 }
467 }
468 if (adjacent[i][j]) break;
469 }
470 }
471 }
472
473 for (unsigned int i = 0; i < nElements; ++i) {
474 m_elements[i].neighbours.clear();
475 for (unsigned int j = 0; j < nElements; ++j) {
476 if (adjacent[i][j]) {
477 m_elements[i].neighbours.push_back(j);
478 }
479 }
480 }
481}
482
483bool ComponentTcad3d::GetBoundingBox(double& xmin, double& ymin, double& zmin,
484 double& xmax, double& ymax, double& zmax) {
485 if (!m_ready) return false;
486 xmin = m_xMinBB;
487 ymin = m_yMinBB;
488 zmin = m_zMinBB;
489 xmax = m_xMaxBB;
490 ymax = m_yMaxBB;
491 zmax = m_zMaxBB;
492 if (m_periodic[0] || m_mirrorPeriodic[0]) {
493 xmin = -INFINITY;
494 xmax = +INFINITY;
495 }
496 if (m_periodic[1] || m_mirrorPeriodic[1]) {
497 ymin = -INFINITY;
498 ymax = +INFINITY;
499 }
500 if (m_periodic[2] || m_mirrorPeriodic[2]) {
501 zmin = -INFINITY;
502 zmax = +INFINITY;
503 }
504 return true;
505}
506
507bool ComponentTcad3d::GetVoltageRange(double& vmin, double& vmax) {
508 if (!m_ready) return false;
509 vmin = m_pMin;
510 vmax = m_pMax;
511 return true;
512}
513
515 // Do not proceed if not properly initialised.
516 if (!m_ready) {
517 std::cerr << m_className << "::PrintRegions:\n"
518 << " Field map not yet initialised.\n";
519 return;
520 }
521
522 if (m_regions.empty()) {
523 std::cerr << m_className << "::PrintRegions:\n"
524 << " No regions are currently defined.\n";
525 return;
526 }
527
528 const unsigned int nRegions = m_regions.size();
529 std::cout << m_className << "::PrintRegions:\n"
530 << " Currently " << nRegions << " regions are defined.\n"
531 << " Index Name Medium\n";
532 for (unsigned int i = 0; i < nRegions; ++i) {
533 std::cout << " " << i << " " << m_regions[i].name;
534 if (!m_regions[i].medium) {
535 std::cout << " none ";
536 } else {
537 std::cout << " " << m_regions[i].medium->GetName();
538 }
539 if (m_regions[i].drift) {
540 std::cout << " (active region)\n";
541 } else {
542 std::cout << "\n";
543 }
544 }
545}
546
547void ComponentTcad3d::GetRegion(const unsigned int i, std::string& name,
548 bool& active) const {
549 if (i >= m_regions.size()) {
550 std::cerr << m_className << "::GetRegion: Index out of range.\n";
551 return;
552 }
553 name = m_regions[i].name;
554 active = m_regions[i].drift;
555}
556
557void ComponentTcad3d::SetDriftRegion(const unsigned int i) {
558 if (i >= m_regions.size()) {
559 std::cerr << m_className << "::SetDriftRegion: Index out of range.\n";
560 return;
561 }
562 m_regions[i].drift = true;
563}
564
565void ComponentTcad3d::UnsetDriftRegion(const unsigned int i) {
566 if (i >= m_regions.size()) {
567 std::cerr << m_className << "::UnsetDriftRegion: Index out of range.\n";
568 return;
569 }
570 m_regions[i].drift = false;
571}
572
573void ComponentTcad3d::SetMedium(const unsigned int i, Medium* medium) {
574 if (i >= m_regions.size()) {
575 std::cerr << m_className << "::SetMedium: Index out of range.\n";
576 return;
577 }
578
579 if (!medium) {
580 std::cerr << m_className << "::SetMedium: Null pointer.\n";
581 return;
582 }
583 m_regions[i].medium = medium;
584}
585
586bool ComponentTcad3d::GetMedium(const unsigned int i, Medium*& m) const {
587 if (i >= m_regions.size()) {
588 std::cerr << m_className << "::GetMedium: Index out of range.\n";
589 return false;
590 }
591
592 m = m_regions[i].medium;
593 if (!m) return false;
594 return true;
595}
596
597unsigned int ComponentTcad3d::FindElement(
598 const double x, const double y, const double z,
599 std::array<double, nMaxVertices>& w) const {
600
601 w.fill(0.);
602 if (m_lastElement >= 0) {
603 // Check if the point is still located in the previously found element.
604 const Element& last = m_elements[m_lastElement];
605 if (x >= last.xmin && x <= last.xmax && y >= last.ymin && y <= last.ymax &&
606 z >= last.zmin && z <= last.zmax) {
607 if (CheckElement(x, y, z, last, w)) return m_lastElement;
608 }
609 // The point is not in the previous element.
610 // Check the adjacent elements.
611 const unsigned int nNeighbours = last.neighbours.size();
612 for (unsigned int i = 0; i < nNeighbours; ++i) {
613 const Element& element = m_elements[last.neighbours[i]];
614 if (x < element.xmin || x > element.xmax || y < element.ymin ||
615 y > element.ymax || z < element.zmin || z > element.zmax)
616 continue;
617 if (CheckElement(x, y, z, element, w)) continue;
618 return last.neighbours[i];
619 }
620 }
621
622 // The point is not in the previous element nor in the adjacent ones.
623 // We have to loop over all elements.
624 const unsigned int nElements = m_elements.size();
625 for (unsigned int i = 0; i < nElements; ++i) {
626 const Element& element = m_elements[i];
627 if (x < element.xmin || x > element.xmax || y < element.ymin ||
628 y > element.ymax || z < element.zmin || z > element.zmax)
629 continue;
630 if (CheckElement(x, y, z, element, w)) return i;
631 }
632
633 if (m_debug) {
634 std::cerr << m_className << "::FindElement:\n"
635 << " Point (" << x << ", " << y << ", " << z
636 << ") is outside the mesh.\n";
637 }
638 return nElements;
639}
640
641bool ComponentTcad3d::GetElement(const unsigned int i, double& vol,
642 double& dmin, double& dmax, int& type) const {
643 if (i >= m_elements.size()) {
644 std::cerr << m_className << "::GetElement: Index out of range.\n";
645 return false;
646 }
647
648 const Element& element = m_elements[i];
649 if (element.type == 2) {
650 // Triangle
651 const Vertex& v0 = m_vertices[element.vertex[0]];
652 const Vertex& v1 = m_vertices[element.vertex[1]];
653 const Vertex& v2 = m_vertices[element.vertex[2]];
654 const double vx =
655 (v1.y - v0.y) * (v2.z - v0.z) - (v1.z - v0.z) * (v2.y - v0.y);
656 const double vy =
657 (v1.z - v0.z) * (v2.x - v0.x) - (v1.x - v0.x) * (v2.z - v0.z);
658 const double vz =
659 (v1.x - v0.x) * (v2.y - v0.y) - (v1.y - v0.y) * (v2.x - v0.x);
660 vol = sqrt(vx * vx + vy * vy + vz * vz);
661 const double a =
662 sqrt(pow(v1.x - v0.x, 2) + pow(v1.y - v0.y, 2) + pow(v1.z - v0.z, 2));
663 const double b =
664 sqrt(pow(v2.x - v0.x, 2) + pow(v2.y - v0.y, 2) + pow(v2.z - v0.z, 2));
665 const double c =
666 sqrt(pow(v1.x - v2.x, 2) + pow(v1.y - v2.y, 2) + pow(v1.z - v2.z, 2));
667 dmin = dmax = a;
668 if (b < dmin) dmin = b;
669 if (c < dmin) dmin = c;
670 if (b > dmax) dmax = b;
671 if (c > dmax) dmax = c;
672 } else if (element.type == 5) {
673 // Tetrahedron
674 const Vertex& v0 = m_vertices[element.vertex[0]];
675 const Vertex& v1 = m_vertices[element.vertex[1]];
676 const Vertex& v2 = m_vertices[element.vertex[2]];
677 const Vertex& v3 = m_vertices[element.vertex[3]];
678 vol = fabs((v3.x - v0.x) * ((v1.y - v0.y) * (v2.z - v0.z) -
679 (v2.y - v0.y) * (v1.z - v0.z)) +
680 (v3.y - v0.y) * ((v1.z - v0.z) * (v2.x - v0.x) -
681 (v2.z - v0.z) * (v1.x - v0.x)) +
682 (v3.z - v0.z) * ((v1.x - v0.x) * (v2.y - v0.y) -
683 (v3.x - v0.x) * (v1.y - v0.y))) /
684 6.;
685 // Loop over all pairs of m_vertices.
686 for (int j = 0; j < nMaxVertices - 1; ++j) {
687 const Vertex& vj = m_vertices[element.vertex[j]];
688 for (int k = j + 1; k < nMaxVertices; ++k) {
689 const Vertex& vk = m_vertices[element.vertex[k]];
690 // Compute distance.
691 const double dist = sqrt(pow(vj.x - vk.x, 2) + pow(vj.y - vk.y, 2) +
692 pow(vj.z - vk.z, 2));
693 if (k == 1) {
694 dmin = dmax = dist;
695 } else {
696 if (dist < dmin) dmin = dist;
697 if (dist > dmax) dmax = dist;
698 }
699 }
700 }
701 } else {
702 std::cerr << m_className << "::GetElement:\n"
703 << " Unexpected element type (" << type << ").\n";
704 return false;
705 }
706 return true;
707}
708
709bool ComponentTcad3d::GetElement(const unsigned int i, double& vol,
710 double& dmin, double& dmax, int& type,
711 int& node1, int& node2, int& node3, int& node4,
712 int& node5, int& node6, int& node7,
713 int& reg) const {
714 if (!GetElement(i, vol, dmin, dmax, type)) return false;
715 const Element& element = m_elements[i];
716 node1 = element.vertex[0];
717 node2 = element.vertex[1];
718 node3 = element.vertex[2];
719 node4 = element.vertex[3];
720 node5 = element.vertex[4];
721 node6 = element.vertex[5];
722 node7 = element.vertex[6];
723 reg = element.region;
724 return true;
725}
726
727bool ComponentTcad3d::GetNode(const unsigned int i, double& x, double& y,
728 double& z, double& v, double& ex, double& ey,
729 double& ez) const {
730 if (i >= m_vertices.size()) {
731 std::cerr << m_className << "::GetNode: Index out of range.\n";
732 return false;
733 }
734
735 const Vertex& vi = m_vertices[i];
736 x = vi.x;
737 y = vi.y;
738 z = vi.z;
739 v = vi.p;
740 ex = vi.ex;
741 ey = vi.ey;
742 ez = vi.ez;
743 return true;
744}
745
746bool ComponentTcad3d::LoadData(const std::string& datafilename) {
747 std::ifstream datafile;
748 datafile.open(datafilename.c_str(), std::ios::in);
749 if (!datafile) {
750 std::cerr << m_className << "::LoadData:\n"
751 << " Could not open file " << datafilename << ".\n";
752 return false;
753 }
754
755 const unsigned int nVertices = m_vertices.size();
756 for (unsigned int i = 0; i < nVertices; ++i) {
757 m_vertices[i].p = 0.;
758 m_vertices[i].ex = 0.;
759 m_vertices[i].ey = 0.;
760 m_vertices[i].ez = 0.;
761 }
762 while (!datafile.fail()) {
763 // Read one line.
764 std::string line;
765 std::getline(datafile, line);
766 // Strip white space from beginning of line.
767 ltrim(line);
768 // Find data section.
769 if (line.substr(0, 8) != "function") continue;
770 // Read type of data set.
771 const std::string::size_type pEq = line.find('=');
772 if (pEq == std::string::npos) {
773 // No "=" found.
774 std::cerr << m_className << "::LoadData:\n"
775 << " Error reading file " << datafilename << ".\n"
776 << " Line:\n " << line << "\n";
777 datafile.close();
778 Cleanup();
779 return false;
780 }
781 line = line.substr(pEq + 1);
782 std::string dataset;
783 std::istringstream data;
784 data.str(line);
785 data >> dataset;
786 data.clear();
787 if (dataset == "ElectrostaticPotential") {
788 if (!ReadDataset(datafile, dataset)) return false;
789 } else if (dataset == "ElectricField") {
790 if (!ReadDataset(datafile, dataset)) return false;
791 }
792 }
793 if (datafile.fail() && !datafile.eof()) {
794 std::cerr << m_className << "::LoadData:\n"
795 << " Error reading file " << datafilename << "\n";
796 datafile.close();
797 Cleanup();
798 return false;
799 }
800 datafile.close();
801 return true;
802}
803
804bool ComponentTcad3d::ReadDataset(std::ifstream& datafile,
805 const std::string& dataset) {
806 if (!datafile.is_open()) return false;
807 enum DataSet { ElectrostaticPotential, EField, Unknown };
808 DataSet ds = Unknown;
809 if (dataset == "ElectrostaticPotential") {
810 ds = ElectrostaticPotential;
811 } else if (dataset == "ElectricField") {
812 ds = EField;
813 } else {
814 std::cerr << m_className << "::ReadDataset:\n"
815 << " Unexpected dataset " << dataset << ".\n";
816 return false;
817 }
818 bool isVector = false;
819 if (ds == EField) {
820 isVector = true;
821 }
822
823 std::string line;
824 std::getline(datafile, line);
825 std::getline(datafile, line);
826 std::getline(datafile, line);
827 std::getline(datafile, line);
828 // Get the region name (given in brackets).
829 std::string::size_type bra = line.find('[');
830 std::string::size_type ket = line.find(']');
831 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
832 std::cerr << m_className << "::ReadDataset:\n"
833 << " Cannot extract region name.\n"
834 << " Line:\n " << line << "\n";
835 datafile.close();
836 Cleanup();
837 return false;
838 }
839 line = line.substr(bra + 1, ket - bra - 1);
840 std::string name;
841 std::istringstream data;
842 data.str(line);
843 data >> name;
844 data.clear();
845 // Check if the region name matches one from the mesh file.
846 const int index = FindRegion(name);
847 if (index < 0) {
848 std::cerr << m_className << "::ReadDataset:\n"
849 << " Unknown region " << name << ".\n";
850 datafile.close();
851 Cleanup();
852 return false;
853 }
854 // Get the number of values.
855 std::getline(datafile, line);
856 bra = line.find('(');
857 ket = line.find(')');
858 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
859 std::cerr << m_className << "::ReadDataset:\n"
860 << " Cannot extract number of values to be read.\n"
861 << " Line:\n " << line << "\n";
862 datafile.close();
863 Cleanup();
864 return false;
865 }
866 line = line.substr(bra + 1, ket - bra - 1);
867 int nValues;
868 data.str(line);
869 data >> nValues;
870 if (isVector) nValues /= 3;
871 // Mark the vertices belonging to this region.
872 const unsigned int nVertices = m_vertices.size();
873 std::vector<bool> isInRegion(nVertices, false);
874 const unsigned int nElements = m_elements.size();
875 for (unsigned int j = 0; j < nElements; ++j) {
876 if (m_elements[j].region != index) continue;
877 for (int k = 0; k <= m_elements[j].type; ++k) {
878 isInRegion[m_elements[j].vertex[k]] = true;
879 }
880 }
881
882 unsigned int ivertex = 0;
883 for (int j = 0; j < nValues; ++j) {
884 // Read the next value.
885 double val1, val2, val3;
886 if (isVector) {
887 datafile >> val1 >> val2 >> val3;
888 } else {
889 datafile >> val1;
890 }
891 // Find the next vertex belonging to the region.
892 while (ivertex < nVertices) {
893 if (isInRegion[ivertex]) break;
894 ++ivertex;
895 }
896 // Check if there is a mismatch between the number of m_vertices
897 // and the number of potential values.
898 if (ivertex >= nVertices) {
899 std::cerr << m_className << "::ReadDataset:\n"
900 << " Dataset " << dataset << " has more values than "
901 << "there are vertices in region " << name << "\n";
902 datafile.close();
903 Cleanup();
904 return false;
905 }
906 switch (ds) {
907 case ElectrostaticPotential:
908 m_vertices[ivertex].p = val1;
909 break;
910 case EField:
911 m_vertices[ivertex].ex = val1;
912 m_vertices[ivertex].ey = val2;
913 m_vertices[ivertex].ez = val3;
914 break;
915 default:
916 std::cerr << m_className << "::ReadDataset:\n"
917 << " Unexpected dataset (" << ds << "). Program bug!\n";
918 datafile.close();
919 Cleanup();
920 return false;
921 }
922 ++ivertex;
923 }
924 return true;
925}
926
927bool ComponentTcad3d::LoadWeightingField(const std::string& datafilename,
928 std::vector<std::array<double, 3> >& wf, std::vector<double>& wp) {
929
930 std::ifstream datafile;
931 datafile.open(datafilename.c_str(), std::ios::in);
932 if (!datafile) {
933 std::cerr << m_className << "::LoadWeightingField:\n"
934 << " Could not open file " << datafilename << ".\n";
935 return false;
936 }
937
938 const unsigned int nVertices = m_vertices.size();
939 bool ok = true;
940 while (!datafile.fail()) {
941 // Read one line.
942 std::string line;
943 std::getline(datafile, line);
944 // Strip white space from beginning of line.
945 ltrim(line);
946 // Find data section.
947 if (line.substr(0, 8) != "function") continue;
948 // Read type of data set.
949 const std::string::size_type pEq = line.find('=');
950 if (pEq == std::string::npos) {
951 // No "=" found.
952 std::cerr << m_className << "::LoadWeightingField:\n"
953 << " Error reading file " << datafilename << ".\n"
954 << " Line:\n " << line << "\n";
955 datafile.close();
956 return false;
957 }
958 line = line.substr(pEq + 1);
959 std::string dataset;
960 std::istringstream data;
961 data.str(line);
962 data >> dataset;
963 data.clear();
964 if (dataset != "ElectrostaticPotential" &&
965 dataset != "ElectricField") continue;
966 bool field = false;
967 if (dataset == "ElectricField") {
968 wf.assign(nVertices, {0., 0., 0.});
969 field = true;
970 } else {
971 wp.assign(nVertices, 0.);
972 }
973 std::getline(datafile, line);
974 std::getline(datafile, line);
975 std::getline(datafile, line);
976 std::getline(datafile, line);
977 // Get the region name (given in brackets).
978 auto bra = line.find('[');
979 auto ket = line.find(']');
980 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
981 std::cerr << m_className << "::LoadWeightingField:\n"
982 << " Cannot extract region name.\n"
983 << " Line:\n " << line << "\n";
984 ok = false;
985 break;
986 }
987 line = line.substr(bra + 1, ket - bra - 1);
988 std::string name;
989 data.str(line);
990 data >> name;
991 data.clear();
992 // Check if the region name matches one from the mesh file.
993 const int index = FindRegion(name);
994 if (index < 0) {
995 std::cerr << m_className << "::LoadWeightingField:\n"
996 << " Unknown region " << name << ".\n";
997 ok = false;
998 break;
999 }
1000 // Get the number of values.
1001 std::getline(datafile, line);
1002 bra = line.find('(');
1003 ket = line.find(')');
1004 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1005 std::cerr << m_className << "::LoadWeightingField:\n"
1006 << " Cannot extract number of values to be read.\n"
1007 << " Line:\n " << line << "\n";
1008 ok = false;
1009 break;
1010 }
1011 line = line.substr(bra + 1, ket - bra - 1);
1012 int nValues;
1013 data.str(line);
1014 data >> nValues;
1015 if (field) nValues /= 3;
1016 // Mark the vertices belonging to this region.
1017 std::vector<bool> isInRegion(nVertices, false);
1018 const unsigned int nElements = m_elements.size();
1019 for (unsigned int j = 0; j < nElements; ++j) {
1020 if (m_elements[j].region != index) continue;
1021 for (int k = 0; k <= m_elements[j].type; ++k) {
1022 isInRegion[m_elements[j].vertex[k]] = true;
1023 }
1024 }
1025 unsigned int ivertex = 0;
1026 for (int j = 0; j < nValues; ++j) {
1027 // Read the next value.
1028 double val1, val2, val3;
1029 if (field) {
1030 datafile >> val1 >> val2 >> val3;
1031 } else {
1032 datafile >> val1;
1033 }
1034 // Find the next vertex belonging to the region.
1035 while (ivertex < nVertices) {
1036 if (isInRegion[ivertex]) break;
1037 ++ivertex;
1038 }
1039 // Check if there is a mismatch between the number of m_vertices
1040 // and the number of potential values.
1041 if (ivertex >= nVertices) {
1042 std::cerr << m_className << "::LoadWeightingField:\n"
1043 << " Dataset " << dataset << " has more values than "
1044 << "there are vertices in region " << name << "\n";
1045 ok = false;
1046 break;
1047 }
1048 if (field) {
1049 wf[ivertex] = {val1, val2, val3};
1050 } else {
1051 wp[ivertex] = val1;
1052 }
1053 ++ivertex;
1054 }
1055 }
1056
1057 if (!ok || (datafile.fail() && !datafile.eof())) {
1058 std::cerr << m_className << "::LoadWeightingField:\n"
1059 << " Error reading file " << datafilename << "\n";
1060 datafile.close();
1061 return false;
1062 }
1063 datafile.close();
1064 return true;
1065}
1066
1067bool ComponentTcad3d::LoadGrid(const std::string& gridfilename) {
1068 // Open the file containing the mesh description.
1069 std::ifstream gridfile;
1070 gridfile.open(gridfilename.c_str(), std::ios::in);
1071 if (!gridfile) {
1072 std::cerr << m_className << "::LoadGrid:\n"
1073 << " Could not open file " << gridfilename << ".\n";
1074 return false;
1075 }
1076
1077 // Delete existing mesh information.
1078 Cleanup();
1079
1080 // Count line numbers.
1081 int iLine = 0;
1082
1083 // Get the number of regions.
1084 unsigned int nRegions = 0;
1085 while (!gridfile.fail()) {
1086 // Read one line.
1087 std::string line;
1088 std::getline(gridfile, line);
1089 ++iLine;
1090 // Strip white space from the beginning of the line.
1091 ltrim(line);
1092 // Find entry 'nb_regions'.
1093 if (line.substr(0, 10) != "nb_regions") continue;
1094 const std::string::size_type pEq = line.find('=');
1095 if (pEq == std::string::npos) {
1096 // No "=" sign found.
1097 std::cerr << m_className << "::LoadGrid:\n"
1098 << " Could not read number of regions.\n";
1099 Cleanup();
1100 gridfile.close();
1101 return false;
1102 }
1103 line = line.substr(pEq + 1);
1104 std::istringstream data;
1105 data.str(line);
1106 data >> nRegions;
1107 break;
1108 }
1109 if (gridfile.eof()) {
1110 // Reached end of file.
1111 std::cerr << m_className << "::LoadGrid:\n"
1112 << " Could not find entry 'nb_regions' in file\n"
1113 << " " << gridfilename << ".\n";
1114 Cleanup();
1115 gridfile.close();
1116 return false;
1117 } else if (gridfile.fail()) {
1118 // Error reading from the file.
1119 std::cerr << m_className << "::LoadGrid:\n"
1120 << " Error reading file " << gridfilename << " (line " << iLine
1121 << ").\n";
1122 Cleanup();
1123 gridfile.close();
1124 return false;
1125 }
1126 m_regions.resize(nRegions);
1127 for (unsigned int j = 0; j < nRegions; ++j) {
1128 m_regions[j].name = "";
1129 m_regions[j].drift = false;
1130 m_regions[j].medium = nullptr;
1131 }
1132
1133 if (m_debug) {
1134 std::cout << m_className << "::LoadGrid:\n"
1135 << " Found " << nRegions << " regions.\n";
1136 }
1137
1138 // Get the region names.
1139 while (!gridfile.fail()) {
1140 std::string line;
1141 std::getline(gridfile, line);
1142 ++iLine;
1143 ltrim(line);
1144 // Find entry 'regions'.
1145 if (line.substr(0, 7) != "regions") continue;
1146 // Get region names (given in brackets).
1147 const std::string::size_type bra = line.find('[');
1148 const std::string::size_type ket = line.find(']');
1149 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1150 // No closed brackets [].
1151 std::cerr << m_className << "::LoadGrid:\n"
1152 << " Could not read region names.\n";
1153 Cleanup();
1154 gridfile.close();
1155 return false;
1156 }
1157 line = line.substr(bra + 1, ket - bra - 1);
1158 std::istringstream data;
1159 data.str(line);
1160 for (unsigned int j = 0; j < nRegions; ++j) {
1161 data >> m_regions[j].name;
1162 data.clear();
1163 // Assume by default that all regions are active.
1164 m_regions[j].drift = true;
1165 m_regions[j].medium = 0;
1166 }
1167 break;
1168 }
1169 if (gridfile.eof()) {
1170 // Reached end of file.
1171 std::cerr << m_className << "::LoadGrid:\n"
1172 << " Could not find entry 'regions' in file\n"
1173 << " " << gridfilename << ".\n";
1174 Cleanup();
1175 gridfile.close();
1176 return false;
1177 } else if (gridfile.fail()) {
1178 // Error reading from the file.
1179 std::cerr << m_className << "::LoadGrid:\n"
1180 << " Error reading file " << gridfilename << " (line " << iLine
1181 << ").\n";
1182 Cleanup();
1183 gridfile.close();
1184 return false;
1185 }
1186
1187 // Get the vertices.
1188 unsigned int nVertices = 0;
1189 while (!gridfile.fail()) {
1190 std::string line;
1191 std::getline(gridfile, line);
1192 ++iLine;
1193 ltrim(line);
1194 // Find section 'Vertices'.
1195 if (line.substr(0, 8) != "Vertices") continue;
1196 // Get number of vertices (given in brackets).
1197 const std::string::size_type bra = line.find('(');
1198 const std::string::size_type ket = line.find(')');
1199 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1200 // No closed brackets [].
1201 std::cerr << m_className << "::LoadGrid:\n"
1202 << " Could not read number of vertices.\n";
1203 Cleanup();
1204 gridfile.close();
1205 return false;
1206 }
1207 line = line.substr(bra + 1, ket - bra - 1);
1208 std::istringstream data;
1209 data.str(line);
1210 data >> nVertices;
1211 m_vertices.resize(nVertices);
1212 // Get the coordinates of this vertex.
1213 for (unsigned int j = 0; j < nVertices; ++j) {
1214 gridfile >> m_vertices[j].x >> m_vertices[j].y >> m_vertices[j].z;
1215 // Change units from micron to cm.
1216 m_vertices[j].x *= 1.e-4;
1217 m_vertices[j].y *= 1.e-4;
1218 m_vertices[j].z *= 1.e-4;
1219 }
1220 iLine += nVertices - 1;
1221 break;
1222 }
1223 if (gridfile.eof()) {
1224 std::cerr << m_className << "::LoadGrid:\n"
1225 << " Could not find section 'Vertices' in file\n"
1226 << " " << gridfilename << ".\n";
1227 Cleanup();
1228 gridfile.close();
1229 return false;
1230 } else if (gridfile.fail()) {
1231 std::cerr << m_className << "::LoadGrid:\n"
1232 << " Error reading file " << gridfilename << " (line " << iLine
1233 << ").\n";
1234 Cleanup();
1235 gridfile.close();
1236 return false;
1237 }
1238
1239 // Get the "edges" (lines connecting two vertices).
1240 int nEdges = 0;
1241 // Temporary arrays for storing edge points.
1242 std::vector<int> edgeP1;
1243 std::vector<int> edgeP2;
1244 while (!gridfile.fail()) {
1245 std::string line;
1246 std::getline(gridfile, line);
1247 ++iLine;
1248 ltrim(line);
1249 // Find section 'Edges'.
1250 if (line.substr(0, 5) != "Edges") continue;
1251 // Get the number of edges (given in brackets).
1252 const std::string::size_type bra = line.find('(');
1253 const std::string::size_type ket = line.find(')');
1254 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1255 // No closed brackets ()
1256 std::cerr << m_className << "::LoadGrid:\n"
1257 << " Could not read number of edges.\n";
1258 Cleanup();
1259 gridfile.close();
1260 return false;
1261 }
1262 line = line.substr(bra + 1, ket - bra - 1);
1263 std::istringstream data;
1264 data.str(line);
1265 data >> nEdges;
1266 edgeP1.resize(nEdges);
1267 edgeP2.resize(nEdges);
1268 // Get the indices of the two endpoints.
1269 for (int j = 0; j < nEdges; ++j) {
1270 gridfile >> edgeP1[j] >> edgeP2[j];
1271 }
1272 iLine += nEdges - 1;
1273 break;
1274 }
1275 if (gridfile.eof()) {
1276 std::cerr << m_className << "::LoadGrid:\n"
1277 << " Could not find section 'Edges' in file\n"
1278 << " " << gridfilename << ".\n";
1279 Cleanup();
1280 gridfile.close();
1281 return false;
1282 } else if (gridfile.fail()) {
1283 std::cerr << m_className << "::LoadGrid:\n"
1284 << " Error reading file " << gridfilename << " (line " << iLine
1285 << ").\n";
1286 Cleanup();
1287 gridfile.close();
1288 return false;
1289 }
1290
1291 for (int i = nEdges; i--;) {
1292 // Make sure the indices of the edge endpoints are not out of range.
1293 if (edgeP1[i] < 0 || edgeP1[i] >= (int)nVertices || edgeP2[i] < 0 ||
1294 edgeP2[i] >= (int)nVertices) {
1295 std::cerr << m_className << "::LoadGrid:\n"
1296 << " Vertex index of edge " << i << " out of range.\n";
1297 Cleanup();
1298 gridfile.close();
1299 return false;
1300 }
1301 // Make sure the edge is non-degenerate.
1302 if (edgeP1[i] == edgeP2[i]) {
1303 std::cerr << m_className << "::LoadGrid:\n"
1304 << " Edge " << i << " is degenerate.\n";
1305 Cleanup();
1306 gridfile.close();
1307 return false;
1308 }
1309 }
1310
1311 // Get the "faces".
1312 int nFaces = 0;
1313 std::vector<Face> faces;
1314 while (!gridfile.fail()) {
1315 std::string line;
1316 std::getline(gridfile, line);
1317 ++iLine;
1318 ltrim(line);
1319 // Find section 'Faces'.
1320 if (line.substr(0, 5) != "Faces") continue;
1321 // Get the number of faces (given in brackets).
1322 const std::string::size_type bra = line.find('(');
1323 const std::string::size_type ket = line.find(')');
1324 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1325 // No closed brackets ()
1326 std::cerr << m_className << "::LoadGrid:\n"
1327 << " Could not read number of faces.\n";
1328 Cleanup();
1329 gridfile.close();
1330 return false;
1331 }
1332 line = line.substr(bra + 1, ket - bra - 1);
1333 std::istringstream data;
1334 data.str(line);
1335 data >> nFaces;
1336 faces.resize(nFaces);
1337 // Get the indices of the edges constituting this face.
1338 for (int j = 0; j < nFaces; ++j) {
1339 gridfile >> faces[j].type;
1340 if (faces[j].type != 3 && faces[j].type != 4) {
1341 std::cerr << m_className << "::LoadGrid:\n"
1342 << " Face with index " << j
1343 << " has invalid number of edges, " << faces[j].type << ".\n";
1344 Cleanup();
1345 gridfile.close();
1346 return false;
1347 }
1348 for (int k = 0; k < faces[j].type; ++k) {
1349 gridfile >> faces[j].edge[k];
1350 }
1351 }
1352 iLine += nFaces - 1;
1353 break;
1354 }
1355 if (gridfile.eof()) {
1356 std::cerr << m_className << "::LoadGrid:\n"
1357 << " Could not find section 'Faces' in file\n"
1358 << " " << gridfilename << ".\n";
1359 Cleanup();
1360 gridfile.close();
1361 return false;
1362 } else if (gridfile.fail()) {
1363 std::cerr << m_className << "::LoadGrid:\n"
1364 << " Error reading file " << gridfilename << " (line " << iLine
1365 << ").\n";
1366 Cleanup();
1367 gridfile.close();
1368 return false;
1369 }
1370
1371 // Get the elements.
1372 int nElements = 0;
1373 while (!gridfile.fail()) {
1374 std::string line;
1375 std::getline(gridfile, line);
1376 ++iLine;
1377 ltrim(line);
1378 // Find section 'Elements'.
1379 if (line.substr(0, 8) != "Elements") continue;
1380 // Get number of elements (given in brackets).
1381 const std::string::size_type bra = line.find('(');
1382 const std::string::size_type ket = line.find(')');
1383 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1384 // No closed brackets ().
1385 std::cerr << m_className << "::LoadGrid:\n"
1386 << " Could not read number of elements.\n";
1387 Cleanup();
1388 gridfile.close();
1389 return false;
1390 }
1391 line = line.substr(bra + 1, ket - bra - 1);
1392 std::istringstream data;
1393 data.str(line);
1394 data >> nElements;
1395 data.clear();
1396 // Resize array of elements.
1397 m_elements.resize(nElements);
1398 // Get type and constituting edges of each element.
1399 for (int j = 0; j < nElements; ++j) {
1400 ++iLine;
1401 int type = 0;
1402 gridfile >> type;
1403 if (type == 2) {
1404 // Triangle
1405 int edge0, edge1, edge2;
1406 gridfile >> edge0 >> edge1 >> edge2;
1407 // Get the vertices.
1408 // Negative edge index means that the sequence of the two points
1409 // is supposed to be inverted.
1410 // The actual index is then given by "-index - 1".
1411 // For our purposes, the orientation does not matter.
1412 // Make sure the indices are not out of range.
1413 if (edge0 >= nEdges || -edge0 - 1 >= nEdges || edge1 >= nEdges ||
1414 -edge1 - 1 >= nEdges || edge2 >= nEdges || -edge2 - 1 >= nEdges) {
1415 std::cerr << m_className << "::LoadGrid:\n Error reading file "
1416 << gridfilename << " (line " << iLine << ").\n"
1417 << " Edge index out of range.\n";
1418 Cleanup();
1419 gridfile.close();
1420 return false;
1421 }
1422 if (edge0 < 0) edge0 = -edge0 - 1;
1423 if (edge1 < 0) edge1 = -edge1 - 1;
1424 m_elements[j].vertex[0] = edgeP1[edge0];
1425 m_elements[j].vertex[1] = edgeP2[edge0];
1426 if (edgeP1[edge1] != m_elements[j].vertex[0] &&
1427 edgeP1[edge1] != m_elements[j].vertex[1]) {
1428 m_elements[j].vertex[2] = edgeP1[edge1];
1429 } else {
1430 m_elements[j].vertex[2] = edgeP2[edge1];
1431 }
1432 } else if (type == 5) {
1433 // Tetrahedron
1434 // Get the faces.
1435 // Negative face index means that the sequence of the edges
1436 // is supposed to be inverted.
1437 // For our purposes, the orientation does not matter.
1438 int face0, face1, face2, face3;
1439 gridfile >> face0 >> face1 >> face2 >> face3;
1440 // Make sure the face indices are not out of range.
1441 if (face0 >= nFaces || -face0 - 1 >= nFaces || face1 >= nFaces ||
1442 -face1 - 1 >= nFaces || face2 >= nFaces || -face2 - 1 >= nFaces ||
1443 face3 >= nFaces || -face3 - 1 >= nFaces) {
1444 std::cerr << m_className << "::LoadGrid:\n Error reading file "
1445 << gridfilename << " (line " << iLine << ").\n"
1446 << " Face index out of range.\n";
1447 Cleanup();
1448 gridfile.close();
1449 return false;
1450 }
1451 if (face0 < 0) face0 = -face0 - 1;
1452 if (face1 < 0) face1 = -face1 - 1;
1453 // Get the edges of the first face.
1454 int edge0 = faces[face0].edge[0];
1455 int edge1 = faces[face0].edge[1];
1456 int edge2 = faces[face0].edge[2];
1457 if (edge0 < 0) edge0 = -edge0 - 1;
1458 if (edge1 < 0) edge1 = -edge1 - 1;
1459 if (edge2 < 0) edge2 = -edge2 - 1;
1460 // Make sure the edge indices are not out of range.
1461 if (edge0 >= nEdges || edge1 >= nEdges || edge2 >= nEdges) {
1462 std::cerr << m_className << "::LoadGrid:\n"
1463 << " Error reading file " << gridfilename << "\n"
1464 << " Edge index in element " << j << " out of range.\n";
1465 Cleanup();
1466 gridfile.close();
1467 return false;
1468 }
1469 // Get the first three vertices.
1470 m_elements[j].vertex[0] = edgeP1[edge0];
1471 m_elements[j].vertex[1] = edgeP2[edge0];
1472 if (edgeP1[edge1] != m_elements[j].vertex[0] &&
1473 edgeP1[edge1] != m_elements[j].vertex[1]) {
1474 m_elements[j].vertex[2] = edgeP1[edge1];
1475 } else {
1476 m_elements[j].vertex[2] = edgeP2[edge1];
1477 }
1478 // Get the fourth vertex from face 1.
1479 edge0 = faces[face1].edge[0];
1480 edge1 = faces[face1].edge[1];
1481 edge2 = faces[face1].edge[2];
1482 if (edge0 < 0) edge0 = -edge0 - 1;
1483 if (edge1 < 0) edge1 = -edge1 - 1;
1484 if (edge2 < 0) edge2 = -edge2 - 1;
1485 if (edgeP1[edge0] != m_elements[j].vertex[0] &&
1486 edgeP1[edge0] != m_elements[j].vertex[1] &&
1487 edgeP1[edge0] != m_elements[j].vertex[2]) {
1488 m_elements[j].vertex[3] = edgeP1[edge0];
1489 } else if (edgeP2[edge0] != m_elements[j].vertex[0] &&
1490 edgeP2[edge0] != m_elements[j].vertex[1] &&
1491 edgeP2[edge0] != m_elements[j].vertex[2]) {
1492 m_elements[j].vertex[3] = edgeP2[edge0];
1493 } else if (edgeP1[edge1] != m_elements[j].vertex[0] &&
1494 edgeP1[edge1] != m_elements[j].vertex[1] &&
1495 edgeP1[edge1] != m_elements[j].vertex[2]) {
1496 m_elements[j].vertex[3] = edgeP1[edge1];
1497 } else if (edgeP2[edge1] != m_elements[j].vertex[0] &&
1498 edgeP2[edge1] != m_elements[j].vertex[1] &&
1499 edgeP2[edge1] != m_elements[j].vertex[2]) {
1500 m_elements[j].vertex[3] = edgeP2[edge1];
1501 } else {
1502 std::cerr << m_className << "::LoadGrid:\n"
1503 << " Error reading file " << gridfilename << "\n"
1504 << " Face 1 of element " << j << " is degenerate.\n";
1505 Cleanup();
1506 gridfile.close();
1507 return false;
1508 }
1509 } else {
1510 // Other element types are not allowed.
1511 std::cerr << m_className << "::LoadGrid:\n"
1512 << " Error reading file " << gridfilename << " (line "
1513 << iLine << ").\n";
1514 if (type == 0 || type == 1) {
1515 std::cerr << " Invalid element type (" << type
1516 << ") for 3d mesh.\n";
1517 } else {
1518 std::cerr << " Element type " << type << " is not supported.\n"
1519 << " Remesh with option -t to create only"
1520 << " triangles and tetrahedra.\n";
1521 }
1522 Cleanup();
1523 gridfile.close();
1524 return false;
1525 }
1526 m_elements[j].type = type;
1527 m_elements[j].region = -1;
1528 }
1529 break;
1530 }
1531 if (gridfile.eof()) {
1532 std::cerr << m_className << "::LoadGrid:\n"
1533 << " Could not find section 'Elements' in file\n"
1534 << " " << gridfilename << ".\n";
1535 Cleanup();
1536 gridfile.close();
1537 return false;
1538 } else if (gridfile.fail()) {
1539 std::cerr << m_className << "::LoadGrid:\n Error reading file "
1540 << gridfilename << " (line " << iLine << ").\n";
1541 Cleanup();
1542 gridfile.close();
1543 return false;
1544 }
1545
1546 // Assign regions to elements.
1547 std::string name;
1548 while (!gridfile.fail()) {
1549 std::string line;
1550 std::getline(gridfile, line);
1551 ltrim(line);
1552 // Find section 'Region'.
1553 if (line.substr(0, 6) != "Region") continue;
1554 // Get region name (given in brackets).
1555 std::string::size_type bra = line.find('(');
1556 std::string::size_type ket = line.find(')');
1557 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1558 std::cerr << m_className << "::LoadGrid:\n"
1559 << " Could not read region name.\n";
1560 Cleanup();
1561 gridfile.close();
1562 return false;
1563 }
1564 line = line.substr(bra + 1, ket - bra - 1);
1565 std::istringstream data;
1566 data.str(line);
1567 data >> name;
1568 data.clear();
1569 const int index = FindRegion(name);
1570 if (index == -1) {
1571 // Specified region name is not in the list.
1572 std::cerr << m_className << "::LoadGrid:\n"
1573 << " Error reading file " << gridfilename << ".\n"
1574 << " Unknown region " << name << ".\n";
1575 continue;
1576 }
1577 std::getline(gridfile, line);
1578 std::getline(gridfile, line);
1579 bra = line.find('(');
1580 ket = line.find(')');
1581 if (ket < bra || bra == std::string::npos || ket == std::string::npos) {
1582 // No closed brackets ().
1583 std::cerr << m_className << "::LoadGrid:\n Error reading file "
1584 << gridfilename << ".\n Could not read number of elements "
1585 << "in region " << name << ".\n";
1586 Cleanup();
1587 gridfile.close();
1588 return false;
1589 }
1590 line = line.substr(bra + 1, ket - bra - 1);
1591 int nElementsRegion;
1592 int iElement;
1593 data.str(line);
1594 data >> nElementsRegion;
1595 data.clear();
1596 for (int j = 0; j < nElementsRegion; ++j) {
1597 gridfile >> iElement;
1598 m_elements[iElement].region = index;
1599 }
1600 }
1601
1602 gridfile.close();
1603 if (gridfile.fail() && !gridfile.eof()) {
1604 std::cerr << m_className << "::LoadGrid:\n"
1605 << " Error reading file " << gridfilename << ".\n";
1606 Cleanup();
1607 return false;
1608 }
1609
1610 return true;
1611}
1612
1613void ComponentTcad3d::Cleanup() {
1614 // Vertices
1615 m_vertices.clear();
1616 // Elements
1617 m_elements.clear();
1618 // Regions
1619 m_regions.clear();
1620 // Weighting fields and potentials.
1621 m_wf.clear();
1622 m_wp.clear();
1623}
1624
1625bool ComponentTcad3d::CheckTetrahedron(
1626 const double x, const double y, const double z, const Element& element,
1627 std::array<double, nMaxVertices>& w) const {
1628 const Vertex& v0 = m_vertices[element.vertex[0]];
1629 const Vertex& v1 = m_vertices[element.vertex[1]];
1630 const Vertex& v2 = m_vertices[element.vertex[2]];
1631 const Vertex& v3 = m_vertices[element.vertex[3]];
1632 const double x10 = v1.x - v0.x;
1633 const double y10 = v1.y - v0.y;
1634 const double z10 = v1.z - v0.z;
1635
1636 const double x20 = v2.x - v0.x;
1637 const double y20 = v2.y - v0.y;
1638 const double z20 = v2.z - v0.z;
1639
1640 const double x30 = v3.x - v0.x;
1641 const double y30 = v3.y - v0.y;
1642 const double z30 = v3.z - v0.z;
1643
1644 const double x21 = v2.x - v1.x;
1645 const double y21 = v2.y - v1.y;
1646 const double z21 = v2.z - v1.z;
1647
1648 const double x31 = v3.x - v1.x;
1649 const double y31 = v3.y - v1.y;
1650 const double z31 = v3.z - v1.z;
1651
1652 const double x32 = v3.x - v2.x;
1653 const double y32 = v3.y - v2.y;
1654 const double z32 = v3.z - v2.z;
1655
1656 w[0] = (x - v1.x) * (y21 * z31 - y31 * z21) +
1657 (y - v1.y) * (z21 * x31 - z31 * x21) +
1658 (z - v1.z) * (x21 * y31 - x31 * y21);
1659
1660 w[0] /= x10 * (y31 * z21 - y21 * z31) + y10 * (z31 * x21 - z21 * x31) +
1661 z10 * (x31 * y21 - x21 * y31);
1662 if (w[0] < 0.) return false;
1663
1664 w[1] = (x - v2.x) * (-y20 * z32 + y32 * z20) +
1665 (y - v2.y) * (-z20 * x32 + z32 * x20) +
1666 (z - v2.z) * (-x20 * y32 + x32 * y20);
1667
1668 w[1] /= x21 * (y20 * z32 - y32 * z20) + y21 * (z20 * x32 - z32 * x20) +
1669 z21 * (x20 * y32 - x32 * y20);
1670 if (w[1] < 0.) return false;
1671
1672 w[2] = (x - v3.x) * (y30 * z31 - y31 * z30) +
1673 (y - v3.y) * (z30 * x31 - z31 * x30) +
1674 (z - v3.z) * (x30 * y31 - x31 * y30);
1675
1676 w[2] /= x32 * (y31 * z30 - y30 * z31) + y32 * (z31 * x30 - z30 * x31) +
1677 z32 * (x31 * y30 - x30 * y31);
1678 if (w[2] < 0.) return false;
1679
1680 w[3] = (x - v0.x) * (y20 * z10 - y10 * z20) +
1681 (y - v0.y) * (z20 * x10 - z10 * x20) +
1682 (z - v0.z) * (x20 * y10 - x10 * y20);
1683
1684 w[3] /= x30 * (y20 * z10 - y10 * z20) + y30 * (z20 * x10 - z10 * x20) +
1685 z30 * (x20 * y10 - x10 * y20);
1686 if (w[3] < 0.) return false;
1687
1688 if (m_debug) {
1689 // Reconstruct the point from the local coordinates.
1690 const double xr = w[0] * v0.x + w[1] * v1.x + w[2] * v2.x + w[3] * v3.x;
1691 const double yr = w[0] * v0.y + w[1] * v1.y + w[2] * v2.y + w[3] * v3.y;
1692 const double zr = w[0] * v0.z + w[1] * v1.z + w[2] * v2.z + w[3] * v3.z;
1693 std::cout << m_className << "::CheckTetrahedron:\n"
1694 << " Original coordinates: (" << x << ", " << y << ", "
1695 << z << ")\n"
1696 << " Local coordinates: (" << w[0] << ", " << w[1]
1697 << ", " << w[2] << ", " << w[3] << ")\n"
1698 << " Reconstructed coordinates: (" << xr << ", " << yr << ", "
1699 << zr << ")\n"
1700 << " Checksum: " << w[0] + w[1] + w[2] + w[3] - 1. << "\n";
1701 }
1702
1703 return true;
1704}
1705
1706bool ComponentTcad3d::CheckTriangle(
1707 const double x, const double y, const double z, const Element& element,
1708 std::array<double, nMaxVertices>& w) const {
1709 const Vertex& v0 = m_vertices[element.vertex[0]];
1710 const Vertex& v1 = m_vertices[element.vertex[1]];
1711 const Vertex& v2 = m_vertices[element.vertex[2]];
1712
1713 const double v1x = v1.x - v0.x;
1714 const double v2x = v2.x - v0.x;
1715 const double v1y = v1.y - v0.y;
1716 const double v2y = v2.y - v0.y;
1717 const double v1z = v1.z - v0.z;
1718 const double v2z = v2.z - v0.z;
1719
1720 // Check whether the point lies in the plane of the triangle.
1721 // Compute the coefficients of the plane equation.
1722 const double a = v1y * v2z - v2y * v1z;
1723 const double b = v1z * v2x - v2z * v1x;
1724 const double c = v1x * v2y - v2x * v1y;
1725 const double d = a * v0.x + b * v0.y + c * v0.z;
1726 // Check if the point satisfies the plane equation.
1727 if (a * x + b * y + c * z != d) return false;
1728
1729 // Map (x, y) onto local variables (b, c) such that
1730 // P = A + b * (B - A) + c * (C - A)
1731 // A point P is inside the triangle ABC if b, c > 0 and b + c < 1;
1732 // b, c are also weighting factors for points B, C
1733 w[1] = ((x - v0.x) * v2y - (y - v0.y) * v2x) / (v1x * v2y - v1y * v2x);
1734 if (w[1] < 0. || w[1] > 1.) return false;
1735 w[2] = ((v0.x - x) * v1y - (v0.y - y) * v1x) / (v1x * v2y - v1y * v2x);
1736 if (w[2] < 0. || w[1] + w[2] > 1.) return false;
1737
1738 // Weighting factor for point A
1739 w[0] = 1. - w[1] - w[2];
1740
1741 return true;
1742}
1743
1744void ComponentTcad3d::Reset() {
1745 Cleanup();
1746 m_ready = false;
1747}
1748
1749void ComponentTcad3d::UpdatePeriodicity() {
1750 if (!m_ready) {
1751 std::cerr << m_className << "::UpdatePeriodicity:\n"
1752 << " Field map not available.\n";
1753 return;
1754 }
1755
1756 for (unsigned int i = 0; i < 3; ++i) {
1757 // Check for conflicts.
1758 if (m_periodic[i] && m_mirrorPeriodic[i]) {
1759 std::cerr << m_className << "::UpdatePeriodicity:\n"
1760 << " Both simple and mirror periodicity requested. Reset.\n";
1761 m_periodic[i] = m_mirrorPeriodic[i] = false;
1762 }
1763 }
1764
1766 std::cerr << m_className << "::UpdatePeriodicity:\n"
1767 << " Axial symmetry is not supported. Reset.\n";
1768 m_axiallyPeriodic.fill(false);
1769 }
1770
1773 std::cerr << m_className << "::UpdatePeriodicity:\n"
1774 << " Rotation symmetry is not supported. Reset.\n";
1775 m_rotationSymmetric.fill(false);
1776 }
1777}
1778
1779void ComponentTcad3d::MapCoordinates(double& x, double& y, double& z,
1780 bool& xmirr, bool& ymirr,
1781 bool& zmirr) const {
1782 xmirr = false;
1783 const double cellsx = m_xMaxBB - m_xMinBB;
1784 if (m_periodic[0]) {
1785 x = m_xMinBB + fmod(x - m_xMinBB, cellsx);
1786 if (x < m_xMinBB) x += cellsx;
1787 } else if (m_mirrorPeriodic[0]) {
1788 double xNew = m_xMinBB + fmod(x - m_xMinBB, cellsx);
1789 if (xNew < m_xMinBB) xNew += cellsx;
1790 const int nx = int(floor(0.5 + (xNew - x) / cellsx));
1791 if (nx != 2 * (nx / 2)) {
1792 xNew = m_xMinBB + m_xMaxBB - xNew;
1793 xmirr = true;
1794 }
1795 x = xNew;
1796 }
1797 ymirr = false;
1798 const double cellsy = m_yMaxBB - m_yMinBB;
1799 if (m_periodic[1]) {
1800 y = m_yMinBB + fmod(y - m_yMinBB, cellsy);
1801 if (y < m_yMinBB) y += cellsy;
1802 } else if (m_mirrorPeriodic[1]) {
1803 double yNew = m_yMinBB + fmod(y - m_yMinBB, cellsy);
1804 if (yNew < m_yMinBB) yNew += cellsy;
1805 const int ny = int(floor(0.5 + (yNew - y) / cellsy));
1806 if (ny != 2 * (ny / 2)) {
1807 yNew = m_yMinBB + m_yMaxBB - yNew;
1808 ymirr = true;
1809 }
1810 y = yNew;
1811 }
1812 zmirr = false;
1813 const double cellsz = m_zMaxBB - m_zMinBB;
1814 if (m_periodic[2]) {
1815 z = m_zMinBB + fmod(z - m_zMinBB, cellsz);
1816 if (z < m_zMinBB) z += cellsz;
1817 } else if (m_mirrorPeriodic[2]) {
1818 double zNew = m_zMinBB + fmod(z - m_zMinBB, cellsz);
1819 if (zNew < m_zMinBB) zNew += cellsz;
1820 const int nz = int(floor(0.5 + (zNew - z) / cellsz));
1821 if (nz != 2 * (nz / 2)) {
1822 zNew = m_zMinBB + m_zMaxBB - zNew;
1823 zmirr = true;
1824 }
1825 z = zNew;
1826 }
1827}
1828
1829int ComponentTcad3d::FindRegion(const std::string& name) const {
1830 const unsigned int nRegions = m_regions.size();
1831 for (unsigned int j = 0; j < nRegions; ++j) {
1832 if (name == m_regions[j].name) return j;
1833 }
1834 return -1;
1835}
1836}
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 Initialise(const std::string &gridfilename, const std::string &datafilename)
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).
void UnsetDriftRegion(const unsigned int ireg)
void GetRegion(const unsigned int ireg, std::string &name, bool &active) const
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
bool GetNode(const unsigned int i, double &x, double &y, double &z, double &v, double &ex, double &ey, double &ez) const
void SetDriftRegion(const unsigned int ireg)
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
void PrintRegions()
List all currently defined regions.
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
bool SetWeightingField(const std::string &datfile1, const std::string &datfile2, const double dv)
bool GetElement(const unsigned int i, double &vol, double &dmin, double &dmax, int &type) const
void SetMedium(const unsigned int ireg, Medium *m)
Set the medium for a given region.
Abstract base class for media.
Definition: Medium.hh:13
void ltrim(std::string &line)
Definition: Utilities.hh:10