Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentCST.cc
Go to the documentation of this file.
1#include <math.h>
2#include <stdlib.h>
3#include <sys/stat.h>
4#include <algorithm>
5#include <fstream>
6#include <functional>
7#include <iomanip>
8#include <iostream>
9#include <sstream>
10#include <vector>
11
13
14namespace {
15
16bool ReadHeader(FILE* f, const int fileSize, const bool debug,
17 int& nX, int& nY, int& nZ, int& nNS,
18 int& nES, int& nEM, int& nMaterials) {
19
20 if (!f) return false;
21 // Size of the header in binary files used in the CST export
22 static constexpr int headerSize = 1000;
23 if (fileSize < headerSize) {
24 std::cerr << "ComponentCST::ReadHeader:\n"
25 << " Error. The file is extremely short and does not seem to "
26 << "contain a header or data." << std::endl;
27 return false;
28 }
29 char header[headerSize];
30 size_t result = fread(header, sizeof(char), headerSize, f);
31 if (result != headerSize) {
32 std::cerr << "ComponentCST::ReadHeader: Could not read the header.\n";
33 return false;
34 }
35
36 int nMeshX = 0, nMeshY = 0, nMeshZ = 0;
37 int nNx = 0, nNy = 0, nNz = 0;
38 int nEx = 0, nEy = 0, nEz = 0;
39
40 std::string fmt = "mesh_nx=%d mesh_ny=%d mesh_nz=%d\n";
41 fmt += "mesh_xlines=%d mesh_ylines=%d mesh_zlines=%d\n";
42 fmt += "nodes_scalar=%d ";
43 fmt += "nodes_vector_x=%d nodes_vector_y=%d nodes_vector_z=%d\n";
44 fmt += "elements_scalar=%d elements_vector_x=%d ";
45 fmt += "elements_vector_y=%d elements_vector_z=%d\n";
46 fmt += "elements_material=%d\n";
47 fmt += "n_materials=%d\n";
48 int filled = std::sscanf(header, fmt.c_str(),
49 &nMeshX, &nMeshY, &nMeshZ, &nX, &nY, &nZ,
50 &nNS, &nNx, &nNy, &nNz, &nES, &nEx, &nEy, &nEz, &nEM, &nMaterials);
51 if (filled != 16) {
52 std::cerr << "ComponentCST::ReadHeader: File header is broken.\n";
53 return false;
54 }
55 if (fileSize < 1000 + (nX + nY + nZ) * 8 +
56 (nNS + nNx + nNy + nNz + nES + nEx + nEy + nEz) * 4 +
57 nEM * 1 + nMaterials * 20) {
58 std::cerr << "ComponentCST::ReadHeader: Unexpected file size.\n";
59 return false;
60 }
61 if (debug) {
62 std::cout << "ComponentCST::ReadHeader:\n"
63 << " Mesh (nx): " << nMeshX << "\t Mesh (ny): " << nMeshY
64 << "\t Mesh (nz): " << nMeshZ << std::endl
65 << " Mesh (x_lines): " << nX << "\t Mesh (y_lines): " << nY
66 << "\t Mesh (z_lines): " << nZ << std::endl
67 << " Nodes (scalar): " << nNS << "\t Nodes (x): " << nNx
68 << "\t Nodes (y): " << nNy << "\t Nodes (z): " << nNz << "\n"
69 << " Field (scalar): " << nES << "\t Field (x): " << nEx
70 << "\t Field (y): " << nEy << "\t Field (z): " << nEz << "\n"
71 << " Elements: " << nEM << "\t Materials: " << nMaterials
72 << std::endl;
73 }
74 return true;
75}
76
77}
78namespace Garfield {
79
81 // Default bounding box
82 m_minBoundingBox[2] = -50.;
83 m_maxBoundingBox[2] = 50.;
84 m_deleteBackground = false;
85}
86
87bool ComponentCST::Initialise(std::string elist, std::string nlist,
88 std::string mplist, std::string prnsol,
89 std::string unit) {
90 Reset();
91 // Keep track of the success
92 bool ok = true;
93
94 // Buffer for reading
95 const int size = 200;
96 char line[size];
97 // Open the material list
98 std::ifstream fmplist;
99 fmplist.open(mplist.c_str(), std::ios::in);
100 if (fmplist.fail()) {
101 PrintCouldNotOpen("Initialise", mplist);
102 return false;
103 }
104
105 // Read the material list
106 int il = 0;
107 bool readerror = false;
108 while (fmplist.getline(line, size, '\n')) {
109 il++;
110 // Split the line in tokens
111 char* token = NULL;
112 token = strtok(line, " ");
113 // Skip blank lines and headers
114 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
115 int(token[0]) == 10 || int(token[0]) == 13)
116 continue;
117 // Read number of materials,
118 // ensure it does not exceed the maximum and initialize the list
119 if (strcmp(token, "Materials") == 0) {
120 token = strtok(NULL, " ");
121 const int nMaterials = ReadInteger(token, -1, readerror);
122 if (readerror) {
123 std::cerr << m_className << "::Initialise:\n"
124 << " Error reading file " << mplist << " (line " << il
125 << ")." << std::endl;
126 fmplist.close();
127 return false;
128 }
129 m_materials.resize(nMaterials);
130 for (auto& material : m_materials) {
131 material.ohm = -1;
132 material.eps = -1;
133 material.medium = nullptr;
134 }
135 if (m_debug) {
136 std::cout << m_className << "::Initialise:" << std::endl;
137 std::cout << " Number of materials: " << nMaterials << "\n";
138 }
139 } else if (strcmp(token, "Material") == 0) {
140 token = strtok(NULL, " ");
141 int imat = ReadInteger(token, -1, readerror);
142 if (readerror) {
143 std::cerr << m_className << "::Initialise:" << std::endl;
144 std::cerr << " Error reading file " << mplist << " (line " << il
145 << ").\n";
146 fmplist.close();
147 return false;
148 } else if (imat < 1 || imat > (int)m_materials.size()) {
149 std::cerr << m_className << "::Initialise:\n"
150 << " Found out-of-range material index " << imat << "in\n"
151 << " material properties file " << mplist << ".\n";
152 ok = false;
153 } else {
154 token = strtok(NULL, " ");
155 int itype = 0;
156 if (strcmp(token, "PERX") == 0) {
157 itype = 1;
158 } else if (strcmp(token, "RSVX") == 0) {
159 itype = 2;
160 } else {
161 std::cerr << m_className << "::Initialise:\n"
162 << " Unknown material property flag " << token << "\n"
163 << " in material properties file " << mplist
164 << " (line " << il << ").\n";
165 ok = false;
166 }
167 token = strtok(NULL, " ");
168 if (itype == 1) {
169 m_materials[imat - 1].eps = ReadDouble(token, -1, readerror);
170 } else if (itype == 2) {
171 m_materials[imat - 1].ohm = ReadDouble(token, -1, readerror);
172 token = strtok(NULL, " ");
173 if (strcmp(token, "PERX") != 0) {
174 std::cerr << m_className << "::Initialise:\n"
175 << " Unknown material property flag " << token << "\n"
176 << " in material file " << mplist << " (material "
177 << imat << ").\n";
178 ok = false;
179 } else {
180 token = strtok(NULL, " ");
181 m_materials[imat - 1].eps = ReadDouble(token, -1, readerror);
182 }
183 }
184 if (readerror) {
185 std::cerr << m_className << "::Initialise:\n"
186 << " Error reading file " << mplist
187 << " (line " << il << ")." << std::endl;
188 fmplist.close();
189 return false;
190 }
191 if (m_debug) {
192 std::cout << m_className << "::Initialise:" << std::endl;
193 std::cout << " Read material properties for material "
194 << (imat - 1) << "" << std::endl;
195 if (itype == 2) {
196 std::cout << " eps = " << m_materials[imat - 1].eps
197 << " ohm = " << m_materials[imat - 1].ohm << ""
198 << std::endl;
199 } else {
200 std::cout << " eps = " << m_materials[imat - 1].eps << ""
201 << std::endl;
202 }
203 }
204 }
205 }
206 }
207 // Close the file
208 fmplist.close();
209
210 // Find lowest epsilon, check for eps = 0, set default drift medium.
211 if (!SetDefaultDriftMedium()) ok = false;
212
213 // Tell how many lines read
214 std::cout << m_className << "::Initialise:\n"
215 << " Read properties of " << m_materials.size() << " materials\n"
216 << " from file " << mplist << "." << std::endl;
217 if (m_debug) PrintMaterials();
218
219 // Check the value of the unit
220 double funit = ScalingFactor(unit);
221 if (funit <= 0.) {
222 std::cerr << m_className << "::Initialise:\n"
223 << " Unknown length unit " << unit << ".\n";
224 ok = false;
225 funit = 1.0;
226 }
227 if (m_debug) {
228 std::cout << m_className << "::Initialise: Unit scaling factor = "
229 << funit << ".\n";
230 }
231
232 // Open the node list
233 std::ifstream fnlist;
234 fnlist.open(nlist.c_str(), std::ios::in);
235 if (fnlist.fail()) {
236 PrintCouldNotOpen("Initialise", nlist);
237 return false;
238 }
239 // Read the node list
240 m_nNodes = 0;
241 il = 0;
242 int xlines = 0, ylines = 0, zlines = 0;
243 int lines_type = -1;
244 double line_tmp;
245 while (fnlist.getline(line, size, '\n')) {
246 il++;
247 // Split the line in tokens
248 char* token = NULL;
249 // Split into tokens
250 token = strtok(line, " ");
251 // Skip blank lines and headers
252 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
253 int(token[0]) == 10 || int(token[0]) == 13)
254 continue;
255 // Read max sizes
256 if (strcmp(token, "xmax") == 0) {
257 token = strtok(NULL, " ");
258 xlines = ReadInteger(token, -1, readerror);
259 token = strtok(NULL, " ");
260 token = strtok(NULL, " ");
261 ylines = ReadInteger(token, -1, readerror);
262 token = strtok(NULL, " ");
263 token = strtok(NULL, " ");
264 zlines = ReadInteger(token, -1, readerror);
265 if (readerror) break;
266 continue;
267 }
268 if (strcmp(token, "x-lines\n") == 0 || strcmp(token, "x-lines") == 0) {
269 lines_type = 1;
270 if (m_debug) {
271 std::cout << m_className << "::Initialise:\n"
272 << " Reading x-lines from file " << nlist << ".\n";
273 }
274 continue;
275 }
276 if (strcmp(token, "y-lines\n") == 0 || strcmp(token, "y-lines") == 0) {
277 lines_type = 2;
278 if (m_debug) {
279 std::cout << m_className << "::Initialise:\n"
280 << " Reading y-lines from file " << nlist << ".\n";
281 }
282 continue;
283 }
284 if (strcmp(token, "z-lines\n") == 0 || strcmp(token, "z-lines") == 0) {
285 lines_type = 3;
286 if (m_debug) {
287 std::cout << m_className << "::Initialise:\n"
288 << " Reading z-lines from file " << nlist << ".\n";
289 }
290 continue;
291 }
292 line_tmp = ReadDouble(token, -1, readerror);
293 if (lines_type == 1)
294 m_xlines.push_back(line_tmp * funit);
295 else if (lines_type == 2)
296 m_ylines.push_back(line_tmp * funit);
297 else if (lines_type == 3)
298 m_zlines.push_back(line_tmp * funit);
299 else {
300 std::cerr << m_className << "::Initialise:" << std::endl;
301 std::cerr << " Line type was not set in " << nlist << " (line " << il
302 << ", token = " << token << ")." << std::endl;
303 std::cerr << " Maybe it is in the wrong format" << std::endl;
304 std::cerr << " e.g. missing tailing space after x-lines." << std::endl;
305 ok = false;
306 break;
307 }
308 if (readerror) break;
309 }
310 // Check syntax
311 if (readerror) {
312 std::cerr << m_className << "::Initialise:\n"
313 << " Error reading file " << nlist
314 << " (line " << il << ").\n";
315 fnlist.close();
316 return false;
317 }
318 // Close the file
319 fnlist.close();
320
321 if ((unsigned)xlines == m_xlines.size() &&
322 (unsigned)ylines == m_ylines.size() &&
323 (unsigned)zlines == m_zlines.size()) {
324 std::cout << m_className << "::Initialise:" << std::endl;
325 std::cout << " Found in file " << nlist << "\n " << xlines
326 << " x-lines\n " << ylines << " y-lines\n " << zlines
327 << " z-lines" << std::endl;
328 } else {
329 std::cerr << m_className << "::Initialise:" << std::endl;
330 std::cerr << " There should be " << xlines << " x-lines, " << ylines
331 << " y-lines and " << zlines << " z-lines in file " << nlist
332 << " but I found :\n " << m_xlines.size() << " x-lines, "
333 << m_ylines.size() << " x-lines, " << m_zlines.size()
334 << " z-lines." << std::endl;
335 }
336 m_nx = m_xlines.size();
337 m_ny = m_ylines.size();
338 m_nz = m_zlines.size();
339 m_nNodes = m_nx * m_ny * m_nz;
340 m_nElements = (m_nx - 1) * (m_ny - 1) * (m_nz - 1);
341
342 // Tell how many lines read
343 std::cout << m_className << "::Initialise:" << std::endl;
344 std::cout << " Read " << m_nNodes << " nodes from file " << nlist << "."
345 << std::endl;
346
347 // Open the element list
348 std::ifstream felist;
349 felist.open(elist.c_str(), std::ios::in);
350 if (felist.fail()) {
351 PrintCouldNotOpen("Initialise", elist);
352 return false;
353 }
354 // Read the element list
355 m_elementMaterial.resize(m_nElements);
356 il = 0;
357 while (felist.getline(line, size, '\n')) {
358 il++;
359 // Split the line in tokens
360 char* token = NULL;
361 // Split into tokens
362 token = strtok(line, " ");
363 // Skip blank lines and headers
364 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
365 int(token[0]) == 10 || int(token[0]) == 13 ||
366 strcmp(token, "LIST") == 0 || strcmp(token, "ELEM") == 0)
367 continue;
368 // Read the element
369 int ielem = ReadInteger(token, -1, readerror);
370 token = strtok(NULL, " ");
371 unsigned char imat = atoi(token);
372 // construct node numbers
373 std::vector<int> node_nb;
374 try {
375 // Read element material - the number of the material is stored (1, 2,
376 // ...) but we need the index (0, 1, ...)
377 m_elementMaterial.at(ielem) = (imat - 1);
378 } catch (...) {
379 std::cerr << m_className << "::Initialise:" << std::endl;
380 std::cerr << " Error reading file " << elist << " (line " << il << ")."
381 << std::endl;
382 std::cerr << " The element index (" << ielem
383 << ") is not in the expected range: 0 - " << m_nElements
384 << std::endl;
385 ok = false;
386 }
387 // Check the material number and ensure that epsilon is non-negative
388 // int check_mat = imat;
389 if (imat < 1 || imat > m_materials.size()) {
390 std::cerr << m_className << "::Initialise:" << std::endl;
391 std::cerr << " Out-of-range material number on file " << elist
392 << " (line " << il << ")." << std::endl;
393 std::cerr << " Element: " << ielem << ", material: " << imat
394 << std::endl;
395 ok = false;
396 }
397 if (m_materials[imat - 1].eps < 0) {
398 std::cerr << m_className << "::Initialise:" << std::endl;
399 std::cerr << " Element " << ielem << " in element list " << elist
400 << " uses material " << imat << " which" << std::endl;
401 std::cerr << " has not been assigned a positive permittivity"
402 << std::endl;
403 std::cerr << " in material list " << mplist << "." << std::endl;
404 ok = false;
405 }
406 }
407 // Close the file
408 felist.close();
409 // Tell how many lines read
410 std::cout << m_className << "::Initialise:" << std::endl;
411 std::cout << " Read " << m_nElements << " elements.\n";
412
413 // Open the voltage list
414 m_potential.resize(m_nNodes);
415 std::ifstream fprnsol;
416 fprnsol.open(prnsol.c_str(), std::ios::in);
417 if (fprnsol.fail()) {
418 PrintCouldNotOpen("Initialise", prnsol);
419 return false;
420 }
421 // Read the voltage list
422 il = 0;
423 int nread = 0;
424 readerror = false;
425 while (fprnsol.getline(line, size, '\n')) {
426 il++;
427 // Split the line in tokens
428 char* token = NULL;
429 token = strtok(line, " ");
430 // Skip blank lines and headers
431 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
432 int(token[0]) == 10 || int(token[0]) == 13 || strcmp(token, "Max") == 0)
433 continue;
434 // Read node potential (in prnsol node id starts with 1 and here we will use
435 // 0 as first node...)
436 int inode = ReadInteger(token, -1, readerror);
437 token = strtok(NULL, " ");
438 double volt = ReadDouble(token, -1, readerror);
439
440 try {
441 m_potential.at(inode - 1) = volt;
442 nread++;
443 } catch (...) {
444 std::cerr << m_className << "::Initialise:" << std::endl;
445 std::cerr << " Error reading file " << prnsol << " (line " << il
446 << ")." << std::endl;
447 std::cerr << " The node index (" << inode - 1
448 << ") is not in the expected range: 0 - " << m_nNodes
449 << std::endl;
450 ok = false;
451 }
452 }
453 // Close the file
454 fprnsol.close();
455 // Tell how many lines read
456 std::cout << m_className << "::Initialise:" << std::endl;
457 std::cout << " Read " << nread << "/" << m_nNodes
458 << " (expected) potentials from file " << prnsol << "."
459 << std::endl;
460 // Check number of nodes
461 if (nread != (int)m_nNodes) {
462 std::cerr << m_className << "::Initialise:" << std::endl;
463 std::cerr << " Number of nodes read (" << nread << ") on potential file "
464 << prnsol << " does not" << std::endl;
465 std::cerr << " match the node list (" << m_nNodes << ").\n";
466 ok = false;
467 }
468 // Set the ready flag
469 if (ok) {
470 m_ready = true;
471 } else {
472 std::cerr << m_className << "::Initialise:" << std::endl;
473 std::cerr << " Field map could not be read and cannot be interpolated."
474 << std::endl;
475 return false;
476 }
477 Prepare();
478 return true;
479}
480
481bool ComponentCST::Initialise(std::string dataFile, std::string unit) {
482 m_ready = false;
483
484 // Keep track of the success
485 bool ok = true;
486 // Check the value of the unit
487 double funit = ScalingFactor(unit);
488 if (funit <= 0.) {
489 std::cerr << m_className << "::Initialise:\n"
490 << " Unknown length unit " << unit << ".\n";
491 ok = false;
492 funit = 1.0;
493 }
494 if (m_debug) {
495 std::cout << m_className << "::Initialise: Unit scaling factor = "
496 << funit << ".\n";
497 }
498 FILE* f = fopen(dataFile.c_str(), "rb");
499 if (f == nullptr) {
500 PrintCouldNotOpen("Initialise", dataFile);
501 return false;
502 }
503
504 struct stat fileStatus;
505 stat(dataFile.c_str(), &fileStatus);
506 int fileSize = fileStatus.st_size;
507
508 int nLinesX = 0, nLinesY = 0, nLinesZ = 0;
509 int nNS = 0, nES = 0, nEM = 0;
510 int nMaterials = 0;
511 if (!ReadHeader(f, fileSize, m_debug, nLinesX, nLinesY, nLinesZ,
512 nNS, nES, nEM, nMaterials)) {
513 if (f) fclose(f);
514 return false;
515 }
516 m_nx = nLinesX;
517 m_ny = nLinesY;
518 m_nz = nLinesZ;
519 m_nNodes = m_nx * m_ny * m_nz;
520 m_nElements = (m_nx - 1) * (m_ny - 1) * (m_nz - 1);
521
522 m_xlines.resize(nLinesX);
523 m_ylines.resize(nLinesY);
524 m_zlines.resize(nLinesZ);
525 m_potential.resize(nNS);
526 m_elementMaterial.resize(nEM);
527 m_materials.resize(nMaterials);
528 auto result = fread(m_xlines.data(), sizeof(double), m_xlines.size(), f);
529 if (result != m_xlines.size()) {
530 fputs("Reading error while reading xlines.", stderr);
531 exit(3);
532 } else if (result == 0) {
533 fputs("No xlines are stored in the data file.", stderr);
534 exit(3);
535 }
536 result = fread(m_ylines.data(), sizeof(double), m_ylines.size(), f);
537 if (result != m_ylines.size()) {
538 fputs("Reading error while reading ylines", stderr);
539 exit(3);
540 } else if (result == 0) {
541 fputs("No ylines are stored in the data file.", stderr);
542 exit(3);
543 }
544 result = fread(m_zlines.data(), sizeof(double), m_zlines.size(), f);
545 if (result != m_zlines.size()) {
546 fputs("Reading error while reading zlines", stderr);
547 exit(3);
548 } else if (result == 0) {
549 fputs("No zlines are stored in the data file.", stderr);
550 exit(3);
551 }
552 result = fread(m_potential.data(), sizeof(float), m_potential.size(), f);
553 if (result != m_potential.size()) {
554 fputs("Reading error while reading nodes.", stderr);
555 exit(3);
556 } else if (result == 0) {
557 fputs("No potentials are stored in the data file.", stderr);
558 exit(3);
559 }
560 fseek(f, nES * sizeof(float), SEEK_CUR);
561 // not needed in principle - thus it is ok if nothing is read
562 result = fread(m_elementMaterial.data(), sizeof(unsigned char),
563 m_elementMaterial.size(), f);
564 if (result != m_elementMaterial.size()) {
565 fputs("Reading error while reading element material", stderr);
566 exit(3);
567 }
568 std::stringstream st;
569 st << m_className << "::Initialise:" << std::endl;
570 /*
571 * The material vector is filled according to the material id!
572 * Thus material.at(0) is material with id 0.
573 */
574 for (unsigned int i = 0; i < m_materials.size(); i++) {
575 float id;
576 result = fread(&(id), sizeof(float), 1, f);
577 if (result != 1) {
578 fputs("Input error while reading material id.", stderr);
579 exit(3);
580 }
581 // const unsigned int index = id;
582 const unsigned int index = i;
583 unsigned int description_size = 0;
584 result = fread(&(description_size), sizeof(int), 1, f);
585 if (result != 1) {
586 fputs("Input error while reading material description size.", stderr);
587 exit(3);
588 }
589 char* c = new char[description_size];
590 result = fread(c, sizeof(char), description_size, f);
591 if (result != description_size) {
592 fputs("Input error while reading material description.", stderr);
593 exit(3);
594 }
595 std::string name = c;
596 st << " Read material: " << name.c_str();
597 if (name.compare("gas") == 0) {
598 st << " (considered as drift medium)";
599 m_materials.at(index).driftmedium = true;
600 } else {
601 m_materials.at(index).driftmedium = false;
602 }
603 delete[] c;
604 float eps;
605 result = fread(&(eps), sizeof(float), 1, f);
606 m_materials.at(index).eps = eps;
607 if (result != 1) {
608 fputs("Reading error while reading eps.", stderr);
609 exit(3);
610 }
611 st << "; eps is: " << m_materials.at(index).eps;
612 // float mue;
613 // result = fread(&(mue), sizeof(float), 1, f);
614 // if (result != 1) {
615 // fputs ("Reading error while reading mue.", stderr);
616 // exit (3);
617 // }
618 // st << "\t mue is: " << mue;
619 // float rho;
620 // result = fread(&(rho), sizeof(float), 1, f);
621 // if (result != 1) {
622 // fputs ("Reading error while reading rho.", stderr);
623 // exit (3);
624 // }
625 // st << "\t rho is: " << rho;
626 st << "\t id is: " << id << std::endl;
627 // Skip mue and rho
628 fseek(f, 2 * sizeof(float), SEEK_CUR);
629 // ToDo: Check if rho should be used to decide, which material is driftable
630 }
631 // To be sure that they are sorted (should be already be the case)
632 std::sort(m_xlines.begin(), m_xlines.end());
633 std::sort(m_ylines.begin(), m_ylines.end());
634 std::sort(m_zlines.begin(), m_zlines.end());
635 if (funit != 1) {
636 std::transform(m_xlines.begin(), m_xlines.end(), m_xlines.begin(), [funit](double x) { return x * funit;});
637 std::transform(m_ylines.begin(), m_ylines.end(), m_ylines.begin(), [funit](double x) { return x * funit;});
638 std::transform(m_zlines.begin(), m_zlines.end(), m_zlines.begin(), [funit](double x) { return x * funit;});
639 }
640
641 std::cout << m_className << "::Initialise" << std::endl;
642 std::cout << " x range: " << *(m_xlines.begin()) << " - "
643 << *(m_xlines.end() - 1) << std::endl;
644 std::cout << " y range: " << *(m_ylines.begin()) << " - "
645 << *(m_ylines.end() - 1) << std::endl;
646 std::cout << " z range: " << *(m_zlines.begin()) << " - "
647 << *(m_zlines.end() - 1) << std::endl;
648 fclose(f);
649 // Set the ready flag
650 if (ok) {
651 m_ready = true;
652 } else {
653 std::cerr << m_className << "::Initialise:" << std::endl;
654 std::cerr << " Field map could not be read and cannot be interpolated."
655 << std::endl;
656 return false;
657 }
658
659 SetRange();
661 return true;
662}
663
664bool ComponentCST::SetWeightingField(std::string prnsol, std::string label,
665 bool isBinary) {
666 std::vector<float> potentials(m_nNodes);
667 if (!m_ready) {
668 std::cerr << m_className << "::SetWeightingField:" << std::endl;
669 std::cerr << " No valid field map is present." << std::endl;
670 std::cerr << " Weighting field cannot be added." << std::endl;
671 return false;
672 }
673
674 // Open the voltage list
675 std::ifstream fprnsol;
676 fprnsol.open(prnsol.c_str(), std::ios::in);
677 if (fprnsol.fail()) {
678 PrintCouldNotOpen("SetWeightingField", prnsol);
679 return false;
680 }
681 // Check if a weighting field with the same label already exists.
682 auto it = m_weightingFields.find(label);
683 if (it != m_weightingFields.end()) {
684 std::cout << m_className << "::SetWeightingField:" << std::endl;
685 std::cout << " Replacing existing weighting field " << label << "."
686 << std::endl;
687 } else {
688 m_wfields.push_back(label);
689 m_wfieldsOk.push_back(false);
690 }
691
692 if (std::distance(m_weightingFields.begin(), it) !=
693 std::distance(m_wfields.begin(),
694 find(m_wfields.begin(), m_wfields.end(), label))) {
695 std::cerr << m_className << "::SetWeightingField:" << std::endl;
696 std::cerr << " Indices of the weighting fields and the weighting field "
697 "counter are not equal!"
698 << std::endl;
699 return false;
700 }
701 unsigned int iField = std::distance(m_weightingFields.begin(), it);
702 int nread = 0;
703 bool ok = true;
704
705 if (isBinary) {
706 std::cout << m_className << "::SetWeightingField:" << std::endl;
707 std::cout << " Reading weighting field from binary file:"
708 << prnsol.c_str() << std::endl;
709 FILE* f = fopen(prnsol.c_str(), "rb");
710 if (f == nullptr) {
711 PrintCouldNotOpen("SetWeightingField", prnsol);
712 return false;
713 }
714
715 struct stat fileStatus;
716 stat(prnsol.c_str(), &fileStatus);
717 int fileSize = fileStatus.st_size;
718
719 int nLinesX = 0, nLinesY = 0, nLinesZ = 0;
720 int nES = 0, nEM = 0;
721 int nMaterials = 0;
722 if (!ReadHeader(f, fileSize, m_debug, nLinesX, nLinesY, nLinesZ,
723 nread, nES, nEM, nMaterials)) {
724 if (f) fclose(f);
725 return false;
726 }
727 // Skip everything, but the potential
728 fseek(f, nLinesX * sizeof(double), SEEK_CUR);
729 fseek(f, nLinesY * sizeof(double), SEEK_CUR);
730 fseek(f, nLinesZ * sizeof(double), SEEK_CUR);
731 auto result = fread(potentials.data(), sizeof(float), potentials.size(), f);
732 if (result != potentials.size()) {
733 fputs("Reading error while reading nodes.", stderr);
734 exit(3);
735 } else if (result == 0) {
736 fputs("No weighting potentials are stored in the data file.", stderr);
737 exit(3);
738 }
739 fprnsol.close();
740 } else {
741 std::cout << m_className << "::SetWeightingField:" << std::endl;
742 std::cout << " Reading weighting field from text file:" << prnsol.c_str()
743 << std::endl;
744 // Buffer for reading
745 const int size = 100;
746 char line[size];
747
748 // Read the voltage list
749 int il = 0;
750
751 bool readerror = false;
752 while (fprnsol.getline(line, size, '\n')) {
753 il++;
754 // Split the line in tokens
755 char* token = NULL;
756 token = strtok(line, " ");
757 // Skip blank lines and headers
758 if (!token || strcmp(token, " ") == 0 || strcmp(token, "\n") == 0 ||
759 int(token[0]) == 10 || int(token[0]) == 13 ||
760 strcmp(token, "PRINT") == 0 || strcmp(token, "*****") == 0 ||
761 strcmp(token, "LOAD") == 0 || strcmp(token, "TIME=") == 0 ||
762 strcmp(token, "MAXIMUM") == 0 || strcmp(token, "VALUE") == 0 ||
763 strcmp(token, "NODE") == 0)
764 continue;
765 // Read the element
766 int inode = ReadInteger(token, -1, readerror);
767 token = strtok(NULL, " ");
768 double volt = ReadDouble(token, -1, readerror);
769 try {
770 potentials.at(inode - 1) = volt;
771 nread++;
772 } catch (...) {
773 std::cerr << m_className << "::SetWeightingField:" << std::endl;
774 std::cerr << " Node number " << inode << " out of range."
775 << std::endl;
776 std::cerr << " on potential file " << prnsol << " (line " << il
777 << ")." << std::endl;
778 std::cerr << " Size of the potential vector is: "
779 << potentials.size() << std::endl;
780 ok = false;
781 }
782 }
783 // Close the file
784 fprnsol.close();
785 }
786 // Tell how many lines read
787 std::cout << m_className << "::SetWeightingField:" << std::endl;
788 std::cout << " Read " << nread << "/" << m_nNodes
789 << " (expected) potentials from file " << prnsol << "."
790 << std::endl;
791 // Check number of nodes
792 if (nread != (int)m_nNodes) {
793 std::cerr << m_className << "::SetWeightingField:" << std::endl;
794 std::cerr << " Number of nodes read (" << nread << ")"
795 << " on potential file (" << prnsol << ")" << std::endl;
796 std::cerr << " does not match the node list (" << m_nNodes << ")."
797 << std::endl;
798 ok = false;
799 }
800 if (!ok) {
801 std::cerr << m_className << "::SetWeightingField:" << std::endl;
802 std::cerr << " Field map could not be read "
803 << "and cannot be interpolated." << std::endl;
804 return false;
805 }
806
807 m_weightingFields[label] = potentials;
808
809 // Set the ready flag.
810 m_wfieldsOk[iField] = ok;
811 return true;
812}
813
814void ComponentCST::ShiftComponent(const double xShift, const double yShift,
815 const double zShift) {
816 std::transform(m_xlines.begin(), m_xlines.end(), m_xlines.begin(), [xShift](double x) { return x + xShift;});
817 std::transform(m_ylines.begin(), m_ylines.end(), m_ylines.begin(), [yShift](double x) { return x + yShift;});
818 std::transform(m_zlines.begin(), m_zlines.end(), m_zlines.begin(), [zShift](double x) { return x + zShift;});
819 SetRange();
821
822 std::cout << m_className << "::ShiftComponent:" << std::endl;
823 std::cout << " Shifted component in x-direction: " << xShift
824 << "\t y-direction: " << yShift << "\t z-direction: " << zShift
825 << std::endl;
826}
827
828void ComponentCST::ElectricField(const double xin, const double yin,
829 const double zin, double& ex, double& ey,
830 double& ez, Medium*& m, int& status) {
831 double volt;
832 ElectricFieldBinary(xin, yin, zin, ex, ey, ez, volt, m, status);
833}
834
835void ComponentCST::ElectricField(const double xin, const double yin,
836 const double zin, double& ex, double& ey,
837 double& ez, double& volt, Medium*& m,
838 int& status) {
839 ElectricFieldBinary(xin, yin, zin, ex, ey, ez, volt, m, status, true);
840}
841
842void ComponentCST::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 auto it = m_weightingFields.find(label);
853 if (it == m_weightingFields.end()) {
854 // Do not proceed if the requested weighting field does not exist.
855 std::cerr << "No weighting field named " << label.c_str() << " found!"
856 << std::endl;
857 return;
858 }
859
860 // Check if the weighting field is properly initialised.
861 if (!m_wfieldsOk[std::distance(m_weightingFields.begin(), it)]) return;
862
863 // Copy the coordinates
864 double x = xin, y = yin, z = zin;
865
866 // Map the coordinates onto field map coordinates and get indexes
867 bool mirrored[3];
868 unsigned int i, j, k;
869 double pos[3] = {0., 0., 0.};
870 if (!Coordinate2Index(x, y, z, i, j, k, pos, mirrored)) {
871 return;
872 }
873
874 double rx = (pos[0] - m_xlines.at(i)) / (m_xlines.at(i + 1) - m_xlines.at(i));
875 double ry = (pos[1] - m_ylines.at(j)) / (m_ylines.at(j + 1) - m_ylines.at(j));
876 double rz = (pos[2] - m_zlines.at(k)) / (m_zlines.at(k + 1) - m_zlines.at(k));
877
878 float fwx = 0., fwy = 0., fwz = 0.;
879 if (!disableFieldComponent[0])
880 fwx = GetFieldComponent(i, j, k, rx, ry, rz, 'x', (*it).second);
881 if (!disableFieldComponent[1])
882 fwy = GetFieldComponent(i, j, k, rx, ry, rz, 'y', (*it).second);
883 if (!disableFieldComponent[2])
884 fwz = GetFieldComponent(i, j, k, rx, ry, rz, 'z', (*it).second);
885
886 if (m_elementMaterial.size() > 0 && doShaping) {
887 ShapeField(fwx, fwy, fwz, rx, ry, rz, i, j, k, (*it).second);
888 }
889 if (mirrored[0]) fwx *= -1.f;
890 if (mirrored[1]) fwy *= -1.f;
891 if (mirrored[2]) fwz *= -1.f;
892 if (m_warning) PrintWarning("WeightingField");
893 if (m_materials.at(m_elementMaterial.at(Index2Element(i, j, k))).driftmedium) {
894 if (!disableFieldComponent[0]) wx = fwx;
895 if (!disableFieldComponent[1]) wy = fwy;
896 if (!disableFieldComponent[2]) wz = fwz;
897 }
898}
899
900double ComponentCST::WeightingPotential(const double xin, const double yin,
901 const double zin,
902 const std::string& label) {
903 // Do not proceed if not properly initialised.
904 if (!m_ready) return 0.;
905
906 // Look for the label.
907 auto it = m_weightingFields.find(label);
908 if (it == m_weightingFields.end()) {
909 // Do not proceed if the requested weighting field does not exist.
910 std::cerr << "No weighting field named " << label.c_str() << " found!"
911 << std::endl;
912 return 0.;
913 }
914
915 // Check if the weighting field is properly initialised.
916 if (!m_wfieldsOk[std::distance(m_weightingFields.begin(), it)]) return 0.;
917
918 // Copy the coordinates
919 double x = xin, y = yin, z = zin;
920
921 // Map the coordinates onto field map coordinates
922 bool mirrored[3];
923 unsigned int i, j, k;
924 double pos[3] = {0., 0., 0.};
925 if (!Coordinate2Index(x, y, z, i, j, k, pos, mirrored)) {
926 return 0.;
927 }
928 double rx = (pos[0] - m_xlines.at(i)) /
929 (m_xlines.at(i + 1) - m_xlines.at(i));
930 double ry = (pos[1] - m_ylines.at(j)) /
931 (m_ylines.at(j + 1) - m_ylines.at(j));
932 double rz = (pos[2] - m_zlines.at(k)) /
933 (m_zlines.at(k + 1) - m_zlines.at(k));
934
935 double potential = GetPotential(i, j, k, rx, ry, rz, (*it).second);
936
937 if (m_debug) {
938 std::cout << m_className << "::WeightingPotential:" << std::endl;
939 std::cout << " Global: (" << x << "," << y << "," << z << "),"
940 << std::endl;
941 std::cout << " Local: (" << rx << "," << ry << "," << rz
942 << ") in element with indexes: i=" << i << ", j=" << j
943 << ", k=" << k << std::endl;
944 std::cout << " Node xyzV:" << std::endl;
945 std::cout << "Node 0 position: " << Index2Node(i + 1, j, k)
946 << "\t potential: " << ((*it).second).at(Index2Node(i + 1, j, k))
947 << "Node 1 position: " << Index2Node(i + 1, j + 1, k)
948 << "\t potential: "
949 << ((*it).second).at(Index2Node(i + 1, j + 1, k))
950 << "Node 2 position: " << Index2Node(i, j + 1, k)
951 << "\t potential: " << ((*it).second).at(Index2Node(i, j + 1, k))
952 << "Node 3 position: " << Index2Node(i, j, k)
953 << "\t potential: " << ((*it).second).at(Index2Node(i, j, k))
954 << "Node 4 position: " << Index2Node(i + 1, j, k + 1)
955 << "\t potential: "
956 << ((*it).second).at(Index2Node(i + 1, j, k + 1))
957 << "Node 5 position: " << Index2Node(i + 1, j + 1, k + 1)
958 << "\t potential: "
959 << ((*it).second).at(Index2Node(i + 1, j + 1, k + 1))
960 << "Node 6 position: " << Index2Node(i, j + 1, k + 1)
961 << "\t potential: "
962 << ((*it).second).at(Index2Node(i, j + 1, k + 1))
963 << "Node 7 position: " << Index2Node(i, j, k + 1)
964 << "\t potential: " << ((*it).second).at(Index2Node(i, j, k))
965 << std::endl;
966 }
967 return potential;
968}
969
970void ComponentCST::GetNumberOfMeshLines(unsigned int& n_x, unsigned int& n_y,
971 unsigned int& n_z) const {
972 n_x = m_xlines.size();
973 n_y = m_ylines.size();
974 n_z = m_zlines.size();
975}
976
977bool ComponentCST::GetElement(const size_t element, size_t& mat, bool& drift,
978 std::vector<size_t>& nodes) const {
979 if (element >= m_nElements || element >= m_elementMaterial.size()) {
980 std::cerr << m_className << "::GetElement: Index out of range.\n";
981 return false;
982 }
983 mat = m_elementMaterial[element];
984 drift = m_materials[mat].driftmedium;
985 nodes.clear();
986 unsigned int i0 = 0, j0 = 0, k0 = 0;
987 Element2Index(element, i0, j0, k0);
988 const auto i1 = i0 + 1;
989 const auto j1 = j0 + 1;
990 const auto k1 = k0 + 1;
991 nodes.push_back(Index2Node(i0, j0, k0));
992 nodes.push_back(Index2Node(i1, j0, k0));
993 nodes.push_back(Index2Node(i0, j1, k0));
994 nodes.push_back(Index2Node(i1, j1, k0));
995 nodes.push_back(Index2Node(i0, j0, k1));
996 nodes.push_back(Index2Node(i1, j0, k1));
997 nodes.push_back(Index2Node(i0, j1, k1));
998 nodes.push_back(Index2Node(i1, j1, k1));
999 return true;
1000}
1001
1002bool ComponentCST::GetNode(const size_t node,
1003 double& x, double& y, double& z) const {
1004 if (node >= m_nNodes) {
1005 std::cerr << m_className << "::GetNode: Index out of range.\n";
1006 return false;
1007 }
1008 unsigned int i = 0, j = 0, k = 0;
1009 Node2Index(node, i, j, k);
1010 x = m_xlines[i];
1011 y = m_ylines[j];
1012 z = m_zlines[k];
1013 return true;
1014}
1015
1016void ComponentCST::GetElementBoundaries(unsigned int element, double& xmin,
1017 double& xmax, double& ymin,
1018 double& ymax, double& zmin,
1019 double& zmax) const {
1020 unsigned int i, j, k;
1021 Element2Index(element, i, j, k);
1022 xmin = m_xlines.at(i);
1023 xmax = m_xlines.at(i + 1);
1024 ymin = m_ylines.at(j);
1025 ymax = m_ylines.at(j + 1);
1026 zmin = m_zlines.at(k);
1027 zmax = m_zlines.at(k + 1);
1028}
1029
1030Medium* ComponentCST::GetMedium(const double x, const double y,
1031 const double z) {
1032 unsigned int i, j, k;
1033 Coordinate2Index(x, y, z, i, j, k);
1034 if (m_debug) {
1035 std::cout << m_className << "::GetMedium:\n"
1036 << " Position (" << x << ", " << y << ", " << z << "):\n"
1037 << " Indices are: x: " << i << "/" << m_xlines.size()
1038 << "\t y: " << j << "/" << m_ylines.size()
1039 << "\t z: " << k << "/" << m_zlines.size() << std::endl;
1040 const auto element = Index2Element(i, j, k);
1041 std::cout << " Element index: " << element << std::endl
1042 << " Material index: "
1043 << (int)m_elementMaterial.at(element) << std::endl;
1044 }
1045 return m_materials.at(m_elementMaterial.at(Index2Element(i, j, k))).medium;
1046}
1047
1049 // Establish the ranges
1050 m_mapmin[0] = m_xlines.front();
1051 m_mapmax[0] = m_xlines.back();
1052 m_mapmin[1] = m_ylines.front();
1053 m_mapmax[1] = m_ylines.back();
1054 m_mapmin[2] = m_zlines.front();
1055 m_mapmax[2] = m_zlines.back();
1056 m_mapvmin = *std::min_element(m_potential.begin(), m_potential.end());
1057 m_mapvmax = *std::max_element(m_potential.begin(), m_potential.end());
1058
1059 // Set provisional cell dimensions.
1060 m_minBoundingBox[0] = m_mapmin[0];
1061 m_maxBoundingBox[0] = m_mapmax[0];
1062 m_minBoundingBox[1] = m_mapmin[1];
1063 m_maxBoundingBox[1] = m_mapmax[1];
1064 if (m_is3d) {
1065 m_minBoundingBox[2] = m_mapmin[2];
1066 m_maxBoundingBox[2] = m_mapmax[2];
1067 } else {
1068 m_mapmin[2] = m_minBoundingBox[2];
1069 m_mapmax[2] = m_maxBoundingBox[2];
1070 }
1071 m_hasBoundingBox = true;
1072}
1073
1074void ComponentCST::SetRangeZ(const double zmin, const double zmax) {
1075 if (fabs(zmax - zmin) <= 0.) {
1076 std::cerr << m_className << "::SetRangeZ:" << std::endl;
1077 std::cerr << " Zero range is not permitted." << std::endl;
1078 return;
1079 }
1080 m_minBoundingBox[2] = std::min(zmin, zmax);
1081 m_maxBoundingBox[2] = std::max(zmin, zmax);
1082}
1083
1084bool ComponentCST::Coordinate2Index(const double x, const double y,
1085 const double z, unsigned int& i,
1086 unsigned int& j, unsigned int& k) const {
1087 bool mirrored[3] = {false, false, false};
1088 double pos[3] = {0., 0., 0.};
1089 return Coordinate2Index(x, y, z, i, j, k, pos, mirrored);
1090}
1091
1092
1093bool ComponentCST::Coordinate2Index(const double xin, const double yin,
1094 const double zin, unsigned int& i,
1095 unsigned int& j, unsigned int& k,
1096 double* pos, bool* mirrored) const {
1097 // Map the coordinates onto field map coordinates
1098 pos[0] = xin;
1099 pos[1] = yin;
1100 pos[2] = zin;
1101 double rcoordinate = 0.;
1102 double rotation = 0.;
1103 MapCoordinates(pos[0], pos[1], pos[2],
1104 mirrored[0], mirrored[1], mirrored[2], rcoordinate, rotation);
1105
1106 auto it_x =
1107 std::lower_bound(m_xlines.begin(), m_xlines.end(), pos[0]);
1108 auto it_y =
1109 std::lower_bound(m_ylines.begin(), m_ylines.end(), pos[1]);
1110 auto it_z =
1111 std::lower_bound(m_zlines.begin(), m_zlines.end(), pos[2]);
1112 if (it_x == m_xlines.end() || it_y == m_ylines.end() ||
1113 it_z == m_zlines.end() || pos[0] < m_xlines.at(0) ||
1114 pos[1] < m_ylines.at(0) ||
1115 pos[2] < m_zlines.at(0)) {
1116 if (m_debug) {
1117 std::cerr << m_className << "::ElectricFieldBinary:" << std::endl;
1118 std::cerr << " Could not find the given coordinate!" << std::endl;
1119 std::cerr << " You ask for the following position: " << xin << ", "
1120 << yin << ", " << zin << std::endl;
1121 std::cerr << " The mapped position is: " << pos[0] << ", "
1122 << pos[1] << ", " << pos[2]
1123 << std::endl;
1124 }
1125 return false;
1126 }
1127 /* Lower bound returns the next mesh line behind the position in question.
1128 * If the position in question is on a mesh line this mesh line is returned.
1129 * Since we are interested in the mesh line before the position in question we
1130 * need to move the
1131 * iterator to the left except for the very first mesh line!
1132 */
1133 if (it_x == m_xlines.begin())
1134 i = 0;
1135 else
1136 i = std::distance(m_xlines.begin(), it_x - 1);
1137 if (it_y == m_ylines.begin())
1138 j = 0;
1139 else
1140 j = std::distance(m_ylines.begin(), it_y - 1);
1141 if (it_z == m_zlines.begin())
1142 k = 0;
1143 else
1144 k = std::distance(m_zlines.begin(), it_z - 1);
1145 return true;
1146}
1147
1148int ComponentCST::Index2Element(const unsigned int i, const unsigned int j,
1149 const unsigned int k) const {
1150 if (i > m_nx - 2 || j > m_ny - 2 || k > m_nz - 2) {
1151 throw "ComponentCST::Index2Element: Error. Element indices out of bounds.";
1152 }
1153 return i + j * (m_nx - 1) + k * (m_nx - 1) * (m_ny - 1);
1154}
1155
1159}
1160
1161void ComponentCST::GetAspectRatio(const unsigned int element, double& dmin,
1162 double& dmax) {
1163 if (element >= m_nElements) {
1164 dmin = dmax = 0.;
1165 return;
1166 }
1167 unsigned int i, j, k;
1168 Element2Index(element, i, j, k);
1169 const double dx = fabs(m_xlines.at(i + 1) - m_xlines.at(i));
1170 const double dy = fabs(m_ylines.at(j + 1) - m_ylines.at(j));
1171 const double dz = fabs(m_zlines.at(k + 1) - m_zlines.at(k));
1172 dmin = std::min({dx, dy, dz});
1173 dmax = std::max({dx, dy, dz});
1174}
1175
1176double ComponentCST::GetElementVolume(const unsigned int element) {
1177 if (element >= m_nElements) return 0.;
1178 unsigned int i, j, k;
1179 Element2Index(element, i, j, k);
1180 const double dx = fabs(m_xlines.at(i + 1) - m_xlines.at(i));
1181 const double dy = fabs(m_ylines.at(j + 1) - m_ylines.at(j));
1182 const double dz = fabs(m_zlines.at(k + 1) - m_zlines.at(k));
1183 return dx * dy * dz;
1184}
1185
1186void ComponentCST::ElectricFieldBinary(const double xin, const double yin,
1187 const double zin, double& ex, double& ey,
1188 double& ez, double& volt, Medium*& m,
1189 int& status, bool calculatePotential) const {
1190 // Copy the coordinates
1191 double x = xin, y = yin, z = zin;
1192
1193 ex = ey = ez = 0;
1194
1195 bool mirrored[3];
1196 unsigned int i, j, k;
1197 double pos[3] = {0., 0., 0.};
1198 if (!Coordinate2Index(x, y, z, i, j, k, pos, mirrored)) {
1199 return;
1200 }
1201 double rx = (pos[0] - m_xlines.at(i)) / (m_xlines.at(i + 1) - m_xlines.at(i));
1202 double ry = (pos[1] - m_ylines.at(j)) / (m_ylines.at(j + 1) - m_ylines.at(j));
1203 double rz = (pos[2] - m_zlines.at(k)) / (m_zlines.at(k + 1) - m_zlines.at(k));
1204
1205 float fex = GetFieldComponent(i, j, k, rx, ry, rz, 'x', m_potential);
1206 float fey = GetFieldComponent(i, j, k, rx, ry, rz, 'y', m_potential);
1207 float fez = GetFieldComponent(i, j, k, rx, ry, rz, 'z', m_potential);
1208
1209 if (m_elementMaterial.size() > 0 && doShaping) {
1210 ShapeField(fex, fey, fez, rx, ry, rz, i, j, k, m_potential);
1211 }
1212 if (mirrored[0]) fex *= -1.f;
1213 if (mirrored[1]) fey *= -1.f;
1214 if (mirrored[2]) fez *= -1.f;
1215 if (m_debug) {
1216 std::cout << m_className << "::ElectricFieldBinary:" << std::endl;
1217 std::cout << " Found position (" << x << ", " << y << ", " << z
1218 << "): " << std::endl;
1219 std::cout << " Indices are: x: " << i << "/" << m_xlines.size()
1220 << "\t y: " << j << "/" << m_ylines.size() << "\t z: " << k << "/"
1221 << m_zlines.size() << std::endl;
1222 if (i != 0 && j != 0 && k != 0) {
1223 std::cout << " index: " << i << "\t x before: " << m_xlines.at(i - 1)
1224 << "\t x behind: " << m_xlines.at(i) << "\t r = " << rx
1225 << "\n index: " << j << "\t y before: " << m_ylines.at(j - 1)
1226 << "\t y behind: " << m_ylines.at(j) << "\t r = " << ry
1227 << "\n index: " << k << "\t z before: " << m_zlines.at(k - 1)
1228 << "\t z behind: " << m_zlines.at(k) << "\t r = " << rz
1229 << std::endl;
1230 }
1231 std::cout << " Electric field is: " << fex << ", " << fey << ", " << fez
1232 << "): " << std::endl;
1233 }
1234 // Get the material index of the element and return the medium taken from the
1235 // materials (since the material id is equal to the material vector position)
1236 const auto imat = m_elementMaterial.at(Index2Element(i, j, k));
1237 m = m_materials.at(imat).medium;
1238 // m = materials[elements[imap].matmap].medium;
1239 status = -5;
1240 if (m_materials.at(imat).driftmedium) {
1241 if (m) {
1242 if (m->IsDriftable()) status = 0;
1243 }
1244 }
1245 if (!disableFieldComponent[0]) ex = fex;
1246 if (!disableFieldComponent[1]) ey = fey;
1247 if (!disableFieldComponent[2]) ez = fez;
1248 if (calculatePotential)
1249 volt = GetPotential(i, j, k, rx, ry, rz, m_potential);
1250}
1251
1252float ComponentCST::GetFieldComponent(
1253 const unsigned int i, const unsigned int j, const unsigned int k,
1254 const double rx, const double ry, const double rz,
1255 const char component, const std::vector<float>& potentials) const {
1256 float e = 0.;
1257 if (component == 'x') {
1258 const float dv1 = potentials.at(Index2Node(i + 1, j, k)) -
1259 potentials.at(Index2Node(i, j, k));
1260 const float dv2 = potentials.at(Index2Node(i + 1, j + 1, k)) -
1261 potentials.at(Index2Node(i, j + 1, k));
1262 const float dv3 = potentials.at(Index2Node(i + 1, j + 1, k + 1)) -
1263 potentials.at(Index2Node(i, j + 1, k + 1));
1264 const float dv4 = potentials.at(Index2Node(i + 1, j, k + 1)) -
1265 potentials.at(Index2Node(i, j, k + 1));
1266
1267 const float dv11 = dv1 + (dv4 - dv1) * rz;
1268 const float dv21 = dv2 + (dv3 - dv2) * rz;
1269 const float dv = dv11 + (dv21 - dv11) * ry;
1270 e = -1 * dv / (m_xlines.at(i + 1) - m_xlines.at(i));
1271 }
1272 if (component == 'y') {
1273 const float dv1 = potentials.at(Index2Node(i, j + 1, k)) -
1274 potentials.at(Index2Node(i, j, k));
1275 const float dv2 = potentials.at(Index2Node(i, j + 1, k + 1)) -
1276 potentials.at(Index2Node(i, j, k + 1));
1277 const float dv3 = potentials.at(Index2Node(i + 1, j + 1, k + 1)) -
1278 potentials.at(Index2Node(i + 1, j, k + 1));
1279 const float dv4 = potentials.at(Index2Node(i + 1, j + 1, k)) -
1280 potentials.at(Index2Node(i + 1, j, k));
1281
1282 const float dv11 = dv1 + (dv4 - dv1) * rx;
1283 const float dv21 = dv2 + (dv3 - dv2) * rx;
1284 const float dv = dv11 + (dv21 - dv11) * rz;
1285 e = -1 * dv / (m_ylines.at(j + 1) - m_ylines.at(j));
1286 }
1287 if (component == 'z') {
1288 const float dv1 = potentials.at(Index2Node(i, j, k + 1)) -
1289 potentials.at(Index2Node(i, j, k));
1290 const float dv2 = potentials.at(Index2Node(i + 1, j, k + 1)) -
1291 potentials.at(Index2Node(i + 1, j, k));
1292 const float dv3 = potentials.at(Index2Node(i + 1, j + 1, k + 1)) -
1293 potentials.at(Index2Node(i + 1, j + 1, k));
1294 const float dv4 = potentials.at(Index2Node(i, j + 1, k + 1)) -
1295 potentials.at(Index2Node(i, j + 1, k));
1296
1297 const float dv11 = dv1 + (dv4 - dv1) * ry;
1298 const float dv21 = dv2 + (dv3 - dv2) * ry;
1299 const float dv = dv11 + (dv21 - dv11) * rx;
1300 e = -1 * dv / (m_zlines.at(k + 1) - m_zlines.at(k));
1301 }
1302 return e;
1303}
1304
1305float ComponentCST::GetPotential(
1306 const unsigned int i, const unsigned int j, const unsigned int k,
1307 const double rx, const double ry, const double rz,
1308 const std::vector<float>& potentials) const {
1309 double t1 = rx * 2. - 1;
1310 double t2 = ry * 2. - 1;
1311 double t3 = rz * 2. - 1;
1312 return (potentials.at(Index2Node(i + 1, j, k)) * (1 - t1) * (1 - t2) *
1313 (1 - t3) +
1314 potentials.at(Index2Node(i + 1, j + 1, k)) * (1 + t1) * (1 - t2) *
1315 (1 - t3) +
1316 potentials.at(Index2Node(i, j + 1, k)) * (1 + t1) * (1 + t2) *
1317 (1 - t3) +
1318 potentials.at(Index2Node(i, j, k)) * (1 - t1) * (1 + t2) * (1 - t3) +
1319 potentials.at(Index2Node(i + 1, j, k + 1)) * (1 - t1) * (1 - t2) *
1320 (1 + t3) +
1321 potentials.at(Index2Node(i + 1, j + 1, k + 1)) * (1 + t1) *
1322 (1 - t2) * (1 + t3) +
1323 potentials.at(Index2Node(i, j + 1, k + 1)) * (1 + t1) * (1 + t2) *
1324 (1 + t3) +
1325 potentials.at(Index2Node(i, j, k + 1)) * (1 - t1) * (1 + t2) *
1326 (1 + t3)) /
1327 8.;
1328}
1329
1330void ComponentCST::ShapeField(float& ex, float& ey, float& ez,
1331 const double rx, const double ry, const double rz,
1332 const unsigned int i, const unsigned int j, const unsigned int k,
1333 const std::vector<float>& potentials) const {
1334
1335 const auto m1 = m_elementMaterial.at(Index2Element(i, j, k));
1336 const auto imax = m_xlines.size() - 2;
1337 if ((i == 0 && rx >= 0.5) || (i == imax && rx < 0.5) || (i > 0 && i < imax)) {
1338 if (rx >= 0.5) {
1339 const auto m2 = m_elementMaterial.at(Index2Element(i + 1, j, k));
1340 if (m1 == m2) {
1341 float ex_next =
1342 GetFieldComponent(i + 1, j, k, 0.5, ry, rz, 'x', potentials);
1343 ex = ex +
1344 (rx - 0.5) * (ex_next - ex) *
1345 (m_xlines.at(i + 1) - m_xlines.at(i)) /
1346 (m_xlines.at(i + 2) - m_xlines.at(i + 1));
1347 }
1348 } else {
1349 const auto m2 = m_elementMaterial.at(Index2Element(i - 1, j, k));
1350 if (m1 == m2) {
1351 float ex_before =
1352 GetFieldComponent(i - 1, j, k, 0.5, ry, rz, 'x', potentials);
1353 ex = ex_before +
1354 (rx + 0.5) * (ex - ex_before) *
1355 (m_xlines.at(i) - m_xlines.at(i - 1)) /
1356 (m_xlines.at(i + 1) - m_xlines.at(i));
1357 }
1358 }
1359 }
1360
1361 const auto jmax = m_ylines.size() - 2;
1362 if ((j == 0 && ry >= 0.5) || (j == jmax && ry < 0.5) || (j > 0 && j < jmax)) {
1363 if (ry >= 0.5) {
1364 const auto m2 = m_elementMaterial.at(Index2Element(i, j + 1, k));
1365 if (m1 == m2) {
1366 float ey_next =
1367 GetFieldComponent(i, j + 1, k, rx, 0.5, rz, 'y', potentials);
1368 ey = ey +
1369 (ry - 0.5) * (ey_next - ey) *
1370 (m_ylines.at(j + 1) - m_ylines.at(j)) /
1371 (m_ylines.at(j + 2) - m_ylines.at(j + 1));
1372 }
1373 } else {
1374 const auto m2 = m_elementMaterial.at(Index2Element(i, j - 1, k));
1375 if (m1 == m2) {
1376 float ey_next =
1377 GetFieldComponent(i, j - 1, k, rx, 0.5, rz, 'y', potentials);
1378 ey = ey_next +
1379 (ry + 0.5) * (ey - ey_next) *
1380 (m_ylines.at(j) - m_ylines.at(j - 1)) /
1381 (m_ylines.at(j + 1) - m_ylines.at(j));
1382 }
1383 }
1384 }
1385 const auto kmax = m_zlines.size() - 2;
1386 if ((k == 0 && rz >= 0.5) || (k == kmax && rz < 0.5) || (k > 0 && k < kmax)) {
1387 if (rz >= 0.5) {
1388 const auto m2 = m_elementMaterial.at(Index2Element(i, j, k + 1));
1389 if (m1 == m2) {
1390 float ez_next =
1391 GetFieldComponent(i, j, k + 1, rx, ry, 0.5, 'z', potentials);
1392 ez = ez +
1393 (rz - 0.5) * (ez_next - ez) *
1394 (m_zlines.at(k + 1) - m_zlines.at(k)) /
1395 (m_zlines.at(k + 2) - m_zlines.at(k + 1));
1396 }
1397 } else {
1398 const auto m2 = m_elementMaterial.at(Index2Element(i, j, k - 1));
1399 if (m1 == m2) {
1400 float ez_next =
1401 GetFieldComponent(i, j, k - 1, rx, ry, 0.5, 'z', potentials);
1402 ez = ez_next +
1403 (rz + 0.5) * (ez - ez_next) *
1404 (m_zlines.at(k) - m_zlines.at(k - 1)) /
1405 (m_zlines.at(k + 1) - m_zlines.at(k));
1406 }
1407 }
1408 }
1409}
1410
1411void ComponentCST::Element2Index(const size_t element,
1412 unsigned int& i, unsigned int& j, unsigned int& k) const {
1413 const auto nx = m_xlines.size() - 1;
1414 const auto ny = m_ylines.size() - 1;
1415 const auto nxy = nx * ny;
1416 k = element / nxy;
1417 const auto tmp = element - k * nxy;
1418 j = tmp / nx;
1419 i = tmp - j * nx;
1420}
1421
1422int ComponentCST::Index2Node(const unsigned int i, const unsigned int j,
1423 const unsigned int k) const {
1424 if (i > m_nx - 1 || j > m_ny - 1 || k > m_nz - 1) {
1425 throw "ComponentCST::Index2Node: Error. Node indices out of bounds.";
1426 }
1427 return i + j * m_nx + k * m_nx * m_ny;
1428}
1429
1430void ComponentCST::Node2Index(const size_t node,
1431 unsigned int& i, unsigned int& j, unsigned int& k) const {
1432
1433 const auto nx = m_xlines.size();
1434 const auto ny = m_ylines.size();
1435 const auto nxy = nx * ny;
1436 k = node / nxy;
1437 const auto tmp = node - k * nxy;
1438 j = tmp / nx;
1439 i = tmp - j * nx;
1440}
1441
1442}
double GetPotential(int ele, Point3D *localP)
bool Coordinate2Index(const double x, const double y, const double z, unsigned int &i, unsigned int &j, unsigned int &k) const
void UpdatePeriodicity() override
Verify periodicities.
bool GetElement(const size_t i, size_t &mat, bool &drift, std::vector< size_t > &nodes) const override
Return the material and node indices of a mesh element.
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 WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
bool Initialise(std::string elist, std::string nlist, std::string mplist, std::string prnsol, std::string unit="cm")
Definition: ComponentCST.cc:87
bool GetNode(const size_t i, double &x, double &y, double &z) const override
Medium * GetMedium(const double x, const double y, const double z) override
Get the medium at a given location (x, y, z).
void GetElementBoundaries(unsigned int element, double &xmin, double &xmax, double &ymin, double &ymax, double &zmin, double &zmax) const
void GetNumberOfMeshLines(unsigned int &nx, unsigned int &ny, unsigned int &nz) const
bool SetWeightingField(std::string prnsol, std::string label, bool isBinary=true)
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
void ShiftComponent(const double xShift, const double yShift, const double zShift)
void SetRange() override
Calculate x, y, z, V and angular ranges.
void GetAspectRatio(const unsigned int i, double &dmin, double &dmax) override
void SetRangeZ(const double zmin, const double zmax)
ComponentCST()
Constructor.
Definition: ComponentCST.cc:80
int Index2Element(const unsigned int i, const unsigned int j, const unsigned int k) const
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)
std::array< double, 3 > m_maxBoundingBox
std::array< double, 3 > m_mapmin
std::array< double, 3 > m_minBoundingBox
static double ScalingFactor(std::string unit)
int ReadInteger(char *token, int def, bool &error)
std::vector< Material > m_materials
std::vector< bool > m_wfieldsOk
void PrintCouldNotOpen(const std::string &header, const std::string &filename) const
std::vector< std::string > m_wfields
std::array< double, 3 > m_mapmax
void Reset() override
Reset the component.
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