Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentFieldMap.cc
Go to the documentation of this file.
2
3#include <math.h>
4#include <stdio.h>
5
6#include <algorithm>
7#include <fstream>
8#include <iostream>
9#include <string>
10
12
13namespace Garfield {
14
16 : Component(name) {}
17
19
21 // Do not proceed if not properly initialised.
22 if (!m_ready) PrintNotReady("PrintMaterials");
23
24 if (m_materials.empty()) {
25 std::cerr << m_className << "::PrintMaterials:\n"
26 << " No materials are currently defined.\n";
27 return;
28 }
29
30 const size_t nMaterials = m_materials.size();
31 std::cout << m_className << "::PrintMaterials:\n"
32 << " Currently " << nMaterials << " materials are defined.\n"
33 << " Index Permittivity Resistivity Notes\n";
34 for (size_t i = 0; i < nMaterials; ++i) {
35 printf(" %5zu %12g %12g", i, m_materials[i].eps, m_materials[i].ohm);
36 if (m_materials[i].medium) {
37 std::string name = m_materials[i].medium->GetName();
38 std::cout << " " << name;
39 if (m_materials[i].medium->IsDriftable()) std::cout << ", drift medium";
40 if (m_materials[i].medium->IsIonisable()) std::cout << ", ionisable";
41 }
42 if (m_materials[i].driftmedium) {
43 std::cout << " (drift medium)\n";
44 } else {
45 std::cout << "\n";
46 }
47 }
48}
49
50void ComponentFieldMap::DriftMedium(const unsigned int imat) {
51 // Do not proceed if not properly initialised.
52 if (!m_ready) PrintNotReady("DriftMedium");
53
54 // Check value
55 if (imat >= m_materials.size()) {
56 std::cerr << m_className << "::DriftMedium: Index out of range.\n";
57 return;
58 }
59
60 // Make drift medium
61 m_materials[imat].driftmedium = true;
62}
63
64void ComponentFieldMap::NotDriftMedium(const unsigned int imat) {
65 // Do not proceed if not properly initialised.
66 if (!m_ready) PrintNotReady("NotDriftMedium");
67
68 // Check value
69 if (imat >= m_materials.size()) {
70 std::cerr << m_className << "::NotDriftMedium: Index out of range.\n";
71 return;
72 }
73
74 // Make drift medium
75 m_materials[imat].driftmedium = false;
76}
77
78double ComponentFieldMap::GetPermittivity(const unsigned int imat) const {
79 if (imat >= m_materials.size()) {
80 std::cerr << m_className << "::GetPermittivity: Index out of range.\n";
81 return -1.;
82 }
83
84 return m_materials[imat].eps;
85}
86
87double ComponentFieldMap::GetConductivity(const unsigned int imat) const {
88 if (imat >= m_materials.size()) {
89 std::cerr << m_className << "::GetConductivity: Index out of range.\n";
90 return -1.;
91 }
92
93 return m_materials[imat].ohm;
94}
95
96void ComponentFieldMap::SetMedium(const unsigned int imat, Medium* m) {
97 if (imat >= m_materials.size()) {
98 std::cerr << m_className << "::SetMedium: Index out of range.\n";
99 return;
100 }
101
102 if (!m) {
103 std::cerr << m_className << "::SetMedium: Null pointer.\n";
104 return;
105 }
106
107 if (m_debug) {
108 std::cout << m_className << "::SetMedium:\n Associated material " << imat
109 << " with medium " << m->GetName() << ".\n";
110 }
111
112 m_materials[imat].medium = m;
113}
114
115Medium* ComponentFieldMap::GetMedium(const unsigned int imat) const {
116 if (imat >= m_materials.size()) {
117 std::cerr << m_className << "::GetMedium: Index out of range.\n";
118 return nullptr;
119 }
120
121 return m_materials[imat].medium;
122}
123
124bool ComponentFieldMap::GetElement(const size_t i, double& vol,
125 double& dmin, double& dmax) {
126 if (i >= m_elements.size()) {
127 std::cerr << m_className << "::GetElement: Index out of range.\n";
128 return false;
129 }
130
131 vol = GetElementVolume(i);
132 GetAspectRatio(i, dmin, dmax);
133 return true;
134}
135
136bool ComponentFieldMap::GetElement(const size_t i, size_t& mat, bool& drift,
137 std::vector<size_t>& nodes) const {
138 if (i >= m_elements.size()) {
139 std::cerr << m_className << "::GetElement: Index out of range.\n";
140 return false;
141 }
142 const auto& element = m_elements[i];
143 mat = element.matmap;
144 drift = m_materials[mat].driftmedium;
145 nodes.resize(4);
146 for (size_t j = 0; j < 4; ++j) nodes[j] = element.emap[j];
147 return true;
148}
149
150bool ComponentFieldMap::GetNode(const size_t i, double& x, double& y,
151 double& z) const {
152 if (i >= m_nodes.size()) {
153 std::cerr << m_className << "::GetNode: Index out of range.\n";
154 return false;
155 }
156 x = m_nodes[i].x;
157 y = m_nodes[i].y;
158 z = m_nodes[i].z;
159 return true;
160}
161
163
164 // Find lowest epsilon and set drift medium flags.
165 const size_t nMaterials = m_materials.size();
166 double epsmin = -1;
167 size_t iepsmin = 0;
168 for (size_t i = 0; i < nMaterials; ++i) {
169 m_materials[i].driftmedium = false;
170 if (m_materials[i].eps < 0) continue;
171 // Check for eps == 0.
172 if (m_materials[i].eps == 0) {
173 std::cerr << m_className << "::SetDefaultDriftMedium:\n"
174 << " Material " << i << " has zero permittivity.\n";
175 m_materials[i].eps = -1.;
176 } else if (epsmin < 0. || epsmin > m_materials[i].eps) {
177 epsmin = m_materials[i].eps;
178 iepsmin = i;
179 }
180 }
181 if (epsmin < 0.) {
182 std::cerr << m_className << "::SetDefaultDriftMedium:\n"
183 << " Found no material with positive permittivity.\n";
184 return false;
185 }
186 m_materials[iepsmin].driftmedium = true;
187 return true;
188}
189
190int ComponentFieldMap::FindElement5(const double x, const double y,
191 double const z, double& t1, double& t2,
192 double& t3, double& t4, double jac[4][4],
193 double& det) {
194 // Check if bounding boxes of elements have been computed
195 if (!m_cacheElemBoundingBoxes) {
196 m_cacheElemBoundingBoxes = true;
197 }
198
199 // Tetra list in the block that contains the input 3D point.
200 std::vector<int> tetList;
201 if (m_useTetrahedralTree && m_octree) {
202 tetList = m_octree->GetElementsInBlock(Vec3(x, y, z));
203 }
204 // Backup
205 double jacbak[4][4], detbak = 1.;
206 double t1bak = 0., t2bak = 0., t3bak = 0., t4bak = 0.;
207 int imapbak = -1;
208
209 // Initial values.
210 t1 = t2 = t3 = t4 = 0;
211
212 // Check previously used element
213 if (m_lastElement > -1 && !m_checkMultipleElement) {
214 const Element& element = m_elements[m_lastElement];
215 if (element.degenerate) {
216 if (Coordinates3(x, y, z, t1, t2, t3, t4, jac, det, element) == 0) {
217 if (t1 >= 0 && t1 <= +1 && t2 >= 0 && t2 <= +1 && t3 >= 0 && t3 <= +1) {
218 return m_lastElement;
219 }
220 }
221 } else {
222 if (Coordinates5(x, y, z, t1, t2, t3, t4, jac, det, element) == 0) {
223 if (t1 >= -1 && t1 <= +1 && t2 >= -1 && t2 <= +1) return m_lastElement;
224 }
225 }
226 }
227
228 // Verify the count of volumes that contain the point.
229 int nfound = 0;
230 int imap = -1;
231
232 // Number of elements to scan.
233 // With tetra tree disabled, all elements are scanned.
234 const int numElemToSearch =
235 m_useTetrahedralTree ? tetList.size() : m_elements.size();
236 for (int i = 0; i < numElemToSearch; ++i) {
237 const int idxToElemList = m_useTetrahedralTree ? tetList[i] : i;
238 const Element& element = m_elements[idxToElemList];
239 if (x < element.bbMin[0] || x > element.bbMax[0] ||
240 y < element.bbMin[1] || y > element.bbMax[1] ||
241 z < element.bbMin[2] || z > element.bbMax[2])
242 continue;
243 if (element.degenerate) {
244 // Degenerate element
245 if (Coordinates3(x, y, z, t1, t2, t3, t4, jac, det, element) != 0) {
246 continue;
247 }
248 if (t1 < 0 || t1 > 1 || t2 < 0 || t2 > 1 || t3 < 0 || t3 > 1) continue;
249 ++nfound;
250 imap = idxToElemList;
251 m_lastElement = idxToElemList;
252 if (m_debug) {
253 std::cout << m_className << "::FindElement5:\n";
254 std::cout << " Found matching degenerate element " << idxToElemList
255 << ".\n";
256 }
257 if (!m_checkMultipleElement) return idxToElemList;
258 for (int j = 0; j < 4; ++j) {
259 for (int k = 0; k < 4; ++k) jacbak[j][k] = jac[j][k];
260 }
261 detbak = det;
262 t1bak = t1;
263 t2bak = t2;
264 t3bak = t3;
265 t4bak = t4;
266 imapbak = imap;
267 if (m_debug) {
268 PrintElement("FindElement5", x, y, z, t1, t2, t3, t4, element, 6);
269 }
270 } else {
271 // Non-degenerate element
272 if (Coordinates5(x, y, z, t1, t2, t3, t4, jac, det, element) != 0) {
273 continue;
274 }
275 if (t1 < -1 || t1 > 1 || t2 < -1 || t2 > 1) continue;
276 ++nfound;
277 imap = idxToElemList;
278 m_lastElement = idxToElemList;
279 if (m_debug) {
280 std::cout << m_className << "::FindElement5:\n";
281 std::cout << " Found matching non-degenerate element "
282 << idxToElemList << ".\n";
283 }
284 if (!m_checkMultipleElement) return idxToElemList;
285 for (int j = 0; j < 4; ++j) {
286 for (int k = 0; k < 4; ++k) jacbak[j][k] = jac[j][k];
287 }
288 detbak = det;
289 t1bak = t1;
290 t2bak = t2;
291 t3bak = t3;
292 t4bak = t4;
293 imapbak = imap;
294 if (m_debug) {
295 PrintElement("FindElement5", x, y, z, t1, t2, t3, t4, element, 8);
296 }
297 }
298 }
299
300 // In checking mode, verify the tetrahedron/triangle count.
301 if (m_checkMultipleElement) {
302 if (nfound < 1) {
303 if (m_debug) {
304 std::cout << m_className << "::FindElement5:\n";
305 std::cout << " No element matching point (" << x << ", " << y
306 << ") found.\n";
307 }
308 m_lastElement = -1;
309 return -1;
310 }
311 if (nfound > 1) {
312 std::cout << m_className << "::FindElement5:\n";
313 std::cout << " Found " << nfound << " elements matching point (" << x
314 << ", " << y << ").\n";
315 }
316 for (int j = 0; j < 4; ++j) {
317 for (int k = 0; k < 4; ++k) jac[j][k] = jacbak[j][k];
318 }
319 det = detbak;
320 t1 = t1bak;
321 t2 = t2bak;
322 t3 = t3bak;
323 t4 = t4bak;
324 imap = imapbak;
325 m_lastElement = imap;
326 return imap;
327 }
328
329 if (m_debug) {
330 std::cout << m_className << "::FindElement5:\n";
331 std::cout << " No element matching point (" << x << ", " << y
332 << ") found.\n";
333 }
334 return -1;
335}
336
337int ComponentFieldMap::FindElement13(const double x, const double y,
338 const double z, double& t1, double& t2,
339 double& t3, double& t4, double jac[4][4],
340 double& det) {
341 // Backup
342 double jacbak[4][4];
343 double detbak = 1.;
344 double t1bak = 0., t2bak = 0., t3bak = 0., t4bak = 0.;
345 int imapbak = -1;
346
347 // Initial values.
348 t1 = t2 = t3 = t4 = 0.;
349
350 // Check previously used element
351 if (m_lastElement > -1 && !m_checkMultipleElement) {
352 const Element& element = m_elements[m_lastElement];
353 if (Coordinates13(x, y, z, t1, t2, t3, t4, jac, det, element) == 0) {
354 if (t1 >= 0 && t1 <= +1 && t2 >= 0 && t2 <= +1 && t3 >= 0 && t3 <= +1 &&
355 t4 >= 0 && t4 <= +1) {
356 return m_lastElement;
357 }
358 }
359 }
360
361 // Tetra list in the block that contains the input 3D point.
362 std::vector<int> tetList;
363 if (m_useTetrahedralTree && m_octree) {
364 tetList = m_octree->GetElementsInBlock(Vec3(x, y, z));
365 }
366 // Number of elements to scan.
367 // With tetra tree disabled, all elements are scanned.
368 const int numElemToSearch =
369 m_useTetrahedralTree ? tetList.size() : m_elements.size();
370 // Verify the count of volumes that contain the point.
371 int nfound = 0;
372 int imap = -1;
373
374 // Scan all elements
375 for (int i = 0; i < numElemToSearch; i++) {
376 const int idxToElemList = m_useTetrahedralTree ? tetList[i] : i;
377 const Element& element = m_elements[idxToElemList];
378 if (x < element.bbMin[0] || x > element.bbMax[0] ||
379 y < element.bbMin[1] || y > element.bbMax[1] ||
380 z < element.bbMin[2] || z > element.bbMax[2])
381 continue;
382 if (Coordinates13(x, y, z, t1, t2, t3, t4, jac, det, element) != 0) {
383 continue;
384 }
385 if (t1 < 0 || t1 > 1 || t2 < 0 || t2 > 1 || t3 < 0 || t3 > 1 || t4 < 0 ||
386 t4 > 1) {
387 continue;
388 }
389 ++nfound;
390 imap = idxToElemList;
391 m_lastElement = idxToElemList;
392 if (m_debug) {
393 std::cout << m_className << "::FindElement13:\n";
394 std::cout << " Found matching element " << i << ".\n";
395 }
396 if (!m_checkMultipleElement) return idxToElemList;
397 for (int j = 0; j < 4; ++j) {
398 for (int k = 0; k < 4; ++k) jacbak[j][k] = jac[j][k];
399 }
400 detbak = det;
401 t1bak = t1;
402 t2bak = t2;
403 t3bak = t3;
404 t4bak = t4;
405 imapbak = imap;
406 if (m_debug) {
407 PrintElement("FindElement13", x, y, z, t1, t2, t3, t4, element, 10);
408 }
409 }
410
411 // In checking mode, verify the tetrahedron/triangle count.
412 if (m_checkMultipleElement) {
413 if (nfound < 1) {
414 if (m_debug) {
415 std::cout << m_className << "::FindElement13:\n";
416 std::cout << " No element matching point (" << x << ", " << y << ", "
417 << z << ") found.\n";
418 }
419 m_lastElement = -1;
420 return -1;
421 }
422 if (nfound > 1) {
423 std::cerr << m_className << "::FindElement13:\n";
424 std::cerr << " Found << " << nfound << " elements matching point ("
425 << x << ", " << y << ", " << z << ").\n";
426 }
427 for (int j = 0; j < 4; ++j) {
428 for (int k = 0; k < 4; ++k) jac[j][k] = jacbak[j][k];
429 }
430 det = detbak;
431 t1 = t1bak;
432 t2 = t2bak;
433 t3 = t3bak;
434 t4 = t4bak;
435 imap = imapbak;
436 m_lastElement = imap;
437 return imap;
438 }
439
440 if (m_debug) {
441 std::cout << m_className << "::FindElement13:\n";
442 std::cout << " No element matching point (" << x << ", " << y << ", "
443 << z << ") found.\n";
444 }
445 return -1;
446}
447
448int ComponentFieldMap::FindElementCube(const double x, const double y,
449 const double z, double& t1, double& t2,
450 double& t3, TMatrixD*& jac,
451 std::vector<TMatrixD*>& dN) {
452 int imap = -1;
453 if (m_lastElement >= 0) {
454 const Element& element = m_elements[m_lastElement];
455 const Node& n3 = m_nodes[element.emap[3]];
456 if (x >= n3.x && y >= n3.y && z >= n3.z) {
457 const Node& n0 = m_nodes[element.emap[0]];
458 const Node& n2 = m_nodes[element.emap[2]];
459 const Node& n7 = m_nodes[element.emap[7]];
460 if (x < n0.x && y < n2.y && z < n7.z) {
461 imap = m_lastElement;
462 }
463 }
464 }
465
466 // Default element loop
467 if (imap == -1) {
468 const size_t nElements = m_elements.size();
469 for (size_t i = 0; i < nElements; ++i) {
470 const Element& element = m_elements[i];
471 const Node& n3 = m_nodes[element.emap[3]];
472 if (x < n3.x || y < n3.y || z < n3.z) continue;
473 const Node& n0 = m_nodes[element.emap[0]];
474 const Node& n2 = m_nodes[element.emap[2]];
475 const Node& n7 = m_nodes[element.emap[7]];
476 if (x < n0.x && y < n2.y && z < n7.z) {
477 imap = i;
478 break;
479 }
480 }
481 }
482
483 if (imap < 0) {
484 if (m_debug) {
485 std::cout << m_className << "::FindElementCube:\n";
486 std::cout << " Point (" << x << "," << y << "," << z
487 << ") not in the mesh, it is background or PEC.\n";
488 const Node& first0 = m_nodes[m_elements.front().emap[0]];
489 const Node& first2 = m_nodes[m_elements.front().emap[2]];
490 const Node& first3 = m_nodes[m_elements.front().emap[3]];
491 const Node& first7 = m_nodes[m_elements.front().emap[7]];
492 std::cout << " First node (" << first3.x << "," << first3.y << ","
493 << first3.z << ") in the mesh.\n";
494 std::cout << " dx= " << (first0.x - first3.x)
495 << ", dy= " << (first2.y - first3.y)
496 << ", dz= " << (first7.z - first3.z) << "\n";
497 const Node& last0 = m_nodes[m_elements.back().emap[0]];
498 const Node& last2 = m_nodes[m_elements.back().emap[2]];
499 const Node& last3 = m_nodes[m_elements.back().emap[3]];
500 const Node& last5 = m_nodes[m_elements.back().emap[5]];
501 const Node& last7 = m_nodes[m_elements.back().emap[7]];
502 std::cout << " Last node (" << last5.x << "," << last5.y << ","
503 << last5.z << ") in the mesh.\n";
504 std::cout << " dx= " << (last0.x - last3.x)
505 << ", dy= " << (last2.y - last3.y)
506 << ", dz= " << (last7.z - last3.z) << "\n";
507 }
508 return -1;
509 }
510 CoordinatesCube(x, y, z, t1, t2, t3, jac, dN, m_elements[imap]);
511 if (m_debug) {
512 PrintElement("FindElementCube", x, y, z, t1, t2, t3, 0., m_elements[imap],
513 8);
514 }
515 return imap;
516}
517
518void ComponentFieldMap::Jacobian3(const Element& element, const double u,
519 const double v, const double w, double& det,
520 double jac[4][4]) const {
521 const Node& n0 = m_nodes[element.emap[0]];
522 const Node& n1 = m_nodes[element.emap[1]];
523 const Node& n2 = m_nodes[element.emap[2]];
524 const Node& n3 = m_nodes[element.emap[3]];
525 const Node& n4 = m_nodes[element.emap[4]];
526 const Node& n5 = m_nodes[element.emap[5]];
527
528 // Shorthands.
529 const double fouru = 4 * u;
530 const double fourv = 4 * v;
531 const double fourw = 4 * w;
532
533 const double ax = (-1 + fourv) * n1.x + fouru * n3.x + fourw * n5.x;
534 const double ay = (-1 + fourv) * n1.y + fouru * n3.y + fourw * n5.y;
535 const double bx = (-1 + fourw) * n2.x + fouru * n4.x + fourv * n5.x;
536 const double by = (-1 + fourw) * n2.y + fouru * n4.y + fourv * n5.y;
537 const double cx = (-1 + fouru) * n0.x + fourv * n3.x + fourw * n4.x;
538 const double cy = (-1 + fouru) * n0.y + fourv * n3.y + fourw * n4.y;
539 // Determinant of the quadratic triangular Jacobian
540 det = -(ax - bx) * cy - (cx - ax) * by + (cx - bx) * ay;
541
542 // Terms of the quadratic triangular Jacobian
543 jac[0][0] = ax * by - bx * ay;
544 jac[0][1] = ay - by;
545 jac[0][2] = bx - ax;
546 jac[1][0] = bx * cy - cx * by;
547 jac[1][1] = by - cy;
548 jac[1][2] = cx - bx;
549 jac[2][0] = -ax * cy + cx * ay;
550 jac[2][1] = cy - ay;
551 jac[2][2] = ax - cx;
552}
553
554void ComponentFieldMap::Jacobian5(const Element& element, const double u,
555 const double v, double& det,
556 double jac[4][4]) const {
557 const Node& n0 = m_nodes[element.emap[0]];
558 const Node& n1 = m_nodes[element.emap[1]];
559 const Node& n2 = m_nodes[element.emap[2]];
560 const Node& n3 = m_nodes[element.emap[3]];
561 const Node& n4 = m_nodes[element.emap[4]];
562 const Node& n5 = m_nodes[element.emap[5]];
563 const Node& n6 = m_nodes[element.emap[6]];
564 const Node& n7 = m_nodes[element.emap[7]];
565 const double u2 = u * u;
566 const double v2 = v * v;
567 const double twou = 2 * u;
568 const double twov = 2 * v;
569 const double two0x = 2 * n0.x;
570 const double two0y = 2 * n0.y;
571 const double two1x = 2 * n1.x;
572 const double two1y = 2 * n1.y;
573 const double two2x = 2 * n2.x;
574 const double two2y = 2 * n2.y;
575 const double two3x = 2 * n3.x;
576 const double two3y = 2 * n3.y;
577 const double two4x = 2 * n4.x;
578 const double two4y = 2 * n4.y;
579 const double two5x = 2 * n5.x;
580 const double two5y = 2 * n5.y;
581 const double two6x = 2 * n6.x;
582 const double two6y = 2 * n6.y;
583 const double two7x = 2 * n7.x;
584 const double two7y = 2 * n7.y;
585 // Determinant of the quadrilateral serendipity Jacobian
586 det =
587 (-twou * u2 *
588 ((n2.x + n3.x - two6x) * (n0.y + n1.y - two4y) -
589 (n0.x + n1.x - two4x) * (n2.y + n3.y - two6y)) +
590 twov * v2 *
591 (-((n0.x + n3.x - two7x) * (n1.y + n2.y - two5y)) +
592 (n1.x + n2.x - two5x) * (n0.y + n3.y - two7y)) +
593 2 * (-((n5.x - n7.x) * (n4.y - n6.y)) + (n4.x - n6.x) * (n5.y - n7.y)) +
594 v * (-(n6.x * n0.y) - two7x * n0.y + n6.x * n1.y - two7x * n1.y -
595 n6.x * n2.y - two7x * n2.y + n4.x * (n0.y - n1.y + n2.y - n3.y) +
596 n6.x * n3.y - two7x * n3.y - n0.x * n4.y + n1.x * n4.y -
597 n2.x * n4.y + n3.x * n4.y - two0x * n5.y - two1x * n5.y -
598 two2x * n5.y - two3x * n5.y + 8 * n7.x * n5.y + n0.x * n6.y -
599 n1.x * n6.y + n2.x * n6.y - n3.x * n6.y +
600 two5x * (n0.y + n1.y + n2.y + n3.y - 4 * n7.y) +
601 2 * (n0.x + n1.x + n2.x + n3.x) * n7.y) +
602 v2 * (-(n4.x * n0.y) + two5x * n0.y + n6.x * n0.y + two7x * n0.y +
603 n4.x * n1.y - two5x * n1.y - n6.x * n1.y - two7x * n1.y +
604 n4.x * n2.y + two5x * n2.y - n6.x * n2.y + two7x * n2.y -
605 n4.x * n3.y - two5x * n3.y + n6.x * n3.y - two7x * n3.y +
606 two2x * (n1.y + n3.y) - n2.x * n4.y + two5x * n4.y - two7x * n4.y -
607 two2x * n5.y - two4x * n5.y + two6x * n5.y + n2.x * n6.y -
608 two5x * n6.y + two7x * n6.y +
609 n0.x * (two1y + two3y + n4.y - two5y - n6.y - two7y) -
610 2 * (n2.x - n4.x + n6.x) * n7.y +
611 n3.x * (-two0y - two2y + n4.y + two5y - n6.y + two7y) +
612 n1.x * (-two0y - two2y - n4.y + two5y + n6.y + two7y)) +
613 u * (n5.x * n0.y - two6x * n0.y - n7.x * n0.y - n5.x * n1.y -
614 two6x * n1.y + n7.x * n1.y + n5.x * n2.y - two6x * n2.y -
615 n7.x * n2.y - n5.x * n3.y - two6x * n3.y + n7.x * n3.y -
616 two1x * n4.y - two2x * n4.y - two3x * n4.y + 8 * n6.x * n4.y +
617 n1.x * n5.y - n2.x * n5.y + n3.x * n5.y +
618 two4x * (n0.y + n1.y + n2.y + n3.y - 4 * n6.y) + two1x * n6.y +
619 two2x * n6.y + two3x * n6.y - (n1.x - n2.x + n3.x) * n7.y +
620 n0.x * (-two4y - n5.y + two6y + n7.y) +
621 v2 * (4 * n4.x * n0.y - 3 * n5.x * n0.y - 4 * n6.x * n0.y -
622 5 * n7.x * n0.y + 4 * n4.x * n1.y - 5 * n5.x * n1.y -
623 4 * n6.x * n1.y - 3 * n7.x * n1.y + 4 * n4.x * n2.y +
624 5 * n5.x * n2.y - 4 * n6.x * n2.y + 3 * n7.x * n2.y +
625 4 * n4.x * n3.y + 3 * n5.x * n3.y - 4 * n6.x * n3.y +
626 5 * n7.x * n3.y + 8 * n5.x * n4.y + 8 * n7.x * n4.y -
627 8 * n4.x * n5.y + 8 * n6.x * n5.y - 8 * n5.x * n6.y -
628 8 * n7.x * n6.y +
629 n3.x * (5 * n0.y + 3 * n1.y - 4 * n4.y - 3 * n5.y + 4 * n6.y -
630 5 * n7.y) +
631 n2.x * (3 * n0.y + 5 * n1.y - 4 * n4.y - 5 * n5.y + 4 * n6.y -
632 3 * n7.y) -
633 8 * n4.x * n7.y + 8 * n6.x * n7.y +
634 n1.x * (-5 * n2.y - 3 * n3.y - 4 * n4.y + 5 * n5.y +
635 4 * n6.y + 3 * n7.y) +
636 n0.x * (-3 * n2.y - 5 * n3.y - 4 * n4.y + 3 * n5.y +
637 4 * n6.y + 5 * n7.y)) -
638 twov * (n6.x * n0.y - 3 * n7.x * n0.y + n6.x * n1.y - n7.x * n1.y +
639 3 * n6.x * n2.y - n7.x * n2.y + 3 * n6.x * n3.y -
640 3 * n7.x * n3.y - 3 * n0.x * n4.y - 3 * n1.x * n4.y -
641 n2.x * n4.y - n3.x * n4.y + 4 * n7.x * n4.y + n0.x * n5.y +
642 3 * n1.x * n5.y + 3 * n2.x * n5.y + n3.x * n5.y -
643 4 * n6.x * n5.y - n0.x * n6.y - n1.x * n6.y -
644 3 * n2.x * n6.y - 3 * n3.x * n6.y + 4 * n7.x * n6.y -
645 n5.x * (n0.y + 3 * n1.y + 3 * n2.y + n3.y -
646 4 * (n4.y + n6.y)) +
647 (3 * n0.x + n1.x + n2.x + 3 * n3.x - 4 * n6.x) * n7.y +
648 n4.x * (3 * n0.y + 3 * n1.y + n2.y + n3.y -
649 4 * (n5.y + n7.y)))) +
650 u2 *
651 (two3x * n0.y - two4x * n0.y - n5.x * n0.y - two6x * n0.y +
652 n7.x * n0.y - two0x * n1.y + two4x * n1.y - n5.x * n1.y +
653 two6x * n1.y + n7.x * n1.y + two3x * n2.y - two4x * n2.y +
654 n5.x * n2.y - two6x * n2.y - n7.x * n2.y + two4x * n3.y +
655 n5.x * n3.y + two6x * n3.y - n7.x * n3.y - two3x * n4.y +
656 two5x * n4.y - two7x * n4.y - n3.x * n5.y - two4x * n5.y +
657 two6x * n5.y - two3x * n6.y - two5x * n6.y + two7x * n6.y +
658 n0.x * (-two3y + two4y + n5.y + two6y - n7.y) +
659 (n3.x + two4x - two6x) * n7.y +
660 n2.x * (-two1y - two3y + two4y - n5.y + two6y + n7.y) -
661 3 * v2 *
662 (n5.x * n0.y - n6.x * n0.y - n7.x * n0.y + n5.x * n1.y +
663 n6.x * n1.y - n7.x * n1.y - n5.x * n2.y + n6.x * n2.y +
664 n7.x * n2.y - n5.x * n3.y - n6.x * n3.y + n7.x * n3.y -
665 two5x * n4.y + two7x * n4.y - two6x * n5.y + two5x * n6.y -
666 two7x * n6.y +
667 n4.x * (n0.y - n1.y - n2.y + n3.y + two5y - two7y) +
668 n3.x * (n0.y - n2.y - n4.y + n5.y + n6.y - n7.y) +
669 two6x * n7.y +
670 (n0.x - n2.x) * (n1.y - n3.y - n4.y - n5.y + n6.y + n7.y)) +
671 v * (4 * n5.x * n0.y + 3 * n6.x * n0.y - 4 * n7.x * n0.y +
672 4 * n5.x * n1.y - 3 * n6.x * n1.y - 4 * n7.x * n1.y +
673 4 * n5.x * n2.y - 5 * n6.x * n2.y - 4 * n7.x * n2.y +
674 4 * n5.x * n3.y + 5 * n6.x * n3.y - 4 * n7.x * n3.y -
675 8 * n5.x * n4.y + 8 * n7.x * n4.y + 8 * n6.x * n5.y -
676 8 * n5.x * n6.y + 8 * n7.x * n6.y +
677 n4.x * (5 * n0.y - 5 * n1.y - 3 * n2.y + 3 * n3.y + 8 * n5.y -
678 8 * n7.y) -
679 8 * n6.x * n7.y +
680 n3.x * (3 * n1.y + 5 * n2.y - 3 * n4.y - 4 * n5.y - 5 * n6.y +
681 4 * n7.y) +
682 n0.x * (5 * n1.y + 3 * n2.y - 5 * n4.y - 4 * n5.y - 3 * n6.y +
683 4 * n7.y) +
684 n2.x * (-3 * n0.y - 5 * n3.y + 3 * n4.y - 4 * n5.y + 5 * n6.y +
685 4 * n7.y)) +
686 n1.x * ((-1 + v) * (-2 + 3 * v) * n0.y + two2y - two4y + n5.y -
687 two6y - n7.y +
688 v * (-3 * n3.y + 5 * n4.y - 4 * n5.y + 3 * n6.y + 4 * n7.y -
689 3 * v * (n2.y + n4.y - n5.y - n6.y + n7.y))))) /
690 8;
691 // Jacobian terms
692 jac[0][0] =
693 (u2 * (-n0.y - n1.y + n2.y + n3.y + two4y - two6y) +
694 2 * (-n4.y + n6.y + v * (n0.y + n1.y + n2.y + n3.y - two5y - two7y)) +
695 u * (n0.y - twov * n0.y - n1.y + twov * n1.y + n2.y + twov * n2.y -
696 n3.y - twov * n3.y - twov * two5y + twov * two7y)) /
697 4;
698 jac[0][1] =
699 (u2 * (n0.x + n1.x - n2.x - n3.x - two4x + two6x) -
700 2 * (-n4.x + n6.x + v * (n0.x + n1.x + n2.x + n3.x - two5x - two7x)) +
701 u * ((-1 + twov) * n0.x + n1.x - twov * n1.x - n2.x - twov * n2.x +
702 n3.x + twov * n3.x + twov * two5x - twov * two7x)) /
703 4;
704 jac[1][0] =
705 (v * (-n0.y + n1.y - n2.y + n3.y) - two5y +
706 twou * ((-1 + v) * n0.y + (-1 + v) * n1.y - n2.y - v * n2.y - n3.y -
707 v * n3.y + two4y - twov * n4.y + two6y + twov * n6.y) +
708 v2 * (n0.y - n1.y - n2.y + n3.y + two5y - two7y) + two7y) /
709 4;
710 jac[1][1] =
711 (v * (n0.x - n1.x + n2.x - n3.x) +
712 twou * (n0.x - v * n0.x + n1.x - v * n1.x + n2.x + v * n2.x + n3.x +
713 v * n3.x - two4x + twov * n4.x - two6x - twov * n6.x) +
714 two5x - two7x + v2 * (-n0.x + n1.x + n2.x - n3.x - two5x + two7x)) /
715 4;
716}
717
718void ComponentFieldMap::Jacobian13(const Element& element, const double t,
719 const double u, const double v,
720 const double w, double& det,
721 double jac[4][4]) const {
722 const Node& n0 = m_nodes[element.emap[0]];
723 const Node& n1 = m_nodes[element.emap[1]];
724 const Node& n2 = m_nodes[element.emap[2]];
725 const Node& n3 = m_nodes[element.emap[3]];
726 const Node& n4 = m_nodes[element.emap[4]];
727 const Node& n5 = m_nodes[element.emap[5]];
728 const Node& n6 = m_nodes[element.emap[6]];
729 const Node& n7 = m_nodes[element.emap[7]];
730 const Node& n8 = m_nodes[element.emap[8]];
731 const Node& n9 = m_nodes[element.emap[9]];
732
733 // Shorthands.
734 const double fourt = 4 * t;
735 const double fouru = 4 * u;
736 const double fourv = 4 * v;
737 const double fourw = 4 * w;
738
739 const double x0 = (-1 + fourt) * n0.x;
740 const double y0 = (-1 + fourt) * n0.y;
741 const double z0 = (-1 + fourt) * n0.z;
742 const double xp = fouru * n4.x + fourv * n5.x + fourw * n6.x;
743 const double yp = fouru * n4.y + fourv * n5.y + fourw * n6.y;
744 const double zp = fouru * n4.z + fourv * n5.z + fourw * n6.z;
745
746 const double tx = x0 + xp;
747 const double ty = y0 + yp;
748 const double tz = z0 + zp;
749
750 const double x1 = (-1 + fouru) * n1.x;
751 const double y1 = (-1 + fouru) * n1.y;
752 const double z1 = (-1 + fouru) * n1.z;
753 const double xq = fourt * n4.x + fourv * n7.x + fourw * n8.x;
754 const double yq = fourt * n4.y + fourv * n7.y + fourw * n8.y;
755 const double zq = fourt * n4.z + fourv * n7.z + fourw * n8.z;
756
757 const double ux = x1 + xq;
758 const double uy = y1 + yq;
759 const double uz = z1 + zq;
760
761 const double x2 = (-1 + fourv) * n2.x;
762 const double y2 = (-1 + fourv) * n2.y;
763 const double z2 = (-1 + fourv) * n2.z;
764 const double xr = fourt * n5.x + fouru * n7.x + fourw * n9.x;
765 const double yr = fourt * n5.y + fouru * n7.y + fourw * n9.y;
766 const double zr = fourt * n5.z + fouru * n7.z + fourw * n9.z;
767
768 const double vx = x2 + xr;
769 const double vy = y2 + yr;
770 const double vz = z2 + zr;
771
772 const double x3 = (-1 + fourw) * n3.x;
773 const double y3 = (-1 + fourw) * n3.y;
774 const double z3 = (-1 + fourw) * n3.z;
775 const double xs = fourt * n6.x + fouru * n8.x + fourv * n9.x;
776 const double ys = fourt * n6.y + fouru * n8.y + fourv * n9.y;
777 const double zs = fourt * n6.z + fouru * n8.z + fourv * n9.z;
778
779 const double wx = x3 + xs;
780 const double wy = y3 + ys;
781 const double wz = z3 + zs;
782
783 const double ax = x1 - x3 + xq - xs;
784 const double ay = y1 - y3 + yq - ys;
785
786 const double bx = x1 - x2 + xq - xr;
787 const double by = y1 - y2 + yq - yr;
788
789 const double cx = x2 - x3 + xr - xs;
790 const double cy = y2 - y3 + yr - ys;
791
792 const double dx = x0 - x3 + xp - xs;
793 const double dy = y0 - y3 + yp - ys;
794
795 const double ex = x0 - x2 + xp - xr;
796 const double ey = y0 - y2 + yp - yr;
797
798 const double fx = x0 - x1 + xp - xq;
799 const double fy = y0 - y1 + yp - yq;
800
801 // Determinant of the quadrilateral serendipity Jacobian
802 det = (-ax * vy + bx * wy + cx * uy) * tz -
803 (-ax * ty - fx * wy + dx * uy) * vz +
804 (-bx * ty - fx * vy + ex * uy) * wz +
805 (-cx * ty + dx * vy - ex * wy) * uz;
806
807 const double tu = tx * uy - ux * ty;
808 const double tv = tx * vy - vx * ty;
809 const double tw = tx * wy - wx * ty;
810 const double uv = ux * vy - vx * uy;
811 const double uw = ux * wy - wx * uy;
812 const double vw = vx * wy - wx * vy;
813
814 jac[0][0] = -uw * vz + uv * wz + vw * uz;
815 jac[1][0] = -vw * tz + tw * vz - tv * wz;
816 jac[2][0] = uw * tz + tu * wz - tw * uz;
817 jac[3][0] = -uv * tz - tu * vz + tv * uz;
818
819 jac[0][1] = -ay * vz + by * wz + cy * uz;
820 jac[0][2] = ax * vz - bx * wz - cx * uz;
821 jac[0][3] = -ax * vy + bx * wy + cx * uy;
822
823 jac[1][1] = -cy * tz + dy * vz - ey * wz;
824 jac[1][2] = cx * tz - dx * vz + ex * wz;
825 jac[1][3] = -cx * ty + dx * vy - ex * wy;
826
827 jac[2][1] = ay * tz + fy * wz - dy * uz;
828 jac[2][2] = -ax * tz - fx * wz + dx * uz;
829 jac[2][3] = ax * ty + fx * wy - dx * uy;
830
831 jac[3][1] = -by * tz - fy * vz + ey * uz;
832 jac[3][2] = bx * tz + fx * vz - ex * uz;
833 jac[3][3] = -bx * ty - fx * vy + ex * uy;
834}
835
836void ComponentFieldMap::JacobianCube(const Element& element, const double t1,
837 const double t2, const double t3,
838 TMatrixD*& jac,
839 std::vector<TMatrixD*>& dN) const {
840 if (!jac) {
841 std::cerr << m_className << "::JacobianCube:\n";
842 std::cerr << " Pointer to Jacobian matrix is empty!\n";
843 return;
844 }
845 dN.clear();
846
847 // Here the partial derivatives of the 8 shaping functions are calculated
848 double N1[3] = {-1 * (1 - t2) * (1 - t3), (1 - t1) * -1 * (1 - t3),
849 (1 - t1) * (1 - t2) * -1};
850 double N2[3] = {+1 * (1 - t2) * (1 - t3), (1 + t1) * -1 * (1 - t3),
851 (1 + t1) * (1 - t2) * -1};
852 double N3[3] = {+1 * (1 + t2) * (1 - t3), (1 + t1) * +1 * (1 - t3),
853 (1 + t1) * (1 + t2) * -1};
854 double N4[3] = {-1 * (1 + t2) * (1 - t3), (1 - t1) * +1 * (1 - t3),
855 (1 - t1) * (1 + t2) * -1};
856 double N5[3] = {-1 * (1 - t2) * (1 + t3), (1 - t1) * -1 * (1 + t3),
857 (1 - t1) * (1 - t2) * +1};
858 double N6[3] = {+1 * (1 - t2) * (1 + t3), (1 + t1) * -1 * (1 + t3),
859 (1 + t1) * (1 - t2) * +1};
860 double N7[3] = {+1 * (1 + t2) * (1 + t3), (1 + t1) * +1 * (1 + t3),
861 (1 + t1) * (1 + t2) * +1};
862 double N8[3] = {-1 * (1 + t2) * (1 + t3), (1 - t1) * +1 * (1 + t3),
863 (1 - t1) * (1 + t2) * +1};
864 // Partial derivatives are stored in dN
865 TMatrixD* m_N1 = new TMatrixD(3, 1, N1);
866 *m_N1 = (1. / 8. * (*m_N1));
867 dN.push_back(m_N1);
868 TMatrixD* m_N2 = new TMatrixD(3, 1, N2);
869 *m_N2 = (1. / 8. * (*m_N2));
870 dN.push_back(m_N2);
871 TMatrixD* m_N3 = new TMatrixD(3, 1, N3);
872 *m_N3 = (1. / 8. * (*m_N3));
873 dN.push_back(m_N3);
874 TMatrixD* m_N4 = new TMatrixD(3, 1, N4);
875 *m_N4 = (1. / 8. * (*m_N4));
876 dN.push_back(m_N4);
877 TMatrixD* m_N5 = new TMatrixD(3, 1, N5);
878 *m_N5 = (1. / 8. * (*m_N5));
879 dN.push_back(m_N5);
880 TMatrixD* m_N6 = new TMatrixD(3, 1, N6);
881 *m_N6 = (1. / 8. * (*m_N6));
882 dN.push_back(m_N6);
883 TMatrixD* m_N7 = new TMatrixD(3, 1, N7);
884 *m_N7 = (1. / 8. * (*m_N7));
885 dN.push_back(m_N7);
886 TMatrixD* m_N8 = new TMatrixD(3, 1, N8);
887 *m_N8 = (1. / 8. * (*m_N8));
888 dN.push_back(m_N8);
889 // Calculation of the jacobian using dN
890 for (int j = 0; j < 8; ++j) {
891 const Node& node = m_nodes[element.emap[j]];
892 (*jac)(0, 0) += node.x * ((*dN.at(j))(0, 0));
893 (*jac)(0, 1) += node.y * ((*dN.at(j))(0, 0));
894 (*jac)(0, 2) += node.z * ((*dN.at(j))(0, 0));
895 (*jac)(1, 0) += node.x * ((*dN.at(j))(1, 0));
896 (*jac)(1, 1) += node.y * ((*dN.at(j))(1, 0));
897 (*jac)(1, 2) += node.z * ((*dN.at(j))(1, 0));
898 (*jac)(2, 0) += node.x * ((*dN.at(j))(2, 0));
899 (*jac)(2, 1) += node.y * ((*dN.at(j))(2, 0));
900 (*jac)(2, 2) += node.z * ((*dN.at(j))(2, 0));
901 }
902
903 // compute determinant
904 if (m_debug) {
905 std::cout << m_className << "::JacobianCube:" << std::endl;
906 std::cout << " Det.: " << jac->Determinant() << std::endl;
907 std::cout << " Jacobian matrix.: " << std::endl;
908 jac->Print("%11.10g");
909 std::cout << " Hexahedral coordinates (t, u, v) = (" << t1 << "," << t2
910 << "," << t3 << ")" << std::endl;
911 std::cout << " Node xyzV" << std::endl;
912 for (int j = 0; j < 8; ++j) {
913 const Node& node = m_nodes[element.emap[j]];
914 std::cout << " " << element.emap[j] << " " << node.x
915 << " " << node.y << " " << node.z << " "
916 << node.v << std::endl;
917 }
918 }
919}
920
921int ComponentFieldMap::Coordinates3(const double x, const double y,
922 const double z, double& t1, double& t2,
923 double& t3, double& t4, double jac[4][4],
924 double& det, const Element& element) const {
925 if (m_debug) {
926 std::cout << m_className << "::Coordinates3:\n";
927 std::cout << " Point (" << x << ", " << y << ", " << z << ")\n";
928 }
929
930 // Provisional values
931 t1 = t2 = t3 = t4 = 0;
932
933 // Make a first order approximation, using the linear triangle.
934 const Node& n0 = m_nodes[element.emap[0]];
935 const Node& n1 = m_nodes[element.emap[1]];
936 const Node& n2 = m_nodes[element.emap[2]];
937 const double d1 =
938 (n0.x - n1.x) * (n2.y - n1.y) - (n2.x - n1.x) * (n0.y - n1.y);
939 const double d2 =
940 (n1.x - n2.x) * (n0.y - n2.y) - (n0.x - n2.x) * (n1.y - n2.y);
941 const double d3 =
942 (n2.x - n0.x) * (n1.y - n0.y) - (n1.x - n0.x) * (n2.y - n0.y);
943 if (d1 == 0 || d2 == 0 || d3 == 0) {
944 std::cerr << m_className << "::Coordinates3:\n";
945 std::cerr << " Calculation of linear coordinates failed; abandoned.\n";
946 return 1;
947 }
948 t1 = ((x - n1.x) * (n2.y - n1.y) - (y - n1.y) * (n2.x - n1.x)) / d1;
949 t2 = ((x - n2.x) * (n0.y - n2.y) - (y - n2.y) * (n0.x - n2.x)) / d2;
950 t3 = ((x - n0.x) * (n1.y - n0.y) - (y - n0.y) * (n1.x - n0.x)) / d3;
951
952 const Node& n3 = m_nodes[element.emap[3]];
953 const Node& n4 = m_nodes[element.emap[4]];
954 const Node& n5 = m_nodes[element.emap[5]];
955
956 // Start iterative refinement.
957 double td1 = t1, td2 = t2, td3 = t3;
958 bool converged = false;
959 for (int iter = 0; iter < 10; iter++) {
960 if (m_debug) {
961 std::cout << m_className << "::Coordinates3:\n";
962 std::cout << " Iteration " << iter << ": (u, v, w) = (" << td1
963 << ", " << td2 << ", " << td3 << "), sum = " << td1 + td2 + td3
964 << "\n";
965 }
966 // Evaluate the shape functions.
967 const double f0 = td1 * (2 * td1 - 1);
968 const double f1 = td2 * (2 * td2 - 1);
969 const double f2 = td3 * (2 * td3 - 1);
970 const double f3 = 4 * td1 * td2;
971 const double f4 = 4 * td1 * td3;
972 const double f5 = 4 * td2 * td3;
973 // Re-compute the (x,y,z) position for this coordinate.
974 const double xr =
975 n0.x * f0 + n1.x * f1 + n2.x * f2 + n3.x * f3 + n4.x * f4 + n5.x * f5;
976 const double yr =
977 n0.y * f0 + n1.y * f1 + n2.y * f2 + n3.y * f3 + n4.y * f4 + n5.y * f5;
978 const double sr = td1 + td2 + td3;
979 // Compute the Jacobian.
980 Jacobian3(element, td1, td2, td3, det, jac);
981 // Compute the difference vector.
982 const double diff[3] = {1 - sr, x - xr, y - yr};
983 // Update the estimate.
984 const double invdet = 1. / det;
985 double corr[3] = {0., 0., 0.};
986 for (size_t l = 0; l < 3; l++) {
987 for (size_t k = 0; k < 3; k++) {
988 corr[l] += jac[l][k] * diff[k];
989 }
990 corr[l] *= invdet;
991 }
992 // Debugging
993 if (m_debug) {
994 std::cout << m_className << "::Coordinates3:\n";
995 std::cout << " Difference vector: (1, x, y) = (" << diff[0] << ", "
996 << diff[1] << ", " << diff[2] << ").\n";
997 std::cout << " Correction vector: (u, v, w) = (" << corr[0] << ", "
998 << corr[1] << ", " << corr[2] << ").\n";
999 }
1000 // Update the vector.
1001 td1 += corr[0];
1002 td2 += corr[1];
1003 td3 += corr[2];
1004 // Check for convergence.
1005 constexpr double tol = 1.e-5;
1006 if (fabs(corr[0]) < tol && fabs(corr[1]) < tol && fabs(corr[2]) < tol) {
1007 if (m_debug) {
1008 std::cout << m_className << "::Coordinates3: Convergence reached.";
1009 }
1010 converged = true;
1011 break;
1012 }
1013 }
1014 // No convergence reached
1015 if (!converged) {
1016 const double xmin = std::min({n0.x, n1.x, n2.x});
1017 const double xmax = std::max({n0.x, n1.x, n2.x});
1018 const double ymin = std::min({n0.y, n1.y, n2.y});
1019 const double ymax = std::max({n0.y, n1.y, n2.y});
1020 if (x >= xmin && x <= xmax && y >= ymin && y <= ymax) {
1022 std::cout << m_className << "::Coordinates3:\n"
1023 << " No convergence achieved "
1024 << "when refining internal isoparametric coordinates\n"
1025 << " at position (" << x << ", " << y << ").\n";
1026 }
1027 t1 = t2 = t3 = t4 = 0;
1028 return 1;
1029 }
1030 }
1031
1032 // Convergence reached.
1033 t1 = td1;
1034 t2 = td2;
1035 t3 = td3;
1036 t4 = 0;
1037 if (m_debug) {
1038 std::cout << m_className << "::Coordinates3:\n";
1039 std::cout << " Convergence reached at (t1, t2, t3) = (" << t1 << ", "
1040 << t2 << ", " << t3 << ").\n";
1041 // For debugging purposes, show position
1042 const double f0 = td1 * (2 * td1 - 1);
1043 const double f1 = td2 * (2 * td2 - 1);
1044 const double f2 = td3 * (2 * td3 - 1);
1045 const double f3 = 4 * td1 * td2;
1046 const double f4 = 4 * td1 * td3;
1047 const double f5 = 4 * td2 * td3;
1048 const double xr =
1049 n0.x * f0 + n1.x * f1 + n2.x * f2 + n3.x * f3 + n4.x * f4 + n5.x * f5;
1050 const double yr =
1051 n0.y * f0 + n1.y * f1 + n2.y * f2 + n3.y * f3 + n4.y * f4 + n5.y * f5;
1052 const double sr = td1 + td2 + td3;
1053 std::cout << m_className << "::Coordinates3:\n";
1054 std::cout << " Position requested: (" << x << ", " << y << ")\n";
1055 std::cout << " Reconstructed: (" << xr << ", " << yr << ")\n";
1056 std::cout << " Difference: (" << x - xr << ", " << y - yr
1057 << ")\n";
1058 std::cout << " Checksum - 1: " << sr - 1 << "\n";
1059 }
1060
1061 // Success
1062 return 0;
1063}
1064
1065int ComponentFieldMap::Coordinates4(const double x, const double y,
1066 const double z, double& t1, double& t2,
1067 double& t3, double& t4, double& det,
1068 const Element& element) const {
1069 // Debugging
1070 if (m_debug) {
1071 std::cout << m_className << "::Coordinates4:\n";
1072 std::cout << " Point (" << x << ", " << y << ", " << z << ")\n";
1073 }
1074
1075 // Failure flag
1076 int ifail = 1;
1077
1078 // Provisional values
1079 t1 = t2 = t3 = t4 = 0.;
1080
1081 const Node& n0 = m_nodes[element.emap[0]];
1082 const Node& n1 = m_nodes[element.emap[1]];
1083 const Node& n2 = m_nodes[element.emap[2]];
1084 const Node& n3 = m_nodes[element.emap[3]];
1085 // Compute determinant.
1086 const double dd = -(n0.x * n1.y) + n3.x * n2.y - n2.x * n3.y +
1087 x * (-n0.y + n1.y - n2.y + n3.y) + n1.x * (n0.y - y) +
1088 (n0.x + n2.x - n3.x) * y;
1089 det = -(-((n0.x - n3.x) * (n1.y - n2.y)) + (n1.x - n2.x) * (n0.y - n3.y)) *
1090 (2 * x * (-n0.y + n1.y + n2.y - n3.y) -
1091 (n0.x + n3.x) * (n1.y + n2.y - 2 * y) +
1092 n1.x * (n0.y + n3.y - 2 * y) + n2.x * (n0.y + n3.y - 2 * y)) +
1093 dd * dd;
1094
1095 // Check that the determinant is non-negative
1096 // (this can happen if the point is out of range).
1097 if (det < 0) {
1098 if (m_debug) {
1099 std::cerr << m_className << "::Coordinates4:\n"
1100 << " No solution found for isoparametric coordinates\n"
1101 << " because the determinant " << det << " is < 0.\n";
1102 }
1103 return ifail;
1104 }
1105
1106 // Vector products for evaluation of T1.
1107 double prod = ((n2.x - n3.x) * (n0.y - n1.y) - (n0.x - n1.x) * (n2.y - n3.y));
1108 if (prod * prod >
1109 1.0e-12 *
1110 ((n0.x - n1.x) * (n0.x - n1.x) + (n0.y - n1.y) * (n0.y - n1.y)) *
1111 ((n2.x - n3.x) * (n2.x - n3.x) + (n2.y - n3.y) * (n2.y - n3.y))) {
1112 t1 = (-(n3.x * n0.y) + x * n0.y + n2.x * n1.y - x * n1.y - n1.x * n2.y +
1113 x * n2.y + n0.x * n3.y - x * n3.y - n0.x * y + n1.x * y - n2.x * y +
1114 n3.x * y + sqrt(det)) /
1115 prod;
1116 } else {
1117 double xp = n0.y - n1.y;
1118 double yp = n1.x - n0.x;
1119 double dn = sqrt(xp * xp + yp * yp);
1120 if (dn <= 0) {
1121 std::cerr << m_className << "::Coordinates4:\n"
1122 << " Element appears to be degenerate in the 1 - 2 axis.\n";
1123 return ifail;
1124 }
1125 xp = xp / dn;
1126 yp = yp / dn;
1127 double dpoint = xp * (x - n0.x) + yp * (y - n0.y);
1128 double dbox = xp * (n3.x - n0.x) + yp * (n3.y - n0.y);
1129 if (dbox == 0) {
1130 std::cerr << m_className << "::Coordinates4:\n"
1131 << " Element appears to be degenerate in the 1 - 3 axis.\n";
1132 return ifail;
1133 }
1134 double t = -1 + 2 * dpoint / dbox;
1135 double xt1 = n0.x + 0.5 * (t + 1) * (n3.x - n0.x);
1136 double yt1 = n0.y + 0.5 * (t + 1) * (n3.y - n0.y);
1137 double xt2 = n1.x + 0.5 * (t + 1) * (n2.x - n1.x);
1138 double yt2 = n1.y + 0.5 * (t + 1) * (n2.y - n1.y);
1139 dn = (xt1 - xt2) * (xt1 - xt2) + (yt1 - yt2) * (yt1 - yt2);
1140 if (dn <= 0) {
1141 std::cout << m_className << "::Coordinates4:\n";
1142 std::cout
1143 << " Coordinate requested at convergence point of element.\n";
1144 return ifail;
1145 }
1146 t1 = -1 + 2 * ((x - xt1) * (xt2 - xt1) + (y - yt1) * (yt2 - yt1)) / dn;
1147 }
1148
1149 // Vector products for evaluation of T2.
1150 prod = ((n0.x - n3.x) * (n1.y - n2.y) - (n1.x - n2.x) * (n0.y - n3.y));
1151 if (prod * prod >
1152 1.0e-12 *
1153 ((n0.x - n3.x) * (n0.x - n3.x) + (n0.y - n3.y) * (n0.y - n3.y)) *
1154 ((n1.x - n2.x) * (n1.x - n2.x) + (n1.y - n2.y) * (n1.y - n2.y))) {
1155 t2 = (-(n1.x * n0.y) + x * n0.y + n0.x * n1.y - x * n1.y - n3.x * n2.y +
1156 x * n2.y + n2.x * n3.y - x * n3.y - n0.x * y + n1.x * y - n2.x * y +
1157 n3.x * y - sqrt(det)) /
1158 prod;
1159 } else {
1160 double xp = n0.y - n3.y;
1161 double yp = n3.x - n0.x;
1162 double dn = sqrt(xp * xp + yp * yp);
1163 if (dn <= 0) {
1164 std::cerr << m_className << "Coordinates4:\n"
1165 << " Element appears to be degenerate in the 1 - 4 axis.\n";
1166 return ifail;
1167 }
1168 xp = xp / dn;
1169 yp = yp / dn;
1170 double dpoint = xp * (x - n0.x) + yp * (y - n0.y);
1171 double dbox = xp * (n1.x - n0.x) + yp * (n1.y - n0.y);
1172 if (dbox == 0) {
1173 std::cerr << m_className << "::Coordinates4:\n"
1174 << " Element appears to be degenerate in the 1 - 2 axis.\n";
1175 return ifail;
1176 }
1177 double t = -1 + 2 * dpoint / dbox;
1178 double xt1 = n0.x + 0.5 * (t + 1) * (n1.x - n0.x);
1179 double yt1 = n0.y + 0.5 * (t + 1) * (n1.y - n0.y);
1180 double xt2 = n3.x + 0.5 * (t + 1) * (n2.x - n3.x);
1181 double yt2 = n3.y + 0.5 * (t + 1) * (n2.y - n3.y);
1182 dn = (xt1 - xt2) * (xt1 - xt2) + (yt1 - yt2) * (yt1 - yt2);
1183 if (dn <= 0) {
1184 std::cout
1185 << m_className << "::Coordinates4:\n"
1186 << " Coordinate requested at convergence point of element.\n";
1187 return ifail;
1188 }
1189 t2 = -1 + 2 * ((x - xt1) * (xt2 - xt1) + (y - yt1) * (yt2 - yt1)) / dn;
1190 }
1191 if (m_debug) {
1192 std::cout << m_className << "::Coordinates4:\n";
1193 std::cout << " Isoparametric (u, v): (" << t1 << ", " << t2 << ").\n";
1194 // Re-compute the (x,y,z) position for this coordinate.
1195 const double f0 = (1 - t1) * (1 - t2) * 0.25;
1196 const double f1 = (1 + t1) * (1 - t2) * 0.25;
1197 const double f2 = (1 + t1) * (1 + t2) * 0.25;
1198 const double f3 = (1 - t1) * (1 + t2) * 0.25;
1199 const double xr = n0.x * f0 + n1.x * f1 + n2.x * f2 + n3.x * f3;
1200 const double yr = n0.y * f0 + n1.y * f1 + n2.y * f2 + n3.y * f3;
1201 std::cout << m_className << "::Coordinates4: \n";
1202 std::cout << " Position requested: (" << x << ", " << y << ")\n";
1203 std::cout << " Reconstructed: (" << xr << ", " << yr << ")\n";
1204 std::cout << " Difference: (" << x - xr << ", " << y - yr
1205 << ")\n";
1206 }
1207
1208 // This should have worked if we get this far.
1209 ifail = 0;
1210 return ifail;
1211}
1212
1213int ComponentFieldMap::Coordinates5(const double x, const double y,
1214 const double z, double& t1, double& t2,
1215 double& t3, double& t4, double jac[4][4],
1216 double& det, const Element& element) const {
1217 // Debugging
1218 if (m_debug) {
1219 std::cout << m_className << "::Coordinates5:\n";
1220 std::cout << " Point (" << x << ", " << y << ", " << z << ")\n";
1221 }
1222
1223 // Failure flag
1224 int ifail = 1;
1225
1226 // Provisional values
1227 t1 = t2 = t3 = t4 = 0;
1228
1229 // Degenerate elements should have been treated as triangles.
1230 if (element.degenerate) {
1231 std::cerr << m_className << "::Coordinates5: Degenerate element.\n";
1232 return ifail;
1233 }
1234
1235 // Make a first order approximation.
1236 if (Coordinates4(x, y, z, t1, t2, t3, t4, det, element) > 0) {
1237 if (m_debug) {
1238 std::cout << m_className << "::Coordinates5:\n";
1239 std::cout << " Failure to obtain linear estimate of isoparametric "
1240 "coordinates\n.";
1241 }
1242 return ifail;
1243 }
1244
1245 // Set tolerance parameter.
1246 constexpr double f = 0.5;
1247 // Check whether the point is far outside.
1248 if (t1 < -(1 + f) || t1 > (1 + f) || t2 < -(1 + f) || t2 > (1 + f)) {
1249 if (m_debug) {
1250 std::cout << m_className << "::Coordinates5:\n";
1251 std::cout << " Point far outside, (t1,t2) = (" << t1 << ", " << t2
1252 << ").\n";
1253 }
1254 return ifail;
1255 }
1256
1257 // Start iteration
1258 double td1 = t1, td2 = t2;
1259 const Node& n0 = m_nodes[element.emap[0]];
1260 const Node& n1 = m_nodes[element.emap[1]];
1261 const Node& n2 = m_nodes[element.emap[2]];
1262 const Node& n3 = m_nodes[element.emap[3]];
1263 const Node& n4 = m_nodes[element.emap[4]];
1264 const Node& n5 = m_nodes[element.emap[5]];
1265 const Node& n6 = m_nodes[element.emap[6]];
1266 const Node& n7 = m_nodes[element.emap[7]];
1267 bool converged = false;
1268 for (int iter = 0; iter < 10; iter++) {
1269 if (m_debug) {
1270 std::cout << m_className << "::Coordinates5:\n";
1271 std::cout << " Iteration " << iter << ": (t1, t2) = (" << td1
1272 << ", " << td2 << ").\n";
1273 }
1274 // Re-compute the (x,y,z) position for this coordinate.
1275 const double r0 = (-(1 - td1) * (1 - td2) * (1 + td1 + td2)) * 0.25;
1276 const double r1 = (-(1 + td1) * (1 - td2) * (1 - td1 + td2)) * 0.25;
1277 const double r2 = (-(1 + td1) * (1 + td2) * (1 - td1 - td2)) * 0.25;
1278 const double r3 = (-(1 - td1) * (1 + td2) * (1 + td1 - td2)) * 0.25;
1279 const double r4 = (1 - td1) * (1 + td1) * (1 - td2) * 0.5;
1280 const double r5 = (1 + td1) * (1 + td2) * (1 - td2) * 0.5;
1281 const double r6 = (1 - td1) * (1 + td1) * (1 + td2) * 0.5;
1282 const double r7 = (1 - td1) * (1 + td2) * (1 - td2) * 0.5;
1283 double xr = n0.x * r0 + n1.x * r1 + n2.x * r2 + n3.x * r3 + n4.x * r4 +
1284 n5.x * r5 + n6.x * r6 + n7.x * r7;
1285 double yr = n0.y * r0 + n1.y * r1 + n2.y * r2 + n3.y * r3 + n4.y * r4 +
1286 n5.y * r5 + n6.y * r6 + n7.y * r7;
1287 // Compute the Jacobian.
1288 Jacobian5(element, td1, td2, det, jac);
1289 // Compute the difference vector.
1290 double diff[2] = {x - xr, y - yr};
1291 // Update the estimate.
1292 double corr[2] = {0., 0.};
1293 const double invdet = 1. / det;
1294 for (size_t l = 0; l < 2; ++l) {
1295 for (size_t k = 0; k < 2; ++k) {
1296 corr[l] += jac[l][k] * diff[k];
1297 }
1298 corr[l] *= invdet;
1299 }
1300 // Debugging
1301 if (m_debug) {
1302 std::cout << m_className << "::Coordinates5:\n";
1303 std::cout << " Difference vector: (x, y) = (" << diff[0] << ", "
1304 << diff[1] << ").\n";
1305 std::cout << " Correction vector: (t1, t2) = (" << corr[0] << ", "
1306 << corr[1] << ").\n";
1307 }
1308 // Update the vector.
1309 td1 += corr[0];
1310 td2 += corr[1];
1311 // Check for convergence.
1312 constexpr double tol = 1.e-5;
1313 if (fabs(corr[0]) < tol && fabs(corr[1]) < tol) {
1314 if (m_debug) {
1315 std::cout << m_className << "::Coordinates5: Convergence reached.\n";
1316 }
1317 converged = true;
1318 break;
1319 }
1320 }
1321 // No convergence reached.
1322 if (!converged) {
1323 double xmin = std::min({n0.x, n1.x, n2.x, n3.x, n4.x, n5.x, n6.x, n7.x});
1324 double xmax = std::max({n0.x, n1.x, n2.x, n3.x, n4.x, n5.x, n6.x, n7.x});
1325 double ymin = std::min({n0.y, n1.y, n2.y, n3.y, n4.y, n5.y, n6.y, n7.y});
1326 double ymax = std::max({n0.y, n1.y, n2.y, n3.y, n4.y, n5.y, n6.y, n7.y});
1327 if (x >= xmin && x <= xmax && y >= ymin && y <= ymax) {
1329 std::cout << m_className << "::Coordinates5:\n"
1330 << " No convergence achieved "
1331 << "when refining internal isoparametric coordinates\n"
1332 << " at position (" << x << ", " << y << ").\n";
1333 }
1334 t1 = t2 = 0;
1335 return ifail;
1336 }
1337 }
1338
1339 // Convergence reached.
1340 t1 = td1;
1341 t2 = td2;
1342 t3 = 0;
1343 t4 = 0;
1344 if (m_debug) {
1345 std::cout << m_className << "::Coordinates5:\n";
1346 std::cout << " Convergence reached at (t1, t2) = (" << t1 << ", " << t2
1347 << ").\n";
1348 // For debugging purposes, show position.
1349 const double r0 = (-(1 - td1) * (1 - td2) * (1 + td1 + td2)) * 0.25;
1350 const double r1 = (-(1 + td1) * (1 - td2) * (1 - td1 + td2)) * 0.25;
1351 const double r2 = (-(1 + td1) * (1 + td2) * (1 - td1 - td2)) * 0.25;
1352 const double r3 = (-(1 - td1) * (1 + td2) * (1 + td1 - td2)) * 0.25;
1353 const double r4 = (1 - td1) * (1 + td1) * (1 - td2) * 0.5;
1354 const double r5 = (1 + td1) * (1 + td2) * (1 - td2) * 0.5;
1355 const double r6 = (1 - td1) * (1 + td1) * (1 + td2) * 0.5;
1356 const double r7 = (1 - td1) * (1 + td2) * (1 - td2) * 0.5;
1357 double xr = n0.x * r0 + n1.x * r1 + n2.x * r2 + n3.x * r3 + n4.x * r4 +
1358 n5.x * r5 + n6.x * r6 + n7.x * r7;
1359 double yr = n0.y * r0 + n1.y * r1 + n2.y * r2 + n3.y * r3 + n4.y * r4 +
1360 n5.y * r5 + n6.y * r6 + n7.y * r7;
1361 std::cout << " Position requested: (" << x << ", " << y << ")\n";
1362 std::cout << " Reconstructed: (" << xr << ", " << yr << ")\n";
1363 std::cout << " Difference: (" << x - xr << ", " << y - yr
1364 << ")\n";
1365 }
1366
1367 // Success
1368 ifail = 0;
1369 return ifail;
1370}
1371
1372void ComponentFieldMap::Coordinates12(const double x, const double y,
1373 const double z, double& t1, double& t2,
1374 double& t3, double& t4,
1375 const Element& element) const {
1376 if (m_debug) {
1377 std::cout << m_className << "::Coordinates12:\n"
1378 << " Point (" << x << ", " << y << ", " << z << ").\n";
1379 }
1380
1381 const Node& n0 = m_nodes[element.emap[0]];
1382 const Node& n1 = m_nodes[element.emap[1]];
1383 const Node& n2 = m_nodes[element.emap[2]];
1384 const Node& n3 = m_nodes[element.emap[3]];
1385 // Compute tetrahedral coordinates.
1386 const double f1x =
1387 (n2.y - n1.y) * (n3.z - n1.z) - (n3.y - n1.y) * (n2.z - n1.z);
1388 const double f1y =
1389 (n2.z - n1.z) * (n3.x - n1.x) - (n3.z - n1.z) * (n2.x - n1.x);
1390 const double f1z =
1391 (n2.x - n1.x) * (n3.y - n1.y) - (n3.x - n1.x) * (n2.y - n1.y);
1392 t1 = (x - n1.x) * f1x + (y - n1.y) * f1y + (z - n1.z) * f1z;
1393 t1 = t1 / ((n0.x - n1.x) * f1x + (n0.y - n1.y) * f1y + (n0.z - n1.z) * f1z);
1394 const double f2x =
1395 (n0.y - n2.y) * (n3.z - n2.z) - (n3.y - n2.y) * (n0.z - n2.z);
1396 const double f2y =
1397 (n0.z - n2.z) * (n3.x - n2.x) - (n3.z - n2.z) * (n0.x - n2.x);
1398 const double f2z =
1399 (n0.x - n2.x) * (n3.y - n2.y) - (n3.x - n2.x) * (n0.y - n2.y);
1400 t2 = (x - n2.x) * f2x + (y - n2.y) * f2y + (z - n2.z) * f2z;
1401 t2 = t2 / ((n1.x - n2.x) * f2x + (n1.y - n2.y) * f2y + (n1.z - n2.z) * f2z);
1402 const double f3x =
1403 (n0.y - n3.y) * (n1.z - n3.z) - (n1.y - n3.y) * (n0.z - n3.z);
1404 const double f3y =
1405 (n0.z - n3.z) * (n1.x - n3.x) - (n1.z - n3.z) * (n0.x - n3.x);
1406 const double f3z =
1407 (n0.x - n3.x) * (n1.y - n3.y) - (n1.x - n3.x) * (n0.y - n3.y);
1408 t3 = (x - n3.x) * f3x + (y - n3.y) * f3y + (z - n3.z) * f3z;
1409 t3 = t3 / ((n2.x - n3.x) * f3x + (n2.y - n3.y) * f3y + (n2.z - n3.z) * f3z);
1410 const double f4x =
1411 (n2.y - n0.y) * (n1.z - n0.z) - (n1.y - n0.y) * (n2.z - n0.z);
1412 const double f4y =
1413 (n2.z - n0.z) * (n1.x - n0.x) - (n1.z - n0.z) * (n2.x - n0.x);
1414 const double f4z =
1415 (n2.x - n0.x) * (n1.y - n0.y) - (n1.x - n0.x) * (n2.y - n0.y);
1416 t4 = (x - n0.x) * f4x + (y - n0.y) * f4y + (z - n0.z) * f4z;
1417 t4 = t4 / ((n3.x - n0.x) * f4x + (n3.y - n0.y) * f4y + (n3.z - n0.z) * f4z);
1418
1419 // Result
1420 if (m_debug) {
1421 std::cout << m_className << "::Coordinates12:\n";
1422 std::cout << " Tetrahedral coordinates (t, u, v, w) = (" << t1 << ", "
1423 << t2 << ", " << t3 << ", " << t4
1424 << ") sum = " << t1 + t2 + t3 + t4 << ".\n";
1425 // Re-compute the (x,y,z) position for this coordinate.
1426 const double xr = n0.x * t1 + n1.x * t2 + n2.x * t3 + n3.x * t4;
1427 const double yr = n0.y * t1 + n1.y * t2 + n2.y * t3 + n3.y * t4;
1428 const double zr = n0.z * t1 + n1.z * t2 + n2.z * t3 + n3.z * t4;
1429 const double sr = t1 + t2 + t3 + t4;
1430 std::cout << " Position requested: (" << x << ", " << y << ", " << z
1431 << ")\n";
1432 std::cout << " Reconstructed: (" << xr << ", " << yr << ", "
1433 << zr << ")\n";
1434 std::cout << " Difference: (" << x - xr << ", " << y - yr
1435 << ", " << z - zr << ")\n";
1436 std::cout << " Checksum - 1: " << sr - 1 << "\n";
1437 }
1438}
1439
1440int ComponentFieldMap::Coordinates13(const double x, const double y,
1441 const double z, double& t1, double& t2,
1442 double& t3, double& t4, double jac[4][4],
1443 double& det,
1444 const Element& element) const {
1445 if (m_debug) {
1446 std::cout << m_className << "::Coordinates13:\n";
1447 std::cout << " Point (" << x << ", " << y << ", " << z << ")\n";
1448 }
1449
1450 // Provisional values
1451 t1 = t2 = t3 = t4 = 0.;
1452
1453 // Make a first order approximation.
1454 Coordinates12(x, y, z, t1, t2, t3, t4, element);
1455
1456 // Set tolerance parameter.
1457 constexpr double f = 0.5;
1458 if (t1 < -f || t2 < -f || t3 < -f || t4 < -f || t1 > 1 + f || t2 > 1 + f ||
1459 t3 > 1 + f || t4 > 1 + f) {
1460 if (m_debug) {
1461 std::cout << m_className << "::Coordinates13:\n";
1462 std::cout << " Linear isoparametric coordinates more than\n";
1463 std::cout << " f (" << f << ") out of range.\n";
1464 }
1465 return 0;
1466 }
1467
1468 // Start iteration.
1469 double td1 = t1, td2 = t2, td3 = t3, td4 = t4;
1470 const Node& n0 = m_nodes[element.emap[0]];
1471 const Node& n1 = m_nodes[element.emap[1]];
1472 const Node& n2 = m_nodes[element.emap[2]];
1473 const Node& n3 = m_nodes[element.emap[3]];
1474 const Node& n4 = m_nodes[element.emap[4]];
1475 const Node& n5 = m_nodes[element.emap[5]];
1476 const Node& n6 = m_nodes[element.emap[6]];
1477 const Node& n7 = m_nodes[element.emap[7]];
1478 const Node& n8 = m_nodes[element.emap[8]];
1479 const Node& n9 = m_nodes[element.emap[9]];
1480
1481 // Loop
1482 bool converged = false;
1483 double diff[4], corr[4];
1484 for (int iter = 0; iter < 10; iter++) {
1485 if (m_debug) {
1486 std::cout << m_className << "::Coordinates13:\n";
1487 std::printf(" Iteration %4u: t = (%15.8f, %15.8f %15.8f %15.8f)\n",
1488 iter, td1, td2, td3, td4);
1489 }
1490 const double f0 = td1 * (2 * td1 - 1);
1491 const double f1 = td2 * (2 * td2 - 1);
1492 const double f2 = td3 * (2 * td3 - 1);
1493 const double f3 = td4 * (2 * td4 - 1);
1494 const double f4 = 4 * td1 * td2;
1495 const double f5 = 4 * td1 * td3;
1496 const double f6 = 4 * td1 * td4;
1497 const double f7 = 4 * td2 * td3;
1498 const double f8 = 4 * td2 * td4;
1499 const double f9 = 4 * td3 * td4;
1500 // Re-compute the (x,y,z) position for this coordinate.
1501 const double xr = n0.x * f0 + n1.x * f1 + n2.x * f2 + n3.x * f3 +
1502 n4.x * f4 + n5.x * f5 + n6.x * f6 + n7.x * f7 +
1503 n8.x * f8 + n9.x * f9;
1504 const double yr = n0.y * f0 + n1.y * f1 + n2.y * f2 + n3.y * f3 +
1505 n4.y * f4 + n5.y * f5 + n6.y * f6 + n7.y * f7 +
1506 n8.y * f8 + n9.y * f9;
1507 const double zr = n0.z * f0 + n1.z * f1 + n2.z * f2 + n3.z * f3 +
1508 n4.z * f4 + n5.z * f5 + n6.z * f6 + n7.z * f7 +
1509 n8.z * f8 + n9.z * f9;
1510 const double sr = td1 + td2 + td3 + td4;
1511
1512 // Compute the Jacobian.
1513 Jacobian13(element, td1, td2, td3, td4, det, jac);
1514 // Compute the difference vector.
1515 diff[0] = 1 - sr;
1516 diff[1] = x - xr;
1517 diff[2] = y - yr;
1518 diff[3] = z - zr;
1519 // Update the estimate.
1520 const double invdet = 1. / det;
1521 for (int l = 0; l < 4; ++l) {
1522 corr[l] = 0;
1523 for (int k = 0; k < 4; ++k) {
1524 corr[l] += jac[l][k] * diff[k];
1525 }
1526 corr[l] *= invdet;
1527 }
1528
1529 // Debugging
1530 if (m_debug) {
1531 std::cout << m_className << "::Coordinates13:\n";
1532 std::cout << " Difference vector: (1, x, y, z) = (" << diff[0]
1533 << ", " << diff[1] << ", " << diff[2] << ", " << diff[3]
1534 << ").\n";
1535 std::cout << " Correction vector: (t1,t2,t3,t4) = (" << corr[0]
1536 << ", " << corr[1] << ", " << corr[2] << ", " << corr[3]
1537 << ").\n";
1538 }
1539
1540 // Update the vector.
1541 td1 += corr[0];
1542 td2 += corr[1];
1543 td3 += corr[2];
1544 td4 += corr[3];
1545
1546 // Check for convergence.
1547 constexpr double tol = 1.e-5;
1548 if (fabs(corr[0]) < tol && fabs(corr[1]) < tol && fabs(corr[2]) < tol &&
1549 fabs(corr[3]) < tol) {
1550 if (m_debug) {
1551 std::cout << m_className << "::Coordinates13: Convergence reached.\n";
1552 }
1553 converged = true;
1554 break;
1555 }
1556 }
1557
1558 // No convergence reached.
1559 if (!converged) {
1560 const double xmin = std::min({n0.x, n1.x, n2.x, n3.x});
1561 const double xmax = std::max({n0.x, n1.x, n2.x, n3.x});
1562 const double ymin = std::min({n0.y, n1.y, n2.y, n3.y});
1563 const double ymax = std::max({n0.y, n1.y, n2.y, n3.y});
1564 const double zmin = std::min({n0.z, n1.z, n2.z, n3.z});
1565 const double zmax = std::max({n0.z, n1.z, n2.z, n3.z});
1566 if (x >= xmin && x <= xmax && y >= ymin && y <= ymax && z >= zmin &&
1567 z <= zmax) {
1569 std::cout << m_className << "::Coordinates13:\n"
1570 << " No convergence achieved "
1571 << "when refining internal isoparametric coordinates\n"
1572 << " at position (" << x << ", " << y << ", " << z
1573 << ").\n";
1574 }
1575 t1 = t2 = t3 = t4 = -1;
1576 return 1;
1577 }
1578 }
1579
1580 // Convergence reached.
1581 t1 = td1;
1582 t2 = td2;
1583 t3 = td3;
1584 t4 = td4;
1585 if (m_debug) {
1586 std::cout << m_className << "::Coordinates13:\n";
1587 std::cout << " Convergence reached at (t1, t2, t3, t4) = (" << t1 << ", "
1588 << t2 << ", " << t3 << ", " << t4 << ").\n";
1589 // Re-compute the (x,y,z) position for this coordinate.
1590 const double f0 = td1 * (2 * td1 - 1);
1591 const double f1 = td2 * (2 * td2 - 1);
1592 const double f2 = td3 * (2 * td3 - 1);
1593 const double f3 = td4 * (2 * td4 - 1);
1594 const double f4 = 4 * td1 * td2;
1595 const double f5 = 4 * td1 * td3;
1596 const double f6 = 4 * td1 * td4;
1597 const double f7 = 4 * td2 * td3;
1598 const double f8 = 4 * td2 * td4;
1599 const double f9 = 4 * td3 * td4;
1600 double xr = n0.x * f0 + n1.x * f1 + n2.x * f2 + n3.x * f3 + n4.x * f4 +
1601 n5.x * f5 + n6.x * f6 + n7.x * f7 + n8.x * f8 + n9.x * f9;
1602 double yr = n0.y * f0 + n1.y * f1 + n2.y * f2 + n3.y * f3 + n4.y * f4 +
1603 n5.y * f5 + n6.y * f6 + n7.y * f7 + n8.y * f8 + n9.y * f9;
1604 double zr = n0.z * f0 + n1.z * f1 + n2.z * f2 + n3.z * f3 + n4.z * f4 +
1605 n5.z * f5 + n6.z * f6 + n7.z * f7 + n8.z * f8 + n9.z * f9;
1606 double sr = td1 + td2 + td3 + td4;
1607 std::cout << " Position requested: (" << x << ", " << y << ", " << z
1608 << ")\n";
1609 std::cout << " Reconstructed: (" << xr << ", " << yr << ", "
1610 << zr << ")\n";
1611 std::cout << " Difference: (" << x - xr << ", " << y - yr
1612 << ", " << z - zr << ")\n";
1613 std::cout << " Checksum - 1: " << sr - 1 << "\n";
1614 }
1615
1616 // Success
1617 return 0;
1618}
1619
1620int ComponentFieldMap::CoordinatesCube(const double x, const double y,
1621 const double z, double& t1, double& t2,
1622 double& t3, TMatrixD*& jac,
1623 std::vector<TMatrixD*>& dN,
1624 const Element& element) const {
1625 /*
1626 global coordinates 7__ _ _ 6 t3 t2
1627 / /| ^ /|
1628 ^ z / / | | /
1629 | 4_______5 | | /
1630 | | | | | /
1631 | | 3 | 2 |/ t1
1632 -------> | | / ------->
1633 / y | |/ local coordinates
1634 / 0--------1
1635 /
1636 v x
1637 */
1638
1639 // Failure flag
1640 int ifail = 1;
1641
1642 const Node& n0 = m_nodes[element.emap[0]];
1643 const Node& n2 = m_nodes[element.emap[2]];
1644 const Node& n3 = m_nodes[element.emap[3]];
1645 const Node& n7 = m_nodes[element.emap[7]];
1646
1647 // Compute hexahedral coordinates (t1->[-1,1],t2->[-1,1],t3->[-1,1]) and
1648 // t1 (zeta) is in y-direction
1649 // t2 (eta) is in opposite x-direction
1650 // t3 (mu) is in z-direction
1651 // Nodes are set in that way, that node [0] has always lowest x,y,z!
1652 t2 = (2. * (x - n3.x) / (n0.x - n3.x) - 1) * -1.;
1653 t1 = 2. * (y - n3.y) / (n2.y - n3.y) - 1;
1654 t3 = 2. * (z - n3.z) / (n7.z - n3.z) - 1;
1655 // Re-compute the (x,y,z) position for this coordinate.
1656 if (m_debug) {
1657 double n[8];
1658 n[0] = 1. / 8 * (1 - t1) * (1 - t2) * (1 - t3);
1659 n[1] = 1. / 8 * (1 + t1) * (1 - t2) * (1 - t3);
1660 n[2] = 1. / 8 * (1 + t1) * (1 + t2) * (1 - t3);
1661 n[3] = 1. / 8 * (1 - t1) * (1 + t2) * (1 - t3);
1662 n[4] = 1. / 8 * (1 - t1) * (1 - t2) * (1 + t3);
1663 n[5] = 1. / 8 * (1 + t1) * (1 - t2) * (1 + t3);
1664 n[6] = 1. / 8 * (1 + t1) * (1 + t2) * (1 + t3);
1665 n[7] = 1. / 8 * (1 - t1) * (1 + t2) * (1 + t3);
1666
1667 double xr = 0;
1668 double yr = 0;
1669 double zr = 0;
1670
1671 for (int i = 0; i < 8; i++) {
1672 const Node& node = m_nodes[element.emap[i]];
1673 xr += node.x * n[i];
1674 yr += node.y * n[i];
1675 zr += node.z * n[i];
1676 }
1677 double sr = n[0] + n[1] + n[2] + n[3] + n[4] + n[5] + n[6] + n[7];
1678 std::cout << m_className << "::CoordinatesCube:\n";
1679 std::cout << " Position requested: (" << x << "," << y << "," << z
1680 << ")\n";
1681 std::cout << " Position reconstructed: (" << xr << "," << yr << "," << zr
1682 << ")\n";
1683 std::cout << " Difference: (" << (x - xr) << "," << (y - yr)
1684 << "," << (z - zr) << ")\n";
1685 std::cout << " Hexahedral coordinates (t, u, v) = (" << t1 << "," << t2
1686 << "," << t3 << ")\n";
1687 std::cout << " Checksum - 1: " << (sr - 1) << "\n";
1688 }
1689 if (jac != 0) JacobianCube(element, t1, t2, t3, jac, dN);
1690 // This should always work.
1691 ifail = 0;
1692 return ifail;
1693}
1694
1696
1697 m_ready = false;
1698
1699 m_elements.clear();
1700 m_nodes.clear();
1701 m_materials.clear();
1702 m_wfields.clear();
1703 m_wfieldsOk.clear();
1704 m_hasBoundingBox = false;
1705 m_warning = false;
1706 m_nWarnings = 0;
1707
1708 m_octree.reset(nullptr);
1709 m_cacheElemBoundingBoxes = false;
1710 m_lastElement = -1;
1711}
1712
1714
1715 // Establish the ranges.
1716 SetRange();
1718 std::cout << m_className << "::Prepare:\n"
1719 << " Caching the bounding boxes of all elements...";
1720 CalculateElementBoundingBoxes();
1721 std::cout << " done.\n";
1722 // Initialize the tetrahedral tree.
1723 InitializeTetrahedralTree();
1724}
1725
1727 // Check the required data is available.
1728 if (!m_ready) {
1729 PrintNotReady("UpdatePeriodicityCommon");
1730 return;
1731 }
1732
1733 for (size_t i = 0; i < 3; ++i) {
1734 // No regular and mirror periodicity at the same time.
1735 if (m_periodic[i] && m_mirrorPeriodic[i]) {
1736 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
1737 << " Both simple and mirror periodicity requested. Reset.\n";
1738 m_periodic[i] = false;
1739 m_mirrorPeriodic[i] = false;
1740 m_warning = true;
1741 }
1742 // In case of axial periodicity,
1743 // the range must be an integral part of two pi.
1744 if (m_axiallyPeriodic[i]) {
1745 if (m_mapamin[i] >= m_mapamax[i]) {
1746 m_mapna[i] = 0;
1747 } else {
1748 m_mapna[i] = TwoPi / (m_mapamax[i] - m_mapamin[i]);
1749 }
1750 if (fabs(m_mapna[i] - int(0.5 + m_mapna[i])) > 0.001 ||
1751 m_mapna[i] < 1.5) {
1752 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
1753 << " Axial symmetry has been requested but map does not\n"
1754 << " cover an integral fraction of 2 pi. Reset.\n";
1755 m_axiallyPeriodic[i] = false;
1756 m_warning = true;
1757 }
1758 }
1759 }
1760
1761 // Not more than 1 rotational symmetry
1765 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
1766 << " Only one rotational symmetry allowed; reset.\n";
1767 m_rotationSymmetric.fill(false);
1768 m_warning = true;
1769 }
1770
1771 // No rotational symmetry as well as axial periodicity
1773 m_rotationSymmetric[2]) &&
1775 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
1776 << " Not allowed to combine rotational symmetry\n"
1777 << " and axial periodicity; reset.\n";
1778 m_axiallyPeriodic.fill(false);
1779 m_rotationSymmetric.fill(false);
1780 m_warning = true;
1781 }
1782
1783 // In case of rotational symmetry, the x-range should not straddle 0.
1786 if (m_mapmin[0] * m_mapmax[0] < 0) {
1787 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
1788 << " Rotational symmetry requested, \n"
1789 << " but x-range straddles 0; reset.\n";
1790 m_rotationSymmetric.fill(false);
1791 m_warning = true;
1792 }
1793 }
1794
1795 // Recompute the cell ranges.
1796 for (size_t i = 0; i < 3; ++i) {
1797 m_minBoundingBox[i] = m_mapmin[i];
1798 m_maxBoundingBox[i] = m_mapmax[i];
1799 m_cells[i] = fabs(m_mapmax[i] - m_mapmin[i]);
1800 }
1801 for (size_t i = 0; i < 3; ++i) {
1802 if (!m_rotationSymmetric[i]) continue;
1803 const double r = std::max(fabs(m_mapmin[0]), fabs(m_mapmax[0]));
1804 m_minBoundingBox.fill(-r);
1805 m_maxBoundingBox.fill(+r);
1806 m_minBoundingBox[i] = m_mapmin[1];
1807 m_maxBoundingBox[i] = m_mapmax[1];
1808 break;
1809 }
1810
1811 if (m_axiallyPeriodic[0]) {
1812 const double yzmax = std::max({fabs(m_mapmin[1]), fabs(m_mapmax[1]),
1813 fabs(m_mapmin[2]), fabs(m_mapmax[2])});
1814 m_minBoundingBox[1] = -yzmax;
1815 m_maxBoundingBox[1] = +yzmax;
1816 m_minBoundingBox[2] = -yzmax;
1817 m_maxBoundingBox[2] = +yzmax;
1818 } else if (m_axiallyPeriodic[1]) {
1819 const double xzmax = std::max({fabs(m_mapmin[0]), fabs(m_mapmax[0]),
1820 fabs(m_mapmin[2]), fabs(m_mapmax[2])});
1821 m_minBoundingBox[0] = -xzmax;
1822 m_maxBoundingBox[0] = +xzmax;
1823 m_minBoundingBox[2] = -xzmax;
1824 m_maxBoundingBox[2] = +xzmax;
1825 } else if (m_axiallyPeriodic[2]) {
1826 const double xymax = std::max({fabs(m_mapmin[0]), fabs(m_mapmax[0]),
1827 fabs(m_mapmin[1]), fabs(m_mapmax[1])});
1828 m_minBoundingBox[0] = -xymax;
1829 m_maxBoundingBox[0] = +xymax;
1830 m_minBoundingBox[1] = -xymax;
1831 m_maxBoundingBox[1] = +xymax;
1832 }
1833
1834 for (size_t i = 0; i < 3; ++i) {
1835 if (m_periodic[i] || m_mirrorPeriodic[i]) {
1836 m_minBoundingBox[i] = -INFINITY;
1837 m_maxBoundingBox[i] = +INFINITY;
1838 }
1839 }
1840
1841 // Display the range if requested.
1842 if (m_debug) PrintRange();
1843}
1844
1846 // Check the required data is available.
1847 if (!m_ready) {
1848 PrintNotReady("UpdatePeriodicity2d");
1849 return;
1850 }
1851
1852 // No z-periodicity in 2d
1853 if (m_periodic[2] || m_mirrorPeriodic[2]) {
1854 std::cerr << m_className << "::UpdatePeriodicity2d:\n"
1855 << " Simple or mirror periodicity along z\n"
1856 << " requested for a 2d map; reset.\n";
1857 m_periodic[2] = false;
1858 m_mirrorPeriodic[2] = false;
1859 m_warning = true;
1860 }
1861
1862 // Only z-axial periodicity in 2d maps
1863 if (m_axiallyPeriodic[0] || m_axiallyPeriodic[1]) {
1864 std::cerr << m_className << "::UpdatePeriodicity2d:\n"
1865 << " Axial symmetry has been requested \n"
1866 << " around x or y for a 2d map; reset.\n";
1867 m_axiallyPeriodic[0] = false;
1868 m_axiallyPeriodic[1] = false;
1869 m_warning = true;
1870 }
1871}
1872
1874 // Initial values
1875 m_mapmin.fill(0.);
1876 m_mapmax.fill(0.);
1877 m_mapamin.fill(0.);
1878 m_mapamax.fill(0.);
1879 m_mapvmin = m_mapvmax = 0.;
1880 m_setang.fill(false);
1881
1882 // Make sure the required data is available.
1883 if (!m_ready || m_nodes.empty()) {
1884 std::cerr << m_className << "::SetRange: Field map not yet set.\n";
1885 return;
1886 }
1887
1888 // Loop over the nodes.
1889 m_mapmin[0] = m_mapmax[0] = m_nodes[0].x;
1890 m_mapmin[1] = m_mapmax[1] = m_nodes[0].y;
1891 m_mapmin[2] = m_mapmax[2] = m_nodes[0].z;
1892 m_mapvmin = m_mapvmax = m_nodes[0].v;
1893
1894 for (const auto& node : m_nodes) {
1895 const std::array<double, 3> pos = {{node.x, node.y, node.z}};
1896 for (unsigned int i = 0; i < 3; ++i) {
1897 m_mapmin[i] = std::min(m_mapmin[i], pos[i]);
1898 m_mapmax[i] = std::max(m_mapmax[i], pos[i]);
1899 }
1900 m_mapvmin = std::min(m_mapvmin, node.v);
1901 m_mapvmax = std::max(m_mapvmax, node.v);
1902
1903 if (node.y != 0 || node.z != 0) {
1904 const double ang = atan2(node.z, node.y);
1905 if (m_setang[0]) {
1906 m_mapamin[0] = std::min(m_mapamin[0], ang);
1907 m_mapamax[0] = std::max(m_mapamax[0], ang);
1908 } else {
1909 m_mapamin[0] = m_mapamax[0] = ang;
1910 m_setang[0] = true;
1911 }
1912 }
1913
1914 if (node.z != 0 || node.x != 0) {
1915 const double ang = atan2(node.x, node.z);
1916 if (m_setang[1]) {
1917 m_mapamin[1] = std::min(m_mapamin[1], ang);
1918 m_mapamax[1] = std::max(m_mapamax[1], ang);
1919 } else {
1920 m_mapamin[1] = m_mapamax[1] = ang;
1921 m_setang[1] = true;
1922 }
1923 }
1924
1925 if (node.x != 0 || node.y != 0) {
1926 const double ang = atan2(node.y, node.x);
1927 if (m_setang[2]) {
1928 m_mapamin[2] = std::min(m_mapamin[2], ang);
1929 m_mapamax[2] = std::max(m_mapamax[2], ang);
1930 } else {
1931 m_mapamin[2] = m_mapamax[2] = ang;
1932 m_setang[2] = true;
1933 }
1934 }
1935 }
1936
1937 // Fix the angular ranges.
1938 for (unsigned int i = 0; i < 3; ++i) {
1939 if (m_mapamax[i] - m_mapamin[i] > Pi) {
1940 const double aux = m_mapamin[i];
1941 m_mapamin[i] = m_mapamax[i];
1942 m_mapamax[i] = aux + TwoPi;
1943 }
1944 }
1945
1946 // Set provisional cell dimensions.
1947 m_minBoundingBox[0] = m_mapmin[0];
1948 m_maxBoundingBox[0] = m_mapmax[0];
1949 m_minBoundingBox[1] = m_mapmin[1];
1950 m_maxBoundingBox[1] = m_mapmax[1];
1951 if (m_is3d) {
1952 m_minBoundingBox[2] = m_mapmin[2];
1953 m_maxBoundingBox[2] = m_mapmax[2];
1954 } else {
1955 m_mapmin[2] = m_minBoundingBox[2];
1956 m_mapmax[2] = m_maxBoundingBox[2];
1957 }
1958 m_hasBoundingBox = true;
1959
1960 // Display the range if requested.
1961 if (m_debug) PrintRange();
1962}
1963
1965 std::cout << m_className << "::PrintRange:\n";
1966 std::cout << " Dimensions of the elementary block\n";
1967 printf(" %15g < x < %-15g cm,\n", m_mapmin[0], m_mapmax[0]);
1968 printf(" %15g < y < %-15g cm,\n", m_mapmin[1], m_mapmax[1]);
1969 printf(" %15g < z < %-15g cm,\n", m_mapmin[2], m_mapmax[2]);
1970 printf(" %15g < V < %-15g V.\n", m_mapvmin, m_mapvmax);
1971
1972 std::cout << " Periodicities\n";
1973 const std::array<std::string, 3> axes = {{"x", "y", "z"}};
1974 for (unsigned int i = 0; i < 3; ++i) {
1975 std::cout << " " << axes[i] << ":";
1976 if (m_periodic[i]) {
1977 std::cout << " simple with length " << m_cells[i] << " cm";
1978 }
1979 if (m_mirrorPeriodic[i]) {
1980 std::cout << " mirror with length " << m_cells[i] << " cm";
1981 }
1982 if (m_axiallyPeriodic[i]) {
1983 std::cout << " axial " << int(0.5 + m_mapna[i]) << "-fold repetition";
1984 }
1985 if (m_rotationSymmetric[i]) std::cout << " rotational symmetry";
1986 if (!(m_periodic[i] || m_mirrorPeriodic[i] || m_axiallyPeriodic[i] ||
1988 std::cout << " none";
1989 std::cout << "\n";
1990 }
1991}
1992
1994 double& xmin, double& ymin, double& zmin,
1995 double& xmax, double& ymax, double& zmax) {
1996 if (!m_ready) return false;
1997
1998 xmin = m_minBoundingBox[0];
1999 xmax = m_maxBoundingBox[0];
2000 ymin = m_minBoundingBox[1];
2001 ymax = m_maxBoundingBox[1];
2002 zmin = m_minBoundingBox[2];
2003 zmax = m_maxBoundingBox[2];
2004 return true;
2005}
2006
2008 double& xmin, double& ymin, double& zmin,
2009 double& xmax, double& ymax, double& zmax) {
2010
2011 if (!m_ready) return false;
2012 xmin = m_mapmin[0];
2013 xmax = m_mapmax[0];
2014 ymin = m_mapmin[1];
2015 ymax = m_mapmax[1];
2016 zmin = m_mapmin[2];
2017 zmax = m_mapmax[2];
2018 return true;
2019}
2020
2021void ComponentFieldMap::MapCoordinates(double& xpos, double& ypos, double& zpos,
2022 bool& xmirrored, bool& ymirrored,
2023 bool& zmirrored, double& rcoordinate,
2024 double& rotation) const {
2025 // Initial values
2026 rotation = 0;
2027
2028 // If chamber is periodic, reduce to the cell volume.
2029 xmirrored = false;
2030 if (m_periodic[0]) {
2031 const double xrange = m_mapmax[0] - m_mapmin[0];
2032 xpos = m_mapmin[0] + fmod(xpos - m_mapmin[0], xrange);
2033 if (xpos < m_mapmin[0]) xpos += xrange;
2034 } else if (m_mirrorPeriodic[0]) {
2035 const double xrange = m_mapmax[0] - m_mapmin[0];
2036 double xnew = m_mapmin[0] + fmod(xpos - m_mapmin[0], xrange);
2037 if (xnew < m_mapmin[0]) xnew += xrange;
2038 int nx = int(floor(0.5 + (xnew - xpos) / xrange));
2039 if (nx != 2 * (nx / 2)) {
2040 xnew = m_mapmin[0] + m_mapmax[0] - xnew;
2041 xmirrored = true;
2042 }
2043 xpos = xnew;
2044 }
2045 if (m_axiallyPeriodic[0] && (zpos != 0 || ypos != 0)) {
2046 const double auxr = sqrt(zpos * zpos + ypos * ypos);
2047 double auxphi = atan2(zpos, ypos);
2048 const double phirange = m_mapamax[0] - m_mapamin[0];
2049 const double phim = 0.5 * (m_mapamin[0] + m_mapamax[0]);
2050 rotation = phirange * floor(0.5 + (auxphi - phim) / phirange);
2051 if (auxphi - rotation < m_mapamin[0]) rotation -= phirange;
2052 if (auxphi - rotation > m_mapamax[0]) rotation += phirange;
2053 auxphi = auxphi - rotation;
2054 ypos = auxr * cos(auxphi);
2055 zpos = auxr * sin(auxphi);
2056 }
2057
2058 ymirrored = false;
2059 if (m_periodic[1]) {
2060 const double yrange = m_mapmax[1] - m_mapmin[1];
2061 ypos = m_mapmin[1] + fmod(ypos - m_mapmin[1], yrange);
2062 if (ypos < m_mapmin[1]) ypos += yrange;
2063 } else if (m_mirrorPeriodic[1]) {
2064 const double yrange = m_mapmax[1] - m_mapmin[1];
2065 double ynew = m_mapmin[1] + fmod(ypos - m_mapmin[1], yrange);
2066 if (ynew < m_mapmin[1]) ynew += yrange;
2067 int ny = int(floor(0.5 + (ynew - ypos) / yrange));
2068 if (ny != 2 * (ny / 2)) {
2069 ynew = m_mapmin[1] + m_mapmax[1] - ynew;
2070 ymirrored = true;
2071 }
2072 ypos = ynew;
2073 }
2074 if (m_axiallyPeriodic[1] && (xpos != 0 || zpos != 0)) {
2075 const double auxr = sqrt(xpos * xpos + zpos * zpos);
2076 double auxphi = atan2(xpos, zpos);
2077 const double phirange = (m_mapamax[1] - m_mapamin[1]);
2078 const double phim = 0.5 * (m_mapamin[1] + m_mapamax[1]);
2079 rotation = phirange * floor(0.5 + (auxphi - phim) / phirange);
2080 if (auxphi - rotation < m_mapamin[1]) rotation -= phirange;
2081 if (auxphi - rotation > m_mapamax[1]) rotation += phirange;
2082 auxphi = auxphi - rotation;
2083 zpos = auxr * cos(auxphi);
2084 xpos = auxr * sin(auxphi);
2085 }
2086
2087 zmirrored = false;
2088 if (m_periodic[2]) {
2089 const double zrange = m_mapmax[2] - m_mapmin[2];
2090 zpos = m_mapmin[2] + fmod(zpos - m_mapmin[2], zrange);
2091 if (zpos < m_mapmin[2]) zpos += zrange;
2092 } else if (m_mirrorPeriodic[2]) {
2093 const double zrange = m_mapmax[2] - m_mapmin[2];
2094 double znew = m_mapmin[2] + fmod(zpos - m_mapmin[2], zrange);
2095 if (znew < m_mapmin[2]) znew += zrange;
2096 int nz = int(floor(0.5 + (znew - zpos) / zrange));
2097 if (nz != 2 * (nz / 2)) {
2098 znew = m_mapmin[2] + m_mapmax[2] - znew;
2099 zmirrored = true;
2100 }
2101 zpos = znew;
2102 }
2103 if (m_axiallyPeriodic[2] && (ypos != 0 || xpos != 0)) {
2104 const double auxr = sqrt(ypos * ypos + xpos * xpos);
2105 double auxphi = atan2(ypos, xpos);
2106 const double phirange = m_mapamax[2] - m_mapamin[2];
2107 const double phim = 0.5 * (m_mapamin[2] + m_mapamax[2]);
2108 rotation = phirange * floor(0.5 + (auxphi - phim) / phirange);
2109 if (auxphi - rotation < m_mapamin[2]) rotation -= phirange;
2110 if (auxphi - rotation > m_mapamax[2]) rotation += phirange;
2111 auxphi = auxphi - rotation;
2112 xpos = auxr * cos(auxphi);
2113 ypos = auxr * sin(auxphi);
2114 }
2115
2116 // If we have a rotationally symmetric field map, store coordinates.
2117 rcoordinate = 0;
2118 double zcoordinate = 0;
2119 if (m_rotationSymmetric[0]) {
2120 rcoordinate = sqrt(ypos * ypos + zpos * zpos);
2121 zcoordinate = xpos;
2122 } else if (m_rotationSymmetric[1]) {
2123 rcoordinate = sqrt(xpos * xpos + zpos * zpos);
2124 zcoordinate = ypos;
2125 } else if (m_rotationSymmetric[2]) {
2126 rcoordinate = sqrt(xpos * xpos + ypos * ypos);
2127 zcoordinate = zpos;
2128 }
2129
2132 xpos = rcoordinate;
2133 ypos = zcoordinate;
2134 zpos = 0;
2135 }
2136}
2137
2138void ComponentFieldMap::UnmapFields(double& ex, double& ey, double& ez,
2139 double& xpos, double& ypos, double& zpos,
2140 bool& xmirrored, bool& ymirrored,
2141 bool& zmirrored, double& rcoordinate,
2142 double& rotation) const {
2143 // Apply mirror imaging.
2144 if (xmirrored) ex = -ex;
2145 if (ymirrored) ey = -ey;
2146 if (zmirrored) ez = -ez;
2147
2148 // Rotate the field.
2149 double er, theta;
2150 if (m_axiallyPeriodic[0]) {
2151 er = sqrt(ey * ey + ez * ez);
2152 theta = atan2(ez, ey);
2153 theta += rotation;
2154 ey = er * cos(theta);
2155 ez = er * sin(theta);
2156 }
2157 if (m_axiallyPeriodic[1]) {
2158 er = sqrt(ez * ez + ex * ex);
2159 theta = atan2(ex, ez);
2160 theta += rotation;
2161 ez = er * cos(theta);
2162 ex = er * sin(theta);
2163 }
2164 if (m_axiallyPeriodic[2]) {
2165 er = sqrt(ex * ex + ey * ey);
2166 theta = atan2(ey, ex);
2167 theta += rotation;
2168 ex = er * cos(theta);
2169 ey = er * sin(theta);
2170 }
2171
2172 // Take care of symmetry.
2173 double eaxis;
2174 er = ex;
2175 eaxis = ey;
2176
2177 // Rotational symmetry
2178 if (m_rotationSymmetric[0]) {
2179 if (rcoordinate <= 0) {
2180 ex = eaxis;
2181 ey = 0;
2182 ez = 0;
2183 } else {
2184 ex = eaxis;
2185 ey = er * ypos / rcoordinate;
2186 ez = er * zpos / rcoordinate;
2187 }
2188 }
2189 if (m_rotationSymmetric[1]) {
2190 if (rcoordinate <= 0) {
2191 ex = 0;
2192 ey = eaxis;
2193 ez = 0;
2194 } else {
2195 ex = er * xpos / rcoordinate;
2196 ey = eaxis;
2197 ez = er * zpos / rcoordinate;
2198 }
2199 }
2200 if (m_rotationSymmetric[2]) {
2201 if (rcoordinate <= 0) {
2202 ex = 0;
2203 ey = 0;
2204 ez = eaxis;
2205 } else {
2206 ex = er * xpos / rcoordinate;
2207 ey = er * ypos / rcoordinate;
2208 ez = eaxis;
2209 }
2210 }
2211}
2212
2213double ComponentFieldMap::ScalingFactor(std::string unit) {
2214 std::transform(unit.begin(), unit.end(), unit.begin(), toupper);
2215 if (unit == "MUM" || unit == "MICRON" || unit == "MICROMETER") {
2216 return 0.0001;
2217 } else if (unit == "MM" || unit == "MILLIMETER") {
2218 return 0.1;
2219 } else if (unit == "CM" || unit == "CENTIMETER") {
2220 return 1.0;
2221 } else if (unit == "M" || unit == "METER") {
2222 return 100.0;
2223 }
2224 return -1.;
2225}
2226
2227int ComponentFieldMap::ReadInteger(char* token, int def, bool& error) {
2228 if (!token) {
2229 error = true;
2230 return def;
2231 }
2232
2233 return atoi(token);
2234}
2235
2236double ComponentFieldMap::ReadDouble(char* token, double def, bool& error) {
2237 if (!token) {
2238 error = true;
2239 return def;
2240 }
2241 return atof(token);
2242}
2243
2244void ComponentFieldMap::CalculateElementBoundingBoxes() {
2245 // Do not proceed if not properly initialised.
2246 if (!m_ready) {
2247 PrintNotReady("CalculateElementBoundingBoxes");
2248 return;
2249 }
2250
2251 // Calculate the bounding boxes of all elements
2252 for (auto& element : m_elements) {
2253 const Node& n0 = m_nodes[element.emap[0]];
2254 const Node& n1 = m_nodes[element.emap[1]];
2255 const Node& n2 = m_nodes[element.emap[2]];
2256 const Node& n3 = m_nodes[element.emap[3]];
2257 element.bbMin[0] = std::min({n0.x, n1.x, n2.x, n3.x});
2258 element.bbMax[0] = std::max({n0.x, n1.x, n2.x, n3.x});
2259 element.bbMin[1] = std::min({n0.y, n1.y, n2.y, n3.y});
2260 element.bbMax[1] = std::max({n0.y, n1.y, n2.y, n3.y});
2261 element.bbMin[2] = std::min({n0.z, n1.z, n2.z, n3.z});
2262 element.bbMax[2] = std::max({n0.z, n1.z, n2.z, n3.z});
2263 // Add tolerances.
2264 constexpr float f = 0.2;
2265 const float tolx = f * (element.bbMax[0] - element.bbMin[0]);
2266 element.bbMin[0] -= tolx;
2267 element.bbMax[0] += tolx;
2268 const float toly = f * (element.bbMax[1] - element.bbMin[1]);
2269 element.bbMin[1] -= toly;
2270 element.bbMax[1] += toly;
2271 const float tolz = f * (element.bbMax[2] - element.bbMin[2]);
2272 element.bbMin[2] -= tolz;
2273 element.bbMax[2] += tolz;
2274 }
2275}
2276
2277bool ComponentFieldMap::InitializeTetrahedralTree() {
2278 // Do not proceed if not properly initialised.
2279 if (!m_ready) {
2280 PrintNotReady("InitializeTetrahedralTree");
2281 return false;
2282 }
2283
2284 if (m_debug) {
2285 std::cout << m_className << "::InitializeTetrahedralTree:\n"
2286 << " About to initialize the tetrahedral tree.\n";
2287 }
2288
2289 // Cache the bounding boxes if it has not been done yet.
2290 if (!m_cacheElemBoundingBoxes) CalculateElementBoundingBoxes();
2291
2292 if (m_nodes.empty()) {
2293 std::cerr << m_className << "::InitializeTetrahedralTree: Empty mesh.\n";
2294 return false;
2295 }
2296
2297 // Determine the bounding box
2298 double xmin = m_nodes.front().x;
2299 double ymin = m_nodes.front().y;
2300 double zmin = m_nodes.front().z;
2301 double xmax = xmin;
2302 double ymax = ymin;
2303 double zmax = zmin;
2304 for (const auto& node : m_nodes) {
2305 xmin = std::min(xmin, node.x);
2306 xmax = std::max(xmax, node.x);
2307 ymin = std::min(ymin, node.y);
2308 ymax = std::max(ymax, node.y);
2309 zmin = std::min(zmin, node.z);
2310 zmax = std::max(zmax, node.z);
2311 }
2312
2313 if (m_debug) {
2314 std::cout << " Bounding box:\n"
2315 << std::scientific << "\tx: " << xmin << " -> " << xmax << "\n"
2316 << std::scientific << "\ty: " << ymin << " -> " << ymax << "\n"
2317 << std::scientific << "\tz: " << zmin << " -> " << zmax << "\n";
2318 }
2319
2320 const double hx = 0.5 * (xmax - xmin);
2321 const double hy = 0.5 * (ymax - ymin);
2322 const double hz = 0.5 * (zmax - zmin);
2323 m_octree.reset(new TetrahedralTree(Vec3(xmin + hx, ymin + hy, zmin + hz),
2324 Vec3(hx, hy, hz)));
2325
2326 if (m_debug) std::cout << " Tree instantiated.\n";
2327
2328 // Insert all mesh nodes in the tree
2329 for (unsigned int i = 0; i < m_nodes.size(); i++) {
2330 const Node& n = m_nodes[i];
2331 m_octree->InsertMeshNode(Vec3(n.x, n.y, n.z), i);
2332 }
2333
2334 if (m_debug) std::cout << " Tree nodes initialized successfully.\n";
2335
2336 // Insert all mesh elements (tetrahedrons) in the tree
2337 for (unsigned int i = 0; i < m_elements.size(); i++) {
2338 const Element& e = m_elements[i];
2339 const double bb[6] = {e.bbMin[0], e.bbMin[1], e.bbMin[2],
2340 e.bbMax[0], e.bbMax[1], e.bbMax[2]};
2341 m_octree->InsertMeshElement(bb, i);
2342 }
2343
2344 std::cout << m_className << "::InitializeTetrahedralTree: Success.\n";
2345 return true;
2346}
2347
2349 const std::string& label) const {
2350 const size_t nWeightingFields = m_wfields.size();
2351 for (size_t i = 0; i < nWeightingFields; ++i) {
2352 if (m_wfields[i] == label) return i;
2353 }
2354 return nWeightingFields;
2355}
2356
2358 const std::string& label) {
2359 // Check if a weighting field with the same label already exists.
2360 size_t nWeightingFields = m_wfields.size();
2361 for (size_t i = 0; i < nWeightingFields; ++i) {
2362 if (m_wfields[i] == label) return i;
2363 }
2364 ++nWeightingFields;
2365 m_wfields.resize(nWeightingFields);
2366 m_wfieldsOk.resize(nWeightingFields);
2367 for (auto& node : m_nodes) {
2368 node.w.resize(nWeightingFields);
2369 node.dw.resize(nWeightingFields);
2370 }
2371 m_wfields.back() = label;
2372 return nWeightingFields - 1;
2373}
2374
2375void ComponentFieldMap::PrintWarning(const std::string& header) {
2376
2377 if (!m_warning || m_nWarnings > 10) return;
2378 std::cerr << m_className << "::" << header << ":\n"
2379 << " Warnings have been issued for this field map.\n";
2380 ++m_nWarnings;
2381}
2382
2383void ComponentFieldMap::PrintNotReady(const std::string& header) const {
2384 std::cerr << m_className << "::" << header << ":\n"
2385 << " Field map not yet initialised.\n";
2386}
2387
2388void ComponentFieldMap::PrintCouldNotOpen(const std::string& header,
2389 const std::string& filename) const {
2390 std::cerr << m_className << "::" << header << ":\n"
2391 << " Could not open file " << filename << " for reading.\n"
2392 << " The file perhaps does not exist.\n";
2393}
2394
2395void ComponentFieldMap::PrintElement(const std::string& header, const double x,
2396 const double y, const double z,
2397 const double t1, const double t2,
2398 const double t3, const double t4,
2399 const Element& element,
2400 const unsigned int n, const int iw) const {
2401 std::cout << m_className << "::" << header << ":\n"
2402 << " Global = (" << x << ", " << y << ", " << z << ")\n"
2403 << " Local = (" << t1 << ", " << t2 << ", " << t3 << ", " << t4
2404 << ")\n";
2405 if (element.degenerate) std::cout << " Element is degenerate.\n";
2406 std::cout << " Node x y z V\n";
2407 for (unsigned int ii = 0; ii < n; ++ii) {
2408 const Node& node = m_nodes[element.emap[ii]];
2409 const double v = iw < 0 ? node.v : node.w[iw];
2410 printf(" %-5d %12g %12g %12g %12g\n", element.emap[ii], node.x, node.y,
2411 node.z, v);
2412 }
2413}
2414} // namespace Garfield
void DriftMedium(const unsigned int imat)
Flag a field map material as a drift medium.
virtual bool GetNode(const size_t i, double &x, double &y, double &z) const
void PrintRange()
Show x, y, z, V and angular ranges.
virtual double GetElementVolume(const unsigned int i)=0
void SetMedium(const unsigned int imat, Medium *medium)
Associate a field map material with a Medium class.
void PrintMaterials()
List all currently defined materials.
bool SetDefaultDriftMedium()
Find lowest epsilon, check for eps = 0, set default drift media flags.
std::array< double, 3 > m_mapamin
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)
virtual void GetAspectRatio(const unsigned int i, double &dmin, double &dmax)=0
std::array< double, 3 > m_maxBoundingBox
std::array< double, 3 > m_mapmin
std::array< double, 3 > m_minBoundingBox
static double ScalingFactor(std::string unit)
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
int ReadInteger(char *token, int def, bool &error)
std::vector< Material > m_materials
int FindElementCube(const double x, const double y, const double z, double &t1, double &t2, double &t3, TMatrixD *&jac, std::vector< TMatrixD * > &dN)
Find the element for a point in a cube.
std::vector< bool > m_wfieldsOk
virtual ~ComponentFieldMap()
Destructor.
size_t GetWeightingFieldIndex(const std::string &label) const
std::array< double, 3 > m_mapna
double GetConductivity(const unsigned int imat) const
Return the conductivity of a field map material.
ComponentFieldMap()=delete
Default constructor.
void PrintCouldNotOpen(const std::string &header, const std::string &filename) const
int FindElement5(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 quadrilaterals.
double GetPermittivity(const unsigned int imat) const
Return the permittivity of a field map material.
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.
bool GetElementaryCell(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the coordinates of the elementary cell.
std::vector< Element > m_elements
Medium * GetMedium(const unsigned int i) const
Return the Medium associated to a field map material.
void NotDriftMedium(const unsigned int imat)
Flag a field map materials as a non-drift medium.
std::array< double, 3 > m_mapamax
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
std::array< double, 3 > m_cells
std::array< bool, 3 > m_setang
virtual void SetRange()
Calculate x, y, z, V and angular ranges.
bool GetElement(const size_t i, double &vol, double &dmin, double &dmax)
Return the volume and aspect ratio of a mesh element.
std::array< double, 3 > m_mapmax
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)
Abstract base class for components.
Definition: Component.hh:13
virtual void UpdatePeriodicity()=0
Verify periodicities.
std::array< bool, 3 > m_rotationSymmetric
Rotation symmetry around x-axis, y-axis, z-axis.
Definition: Component.hh:350
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
Definition: Component.hh:346
bool m_debug
Switch on/off debugging messages.
Definition: Component.hh:341
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition: Component.hh:344
std::string m_className
Class name.
Definition: Component.hh:329
bool m_ready
Ready for use?
Definition: Component.hh:338
std::array< bool, 3 > m_axiallyPeriodic
Axial periodicity in x, y, z.
Definition: Component.hh:348
Abstract base class for media.
Definition: Medium.hh:13
const std::string & GetName() const
Get the medium name/identifier.
Definition: Medium.hh:23
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615
DoubleAc sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:314
Definition: neBEM.h:155