Garfield++ v2r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentComsol.cc
Go to the documentation of this file.
1// Copied and modified ComponentAnsys123.cc
2
3#include <iostream>
4#include <fstream>
5#include <sstream>
6#include <stdlib.h>
7#include <math.h>
8#include <map>
9
10#include "ComponentComsol.hh"
11
12namespace Garfield {
13
15
16 m_className = "ComponentComsol";
17}
18
19ComponentComsol::ComponentComsol(std::string mesh, std::string mplist,
20 std::string field)
22
23 m_className = "ComponentComsol";
24 Initialise(mesh, mplist, field);
25}
26
27bool ends_with(std::string s, std::string t) {
28 return s.size() >= t.size() && s.substr(s.size() - t.size(), t.size()) == t;
29}
30
31int readInt(std::string s) {
32 std::istringstream iss(s);
33 int ret;
34 iss >> ret;
35 return ret;
36}
37
38bool ComponentComsol::Initialise(std::string mesh, std::string mplist,
39 std::string field) {
40
41 m_ready = false;
42 m_warning = false;
43 m_nWarnings = 0;
44
45 double unit = 100.0; // m
46
47 std::string line;
48
49 // Open the materials file.
50 materials.clear();
51 std::ifstream fmplist;
52 fmplist.open(mplist.c_str(), std::ios::in);
53 if (fmplist.fail()) {
54 std::cerr << m_className << "::Initialise:\n";
55 std::cerr << " Could not open result file " << mplist
56 << " for reading.\n";
57 return false;
58 }
59 fmplist >> m_nMaterials;
60 for (unsigned int i = 0; i < m_nMaterials; ++i) {
61 Material newMaterial;
62 newMaterial.driftmedium = true;
63 newMaterial.medium = nullptr;
64 newMaterial.ohm = -1;
65 fmplist >> newMaterial.eps;
66 materials.push_back(newMaterial);
67 }
68 {
69 // add default material
70 Material newMaterial;
71 newMaterial.driftmedium = false;
72 newMaterial.medium = nullptr;
73 newMaterial.eps = newMaterial.ohm = -1;
74 materials.push_back(newMaterial);
76 }
77 std::map<int, int> domain2material;
78 int d2msize;
79 fmplist >> d2msize;
80 for (int i = 0; i < d2msize; ++i) {
81 int domain;
82 fmplist >> domain;
83 fmplist >> domain2material[domain];
84 }
85 fmplist.close();
86
87 nodes.clear();
88 std::ifstream fmesh;
89 fmesh.open(mesh.c_str(), std::ios::in);
90 if (fmesh.fail()) {
91 std::cerr << m_className << "::Initialise:\n";
92 std::cerr << " Could not open nodes file " << mesh << " for reading.\n";
93 return false;
94 }
95
96 do {
97 std::getline(fmesh, line);
98 } while (!ends_with(line, "# number of mesh points"));
99 nNodes = readInt(line);
100
101 std::cout << m_className << "::Initialise:\n";
102 std::cout << " Read " << nNodes << " nodes from file " << mesh << ".\n";
103 do {
104 std::getline(fmesh, line);
105 } while (line != "# Mesh point coordinates");
106 double minx = 1e100, miny = 1e100, minz = 1e100, maxx = -1e100, maxy = -1e100,
107 maxz = -1e100;
108 for (int i = 0; i < nNodes; ++i) {
109 Node newNode;
110 fmesh >> newNode.x >> newNode.y >> newNode.z;
111 newNode.x *= unit;
112 newNode.y *= unit;
113 newNode.z *= unit;
114 nodes.push_back(newNode);
115 minx = std::min(minx, newNode.x);
116 maxx = std::max(maxx, newNode.x);
117 miny = std::min(miny, newNode.y);
118 maxy = std::max(maxy, newNode.y);
119 minz = std::min(minz, newNode.z);
120 maxz = std::max(maxz, newNode.z);
121 }
122 std::cout << minx << " < x < " << maxx << "\n";
123 std::cout << miny << " < y < " << maxy << "\n";
124 std::cout << minz << " < z < " << maxz << "\n";
125
126 do {
127 std::getline(fmesh, line);
128 } while (line != "4 tet2 # type name");
129 do {
130 std::getline(fmesh, line);
131 } while (!ends_with(line, "# number of elements"));
132 nElements = readInt(line);
133 elements.clear();
134 std::cout << m_className << "::Initialise:\n";
135 std::cout << " Read " << nElements << " elements from file " << mesh
136 << ".\n";
137 std::getline(fmesh, line);
138 // elements 6 & 7 are swapped due to differences in COMSOL and ANSYS
139 // representation
140 int perm[10] = {0, 1, 2, 3, 4, 5, 7, 6, 8, 9};
141 for (int i = 0; i < nElements; ++i) {
142 Element newElement;
143 newElement.degenerate = false;
144 for (int j = 0; j < 10; ++j) {
145 fmesh >> newElement.emap[perm[j]];
146 }
147 elements.push_back(newElement);
148 }
149
150 do {
151 std::getline(fmesh, line);
152 } while (line != "# Geometric entity indices");
153 for (int i = 0; i < nElements; ++i) {
154 int domain;
155 fmesh >> domain;
156 elements[i].matmap = domain2material.count(domain) ? domain2material[domain]
157 : m_nMaterials - 1;
158 }
159 fmesh.close();
160
161 std::map<Node, std::vector<int>, nodeCmp> nodeIdx;
162 for (int i = 0; i < nNodes; ++i) {
163 nodeIdx[nodes[i]].push_back(i);
164 }
165 std::cout << "Map size: " << nodeIdx.size() << std::endl;
166
167 std::ifstream ffield;
168 ffield.open(field.c_str(), std::ios::in);
169 if (ffield.fail()) {
170 std::cerr << m_className << "::Initialise:\n";
171 std::cerr << " Could not open field potentials file " << field
172 << " for reading.\n";
173 return false;
174 }
175 do {
176 std::getline(ffield, line);
177 } while (line.substr(0, 81) !=
178 "% x y z "
179 " V (V)");
180 {
181 std::istringstream sline(line);
182 std::string token;
183 sline >> token; // %
184 sline >> token; // x
185 sline >> token; // y
186 sline >> token; // z
187 sline >> token; // V
188 sline >> token; // (V)
189 while (sline >> token) {
190 std::cout << m_className << "::Initialise:\n";
191 std::cout << " Reading data for weighting field " << token << ".\n";
193 wfields.push_back(token);
194 wfieldsOk.push_back(true);
195 sline >> token; // (V)
196 }
197 }
198 for (int i = 0; i < nNodes; ++i) {
199 Node tmp;
200 ffield >> tmp.x >> tmp.y >> tmp.z >> tmp.v;
201 tmp.x *= unit;
202 tmp.y *= unit;
203 tmp.z *= unit;
204 for (int j = 0; j < nWeightingFields; ++j) {
205 double w;
206 ffield >> w;
207 tmp.w.push_back(w);
208 }
209 int closest = -1;
210 double closestDist = 1;
211 const unsigned int nIdx = nodeIdx[tmp].size();
212 // for (int j : nodeIdx[tmp]) {
213 for (unsigned int k = 0; k < nIdx; ++k) {
214 int j = nodeIdx[tmp][k];
215 double dist = (tmp.x - nodes[j].x) * (tmp.x - nodes[j].x) +
216 (tmp.y - nodes[j].y) * (tmp.y - nodes[j].y) +
217 (tmp.z - nodes[j].z) * (tmp.z - nodes[j].z);
218 if (dist < closestDist) {
219 closestDist = dist;
220 closest = j;
221 }
222 }
223 if (closest == -1) {
224 std::cerr << m_className << "::Initialise:\n";
225 std::cerr << " Could not match the node from field potentials file: "
226 << tmp.x << " " << tmp.y << " " << tmp.z << "\n.";
227 return false;
228 }
229 nodes[closest].v = tmp.v;
230 nodes[closest].w = tmp.w;
231 }
232
233 m_ready = true;
234
235 // for (int i = 0; i < nNodes; ++i) {
236 // double ex, ey, ez, v;
237 // Medium* m;
238 // int status;
239 // ElectricField(nodes[i].x, nodes[i].y, nodes[i].z, ex, ey, ez, v, m,
240 // status);
241 // std::cout << "Field at " << nodes[i].x << " " << nodes[i].y << " " <<
242 // nodes[i].z << ": " << ex << " " << ey << " " << ez << " " << v << "\n";
243 // }
244
245 // Establish the ranges.
246 SetRange();
248 return true;
249}
250
251bool ComponentComsol::SetWeightingField(std::string field, std::string label) {
252 double unit = 100.0; // m;
253
254 if (!m_ready) {
255 std::cerr << m_className << "::SetWeightingField:\n";
256 std::cerr << " No valid field map is present.\n";
257 std::cerr << " Weighting field cannot be added.\n";
258 return false;
259 }
260
261 // Open the voltage list.
262 std::ifstream ffield;
263 ffield.open(field.c_str(), std::ios::in);
264 if (ffield.fail()) {
265 std::cerr << m_className << "::Initialise:\n";
266 std::cerr << " Could not open field potentials file " << field
267 << " for reading.\n";
268 return false;
269 }
270
271 // Check if a weighting field with the same label alm_ready exists.
272 int iw = nWeightingFields;
273 for (int i = nWeightingFields; i--;) {
274 if (wfields[i] == label) {
275 iw = i;
276 break;
277 }
278 }
279 if (iw == nWeightingFields) {
283 for (int j = 0; j < nNodes; ++j) {
284 nodes[j].w.resize(nWeightingFields);
285 }
286 } else {
287 std::cout << m_className << "::SetWeightingField:\n";
288 std::cout << " Replacing existing weighting field " << label << ".\n";
289 }
290 wfields[iw] = label;
291 wfieldsOk[iw] = false;
292 std::map<Node, std::vector<int>, nodeCmp> nodeIdx;
293 for (int i = 0; i < nNodes; ++i) {
294 nodeIdx[nodes[i]].push_back(i);
295 }
296 std::cout << "Map size: " << nodeIdx.size() << std::endl;
297
298 std::string line;
299 do {
300 std::getline(ffield, line);
301 } while (line !=
302 "% x y z "
303 " V (V)");
304 for (int i = 0; i < nNodes; ++i) {
305 Node tmp;
306 ffield >> tmp.x >> tmp.y >> tmp.z >> tmp.v;
307 tmp.x *= unit;
308 tmp.y *= unit;
309 tmp.z *= unit;
310 int closest = -1;
311 double closestDist = 1;
312 const unsigned int nIdx = nodeIdx[tmp].size();
313 // for (int j : nodeIdx[tmp]) {
314 for (unsigned int k = 0; k < nIdx; ++k) {
315 int j = nodeIdx[tmp][k];
316 double dist = (tmp.x - nodes[j].x) * (tmp.x - nodes[j].x) +
317 (tmp.y - nodes[j].y) * (tmp.y - nodes[j].y) +
318 (tmp.z - nodes[j].z) * (tmp.z - nodes[j].z);
319 if (dist < closestDist) {
320 closestDist = dist;
321 closest = j;
322 }
323 }
324 if (closest == -1) {
325 std::cerr << m_className << "::Initialise:\n";
326 std::cerr << " Could not match the node from field potentials file: "
327 << tmp.x << " " << tmp.y << " " << tmp.z << "\n.";
328 return false;
329 }
330 nodes[closest].w[iw] = tmp.v;
331 }
332
333 return true;
334}
335
336void ComponentComsol::ElectricField(const double x, const double y,
337 const double z, double& ex, double& ey,
338 double& ez, Medium*& m, int& status) {
339
340 double v = 0.;
341 ElectricField(x, y, z, ex, ey, ez, v, m, status);
342}
343
344void ComponentComsol::ElectricField(const double xin, const double yin,
345 const double zin, double& ex, double& ey,
346 double& ez, double& volt, Medium*& m,
347 int& status) {
348
349 // Copy the coordinates
350 double x = xin, y = yin, z = zin;
351
352 // Map the coordinates onto field map coordinates
353 bool xmirr, ymirr, zmirr;
354 double rcoordinate, rotation;
355 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
356
357 // Initial values
358 ex = ey = ez = volt = 0.;
359 status = 0;
360 m = NULL;
361
362 // Do not proceed if not properly initialised.
363 if (!m_ready) {
364 status = -10;
365 PrintNotReady("ElectricField");
366 return;
367 }
368
369 if (m_warning) PrintWarning("ElectricField");
370
371 // Find the element that contains this point
372 double t1, t2, t3, t4, jac[4][4], det;
373 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
374 if (imap < 0) {
375 if (m_debug) {
376 std::cout << m_className << "::ElectricField:\n";
377 std::cout << " Point (" << x << ", " << y << ", " << z
378 << " not in the mesh.\n";
379 }
380 status = -6;
381 return;
382 }
383
384 if (m_debug) {
385 PrintElement("ElectricField", x, y, z, t1, t2, t3, t4, imap, 10);
386 }
387
388 const Element& element = elements[imap];
389 const Node& n0 = nodes[element.emap[0]];
390 const Node& n1 = nodes[element.emap[1]];
391 const Node& n2 = nodes[element.emap[2]];
392 const Node& n3 = nodes[element.emap[3]];
393 const Node& n4 = nodes[element.emap[4]];
394 const Node& n5 = nodes[element.emap[5]];
395 const Node& n6 = nodes[element.emap[6]];
396 const Node& n7 = nodes[element.emap[7]];
397 const Node& n8 = nodes[element.emap[8]];
398 const Node& n9 = nodes[element.emap[9]];
399 // Tetrahedral field
400 volt = n0.v * t1 * (2 * t1 - 1) + n1.v * t2 * (2 * t2 - 1) +
401 n2.v * t3 * (2 * t3 - 1) + n3.v * t4 * (2 * t4 - 1) +
402 4 * n4.v * t1 * t2 + 4 * n5.v * t1 * t3 + 4 * n6.v * t1 * t4 +
403 4 * n7.v * t2 * t3 + 4 * n8.v * t2 * t4 + 4 * n9.v * t3 * t4;
404 ex = -(n0.v * (4 * t1 - 1) * jac[0][1] + n1.v * (4 * t2 - 1) * jac[1][1] +
405 n2.v * (4 * t3 - 1) * jac[2][1] + n3.v * (4 * t4 - 1) * jac[3][1] +
406 n4.v * (4 * t2 * jac[0][1] + 4 * t1 * jac[1][1]) +
407 n5.v * (4 * t3 * jac[0][1] + 4 * t1 * jac[2][1]) +
408 n6.v * (4 * t4 * jac[0][1] + 4 * t1 * jac[3][1]) +
409 n7.v * (4 * t3 * jac[1][1] + 4 * t2 * jac[2][1]) +
410 n8.v * (4 * t4 * jac[1][1] + 4 * t2 * jac[3][1]) +
411 n9.v * (4 * t4 * jac[2][1] + 4 * t3 * jac[3][1])) /
412 det;
413 ey = -(n0.v * (4 * t1 - 1) * jac[0][2] + n1.v * (4 * t2 - 1) * jac[1][2] +
414 n2.v * (4 * t3 - 1) * jac[2][2] + n3.v * (4 * t4 - 1) * jac[3][2] +
415 n4.v * (4 * t2 * jac[0][2] + 4 * t1 * jac[1][2]) +
416 n5.v * (4 * t3 * jac[0][2] + 4 * t1 * jac[2][2]) +
417 n6.v * (4 * t4 * jac[0][2] + 4 * t1 * jac[3][2]) +
418 n7.v * (4 * t3 * jac[1][2] + 4 * t2 * jac[2][2]) +
419 n8.v * (4 * t4 * jac[1][2] + 4 * t2 * jac[3][2]) +
420 n9.v * (4 * t4 * jac[2][2] + 4 * t3 * jac[3][2])) /
421 det;
422 ez = -(n0.v * (4 * t1 - 1) * jac[0][3] + n1.v * (4 * t2 - 1) * jac[1][3] +
423 n2.v * (4 * t3 - 1) * jac[2][3] + n3.v * (4 * t4 - 1) * jac[3][3] +
424 n4.v * (4 * t2 * jac[0][3] + 4 * t1 * jac[1][3]) +
425 n5.v * (4 * t3 * jac[0][3] + 4 * t1 * jac[2][3]) +
426 n6.v * (4 * t4 * jac[0][3] + 4 * t1 * jac[3][3]) +
427 n7.v * (4 * t3 * jac[1][3] + 4 * t2 * jac[2][3]) +
428 n8.v * (4 * t4 * jac[1][3] + 4 * t2 * jac[3][3]) +
429 n9.v * (4 * t4 * jac[2][3] + 4 * t3 * jac[3][3])) /
430 det;
431
432 // Transform field to global coordinates
433 UnmapFields(ex, ey, ez, x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
434 // std::cout << "ef @(" << xin << ", " << yin << ", " << zin << ") = " <<
435 // volt << "\n";
436
437 // Drift medium?
438 if (m_debug) {
439 std::cout << m_className << "::ElectricField:\n";
440 std::cout << " Material " << element.matmap << ", drift flag "
441 << materials[element.matmap].driftmedium << "\n";
442 }
443 m = materials[element.matmap].medium;
444 status = -5;
445 if (materials[element.matmap].driftmedium) {
446 if (m && m->IsDriftable()) status = 0;
447 }
448}
449
450void ComponentComsol::WeightingField(const double xin, const double yin,
451 const double zin, double& wx, double& wy,
452 double& wz, const std::string& label) {
453
454 // Initial values
455 wx = wy = wz = 0;
456
457 // Do not proceed if not properly initialised.
458 if (!m_ready) return;
459
460 // Look for the label.
461 int iw = 0;
462 bool found = false;
463 for (int i = nWeightingFields; i--;) {
464 if (wfields[i] == label) {
465 iw = i;
466 found = true;
467 break;
468 }
469 }
470
471 // Do not proceed if the requested weighting field does not exist.
472 if (!found) return;
473 // Check if the weighting field is properly initialised.
474 if (!wfieldsOk[iw]) return;
475
476 // Copy the coordinates.
477 double x = xin, y = yin, z = zin;
478
479 // Map the coordinates onto field map coordinates
480 bool xmirr, ymirr, zmirr;
481 double rcoordinate, rotation;
482 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
483
484 if (m_warning) PrintWarning("WeightingField");
485
486 // Find the element that contains this point.
487 double t1, t2, t3, t4, jac[4][4], det;
488 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
489 // Check if the point is in the mesh.
490 if (imap < 0) return;
491
492 if (m_debug) {
493 PrintElement("WeightingField", x, y, z, t1, t2, t3, t4, imap, 10, iw);
494 }
495
496 const Element& element = elements[imap];
497 const Node& n0 = nodes[element.emap[0]];
498 const Node& n1 = nodes[element.emap[1]];
499 const Node& n2 = nodes[element.emap[2]];
500 const Node& n3 = nodes[element.emap[3]];
501 const Node& n4 = nodes[element.emap[4]];
502 const Node& n5 = nodes[element.emap[5]];
503 const Node& n6 = nodes[element.emap[6]];
504 const Node& n7 = nodes[element.emap[7]];
505 const Node& n8 = nodes[element.emap[8]];
506 const Node& n9 = nodes[element.emap[9]];
507 // Tetrahedral field
508 wx = -(n0.w[iw] * (4 * t1 - 1) * jac[0][1] +
509 n1.w[iw] * (4 * t2 - 1) * jac[1][1] +
510 n2.w[iw] * (4 * t3 - 1) * jac[2][1] +
511 n3.w[iw] * (4 * t4 - 1) * jac[3][1] +
512 n4.w[iw] * (4 * t2 * jac[0][1] + 4 * t1 * jac[1][1]) +
513 n5.w[iw] * (4 * t3 * jac[0][1] + 4 * t1 * jac[2][1]) +
514 n6.w[iw] * (4 * t4 * jac[0][1] + 4 * t1 * jac[3][1]) +
515 n7.w[iw] * (4 * t3 * jac[1][1] + 4 * t2 * jac[2][1]) +
516 n8.w[iw] * (4 * t4 * jac[1][1] + 4 * t2 * jac[3][1]) +
517 n9.w[iw] * (4 * t4 * jac[2][1] + 4 * t3 * jac[3][1])) /
518 det;
519
520 wy = -(n0.w[iw] * (4 * t1 - 1) * jac[0][2] +
521 n1.w[iw] * (4 * t2 - 1) * jac[1][2] +
522 n2.w[iw] * (4 * t3 - 1) * jac[2][2] +
523 n3.w[iw] * (4 * t4 - 1) * jac[3][2] +
524 n4.w[iw] * (4 * t2 * jac[0][2] + 4 * t1 * jac[1][2]) +
525 n5.w[iw] * (4 * t3 * jac[0][2] + 4 * t1 * jac[2][2]) +
526 n6.w[iw] * (4 * t4 * jac[0][2] + 4 * t1 * jac[3][2]) +
527 n7.w[iw] * (4 * t3 * jac[1][2] + 4 * t2 * jac[2][2]) +
528 n8.w[iw] * (4 * t4 * jac[1][2] + 4 * t2 * jac[3][2]) +
529 n9.w[iw] * (4 * t4 * jac[2][2] + 4 * t3 * jac[3][2])) /
530 det;
531
532 wz = -(n0.w[iw] * (4 * t1 - 1) * jac[0][3] +
533 n1.w[iw] * (4 * t2 - 1) * jac[1][3] +
534 n2.w[iw] * (4 * t3 - 1) * jac[2][3] +
535 n3.w[iw] * (4 * t4 - 1) * jac[3][3] +
536 n4.w[iw] * (4 * t2 * jac[0][3] + 4 * t1 * jac[1][3]) +
537 n5.w[iw] * (4 * t3 * jac[0][3] + 4 * t1 * jac[2][3]) +
538 n6.w[iw] * (4 * t4 * jac[0][3] + 4 * t1 * jac[3][3]) +
539 n7.w[iw] * (4 * t3 * jac[1][3] + 4 * t2 * jac[2][3]) +
540 n8.w[iw] * (4 * t4 * jac[1][3] + 4 * t2 * jac[3][3]) +
541 n9.w[iw] * (4 * t4 * jac[2][3] + 4 * t3 * jac[3][3])) /
542 det;
543
544 // Transform field to global coordinates
545 UnmapFields(wx, wy, wz, x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
546}
547
548double ComponentComsol::WeightingPotential(const double xin, const double yin,
549 const double zin,
550 const std::string& label) {
551
552 // Do not proceed if not properly initialised.
553 if (!m_ready) return 0.;
554
555 // Look for the label.
556 int iw = 0;
557 bool found = false;
558 for (int i = nWeightingFields; i--;) {
559 if (wfields[i] == label) {
560 iw = i;
561 found = true;
562 break;
563 }
564 }
565
566 // Do not proceed if the requested weighting field does not exist.
567 if (!found) return 0.;
568 // Check if the weighting field is properly initialised.
569 if (!wfieldsOk[iw]) return 0.;
570
571 // Copy the coordinates.
572 double x = xin, y = yin, z = zin;
573
574 // Map the coordinates onto field map coordinates.
575 bool xmirr, ymirr, zmirr;
576 double rcoordinate, rotation;
577 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
578
579 if (m_warning) PrintWarning("WeightingPotential");
580
581 // Find the element that contains this point.
582 double t1, t2, t3, t4, jac[4][4], det;
583 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
584 if (imap < 0) return 0.;
585
586 if (m_debug) {
587 PrintElement("WeightingPotential", x, y, z, t1, t2, t3, t4, imap, 10, iw);
588 }
589
590 const Element& element = elements[imap];
591 const Node& n0 = nodes[element.emap[0]];
592 const Node& n1 = nodes[element.emap[1]];
593 const Node& n2 = nodes[element.emap[2]];
594 const Node& n3 = nodes[element.emap[3]];
595 const Node& n4 = nodes[element.emap[4]];
596 const Node& n5 = nodes[element.emap[5]];
597 const Node& n6 = nodes[element.emap[6]];
598 const Node& n7 = nodes[element.emap[7]];
599 const Node& n8 = nodes[element.emap[8]];
600 const Node& n9 = nodes[element.emap[9]];
601 // Tetrahedral field
602 return n0.w[iw] * t1 * (2 * t1 - 1) + n1.w[iw] * t2 * (2 * t2 - 1) +
603 n2.w[iw] * t3 * (2 * t3 - 1) + n3.w[iw] * t4 * (2 * t4 - 1) +
604 4 * n4.w[iw] * t1 * t2 + 4 * n5.w[iw] * t1 * t3 +
605 4 * n6.w[iw] * t1 * t4 + 4 * n7.w[iw] * t2 * t3 +
606 4 * n8.w[iw] * t2 * t4 + 4 * n9.w[iw] * t3 * t4;
607}
608
609Medium* ComponentComsol::GetMedium(const double xin, const double yin,
610 const double zin) {
611
612 // Copy the coordinates
613 double x = xin, y = yin, z = zin;
614
615 // Map the coordinates onto field map coordinates
616 bool xmirr, ymirr, zmirr;
617 double rcoordinate, rotation;
618 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
619
620 // Do not proceed if not properly initialised.
621 if (!m_ready) {
622 PrintNotReady("GetMedium");
623 return nullptr;
624 }
625 if (m_warning) PrintWarning("GetMedium");
626
627 // Find the element that contains this point
628 double t1, t2, t3, t4, jac[4][4], det;
629 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
630 if (imap < 0) {
631 if (m_debug) {
632 std::cout << m_className << "::GetMedium:\n";
633 std::cout << " Point (" << x << ", " << y << ", " << z
634 << ") not in the mesh.\n";
635 }
636 return nullptr;
637 }
638 const Element& element = elements[imap];
639 if (element.matmap >= m_nMaterials) {
640 if (m_debug) {
641 std::cerr << m_className << "::GetMedium:\n";
642 std::cerr << " Point (" << x << ", " << y
643 << ") has out of range material number " << imap << ".\n";
644 }
645 return nullptr;
646 }
647
648 if (m_debug) {
649 PrintElement("GetMedium", x, y, z, t1, t2, t3, t4, imap, 10);
650 }
651
652 return materials[element.matmap].medium;
653}
654
655double ComponentComsol::GetElementVolume(const unsigned int i) {
656
657 if (i >= elements.size()) return 0.;
658 const Element& element = elements[i];
659 const Node& n0 = nodes[element.emap[0]];
660 const Node& n1 = nodes[element.emap[1]];
661 const Node& n2 = nodes[element.emap[2]];
662 const Node& n3 = nodes[element.emap[3]];
663
664 // Uses formula V = |a (dot) b x c|/6
665 // with a => "3", b => "1", c => "2" and origin "0"
666 const double vol =
667 fabs((n3.x - n0.x) *
668 ((n1.y - n0.y) * (n2.z - n0.z) - (n2.y - n0.y) * (n1.z - n0.z)) +
669 (n3.y - n0.y) *
670 ((n1.z - n0.z) * (n2.x - n0.x) - (n2.z - n0.z) * (n1.x - n0.x)) +
671 (n3.z - n0.z) * ((n1.x - n0.x) * (n2.y - n0.y) -
672 (n3.x - n0.x) * (n1.y - n0.y))) /
673 6.;
674 return vol;
675}
676
677void ComponentComsol::GetAspectRatio(const unsigned int i, double& dmin,
678 double& dmax) {
679
680 if (i >= elements.size()) {
681 dmin = dmax = 0.;
682 return;
683 }
684
685 const Element& element = elements[i];
686 const int np = 4;
687 // Loop over all pairs of vertices.
688 for (int j = 0; j < np - 1; ++j) {
689 const Node& nj = nodes[element.emap[j]];
690 for (int k = j + 1; k < np; ++k) {
691 const Node& nk = nodes[element.emap[k]];
692 // Compute distance.
693 const double dx = nj.x - nk.x;
694 const double dy = nj.y - nk.y;
695 const double dz = nj.z - nk.z;
696 const double dist = sqrt(dx * dx + dy * dy + dz * dz);
697 if (k == 1) {
698 dmin = dmax = dist;
699 } else {
700 if (dist < dmin) dmin = dist;
701 if (dist > dmax) dmax = dist;
702 }
703 }
704 }
705}
706
707} // namespace Garfield
bool m_ready
Ready for use?
std::string m_className
Class name.
bool m_debug
Switch on/off debugging messages.
Medium * GetMedium(const double x, const double y, const double z)
Get the medium at a given location (x, y, z).
void UpdatePeriodicity()
Verify periodicities.
void GetAspectRatio(const unsigned int i, double &dmin, double &dmax)
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status)
double GetElementVolume(const unsigned int i)
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
bool SetWeightingField(std::string file, std::string label)
double WeightingPotential(const double x, const double y, const double z, const std::string &label)
bool Initialise(std::string header="mesh.mphtxt", std::string mplist="dielectrics.dat", std::string field="field.txt")
Base class for components based on finite-element field maps.
void MapCoordinates(double &xpos, double &ypos, double &zpos, bool &xmirrored, bool &ymirrored, bool &zmirrored, double &rcoordinate, double &rotation) const
Move (xpos, ypos, zpos) to field map coordinates.
void PrintWarning(const std::string &header)
void PrintElement(const std::string &header, const double x, const double y, const double z, const double t1, const double t2, const double t3, const double t4, const unsigned int i, const unsigned int n, const int iw=-1) const
std::vector< Material > materials
int FindElement13(const double x, const double y, const double z, double &t1, double &t2, double &t3, double &t4, double jac[4][4], double &det)
Find the element for a point in curved quadratic tetrahedra.
virtual void SetRange()
Calculate x, y, z, V and angular ranges.
std::vector< Element > elements
std::vector< std::string > wfields
void UnmapFields(double &ex, double &ey, double &ez, double &xpos, double &ypos, double &zpos, bool &xmirrored, bool &ymirrored, bool &zmirrored, double &rcoordinate, double &rotation) const
Move (ex, ey, ez) to global coordinates.
void PrintNotReady(const std::string &header) const
Abstract base class for media.
Definition: Medium.hh:11
bool IsDriftable() const
Definition: Medium.hh:57
int readInt(std::string s)
bool ends_with(std::string s, std::string t)