Garfield++ 4.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 <iostream>
4#include <limits>
5#include <string>
6
8
9namespace Garfield {
10
12
13void ComponentTcad3d::ElectricField(const double xin, const double yin,
14 const double zin, double& ex, double& ey,
15 double& ez, double& p, Medium*& m,
16 int& status) {
17 // Assume this will work.
18 status = 0;
19 ex = ey = ez = p = 0.;
20 m = nullptr;
21 // Make sure the field map has been loaded.
22 if (!m_ready) {
23 std::cerr << m_className << "::ElectricField:\n"
24 << " Field map is not available for interpolation.\n";
25 status = -10;
26 return;
27 }
28 std::array<double, 3> x = {xin, yin, zin};
29 std::array<bool, 3> mirr = {false, false, false};
30 // In case of periodicity, reduce to the cell volume.
31 MapCoordinates(x, mirr);
32 // Check if the point is inside the bounding box.
33 if (!InBoundingBox(x)) {
34 status = -6;
35 return;
36 }
37
38 std::array<double, nMaxVertices> w;
39 const size_t i = FindElement(x[0], x[1], x[2], w);
40 if (i >= m_elements.size()) {
41 // Point is outside the mesh.
42 status = -6;
43 return;
44 }
45 const Element& element = m_elements[i];
46 const unsigned int nVertices = ElementVertices(element);
47 for (unsigned int j = 0; j < nVertices; ++j) {
48 const auto index = element.vertex[j];
49 ex += w[j] * m_efield[index][0];
50 ey += w[j] * m_efield[index][1];
51 ez += w[j] * m_efield[index][2];
52 p += w[j] * m_epot[index];
53 }
54 if (mirr[0]) ex = -ex;
55 if (mirr[1]) ey = -ey;
56 if (mirr[2]) ez = -ez;
57 m = m_regions[element.region].medium;
58 if (!m_regions[element.region].drift || !m) status = -5;
59}
60
61void ComponentTcad3d::ElectricField(const double x, const double y,
62 const double z, double& ex, double& ey,
63 double& ez, Medium*& m, int& status) {
64 double v = 0.;
65 ElectricField(x, y, z, ex, ey, ez, v, m, status);
66}
67
68bool ComponentTcad3d::Interpolate(
69 const double xin, const double yin, const double zin,
70 const std::vector<std::array<double, 3> >& field,
71 double& fx, double& fy, double& fz) {
72
73 if (field.empty()) return false;
74 std::array<double, 3> x = {xin, yin, zin};
75 std::array<bool, 3> mirr = {false, false, false};
76 // In case of periodicity, reduce to the cell volume.
77 MapCoordinates(x, mirr);
78 // Make sure the point is inside the bounding box.
79 if (!InBoundingBox(x)) return false;
80
81 std::array<double, nMaxVertices> w;
82 const size_t i = FindElement(x[0], x[1], x[2], w);
83 // Stop if the point is outside the mesh.
84 if (i >= m_elements.size()) return false;
85
86 const Element& element = m_elements[i];
87 const size_t nVertices = ElementVertices(element);
88 for (size_t j = 0; j < nVertices; ++j) {
89 const auto index = element.vertex[j];
90 fx += w[j] * field[index][0];
91 fy += w[j] * field[index][1];
92 fz += w[j] * field[index][2];
93 }
94 if (mirr[0]) fx = -fx;
95 if (mirr[1]) fy = -fy;
96 if (mirr[2]) fz = -fz;
97 return true;
98}
99
100bool ComponentTcad3d::Interpolate(
101 const double xin, const double yin, const double zin,
102 const std::vector<double>& field, double& f) {
103
104 f = 0.;
105 if (field.empty()) return false;
106 std::array<double, 3> x = {xin, yin, zin};
107 std::array<bool, 3> mirr = {false, false, false};
108 // In case of periodicity, reduce to the cell volume.
109 MapCoordinates(x, mirr);
110 // Make sure the point is inside the bounding box.
111 if (!InBoundingBox(x)) return false;
112
113 std::array<double, nMaxVertices> w;
114 const auto i = FindElement(x[0], x[1], x[2], w);
115 // Stop if the point is outside the mesh.
116 if (i >= m_elements.size()) return false;
117
118 const Element& element = m_elements[i];
119 const size_t nVertices = ElementVertices(element);
120 for (size_t j = 0; j < nVertices; ++j) {
121 f += w[j] * field[element.vertex[j]];
122 }
123 return true;
124}
125
126Medium* ComponentTcad3d::GetMedium(const double xin, const double yin,
127 const double zin) {
128 // Make sure the field map has been loaded.
129 if (!m_ready) {
130 std::cerr << m_className << "::GetMedium:\n"
131 << " Field map not available for interpolation.\n";
132 return nullptr;
133 }
134
135 std::array<double, 3> x = {xin, yin, zin};
136 std::array<bool, 3> mirr = {false, false, false};
137 MapCoordinates(x, mirr);
138 // Check if the point is inside the bounding box.
139 if (!InBoundingBox(x)) return nullptr;
140
141 // Determine the shape functions.
142 std::array<double, nMaxVertices> w;
143 const size_t i = FindElement(x[0], x[1], x[2], w);
144 if (i >= m_elements.size()) {
145 // Point is outside the mesh.
146 return nullptr;
147 }
148 return m_regions[m_elements[i].region].medium;
149}
150
151void ComponentTcad3d::FillTree() {
152
153 // Set up the octree.
154 const float hx = 0.5 * (m_bbMax[0] - m_bbMin[0]);
155 const float hy = 0.5 * (m_bbMax[1] - m_bbMin[1]);
156 const float hz = 0.5 * (m_bbMax[2] - m_bbMin[2]);
157 m_tree.reset(new TetrahedralTree(Vec3(m_bbMin[0] + hx, m_bbMin[1] + hy, m_bbMin[2] + hz),
158 Vec3(hx, hy, hz)));
159
160 // Insert the mesh nodes in the tree.
161 const size_t nVertices = m_vertices.size();
162 for (size_t i = 0; i < nVertices; ++i) {
163 const auto& vtx = m_vertices[i];
164 m_tree->InsertMeshNode(Vec3(vtx[0], vtx[1], vtx[2]), i);
165 }
166
167 // Insert the mesh elements in the tree.
168 const size_t nElements = m_elements.size();
169 for (size_t i = 0; i < nElements; ++i) {
170 const Element& element = m_elements[i];
171 const double bb[6] = {element.bbMin[0], element.bbMin[1], element.bbMin[2],
172 element.bbMax[0], element.bbMax[1], element.bbMax[2]};
173 m_tree->InsertMeshElement(bb, i);
174 }
175}
176
177bool ComponentTcad3d::GetBoundingBox(double& xmin, double& ymin, double& zmin,
178 double& xmax, double& ymax, double& zmax) {
179 if (!m_ready) return false;
180 xmin = m_bbMin[0];
181 ymin = m_bbMin[1];
182 zmin = m_bbMin[2];
183 xmax = m_bbMax[0];
184 ymax = m_bbMax[1];
185 zmax = m_bbMax[2];
186 if (m_periodic[0] || m_mirrorPeriodic[0]) {
187 xmin = -std::numeric_limits<double>::infinity();
188 xmax = +std::numeric_limits<double>::infinity();
189 }
190 if (m_periodic[1] || m_mirrorPeriodic[1]) {
191 ymin = -std::numeric_limits<double>::infinity();
192 ymax = +std::numeric_limits<double>::infinity();
193 }
194 if (m_periodic[2] || m_mirrorPeriodic[2]) {
195 zmin = -std::numeric_limits<double>::infinity();
196 zmax = +std::numeric_limits<double>::infinity();
197 }
198 return true;
199}
200
202 double& xmin, double& ymin, double& zmin,
203 double& xmax, double& ymax, double& zmax) {
204 if (!m_ready) return false;
205 xmin = m_bbMin[0];
206 ymin = m_bbMin[1];
207 zmin = m_bbMin[2];
208 xmax = m_bbMax[0];
209 ymax = m_bbMax[1];
210 zmax = m_bbMax[2];
211 return true;
212}
213
214size_t ComponentTcad3d::FindElement(
215 const double x, const double y, const double z,
216 std::array<double, nMaxVertices>& w) const {
217
218 w.fill(0.);
219
220 std::vector<int> elementsToSearch;
221 if (m_tree) {
222 elementsToSearch = m_tree->GetElementsInBlock(Vec3(x, y, z));
223 }
224 const size_t nElementsToSearch = m_tree ? elementsToSearch.size() : m_elements.size();
225 // Loop over the elements.
226 for (size_t i = 0; i < nElementsToSearch; ++i) {
227 const size_t idx = m_tree ? elementsToSearch[i] : i;
228 if (InElement(x, y, z, m_elements[idx], w)) return idx;
229 }
230
231 if (m_debug) {
232 std::cerr << m_className << "::FindElement:\n"
233 << " Point (" << x << ", " << y << ", " << z
234 << ") is outside the mesh.\n";
235 }
236 return m_elements.size();
237}
238
239bool ComponentTcad3d::GetElement(const size_t i, double& vol,
240 double& dmin, double& dmax, int& type,
241 std::vector<size_t>& nodes, int& reg) const {
242 nodes.clear();
243 if (i >= m_elements.size()) {
244 std::cerr << m_className << "::GetElement: Index out of range.\n";
245 return false;
246 }
247
248 const Element& element = m_elements[i];
249 if (element.type == 2) {
250 // Triangle
251 const auto& v0 = m_vertices[element.vertex[0]];
252 const auto& v1 = m_vertices[element.vertex[1]];
253 const auto& v2 = m_vertices[element.vertex[2]];
254 const double vx = (v1[1] - v0[1]) * (v2[2] - v0[2]) -
255 (v1[2] - v0[2]) * (v2[1] - v0[1]);
256 const double vy = (v1[2] - v0[2]) * (v2[0] - v0[0]) -
257 (v1[0] - v0[0]) * (v2[2] - v0[2]);
258 const double vz = (v1[0] - v0[0]) * (v2[1] - v0[1]) -
259 (v1[1] - v0[1]) * (v2[0] - v0[0]);
260 vol = sqrt(vx * vx + vy * vy + vz * vz);
261 const double a = sqrt(pow(v1[0] - v0[0], 2) + pow(v1[1] - v0[1], 2) +
262 pow(v1[2] - v0[2], 2));
263 const double b = sqrt(pow(v2[0] - v0[0], 2) + pow(v2[1] - v0[1], 2) +
264 pow(v2[2] - v0[2], 2));
265 const double c = sqrt(pow(v1[0] - v2[0], 2) + pow(v1[1] - v2[1], 2) +
266 pow(v1[2] - v2[2], 2));
267 dmin = std::min({a, b, c});
268 dmax = std::max({a, b, c});
269 } else if (element.type == 5) {
270 // Tetrahedron
271 const auto& v0 = m_vertices[element.vertex[0]];
272 const auto& v1 = m_vertices[element.vertex[1]];
273 const auto& v2 = m_vertices[element.vertex[2]];
274 const auto& v3 = m_vertices[element.vertex[3]];
275 vol = fabs((v3[0] - v0[0]) * ((v1[1] - v0[1]) * (v2[2] - v0[2]) -
276 (v2[1] - v0[1]) * (v1[2] - v0[2])) +
277 (v3[1] - v0[1]) * ((v1[2] - v0[2]) * (v2[0] - v0[0]) -
278 (v2[2] - v0[2]) * (v1[0] - v0[0])) +
279 (v3[2] - v0[2]) * ((v1[0] - v0[0]) * (v2[1] - v0[1]) -
280 (v3[0] - v0[0]) * (v1[1] - v0[1]))) /
281 6.;
282 // Loop over all pairs of m_vertices.
283 for (size_t j = 0; j < nMaxVertices - 1; ++j) {
284 const auto& vj = m_vertices[element.vertex[j]];
285 for (size_t k = j + 1; k < nMaxVertices; ++k) {
286 const auto& vk = m_vertices[element.vertex[k]];
287 // Compute distance.
288 const double dist = sqrt(pow(vj[0] - vk[0], 2) + pow(vj[1] - vk[1], 2) +
289 pow(vj[2] - vk[2], 2));
290 if (k == 1) {
291 dmin = dmax = dist;
292 } else {
293 if (dist < dmin) dmin = dist;
294 if (dist > dmax) dmax = dist;
295 }
296 }
297 }
298 } else {
299 std::cerr << m_className << "::GetElement:\n"
300 << " Unexpected element type (" << type << ").\n";
301 return false;
302 }
303 const size_t nVertices = ElementVertices(element);
304 for (size_t j = 0; j < nVertices; ++j) {
305 nodes.push_back(element.vertex[j]);
306 }
307 reg = element.region;
308 return true;
309}
310
311bool ComponentTcad3d::GetNode(const size_t i, double& x, double& y,
312 double& z, double& v, double& ex, double& ey,
313 double& ez) const {
314 if (i >= m_vertices.size()) {
315 std::cerr << m_className << "::GetNode: Index out of range.\n";
316 return false;
317 }
318
319 x = m_vertices[i][0];
320 y = m_vertices[i][1];
321 z = m_vertices[i][2];
322 if (!m_epot.empty()) v = m_epot[i];
323 if (!m_efield.empty()) {
324 ex = m_efield[i][0];
325 ey = m_efield[i][1];
326 ez = m_efield[i][2];
327 }
328 return true;
329}
330
331bool ComponentTcad3d::InTetrahedron(
332 const double x, const double y, const double z, const Element& element,
333 std::array<double, nMaxVertices>& w) const {
334 const auto& v0 = m_vertices[element.vertex[0]];
335 const auto& v1 = m_vertices[element.vertex[1]];
336 const auto& v2 = m_vertices[element.vertex[2]];
337 const auto& v3 = m_vertices[element.vertex[3]];
338 const double x10 = v1[0] - v0[0];
339 const double y10 = v1[1] - v0[1];
340 const double z10 = v1[2] - v0[2];
341
342 const double x20 = v2[0] - v0[0];
343 const double y20 = v2[1] - v0[1];
344 const double z20 = v2[2] - v0[2];
345
346 const double x30 = v3[0] - v0[0];
347 const double y30 = v3[1] - v0[1];
348 const double z30 = v3[2] - v0[2];
349
350 const double x21 = v2[0] - v1[0];
351 const double y21 = v2[1] - v1[1];
352 const double z21 = v2[2] - v1[2];
353
354 const double x31 = v3[0] - v1[0];
355 const double y31 = v3[1] - v1[1];
356 const double z31 = v3[2] - v1[2];
357
358 const double x32 = v3[0] - v2[0];
359 const double y32 = v3[1] - v2[1];
360 const double z32 = v3[2] - v2[2];
361
362 w[0] = (x - v1[0]) * (y21 * z31 - y31 * z21) +
363 (y - v1[1]) * (z21 * x31 - z31 * x21) +
364 (z - v1[2]) * (x21 * y31 - x31 * y21);
365
366 w[0] /= x10 * (y31 * z21 - y21 * z31) + y10 * (z31 * x21 - z21 * x31) +
367 z10 * (x31 * y21 - x21 * y31);
368 if (w[0] < 0.) return false;
369
370 w[1] = (x - v2[0]) * (-y20 * z32 + y32 * z20) +
371 (y - v2[1]) * (-z20 * x32 + z32 * x20) +
372 (z - v2[2]) * (-x20 * y32 + x32 * y20);
373
374 w[1] /= x21 * (y20 * z32 - y32 * z20) + y21 * (z20 * x32 - z32 * x20) +
375 z21 * (x20 * y32 - x32 * y20);
376 if (w[1] < 0.) return false;
377
378 w[2] = (x - v3[0]) * (y30 * z31 - y31 * z30) +
379 (y - v3[1]) * (z30 * x31 - z31 * x30) +
380 (z - v3[2]) * (x30 * y31 - x31 * y30);
381
382 w[2] /= x32 * (y31 * z30 - y30 * z31) + y32 * (z31 * x30 - z30 * x31) +
383 z32 * (x31 * y30 - x30 * y31);
384 if (w[2] < 0.) return false;
385
386 w[3] = (x - v0[0]) * (y20 * z10 - y10 * z20) +
387 (y - v0[1]) * (z20 * x10 - z10 * x20) +
388 (z - v0[2]) * (x20 * y10 - x10 * y20);
389
390 w[3] /= x30 * (y20 * z10 - y10 * z20) + y30 * (z20 * x10 - z10 * x20) +
391 z30 * (x20 * y10 - x10 * y20);
392 if (w[3] < 0.) return false;
393
394 if (m_debug) {
395 // Reconstruct the point from the local coordinates.
396 const double xr = w[0] * v0[0] + w[1] * v1[0] + w[2] * v2[0] + w[3] * v3[0];
397 const double yr = w[0] * v0[1] + w[1] * v1[1] + w[2] * v2[1] + w[3] * v3[1];
398 const double zr = w[0] * v0[2] + w[1] * v1[2] + w[2] * v2[2] + w[3] * v3[2];
399 std::cout << m_className << "::InTetrahedron:\n"
400 << " Original coordinates: (" << x << ", " << y << ", "
401 << z << ")\n"
402 << " Local coordinates: (" << w[0] << ", " << w[1]
403 << ", " << w[2] << ", " << w[3] << ")\n"
404 << " Reconstructed coordinates: (" << xr << ", " << yr << ", "
405 << zr << ")\n"
406 << " Checksum: " << w[0] + w[1] + w[2] + w[3] - 1. << "\n";
407 }
408
409 return true;
410}
411
412bool ComponentTcad3d::InTriangle(
413 const double x, const double y, const double z, const Element& element,
414 std::array<double, nMaxVertices>& w) const {
415 const auto& v0 = m_vertices[element.vertex[0]];
416 const auto& v1 = m_vertices[element.vertex[1]];
417 const auto& v2 = m_vertices[element.vertex[2]];
418
419 const double v1x = v1[0] - v0[0];
420 const double v2x = v2[0] - v0[0];
421 const double v1y = v1[1] - v0[1];
422 const double v2y = v2[1] - v0[1];
423 const double v1z = v1[2] - v0[2];
424 const double v2z = v2[2] - v0[2];
425
426 // Check whether the point lies in the plane of the triangle.
427 // Compute the coefficients of the plane equation.
428 const double a = v1y * v2z - v2y * v1z;
429 const double b = v1z * v2x - v2z * v1x;
430 const double c = v1x * v2y - v2x * v1y;
431 const double d = a * v0[0] + b * v0[1] + c * v0[2];
432 // Check if the point satisfies the plane equation.
433 if (a * x + b * y + c * z != d) return false;
434
435 // Map (x, y) onto local variables (b, c) such that
436 // P = A + b * (B - A) + c * (C - A)
437 // A point P is inside the triangle ABC if b, c > 0 and b + c < 1;
438 // b, c are also weighting factors for points B, C
439 w[1] = ((x - v0[0]) * v2y - (y - v0[1]) * v2x) / (v1x * v2y - v1y * v2x);
440 if (w[1] < 0. || w[1] > 1.) return false;
441 w[2] = ((v0[0] - x) * v1y - (v0[1] - y) * v1x) / (v1x * v2y - v1y * v2x);
442 if (w[2] < 0. || w[1] + w[2] > 1.) return false;
443
444 // Weighting factor for point A
445 w[0] = 1. - w[1] - w[2];
446
447 return true;
448}
449
450}
bool GetElementaryCell(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the coordinates of the elementary cell.
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&m, int &status) override
Calculate the drift field [V/cm] and potential [V] at (x, y, z).
bool GetNode(const size_t i, double &x, double &y, double &z, double &v, double &ex, double &ey, double &ez) const
bool GetElement(const size_t i, double &vol, double &dmin, double &dmax, int &type, std::vector< size_t > &nodes, int &reg) const
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
Interpolation in a field map created by Sentaurus Device.
bool InBoundingBox(const std::array< double, N > &x) const
std::vector< std::array< double, N > > m_vertices
static unsigned int ElementVertices(const Element &element)
static constexpr size_t nMaxVertices
std::vector< std::array< double, N > > m_efield
void MapCoordinates(std::array< double, N > &x, std::array< bool, N > &mirr) const
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
Definition: Component.hh:346
bool m_debug
Switch on/off debugging messages.
Definition: Component.hh:341
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition: Component.hh:344
std::string m_className
Class name.
Definition: Component.hh:329
bool m_ready
Ready for use?
Definition: Component.hh:338
Abstract base class for media.
Definition: Medium.hh:13
Helper class for searches in field maps.
Definition: neBEM.h:155