Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentAnsys123.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 Garfield {
9
11
12bool ComponentAnsys123::Initialise(std::string elist, std::string nlist,
13 std::string mplist, std::string prnsol,
14 std::string unit) {
15 Reset();
16 // Keep track of the success.
17 bool ok = true;
18
19 // Buffer for reading
20 constexpr int size = 100;
21 char line[size];
22
23 // Open the material list.
24 std::ifstream fmplist;
25 fmplist.open(mplist.c_str(), std::ios::in);
26 if (fmplist.fail()) {
27 PrintCouldNotOpen("Initialise", mplist);
28 return false;
29 }
30
31 // Read the material list.
32 long il = 0;
33 unsigned int icurrmat = 0;
34 bool readerror = false;
35 while (fmplist.getline(line, size, '\n')) {
36 il++;
37 // Skip page feed
38 if (strcmp(line, "1") == 0) {
39 fmplist.getline(line, size, '\n');
40 il++;
41 fmplist.getline(line, size, '\n');
42 il++;
43 fmplist.getline(line, size, '\n');
44 il++;
45 fmplist.getline(line, size, '\n');
46 il++;
47 fmplist.getline(line, size, '\n');
48 il++;
49 continue;
50 }
51 // Split the line in tokens
52 char* token = NULL;
53 token = strtok(line, " ");
54 // Skip blank lines and headers
55 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
56 strcmp(token, "TEMPERATURE") == 0 || strcmp(token, "PROPERTY=") == 0 ||
57 int(token[0]) == 10 || int(token[0]) == 13)
58 continue;
59 // Read number of materials and initialise the list.
60 if (strcmp(token, "LIST") == 0) {
61 token = strtok(NULL, " ");
62 token = strtok(NULL, " ");
63 token = strtok(NULL, " ");
64 token = strtok(NULL, " ");
65 const unsigned int nMaterials = ReadInteger(token, -1, readerror);
66 if (readerror) {
67 std::cerr << m_className << "::Initialise:\n";
68 std::cerr << " Error reading file " << mplist << " (line " << il
69 << ").\n";
70 fmplist.close();
71 return false;
72 }
73 m_materials.resize(nMaterials);
74 for (auto& material : m_materials) {
75 material.ohm = -1;
76 material.eps = -1;
77 material.medium = nullptr;
78 }
79 if (m_debug) {
80 std::cout << m_className << "::Initialise: " << nMaterials
81 << " materials.\n";
82 }
83 } else if (strcmp(token, "MATERIAL") == 0) {
84 // Version 12 format: read material number
85 token = strtok(NULL, " ");
86 token = strtok(NULL, " ");
87 const int imat = ReadInteger(token, -1, readerror);
88 if (readerror || imat < 0) {
89 std::cerr << m_className << "::Initialise:\n";
90 std::cerr << " Error reading file " << mplist << " (line " << il
91 << ").\n";
92 fmplist.close();
93 return false;
94 }
95 icurrmat = imat;
96 } else if (strcmp(token, "TEMP") == 0) {
97 // Version 12 format: read property tag and value
98 token = strtok(NULL, " ");
99 int itype = 0;
100 if (strncmp(token, "PERX", 4) == 0) {
101 itype = 1;
102 } else if (strncmp(token, "RSVX", 4) == 0) {
103 itype = 2;
104 } else {
105 std::cerr << m_className << "::Initialise:\n";
106 std::cerr << " Found unknown material property flag " << token
107 << "\n";
108 std::cerr << " on material properties file " << mplist << " (line "
109 << il << ").\n";
110 ok = false;
111 }
112 fmplist.getline(line, size, '\n');
113 il++;
114 token = NULL;
115 token = strtok(line, " ");
116 if (icurrmat < 1 || icurrmat > m_materials.size()) {
117 std::cerr << m_className << "::Initialise:\n";
118 std::cerr << " Found out-of-range current material index "
119 << icurrmat << "\n";
120 std::cerr << " in material properties file " << mplist << ".\n";
121 ok = false;
122 readerror = false;
123 } else if (itype == 1) {
124 m_materials[icurrmat - 1].eps = ReadDouble(token, -1, readerror);
125 } else if (itype == 2) {
126 m_materials[icurrmat - 1].ohm = ReadDouble(token, -1, readerror);
127 }
128 if (readerror) {
129 std::cerr << m_className << "::Initialise:\n";
130 std::cerr << " Error reading file " << mplist << " line " << il
131 << ").\n";
132 fmplist.close();
133 return false;
134 }
135 } else if (strcmp(token, "PROPERTY") == 0) {
136 // Version 11 format
137 token = strtok(NULL, " ");
138 token = strtok(NULL, " ");
139 int itype = 0;
140 if (strcmp(token, "PERX") == 0) {
141 itype = 1;
142 } else if (strcmp(token, "RSVX") == 0) {
143 itype = 2;
144 } else {
145 std::cerr << m_className << "::Initialise:\n";
146 std::cerr << " Found unknown material property flag " << token
147 << "\n";
148 std::cerr << " on material properties file " << mplist << " (line "
149 << il << ").\n";
150 ok = false;
151 }
152 token = strtok(NULL, " ");
153 token = strtok(NULL, " ");
154 int imat = ReadInteger(token, -1, readerror);
155 if (readerror) {
156 std::cerr << m_className << "::Initialise:\n";
157 std::cerr << " Error reading file " << mplist << " (line " << il
158 << ").\n";
159 fmplist.close();
160 return false;
161 } else if (imat < 1 || imat > (int)m_materials.size()) {
162 std::cerr << m_className << "::Initialise:\n";
163 std::cerr << " Found out-of-range material index " << imat << "\n";
164 std::cerr << " in material properties file " << mplist << ".\n";
165 ok = false;
166 } else {
167 fmplist.getline(line, size, '\n');
168 il++;
169 fmplist.getline(line, size, '\n');
170 il++;
171 token = NULL;
172 token = strtok(line, " ");
173 token = strtok(NULL, " ");
174 if (itype == 1) {
175 m_materials[imat - 1].eps = ReadDouble(token, -1, readerror);
176 } else if (itype == 2) {
177 m_materials[imat - 1].ohm = ReadDouble(token, -1, readerror);
178 }
179 if (readerror) {
180 std::cerr << m_className << "::Initialise:\n";
181 std::cerr << " Error reading file " << mplist << " (line " << il
182 << ").\n";
183 fmplist.close();
184 return false;
185 }
186 }
187 }
188 }
189 // Close the file
190 fmplist.close();
191
192 // Find lowest epsilon, check for eps = 0, set default drift medium.
193 if (!SetDefaultDriftMedium()) ok = false;
194
195 // Tell how many lines read
196 std::cout << m_className << "::Initialise:\n"
197 << " Read properties of " << m_materials.size()
198 << " materials from file " << mplist << ".\n";
199 if (m_debug) PrintMaterials();
200
201 // Open the element list
202 std::ifstream felist;
203 felist.open(elist.c_str(), std::ios::in);
204 if (felist.fail()) {
205 PrintCouldNotOpen("Initialise", elist);
206 return false;
207 }
208
209 // Read the element list
210 m_elements.clear();
211 int nbackground = 0;
212 il = 0;
213 int highestnode = 0;
214 while (felist.getline(line, size, '\n')) {
215 il++;
216 // Skip page feed
217 if (strcmp(line, "1") == 0) {
218 felist.getline(line, size, '\n');
219 il++;
220 felist.getline(line, size, '\n');
221 il++;
222 felist.getline(line, size, '\n');
223 il++;
224 felist.getline(line, size, '\n');
225 il++;
226 felist.getline(line, size, '\n');
227 il++;
228 continue;
229 }
230 // Skip page feed if ansys > v15.x
231 if (strstr(line,"***") != NULL) {
232 felist.getline(line, size, '\n');
233 il++;
234 felist.getline(line, size, '\n');
235 il++;
236 felist.getline(line, size, '\n');
237 il++;
238 continue;
239 }
240 // Split the line in tokens
241 char* token = NULL;
242 // Split into tokens
243 token = strtok(line, " ");
244 // Skip blank lines and headers
245 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
246 int(token[0]) == 10 || int(token[0]) == 13 ||
247 strcmp(token, "LIST") == 0 || strcmp(token, "ELEM") == 0) {
248 continue;
249 }
250 // Read the element
251 int ielem = ReadInteger(token, -1, readerror);
252 token = strtok(NULL, " ");
253 int imat = ReadInteger(token, -1, readerror);
254 token = strtok(NULL, " ");
255 token = strtok(NULL, " ");
256 token = strtok(NULL, " ");
257 token = strtok(NULL, " ");
258 token = strtok(NULL, " ");
259 int in0 = ReadInteger(token, -1, readerror);
260 token = strtok(NULL, " ");
261 int in1 = ReadInteger(token, -1, readerror);
262 token = strtok(NULL, " ");
263 int in2 = ReadInteger(token, -1, readerror);
264 token = strtok(NULL, " ");
265 int in3 = ReadInteger(token, -1, readerror);
266 token = strtok(NULL, " ");
267 int in4 = ReadInteger(token, -1, readerror);
268 token = strtok(NULL, " ");
269 int in5 = ReadInteger(token, -1, readerror);
270 token = strtok(NULL, " ");
271 int in6 = ReadInteger(token, -1, readerror);
272 token = strtok(NULL, " ");
273 int in7 = ReadInteger(token, -1, readerror);
274 if (!felist.getline(line, size, '\n')) {
275 std::cerr << m_className << "::Initialise:\n";
276 std::cerr << " Error reading element " << ielem << ".\n";
277 ok = false;
278 break;
279 }
280 ++il;
281 token = NULL;
282 token = strtok(line, " ");
283 int in8 = ReadInteger(token, -1, readerror);
284 token = strtok(NULL, " ");
285 int in9 = ReadInteger(token, -1, readerror);
286
287 // Check synchronisation
288 if (readerror) {
289 std::cerr << m_className << "::Initialise:\n";
290 std::cerr << " Error reading file " << elist << " (line " << il
291 << ").\n";
292 felist.close();
293 return false;
294 } else if (ielem - 1 != (int)m_elements.size() + nbackground) {
295 std::cerr << m_className << "::Initialise:\n";
296 std::cerr << " Synchronisation lost on file " << elist << " (line "
297 << il << ").\n";
298 std::cerr << " Element: " << ielem << " (expected "
299 << m_elements.size() << "), material: " << imat << ",\n"
300 << " nodes: (" << in0 << ", " << in1 << ", " << in2 << ", "
301 << in3 << ", " << in4 << ", " << in5 << ", " << in6 << ", "
302 << in7 << ", " << in8 << ", " << in9 << ")\n";
303 ok = false;
304 }
305
306 // Check the material number and ensure that epsilon is non-negative
307 if (imat < 1 || imat > (int)m_materials.size()) {
308 std::cerr << m_className << "::Initialise:\n";
309 std::cerr << " Out-of-range material number on file " << elist
310 << " (line " << il << ").\n";
311 std::cerr << " Element: " << ielem << ", material: " << imat << ",\n";
312 std::cerr << " nodes: (" << in0 << ", " << in1 << ", " << in2 << ", "
313 << in3 << ", " << in4 << ", " << in5 << ", " << in6 << ", "
314 << in7 << ", " << in8 << ", " << in9 << ")\n";
315 ok = false;
316 }
317 if (m_materials[imat - 1].eps < 0) {
318 std::cerr << m_className << "::Initialise:\n";
319 std::cerr << " Element " << ielem << " in element list " << elist
320 << "\n";
321 std::cerr << " uses material " << imat
322 << " which has not been assigned\n";
323 std::cerr << " a positive permittivity in material list " << mplist
324 << ".\n";
325 ok = false;
326 }
327
328 // Check the node numbers
329 if (in0 < 1 || in1 < 1 || in2 < 1 || in3 < 1 || in4 < 1 || in5 < 1 ||
330 in6 < 1 || in7 < 1 || in8 < 1 || in9 < 1) {
331 std::cerr << m_className << "::Initialise:\n";
332 std::cerr << " Found a node number < 1 on file " << elist << " (line "
333 << il << ").\n";
334 std::cerr << " Element: " << ielem << ", material: " << imat << ",\n";
335 std::cerr << " nodes: (" << in0 << ", " << in1 << ", " << in2 << ", "
336 << in3 << ", " << in4 << ", " << in5 << ", " << in6 << ", "
337 << in7 << ", " << in8 << ", " << in9 << ")\n";
338 ok = false;
339 }
340 if (in0 > highestnode) highestnode = in0;
341 if (in1 > highestnode) highestnode = in1;
342 if (in2 > highestnode) highestnode = in2;
343 if (in3 > highestnode) highestnode = in3;
344 if (in4 > highestnode) highestnode = in4;
345 if (in5 > highestnode) highestnode = in5;
346 if (in6 > highestnode) highestnode = in6;
347 if (in7 > highestnode) highestnode = in7;
348 if (in8 > highestnode) highestnode = in8;
349 if (in9 > highestnode) highestnode = in9;
350
351 // Skip elements in conductors.
352 if (m_deleteBackground && m_materials[imat - 1].ohm == 0) {
353 nbackground++;
354 continue;
355 }
356
357 // These elements must not be degenerate.
358 if (in0 == in1 || in0 == in2 || in0 == in3 || in0 == in4 || in0 == in5 ||
359 in0 == in6 || in0 == in7 || in0 == in8 || in0 == in9 || in1 == in2 ||
360 in1 == in3 || in1 == in4 || in1 == in5 || in1 == in6 || in1 == in7 ||
361 in1 == in8 || in1 == in9 || in2 == in3 || in2 == in4 || in2 == in5 ||
362 in2 == in6 || in2 == in7 || in2 == in8 || in2 == in9 || in3 == in4 ||
363 in3 == in5 || in3 == in6 || in3 == in7 || in3 == in8 || in3 == in9 ||
364 in4 == in5 || in4 == in6 || in4 == in7 || in4 == in8 || in4 == in9 ||
365 in5 == in6 || in5 == in7 || in5 == in8 || in5 == in9 || in6 == in7 ||
366 in6 == in8 || in6 == in9 || in7 == in8 || in7 == in9 || in8 == in9) {
367 std::cerr << m_className << "::Initialise:\n";
368 std::cerr << " Element " << ielem << " of file " << elist
369 << " is degenerate,\n";
370 std::cerr << " no such elements allowed in this type of map.\n";
371 ok = false;
372 }
373 Element newElement;
374 newElement.degenerate = false;
375
376 // Store the material reference
377 newElement.matmap = imat - 1;
378
379 // Node references
380 newElement.emap[0] = in0 - 1;
381 newElement.emap[1] = in1 - 1;
382 newElement.emap[2] = in2 - 1;
383 newElement.emap[3] = in3 - 1;
384 newElement.emap[4] = in4 - 1;
385 newElement.emap[7] = in5 - 1;
386 newElement.emap[5] = in6 - 1;
387 newElement.emap[6] = in7 - 1;
388 newElement.emap[8] = in8 - 1;
389 newElement.emap[9] = in9 - 1;
390 m_elements.push_back(std::move(newElement));
391 }
392 // Close the file
393 felist.close();
394
395 // Tell how many lines read.
396 std::cout << m_className << "::Initialise:\n"
397 << " Read " << m_elements.size() << " elements from file "
398 << elist << ",\n";
399 std::cout << " highest node number: " << highestnode << ",\n";
400 std::cout << " background elements skipped: " << nbackground << "\n";
401 // Check the value of the unit
402 double funit = ScalingFactor(unit);
403 if (funit <= 0.) {
404 std::cerr << m_className << "::Initialise:\n"
405 << " Unknown length unit " << unit << ".\n";
406 ok = false;
407 funit = 1.0;
408 }
409 if (m_debug) {
410 std::cout << m_className << ":Initialise: Unit scaling factor = "
411 << funit << ".\n";
412 }
413
414 // Open the node list
415 std::ifstream fnlist;
416 fnlist.open(nlist.c_str(), std::ios::in);
417 if (fnlist.fail()) {
418 PrintCouldNotOpen("Initialise", nlist);
419 return false;
420 }
421
422 // Read the node list
423 m_nodes.clear();
424 il = 0;
425 while (fnlist.getline(line, size, '\n')) {
426 il++;
427 // Skip page feed
428 if (strcmp(line, "1") == 0) {
429 fnlist.getline(line, size, '\n');
430 il++;
431 fnlist.getline(line, size, '\n');
432 il++;
433 fnlist.getline(line, size, '\n');
434 il++;
435 fnlist.getline(line, size, '\n');
436 il++;
437 fnlist.getline(line, size, '\n');
438 il++;
439 continue;
440 }
441 // Skip page feed if ansys > v15.x
442 if (strstr(line,"***") != NULL) {
443 fnlist.getline(line, size, '\n');
444 il++;
445 fnlist.getline(line, size, '\n');
446 il++;
447 fnlist.getline(line, size, '\n');
448 il++;
449 continue;
450 }
451 // Split the line in tokens
452 char* token = NULL;
453 token = strtok(line, " ");
454 // Skip blank lines and headers
455 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
456 int(token[0]) == 10 || int(token[0]) == 13 ||
457 strcmp(token, "LIST") == 0 || strcmp(token, "NODE") == 0)
458 continue;
459 // Read the element
460 int inode = ReadInteger(token, -1, readerror);
461 token = strtok(NULL, " ");
462 double xnode = ReadDouble(token, -1, readerror);
463 token = strtok(NULL, " ");
464 double ynode = ReadDouble(token, -1, readerror);
465 token = strtok(NULL, " ");
466 double znode = ReadDouble(token, -1, readerror);
467 // Check syntax
468 if (readerror) {
469 std::cerr << m_className << "::Initialise:\n";
470 std::cerr << " Error reading file " << nlist << " (line " << il
471 << ").\n";
472 fnlist.close();
473 return false;
474 }
475 // Check synchronisation
476 if (inode - 1 != (int)m_nodes.size()) {
477 std::cerr << m_className << "::Initialise:\n";
478 std::cerr << " Synchronisation lost on file " << nlist << " (line "
479 << il << ").\n";
480 std::cerr << " Node: " << inode << " (expected " << m_nodes.size()
481 << "), (x,y,z) = (" << xnode << ", " << ynode << ", " << znode
482 << ")\n";
483 ok = false;
484 }
485 // Store the point coordinates
486 Node newNode;
487 newNode.w.clear();
488 newNode.x = xnode * funit;
489 newNode.y = ynode * funit;
490 newNode.z = znode * funit;
491 m_nodes.push_back(std::move(newNode));
492 }
493 // Close the file
494 fnlist.close();
495 // Tell how many lines read
496 std::cout << m_className << "::Initialise:\n";
497 std::cout << " Read " << m_nodes.size() << " nodes from file " << nlist << ".\n";
498 // Check number of nodes
499 if ((int)m_nodes.size() != highestnode) {
500 std::cerr << m_className << "::Initialise:\n";
501 std::cerr << " Number of nodes read (" << m_nodes.size() << ") on " << nlist
502 << "\n";
503 std::cerr << " does not match element list (" << highestnode << ").\n";
504 ok = false;
505 }
506
507 // Open the voltage list
508 std::ifstream fprnsol;
509 fprnsol.open(prnsol.c_str(), std::ios::in);
510 if (fprnsol.fail()) {
511 PrintCouldNotOpen("Initialise", prnsol);
512 return false;
513 }
514
515 // Read the voltage list
516 il = 0;
517 unsigned int nread = 0;
518 while (fprnsol.getline(line, size, '\n')) {
519 il++;
520 // Skip page feed
521 if (strcmp(line, "1") == 0) {
522 fprnsol.getline(line, size, '\n');
523 il++;
524 fprnsol.getline(line, size, '\n');
525 il++;
526 fprnsol.getline(line, size, '\n');
527 il++;
528 fprnsol.getline(line, size, '\n');
529 il++;
530 fprnsol.getline(line, size, '\n');
531 il++;
532 continue;
533 }
534 // Skip page feed if ansys > v15.x
535 if (strstr(line,"***") != NULL) {
536 fprnsol.getline(line, size, '\n');
537 il++;
538 fprnsol.getline(line, size, '\n');
539 il++;
540 fprnsol.getline(line, size, '\n');
541 il++;
542 continue;
543 }
544 // Split the line in tokens
545 char* token = NULL;
546 token = strtok(line, " ");
547 // Skip blank lines and headers
548 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
549 int(token[0]) == 10 || int(token[0]) == 13 ||
550 strcmp(token, "PRINT") == 0 || strcmp(token, "*****") == 0 ||
551 strcmp(token, "LOAD") == 0 || strcmp(token, "TIME=") == 0 ||
552 strcmp(token, "MAXIMUM") == 0 || strcmp(token, "VALUE") == 0 ||
553 strcmp(token, "NODE") == 0)
554 continue;
555 // Read the element
556 int inode = ReadInteger(token, -1, readerror);
557 token = strtok(NULL, " ");
558 double volt = ReadDouble(token, -1, readerror);
559 // Check syntax
560 if (readerror) {
561 std::cerr << m_className << "::Initialise:\n";
562 std::cerr << " Error reading file " << prnsol << " (line << " << il
563 << ").\n";
564 fprnsol.close();
565 return false;
566 }
567 // Check node number and store if OK
568 if (inode < 1 || inode > highestnode) {
569 std::cerr << m_className << "::Initialise:\n";
570 std::cerr << " Node number " << inode << " out of range\n";
571 std::cerr << " on potential file " << prnsol << " (line " << il
572 << ").\n";
573 ok = false;
574 } else {
575 m_nodes[inode - 1].v = volt;
576 nread++;
577 }
578 }
579 // Close the file
580 fprnsol.close();
581 // Tell how many lines read
582 std::cout << m_className << "::Initialise:\n Read "
583 << nread << " potentials from file " << prnsol << ".\n";
584 // Check number of nodes
585 if (nread != m_nodes.size()) {
586 std::cerr << m_className << "::Initialise:\n";
587 std::cerr << " Number of nodes read (" << nread << ") on potential file "
588 << prnsol << "\n";
589 std::cerr << " does not match the node list (" << m_nodes.size() << ").\n";
590 ok = false;
591 }
592
593 // Set the ready flag
594 if (ok) {
595 m_ready = true;
596 } else {
597 std::cerr << m_className << "::Initialise:\n";
598 std::cerr
599 << " Field map could not be read and can not be interpolated.\n";
600 return false;
601 }
602 Prepare();
603 return true;
604}
605
607 std::string label) {
608 if (!m_ready) {
609 PrintNotReady("SetWeightingField");
610 std::cerr << " Weighting field cannot be added.\n";
611 return false;
612 }
613
614 // Open the voltage list.
615 std::ifstream fprnsol;
616 fprnsol.open(prnsol.c_str(), std::ios::in);
617 if (fprnsol.fail()) {
618 PrintCouldNotOpen("SetWeightingField", prnsol);
619 return false;
620 }
621
622 // Check if a weighting field with the same label already exists.
623 const size_t iw = GetOrCreateWeightingFieldIndex(label);
624 if (iw + 1 != m_wfields.size()) {
625 std::cout << m_className << "::SetWeightingField:\n"
626 << " Replacing existing weighting field " << label << ".\n";
627 }
628 m_wfieldsOk[iw] = false;
629
630 // Buffer for reading
631 constexpr int size = 100;
632 char line[size];
633
634 bool ok = true;
635 // Read the voltage list.
636 int il = 0;
637 unsigned int nread = 0;
638 bool readerror = false;
639 while (fprnsol.getline(line, size, '\n')) {
640 il++;
641 // Skip page feed
642 if (strcmp(line, "1") == 0) {
643 fprnsol.getline(line, size, '\n');
644 il++;
645 fprnsol.getline(line, size, '\n');
646 il++;
647 fprnsol.getline(line, size, '\n');
648 il++;
649 fprnsol.getline(line, size, '\n');
650 il++;
651 fprnsol.getline(line, size, '\n');
652 il++;
653 continue;
654 }
655 // Skip page feed (Ansys > v15.x).
656 if (strstr(line, "***") != NULL) {
657 fprnsol.getline(line, size, '\n');
658 il++;
659 fprnsol.getline(line, size, '\n');
660 il++;
661 fprnsol.getline(line, size, '\n');
662 il++;
663 continue;
664 }
665 // Split the line in tokens.
666 char* token = NULL;
667 token = strtok(line, " ");
668 // Skip blank lines and headers.
669 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
670 int(token[0]) == 10 || int(token[0]) == 13 ||
671 strcmp(token, "PRINT") == 0 || strcmp(token, "*****") == 0 ||
672 strcmp(token, "LOAD") == 0 || strcmp(token, "TIME=") == 0 ||
673 strcmp(token, "MAXIMUM") == 0 || strcmp(token, "VALUE") == 0 ||
674 strcmp(token, "NODE") == 0)
675 continue;
676 // Read the element.
677 int inode = ReadInteger(token, -1, readerror);
678 token = strtok(NULL, " ");
679 double volt = ReadDouble(token, -1, readerror);
680 // Check the syntax.
681 if (readerror) {
682 std::cerr << m_className << "::SetWeightingField:\n";
683 std::cerr << " Error reading file " << prnsol.c_str() << " (line "
684 << il << ").\n";
685 fprnsol.close();
686 return false;
687 }
688 // Check node number and store if OK.
689 if (inode < 1 || inode > (int)m_nodes.size()) {
690 std::cerr << m_className << "::SetWeightingField:\n";
691 std::cerr << " Node number " << inode << " out of range\n";
692 std::cerr << " on potential file " << prnsol.c_str() << " (line " << il
693 << ").\n";
694 ok = false;
695 } else {
696 m_nodes[inode - 1].w[iw] = volt;
697 nread++;
698 }
699 }
700 // Close the file.
701 fprnsol.close();
702
703 std::cout << m_className << "::SetWeightingField:\n"
704 << " Read " << nread << " potentials from file "
705 << prnsol << ".\n";
706 // Check the number of nodes.
707 if (nread != m_nodes.size()) {
708 std::cerr << m_className << "::SetWeightingField:\n"
709 << " Number of nodes read from potential file " << prnsol
710 << " (" << nread << ")\n does not match the node list ("
711 << m_nodes.size() << ").\n";
712 ok = false;
713 }
714
715 // Set the ready flag.
716 m_wfieldsOk[iw] = ok;
717 if (!ok) {
718 std::cerr << m_className << "::SetWeightingField:\n";
719 std::cerr << " Field map could not be read "
720 << "and cannot be interpolated.\n";
721 return false;
722 }
723 return true;
724}
725
726void ComponentAnsys123::ElectricField(const double x, const double y,
727 const double z, double& ex, double& ey,
728 double& ez, Medium*& m, int& status) {
729 double v = 0.;
730 ElectricField(x, y, z, ex, ey, ez, v, m, status);
731}
732
733void ComponentAnsys123::ElectricField(const double xin, const double yin,
734 const double zin, double& ex, double& ey,
735 double& ez, double& volt, Medium*& m,
736 int& status) {
737 // Copy the coordinates
738 double x = xin, y = yin, z = zin;
739
740 // Map the coordinates onto field map coordinates
741 bool xmirr, ymirr, zmirr;
742 double rcoordinate, rotation;
743 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
744
745 // Initial values
746 ex = ey = ez = volt = 0.;
747 status = 0;
748 m = nullptr;
749
750 // Do not proceed if not properly initialised.
751 if (!m_ready) {
752 status = -10;
753 PrintNotReady("ElectricField");
754 return;
755 }
756
757 if (m_warning) PrintWarning("ElectricField");
758
759 // Find the element that contains this point
760 double t1, t2, t3, t4, jac[4][4], det;
761 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
762 if (imap < 0) {
763 if (m_debug) {
764 std::cerr << m_className << "::ElectricField: Point (" << x << ", " << y
765 << ", " << z << ") not in the mesh.\n";
766 }
767 status = -6;
768 return;
769 }
770
771 const Element& element = m_elements[imap];
772 if (m_debug) {
773 PrintElement("ElectricField", x, y, z, t1, t2, t3, t4, element, 10);
774 }
775 const Node& n0 = m_nodes[element.emap[0]];
776 const Node& n1 = m_nodes[element.emap[1]];
777 const Node& n2 = m_nodes[element.emap[2]];
778 const Node& n3 = m_nodes[element.emap[3]];
779 const Node& n4 = m_nodes[element.emap[4]];
780 const Node& n5 = m_nodes[element.emap[5]];
781 const Node& n6 = m_nodes[element.emap[6]];
782 const Node& n7 = m_nodes[element.emap[7]];
783 const Node& n8 = m_nodes[element.emap[8]];
784 const Node& n9 = m_nodes[element.emap[9]];
785 // Shorthands.
786 const double fourt1 = 4 * t1;
787 const double fourt2 = 4 * t2;
788 const double fourt3 = 4 * t3;
789 const double fourt4 = 4 * t4;
790 const double invdet = 1. / det;
791 // Tetrahedral field
792 volt = n0.v * t1 * (2 * t1 - 1) + n1.v * t2 * (2 * t2 - 1) +
793 n2.v * t3 * (2 * t3 - 1) + n3.v * t4 * (2 * t4 - 1) +
794 n4.v * fourt1 * t2 + n5.v * fourt1 * t3 + n6.v * fourt1 * t4 +
795 n7.v * fourt2 * t3 + n8.v * fourt2 * t4 + n9.v * fourt3 * t4;
796 ex = -(n0.v * (fourt1 - 1) * jac[0][1] + n1.v * (fourt2 - 1) * jac[1][1] +
797 n2.v * (fourt3 - 1) * jac[2][1] + n3.v * (fourt4 - 1) * jac[3][1] +
798 n4.v * (fourt2 * jac[0][1] + fourt1 * jac[1][1]) +
799 n5.v * (fourt3 * jac[0][1] + fourt1 * jac[2][1]) +
800 n6.v * (fourt4 * jac[0][1] + fourt1 * jac[3][1]) +
801 n7.v * (fourt3 * jac[1][1] + fourt2 * jac[2][1]) +
802 n8.v * (fourt4 * jac[1][1] + fourt2 * jac[3][1]) +
803 n9.v * (fourt4 * jac[2][1] + fourt3 * jac[3][1])) *
804 invdet;
805
806 ey = -(n0.v * (fourt1 - 1) * jac[0][2] + n1.v * (fourt2 - 1) * jac[1][2] +
807 n2.v * (fourt3 - 1) * jac[2][2] + n3.v * (fourt4 - 1) * jac[3][2] +
808 n4.v * (fourt2 * jac[0][2] + fourt1 * jac[1][2]) +
809 n5.v * (fourt3 * jac[0][2] + fourt1 * jac[2][2]) +
810 n6.v * (fourt4 * jac[0][2] + fourt1 * jac[3][2]) +
811 n7.v * (fourt3 * jac[1][2] + fourt2 * jac[2][2]) +
812 n8.v * (fourt4 * jac[1][2] + fourt2 * jac[3][2]) +
813 n9.v * (fourt4 * jac[2][2] + fourt3 * jac[3][2])) *
814 invdet;
815
816 ez = -(n0.v * (fourt1 - 1) * jac[0][3] + n1.v * (fourt2 - 1) * jac[1][3] +
817 n2.v * (fourt3 - 1) * jac[2][3] + n3.v * (fourt4 - 1) * jac[3][3] +
818 n4.v * (fourt2 * jac[0][3] + fourt1 * jac[1][3]) +
819 n5.v * (fourt3 * jac[0][3] + fourt1 * jac[2][3]) +
820 n6.v * (fourt4 * jac[0][3] + fourt1 * jac[3][3]) +
821 n7.v * (fourt3 * jac[1][3] + fourt2 * jac[2][3]) +
822 n8.v * (fourt4 * jac[1][3] + fourt2 * jac[3][3]) +
823 n9.v * (fourt4 * jac[2][3] + fourt3 * jac[3][3])) *
824 invdet;
825
826 // Transform field to global coordinates
827 UnmapFields(ex, ey, ez, x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
828
829 // Drift medium?
830 if (m_debug) {
831 std::cout << m_className << "::ElectricField:\n";
832 std::cout << " Material " << element.matmap << ", drift flag "
833 << m_materials[element.matmap].driftmedium << ".\n";
834 }
835 m = m_materials[element.matmap].medium;
836 status = -5;
837 if (m_materials[element.matmap].driftmedium) {
838 if (m && m->IsDriftable()) status = 0;
839 }
840}
841
842void ComponentAnsys123::WeightingField(const double xin, const double yin,
843 const double zin, double& wx, double& wy,
844 double& wz, const std::string& label) {
845 // Initial values
846 wx = wy = wz = 0;
847
848 // Do not proceed if not properly initialised.
849 if (!m_ready) return;
850
851 // Look for the label.
852 const size_t iw = GetWeightingFieldIndex(label);
853 // Do not proceed if the requested weighting field does not exist.
854 if (iw == m_wfields.size()) return;
855 // Check if the weighting field is properly initialised.
856 if (!m_wfieldsOk[iw]) return;
857
858 // Copy the coordinates.
859 double x = xin, y = yin, z = zin;
860
861 // Map the coordinates onto field map coordinates
862 bool xmirr, ymirr, zmirr;
863 double rcoordinate, rotation;
864 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
865
866 if (m_warning) PrintWarning("WeightingField");
867
868 // Find the element that contains this point.
869 double t1, t2, t3, t4, jac[4][4], det;
870 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
871 // Check if the point is in the mesh.
872 if (imap < 0) return;
873
874 const Element& element = m_elements[imap];
875 if (m_debug) {
876 PrintElement("WeightingField", x, y, z, t1, t2, t3, t4, element, 10, iw);
877 }
878 const Node& n0 = m_nodes[element.emap[0]];
879 const Node& n1 = m_nodes[element.emap[1]];
880 const Node& n2 = m_nodes[element.emap[2]];
881 const Node& n3 = m_nodes[element.emap[3]];
882 const Node& n4 = m_nodes[element.emap[4]];
883 const Node& n5 = m_nodes[element.emap[5]];
884 const Node& n6 = m_nodes[element.emap[6]];
885 const Node& n7 = m_nodes[element.emap[7]];
886 const Node& n8 = m_nodes[element.emap[8]];
887 const Node& n9 = m_nodes[element.emap[9]];
888 // Tetrahedral field
889 const double invdet = 1. / det;
890 const double fourt1 = 4 * t1;
891 const double fourt2 = 4 * t2;
892 const double fourt3 = 4 * t3;
893 const double fourt4 = 4 * t4;
894 wx = -(n0.w[iw] * (fourt1 - 1) * jac[0][1] +
895 n1.w[iw] * (fourt2 - 1) * jac[1][1] +
896 n2.w[iw] * (fourt3 - 1) * jac[2][1] +
897 n3.w[iw] * (fourt4 - 1) * jac[3][1] +
898 n4.w[iw] * (fourt2 * jac[0][1] + fourt1 * jac[1][1]) +
899 n5.w[iw] * (fourt3 * jac[0][1] + fourt1 * jac[2][1]) +
900 n6.w[iw] * (fourt4 * jac[0][1] + fourt1 * jac[3][1]) +
901 n7.w[iw] * (fourt3 * jac[1][1] + fourt2 * jac[2][1]) +
902 n8.w[iw] * (fourt4 * jac[1][1] + fourt2 * jac[3][1]) +
903 n9.w[iw] * (fourt4 * jac[2][1] + fourt3 * jac[3][1])) *
904 invdet;
905
906 wy = -(n0.w[iw] * (fourt1 - 1) * jac[0][2] +
907 n1.w[iw] * (fourt2 - 1) * jac[1][2] +
908 n2.w[iw] * (fourt3 - 1) * jac[2][2] +
909 n3.w[iw] * (fourt4 - 1) * jac[3][2] +
910 n4.w[iw] * (fourt2 * jac[0][2] + fourt1 * jac[1][2]) +
911 n5.w[iw] * (fourt3 * jac[0][2] + fourt1 * jac[2][2]) +
912 n6.w[iw] * (fourt4 * jac[0][2] + fourt1 * jac[3][2]) +
913 n7.w[iw] * (fourt3 * jac[1][2] + fourt2 * jac[2][2]) +
914 n8.w[iw] * (fourt4 * jac[1][2] + fourt2 * jac[3][2]) +
915 n9.w[iw] * (fourt4 * jac[2][2] + fourt3 * jac[3][2])) *
916 invdet;
917
918 wz = -(n0.w[iw] * (fourt1 - 1) * jac[0][3] +
919 n1.w[iw] * (fourt2 - 1) * jac[1][3] +
920 n2.w[iw] * (fourt3 - 1) * jac[2][3] +
921 n3.w[iw] * (fourt4 - 1) * jac[3][3] +
922 n4.w[iw] * (fourt2 * jac[0][3] + fourt1 * jac[1][3]) +
923 n5.w[iw] * (fourt3 * jac[0][3] + fourt1 * jac[2][3]) +
924 n6.w[iw] * (fourt4 * jac[0][3] + fourt1 * jac[3][3]) +
925 n7.w[iw] * (fourt3 * jac[1][3] + fourt2 * jac[2][3]) +
926 n8.w[iw] * (fourt4 * jac[1][3] + fourt2 * jac[3][3]) +
927 n9.w[iw] * (fourt4 * jac[2][3] + fourt3 * jac[3][3])) *
928 invdet;
929
930 // Transform field to global coordinates
931 UnmapFields(wx, wy, wz, x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
932}
933
934double ComponentAnsys123::WeightingPotential(const double xin, const double yin,
935 const double zin,
936 const std::string& label) {
937 // Do not proceed if not properly initialised.
938 if (!m_ready) return 0.;
939
940 // Look for the label.
941 const size_t iw = GetWeightingFieldIndex(label);
942 // Do not proceed if the requested weighting field does not exist.
943 if (iw == m_wfields.size()) return 0.;
944 // Check if the weighting field is properly initialised.
945 if (!m_wfieldsOk[iw]) return 0.;
946
947 // Copy the coordinates.
948 double x = xin, y = yin, z = zin;
949
950 // Map the coordinates onto field map coordinates.
951 bool xmirr, ymirr, zmirr;
952 double rcoordinate, rotation;
953 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
954
955 if (m_warning) PrintWarning("WeightingPotential");
956
957 // Find the element that contains this point.
958 double t1, t2, t3, t4, jac[4][4], det;
959 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
960 if (imap < 0) return 0.;
961
962 const Element& element = m_elements[imap];
963 if (m_debug) {
964 PrintElement("WeightingPotential", x, y, z, t1, t2, t3, t4, element, 10,
965 iw);
966 }
967 const Node& n0 = m_nodes[element.emap[0]];
968 const Node& n1 = m_nodes[element.emap[1]];
969 const Node& n2 = m_nodes[element.emap[2]];
970 const Node& n3 = m_nodes[element.emap[3]];
971 const Node& n4 = m_nodes[element.emap[4]];
972 const Node& n5 = m_nodes[element.emap[5]];
973 const Node& n6 = m_nodes[element.emap[6]];
974 const Node& n7 = m_nodes[element.emap[7]];
975 const Node& n8 = m_nodes[element.emap[8]];
976 const Node& n9 = m_nodes[element.emap[9]];
977 return n0.w[iw] * t1 * (2 * t1 - 1) + n1.w[iw] * t2 * (2 * t2 - 1) +
978 n2.w[iw] * t3 * (2 * t3 - 1) + n3.w[iw] * t4 * (2 * t4 - 1) +
979 4 * n4.w[iw] * t1 * t2 + 4 * n5.w[iw] * t1 * t3 +
980 4 * n6.w[iw] * t1 * t4 + 4 * n7.w[iw] * t2 * t3 +
981 4 * n8.w[iw] * t2 * t4 + 4 * n9.w[iw] * t3 * t4;
982}
983
984Medium* ComponentAnsys123::GetMedium(const double xin, const double yin,
985 const double zin) {
986 // Copy the coordinates.
987 double x = xin, y = yin, z = zin;
988
989 // Map the coordinates onto field map coordinates.
990 bool xmirr, ymirr, zmirr;
991 double rcoordinate, rotation;
992 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
993
994 // Do not proceed if not properly initialised.
995 if (!m_ready) {
996 std::cerr << m_className << "::GetMedium:\n";
997 std::cerr << " Field map not available for interpolation.\n";
998 return nullptr;
999 }
1000 if (m_warning) PrintWarning("GetMedium");
1001
1002 // Find the element that contains this point.
1003 double t1, t2, t3, t4, jac[4][4], det;
1004 const int imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
1005 if (imap < 0) {
1006 if (m_debug) {
1007 std::cerr << m_className << "::GetMedium:\n";
1008 std::cerr << " Point (" << x << ", " << y << ", " << z
1009 << ") not in the mesh.\n";
1010 }
1011 return nullptr;
1012 }
1013 const Element& element = m_elements[imap];
1014 if (element.matmap >= m_materials.size()) {
1015 if (m_debug) {
1016 std::cerr << m_className << "::GetMedium:\n";
1017 std::cerr << " Point (" << x << ", " << y << ", " << z << ")"
1018 << " has out of range material number " << imap << ".\n";
1019 }
1020 return nullptr;
1021 }
1022
1023 if (m_debug) {
1024 PrintElement("GetMedium", x, y, z, t1, t2, t3, t4, element, 10);
1025 }
1026
1027 // Assign a medium.
1028 return m_materials[element.matmap].medium;
1029}
1030
1031double ComponentAnsys123::GetElementVolume(const unsigned int i) {
1032 if (i >= m_elements.size()) return 0.;
1033 const Element& element = m_elements[i];
1034 const Node& n0 = m_nodes[element.emap[0]];
1035 const Node& n1 = m_nodes[element.emap[1]];
1036 const Node& n2 = m_nodes[element.emap[2]];
1037 const Node& n3 = m_nodes[element.emap[3]];
1038 const double vol =
1039 fabs((n3.x - n0.x) *
1040 ((n1.y - n0.y) * (n2.z - n0.z) - (n2.y - n0.y) * (n1.z - n0.z)) +
1041 (n3.y - n0.y) *
1042 ((n1.z - n0.z) * (n2.x - n0.x) - (n2.z - n0.z) * (n1.x - n0.x)) +
1043 (n3.z - n0.z) * ((n1.x - n0.x) * (n2.y - n0.y) -
1044 (n3.x - n0.x) * (n1.y - n0.y))) /
1045 6.;
1046 return vol;
1047}
1048
1049void ComponentAnsys123::GetAspectRatio(const unsigned int i, double& dmin,
1050 double& dmax) {
1051 if (i >= m_elements.size()) {
1052 dmin = dmax = 0.;
1053 return;
1054 }
1055
1056 const Element& element = m_elements[i];
1057 const int np = 4;
1058 // Loop over all pairs of vertices.
1059 for (int j = 0; j < np - 1; ++j) {
1060 const Node& nj = m_nodes[element.emap[j]];
1061 for (int k = j + 1; k < np; ++k) {
1062 const Node& nk = m_nodes[element.emap[k]];
1063 // Compute distance.
1064 const double dx = nj.x - nk.x;
1065 const double dy = nj.y - nk.y;
1066 const double dz = nj.z - nk.z;
1067 const double dist = sqrt(dx * dx + dy * dy + dz * dz);
1068 if (k == 1) {
1069 dmin = dmax = dist;
1070 } else {
1071 if (dist < dmin) dmin = dist;
1072 if (dist > dmax) dmax = dist;
1073 }
1074 }
1075 }
1076}
1077}
bool Initialise(std::string elist="ELIST.lis", std::string nlist="NLIST.lis", std::string mplist="MPLIST.lis", std::string prnsol="PRNSOL.lis", std::string unit="cm")
double GetElementVolume(const unsigned int i) override
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
void GetAspectRatio(const unsigned int i, double &dmin, double &dmax) override
bool SetWeightingField(std::string prnsol, std::string label)
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status) 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.
void PrintMaterials()
List all currently defined materials.
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