Garfield++ 5.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 type = element.type;
241 if (element.type == 0) {
242 dmin = dmax = vol = 0;
243 } else if (element.type == 1) {
244 const auto& v0 = m_vertices[element.vertex[0]];
245 const auto& v1 = m_vertices[element.vertex[1]];
246 const double d = std::hypot(v1[0] - v0[0], v1[1] - v0[1]);
247 dmin = dmax = vol = d;
248 } else if (element.type == 2) {
249 const auto& v0 = m_vertices[element.vertex[0]];
250 const auto& v1 = m_vertices[element.vertex[1]];
251 const auto& v2 = m_vertices[element.vertex[2]];
252 vol = 0.5 * fabs((v2[0] - v0[0]) * (v1[1] - v0[1]) -
253 (v2[1] - v0[1]) * (v1[0] - v0[0]));
254 const double a = std::hypot(v1[0] - v0[0], v1[1] - v0[1]);
255 const double b = std::hypot(v2[0] - v0[0], v2[1] - v0[1]);
256 const double c = std::hypot(v1[0] - v2[0], v1[1] - v2[1]);
257 dmin = std::min({a, b, c});
258 dmax = std::max({a, b, c});
259 } else if (element.type == 3) {
260 const auto& v0 = m_vertices[element.vertex[0]];
261 const auto& v1 = m_vertices[element.vertex[1]];
262 const auto& v3 = m_vertices[element.vertex[3]];
263 const double a = std::hypot(v1[0] - v0[0], v1[1] - v0[1]);
264 const double b = std::hypot(v3[0] - v0[0], v3[1] - v0[1]);
265 vol = a * b;
266 dmin = std::min(a, b);
267 dmax = sqrt(a * a + b * b);
268 } else {
269 std::cerr << m_className << "::GetElement:\n"
270 << " Unexpected element type (" << element.type << ")\n";
271 return false;
272 }
273 const size_t nVertices = ElementVertices(element);
274 for (size_t j = 0; j < nVertices; ++j) {
275 nodes.push_back(element.vertex[j]);
276 }
277 reg = element.region;
278 return true;
279}
280
281bool ComponentTcad2d::GetNode(const size_t i, double& x, double& y,
282 double& v, double& ex, double& ey) const {
283 if (i >= m_vertices.size()) {
284 std::cerr << m_className << "::GetNode: Index out of range.\n";
285 return false;
286 }
287
288 x = m_vertices[i][0];
289 y = m_vertices[i][1];
290 if (!m_epot.empty()) v = m_epot[i];
291 if (!m_efield.empty()) {
292 ex = m_efield[i][0];
293 ey = m_efield[i][1];
294 }
295 return true;
296}
297
298size_t ComponentTcad2d::FindElement(const double x, const double y,
299 std::array<double, nMaxVertices>& w) const {
300
301 w.fill(0.);
302 if (m_tree) {
303 const auto& elements = m_tree->GetElementsInBlock(x, y);
304 for (const auto i : elements) {
305 if (InElement(x, y, m_elements[i], w)) return i;
306 }
307 } else {
308 const size_t nElements = m_elements.size();
309 for (size_t i = 0; i < nElements; ++i) {
310 if (InElement(x, y, m_elements[i], w)) return i;
311 }
312 }
313 // Point is outside the mesh.
314 if (m_debug) {
315 std::cerr << m_className << "::FindElement:\n"
316 << " Point (" << x << ", " << y << ") is outside the mesh.\n";
317 }
318 return m_elements.size();
319}
320
321bool ComponentTcad2d::InElement(const double x, const double y,
322 const Element& element,
323 std::array<double, nMaxVertices>& w) const {
324 if (x < element.bbMin[0] || x > element.bbMax[0] ||
325 y < element.bbMin[1] || y > element.bbMax[1]) {
326 return false;
327 }
328 switch (element.type) {
329 case 0:
330 return AtPoint(x, y, element, w);
331 case 1:
332 return OnLine(x, y, element, w);
333 case 2:
334 return InTriangle(x, y, element, w);
335 case 3:
336 return InRectangle(x, y, element, w);
337 default:
338 std::cerr << m_className << "::InElement:\n"
339 << " Unknown element type. Program bug!\n";
340 break;
341 }
342 return false;
343}
344
345bool ComponentTcad2d::InRectangle(const double x, const double y,
346 const Element& element,
347 std::array<double, nMaxVertices>& w) const {
348 const auto& v0 = m_vertices[element.vertex[0]];
349 const auto& v1 = m_vertices[element.vertex[1]];
350 const auto& v3 = m_vertices[element.vertex[3]];
351 if (y < v0[1] || x > v3[0] || y > v1[1]) return false;
352
353 // Map (x, y) to local variables (u, v) in [-1, 1].
354 const double u = (x - 0.5 * (v0[0] + v3[0])) / (v3[0] - v0[0]);
355 const double v = (y - 0.5 * (v0[1] + v1[1])) / (v1[1] - v0[1]);
356 // Compute weighting factors for each corner.
357 w[0] = (0.5 - u) * (0.5 - v);
358 w[1] = (0.5 - u) * (0.5 + v);
359 w[2] = (0.5 + u) * (0.5 + v);
360 w[3] = (0.5 + u) * (0.5 - v);
361 return true;
362}
363
364bool ComponentTcad2d::InTriangle(const double x, const double y,
365 const Element& element,
366 std::array<double, nMaxVertices>& w) const {
367 const auto& v0 = m_vertices[element.vertex[0]];
368 const auto& v1 = m_vertices[element.vertex[1]];
369 const auto& v2 = m_vertices[element.vertex[2]];
370 if (x > v1[0] && x > v2[0]) return false;
371 if (y < v0[1] && y < v1[1] && y < v2[1]) return false;
372 if (y > v0[1] && y > v1[1] && y > v2[1]) return false;
373
374 // Map (x, y) onto local variables (b, c) such that
375 // P = A + b * (B - A) + c * (C - A)
376 // A point P is inside the triangle ABC if b, c > 0 and b + c < 1;
377 // b, c are also weighting factors for points B, C
378 const double sx = v1[0] - v0[0];
379 const double sy = v1[1] - v0[1];
380 const double tx = v2[0] - v0[0];
381 const double ty = v2[1] - v0[1];
382 const double d = 1. / (sx * ty - sy * tx);
383 w[1] = ((x - v0[0]) * ty - (y - v0[1]) * tx) * d;
384 if (w[1] < 0. || w[1] > 1.) return false;
385 w[2] = ((v0[0] - x) * sy - (v0[1] - y) * sx) * d;
386 if (w[2] < 0. || w[1] + w[2] > 1.) return false;
387
388 // Weighting factor for point A
389 w[0] = 1. - w[1] - w[2];
390
391 return true;
392}
393
394bool ComponentTcad2d::OnLine(const double x, const double y,
395 const Element& element,
396 std::array<double, nMaxVertices>& w) const {
397 const auto& v0 = m_vertices[element.vertex[0]];
398 const auto& v1 = m_vertices[element.vertex[1]];
399 if (x > v1[0]) return false;
400 if (y < v0[1] && y < v1[1]) return false;
401 if (y > v0[1] && y > v1[1]) return false;
402 const double tx = (x - v0[0]) / (v1[0] - v0[0]);
403 if (tx < 0. || tx > 1.) return false;
404 const double ty = (y - v0[1]) / (v1[1] - v0[1]);
405 if (ty < 0. || ty > 1.) return false;
406 if (tx == ty) {
407 // Compute weighting factors for endpoints A, B
408 w[0] = tx;
409 w[1] = 1. - w[0];
410 return true;
411 }
412 return false;
413}
414
415bool ComponentTcad2d::AtPoint(const double x, const double y,
416 const Element& element,
417 std::array<double, nMaxVertices>& w) const {
418 const auto& v0 = m_vertices[element.vertex[0]];
419 if (x != v0[0] || y != v0[1]) return false;
420 w[0] = 1;
421 return true;
422}
423
424}
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.
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:376
bool m_debug
Switch on/off debugging messages.
Definition Component.hh:371
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition Component.hh:374
std::string m_className
Class name.
Definition Component.hh:359
bool m_ready
Ready for use?
Definition Component.hh:368
Abstract base class for media.
Definition Medium.hh:16
Quadtree search.
Definition QuadTree.hh:12