Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentTcad2d.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <cmath>
3#include <iostream>
4#include <limits>
5#include <string>
6
8
9namespace Garfield {
10
12
13void ComponentTcad2d::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 // Initialise.
20 ex = ey = ez = p = 0.;
21 m = nullptr;
22
23 // Make sure the field map has been loaded.
24 if (!m_ready) {
25 std::cerr << m_className << "::ElectricField:\n"
26 << " Field map is not available for interpolation.\n";
27 status = -10;
28 return;
29 }
30
31 if (m_hasRangeZ && (zin < m_bbMin[2] || zin > m_bbMax[2])) {
32 status = -6;
33 return;
34 }
35 // In case of periodicity, reduce to the cell volume.
36 std::array<double, 2> x = {xin, yin};
37 std::array<bool, 2> mirr = {false, false};
38 MapCoordinates(x, mirr);
39 // Check if the point is inside the bounding box.
40 if (!InBoundingBox(x)) {
41 status = -6;
42 return;
43 }
44
45 std::array<double, nMaxVertices> w;
46 const auto i = FindElement(x[0], x[1], w);
47 if (i >= m_elements.size()) {
48 // Point is outside the mesh.
49 status = -6;
50 return;
51 }
52
53 const Element& element = m_elements[i];
54 const size_t nVertices = ElementVertices(element);
55 for (size_t j = 0; j < nVertices; ++j) {
56 const size_t index = element.vertex[j];
57 ex += w[j] * m_efield[index][0];
58 ey += w[j] * m_efield[index][1];
59 p += w[j] * m_epot[index];
60 }
61 if (mirr[0]) ex = -ex;
62 if (mirr[1]) ey = -ey;
63 m = m_regions[element.region].medium;
64 if (!m_regions[element.region].drift || !m) status = -5;
65}
66
67bool ComponentTcad2d::Interpolate(const double xin, const double yin,
68 const double z,
69 const std::vector<std::array<double, 2> >& field,
70 double& fx, double& fy, double& fz) {
71
72 fx = fy = fz = 0.;
73 if (field.empty()) return false;
74 if (m_hasRangeZ && (z < m_bbMin[2] || z > m_bbMax[2])) return false;
75 std::array<double, 2> x = {xin, yin};
76 std::array<bool, 2> mirr = {false, false};
77 // In case of periodicity, reduce to the cell volume.
78 MapCoordinates(x, mirr);
79 // Make sure the point is inside the bounding box.
80 if (!InBoundingBox(x)) return false;
81
82 std::array<double, nMaxVertices> w;
83 const auto i = FindElement(x[0], x[1], w);
84 // Stop if the point is outside the mesh.
85 if (i >= m_elements.size()) return false;
86
87 const Element& element = m_elements[i];
88 const size_t nVertices = ElementVertices(element);
89 for (size_t j = 0; j < nVertices; ++j) {
90 const auto index = element.vertex[j];
91 fx += w[j] * field[index][0];
92 fy += w[j] * field[index][1];
93 }
94 if (mirr[0]) fx = -fx;
95 if (mirr[1]) fy = -fy;
96 return true;
97}
98
99bool ComponentTcad2d::Interpolate(const double xin, const double yin,
100 const double z,
101 const std::vector<double>& field, double& f) {
102
103 f = 0.;
104 if (field.empty()) return false;
105 if (m_hasRangeZ && (z < m_bbMin[2] || z > m_bbMax[2])) return false;
106 std::array<double, 2> x = {xin, yin};
107 std::array<bool, 2> mirr = {false, false};
108 // In case of periodicity, reduce to the cell volume.
109 MapCoordinates(x, mirr);
110 if (!InBoundingBox(x)) return false;
111
112 std::array<double, nMaxVertices> w;
113 const auto i = FindElement(x[0], x[1], w);
114 // Stop if the point is outside the mesh.
115 if (i >= m_elements.size()) return false;
116
117 const Element& element = m_elements[i];
118 const size_t nVertices = ElementVertices(element);
119 for (size_t j = 0; j < nVertices; ++j) {
120 f += w[j] * field[element.vertex[j]];
121 }
122 return true;
123}
124
125Medium* ComponentTcad2d::GetMedium(const double xin, const double yin,
126 const double zin) {
127 // Make sure the field map has been loaded.
128 if (!m_ready) {
129 std::cerr << m_className << "::GetMedium:\n"
130 << " Field map not available for interpolation.\n";
131 return nullptr;
132 }
133
134 if (m_hasRangeZ && (zin < m_bbMin[2] || zin > m_bbMax[2])) return nullptr;
135 std::array<double, 2> x = {xin, yin};
136 std::array<bool, 2> mirr = {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], 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 ComponentTcad2d::FillTree() {
152
153 // Set up the quad tree.
154 const double hx = 0.5 * (m_bbMax[0] - m_bbMin[0]);
155 const double hy = 0.5 * (m_bbMax[1] - m_bbMin[1]);
156 m_tree.reset(new QuadTree(m_bbMin[0] + hx, m_bbMin[1] + hy, hx, hy));
157 // Insert the mesh nodes in the tree.
158 const auto nVertices = m_vertices.size();
159 for (size_t i = 0; i < nVertices; ++i) {
160 m_tree->InsertMeshNode(m_vertices[i][0], m_vertices[i][1], i);
161 }
162
163 const auto nElements = m_elements.size();
164 // Insert the mesh elements in the tree.
165 for (size_t i = 0; i < nElements; ++i) {
166 const Element& element = m_elements[i];
167 const double bb[4] = {element.bbMin[0], element.bbMin[1],
168 element.bbMax[0], element.bbMax[1]};
169 m_tree->InsertMeshElement(bb, i);
170 }
171
172}
173
174bool ComponentTcad2d::GetBoundingBox(double& xmin, double& ymin, double& zmin,
175 double& xmax, double& ymax, double& zmax) {
176 if (!m_ready) return false;
177 if (m_periodic[0] || m_mirrorPeriodic[0]) {
178 xmin = -std::numeric_limits<double>::infinity();
179 xmax = +std::numeric_limits<double>::infinity();
180 } else {
181 xmin = m_bbMin[0];
182 xmax = m_bbMax[0];
183 }
184
185 if (m_periodic[1] || m_mirrorPeriodic[1]) {
186 ymin = -std::numeric_limits<double>::infinity();
187 ymax = +std::numeric_limits<double>::infinity();
188 } else {
189 ymin = m_bbMin[1];
190 ymax = m_bbMax[1];
191 }
192
193 if (m_hasRangeZ) {
194 zmin = m_bbMin[2];
195 zmax = m_bbMax[2];
196 }
197 return true;
198}
199
201 double& xmin, double& ymin, double& zmin,
202 double& xmax, double& ymax, double& zmax) {
203 if (!m_ready) return false;
204 xmin = m_bbMin[0];
205 xmax = m_bbMax[0];
206 ymin = m_bbMin[1];
207 ymax = m_bbMax[1];
208 if (m_hasRangeZ) {
209 zmin = m_bbMin[2];
210 zmax = m_bbMax[2];
211 } else {
212 const double xymax = std::max({fabs(xmin), fabs(xmax),
213 fabs(ymin), fabs(ymax)});
214 zmin = -xymax;
215 zmax = +xymax;
216 }
217 return true;
218}
219
220void ComponentTcad2d::SetRangeZ(const double zmin, const double zmax) {
221 if (fabs(zmax - zmin) <= 0.) {
222 std::cerr << m_className << "::SetRangeZ: Zero range is not permitted.\n";
223 return;
224 }
225 m_bbMin[2] = std::min(zmin, zmax);
226 m_bbMax[2] = std::max(zmin, zmax);
227 m_hasRangeZ = true;
228}
229
230bool ComponentTcad2d::GetElement(const size_t i, double& vol,
231 double& dmin, double& dmax, int& type,
232 std::vector<size_t>& nodes, int& reg) const {
233 nodes.clear();
234 if (i >= m_elements.size()) {
235 std::cerr << m_className << "::GetElement: Index out of range.\n";
236 return false;
237 }
238
239 const Element& element = m_elements[i];
240 if (element.type == 0) {
241 dmin = dmax = vol = 0;
242 } else if (element.type == 1) {
243 const auto& v0 = m_vertices[element.vertex[0]];
244 const auto& v1 = m_vertices[element.vertex[1]];
245 const double d = std::hypot(v1[0] - v0[0], v1[1] - v0[1]);
246 dmin = dmax = vol = d;
247 } else if (m_elements[i].type == 2) {
248 const auto& v0 = m_vertices[element.vertex[0]];
249 const auto& v1 = m_vertices[element.vertex[1]];
250 const auto& v2 = m_vertices[element.vertex[2]];
251 vol = 0.5 * fabs((v2[0] - v0[0]) * (v1[1] - v0[1]) -
252 (v2[1] - v0[1]) * (v1[0] - v0[0]));
253 const double a = std::hypot(v1[0] - v0[0], v1[1] - v0[1]);
254 const double b = std::hypot(v2[0] - v0[0], v2[1] - v0[1]);
255 const double c = std::hypot(v1[0] - v2[0], v1[1] - v2[1]);
256 dmin = std::min({a, b, c});
257 dmax = std::max({a, b, c});
258 } else if (m_elements[i].type == 3) {
259 const auto& v0 = m_vertices[element.vertex[0]];
260 const auto& v1 = m_vertices[element.vertex[1]];
261 const auto& v3 = m_vertices[element.vertex[3]];
262 const double a = std::hypot(v1[0] - v0[0], v1[1] - v0[1]);
263 const double b = std::hypot(v3[0] - v0[0], v3[1] - v0[1]);
264 vol = a * b;
265 dmin = std::min(a, b);
266 dmax = sqrt(a * a + b * b);
267 } else {
268 std::cerr << m_className << "::GetElement:\n"
269 << " Unexpected element type (" << type << ")\n";
270 return false;
271 }
272 const size_t nVertices = ElementVertices(element);
273 for (size_t j = 0; j < nVertices; ++j) {
274 nodes.push_back(element.vertex[j]);
275 }
276 reg = element.region;
277 return true;
278}
279
280bool ComponentTcad2d::GetNode(const size_t i, double& x, double& y,
281 double& v, double& ex, double& ey) const {
282 if (i >= m_vertices.size()) {
283 std::cerr << m_className << "::GetNode: Index out of range.\n";
284 return false;
285 }
286
287 x = m_vertices[i][0];
288 y = m_vertices[i][1];
289 if (!m_epot.empty()) v = m_epot[i];
290 if (!m_efield.empty()) {
291 ex = m_efield[i][0];
292 ey = m_efield[i][1];
293 }
294 return true;
295}
296
297size_t ComponentTcad2d::FindElement(const double x, const double y,
298 std::array<double, nMaxVertices>& w) const {
299
300 w.fill(0.);
301
302 std::vector<int> elementsToSearch;
303 if (m_tree) elementsToSearch = m_tree->GetElementsInBlock(x, y);
304 const size_t nElementsToSearch = m_tree ? elementsToSearch.size() : m_elements.size();
305 for (size_t i = 0; i < nElementsToSearch; ++i) {
306 const size_t idx = m_tree ? elementsToSearch[i] : i;
307 if (InElement(x, y, m_elements[idx], w)) return idx;
308 }
309 // Point is outside the mesh.
310 if (m_debug) {
311 std::cerr << m_className << "::FindElement:\n"
312 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
313 }
314 return m_elements.size();
315}
316
317bool ComponentTcad2d::InElement(const double x, const double y,
318 const Element& element,
319 std::array<double, nMaxVertices>& w) const {
320 if (x < element.bbMin[0] || x > element.bbMax[0] ||
321 y < element.bbMin[1] || y > element.bbMax[1]) {
322 return false;
323 }
324 switch (element.type) {
325 case 0:
326 return AtPoint(x, y, element, w);
327 case 1:
328 return OnLine(x, y, element, w);
329 case 2:
330 return InTriangle(x, y, element, w);
331 case 3:
332 return InRectangle(x, y, element, w);
333 default:
334 std::cerr << m_className << "::InElement:\n"
335 << " Unknown element type. Program bug!\n";
336 break;
337 }
338 return false;
339}
340
341bool ComponentTcad2d::InRectangle(const double x, const double y,
342 const Element& element,
343 std::array<double, nMaxVertices>& w) const {
344 const auto& v0 = m_vertices[element.vertex[0]];
345 const auto& v1 = m_vertices[element.vertex[1]];
346 const auto& v3 = m_vertices[element.vertex[3]];
347 if (y < v0[1] || x > v3[0] || y > v1[1]) return false;
348
349 // Map (x, y) to local variables (u, v) in [-1, 1].
350 const double u = (x - 0.5 * (v0[0] + v3[0])) / (v3[0] - v0[0]);
351 const double v = (y - 0.5 * (v0[1] + v1[1])) / (v1[1] - v0[1]);
352 // Compute weighting factors for each corner.
353 w[0] = (0.5 - u) * (0.5 - v);
354 w[1] = (0.5 - u) * (0.5 + v);
355 w[2] = (0.5 + u) * (0.5 + v);
356 w[3] = (0.5 + u) * (0.5 - v);
357 return true;
358}
359
360bool ComponentTcad2d::InTriangle(const double x, const double y,
361 const Element& element,
362 std::array<double, nMaxVertices>& w) const {
363 const auto& v0 = m_vertices[element.vertex[0]];
364 const auto& v1 = m_vertices[element.vertex[1]];
365 const auto& v2 = m_vertices[element.vertex[2]];
366 if (x > v1[0] && x > v2[0]) return false;
367 if (y < v0[1] && y < v1[1] && y < v2[1]) return false;
368 if (y > v0[1] && y > v1[1] && y > v2[1]) return false;
369
370 // Map (x, y) onto local variables (b, c) such that
371 // P = A + b * (B - A) + c * (C - A)
372 // A point P is inside the triangle ABC if b, c > 0 and b + c < 1;
373 // b, c are also weighting factors for points B, C
374 const double sx = v1[0] - v0[0];
375 const double sy = v1[1] - v0[1];
376 const double tx = v2[0] - v0[0];
377 const double ty = v2[1] - v0[1];
378 const double d = 1. / (sx * ty - sy * tx);
379 w[1] = ((x - v0[0]) * ty - (y - v0[1]) * tx) * d;
380 if (w[1] < 0. || w[1] > 1.) return false;
381 w[2] = ((v0[0] - x) * sy - (v0[1] - y) * sx) * d;
382 if (w[2] < 0. || w[1] + w[2] > 1.) return false;
383
384 // Weighting factor for point A
385 w[0] = 1. - w[1] - w[2];
386
387 return true;
388}
389
390bool ComponentTcad2d::OnLine(const double x, const double y,
391 const Element& element,
392 std::array<double, nMaxVertices>& w) const {
393 const auto& v0 = m_vertices[element.vertex[0]];
394 const auto& v1 = m_vertices[element.vertex[1]];
395 if (x > v1[0]) return false;
396 if (y < v0[1] && y < v1[1]) return false;
397 if (y > v0[1] && y > v1[1]) return false;
398 const double tx = (x - v0[0]) / (v1[0] - v0[0]);
399 if (tx < 0. || tx > 1.) return false;
400 const double ty = (y - v0[1]) / (v1[1] - v0[1]);
401 if (ty < 0. || ty > 1.) return false;
402 if (tx == ty) {
403 // Compute weighting factors for endpoints A, B
404 w[0] = tx;
405 w[1] = 1. - w[0];
406 return true;
407 }
408 return false;
409}
410
411bool ComponentTcad2d::AtPoint(const double x, const double y,
412 const Element& element,
413 std::array<double, nMaxVertices>& w) const {
414 const auto& v0 = m_vertices[element.vertex[0]];
415 if (x != v0[0] || y != v0[1]) return false;
416 w[0] = 1;
417 return true;
418}
419
420}
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
void SetRangeZ(const double zmin, const double zmax)
Set the z-extent of the bounding box (default: unlimited).
bool GetElement(const size_t i, double &vol, double &dmin, double &dmax, int &type, std::vector< size_t > &nodes, int &reg) const
bool GetNode(const size_t i, double &x, double &y, double &v, double &ex, double &ey) const
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
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 GetElementaryCell(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the coordinates of the elementary cell.
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)
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
Quadtree search.
Definition: QuadTree.hh:12
Definition: neBEM.h:155