Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentElmer.cc
Go to the documentation of this file.
1#include <math.h>
2#include <stdlib.h>
3#include <fstream>
4#include <iostream>
5
7
8namespace {
9
10void PrintErrorReadingFile(const std::string& hdr, const std::string& file,
11 const int line) {
12 std::cerr << hdr << "\n Error reading file " << file << " (line " << line
13 << ").\n";
14}
15
16}
17
18namespace Garfield {
19
21
22ComponentElmer::ComponentElmer(const std::string& header,
23 const std::string& elist,
24 const std::string& nlist,
25 const std::string& mplist,
26 const std::string& volt, const std::string& unit)
27 : ComponentFieldMap("Elmer") {
28 Initialise(header, elist, nlist, mplist, volt, unit);
29}
30
31bool ComponentElmer::Initialise(const std::string& header,
32 const std::string& elist,
33 const std::string& nlist,
34 const std::string& mplist,
35 const std::string& volt,
36 const std::string& unit) {
37 const std::string hdr = m_className + "::Initialise:";
38 Reset();
39
40 // Keep track of the success.
41 bool ok = true;
42
43 // Buffer for reading
44 constexpr int size = 100;
45 char line[size];
46
47 // Open the header.
48 std::ifstream fheader;
49 fheader.open(header.c_str(), std::ios::in);
50 if (fheader.fail()) {
51 PrintCouldNotOpen("Initialise", header);
52 return false;
53 }
54
55 // Temporary variables for use in file reading
56 char* token = NULL;
57 bool readerror = false;
58 bool readstop = false;
59 int il = 0;
60
61 // Read the header to get the number of nodes and elements.
62 fheader.getline(line, size, '\n');
63 token = strtok(line, " ");
64 const int nNodes = ReadInteger(token, 0, readerror);
65 token = strtok(NULL, " ");
66 const int nElements = ReadInteger(token, 0, readerror);
67 std::cout << hdr << "\n Read " << nNodes << " nodes and " << nElements
68 << " elements from file " << header << ".\n";
69 if (readerror) {
70 PrintErrorReadingFile(hdr, header, il);
71 fheader.close();
72 return false;
73 }
74
75 // Close the header file.
76 fheader.close();
77
78 // Open the nodes list.
79 std::ifstream fnodes;
80 fnodes.open(nlist.c_str(), std::ios::in);
81 if (fnodes.fail()) {
82 PrintCouldNotOpen("Initialise", nlist);
83 return false;
84 }
85
86 // Check the value of the unit.
87 double funit = ScalingFactor(unit);
88 if (funit <= 0.) {
89 std::cerr << hdr << " Unknown length unit " << unit << ".\n";
90 ok = false;
91 funit = 1.0;
92 }
93 if (m_debug) std::cout << hdr << " Unit scaling factor = " << funit << ".\n";
94
95 // Read the nodes from the file.
96 for (il = 0; il < nNodes; il++) {
97 // Get a line from the nodes file.
98 fnodes.getline(line, size, '\n');
99
100 // Ignore the first two characters.
101 token = strtok(line, " ");
102 token = strtok(NULL, " ");
103
104 // Get the node coordinates.
105 token = strtok(NULL, " ");
106 double xnode = ReadDouble(token, -1, readerror);
107 token = strtok(NULL, " ");
108 double ynode = ReadDouble(token, -1, readerror);
109 token = strtok(NULL, " ");
110 double znode = ReadDouble(token, -1, readerror);
111 if (readerror) {
112 PrintErrorReadingFile(hdr, nlist, il);
113 fnodes.close();
114 return false;
115 }
116
117 // Set up and create a new node.
118 Node newNode;
119 newNode.w.clear();
120 newNode.x = xnode * funit;
121 newNode.y = ynode * funit;
122 newNode.z = znode * funit;
123 m_nodes.push_back(std::move(newNode));
124 }
125
126 // Close the nodes file.
127 fnodes.close();
128
129 // Open the potential file.
130 std::ifstream fvolt;
131 fvolt.open(volt.c_str(), std::ios::in);
132 if (fvolt.fail()) {
133 PrintCouldNotOpen("Initialise", volt);
134 return false;
135 }
136
137 // Reset the line counter.
138 il = 1;
139
140 // Read past the header.
141 while (!readstop && fvolt.getline(line, size, '\n')) {
142 token = strtok(line, " ");
143 if (strcmp(token, "Perm:") == 0) readstop = true;
144 il++;
145 }
146
147 // Should have stopped: if not, print error message.
148 if (!readstop) {
149 std::cerr << hdr << "\n Error reading past header of potentials file "
150 << volt << ".\n";
151 fvolt.close();
152 return false;
153 }
154
155 // Read past the permutation map (number of lines = nNodes).
156 for (int tl = 0; tl < nNodes; tl++) {
157 fvolt.getline(line, size, '\n');
158 il++;
159 }
160
161 // Read the potentials.
162 for (int tl = 0; tl < nNodes; tl++) {
163 fvolt.getline(line, size, '\n');
164 token = strtok(line, " ");
165 double v = ReadDouble(token, -1, readerror);
166 if (readerror) {
167 PrintErrorReadingFile(hdr, volt, il);
168 fvolt.close();
169 return false;
170 }
171 // Place the voltage in its appropriate node.
172 m_nodes[tl].v = v;
173 }
174
175 // Close the potentials file.
176 fvolt.close();
177
178 // Open the materials file.
179 std::ifstream fmplist;
180 fmplist.open(mplist.c_str(), std::ios::in);
181 if (fmplist.fail()) {
182 PrintCouldNotOpen("Initialise", mplist);
183 return false;
184 }
185
186 // Read the dielectric constants from the materials file.
187 fmplist.getline(line, size, '\n');
188 token = strtok(line, " ");
189 if (readerror) {
190 std::cerr << hdr << "\n Error reading number of materials from "
191 << mplist << ".\n";
192 fmplist.close();
193 return false;
194 }
195 const unsigned int nMaterials = ReadInteger(token, 0, readerror);
196 m_materials.resize(nMaterials);
197 for (auto& material : m_materials) {
198 material.ohm = -1;
199 material.eps = -1;
200 material.medium = nullptr;
201 }
202 for (il = 2; il < ((int)nMaterials + 2); il++) {
203 fmplist.getline(line, size, '\n');
204 token = strtok(line, " ");
205 ReadInteger(token, -1, readerror);
206 token = strtok(NULL, " ");
207 double dc = ReadDouble(token, -1.0, readerror);
208 if (readerror) {
209 PrintErrorReadingFile(hdr, mplist, il);
210 fmplist.close();
211 return false;
212 }
213 m_materials[il - 2].eps = dc;
214 std::cout << hdr << "\n Set material " << il - 2 << " of "
215 << nMaterials << " to eps " << dc << ".\n";
216 }
217
218 // Close the materials file.
219 fmplist.close();
220
221 // Find lowest epsilon, check for eps = 0, set default drift medium.
222 if (!SetDefaultDriftMedium()) ok = false;
223
224 // Open the elements file.
225 std::ifstream felems;
226 felems.open(elist.c_str(), std::ios::in);
227 if (felems.fail()) {
228 PrintCouldNotOpen("Initialise", elist);
229 return false;
230 }
231
232 // Read the elements and their material indices.
233 m_elements.clear();
234 int highestnode = 0;
235
236 for (il = 0; il < nElements; il++) {
237 // Get a line
238 felems.getline(line, size, '\n');
239
240 // Split into tokens.
241 token = strtok(line, " ");
242 // Read the 2nd-order element
243 // Note: Ordering of Elmer elements can be described in the
244 // ElmerSolver manual (appendix D. at the time of this comment)
245 // If the order read below is compared to the shape functions used
246 // eg. in ElectricField, the order is wrong, but note at the
247 // end of this function the order of elements 5,6,7 will change to
248 // 7,5,6 when actually recorded in newElement.emap to correct for this
249 token = strtok(NULL, " ");
250 int imat = ReadInteger(token, -1, readerror) - 1;
251 token = strtok(NULL, " ");
252 token = strtok(NULL, " ");
253 int in0 = ReadInteger(token, -1, readerror);
254 token = strtok(NULL, " ");
255 int in1 = ReadInteger(token, -1, readerror);
256 token = strtok(NULL, " ");
257 int in2 = ReadInteger(token, -1, readerror);
258 token = strtok(NULL, " ");
259 int in3 = ReadInteger(token, -1, readerror);
260 token = strtok(NULL, " ");
261 int in4 = ReadInteger(token, -1, readerror);
262 token = strtok(NULL, " ");
263 int in5 = ReadInteger(token, -1, readerror);
264 token = strtok(NULL, " ");
265 int in6 = ReadInteger(token, -1, readerror);
266 token = strtok(NULL, " ");
267 int in7 = ReadInteger(token, -1, readerror);
268 token = strtok(NULL, " ");
269 int in8 = ReadInteger(token, -1, readerror);
270 token = strtok(NULL, " ");
271 int in9 = ReadInteger(token, -1, readerror);
272
273 if (m_debug && il < 10) {
274 std::cout << " Read nodes " << in0 << ", " << in1 << ", " << in2
275 << ", " << in3 << ", ... from element " << il + 1 << " of "
276 << nElements << " with mat " << imat << ".\n";
277 }
278
279 // Check synchronisation.
280 if (readerror) {
281 PrintErrorReadingFile(hdr, elist, il);
282 felems.close();
283 return false;
284 }
285
286 // Check the material number and ensure that epsilon is non-negative.
287 if (imat < 0 || imat > (int)nMaterials) {
288 std::cerr << hdr << "\n Out-of-range material number on file " << elist
289 << " (line " << il << ").\n";
290 std::cerr << " Element: " << il << ", material: " << imat << "\n";
291 std::cerr << " nodes: (" << in0 << ", " << in1 << ", " << in2 << ", "
292 << in3 << ", " << in4 << ", " << in5 << ", " << in6 << ", "
293 << in7 << ", " << in8 << ", " << in9 << ")\n";
294 ok = false;
295 }
296 if (m_materials[imat].eps < 0) {
297 std::cerr << hdr << "\n Element " << il << " in element list " << elist
298 << "\n uses material " << imat
299 << " which has not been assigned a positive permittivity in "
300 << mplist << ".\n";
301 ok = false;
302 }
303
304 // Check the node numbers.
305 if (in0 < 1 || in1 < 1 || in2 < 1 || in3 < 1 || in4 < 1 || in5 < 1 ||
306 in6 < 1 || in7 < 1 || in8 < 1 || in9 < 1) {
307 std::cerr << hdr << "\n Found a node number < 1 on file " << elist
308 << " (line " << il << ").\n Element: " << il
309 << ", material: " << imat << "\n nodes: (" << in0 << ", "
310 << in1 << ", " << in2 << ", " << in3 << ", " << in4 << ", "
311 << in5 << ", " << in6 << ", " << in7 << ", " << in8 << ", "
312 << in9 << ")\n";
313 ok = false;
314 }
315 if (in0 > highestnode) highestnode = in0;
316 if (in1 > highestnode) highestnode = in1;
317 if (in2 > highestnode) highestnode = in2;
318 if (in3 > highestnode) highestnode = in3;
319 if (in4 > highestnode) highestnode = in4;
320 if (in5 > highestnode) highestnode = in5;
321 if (in6 > highestnode) highestnode = in6;
322 if (in7 > highestnode) highestnode = in7;
323 if (in8 > highestnode) highestnode = in8;
324 if (in9 > highestnode) highestnode = in9;
325
326 // These elements must not be degenerate.
327 if (in0 == in1 || in0 == in2 || in0 == in3 || in0 == in4 || in0 == in5 ||
328 in0 == in6 || in0 == in7 || in0 == in8 || in0 == in9 || in1 == in2 ||
329 in1 == in3 || in1 == in4 || in1 == in5 || in1 == in6 || in1 == in7 ||
330 in1 == in8 || in1 == in9 || in2 == in3 || in2 == in4 || in2 == in5 ||
331 in2 == in6 || in2 == in7 || in2 == in8 || in2 == in9 || in3 == in4 ||
332 in3 == in5 || in3 == in6 || in3 == in7 || in3 == in8 || in3 == in9 ||
333 in4 == in5 || in4 == in6 || in4 == in7 || in4 == in8 || in4 == in9 ||
334 in5 == in6 || in5 == in7 || in5 == in8 || in5 == in9 || in6 == in7 ||
335 in6 == in8 || in6 == in9 || in7 == in8 || in7 == in9 || in8 == in9) {
336 std::cerr << hdr << "\n Element " << il << " of file " << elist
337 << " is degenerate,\n"
338 << " no such elements are allowed in this type of map.\n";
339 ok = false;
340 }
341 Element newElement;
342 newElement.degenerate = false;
343
344 // Store the material reference.
345 newElement.matmap = imat;
346
347 // Node references
348 newElement.emap[0] = in0 - 1;
349 newElement.emap[1] = in1 - 1;
350 newElement.emap[2] = in2 - 1;
351 newElement.emap[3] = in3 - 1;
352 newElement.emap[4] = in4 - 1;
353 newElement.emap[7] = in5 - 1;
354 newElement.emap[5] = in6 - 1;
355 newElement.emap[6] = in7 - 1;
356 newElement.emap[8] = in8 - 1;
357 newElement.emap[9] = in9 - 1;
358 m_elements.push_back(std::move(newElement));
359 }
360
361 // Close the elements file.
362 felems.close();
363
364 // Set the ready flag.
365 if (ok) {
366 m_ready = true;
367 } else {
368 std::cerr << hdr << "\n Field map could not be "
369 << "read and cannot be interpolated.\n";
370 return false;
371 }
372
373 std::cout << hdr << " Finished.\n";
374
375 Prepare();
376 return true;
377}
378
379bool ComponentElmer::SetWeightingField(std::string wvolt, std::string label) {
380 const std::string hdr = m_className + "::SetWeightingField:";
381 if (!m_ready) {
382 PrintNotReady("SetWeightingField");
383 std::cerr << " Weighting field cannot be added.\n";
384 return false;
385 }
386
387 // Open the voltage list.
388 std::ifstream fwvolt;
389 fwvolt.open(wvolt.c_str(), std::ios::in);
390 if (fwvolt.fail()) {
391 PrintCouldNotOpen("SetWeightingField", wvolt);
392 return false;
393 }
394
395 // Check if a weighting field with the same label already exists.
396 const size_t iw = GetOrCreateWeightingFieldIndex(label);
397 if (iw + 1 != m_wfields.size()) {
398 std::cout << m_className << "::SetWeightingField:\n"
399 << " Replacing existing weighting field " << label << ".\n";
400 }
401 m_wfieldsOk[iw] = false;
402
403 // Temporary variables for use in file reading
404 constexpr int size = 100;
405 char line[size];
406 char* token = NULL;
407 bool readerror = false;
408 bool readstop = false;
409 int il = 1;
410
411 // Read past the header.
412 while (!readstop && fwvolt.getline(line, size, '\n')) {
413 token = strtok(line, " ");
414 if (strcmp(token, "Perm:") == 0) readstop = true;
415 il++;
416 }
417
418 // Should have stopped: if not, print error message.
419 if (!readstop) {
420 std::cerr << hdr << "\n Error reading past header of potentials file "
421 << wvolt << ".\n";
422 fwvolt.close();
423 return false;
424 }
425
426 // Read past the permutation map (number of lines = nNodes).
427 const int nNodes = m_nodes.size();
428 for (int tl = 0; tl < nNodes; tl++) {
429 fwvolt.getline(line, size, '\n');
430 il++;
431 }
432
433 // Read the potentials.
434 for (int tl = 0; tl < nNodes; tl++) {
435 double v;
436 fwvolt.getline(line, size, '\n');
437 token = strtok(line, " ");
438 v = ReadDouble(token, -1, readerror);
439 if (readerror) {
440 PrintErrorReadingFile(hdr, wvolt, il);
441 fwvolt.close();
442 return false;
443 }
444 // Place the weighting potential at its appropriate node and index.
445 m_nodes[tl].w[iw] = v;
446 }
447
448 // Close the potentials file.
449 fwvolt.close();
450 std::cout << hdr << "\n Read potentials from file " << wvolt << ".\n";
451
452 // Set the ready flag.
453 m_wfieldsOk[iw] = true;
454 return true;
455}
456
457void ComponentElmer::ElectricField(const double x, const double y,
458 const double z, double& ex, double& ey,
459 double& ez, Medium*& m, int& status) {
460 double v = 0.;
461 ElectricField(x, y, z, ex, ey, ez, v, m, status);
462}
463
464void ComponentElmer::ElectricField(const double xin, const double yin,
465 const double zin, double& ex, double& ey,
466 double& ez, double& volt, Medium*& m,
467 int& status) {
468 // Copy the coordinates
469 double x = xin, y = yin, z = zin;
470
471 // Map the coordinates onto field map coordinates
472 bool xmirr, ymirr, zmirr;
473 double rcoordinate, rotation;
474 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
475
476 // Initial values
477 ex = ey = ez = volt = 0.;
478 status = 0;
479 m = nullptr;
480
481 // Do not proceed if not properly initialised.
482 if (!m_ready) {
483 status = -10;
484 PrintNotReady("ElectricField");
485 return;
486 }
487
488 if (m_warning) PrintWarning("ElectricField");
489
490 // Find the element that contains this point
491 double t1, t2, t3, t4, jac[4][4], det;
492 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
493 if (imap < 0) {
494 if (m_debug) {
495 std::cout << m_className << "::ElectricField:\n Point (" << x << ", "
496 << y << ", " << z << ") is not in the mesh.\n";
497 }
498 status = -6;
499 return;
500 }
501
502 const Element& element = m_elements[imap];
503 if (m_debug) {
504 PrintElement("ElectricField", x, y, z, t1, t2, t3, t4, element, 10);
505 }
506 const Node& n0 = m_nodes[element.emap[0]];
507 const Node& n1 = m_nodes[element.emap[1]];
508 const Node& n2 = m_nodes[element.emap[2]];
509 const Node& n3 = m_nodes[element.emap[3]];
510 const Node& n4 = m_nodes[element.emap[4]];
511 const Node& n5 = m_nodes[element.emap[5]];
512 const Node& n6 = m_nodes[element.emap[6]];
513 const Node& n7 = m_nodes[element.emap[7]];
514 const Node& n8 = m_nodes[element.emap[8]];
515 const Node& n9 = m_nodes[element.emap[9]];
516 // Shorthands.
517 const double fourt1 = 4 * t1;
518 const double fourt2 = 4 * t2;
519 const double fourt3 = 4 * t3;
520 const double fourt4 = 4 * t4;
521 const double invdet = 1. / det;
522 // Tetrahedral field
523 volt = n0.v * t1 * (2 * t1 - 1) + n1.v * t2 * (2 * t2 - 1) +
524 n2.v * t3 * (2 * t3 - 1) + n3.v * t4 * (2 * t4 - 1) +
525 4 * n4.v * t1 * t2 + 4 * n5.v * t1 * t3 + 4 * n6.v * t1 * t4 +
526 4 * n7.v * t2 * t3 + 4 * n8.v * t2 * t4 + 4 * n9.v * t3 * t4;
527 ex = -(n0.v * (fourt1 - 1) * jac[0][1] + n1.v * (fourt2 - 1) * jac[1][1] +
528 n2.v * (fourt3 - 1) * jac[2][1] + n3.v * (fourt4 - 1) * jac[3][1] +
529 n4.v * (fourt2 * jac[0][1] + fourt1 * jac[1][1]) +
530 n5.v * (fourt3 * jac[0][1] + fourt1 * jac[2][1]) +
531 n6.v * (fourt4 * jac[0][1] + fourt1 * jac[3][1]) +
532 n7.v * (fourt3 * jac[1][1] + fourt2 * jac[2][1]) +
533 n8.v * (fourt4 * jac[1][1] + fourt2 * jac[3][1]) +
534 n9.v * (fourt4 * jac[2][1] + fourt3 * jac[3][1])) *
535 invdet;
536 ey = -(n0.v * (fourt1 - 1) * jac[0][2] + n1.v * (fourt2 - 1) * jac[1][2] +
537 n2.v * (fourt3 - 1) * jac[2][2] + n3.v * (fourt4 - 1) * jac[3][2] +
538 n4.v * (fourt2 * jac[0][2] + fourt1 * jac[1][2]) +
539 n5.v * (fourt3 * jac[0][2] + fourt1 * jac[2][2]) +
540 n6.v * (fourt4 * jac[0][2] + fourt1 * jac[3][2]) +
541 n7.v * (fourt3 * jac[1][2] + fourt2 * jac[2][2]) +
542 n8.v * (fourt4 * jac[1][2] + fourt2 * jac[3][2]) +
543 n9.v * (fourt4 * jac[2][2] + fourt3 * jac[3][2])) *
544 invdet;
545 ez = -(n0.v * (fourt1 - 1) * jac[0][3] + n1.v * (fourt2 - 1) * jac[1][3] +
546 n2.v * (fourt3 - 1) * jac[2][3] + n3.v * (fourt4 - 1) * jac[3][3] +
547 n4.v * (fourt2 * jac[0][3] + fourt1 * jac[1][3]) +
548 n5.v * (fourt3 * jac[0][3] + fourt1 * jac[2][3]) +
549 n6.v * (fourt4 * jac[0][3] + fourt1 * jac[3][3]) +
550 n7.v * (fourt3 * jac[1][3] + fourt2 * jac[2][3]) +
551 n8.v * (fourt4 * jac[1][3] + fourt2 * jac[3][3]) +
552 n9.v * (fourt4 * jac[2][3] + fourt3 * jac[3][3])) *
553 invdet;
554
555 // Transform field to global coordinates
556 UnmapFields(ex, ey, ez, x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
557
558 // Drift medium?
559 const Material& mat = m_materials[element.matmap];
560 if (m_debug) {
561 std::cout << m_className << "::ElectricField:\n Material "
562 << element.matmap << ", drift flag " << mat.driftmedium << ".\n";
563 }
564 m = mat.medium;
565 status = -5;
566 if (mat.driftmedium && m && m->IsDriftable()) status = 0;
567}
568
569void ComponentElmer::WeightingField(const double xin, const double yin,
570 const double zin, double& wx, double& wy,
571 double& wz, const std::string& label) {
572 // Initial values
573 wx = wy = wz = 0;
574
575 // Do not proceed if not properly initialised.
576 if (!m_ready) return;
577
578 // Look for the label.
579 const size_t iw = GetWeightingFieldIndex(label);
580 // Do not proceed if the requested weighting field does not exist.
581 if (iw == m_wfields.size()) return;
582 // Check if the weighting field is properly initialised.
583 if (!m_wfieldsOk[iw]) return;
584
585 // Copy the coordinates.
586 double x = xin, y = yin, z = zin;
587
588 // Map the coordinates onto field map coordinates
589 bool xmirr, ymirr, zmirr;
590 double rcoordinate, rotation;
591 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
592
593 if (m_warning) PrintWarning("WeightingField");
594
595 // Find the element that contains this point.
596 double t1, t2, t3, t4, jac[4][4], det;
597 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
598 // Check if the point is in the mesh.
599 if (imap < 0) return;
600
601 const Element& element = m_elements[imap];
602 if (m_debug) {
603 PrintElement("WeightingField", x, y, z, t1, t2, t3, t4, element, 10, iw);
604 }
605 const Node& n0 = m_nodes[element.emap[0]];
606 const Node& n1 = m_nodes[element.emap[1]];
607 const Node& n2 = m_nodes[element.emap[2]];
608 const Node& n3 = m_nodes[element.emap[3]];
609 const Node& n4 = m_nodes[element.emap[4]];
610 const Node& n5 = m_nodes[element.emap[5]];
611 const Node& n6 = m_nodes[element.emap[6]];
612 const Node& n7 = m_nodes[element.emap[7]];
613 const Node& n8 = m_nodes[element.emap[8]];
614 const Node& n9 = m_nodes[element.emap[9]];
615 // Shorthands.
616 const double fourt1 = 4 * t1;
617 const double fourt2 = 4 * t2;
618 const double fourt3 = 4 * t3;
619 const double fourt4 = 4 * t4;
620 const double invdet = 1. / det;
621 // Tetrahedral field
622 wx = -(n0.w[iw] * (fourt1 - 1) * jac[0][1] +
623 n1.w[iw] * (fourt2 - 1) * jac[1][1] +
624 n2.w[iw] * (fourt3 - 1) * jac[2][1] +
625 n3.w[iw] * (fourt4 - 1) * jac[3][1] +
626 n4.w[iw] * (fourt2 * jac[0][1] + fourt1 * jac[1][1]) +
627 n5.w[iw] * (fourt3 * jac[0][1] + fourt1 * jac[2][1]) +
628 n6.w[iw] * (fourt4 * jac[0][1] + fourt1 * jac[3][1]) +
629 n7.w[iw] * (fourt3 * jac[1][1] + fourt2 * jac[2][1]) +
630 n8.w[iw] * (fourt4 * jac[1][1] + fourt2 * jac[3][1]) +
631 n9.w[iw] * (fourt4 * jac[2][1] + fourt3 * jac[3][1])) *
632 invdet;
633 wy = -(n0.w[iw] * (fourt1 - 1) * jac[0][2] +
634 n1.w[iw] * (fourt2 - 1) * jac[1][2] +
635 n2.w[iw] * (fourt3 - 1) * jac[2][2] +
636 n3.w[iw] * (fourt4 - 1) * jac[3][2] +
637 n4.w[iw] * (fourt2 * jac[0][2] + fourt1 * jac[1][2]) +
638 n5.w[iw] * (fourt3 * jac[0][2] + fourt1 * jac[2][2]) +
639 n6.w[iw] * (fourt4 * jac[0][2] + fourt1 * jac[3][2]) +
640 n7.w[iw] * (fourt3 * jac[1][2] + fourt2 * jac[2][2]) +
641 n8.w[iw] * (fourt4 * jac[1][2] + fourt2 * jac[3][2]) +
642 n9.w[iw] * (fourt4 * jac[2][2] + fourt3 * jac[3][2])) *
643 invdet;
644 wz = -(n0.w[iw] * (fourt1 - 1) * jac[0][3] +
645 n1.w[iw] * (fourt2 - 1) * jac[1][3] +
646 n2.w[iw] * (fourt3 - 1) * jac[2][3] +
647 n3.w[iw] * (fourt4 - 1) * jac[3][3] +
648 n4.w[iw] * (fourt2 * jac[0][3] + fourt1 * jac[1][3]) +
649 n5.w[iw] * (fourt3 * jac[0][3] + fourt1 * jac[2][3]) +
650 n6.w[iw] * (fourt4 * jac[0][3] + fourt1 * jac[3][3]) +
651 n7.w[iw] * (fourt3 * jac[1][3] + fourt2 * jac[2][3]) +
652 n8.w[iw] * (fourt4 * jac[1][3] + fourt2 * jac[3][3]) +
653 n9.w[iw] * (fourt4 * jac[2][3] + fourt3 * jac[3][3])) *
654 invdet;
655
656 // Transform field to global coordinates
657 UnmapFields(wx, wy, wz, x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
658}
659
660double ComponentElmer::WeightingPotential(const double xin, const double yin,
661 const double zin,
662 const std::string& label) {
663 // Do not proceed if not properly initialised.
664 if (!m_ready) return 0.;
665
666 // Look for the label.
667 const size_t iw = GetWeightingFieldIndex(label);
668 // Do not proceed if the requested weighting field does not exist.
669 if (iw == m_wfields.size()) return 0.;
670 // Check if the weighting field is properly initialised.
671 if (!m_wfieldsOk[iw]) return 0.;
672
673 // Copy the coordinates.
674 double x = xin, y = yin, z = zin;
675
676 // Map the coordinates onto field map coordinates.
677 bool xmirr, ymirr, zmirr;
678 double rcoordinate, rotation;
679 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
680
681 if (m_warning) PrintWarning("WeightingPotential");
682
683 // Find the element that contains this point.
684 double t1, t2, t3, t4, jac[4][4], det;
685 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
686 if (imap < 0) return 0.;
687
688 const Element& element = m_elements[imap];
689 if (m_debug) {
690 PrintElement("WeightingPotential", x, y, z, t1, t2, t3, t4, element, 10,
691 iw);
692 }
693 const Node& n0 = m_nodes[element.emap[0]];
694 const Node& n1 = m_nodes[element.emap[1]];
695 const Node& n2 = m_nodes[element.emap[2]];
696 const Node& n3 = m_nodes[element.emap[3]];
697 const Node& n4 = m_nodes[element.emap[4]];
698 const Node& n5 = m_nodes[element.emap[5]];
699 const Node& n6 = m_nodes[element.emap[6]];
700 const Node& n7 = m_nodes[element.emap[7]];
701 const Node& n8 = m_nodes[element.emap[8]];
702 const Node& n9 = m_nodes[element.emap[9]];
703 // Tetrahedral field
704 return n0.w[iw] * t1 * (2 * t1 - 1) + n1.w[iw] * t2 * (2 * t2 - 1) +
705 n2.w[iw] * t3 * (2 * t3 - 1) + n3.w[iw] * t4 * (2 * t4 - 1) +
706 4 * n4.w[iw] * t1 * t2 + 4 * n5.w[iw] * t1 * t3 +
707 4 * n6.w[iw] * t1 * t4 + 4 * n7.w[iw] * t2 * t3 +
708 4 * n8.w[iw] * t2 * t4 + 4 * n9.w[iw] * t3 * t4;
709}
710
711Medium* ComponentElmer::GetMedium(const double xin, const double yin,
712 const double zin) {
713 // Copy the coordinates
714 double x = xin, y = yin, z = zin;
715
716 // Map the coordinates onto field map coordinates
717 bool xmirr, ymirr, zmirr;
718 double rcoordinate, rotation;
719 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
720
721 // Do not proceed if not properly initialised.
722 if (!m_ready) {
723 PrintNotReady("GetMedium");
724 return nullptr;
725 }
726 if (m_warning) PrintWarning("GetMedium");
727
728 // Find the element that contains this point
729 double t1, t2, t3, t4, jac[4][4], det;
730 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
731 if (imap < 0) {
732 if (m_debug) {
733 std::cout << m_className << "::GetMedium:\n Point (" << x << ", " << y
734 << ", " << z << ") is not in the mesh.\n";
735 }
736 return nullptr;
737 }
738 const Element& element = m_elements[imap];
739 if (element.matmap >= m_materials.size()) {
740 if (m_debug) {
741 std::cerr << m_className << "::GetMedium:\n Point (" << x << ", " << y
742 << ", " << z << ") has out of range material number " << imap
743 << ".\n";
744 }
745 return nullptr;
746 }
747
748 if (m_debug) PrintElement("GetMedium", x, y, z, t1, t2, t3, t4, element, 10);
749
750 return m_materials[element.matmap].medium;
751}
752
753double ComponentElmer::GetElementVolume(const unsigned int i) {
754 if (i >= m_elements.size()) return 0.;
755 const Element& element = m_elements[i];
756 const Node& n0 = m_nodes[element.emap[0]];
757 const Node& n1 = m_nodes[element.emap[1]];
758 const Node& n2 = m_nodes[element.emap[2]];
759 const Node& n3 = m_nodes[element.emap[3]];
760
761 // Uses formula V = |a (dot) b x c|/6
762 // with a => "3", b => "1", c => "2" and origin "0"
763 const double vol =
764 fabs((n3.x - n0.x) *
765 ((n1.y - n0.y) * (n2.z - n0.z) - (n2.y - n0.y) * (n1.z - n0.z)) +
766 (n3.y - n0.y) *
767 ((n1.z - n0.z) * (n2.x - n0.x) - (n2.z - n0.z) * (n1.x - n0.x)) +
768 (n3.z - n0.z) * ((n1.x - n0.x) * (n2.y - n0.y) -
769 (n3.x - n0.x) * (n1.y - n0.y))) /
770 6.;
771 return vol;
772}
773
774void ComponentElmer::GetAspectRatio(const unsigned int i, double& dmin,
775 double& dmax) {
776 if (i >= m_elements.size()) {
777 dmin = dmax = 0.;
778 return;
779 }
780
781 const Element& element = m_elements[i];
782 const int np = 4;
783 // Loop over all pairs of vertices.
784 for (int j = 0; j < np - 1; ++j) {
785 const Node& nj = m_nodes[element.emap[j]];
786 for (int k = j + 1; k < np; ++k) {
787 const Node& nk = m_nodes[element.emap[k]];
788 // Compute distance.
789 const double dx = nj.x - nk.x;
790 const double dy = nj.y - nk.y;
791 const double dz = nj.z - nk.z;
792 const double dist = sqrt(dx * dx + dy * dy + dz * dz);
793 if (k == 1) {
794 dmin = dmax = dist;
795 } else {
796 if (dist < dmin) dmin = dist;
797 if (dist > dmax) dmax = dist;
798 }
799 }
800 }
801}
802
803} // namespace Garfield
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
bool Initialise(const std::string &header="mesh.header", const std::string &elist="mesh.elements", const std::string &nlist="mesh.nodes", const std::string &mplist="dielectrics.dat", const std::string &volt="out.result", const std::string &unit="cm")
bool SetWeightingField(std::string prnsol, std::string label)
Import a list of voltages to be used as weighting field.
ComponentElmer()
Default constructor.
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status) override
double GetElementVolume(const unsigned int i) override
void GetAspectRatio(const unsigned int i, double &dmin, double &dmax) override
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
Base class for components based on finite-element field maps.
bool SetDefaultDriftMedium()
Find lowest epsilon, check for eps = 0, set default drift media flags.
double ReadDouble(char *token, double def, bool &error)
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)
static double ScalingFactor(std::string unit)
int ReadInteger(char *token, int def, bool &error)
std::vector< Material > m_materials
std::vector< bool > m_wfieldsOk
size_t GetWeightingFieldIndex(const std::string &label) const
void PrintCouldNotOpen(const std::string &header, const std::string &filename) const
std::vector< std::string > m_wfields
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.
std::vector< Element > m_elements
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 Element &element, const unsigned int n, const int iw=-1) const
void Reset() override
Reset the component.
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
size_t GetOrCreateWeightingFieldIndex(const std::string &label)
bool m_debug
Switch on/off debugging messages.
Definition: Component.hh:341
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
bool IsDriftable() const
Is charge carrier transport enabled in this medium?
Definition: Medium.hh:74