Garfield++ 5.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 <TCanvas.h>
4#include <TH1F.h>
5#include <TMath.h>
6#include <math.h>
7#include <stdio.h>
8
9#include <algorithm>
10#include <fstream>
11#include <iostream>
12#include <numeric>
13#include <string>
14
16
17namespace Garfield {
18
20 : Component(name) {}
21
23
24void ComponentFieldMap::ElectricField(const double x, const double y,
25 const double z, double& ex, double& ey,
26 double& ez, double& volt, Medium*& m, int& status) {
27 ElectricField(x, y, z, ex, ey, ez, m, status);
28 volt = Potential(x, y, z, m_pot);
29}
30
31void ComponentFieldMap::ElectricField(const double xin, const double yin,
32 const double zin, double& ex, double& ey,
33 double& ez, Medium*& m, int& status) {
34 // Initial values
35 ex = ey = ez = 0.;
36 m = nullptr;
37 int iel = -1;
38 status = Field(xin, yin, zin, ex, ey, ez, iel, m_pot);
39 if (status < 0 || iel < 0) {
40 if (status == -10) PrintNotReady("ElectricField");
41 return;
42 }
43
44 const auto& element = m_elements[iel];
45 // Drift medium?
46 if (element.matmap >= m_materials.size()) {
47 if (m_debug) {
48 std::cout << m_className << "::ElectricField: "
49 << "Out-of-range material number.\n";
50 }
51 status = -5;
52 return;
53 }
54
55 const auto& mat = m_materials[element.matmap];
56 if (m_debug) {
57 std::cout << " Material " << element.matmap << ", drift flag "
58 << mat.driftmedium << ".\n";
59 }
60 m = mat.medium;
61 status = -5;
62 if (mat.driftmedium) {
63 if (m && m->IsDriftable()) status = 0;
64 }
65}
66
67int ComponentFieldMap::Field(const double xin, const double yin,
68 const double zin, double& fx, double& fy,
69 double& fz, int& imap,
70 const std::vector<double>& pot) const {
71
72 // Do not proceed if not properly initialised.
73 if (!m_ready) return -10;
74
75 // Copy the coordinates.
76 double x = xin, y = yin;
77 double z = m_is3d ? zin : 0.;
78
79 // Map the coordinates onto field map coordinates
80 bool xmirr, ymirr, zmirr;
81 double rcoordinate, rotation;
82 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
83
84 if (!m_is3d) {
85 if (zin < m_minBoundingBox[2] || zin > m_maxBoundingBox[2]) {
86 return -5;
87 }
88 }
89
90 // Find the element that contains this point.
91 double t1 = 0., t2 = 0., t3 = 0., t4 = 0.;
92 double jac[4][4];
93 double det = 0.;
94 imap = -1;
96 imap = FindElement5(x, y, t1, t2, t3, t4, jac, det);
98 imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
99 }
100 // Stop if the point is not in the mesh.
101 if (imap < 0) {
102 if (m_debug) {
103 std::cerr << m_className << "::Field: ("
104 << x << ", " << y << ", " << z << ") is not in the mesh.\n";
105 }
106 return -6;
107 }
108
109 const Element& element = m_elements[imap];
111 if (m_degenerate[imap]) {
112 std::array<double, 6> v;
113 for (size_t i = 0; i < 6; ++i) v[i] = pot[element.emap[i]];
114 Field3(v, {t1, t2, t3}, jac, det, fx, fy);
115 } else {
116 std::array<double, 8> v;
117 for (size_t i = 0; i < 8; ++i) v[i] = pot[element.emap[i]];
118 Field5(v, {t1, t2}, jac, det, fx, fy);
119 }
121 std::array<double, 10> v;
122 for (size_t i = 0; i < 10; ++i) v[i] = pot[element.emap[i]];
123 Field13(v, {t1, t2, t3, t4}, jac, 4 * det, fx, fy, fz);
124 }
125 if (m_debug) {
126 PrintElement("Field", x, y, z, t1, t2, t3, t4, imap, pot);
127 }
128 // Transform field to global coordinates.
129 UnmapFields(fx, fy, fz, x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
130 return 0;
131}
132
133double ComponentFieldMap::Potential(const double xin, const double yin,
134 const double zin,
135 const std::vector<double>& pot) const {
136
137 // Do not proceed if not properly initialised.
138 if (!m_ready) return 0.;
139
140 // Copy the coordinates.
141 double x = xin, y = yin;
142 double z = m_is3d ? zin : 0.;
143
144 // Map the coordinates onto field map coordinates.
145 bool xmirr, ymirr, zmirr;
146 double rcoordinate, rotation;
147 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
148
149 if (!m_is3d) {
150 if (zin < m_minBoundingBox[2] || zin > m_maxBoundingBox[2]) {
151 return 0.;
152 }
153 }
154
155 // Find the element that contains this point.
156 double t1 = 0., t2 = 0., t3 = 0., t4 = 0.;
157 double jac[4][4];
158 double det = 0.;
159 int imap = -1;
161 imap = FindElement5(x, y, t1, t2, t3, t4, jac, det);
163 imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
164 }
165 if (imap < 0) return 0.;
166
167 double volt = 0.;
168 const Element& element = m_elements[imap];
170 if (m_degenerate[imap]) {
171 std::array<double, 6> v;
172 for (size_t i = 0; i < 6; ++i) v[i] = pot[element.emap[i]];
173 volt = Potential3(v, {t1, t2, t3});
174 } else {
175 std::array<double, 8> v;
176 for (size_t i = 0; i < 8; ++i) v[i] = pot[element.emap[i]];
177 volt = Potential5(v, {t1, t2});
178 }
180 std::array<double, 10> v;
181 for (size_t i = 0; i < 10; ++i) v[i] = pot[element.emap[i]];
182 volt = Potential13(v, {t1, t2, t3, t4});
183 }
184 if (m_debug) {
185 PrintElement("Potential", x, y, z, t1, t2, t3, t4, imap, pot);
186 }
187 return volt;
188}
189
190void ComponentFieldMap::WeightingField(const double xin, const double yin,
191 const double zin, double& wx, double& wy,
192 double& wz, const std::string& label) {
193 // Initial values.
194 wx = wy = wz = 0;
195
196 // Do not proceed if not properly initialised.
197 if (!m_ready) return;
198
199 // Do not proceed if the requested weighting field does not exist.
200 if (m_wpot.count(label) == 0) return;
201 if (m_wpot[label].empty()) return;
202
203 int iel = -1;
204 Field(xin, yin, zin, wx, wy, wz, iel, m_wpot[label]);
205}
206
207double ComponentFieldMap::WeightingPotential(double xin, double yin, double zin,
208 const std::string& label0) {
209 // Do not proceed if not properly initialised.
210 if (!m_ready) return 0.;
211
212 // TODO! From ComponentComsol:
213 // if (!CheckInRange(xin, yin, zin)) return 0.;
214
215 std::string label = label0;
216 if (m_wfieldCopies.count(label0) > 0) {
217 label = m_wfieldCopies[label0].source;
218 TVectorD pos(3);
219 pos(0) = xin;
220 pos(1) = yin;
221 pos(2) = zin;
222 pos = m_wfieldCopies[label0].rot * pos + m_wfieldCopies[label0].trans;
223 xin = pos(0);
224 yin = pos(1);
225 zin = pos(2);
226 }
227 // Do not proceed if the requested weighting field does not exist.
228 if (m_wpot.count(label) == 0) return 0.;
229 if (m_wpot[label].empty()) return 0.;
230
231 return Potential(xin, yin, zin, m_wpot[label]);
232}
233
235 double zin,
236 const double tin,
237 const std::string& label0) {
238 if (m_wdtimes.empty()) return 0.;
239 // Assume no weighting field for times outside the range of available maps.
240 if (tin < m_wdtimes.front()) return 0.;
241 double t = tin;
242 if (tin > m_wdtimes.back()) t = m_wdtimes.back();
243
244 // Do not proceed if not properly initialised.
245 if (!m_ready) return 0.;
246
247 std::string label = label0;
248 if (m_wfieldCopies.count(label0) > 0) {
249 label = m_wfieldCopies[label0].source;
250 TVectorD pos(3);
251 pos(0) = xin;
252 pos(1) = yin;
253 pos(2) = zin;
254 pos = m_wfieldCopies[label0].rot * pos + m_wfieldCopies[label0].trans;
255 xin = pos(0);
256 yin = pos(1);
257 zin = pos(2);
258 }
259
260 // Do not proceed if the requested weighting field does not exist.
261 if (m_dwpot.count(label) == 0) return 0.;
262 if (m_dwpot[label].empty()) return 0.;
263
264 // Copy the coordinates.
265 double x = xin, y = yin, z = zin;
266
267 // Map the coordinates onto field map coordinates.
268 bool xmirr, ymirr, zmirr;
269 double rcoordinate, rotation;
270 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
271
272 if (m_warning) PrintWarning("DelayedWeightingPotential");
273
274 // Find the element that contains this point.
275 double t1, t2, t3, t4, jac[4][4], det;
276
277 int imap = -1;
279 imap = FindElement5(x, y, t1, t2, t3, t4, jac, det);
281 imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
282 }
283 if (imap < 0) return 0.;
284
285 // Linear interpolation between time slices
286 int i0;
287 int i1;
288 double f0;
289 double f1;
290
291 TimeInterpolation(t, f0, f1, i0, i1);
292
293 // Get potential value.
294 double dp0 = 0;
295 double dp1 = 0;
296 const Element& element = m_elements[imap];
298 if (m_degenerate[imap]) {
299 std::array<double, 6> v0, v1;
300 for (size_t i = 0; i < 6; ++i) {
301 v0[i] = m_dwpot[label][element.emap[i]][i0];
302 v1[i] = m_dwpot[label][element.emap[i]][i1];
303 }
304 dp0 = Potential3(v0, {t1, t2, t3});
305 dp1 = Potential3(v1, {t1, t2, t3});
306 } else {
307 std::array<double, 8> v0, v1;
308 for (size_t i = 0; i < 8; ++i) {
309 v0[i] = m_dwpot[label][element.emap[i]][i0];
310 v1[i] = m_dwpot[label][element.emap[i]][i1];
311 }
312 dp0 = Potential5(v0, {t1, t2});
313 dp1 = Potential5(v1, {t1, t2});
314 }
316 std::array<double, 10> v0, v1;
317 for (size_t i = 0; i < 10; ++i) {
318 v0[i] = m_dwpot[label][element.emap[i]][i0];
319 v1[i] = m_dwpot[label][element.emap[i]][i1];
320 }
321 dp0 = Potential13(v0, {t1, t2, t3, t4});
322 dp1 = Potential13(v1, {t1, t2, t3, t4});
323 }
324
325 return f0 * dp0 + f1 * dp1;
326}
327
328Medium* ComponentFieldMap::GetMedium(const double xin, const double yin,
329 const double zin) {
330 // Copy the coordinates.
331 double x = xin, y = yin;
332 double z = m_is3d ? zin : 0.;
333
334 // Map the coordinates onto field map coordinates.
335 bool xmirr, ymirr, zmirr;
336 double rcoordinate, rotation;
337 MapCoordinates(x, y, z, xmirr, ymirr, zmirr, rcoordinate, rotation);
338
339 if (!m_is3d) {
340 if (zin < m_minBoundingBox[2] || z > m_maxBoundingBox[2]) {
341 return nullptr;
342 }
343 }
344
345 // Do not proceed if not properly initialised.
346 if (!m_ready) {
347 PrintNotReady("GetMedium");
348 return nullptr;
349 }
350 if (m_warning) PrintWarning("GetMedium");
351
352 // Find the element that contains this point.
353 double t1 = 0., t2 = 0., t3 = 0., t4 = 0.;
354 double jac[4][4];
355 double det = 0.;
356 int imap = -1;
358 imap = FindElement5(x, y, t1, t2, t3, t4, jac, det);
360 imap = FindElement13(x, y, z, t1, t2, t3, t4, jac, det);
361 }
362 if (imap < 0) {
363 if (m_debug) {
364 std::cerr << m_className << "::GetMedium: (" << x << ", " << y << ", "
365 << z << ") is not in the mesh.\n";
366 }
367 return nullptr;
368 }
369 const Element& element = m_elements[imap];
370 if (element.matmap >= m_materials.size()) {
371 if (m_debug) {
372 std::cerr << m_className << "::GetMedium: Element " << imap
373 << " has out-of-range material number " << element.matmap
374 << ".\n";
375 }
376 return nullptr;
377 }
378 if (m_debug) {
379 PrintElement("GetMedium", x, y, z, t1, t2, t3, t4, imap, m_pot);
380 }
381
382 // Assign a medium.
383 return m_materials[element.matmap].medium;
384}
385
387 // MAPCHK
388 // Ensure there are some mesh elements.
389 if (!m_ready) {
390 PrintNotReady("Check");
391 return false;
392 }
393 // Compute the range of volumes.
394 const size_t nElements = m_elements.size();
395 double vmin = 0., vmax = 0.;
396 for (size_t i = 0; i < nElements; ++i) {
397 const double v = GetElementVolume(i);
398 if (i == 0) {
399 vmin = vmax = v;
400 } else {
401 vmin = std::min(vmin, v);
402 vmax = std::max(vmax, v);
403 }
404 }
405 // Number of bins.
406 constexpr int nBins = 100;
407 double scale = 1.;
408 std::string unit = "cm";
409 if (m_is3d) {
410 if (vmax < 1.e-9) {
411 unit = "um";
412 scale = 1.e12;
413 } else if (vmax < 1.e-3) {
414 unit = "mm";
415 scale = 1.e3;
416 }
417 } else {
418 if (vmax < 1.e-6) {
419 unit = "um";
420 scale = 1.e8;
421 } else if (vmax < 1.e-2) {
422 unit = "mm";
423 scale = 1.e2;
424 }
425 }
426 vmin *= scale;
427 vmax *= scale;
428 // Check we do have a range and round it.
429 vmin = std::max(0., vmin - 0.1 * (vmax - vmin));
430 vmax = vmax + 0.1 * (vmax - vmin);
431 if (vmin == vmax) {
432 vmin -= 1. + std::abs(vmin);
433 vmax += 1. + std::abs(vmax);
434 }
435 // CALL ROUND(SMIN,SMAX,NCHA,'LARGER,COARSER',STEP)
436 std::string title = m_is3d ? ";volume [" : ";surface [";
437 if (unit == "um") {
438 title += "#mum";
439 } else {
440 title += unit;
441 }
442 if (m_is3d) {
443 title += "^{3}];";
444 } else {
445 title += "^{2}];";
446 }
447 TH1F hElementVolume("hElementVolume", title.c_str(), nBins, vmin, vmax);
448
449 TH1F hAspectRatio("hAspectRatio", ";largest / smallest vertex distance;",
450 nBins, 0., 100.);
451
452 // Loop over all mesh elements.
453 size_t nZero = 0;
454 double rmin = 0., rmax = 0.;
455 for (size_t i = 0; i < nElements; ++i) {
456 double v = 0., dmin = 0., dmax = 0.;
457 if (!GetElement(i, v, dmin, dmax)) return false;
458 // Check for null-sizes.
459 if (dmin <= 0. && !m_degenerate[i]) {
460 std::cerr << m_className << "::Check:\n"
461 << " Found element with zero-length vertex separation.\n";
462 return false;
463 }
464 const double r = dmax / dmin;
465 hAspectRatio.Fill(r);
466 if (v <= 0.) ++nZero;
467 v *= scale;
468 hElementVolume.Fill(v);
469 // Update maxima and minima.
470 if (i == 0) {
471 vmin = vmax = v;
472 rmin = rmax = r;
473 } else {
474 vmin = std::min(vmin, v);
475 vmax = std::max(vmax, v);
476 rmin = std::min(rmin, r);
477 rmax = std::max(rmax, r);
478 }
479 }
480 if (nZero > 0) {
481 std::cerr << m_className << "::Check:\n";
482 if (m_is3d) {
483 std::cerr << " Found " << nZero << " element(s) with zero volume.\n";
484 } else {
485 std::cerr << " Found " << nZero << " element(s) with zero surface.\n";
486 }
487 }
488 TCanvas* c1 = new TCanvas("cAspectRatio", "Aspect ratio", 600, 600);
489 c1->cd();
490 hAspectRatio.DrawCopy();
491 c1->Update();
492 TCanvas* c2 = new TCanvas("cElementVolume", "Element measure", 600, 600);
493 c2->cd();
494 hElementVolume.DrawCopy();
495 c2->Update();
496
497 // Printout.
498 std::cout << m_className << "::Check:\n"
499 << " Smallest Largest\n";
500 std::printf(" Aspect ratios: %15.8f %15.8f\n", rmin, rmax);
501 if (m_is3d) {
502 std::printf(" Volumes [%s3]: %15.8f %15.8f\n", unit.c_str(), vmin,
503 vmax);
504 } else {
505 std::printf(" Surfaces [%s2]: %15.8f %15.8f\n", unit.c_str(), vmin,
506 vmax);
507 }
508 return true;
509}
510
512 // Do not proceed if not properly initialised.
513 if (!m_ready) PrintNotReady("PrintMaterials");
514
515 if (m_materials.empty()) {
516 std::cerr << m_className << "::PrintMaterials:\n"
517 << " No materials are currently defined.\n";
518 return;
519 }
520
521 const size_t nMaterials = m_materials.size();
522 std::cout << m_className << "::PrintMaterials:\n"
523 << " Currently " << nMaterials << " materials are defined.\n"
524 << " Index Permittivity Resistivity Notes\n";
525 for (size_t i = 0; i < nMaterials; ++i) {
526 printf(" %5zu %12g %12g", i, m_materials[i].eps, m_materials[i].ohm);
527 if (m_materials[i].medium) {
528 std::string name = m_materials[i].medium->GetName();
529 std::cout << " " << name;
530 if (m_materials[i].medium->IsDriftable()) std::cout << ", drift medium";
531 if (m_materials[i].medium->IsIonisable()) std::cout << ", ionisable";
532 }
533 if (m_materials[i].driftmedium) {
534 std::cout << " (drift medium)\n";
535 } else {
536 std::cout << "\n";
537 }
538 }
539}
540
541void ComponentFieldMap::DriftMedium(const size_t imat) {
542 // Do not proceed if not properly initialised.
543 if (!m_ready) PrintNotReady("DriftMedium");
544
545 // Check value
546 if (imat >= m_materials.size()) {
547 std::cerr << m_className << "::DriftMedium: Index out of range.\n";
548 return;
549 }
550
551 // Make drift medium
552 m_materials[imat].driftmedium = true;
553}
554
555void ComponentFieldMap::NotDriftMedium(const size_t imat) {
556 // Do not proceed if not properly initialised.
557 if (!m_ready) PrintNotReady("NotDriftMedium");
558
559 // Check value
560 if (imat >= m_materials.size()) {
561 std::cerr << m_className << "::NotDriftMedium: Index out of range.\n";
562 return;
563 }
564
565 // Make drift medium
566 m_materials[imat].driftmedium = false;
567}
568
569double ComponentFieldMap::GetPermittivity(const size_t imat) const {
570 if (imat >= m_materials.size()) {
571 std::cerr << m_className << "::GetPermittivity: Index out of range.\n";
572 return -1.;
573 }
574 return m_materials[imat].eps;
575}
576
577double ComponentFieldMap::GetConductivity(const size_t imat) const {
578 if (imat >= m_materials.size()) {
579 std::cerr << m_className << "::GetConductivity: Index out of range.\n";
580 return -1.;
581 }
582 return m_materials[imat].ohm;
583}
584
585void ComponentFieldMap::SetMedium(const size_t imat, Medium* medium) {
586 if (imat >= m_materials.size()) {
587 std::cerr << m_className << "::SetMedium: Index out of range.\n";
588 return;
589 }
590 if (!medium) {
591 std::cerr << m_className << "::SetMedium: Null pointer.\n";
592 return;
593 }
594 if (m_debug) {
595 std::cout << m_className << "::SetMedium: Associated material " << imat
596 << " with medium " << medium->GetName() << ".\n";
597 }
598 m_materials[imat].medium = medium;
599}
600
601Medium* ComponentFieldMap::GetMedium(const size_t imat) const {
602 if (imat >= m_materials.size()) {
603 std::cerr << m_className << "::GetMedium: Index out of range.\n";
604 return nullptr;
605 }
606 return m_materials[imat].medium;
607}
608
610 if (!medium) {
611 std::cerr << m_className << "::SetGas: Null pointer.\n";
612 return;
613 }
614 size_t nMatch = 0;
615 const size_t nMaterials = m_materials.size();
616 for (size_t i = 0; i < nMaterials; ++i) {
617 if (fabs(m_materials[i].eps - 1.) > 1.e-4) continue;
618 m_materials[i].medium = medium;
619 std::cout << m_className << "::SetGas: Associating material " << i
620 << " with " << medium->GetName() << ".\n";
621 ++nMatch;
622 }
623 if (nMatch == 0) {
624 std::cerr << m_className << "::SetGas: Found no material with eps = 1.\n";
625 }
626}
627
628bool ComponentFieldMap::GetElement(const size_t i, double& vol, double& dmin,
629 double& dmax) const {
630 if (i >= m_elements.size()) {
631 std::cerr << m_className << "::GetElement: Index out of range.\n";
632 return false;
633 }
634
635 vol = GetElementVolume(i);
636 GetAspectRatio(i, dmin, dmax);
637 return true;
638}
639
640double ComponentFieldMap::GetElementVolume(const size_t i) const {
641 if (i >= m_elements.size()) return 0.;
642
643 const Element& element = m_elements[i];
645 const Node& n0 = m_nodes[element.emap[0]];
646 const Node& n1 = m_nodes[element.emap[1]];
647 const Node& n2 = m_nodes[element.emap[2]];
648 const Node& n3 = m_nodes[element.emap[3]];
649 // Uses formula V = |a (dot) b x c|/6
650 // with a => "3", b => "1", c => "2" and origin "0"
651 const double vol = (n3.x - n0.x) * ((n1.y - n0.y) * (n2.z - n0.z) -
652 (n2.y - n0.y) * (n1.z - n0.z)) +
653 (n3.y - n0.y) * ((n1.z - n0.z) * (n2.x - n0.x) -
654 (n2.z - n0.z) * (n1.x - n0.x)) +
655 (n3.z - n0.z) * ((n1.x - n0.x) * (n2.y - n0.y) -
656 (n3.x - n0.x) * (n1.y - n0.y));
657 return fabs(vol) / 6.;
659 const Node& n0 = m_nodes[element.emap[0]];
660 const Node& n1 = m_nodes[element.emap[1]];
661 const Node& n2 = m_nodes[element.emap[2]];
662 const Node& n3 = m_nodes[element.emap[3]];
663 const double surf = 0.5 *
664 (fabs((n1.x - n0.x) * (n2.y - n0.y) - (n2.x - n0.x) * (n1.y - n0.y)) +
665 fabs((n3.x - n0.x) * (n2.y - n0.y) - (n2.x - n0.x) * (n3.y - n0.y)));
666 return surf;
667 }
668 return 0.;
669}
670
671void ComponentFieldMap::GetAspectRatio(const size_t i, double& dmin,
672 double& dmax) const {
673 if (i >= m_elements.size()) {
674 dmin = dmax = 0.;
675 return;
676 }
677
678 const Element& element = m_elements[i];
680 const int np = 4;
681 // Loop over all pairs of vertices.
682 for (int j = 0; j < np - 1; ++j) {
683 const Node& nj = m_nodes[element.emap[j]];
684 for (int k = j + 1; k < np; ++k) {
685 const Node& nk = m_nodes[element.emap[k]];
686 // Compute distance.
687 const double dx = nj.x - nk.x;
688 const double dy = nj.y - nk.y;
689 const double dz = nj.z - nk.z;
690 const double dist = sqrt(dx * dx + dy * dy + dz * dz);
691 if (k == 1) {
692 dmin = dmax = dist;
693 } else {
694 if (dist < dmin) dmin = dist;
695 if (dist > dmax) dmax = dist;
696 }
697 }
698 }
700 const int np = 8;
701 // Loop over all pairs of vertices.
702 for (int j = 0; j < np - 1; ++j) {
703 const Node& nj = m_nodes[element.emap[j]];
704 for (int k = j + 1; k < np; ++k) {
705 const Node& nk = m_nodes[element.emap[k]];
706 // Compute distance.
707 const double dx = nj.x - nk.x;
708 const double dy = nj.y - nk.y;
709 const double dist = sqrt(dx * dx + dy * dy);
710 if (k == 1) {
711 dmin = dmax = dist;
712 } else {
713 if (dist < dmin) dmin = dist;
714 if (dist > dmax) dmax = dist;
715 }
716 }
717 }
718 }
719}
720
721bool ComponentFieldMap::GetElement(const size_t i, size_t& mat, bool& drift,
722 std::vector<size_t>& nodes) const {
723 if (i >= m_elements.size()) {
724 std::cerr << m_className << "::GetElement: Index out of range.\n";
725 return false;
726 }
727 const auto& element = m_elements[i];
728 mat = element.matmap;
729 drift = m_materials[mat].driftmedium;
730 size_t nNodes = 4;
732 nNodes = 3;
733 }
734 nodes.resize(nNodes);
735 for (size_t j = 0; j < nNodes; ++j) nodes[j] = element.emap[j];
736 return true;
737}
738
739bool ComponentFieldMap::GetNode(const size_t i, double& x, double& y,
740 double& z) const {
741 if (i >= m_nodes.size()) {
742 std::cerr << m_className << "::GetNode: Index out of range.\n";
743 return false;
744 }
745 x = m_nodes[i].x;
746 y = m_nodes[i].y;
747 z = m_nodes[i].z;
748 return true;
749}
750
751double ComponentFieldMap::GetPotential(const size_t i) const {
752 return i >= m_pot.size() ? 0. : m_pot[i];
753}
754
756 // Find lowest epsilon and set drift medium flags.
757 const size_t nMaterials = m_materials.size();
758 double epsmin = -1;
759 size_t iepsmin = 0;
760 for (size_t i = 0; i < nMaterials; ++i) {
761 m_materials[i].driftmedium = false;
762 if (m_materials[i].eps < 0) continue;
763 // Check for eps == 0.
764 if (m_materials[i].eps == 0) {
765 std::cerr << m_className << "::SetDefaultDriftMedium:\n"
766 << " Material " << i << " has zero permittivity.\n";
767 m_materials[i].eps = -1.;
768 } else if (epsmin < 0. || epsmin > m_materials[i].eps) {
769 epsmin = m_materials[i].eps;
770 iepsmin = i;
771 }
772 }
773 if (epsmin < 0.) {
774 std::cerr << m_className << "::SetDefaultDriftMedium:\n"
775 << " Found no material with positive permittivity.\n";
776 return false;
777 }
778 m_materials[iepsmin].driftmedium = true;
779 return true;
780}
781
782double ComponentFieldMap::Potential3(const std::array<double, 6>& v,
783 const std::array<double, 3>& t) {
784 double sum = 0.;
785 for (size_t i = 0; i < 3; ++i) {
786 sum += v[i] * t[i] * (2 * t[i] - 1);
787 }
788 sum += 4 * (v[3] * t[0] * t[1] + v[4] * t[0] * t[2] + v[5] * t[1] * t[2]);
789 return sum;
790}
791
792void ComponentFieldMap::Field3(const std::array<double, 6>& v,
793 const std::array<double, 3>& t, double jac[4][4],
794 const double det, double& ex, double& ey) {
795 std::array<double, 3> g;
796 g[0] = v[0] * (4 * t[0] - 1) + v[3] * 4 * t[1] + v[4] * 4 * t[2];
797 g[1] = v[1] * (4 * t[1] - 1) + v[3] * 4 * t[0] + v[5] * 4 * t[2];
798 g[2] = v[2] * (4 * t[2] - 1) + v[4] * 4 * t[0] + v[5] * 4 * t[1];
799 const double invdet = 1. / det;
800 ex = -(jac[0][1] * g[0] + jac[1][1] * g[1] + jac[2][1] * g[2]) * invdet;
801 ey = -(jac[0][2] * g[0] + jac[1][2] * g[1] + jac[2][2] * g[2]) * invdet;
802}
803
804double ComponentFieldMap::Potential5(const std::array<double, 8>& v,
805 const std::array<double, 2>& t) {
806 return -v[0] * (1 - t[0]) * (1 - t[1]) * (1 + t[0] + t[1]) * 0.25 -
807 v[1] * (1 + t[0]) * (1 - t[1]) * (1 - t[0] + t[1]) * 0.25 -
808 v[2] * (1 + t[0]) * (1 + t[1]) * (1 - t[0] - t[1]) * 0.25 -
809 v[3] * (1 - t[0]) * (1 + t[1]) * (1 + t[0] - t[1]) * 0.25 +
810 v[4] * (1 - t[0]) * (1 + t[0]) * (1 - t[1]) * 0.5 +
811 v[5] * (1 + t[0]) * (1 + t[1]) * (1 - t[1]) * 0.5 +
812 v[6] * (1 - t[0]) * (1 + t[0]) * (1 + t[1]) * 0.5 +
813 v[7] * (1 - t[0]) * (1 + t[1]) * (1 - t[1]) * 0.5;
814}
815
816void ComponentFieldMap::Field5(const std::array<double, 8>& v,
817 const std::array<double, 2>& t, double jac[4][4],
818 const double det, double& ex, double& ey) {
819 std::array<double, 2> g;
820 g[0] = (v[0] * (1 - t[1]) * (2 * t[0] + t[1]) +
821 v[1] * (1 - t[1]) * (2 * t[0] - t[1]) +
822 v[2] * (1 + t[1]) * (2 * t[0] + t[1]) +
823 v[3] * (1 + t[1]) * (2 * t[0] - t[1])) *
824 0.25 +
825 v[4] * t[0] * (t[1] - 1) + v[5] * (1 - t[1]) * (1 + t[1]) * 0.5 -
826 v[6] * t[0] * (1 + t[1]) + v[7] * (t[1] - 1) * (t[1] + 1) * 0.5;
827 g[1] = (v[0] * (1 - t[0]) * (t[0] + 2 * t[1]) -
828 v[1] * (1 + t[0]) * (t[0] - 2 * t[1]) +
829 v[2] * (1 + t[0]) * (t[0] + 2 * t[1]) -
830 v[3] * (1 - t[0]) * (t[0] - 2 * t[1])) *
831 0.25 +
832 v[4] * (t[0] - 1) * (t[0] + 1) * 0.5 - v[5] * (1 + t[0]) * t[1] +
833 v[6] * (1 - t[0]) * (1 + t[0]) * 0.5 + v[7] * (t[0] - 1) * t[1];
834 const double invdet = 1. / det;
835 ex = -(g[0] * jac[0][0] + g[1] * jac[1][0]) * invdet;
836 ey = -(g[0] * jac[0][1] + g[1] * jac[1][1]) * invdet;
837}
838
839double ComponentFieldMap::Potential13(const std::array<double, 10>& v,
840 const std::array<double, 4>& t) {
841 double sum = 0.;
842 for (size_t i = 0; i < 4; ++i) {
843 sum += v[i] * t[i] * (t[i] - 0.5);
844 }
845 sum *= 2;
846 sum += 4 * (v[4] * t[0] * t[1] + v[5] * t[0] * t[2] + v[6] * t[0] * t[3] +
847 v[7] * t[1] * t[2] + v[8] * t[1] * t[3] + v[9] * t[2] * t[3]);
848 return sum;
849}
850
851void ComponentFieldMap::Field13(const std::array<double, 10>& v,
852 const std::array<double, 4>& t,
853 double jac[4][4], const double det, double& ex,
854 double& ey, double& ez) {
855 std::array<double, 4> g;
856 g[0] = v[0] * (t[0] - 0.25) + v[4] * t[1] + v[5] * t[2] + v[6] * t[3];
857 g[1] = v[1] * (t[1] - 0.25) + v[4] * t[0] + v[7] * t[2] + v[8] * t[3];
858 g[2] = v[2] * (t[2] - 0.25) + v[5] * t[0] + v[7] * t[1] + v[9] * t[3];
859 g[3] = v[3] * (t[3] - 0.25) + v[6] * t[0] + v[8] * t[1] + v[9] * t[2];
860 std::array<double, 3> f = {0., 0., 0.};
861 for (size_t j = 0; j < 4; ++j) {
862 for (size_t i = 0; i < 3; ++i) {
863 f[i] += g[j] * jac[j][i + 1];
864 }
865 }
866 ex = -f[0] * det;
867 ey = -f[1] * det;
868 ez = -f[2] * det;
869}
870
871int ComponentFieldMap::FindElement5(const double x, const double y,
872 double& t1, double& t2,
873 double& t3, double& t4, double jac[4][4],
874 double& det) const {
875 // Backup
876 double jacbak[4][4], detbak = 1.;
877 double t1bak = 0., t2bak = 0., t3bak = 0., t4bak = 0.;
878 int imapbak = -1;
879
880 // Initial values.
881 t1 = t2 = t3 = t4 = 0;
882
883 // Verify the count of volumes that contain the point.
884 int nfound = 0;
885 int imap = -1;
886 std::array<double, 8> xn;
887 std::array<double, 8> yn;
888 const auto& elements = (m_useTetrahedralTree && m_octree) ?
889 m_octree->GetElementsInBlock(Vec3(x, y, 0.)) :
891 for (const auto i : elements) {
892 if (x < m_bbMin[i][0] || y < m_bbMin[i][1] ||
893 x > m_bbMax[i][0] || y > m_bbMax[i][1]) continue;
894 const Element& element = m_elements[i];
895 if (m_degenerate[i]) {
896 // Degenerate element
897 for (size_t j = 0; j < 6; ++j) {
898 const auto& node = m_nodes[element.emap[j]];
899 xn[j] = node.x;
900 yn[j] = node.y;
901 }
902 if (Coordinates3(x, y, t1, t2, t3, t4, jac, det, xn, yn) != 0) {
903 continue;
904 }
905 if (t1 < 0 || t1 > 1 || t2 < 0 || t2 > 1 || t3 < 0 || t3 > 1) continue;
906 } else {
907 // Non-degenerate element
908 for (size_t j = 0; j < 8; ++j) {
909 const auto& node = m_nodes[element.emap[j]];
910 xn[j] = node.x;
911 yn[j] = node.y;
912 }
913 if (Coordinates5(x, y, t1, t2, t3, t4, jac, det, xn, yn) != 0) {
914 continue;
915 }
916 if (t1 < -1 || t1 > 1 || t2 < -1 || t2 > 1) continue;
917 }
918 ++nfound;
919 imap = i;
920 if (m_debug) {
921 std::cout << m_className << "::FindElement5:\n";
922 if (m_degenerate[i]) {
923 std::cout << " Found matching degenerate element ";
924 } else {
925 std::cout << " Found matching non-degenerate element ";
926 }
927 std::cout << i << ".\n";
928 }
929 if (!m_checkMultipleElement) return i;
930 for (int j = 0; j < 4; ++j) {
931 for (int k = 0; k < 4; ++k) jacbak[j][k] = jac[j][k];
932 }
933 detbak = det;
934 t1bak = t1;
935 t2bak = t2;
936 t3bak = t3;
937 t4bak = t4;
938 imapbak = imap;
939 }
940
941 // In checking mode, verify the element count.
942 if (m_checkMultipleElement) {
943 if (nfound < 1) {
944 if (m_debug) {
945 std::cout << m_className << "::FindElement5:\n"
946 << " No element matching point (" << x << ", " << y
947 << ") found.\n";
948 }
949 return -1;
950 }
951 if (nfound > 1) {
952 std::cout << m_className << "::FindElement5:\n"
953 << " Found " << nfound << " elements matching point ("
954 << x << ", " << y << ").\n";
955 }
956 for (int j = 0; j < 4; ++j) {
957 for (int k = 0; k < 4; ++k) jac[j][k] = jacbak[j][k];
958 }
959 det = detbak;
960 t1 = t1bak;
961 t2 = t2bak;
962 t3 = t3bak;
963 t4 = t4bak;
964 imap = imapbak;
965 return imap;
966 }
967
968 if (m_debug) {
969 std::cout << m_className << "::FindElement5:\n"
970 << " No element matching point (" << x << ", " << y
971 << ") found.\n";
972 }
973 return -1;
974}
975
977 const double x, const double y, const double z,
978 double& t1, double& t2, double& t3, double& t4,
979 double jac[4][4], double& det) const {
980
981 // Backup
982 double jacbak[4][4];
983 double detbak = 1.;
984 double t1bak = 0., t2bak = 0., t3bak = 0., t4bak = 0.;
985 int imapbak = -1;
986
987 // Initial values.
988 t1 = t2 = t3 = t4 = 0.;
989
990 // Verify the count of volumes that contain the point.
991 int nfound = 0;
992 int imap = -1;
993 std::array<double, 10> xn;
994 std::array<double, 10> yn;
995 std::array<double, 10> zn;
996 const auto& elements = (m_useTetrahedralTree && m_octree) ?
997 m_octree->GetElementsInBlock(Vec3(x, y, z)) : m_elementIndices;
998 for (const auto i : elements) {
999 if (x < m_bbMin[i][0] || y < m_bbMin[i][1] || z < m_bbMin[i][2] ||
1000 x > m_bbMax[i][0] || y > m_bbMax[i][1] || z > m_bbMax[i][2]) {
1001 continue;
1002 }
1003 for (size_t j = 0; j < 10; ++j) {
1004 const auto& node = m_nodes[m_elements[i].emap[j]];
1005 xn[j] = node.x;
1006 yn[j] = node.y;
1007 zn[j] = node.z;
1008 }
1009 if (Coordinates13(x, y, z, t1, t2, t3, t4, jac, det, xn, yn, zn, m_w12[i]) != 0) {
1010 continue;
1011 }
1012 if (t1 < 0 || t1 > 1 || t2 < 0 || t2 > 1 || t3 < 0 || t3 > 1 || t4 < 0 ||
1013 t4 > 1) {
1014 continue;
1015 }
1016 if (m_debug) {
1017 std::cout << m_className << "::FindElement13:\n"
1018 << " Found matching element " << i << ".\n";
1019 }
1020 if (!m_checkMultipleElement) return i;
1021 ++nfound;
1022 imap = i;
1023 for (int j = 0; j < 4; ++j) {
1024 for (int k = 0; k < 4; ++k) jacbak[j][k] = jac[j][k];
1025 }
1026 detbak = det;
1027 t1bak = t1;
1028 t2bak = t2;
1029 t3bak = t3;
1030 t4bak = t4;
1031 imapbak = imap;
1032 }
1033
1034 // In checking mode, verify the tetrahedron/triangle count.
1035 if (m_checkMultipleElement) {
1036 if (nfound < 1) {
1037 if (m_debug) {
1038 std::cout << m_className << "::FindElement13:\n"
1039 << " No element matching point ("
1040 << x << ", " << y << ", " << z << ") found.\n";
1041 }
1042 return -1;
1043 }
1044 if (nfound > 1) {
1045 std::cerr << m_className << "::FindElement13:\n"
1046 << " Found << " << nfound << " elements matching point ("
1047 << x << ", " << y << ", " << z << ").\n";
1048 }
1049 for (int j = 0; j < 4; ++j) {
1050 for (int k = 0; k < 4; ++k) jac[j][k] = jacbak[j][k];
1051 }
1052 det = detbak;
1053 t1 = t1bak;
1054 t2 = t2bak;
1055 t3 = t3bak;
1056 t4 = t4bak;
1057 imap = imapbak;
1058 return imap;
1059 }
1060 if (m_debug) {
1061 std::cout << m_className << "::FindElement13:\n"
1062 << " No element matching point (" << x << ", " << y << ", "
1063 << z << ") found.\n";
1064 }
1065 return -1;
1066}
1067
1068int ComponentFieldMap::FindElementCube(const double x, const double y,
1069 const double z, double& t1, double& t2,
1070 double& t3, TMatrixD*& jac,
1071 std::vector<TMatrixD*>& dN) const {
1072 int imap = -1;
1073 const size_t nElements = m_elements.size();
1074 for (size_t i = 0; i < nElements; ++i) {
1075 const Element& element = m_elements[i];
1076 const Node& n3 = m_nodes[element.emap[3]];
1077 if (x < n3.x || y < n3.y || z < n3.z) continue;
1078 const Node& n0 = m_nodes[element.emap[0]];
1079 const Node& n2 = m_nodes[element.emap[2]];
1080 const Node& n7 = m_nodes[element.emap[7]];
1081 if (x < n0.x && y < n2.y && z < n7.z) {
1082 imap = i;
1083 break;
1084 }
1085 }
1086
1087 if (imap < 0) {
1088 if (m_debug) {
1089 std::cout << m_className << "::FindElementCube:\n"
1090 << " Point (" << x << "," << y << "," << z
1091 << ") not in the mesh, it is background or PEC.\n";
1092 const Node& first0 = m_nodes[m_elements.front().emap[0]];
1093 const Node& first2 = m_nodes[m_elements.front().emap[2]];
1094 const Node& first3 = m_nodes[m_elements.front().emap[3]];
1095 const Node& first7 = m_nodes[m_elements.front().emap[7]];
1096 std::cout << " First node (" << first3.x << "," << first3.y << ","
1097 << first3.z << ") in the mesh.\n";
1098 std::cout << " dx= " << (first0.x - first3.x)
1099 << ", dy= " << (first2.y - first3.y)
1100 << ", dz= " << (first7.z - first3.z) << "\n";
1101 const Node& last0 = m_nodes[m_elements.back().emap[0]];
1102 const Node& last2 = m_nodes[m_elements.back().emap[2]];
1103 const Node& last3 = m_nodes[m_elements.back().emap[3]];
1104 const Node& last5 = m_nodes[m_elements.back().emap[5]];
1105 const Node& last7 = m_nodes[m_elements.back().emap[7]];
1106 std::cout << " Last node (" << last5.x << "," << last5.y << ","
1107 << last5.z << ") in the mesh.\n";
1108 std::cout << " dx= " << (last0.x - last3.x)
1109 << ", dy= " << (last2.y - last3.y)
1110 << ", dz= " << (last7.z - last3.z) << "\n";
1111 }
1112 return -1;
1113 }
1114 CoordinatesCube(x, y, z, t1, t2, t3, jac, dN, m_elements[imap]);
1115 return imap;
1116}
1117
1118void ComponentFieldMap::Jacobian3(
1119 const std::array<double, 8>& xn,
1120 const std::array<double, 8>& yn,
1121 const double u, const double v, const double w,
1122 double& det, double jac[4][4]) {
1123 // Shorthands.
1124 const double fouru = 4 * u;
1125 const double fourv = 4 * v;
1126 const double fourw = 4 * w;
1127
1128 const double j10 = (-1 + fouru) * xn[0] + fourv * xn[3] + fourw * xn[4];
1129 const double j20 = (-1 + fouru) * yn[0] + fourv * yn[3] + fourw * yn[4];
1130 const double j11 = (-1 + fourv) * xn[1] + fouru * xn[3] + fourw * xn[5];
1131 const double j21 = (-1 + fourv) * yn[1] + fouru * yn[3] + fourw * yn[5];
1132 const double j12 = (-1 + fourw) * xn[2] + fouru * xn[4] + fourv * xn[5];
1133 const double j22 = (-1 + fourw) * yn[2] + fouru * yn[4] + fourv * yn[5];
1134 // Determinant of the quadratic triangular Jacobian
1135 det = -(j11 - j12) * j20 - (j10 - j11) * j22 + (j10 - j12) * j21;
1136
1137 // Terms of the quadratic triangular Jacobian
1138 jac[0][0] = j11 * j22 - j12 * j21;
1139 jac[0][1] = j21 - j22;
1140 jac[0][2] = j12 - j11;
1141 jac[1][0] = j12 * j20 - j10 * j22;
1142 jac[1][1] = j22 - j20;
1143 jac[1][2] = j10 - j12;
1144 jac[2][0] = j10 * j21 - j11 * j20;
1145 jac[2][1] = j20 - j21;
1146 jac[2][2] = j11 - j10;
1147}
1148
1149void ComponentFieldMap::Jacobian5(
1150 const std::array<double, 8>& xn,
1151 const std::array<double, 8>& yn,
1152 const double u, const double v, double& det, double jac[4][4]) {
1153 // Jacobian terms
1154 const double g0 = (1 - u) * (2 * v + u);
1155 const double g1 = (1 + u) * (2 * v - u);
1156 const double g2 = (1 + u) * (2 * v + u);
1157 const double g3 = (1 - u) * (2 * v - u);
1158 const double g4 = (1 - u) * (1 + u);
1159 const double g5 = (1 + u) * v;
1160 const double g7 = (1 - u) * v;
1161 jac[0][0] = 0.25 * (g0 * yn[0] + g1 * yn[1] + g2 * yn[2] + g3 * yn[3]) -
1162 0.5 * g4 * yn[4] - g5 * yn[5] +
1163 0.5 * g4 * yn[6] - g7 * yn[7];
1164 jac[0][1] = -0.25 * (g0 * xn[0] + g1 * xn[1] + g2 * xn[2] + g3 * xn[3]) +
1165 0.5 * g4 * xn[4] + g5 * xn[5] -
1166 0.5 * g4 * xn[6] + g7 * xn[7];
1167 const double h0 = (1 - v) * (2 * u + v);
1168 const double h1 = (1 - v) * (2 * u - v);
1169 const double h2 = (1 + v) * (2 * u + v);
1170 const double h3 = (1 + v) * (2 * u - v);
1171 const double h4 = (1 - v) * u;
1172 const double h5 = (1 - v) * (1 + v);
1173 const double h6 = (1 + v) * u;
1174 jac[1][0] = -0.25 * (h0 * yn[0] + h1 * yn[1] + h2 * yn[2] + h3 * yn[3]) +
1175 h4 * yn[4] - 0.5 * h5 * yn[5] +
1176 h6 * yn[6] + 0.5 * h5 * yn[7];
1177 jac[1][1] = 0.25 * (h0 * xn[0] + h1 * xn[1] + h2 * xn[2] + h3 * xn[3]) -
1178 h4 * xn[4] + 0.5 * h5 * xn[5] -
1179 h6 * xn[6] - 0.5 * h5 * xn[7];
1180
1181 // Determinant.
1182 det = jac[0][0] * jac[1][1] - jac[0][1] * jac[1][0];
1183}
1184
1185void ComponentFieldMap::Jacobian13(
1186 const std::array<double, 10>& xn,
1187 const std::array<double, 10>& yn,
1188 const std::array<double, 10>& zn,
1189 const double fourt0, const double fourt1,
1190 const double fourt2, const double fourt3,
1191 double& det, double jac[4][4]) {
1192
1193 const double fourt0m1 = fourt0 - 1.;
1194 const double j10 = fourt0m1 * xn[0] + fourt1 * xn[4] + fourt2 * xn[5] + fourt3 * xn[6];
1195 const double j20 = fourt0m1 * yn[0] + fourt1 * yn[4] + fourt2 * yn[5] + fourt3 * yn[6];
1196 const double j30 = fourt0m1 * zn[0] + fourt1 * zn[4] + fourt2 * zn[5] + fourt3 * zn[6];
1197
1198 const double fourt1m1 = fourt1 - 1.;
1199 const double j11 = fourt1m1 * xn[1] + fourt0 * xn[4] + fourt2 * xn[7] + fourt3 * xn[8];
1200 const double j21 = fourt1m1 * yn[1] + fourt0 * yn[4] + fourt2 * yn[7] + fourt3 * yn[8];
1201 const double j31 = fourt1m1 * zn[1] + fourt0 * zn[4] + fourt2 * zn[7] + fourt3 * zn[8];
1202
1203 const double fourt2m1 = fourt2 - 1.;
1204 const double j12 = fourt2m1 * xn[2] + fourt0 * xn[5] + fourt1 * xn[7] + fourt3 * xn[9];
1205 const double j22 = fourt2m1 * yn[2] + fourt0 * yn[5] + fourt1 * yn[7] + fourt3 * yn[9];
1206 const double j32 = fourt2m1 * zn[2] + fourt0 * zn[5] + fourt1 * zn[7] + fourt3 * zn[9];
1207
1208 const double fourt3m1 = fourt3 - 1.;
1209 const double j13 = fourt3m1 * xn[3] + fourt0 * xn[6] + fourt1 * xn[8] + fourt2 * xn[9];
1210 const double j23 = fourt3m1 * yn[3] + fourt0 * yn[6] + fourt1 * yn[8] + fourt2 * yn[9];
1211 const double j33 = fourt3m1 * zn[3] + fourt0 * zn[6] + fourt1 * zn[8] + fourt2 * zn[9];
1212
1213 const double a1 = j10 * j21 - j20 * j11;
1214 const double a2 = j10 * j22 - j20 * j12;
1215 const double a3 = j10 * j23 - j20 * j13;
1216 const double a4 = j11 * j22 - j21 * j12;
1217 const double a5 = j11 * j23 - j21 * j13;
1218 const double a6 = j12 * j23 - j22 * j13;
1219
1220 const double d1011 = j10 - j11;
1221 const double d1012 = j10 - j12;
1222 const double d1013 = j10 - j13;
1223 const double d1112 = j11 - j12;
1224 const double d1113 = j11 - j13;
1225 const double d1213 = j12 - j13;
1226
1227 const double d2021 = j20 - j21;
1228 const double d2022 = j20 - j22;
1229 const double d2023 = j20 - j23;
1230 const double d2122 = j21 - j22;
1231 const double d2123 = j21 - j23;
1232 const double d2223 = j22 - j23;
1233
1234 jac[0][0] = -a5 * j32 + a4 * j33 + a6 * j31;
1235 jac[0][1] = -d2123 * j32 + d2122 * j33 + d2223 * j31;
1236 jac[0][2] = d1113 * j32 - d1112 * j33 - d1213 * j31;
1237 jac[0][3] = -d1113 * j22 + d1112 * j23 + d1213 * j21;
1238
1239 jac[1][0] = -a6 * j30 + a3 * j32 - a2 * j33;
1240 jac[1][1] = -d2223 * j30 + d2023 * j32 - d2022 * j33;
1241 jac[1][2] = d1213 * j30 - d1013 * j32 + d1012 * j33;
1242 jac[1][3] = -d1213 * j20 + d1013 * j22 - d1012 * j23;
1243
1244 jac[2][0] = a5 * j30 + a1 * j33 - a3 * j31;
1245 jac[2][1] = d2123 * j30 + d2021 * j33 - d2023 * j31;
1246 jac[2][2] = -d1113 * j30 - d1011 * j33 + d1013 * j31;
1247 jac[2][3] = d1113 * j20 + d1011 * j23 - d1013 * j21;
1248
1249 jac[3][0] = -a4 * j30 - a1 * j32 + a2 * j31;
1250 jac[3][1] = -d2122 * j30 - d2021 * j32 + d2022 * j31;
1251 jac[3][2] = d1112 * j30 + d1011 * j32 - d1012 * j31;
1252 jac[3][3] = -d1112 * j20 - d1011 * j22 + d1012 * j21;
1253
1254 det = 1. / (jac[0][3] * j30 + jac[1][3] * j31 + jac[2][3] * j32 + jac[3][3] * j33);
1255}
1256
1257void ComponentFieldMap::JacobianCube(const Element& element, const double t1,
1258 const double t2, const double t3,
1259 TMatrixD*& jac,
1260 std::vector<TMatrixD*>& dN) const {
1261 if (!jac) {
1262 std::cerr << m_className << "::JacobianCube:\n";
1263 std::cerr << " Pointer to Jacobian matrix is empty!\n";
1264 return;
1265 }
1266 dN.clear();
1267
1268 // Here the partial derivatives of the 8 shaping functions are calculated
1269 double N1[3] = {-1 * (1 - t2) * (1 - t3), (1 - t1) * -1 * (1 - t3),
1270 (1 - t1) * (1 - t2) * -1};
1271 double N2[3] = {+1 * (1 - t2) * (1 - t3), (1 + t1) * -1 * (1 - t3),
1272 (1 + t1) * (1 - t2) * -1};
1273 double N3[3] = {+1 * (1 + t2) * (1 - t3), (1 + t1) * +1 * (1 - t3),
1274 (1 + t1) * (1 + t2) * -1};
1275 double N4[3] = {-1 * (1 + t2) * (1 - t3), (1 - t1) * +1 * (1 - t3),
1276 (1 - t1) * (1 + t2) * -1};
1277 double N5[3] = {-1 * (1 - t2) * (1 + t3), (1 - t1) * -1 * (1 + t3),
1278 (1 - t1) * (1 - t2) * +1};
1279 double N6[3] = {+1 * (1 - t2) * (1 + t3), (1 + t1) * -1 * (1 + t3),
1280 (1 + t1) * (1 - t2) * +1};
1281 double N7[3] = {+1 * (1 + t2) * (1 + t3), (1 + t1) * +1 * (1 + t3),
1282 (1 + t1) * (1 + t2) * +1};
1283 double N8[3] = {-1 * (1 + t2) * (1 + t3), (1 - t1) * +1 * (1 + t3),
1284 (1 - t1) * (1 + t2) * +1};
1285 // Partial derivatives are stored in dN
1286 TMatrixD* m_N1 = new TMatrixD(3, 1, N1);
1287 *m_N1 = (1. / 8. * (*m_N1));
1288 dN.push_back(m_N1);
1289 TMatrixD* m_N2 = new TMatrixD(3, 1, N2);
1290 *m_N2 = (1. / 8. * (*m_N2));
1291 dN.push_back(m_N2);
1292 TMatrixD* m_N3 = new TMatrixD(3, 1, N3);
1293 *m_N3 = (1. / 8. * (*m_N3));
1294 dN.push_back(m_N3);
1295 TMatrixD* m_N4 = new TMatrixD(3, 1, N4);
1296 *m_N4 = (1. / 8. * (*m_N4));
1297 dN.push_back(m_N4);
1298 TMatrixD* m_N5 = new TMatrixD(3, 1, N5);
1299 *m_N5 = (1. / 8. * (*m_N5));
1300 dN.push_back(m_N5);
1301 TMatrixD* m_N6 = new TMatrixD(3, 1, N6);
1302 *m_N6 = (1. / 8. * (*m_N6));
1303 dN.push_back(m_N6);
1304 TMatrixD* m_N7 = new TMatrixD(3, 1, N7);
1305 *m_N7 = (1. / 8. * (*m_N7));
1306 dN.push_back(m_N7);
1307 TMatrixD* m_N8 = new TMatrixD(3, 1, N8);
1308 *m_N8 = (1. / 8. * (*m_N8));
1309 dN.push_back(m_N8);
1310 // Calculation of the jacobian using dN
1311 for (int j = 0; j < 8; ++j) {
1312 const Node& node = m_nodes[element.emap[j]];
1313 (*jac)(0, 0) += node.x * ((*dN.at(j))(0, 0));
1314 (*jac)(0, 1) += node.y * ((*dN.at(j))(0, 0));
1315 (*jac)(0, 2) += node.z * ((*dN.at(j))(0, 0));
1316 (*jac)(1, 0) += node.x * ((*dN.at(j))(1, 0));
1317 (*jac)(1, 1) += node.y * ((*dN.at(j))(1, 0));
1318 (*jac)(1, 2) += node.z * ((*dN.at(j))(1, 0));
1319 (*jac)(2, 0) += node.x * ((*dN.at(j))(2, 0));
1320 (*jac)(2, 1) += node.y * ((*dN.at(j))(2, 0));
1321 (*jac)(2, 2) += node.z * ((*dN.at(j))(2, 0));
1322 }
1323
1324 // compute determinant
1325 if (m_debug) {
1326 std::cout << m_className << "::JacobianCube:" << std::endl;
1327 std::cout << " Det.: " << jac->Determinant() << std::endl;
1328 std::cout << " Jacobian matrix.: " << std::endl;
1329 jac->Print("%11.10g");
1330 std::cout << " Hexahedral coordinates (t, u, v) = (" << t1 << "," << t2
1331 << "," << t3 << ")" << std::endl;
1332 std::cout << " Node xyzV" << std::endl;
1333 for (int j = 0; j < 8; ++j) {
1334 const Node& node = m_nodes[element.emap[j]];
1335 std::cout << " " << element.emap[j] << " " << node.x
1336 << " " << node.y << " " << node.z << " "
1337 << m_pot[element.emap[j]] << std::endl;
1338 }
1339 }
1340}
1341
1342int ComponentFieldMap::Coordinates3(
1343 const double x, const double y,
1344 double& t1, double& t2, double& t3, double& t4,
1345 double jac[4][4], double& det,
1346 const std::array<double, 8>& xn,
1347 const std::array<double, 8>& yn) const {
1348 if (m_debug) {
1349 std::cout << m_className << "::Coordinates3:\n"
1350 << " Point (" << x << ", " << y << ")\n";
1351 }
1352
1353 // Provisional values
1354 t1 = t2 = t3 = t4 = 0;
1355
1356 // Make a first order approximation, using the linear triangle.
1357 const double d1 =
1358 (xn[0] - xn[1]) * (yn[2] - yn[1]) -
1359 (xn[2] - xn[1]) * (yn[0] - yn[1]);
1360 const double d2 =
1361 (xn[1] - xn[2]) * (yn[0] - yn[2]) -
1362 (xn[0] - xn[2]) * (yn[1] - yn[2]);
1363 const double d3 =
1364 (xn[2] - xn[0]) * (yn[1] - yn[0]) -
1365 (xn[1] - xn[0]) * (yn[2] - yn[0]);
1366 if (d1 == 0 || d2 == 0 || d3 == 0) {
1367 std::cerr << m_className << "::Coordinates3:\n"
1368 << " Calculation of linear coordinates failed; abandoned.\n";
1369 return 1;
1370 }
1371 t1 = ((x - xn[1]) * (yn[2] - yn[1]) -
1372 (y - yn[1]) * (xn[2] - xn[1])) / d1;
1373 t2 = ((x - xn[2]) * (yn[0] - yn[2]) -
1374 (y - yn[2]) * (xn[0] - xn[2])) / d2;
1375 t3 = ((x - xn[0]) * (yn[1] - yn[0]) -
1376 (y - yn[0]) * (xn[1] - xn[0])) / d3;
1377
1378 // Start iterative refinement.
1379 double td1 = t1, td2 = t2, td3 = t3;
1380 bool converged = false;
1381 std::array<double, 6> f;
1382 for (int iter = 0; iter < 10; iter++) {
1383 if (m_debug) {
1384 std::cout << m_className << "::Coordinates3:\n";
1385 std::cout << " Iteration " << iter << ": (u, v, w) = (" << td1
1386 << ", " << td2 << ", " << td3 << "), sum = " << td1 + td2 + td3
1387 << "\n";
1388 }
1389 // Evaluate the shape functions.
1390 f[0] = td1 * (2 * td1 - 1);
1391 f[1] = td2 * (2 * td2 - 1);
1392 f[2] = td3 * (2 * td3 - 1);
1393 f[3] = 4 * td1 * td2;
1394 f[4] = 4 * td1 * td3;
1395 f[5] = 4 * td2 * td3;
1396 // Re-compute the (x,y) position for this coordinate.
1397 double xr = 0., yr = 0.;
1398 for (size_t i = 0; i < 6; ++i) {
1399 xr += xn[i] * f[i];
1400 yr += yn[i] * f[i];
1401 }
1402 const double sr = td1 + td2 + td3;
1403 // Compute the Jacobian.
1404 Jacobian3(xn, yn, td1, td2, td3, det, jac);
1405 // Compute the difference vector.
1406 const double diff[3] = {1 - sr, x - xr, y - yr};
1407 // Update the estimate.
1408 const double invdet = 1. / det;
1409 double corr[3] = {0., 0., 0.};
1410 for (size_t l = 0; l < 3; l++) {
1411 for (size_t k = 0; k < 3; k++) {
1412 corr[l] += jac[l][k] * diff[k];
1413 }
1414 corr[l] *= invdet;
1415 }
1416 // Debugging
1417 if (m_debug) {
1418 std::cout << m_className << "::Coordinates3:\n";
1419 std::cout << " Difference vector: (1, x, y) = (" << diff[0] << ", "
1420 << diff[1] << ", " << diff[2] << ").\n";
1421 std::cout << " Correction vector: (u, v, w) = (" << corr[0] << ", "
1422 << corr[1] << ", " << corr[2] << ").\n";
1423 }
1424 // Update the vector.
1425 td1 += corr[0];
1426 td2 += corr[1];
1427 td3 += corr[2];
1428 // Check for convergence.
1429 constexpr double tol = 1.e-5;
1430 if (fabs(corr[0]) < tol && fabs(corr[1]) < tol && fabs(corr[2]) < tol) {
1431 if (m_debug) {
1432 std::cout << m_className << "::Coordinates3: Convergence reached.";
1433 }
1434 converged = true;
1435 break;
1436 }
1437 }
1438 // No convergence reached
1439 if (!converged) {
1440 const double xmin = std::min({xn[0], xn[1], xn[2]});
1441 const double xmax = std::max({xn[0], xn[1], xn[2]});
1442 const double ymin = std::min({yn[0], yn[1], yn[2]});
1443 const double ymax = std::max({yn[0], yn[1], yn[2]});
1444 if (x >= xmin && x <= xmax && y >= ymin && y <= ymax) {
1446 std::cout << m_className << "::Coordinates3:\n"
1447 << " No convergence achieved "
1448 << "when refining internal isoparametric coordinates\n"
1449 << " at position (" << x << ", " << y << ").\n";
1450 }
1451 t1 = t2 = t3 = t4 = 0;
1452 return 1;
1453 }
1454 }
1455
1456 // Convergence reached.
1457 t1 = td1;
1458 t2 = td2;
1459 t3 = td3;
1460 t4 = 0;
1461 if (m_debug) {
1462 std::cout << m_className << "::Coordinates3:\n";
1463 std::cout << " Convergence reached at (t1, t2, t3) = (" << t1 << ", "
1464 << t2 << ", " << t3 << ").\n";
1465 // For debugging purposes, show position
1466 const double f0 = td1 * (2 * td1 - 1);
1467 const double f1 = td2 * (2 * td2 - 1);
1468 const double f2 = td3 * (2 * td3 - 1);
1469 const double f3 = 4 * td1 * td2;
1470 const double f4 = 4 * td1 * td3;
1471 const double f5 = 4 * td2 * td3;
1472 const double xr =
1473 xn[0] * f0 + xn[1] * f1 + xn[2] * f2 + xn[3] * f3 + xn[4] * f4 + xn[5] * f5;
1474 const double yr =
1475 yn[0] * f0 + yn[1] * f1 + yn[2] * f2 + yn[3] * f3 + yn[4] * f4 + yn[5] * f5;
1476 const double sr = td1 + td2 + td3;
1477 std::cout << m_className << "::Coordinates3:\n";
1478 std::cout << " Position requested: (" << x << ", " << y << ")\n";
1479 std::cout << " Reconstructed: (" << xr << ", " << yr << ")\n";
1480 std::cout << " Difference: (" << x - xr << ", " << y - yr
1481 << ")\n";
1482 std::cout << " Checksum - 1: " << sr - 1 << "\n";
1483 }
1484
1485 // Success
1486 return 0;
1487}
1488
1489int ComponentFieldMap::Coordinates4(
1490 const double x, const double y,
1491 double& t1, double& t2, double& t3, double& t4, double& det,
1492 const std::array<double, 8>& xn,
1493 const std::array<double, 8>& yn) const {
1494 if (m_debug) {
1495 std::cout << m_className << "::Coordinates4:\n"
1496 << " Point (" << x << ", " << y << ")\n";
1497 }
1498 // Failure flag
1499 int ifail = 1;
1500
1501 // Provisional values
1502 t1 = t2 = t3 = t4 = 0.;
1503
1504 // Compute determinant.
1505 const double dd =
1506 -(xn[0] * yn[1]) + xn[3] * yn[2] -
1507 xn[2] * yn[3] +
1508 x * (-yn[0] + yn[1] - yn[2] + yn[3]) +
1509 xn[1] * (yn[0] - y) +
1510 (xn[0] + xn[2] - xn[3]) * y;
1511 det = -(-((xn[0] - xn[3]) * (yn[1] - yn[2])) +
1512 (xn[1] - xn[2]) * (yn[0] - yn[3])) *
1513 (2 * x * (-yn[0] + yn[1] + yn[2] - yn[3]) -
1514 (xn[0] + xn[3]) * (yn[1] + yn[2] - 2 * y) +
1515 xn[1] * (yn[0] + yn[3] - 2 * y) +
1516 xn[2] * (yn[0] + yn[3] - 2 * y)) +
1517 dd * dd;
1518
1519 // Check that the determinant is non-negative
1520 // (this can happen if the point is out of range).
1521 if (det < 0) {
1522 if (m_debug) {
1523 std::cerr << m_className << "::Coordinates4:\n"
1524 << " No solution found for isoparametric coordinates\n"
1525 << " because the determinant " << det << " is < 0.\n";
1526 }
1527 return ifail;
1528 }
1529
1530 // Vector products for evaluation of T1.
1531 double prod = ((xn[2] - xn[3]) * (yn[0] - yn[1]) -
1532 (xn[0] - xn[1]) * (yn[2] - yn[3]));
1533 if (prod * prod > 1.0e-12 *
1534 ((xn[0] - xn[1]) * (xn[0] - xn[1]) +
1535 (yn[0] - yn[1]) * (yn[0] - yn[1])) *
1536 ((xn[2] - xn[3]) * (xn[2] - xn[3]) +
1537 (yn[2] - yn[3]) * (yn[2] - yn[3]))) {
1538 t1 = (-(xn[3] * yn[0]) + x * yn[0] + xn[2] * yn[1] - x * yn[1] - xn[1] * yn[2] +
1539 x * yn[2] + xn[0] * yn[3] - x * yn[3] - xn[0] * y + xn[1] * y - xn[2] * y +
1540 xn[3] * y + sqrt(det)) /
1541 prod;
1542 } else {
1543 double xp = yn[0] - yn[1];
1544 double yp = xn[1] - xn[0];
1545 double dn = sqrt(xp * xp + yp * yp);
1546 if (dn <= 0) {
1547 std::cerr << m_className << "::Coordinates4:\n"
1548 << " Element appears to be degenerate in the 1 - 2 axis.\n";
1549 return ifail;
1550 }
1551 xp = xp / dn;
1552 yp = yp / dn;
1553 double dpoint = xp * (x - xn[0]) + yp * (y - yn[0]);
1554 double dbox = xp * (xn[3] - xn[0]) + yp * (yn[3] - yn[0]);
1555 if (dbox == 0) {
1556 std::cerr << m_className << "::Coordinates4:\n"
1557 << " Element appears to be degenerate in the 1 - 3 axis.\n";
1558 return ifail;
1559 }
1560 double t = -1 + 2 * dpoint / dbox;
1561 double xt1 = xn[0] + 0.5 * (t + 1) * (xn[3] - xn[0]);
1562 double yt1 = yn[0] + 0.5 * (t + 1) * (yn[3] - yn[0]);
1563 double xt2 = xn[1] + 0.5 * (t + 1) * (xn[2] - xn[1]);
1564 double yt2 = yn[1] + 0.5 * (t + 1) * (yn[2] - yn[1]);
1565 dn = (xt1 - xt2) * (xt1 - xt2) + (yt1 - yt2) * (yt1 - yt2);
1566 if (dn <= 0) {
1567 std::cout << m_className << "::Coordinates4:\n";
1568 std::cout
1569 << " Coordinate requested at convergence point of element.\n";
1570 return ifail;
1571 }
1572 t1 = -1 + 2 * ((x - xt1) * (xt2 - xt1) + (y - yt1) * (yt2 - yt1)) / dn;
1573 }
1574
1575 // Vector products for evaluation of T2.
1576 prod = ((xn[0] - xn[3]) * (yn[1] - yn[2]) -
1577 (xn[1] - xn[2]) * (yn[0] - yn[3]));
1578 if (prod * prod > 1.0e-12 *
1579 ((xn[0] - xn[3]) * (xn[0] - xn[3]) +
1580 (yn[0] - yn[3]) * (yn[0] - yn[3])) *
1581 ((xn[1] - xn[2]) * (xn[1] - xn[2]) +
1582 (yn[1] - yn[2]) * (yn[1] - yn[2]))) {
1583 t2 = (-(xn[1] * yn[0]) + x * yn[0] + xn[0] * yn[1] - x * yn[1] - xn[3] * yn[2] +
1584 x * yn[2] + xn[2] * yn[3] - x * yn[3] - xn[0] * y + xn[1] * y - xn[2] * y +
1585 xn[3] * y - sqrt(det)) /
1586 prod;
1587 } else {
1588 double xp = yn[0] - yn[3];
1589 double yp = xn[3] - xn[0];
1590 double dn = sqrt(xp * xp + yp * yp);
1591 if (dn <= 0) {
1592 std::cerr << m_className << "Coordinates4:\n"
1593 << " Element appears to be degenerate in the 1 - 4 axis.\n";
1594 return ifail;
1595 }
1596 xp = xp / dn;
1597 yp = yp / dn;
1598 double dpoint = xp * (x - xn[0]) + yp * (y - yn[0]);
1599 double dbox = xp * (xn[1] - xn[0]) + yp * (yn[1] - yn[0]);
1600 if (dbox == 0) {
1601 std::cerr << m_className << "::Coordinates4:\n"
1602 << " Element appears to be degenerate in the 1 - 2 axis.\n";
1603 return ifail;
1604 }
1605 double t = -1 + 2 * dpoint / dbox;
1606 double xt1 = xn[0] + 0.5 * (t + 1) * (xn[1] - xn[0]);
1607 double yt1 = yn[0] + 0.5 * (t + 1) * (yn[1] - yn[0]);
1608 double xt2 = xn[3] + 0.5 * (t + 1) * (xn[2] - xn[3]);
1609 double yt2 = yn[3] + 0.5 * (t + 1) * (yn[2] - yn[3]);
1610 dn = (xt1 - xt2) * (xt1 - xt2) + (yt1 - yt2) * (yt1 - yt2);
1611 if (dn <= 0) {
1612 std::cout
1613 << m_className << "::Coordinates4:\n"
1614 << " Coordinate requested at convergence point of element.\n";
1615 return ifail;
1616 }
1617 t2 = -1 + 2 * ((x - xt1) * (xt2 - xt1) + (y - yt1) * (yt2 - yt1)) / dn;
1618 }
1619 if (m_debug) {
1620 std::cout << m_className << "::Coordinates4:\n";
1621 std::cout << " Isoparametric (u, v): (" << t1 << ", " << t2 << ").\n";
1622 // Re-compute the (x,y,z) position for this coordinate.
1623 const double f0 = (1 - t1) * (1 - t2) * 0.25;
1624 const double f1 = (1 + t1) * (1 - t2) * 0.25;
1625 const double f2 = (1 + t1) * (1 + t2) * 0.25;
1626 const double f3 = (1 - t1) * (1 + t2) * 0.25;
1627 const double xr = xn[0] * f0 + xn[1] * f1 + xn[2] * f2 + xn[3] * f3;
1628 const double yr = yn[0] * f0 + yn[1] * f1 + yn[2] * f2 + yn[3] * f3;
1629 std::cout << m_className << "::Coordinates4: \n";
1630 std::cout << " Position requested: (" << x << ", " << y << ")\n";
1631 std::cout << " Reconstructed: (" << xr << ", " << yr << ")\n";
1632 std::cout << " Difference: (" << x - xr << ", " << y - yr
1633 << ")\n";
1634 }
1635
1636 // This should have worked if we get this far.
1637 ifail = 0;
1638 return ifail;
1639}
1640
1641int ComponentFieldMap::Coordinates5(const double x, const double y,
1642 double& t1, double& t2, double& t3, double& t4,
1643 double jac[4][4], double& det,
1644 const std::array<double, 8>& xn,
1645 const std::array<double, 8>& yn) const {
1646 // Debugging
1647 if (m_debug) {
1648 std::cout << m_className << "::Coordinates5:\n"
1649 << " Point (" << x << ", " << y << ")\n";
1650 }
1651
1652 // Failure flag
1653 int ifail = 1;
1654
1655 // Provisional values
1656 t1 = t2 = t3 = t4 = 0;
1657
1658 // Make a first order approximation.
1659 if (Coordinates4(x, y, t1, t2, t3, t4, det, xn, yn) > 0) {
1660 if (m_debug) {
1661 std::cout << " Failure to obtain linear estimate of isoparametric "
1662 "coordinates\n.";
1663 }
1664 return ifail;
1665 }
1666
1667 // Check whether the point is far outside.
1668 if (t1 < -1.5 || t1 > 1.5 || t2 < -1.5 || t2 > 1.5) {
1669 if (m_debug) {
1670 std::cout << " Point far outside, (t1,t2) = (" << t1 << ", " << t2
1671 << ").\n";
1672 }
1673 return ifail;
1674 }
1675
1676 // Start iteration
1677 double td1 = t1, td2 = t2;
1678 bool converged = false;
1679 std::array<double, 8> f;
1680 for (int iter = 0; iter < 10; iter++) {
1681 if (m_debug) {
1682 std::cout << " Iteration " << iter << ": (t1, t2) = (" << td1
1683 << ", " << td2 << ").\n";
1684 }
1685 // Re-compute the (x,y,z) position for this coordinate.
1686 f[0] = (-(1 - td1) * (1 - td2) * (1 + td1 + td2)) * 0.25;
1687 f[1] = (-(1 + td1) * (1 - td2) * (1 - td1 + td2)) * 0.25;
1688 f[2] = (-(1 + td1) * (1 + td2) * (1 - td1 - td2)) * 0.25;
1689 f[3] = (-(1 - td1) * (1 + td2) * (1 + td1 - td2)) * 0.25;
1690 f[4] = (1 - td1) * (1 + td1) * (1 - td2) * 0.5;
1691 f[5] = (1 + td1) * (1 + td2) * (1 - td2) * 0.5;
1692 f[6] = (1 - td1) * (1 + td1) * (1 + td2) * 0.5;
1693 f[7] = (1 - td1) * (1 + td2) * (1 - td2) * 0.5;
1694 double xr = 0., yr = 0.;
1695 for (size_t i = 0; i < 8; ++i) {
1696 xr += xn[i] * f[i];
1697 yr += yn[i] * f[i];
1698 }
1699 // Compute the Jacobian.
1700 Jacobian5(xn, yn, td1, td2, det, jac);
1701 // Compute the difference vector.
1702 double diff[2] = {x - xr, y - yr};
1703 // Update the estimate.
1704 double corr[2] = {0., 0.};
1705 const double invdet = 1. / det;
1706 for (size_t l = 0; l < 2; ++l) {
1707 for (size_t k = 0; k < 2; ++k) {
1708 corr[l] += jac[l][k] * diff[k];
1709 }
1710 corr[l] *= invdet;
1711 }
1712 // Debugging
1713 if (m_debug) {
1714 std::cout << " Difference vector: (x, y) = (" << diff[0] << ", "
1715 << diff[1] << ").\n";
1716 std::cout << " Correction vector: (t1, t2) = (" << corr[0] << ", "
1717 << corr[1] << ").\n";
1718 }
1719 // Update the vector.
1720 td1 += corr[0];
1721 td2 += corr[1];
1722 // Check for convergence.
1723 constexpr double tol = 1.e-5;
1724 if (fabs(corr[0]) < tol && fabs(corr[1]) < tol) {
1725 if (m_debug) std::cout << " Convergence reached.\n";
1726 converged = true;
1727 break;
1728 }
1729 }
1730 // No convergence reached.
1731 if (!converged) {
1732 double xmin = *std::min_element(xn.begin(), xn.end());
1733 double xmax = *std::max_element(xn.begin(), xn.end());
1734 double ymin = *std::min_element(yn.begin(), yn.end());
1735 double ymax = *std::max_element(yn.begin(), yn.end());
1736 if (x >= xmin && x <= xmax && y >= ymin && y <= ymax) {
1738 std::cout << m_className << "::Coordinates5:\n"
1739 << " No convergence achieved "
1740 << "when refining internal isoparametric coordinates\n"
1741 << " at position (" << x << ", " << y << ").\n";
1742 }
1743 t1 = t2 = 0;
1744 return ifail;
1745 }
1746 }
1747
1748 // Convergence reached.
1749 t1 = td1;
1750 t2 = td2;
1751 t3 = 0;
1752 t4 = 0;
1753 if (m_debug) {
1754 std::cout << " Convergence reached at (t1, t2) = (" << t1 << ", " << t2
1755 << ").\n";
1756 // For debugging purposes, show position.
1757 f[0] = (-(1 - td1) * (1 - td2) * (1 + td1 + td2)) * 0.25;
1758 f[1] = (-(1 + td1) * (1 - td2) * (1 - td1 + td2)) * 0.25;
1759 f[2] = (-(1 + td1) * (1 + td2) * (1 - td1 - td2)) * 0.25;
1760 f[3] = (-(1 - td1) * (1 + td2) * (1 + td1 - td2)) * 0.25;
1761 f[4] = (1 - td1) * (1 + td1) * (1 - td2) * 0.5;
1762 f[5] = (1 + td1) * (1 + td2) * (1 - td2) * 0.5;
1763 f[6] = (1 - td1) * (1 + td1) * (1 + td2) * 0.5;
1764 f[7] = (1 - td1) * (1 + td2) * (1 - td2) * 0.5;
1765 double xr = 0., yr = 0.;
1766 for (size_t i = 0; i < 8; ++i) {
1767 xr += xn[i] * f[i];
1768 yr += yn[i] * f[i];
1769 }
1770 std::cout << " Position requested: (" << x << ", " << y << ")\n";
1771 std::cout << " Reconstructed: (" << xr << ", " << yr << ")\n";
1772 std::cout << " Difference: (" << x - xr << ", " << y - yr
1773 << ")\n";
1774 }
1775
1776 // Success
1777 ifail = 0;
1778 return ifail;
1779}
1780
1781std::array<std::array<double, 3>, 4> ComponentFieldMap::Weights12(
1782 const std::array<double, 10>& xn,
1783 const std::array<double, 10>& yn,
1784 const std::array<double, 10>& zn) {
1785
1786 std::array<std::array<double, 3>, 4> w;
1787 w[0][0] = (yn[2] - yn[1]) * (zn[3] - zn[1]) -
1788 (yn[3] - yn[1]) * (zn[2] - zn[1]);
1789 w[0][1] = (zn[2] - zn[1]) * (xn[3] - xn[1]) -
1790 (zn[3] - zn[1]) * (xn[2] - xn[1]);
1791 w[0][2] = (xn[2] - xn[1]) * (yn[3] - yn[1]) -
1792 (xn[3] - xn[1]) * (yn[2] - yn[1]);
1793 const double s0 = 1. / ((xn[0] - xn[1]) * w[0][0] +
1794 (yn[0] - yn[1]) * w[0][1] +
1795 (zn[0] - zn[1]) * w[0][2]);
1796 for (size_t i = 0; i < 3; ++i) w[0][i] *= s0;
1797
1798 w[1][0] = (yn[0] - yn[2]) * (zn[3] - zn[2]) -
1799 (yn[3] - yn[2]) * (zn[0] - zn[2]);
1800 w[1][1] = (zn[0] - zn[2]) * (xn[3] - xn[2]) -
1801 (zn[3] - zn[2]) * (xn[0] - xn[2]);
1802 w[1][2] = (xn[0] - xn[2]) * (yn[3] - yn[2]) -
1803 (xn[3] - xn[2]) * (yn[0] - yn[2]);
1804 const double s1 = 1. / ((xn[1] - xn[2]) * w[1][0] +
1805 (yn[1] - yn[2]) * w[1][1] +
1806 (zn[1] - zn[2]) * w[1][2]);
1807 for (size_t i = 0; i < 3; ++i) w[1][i] *= s1;
1808
1809 w[2][0] = (yn[0] - yn[3]) * (zn[1] - zn[3]) -
1810 (yn[1] - yn[3]) * (zn[0] - zn[3]);
1811 w[2][1] = (zn[0] - zn[3]) * (xn[1] - xn[3]) -
1812 (zn[1] - zn[3]) * (xn[0] - xn[3]);
1813 w[2][2] = (xn[0] - xn[3]) * (yn[1] - yn[3]) -
1814 (xn[1] - xn[3]) * (yn[0] - yn[3]);
1815 const double s2 = 1. / ((xn[2] - xn[3]) * w[2][0] +
1816 (yn[2] - yn[3]) * w[2][1] +
1817 (zn[2] - zn[3]) * w[2][2]);
1818 for (size_t i = 0; i < 3; ++i) w[2][i] *= s2;
1819
1820 w[3][0] = (yn[2] - yn[0]) * (zn[1] - zn[0]) -
1821 (yn[1] - yn[0]) * (zn[2] - zn[0]);
1822 w[3][1] = (zn[2] - zn[0]) * (xn[1] - xn[0]) -
1823 (zn[1] - zn[0]) * (xn[2] - xn[0]);
1824 w[3][2] = (xn[2] - xn[0]) * (yn[1] - yn[0]) -
1825 (xn[1] - xn[0]) * (yn[2] - yn[0]);
1826 const double s3 = 1. / ((xn[3] - xn[0]) * w[3][0] +
1827 (yn[3] - yn[0]) * w[3][1] +
1828 (zn[3] - zn[0]) * w[3][2]);
1829 for (size_t i = 0; i < 3; ++i) w[3][i] *= s3;
1830 return w;
1831}
1832
1833void ComponentFieldMap::Coordinates12(
1834 const double x, const double y, const double z,
1835 double& t1, double& t2, double& t3, double& t4,
1836 const std::array<double, 10>& xn,
1837 const std::array<double, 10>& yn,
1838 const std::array<double, 10>& zn,
1839 const std::array<std::array<double, 3>, 4>& w) const {
1840 if (m_debug) {
1841 std::cout << m_className << "::Coordinates12:\n"
1842 << " Point (" << x << ", " << y << ", " << z << ").\n";
1843 }
1844
1845 // Compute tetrahedral coordinates.
1846 t1 = (x - xn[1]) * w[0][0] + (y - yn[1]) * w[0][1] + (z - zn[1]) * w[0][2];
1847 t2 = (x - xn[2]) * w[1][0] + (y - yn[2]) * w[1][1] + (z - zn[2]) * w[1][2];
1848 t3 = (x - xn[3]) * w[2][0] + (y - yn[3]) * w[2][1] + (z - zn[3]) * w[2][2];
1849 t4 = (x - xn[0]) * w[3][0] + (y - yn[0]) * w[3][1] + (z - zn[0]) * w[3][2];
1850
1851 // Result.
1852 if (m_debug) {
1853 std::cout << " Tetrahedral coordinates (t, u, v, w) = (" << t1 << ", "
1854 << t2 << ", " << t3 << ", " << t4
1855 << ") sum = " << t1 + t2 + t3 + t4 << ".\n";
1856 // Re-compute the (x,y,z) position for this coordinate.
1857 const double xr = xn[0] * t1 + xn[1] * t2 + xn[2] * t3 + xn[3] * t4;
1858 const double yr = yn[0] * t1 + yn[1] * t2 + yn[2] * t3 + yn[3] * t4;
1859 const double zr = zn[0] * t1 + zn[1] * t2 + zn[2] * t3 + zn[3] * t4;
1860 const double sr = t1 + t2 + t3 + t4;
1861 std::cout << " Position requested: (" << x << ", " << y << ", " << z
1862 << ")\n";
1863 std::cout << " Reconstructed: (" << xr << ", " << yr << ", "
1864 << zr << ")\n";
1865 std::cout << " Difference: (" << x - xr << ", " << y - yr
1866 << ", " << z - zr << ")\n";
1867 std::cout << " Checksum - 1: " << sr - 1 << "\n";
1868 }
1869}
1870
1871int ComponentFieldMap::Coordinates13(
1872 const double x, const double y, const double z,
1873 double& t1, double& t2, double& t3, double& t4,
1874 double jac[4][4], double& det,
1875 const std::array<double, 10>& xn,
1876 const std::array<double, 10>& yn,
1877 const std::array<double, 10>& zn,
1878 const std::array<std::array<double, 3>, 4>& w) const {
1879
1880 // Make a first order approximation.
1881 t1 = (x - xn[1]) * w[0][0] + (y - yn[1]) * w[0][1] + (z - zn[1]) * w[0][2];
1882 // Stop if we are far outside.
1883 if (t1 < -0.5 || t1 > 1.5) return 1;
1884 t2 = (x - xn[2]) * w[1][0] + (y - yn[2]) * w[1][1] + (z - zn[2]) * w[1][2];
1885 if (t2 < -0.5 || t2 > 1.5) return 1;
1886 t3 = (x - xn[3]) * w[2][0] + (y - yn[3]) * w[2][1] + (z - zn[3]) * w[2][2];
1887 if (t3 < -0.5 || t3 > 1.5) return 1;
1888 t4 = (x - xn[0]) * w[3][0] + (y - yn[0]) * w[3][1] + (z - zn[0]) * w[3][2];
1889 if (t4 < -0.5 || t4 > 1.5) return 1;
1890
1891 // Start iteration.
1892 std::array<double, 4> td = {t1, t2, t3, t4};
1893
1894 // Loop
1895 bool converged = false;
1896 for (int iter = 0; iter < 10; ++iter) {
1897 if (m_debug) {
1898 std::printf(" Iteration %4u: t = (%15.8f, %15.8f %15.8f %15.8f)\n",
1899 iter, td[0], td[1], td[2], td[3]);
1900 }
1901 // Evaluate the shape functions and re-compute the (x,y,z) position
1902 // for this set of isoparametric coordinates.
1903 const double f0 = td[0] * (td[0] - 0.5);
1904 const double f1 = td[1] * (td[1] - 0.5);
1905 const double f2 = td[2] * (td[2] - 0.5);
1906 const double f3 = td[3] * (td[3] - 0.5);
1907 double xr = 2 * (f0 * xn[0] + f1 * xn[1] + f2 * xn[2] + f3 * xn[3]);
1908 double yr = 2 * (f0 * yn[0] + f1 * yn[1] + f2 * yn[2] + f3 * yn[3]);
1909 double zr = 2 * (f0 * zn[0] + f1 * zn[1] + f2 * zn[2] + f3 * zn[3]);
1910 const double fourt0 = 4 * td[0];
1911 const double fourt1 = 4 * td[1];
1912 const double fourt2 = 4 * td[2];
1913 const double fourt3 = 4 * td[3];
1914 const double f4 = fourt0 * td[1];
1915 const double f5 = fourt0 * td[2];
1916 const double f6 = fourt0 * td[3];
1917 const double f7 = fourt1 * td[2];
1918 const double f8 = fourt1 * td[3];
1919 const double f9 = fourt2 * td[3];
1920 xr += f4 * xn[4] + f5 * xn[5] + f6 * xn[6] + f7 * xn[7] +
1921 f8 * xn[8] + f9 * xn[9];
1922 yr += f4 * yn[4] + f5 * yn[5] + f6 * yn[6] + f7 * yn[7] +
1923 f8 * yn[8] + f9 * yn[9];
1924 zr += f4 * zn[4] + f5 * zn[5] + f6 * zn[6] + f7 * zn[7] +
1925 f8 * zn[8] + f9 * zn[9];
1926 // Compute the Jacobian.
1927 Jacobian13(xn, yn, zn, fourt0, fourt1, fourt2, fourt3, det, jac);
1928 // Compute the difference vector.
1929 const double sr = std::accumulate(td.cbegin(), td.cend(), 0.);
1930 const double diff[4] = {1. - sr, x - xr, y - yr, z - zr};
1931 // Update the estimate.
1932 double corr[4] = {0., 0., 0., 0.};
1933 for (size_t l = 0; l < 4; ++l) {
1934 for (size_t k = 0; k < 4; ++k) {
1935 corr[l] += jac[l][k] * diff[k];
1936 }
1937 corr[l] *= det;
1938 td[l] += corr[l];
1939 }
1940
1941 // Debugging
1942 if (m_debug) {
1943 std::cout << " Difference vector: (1, x, y, z) = (" << diff[0]
1944 << ", " << diff[1] << ", " << diff[2] << ", " << diff[3]
1945 << ").\n";
1946 std::cout << " Correction vector: (t1,t2,t3,t4) = (" << corr[0]
1947 << ", " << corr[1] << ", " << corr[2] << ", " << corr[3]
1948 << ").\n";
1949 }
1950
1951 // Check for convergence.
1952 constexpr double tol = 1.e-5;
1953 if (fabs(corr[0]) < tol && fabs(corr[1]) < tol && fabs(corr[2]) < tol &&
1954 fabs(corr[3]) < tol) {
1955 if (m_debug) std::cout << " Convergence reached.\n";
1956 converged = true;
1957 break;
1958 }
1959 }
1960
1961 // No convergence reached.
1962 if (!converged) {
1963 const double xmin = std::min({xn[0], xn[1], xn[2], xn[3]});
1964 const double xmax = std::max({xn[0], xn[1], xn[2], xn[3]});
1965 const double ymin = std::min({yn[0], yn[1], yn[2], yn[3]});
1966 const double ymax = std::max({yn[0], yn[1], yn[2], yn[3]});
1967 const double zmin = std::min({zn[0], zn[1], zn[2], zn[3]});
1968 const double zmax = std::max({zn[0], zn[1], zn[2], zn[3]});
1969 if (x >= xmin && x <= xmax && y >= ymin && y <= ymax && z >= zmin &&
1970 z <= zmax) {
1972 std::cout << m_className << "::Coordinates13:\n"
1973 << " No convergence achieved "
1974 << "when refining internal isoparametric coordinates\n"
1975 << " at position (" << x << ", " << y << ", " << z
1976 << ").\n";
1977 }
1978 t1 = t2 = t3 = t4 = -1;
1979 return 1;
1980 }
1981 }
1982
1983 // Convergence reached.
1984 t1 = td[0];
1985 t2 = td[1];
1986 t3 = td[2];
1987 t4 = td[3];
1988 if (m_debug) {
1989 std::cout << " Convergence reached at (t1, t2, t3, t4) = (" << t1 << ", "
1990 << t2 << ", " << t3 << ", " << t4 << ").\n";
1991 // Re-compute the (x,y,z) position for this coordinate.
1992 const double f0 = td[0] * (td[0] - 0.5);
1993 const double f1 = td[1] * (td[1] - 0.5);
1994 const double f2 = td[2] * (td[2] - 0.5);
1995 const double f3 = td[3] * (td[3] - 0.5);
1996 double xr = 2 * (f0 * xn[0] + f1 * xn[1] + f2 * xn[2] + f3 * xn[3]);
1997 double yr = 2 * (f0 * yn[0] + f1 * yn[1] + f2 * yn[2] + f3 * yn[3]);
1998 double zr = 2 * (f0 * zn[0] + f1 * zn[1] + f2 * zn[2] + f3 * zn[3]);
1999 const double fourt0 = 4 * td[0];
2000 const double fourt1 = 4 * td[1];
2001 const double fourt2 = 4 * td[2];
2002 const double f4 = fourt0 * td[1];
2003 const double f5 = fourt0 * td[2];
2004 const double f6 = fourt0 * td[3];
2005 const double f7 = fourt1 * td[2];
2006 const double f8 = fourt1 * td[3];
2007 const double f9 = fourt2 * td[3];
2008 xr += f4 * xn[4] + f5 * xn[5] + f6 * xn[6] + f7 * xn[7] +
2009 f8 * xn[8] + f9 * xn[9];
2010 yr += f4 * yn[4] + f5 * yn[5] + f6 * yn[6] + f7 * yn[7] +
2011 f8 * yn[8] + f9 * yn[9];
2012 zr += f4 * zn[4] + f5 * zn[5] + f6 * zn[6] + f7 * zn[7] +
2013 f8 * zn[8] + f9 * zn[9];
2014 const double sr = std::accumulate(td.cbegin(), td.cend(), 0.);
2015 std::cout << " Position requested: (" << x << ", " << y << ", " << z
2016 << ")\n";
2017 std::cout << " Reconstructed: (" << xr << ", " << yr << ", "
2018 << zr << ")\n";
2019 std::cout << " Difference: (" << x - xr << ", " << y - yr
2020 << ", " << z - zr << ")\n";
2021 std::cout << " Checksum - 1: " << sr - 1 << "\n";
2022 }
2023
2024 // Success
2025 return 0;
2026}
2027
2028int ComponentFieldMap::CoordinatesCube(const double x, const double y,
2029 const double z, double& t1, double& t2,
2030 double& t3, TMatrixD*& jac,
2031 std::vector<TMatrixD*>& dN,
2032 const Element& element) const {
2033 /*
2034 global coordinates 7__ _ _ 6 t3 t2
2035 / /| ^ /|
2036 ^ z / / | | /
2037 | 4_______5 | | /
2038 | | | | | /
2039 | | 3 | 2 |/ t1
2040 -------> | | / ------->
2041 / y | |/ local coordinates
2042 / 0--------1
2043 /
2044 v x
2045 */
2046
2047 const Node& n0 = m_nodes[element.emap[0]];
2048 const Node& n2 = m_nodes[element.emap[2]];
2049 const Node& n3 = m_nodes[element.emap[3]];
2050 const Node& n7 = m_nodes[element.emap[7]];
2051
2052 // Compute hexahedral coordinates (t1->[-1,1],t2->[-1,1],t3->[-1,1]) and
2053 // t1 (zeta) is in y-direction
2054 // t2 (eta) is in opposite x-direction
2055 // t3 (mu) is in z-direction
2056 // Nodes are set in that way, that node [0] has always lowest x,y,z!
2057 t2 = (2. * (x - n3.x) / (n0.x - n3.x) - 1) * -1.;
2058 t1 = 2. * (y - n3.y) / (n2.y - n3.y) - 1;
2059 t3 = 2. * (z - n3.z) / (n7.z - n3.z) - 1;
2060 // Re-compute the (x,y,z) position for this coordinate.
2061 if (m_debug) {
2062 double n[8];
2063 n[0] = 1. / 8 * (1 - t1) * (1 - t2) * (1 - t3);
2064 n[1] = 1. / 8 * (1 + t1) * (1 - t2) * (1 - t3);
2065 n[2] = 1. / 8 * (1 + t1) * (1 + t2) * (1 - t3);
2066 n[3] = 1. / 8 * (1 - t1) * (1 + t2) * (1 - t3);
2067 n[4] = 1. / 8 * (1 - t1) * (1 - t2) * (1 + t3);
2068 n[5] = 1. / 8 * (1 + t1) * (1 - t2) * (1 + t3);
2069 n[6] = 1. / 8 * (1 + t1) * (1 + t2) * (1 + t3);
2070 n[7] = 1. / 8 * (1 - t1) * (1 + t2) * (1 + t3);
2071
2072 double xr = 0;
2073 double yr = 0;
2074 double zr = 0;
2075
2076 for (int i = 0; i < 8; i++) {
2077 const Node& node = m_nodes[element.emap[i]];
2078 xr += node.x * n[i];
2079 yr += node.y * n[i];
2080 zr += node.z * n[i];
2081 }
2082 double sr = n[0] + n[1] + n[2] + n[3] + n[4] + n[5] + n[6] + n[7];
2083 std::cout << m_className << "::CoordinatesCube:\n";
2084 std::cout << " Position requested: (" << x << "," << y << "," << z
2085 << ")\n";
2086 std::cout << " Position reconstructed: (" << xr << "," << yr << "," << zr
2087 << ")\n";
2088 std::cout << " Difference: (" << (x - xr) << "," << (y - yr)
2089 << "," << (z - zr) << ")\n";
2090 std::cout << " Hexahedral coordinates (t, u, v) = (" << t1 << "," << t2
2091 << "," << t3 << ")\n";
2092 std::cout << " Checksum - 1: " << (sr - 1) << "\n";
2093 }
2094 if (jac != 0) JacobianCube(element, t1, t2, t3, jac, dN);
2095 // This should always work.
2096 return 0;
2097}
2098
2100 m_ready = false;
2101
2102 m_elements.clear();
2103 m_degenerate.clear();
2104 m_bbMin.clear();
2105 m_bbMax.clear();
2106 m_w12.clear();
2107 m_nodes.clear();
2108 m_pot.clear();
2109 m_wpot.clear();
2110 m_dwpot.clear();
2111 m_wfieldCopies.clear();
2112 m_materials.clear();
2113 m_hasBoundingBox = false;
2114 m_warning = false;
2115 m_nWarnings = 0;
2116
2117 m_octree.reset(nullptr);
2118 m_cacheElemBoundingBoxes = false;
2119}
2120
2122 // Establish the ranges.
2123 SetRange();
2125 std::cout << m_className << "::Prepare:\n"
2126 << " Caching the bounding boxes of all elements...";
2127 CalculateElementBoundingBoxes();
2128 std::cout << " done.\n";
2129 // Initialize the tetrahedral tree.
2130 if (InitializeTetrahedralTree()) {
2131 std::cout << " Initialized tetrahedral tree.\n";
2132 }
2133 m_elementIndices.resize(m_elements.size());
2134 std::iota(m_elementIndices.begin(), m_elementIndices.end(), 0);
2135 // Precompute terms for interpolation in linear tetrahedra.
2137 std::array<double, 10> xn;
2138 std::array<double, 10> yn;
2139 std::array<double, 10> zn;
2140 for (const auto& element : m_elements) {
2141 for (size_t j = 0; j < 10; ++j) {
2142 const auto& node = m_nodes[element.emap[j]];
2143 xn[j] = node.x;
2144 yn[j] = node.y;
2145 zn[j] = node.z;
2146 }
2147 m_w12.emplace_back(Weights12(xn, yn, zn));
2148 }
2149 }
2150}
2151
2153 // Check the required data is available.
2154 if (!m_ready) {
2155 PrintNotReady("UpdatePeriodicityCommon");
2156 return;
2157 }
2158
2159 for (size_t i = 0; i < 3; ++i) {
2160 // No regular and mirror periodicity at the same time.
2161 if (m_periodic[i] && m_mirrorPeriodic[i]) {
2162 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
2163 << " Both simple and mirror periodicity requested. Reset.\n";
2164 m_periodic[i] = false;
2165 m_mirrorPeriodic[i] = false;
2166 m_warning = true;
2167 }
2168 // In case of axial periodicity,
2169 // the range must be an integral part of two pi.
2170 if (m_axiallyPeriodic[i]) {
2171 if (m_mapamin[i] >= m_mapamax[i]) {
2172 m_mapna[i] = 0;
2173 } else {
2174 m_mapna[i] = TwoPi / (m_mapamax[i] - m_mapamin[i]);
2175 }
2176 if (fabs(m_mapna[i] - int(0.5 + m_mapna[i])) > 0.001 ||
2177 m_mapna[i] < 1.5) {
2178 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
2179 << " Axial symmetry has been requested but map does not\n"
2180 << " cover an integral fraction of 2 pi. Reset.\n";
2181 m_axiallyPeriodic[i] = false;
2182 m_warning = true;
2183 }
2184 }
2185 }
2186
2187 // Not more than 1 rotational symmetry
2191 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
2192 << " Only one rotational symmetry allowed; reset.\n";
2193 m_rotationSymmetric.fill(false);
2194 m_warning = true;
2195 }
2196
2197 // No rotational symmetry as well as axial periodicity
2199 m_rotationSymmetric[2]) &&
2201 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
2202 << " Not allowed to combine rotational symmetry\n"
2203 << " and axial periodicity; reset.\n";
2204 m_axiallyPeriodic.fill(false);
2205 m_rotationSymmetric.fill(false);
2206 m_warning = true;
2207 }
2208
2209 // In case of rotational symmetry, the x-range should not straddle 0.
2212 if (m_mapmin[0] * m_mapmax[0] < 0) {
2213 std::cerr << m_className << "::UpdatePeriodicityCommon:\n"
2214 << " Rotational symmetry requested, \n"
2215 << " but x-range straddles 0; reset.\n";
2216 m_rotationSymmetric.fill(false);
2217 m_warning = true;
2218 }
2219 }
2220
2221 // Recompute the cell ranges.
2222 for (size_t i = 0; i < 3; ++i) {
2223 m_minBoundingBox[i] = m_mapmin[i];
2224 m_maxBoundingBox[i] = m_mapmax[i];
2225 m_cells[i] = fabs(m_mapmax[i] - m_mapmin[i]);
2226 }
2227 for (size_t i = 0; i < 3; ++i) {
2228 if (!m_rotationSymmetric[i]) continue;
2229 const double r = std::max(fabs(m_mapmin[0]), fabs(m_mapmax[0]));
2230 m_minBoundingBox.fill(-r);
2231 m_maxBoundingBox.fill(+r);
2232 m_minBoundingBox[i] = m_mapmin[1];
2233 m_maxBoundingBox[i] = m_mapmax[1];
2234 break;
2235 }
2236
2237 if (m_axiallyPeriodic[0]) {
2238 const double yzmax = std::max({fabs(m_mapmin[1]), fabs(m_mapmax[1]),
2239 fabs(m_mapmin[2]), fabs(m_mapmax[2])});
2240 m_minBoundingBox[1] = -yzmax;
2241 m_maxBoundingBox[1] = +yzmax;
2242 m_minBoundingBox[2] = -yzmax;
2243 m_maxBoundingBox[2] = +yzmax;
2244 } else if (m_axiallyPeriodic[1]) {
2245 const double xzmax = std::max({fabs(m_mapmin[0]), fabs(m_mapmax[0]),
2246 fabs(m_mapmin[2]), fabs(m_mapmax[2])});
2247 m_minBoundingBox[0] = -xzmax;
2248 m_maxBoundingBox[0] = +xzmax;
2249 m_minBoundingBox[2] = -xzmax;
2250 m_maxBoundingBox[2] = +xzmax;
2251 } else if (m_axiallyPeriodic[2]) {
2252 const double xymax = std::max({fabs(m_mapmin[0]), fabs(m_mapmax[0]),
2253 fabs(m_mapmin[1]), fabs(m_mapmax[1])});
2254 m_minBoundingBox[0] = -xymax;
2255 m_maxBoundingBox[0] = +xymax;
2256 m_minBoundingBox[1] = -xymax;
2257 m_maxBoundingBox[1] = +xymax;
2258 }
2259
2260 for (size_t i = 0; i < 3; ++i) {
2261 if (m_periodic[i] || m_mirrorPeriodic[i]) {
2262 m_minBoundingBox[i] = -INFINITY;
2263 m_maxBoundingBox[i] = +INFINITY;
2264 }
2265 }
2266
2267 // Display the range if requested.
2268 if (m_debug) PrintRange();
2269}
2270
2272 // Check the required data is available.
2273 if (!m_ready) {
2274 PrintNotReady("UpdatePeriodicity2d");
2275 return;
2276 }
2277
2278 // No z-periodicity in 2d
2279 if (m_periodic[2] || m_mirrorPeriodic[2]) {
2280 std::cerr << m_className << "::UpdatePeriodicity2d:\n"
2281 << " Simple or mirror periodicity along z\n"
2282 << " requested for a 2d map; reset.\n";
2283 m_periodic[2] = false;
2284 m_mirrorPeriodic[2] = false;
2285 m_warning = true;
2286 }
2287
2288 // Only z-axial periodicity in 2d maps
2289 if (m_axiallyPeriodic[0] || m_axiallyPeriodic[1]) {
2290 std::cerr << m_className << "::UpdatePeriodicity2d:\n"
2291 << " Axial symmetry has been requested \n"
2292 << " around x or y for a 2d map; reset.\n";
2293 m_axiallyPeriodic[0] = false;
2294 m_axiallyPeriodic[1] = false;
2295 m_warning = true;
2296 }
2297}
2298
2300 // Initial values
2301 m_mapmin.fill(0.);
2302 m_mapmax.fill(0.);
2303 m_mapamin.fill(0.);
2304 m_mapamax.fill(0.);
2305 m_mapvmin = m_mapvmax = 0.;
2306 m_setang.fill(false);
2307
2308 // Make sure the required data is available.
2309 if (!m_ready || m_nodes.empty()) {
2310 std::cerr << m_className << "::SetRange: Field map not yet set.\n";
2311 return;
2312 }
2313
2314 m_mapvmin = *std::min_element(std::begin(m_pot), std::end(m_pot));
2315 m_mapvmax = *std::max_element(std::begin(m_pot), std::end(m_pot));
2316
2317 // Loop over the nodes.
2318 m_mapmin[0] = m_mapmax[0] = m_nodes[0].x;
2319 m_mapmin[1] = m_mapmax[1] = m_nodes[0].y;
2320 m_mapmin[2] = m_mapmax[2] = m_nodes[0].z;
2321
2322 for (const auto& node : m_nodes) {
2323 const std::array<double, 3> pos = {{node.x, node.y, node.z}};
2324 for (unsigned int i = 0; i < 3; ++i) {
2325 m_mapmin[i] = std::min(m_mapmin[i], pos[i]);
2326 m_mapmax[i] = std::max(m_mapmax[i], pos[i]);
2327 }
2328
2329 if (node.y != 0 || node.z != 0) {
2330 const double ang = atan2(node.z, node.y);
2331 if (m_setang[0]) {
2332 m_mapamin[0] = std::min(m_mapamin[0], ang);
2333 m_mapamax[0] = std::max(m_mapamax[0], ang);
2334 } else {
2335 m_mapamin[0] = m_mapamax[0] = ang;
2336 m_setang[0] = true;
2337 }
2338 }
2339
2340 if (node.z != 0 || node.x != 0) {
2341 const double ang = atan2(node.x, node.z);
2342 if (m_setang[1]) {
2343 m_mapamin[1] = std::min(m_mapamin[1], ang);
2344 m_mapamax[1] = std::max(m_mapamax[1], ang);
2345 } else {
2346 m_mapamin[1] = m_mapamax[1] = ang;
2347 m_setang[1] = true;
2348 }
2349 }
2350
2351 if (node.x != 0 || node.y != 0) {
2352 const double ang = atan2(node.y, node.x);
2353 if (m_setang[2]) {
2354 m_mapamin[2] = std::min(m_mapamin[2], ang);
2355 m_mapamax[2] = std::max(m_mapamax[2], ang);
2356 } else {
2357 m_mapamin[2] = m_mapamax[2] = ang;
2358 m_setang[2] = true;
2359 }
2360 }
2361 }
2362
2363 // Fix the angular ranges.
2364 for (unsigned int i = 0; i < 3; ++i) {
2365 if (m_mapamax[i] - m_mapamin[i] > Pi) {
2366 const double aux = m_mapamin[i];
2367 m_mapamin[i] = m_mapamax[i];
2368 m_mapamax[i] = aux + TwoPi;
2369 }
2370 }
2371
2372 // Set provisional cell dimensions.
2373 m_minBoundingBox[0] = m_mapmin[0];
2374 m_maxBoundingBox[0] = m_mapmax[0];
2375 m_minBoundingBox[1] = m_mapmin[1];
2376 m_maxBoundingBox[1] = m_mapmax[1];
2377 if (m_is3d) {
2378 m_minBoundingBox[2] = m_mapmin[2];
2379 m_maxBoundingBox[2] = m_mapmax[2];
2380 } else {
2381 m_mapmin[2] = m_minBoundingBox[2];
2382 m_mapmax[2] = m_maxBoundingBox[2];
2383 }
2384 m_hasBoundingBox = true;
2385
2386 // Display the range if requested.
2387 if (m_debug) PrintRange();
2388}
2389
2391 std::cout << m_className << "::PrintRange:\n";
2392 std::cout << " Dimensions of the elementary block\n";
2393 printf(" %15g < x < %-15g cm,\n", m_mapmin[0], m_mapmax[0]);
2394 printf(" %15g < y < %-15g cm,\n", m_mapmin[1], m_mapmax[1]);
2395 printf(" %15g < z < %-15g cm,\n", m_mapmin[2], m_mapmax[2]);
2396 printf(" %15g < V < %-15g V.\n", m_mapvmin, m_mapvmax);
2397
2398 std::cout << " Periodicities\n";
2399 const std::array<std::string, 3> axes = {{"x", "y", "z"}};
2400 for (unsigned int i = 0; i < 3; ++i) {
2401 std::cout << " " << axes[i] << ":";
2402 if (m_periodic[i]) {
2403 std::cout << " simple with length " << m_cells[i] << " cm";
2404 }
2405 if (m_mirrorPeriodic[i]) {
2406 std::cout << " mirror with length " << m_cells[i] << " cm";
2407 }
2408 if (m_axiallyPeriodic[i]) {
2409 std::cout << " axial " << int(0.5 + m_mapna[i]) << "-fold repetition";
2410 }
2411 if (m_rotationSymmetric[i]) std::cout << " rotational symmetry";
2412 if (!(m_periodic[i] || m_mirrorPeriodic[i] || m_axiallyPeriodic[i] ||
2414 std::cout << " none";
2415 std::cout << "\n";
2416 }
2417}
2418
2419bool ComponentFieldMap::GetBoundingBox(double& xmin, double& ymin, double& zmin,
2420 double& xmax, double& ymax,
2421 double& zmax) {
2422 if (!m_ready) return false;
2423
2424 xmin = m_minBoundingBox[0];
2425 xmax = m_maxBoundingBox[0];
2426 ymin = m_minBoundingBox[1];
2427 ymax = m_maxBoundingBox[1];
2428 zmin = m_minBoundingBox[2];
2429 zmax = m_maxBoundingBox[2];
2430 return true;
2431}
2432
2433bool ComponentFieldMap::GetElementaryCell(double& xmin, double& ymin,
2434 double& zmin, double& xmax,
2435 double& ymax, double& zmax) {
2436 if (!m_ready) return false;
2437 xmin = m_mapmin[0];
2438 xmax = m_mapmax[0];
2439 ymin = m_mapmin[1];
2440 ymax = m_mapmax[1];
2441 zmin = m_mapmin[2];
2442 zmax = m_mapmax[2];
2443 return true;
2444}
2445
2446void ComponentFieldMap::MapCoordinates(double& xpos, double& ypos, double& zpos,
2447 bool& xmirrored, bool& ymirrored,
2448 bool& zmirrored, double& rcoordinate,
2449 double& rotation) const {
2450 // Initial values
2451 rotation = 0;
2452
2453 // If chamber is periodic, reduce to the cell volume.
2454 xmirrored = false;
2455 if (m_periodic[0]) {
2456 const double xrange = m_mapmax[0] - m_mapmin[0];
2457 xpos = m_mapmin[0] + fmod(xpos - m_mapmin[0], xrange);
2458 if (xpos < m_mapmin[0]) xpos += xrange;
2459 } else if (m_mirrorPeriodic[0]) {
2460 const double xrange = m_mapmax[0] - m_mapmin[0];
2461 double xnew = m_mapmin[0] + fmod(xpos - m_mapmin[0], xrange);
2462 if (xnew < m_mapmin[0]) xnew += xrange;
2463 int nx = int(floor(0.5 + (xnew - xpos) / xrange));
2464 if (nx != 2 * (nx / 2)) {
2465 xnew = m_mapmin[0] + m_mapmax[0] - xnew;
2466 xmirrored = true;
2467 }
2468 xpos = xnew;
2469 }
2470 if (m_axiallyPeriodic[0] && (zpos != 0 || ypos != 0)) {
2471 const double auxr = sqrt(zpos * zpos + ypos * ypos);
2472 double auxphi = atan2(zpos, ypos);
2473 const double phirange = m_mapamax[0] - m_mapamin[0];
2474 const double phim = 0.5 * (m_mapamin[0] + m_mapamax[0]);
2475 rotation = phirange * floor(0.5 + (auxphi - phim) / phirange);
2476 if (auxphi - rotation < m_mapamin[0]) rotation -= phirange;
2477 if (auxphi - rotation > m_mapamax[0]) rotation += phirange;
2478 auxphi = auxphi - rotation;
2479 ypos = auxr * cos(auxphi);
2480 zpos = auxr * sin(auxphi);
2481 }
2482
2483 ymirrored = false;
2484 if (m_periodic[1]) {
2485 const double yrange = m_mapmax[1] - m_mapmin[1];
2486 ypos = m_mapmin[1] + fmod(ypos - m_mapmin[1], yrange);
2487 if (ypos < m_mapmin[1]) ypos += yrange;
2488 } else if (m_mirrorPeriodic[1]) {
2489 const double yrange = m_mapmax[1] - m_mapmin[1];
2490 double ynew = m_mapmin[1] + fmod(ypos - m_mapmin[1], yrange);
2491 if (ynew < m_mapmin[1]) ynew += yrange;
2492 int ny = int(floor(0.5 + (ynew - ypos) / yrange));
2493 if (ny != 2 * (ny / 2)) {
2494 ynew = m_mapmin[1] + m_mapmax[1] - ynew;
2495 ymirrored = true;
2496 }
2497 ypos = ynew;
2498 }
2499 if (m_axiallyPeriodic[1] && (xpos != 0 || zpos != 0)) {
2500 const double auxr = sqrt(xpos * xpos + zpos * zpos);
2501 double auxphi = atan2(xpos, zpos);
2502 const double phirange = (m_mapamax[1] - m_mapamin[1]);
2503 const double phim = 0.5 * (m_mapamin[1] + m_mapamax[1]);
2504 rotation = phirange * floor(0.5 + (auxphi - phim) / phirange);
2505 if (auxphi - rotation < m_mapamin[1]) rotation -= phirange;
2506 if (auxphi - rotation > m_mapamax[1]) rotation += phirange;
2507 auxphi = auxphi - rotation;
2508 zpos = auxr * cos(auxphi);
2509 xpos = auxr * sin(auxphi);
2510 }
2511
2512 zmirrored = false;
2513 if (m_periodic[2]) {
2514 const double zrange = m_mapmax[2] - m_mapmin[2];
2515 zpos = m_mapmin[2] + fmod(zpos - m_mapmin[2], zrange);
2516 if (zpos < m_mapmin[2]) zpos += zrange;
2517 } else if (m_mirrorPeriodic[2]) {
2518 const double zrange = m_mapmax[2] - m_mapmin[2];
2519 double znew = m_mapmin[2] + fmod(zpos - m_mapmin[2], zrange);
2520 if (znew < m_mapmin[2]) znew += zrange;
2521 int nz = int(floor(0.5 + (znew - zpos) / zrange));
2522 if (nz != 2 * (nz / 2)) {
2523 znew = m_mapmin[2] + m_mapmax[2] - znew;
2524 zmirrored = true;
2525 }
2526 zpos = znew;
2527 }
2528 if (m_axiallyPeriodic[2] && (ypos != 0 || xpos != 0)) {
2529 const double auxr = sqrt(ypos * ypos + xpos * xpos);
2530 double auxphi = atan2(ypos, xpos);
2531 const double phirange = m_mapamax[2] - m_mapamin[2];
2532 const double phim = 0.5 * (m_mapamin[2] + m_mapamax[2]);
2533 rotation = phirange * floor(0.5 + (auxphi - phim) / phirange);
2534 if (auxphi - rotation < m_mapamin[2]) rotation -= phirange;
2535 if (auxphi - rotation > m_mapamax[2]) rotation += phirange;
2536 auxphi = auxphi - rotation;
2537 xpos = auxr * cos(auxphi);
2538 ypos = auxr * sin(auxphi);
2539 }
2540
2541 // If we have a rotationally symmetric field map, store coordinates.
2542 rcoordinate = 0;
2543 double zcoordinate = 0;
2544 if (m_rotationSymmetric[0]) {
2545 rcoordinate = sqrt(ypos * ypos + zpos * zpos);
2546 zcoordinate = xpos;
2547 } else if (m_rotationSymmetric[1]) {
2548 rcoordinate = sqrt(xpos * xpos + zpos * zpos);
2549 zcoordinate = ypos;
2550 } else if (m_rotationSymmetric[2]) {
2551 rcoordinate = sqrt(xpos * xpos + ypos * ypos);
2552 zcoordinate = zpos;
2553 }
2554
2557 xpos = rcoordinate;
2558 ypos = zcoordinate;
2559 zpos = 0;
2560 }
2561}
2562
2563void ComponentFieldMap::UnmapFields(double& ex, double& ey, double& ez,
2564 const double xpos, const double ypos, const double zpos,
2565 const bool xmirrored, const bool ymirrored, const bool zmirrored,
2566 const double rcoordinate, const double rotation) const {
2567 // Apply mirror imaging.
2568 if (xmirrored) ex = -ex;
2569 if (ymirrored) ey = -ey;
2570 if (zmirrored) ez = -ez;
2571
2572 // Rotate the field.
2573 double er, theta;
2574 if (m_axiallyPeriodic[0]) {
2575 er = sqrt(ey * ey + ez * ez);
2576 theta = atan2(ez, ey);
2577 theta += rotation;
2578 ey = er * cos(theta);
2579 ez = er * sin(theta);
2580 }
2581 if (m_axiallyPeriodic[1]) {
2582 er = sqrt(ez * ez + ex * ex);
2583 theta = atan2(ex, ez);
2584 theta += rotation;
2585 ez = er * cos(theta);
2586 ex = er * sin(theta);
2587 }
2588 if (m_axiallyPeriodic[2]) {
2589 er = sqrt(ex * ex + ey * ey);
2590 theta = atan2(ey, ex);
2591 theta += rotation;
2592 ex = er * cos(theta);
2593 ey = er * sin(theta);
2594 }
2595
2596 // Take care of symmetry.
2597 double eaxis;
2598 er = ex;
2599 eaxis = ey;
2600
2601 // Rotational symmetry
2602 if (m_rotationSymmetric[0]) {
2603 if (rcoordinate <= 0) {
2604 ex = eaxis;
2605 ey = 0;
2606 ez = 0;
2607 } else {
2608 ex = eaxis;
2609 ey = er * ypos / rcoordinate;
2610 ez = er * zpos / rcoordinate;
2611 }
2612 }
2613 if (m_rotationSymmetric[1]) {
2614 if (rcoordinate <= 0) {
2615 ex = 0;
2616 ey = eaxis;
2617 ez = 0;
2618 } else {
2619 ex = er * xpos / rcoordinate;
2620 ey = eaxis;
2621 ez = er * zpos / rcoordinate;
2622 }
2623 }
2624 if (m_rotationSymmetric[2]) {
2625 if (rcoordinate <= 0) {
2626 ex = 0;
2627 ey = 0;
2628 ez = eaxis;
2629 } else {
2630 ex = er * xpos / rcoordinate;
2631 ey = er * ypos / rcoordinate;
2632 ez = eaxis;
2633 }
2634 }
2635}
2636
2637double ComponentFieldMap::ScalingFactor(std::string unit) {
2638 std::transform(unit.begin(), unit.end(), unit.begin(), toupper);
2639 if (unit == "MUM" || unit == "MICRON" || unit == "MICROMETER") {
2640 return 0.0001;
2641 } else if (unit == "MM" || unit == "MILLIMETER") {
2642 return 0.1;
2643 } else if (unit == "CM" || unit == "CENTIMETER") {
2644 return 1.0;
2645 } else if (unit == "M" || unit == "METER") {
2646 return 100.0;
2647 }
2648 return -1.;
2649}
2650
2651int ComponentFieldMap::ReadInteger(char* token, int def, bool& error) {
2652 if (!token) {
2653 error = true;
2654 return def;
2655 }
2656
2657 return atoi(token);
2658}
2659
2660double ComponentFieldMap::ReadDouble(char* token, double def, bool& error) {
2661 if (!token) {
2662 error = true;
2663 return def;
2664 }
2665 return atof(token);
2666}
2667
2668void ComponentFieldMap::CalculateElementBoundingBoxes() {
2669 // Do not proceed if not properly initialised.
2670 if (!m_ready) {
2671 PrintNotReady("CalculateElementBoundingBoxes");
2672 return;
2673 }
2674
2675 // Calculate the bounding boxes of all elements.
2676 const size_t nElements = m_elements.size();
2677 m_bbMin.resize(nElements);
2678 m_bbMax.resize(nElements);
2679 for (size_t i = 0; i < nElements; ++i) {
2680 const auto& element = m_elements[i];
2681 const Node& n0 = m_nodes[element.emap[0]];
2682 const Node& n1 = m_nodes[element.emap[1]];
2683 const Node& n2 = m_nodes[element.emap[2]];
2684 const Node& n3 = m_nodes[element.emap[3]];
2685 m_bbMin[i][0] = std::min({n0.x, n1.x, n2.x, n3.x});
2686 m_bbMax[i][0] = std::max({n0.x, n1.x, n2.x, n3.x});
2687 m_bbMin[i][1] = std::min({n0.y, n1.y, n2.y, n3.y});
2688 m_bbMax[i][1] = std::max({n0.y, n1.y, n2.y, n3.y});
2689 m_bbMin[i][2] = std::min({n0.z, n1.z, n2.z, n3.z});
2690 m_bbMax[i][2] = std::max({n0.z, n1.z, n2.z, n3.z});
2691 // Add tolerances.
2692 constexpr double f = 0.2;
2693 const double tolx = f * (m_bbMax[i][0] - m_bbMin[i][0]);
2694 m_bbMin[i][0] -= tolx;
2695 m_bbMax[i][0] += tolx;
2696 const double toly = f * (m_bbMax[i][1] - m_bbMin[i][1]);
2697 m_bbMin[i][1] -= toly;
2698 m_bbMax[i][1] += toly;
2699 const double tolz = f * (m_bbMax[i][2] - m_bbMin[i][2]);
2700 m_bbMin[i][2] -= tolz;
2701 m_bbMax[i][2] += tolz;
2702 }
2703}
2704
2705bool ComponentFieldMap::InitializeTetrahedralTree() {
2706 // Do not proceed if not properly initialised.
2707 if (!m_ready) {
2708 PrintNotReady("InitializeTetrahedralTree");
2709 return false;
2710 }
2711
2712 if (m_debug) {
2713 std::cout << m_className << "::InitializeTetrahedralTree:\n"
2714 << " About to initialize the tetrahedral tree.\n";
2715 }
2716
2717 // Cache the bounding boxes if it has not been done yet.
2718 if (!m_cacheElemBoundingBoxes) CalculateElementBoundingBoxes();
2719
2720 if (m_nodes.empty()) {
2721 std::cerr << m_className << "::InitializeTetrahedralTree: Empty mesh.\n";
2722 return false;
2723 }
2724
2725 // Determine the bounding box
2726 auto xmin = m_nodes.front().x;
2727 auto ymin = m_nodes.front().y;
2728 auto zmin = m_nodes.front().z;
2729 auto xmax = xmin;
2730 auto ymax = ymin;
2731 auto zmax = zmin;
2732 for (const auto& node : m_nodes) {
2733 xmin = std::min(xmin, node.x);
2734 xmax = std::max(xmax, node.x);
2735 ymin = std::min(ymin, node.y);
2736 ymax = std::max(ymax, node.y);
2737 zmin = std::min(zmin, node.z);
2738 zmax = std::max(zmax, node.z);
2739 }
2740
2741 if (m_debug) {
2742 std::cout << " Bounding box:\n"
2743 << std::scientific << "\tx: " << xmin << " -> " << xmax << "\n"
2744 << std::scientific << "\ty: " << ymin << " -> " << ymax << "\n"
2745 << std::scientific << "\tz: " << zmin << " -> " << zmax << "\n";
2746 }
2747
2748 const double hx = 0.5 * (xmax - xmin);
2749 const double hy = 0.5 * (ymax - ymin);
2750 const double hz = 0.5 * (zmax - zmin);
2751 m_octree.reset(new TetrahedralTree(Vec3(xmin + hx, ymin + hy, zmin + hz),
2752 Vec3(hx, hy, hz)));
2753
2754 if (m_debug) std::cout << " Tree instantiated.\n";
2755
2756 // Insert all mesh nodes in the tree
2757 for (unsigned int i = 0; i < m_nodes.size(); i++) {
2758 const Node& n = m_nodes[i];
2759 m_octree->InsertMeshNode(Vec3(n.x, n.y, n.z), i);
2760 }
2761
2762 if (m_debug) std::cout << " Tree nodes initialized successfully.\n";
2763
2764 // Insert all mesh elements (tetrahedrons) in the tree
2765 for (unsigned int i = 0; i < m_elements.size(); i++) {
2766 const double bb[6] = {m_bbMin[i][0], m_bbMin[i][1], m_bbMin[i][2],
2767 m_bbMax[i][0], m_bbMax[i][1], m_bbMax[i][2]};
2768 m_octree->InsertMeshElement(bb, i);
2769 }
2770 return true;
2771}
2772
2773void ComponentFieldMap::PrintWarning(const std::string& header) {
2774 if (!m_warning || m_nWarnings > 10) return;
2775 std::cerr << m_className << "::" << header << ":\n"
2776 << " Warnings have been issued for this field map.\n";
2777 ++m_nWarnings;
2778}
2779
2780void ComponentFieldMap::PrintNotReady(const std::string& header) const {
2781 std::cerr << m_className << "::" << header << ":\n"
2782 << " Field map not yet initialised.\n";
2783}
2784
2785void ComponentFieldMap::PrintCouldNotOpen(const std::string& header,
2786 const std::string& filename) const {
2787 std::cerr << m_className << "::" << header << ":\n"
2788 << " Could not open file " << filename << " for reading.\n"
2789 << " The file perhaps does not exist.\n";
2790}
2791
2792void ComponentFieldMap::PrintElement(const std::string& header, const double x,
2793 const double y, const double z,
2794 const double t1, const double t2,
2795 const double t3, const double t4,
2796 const size_t i,
2797 const std::vector<double>& pot) const {
2798 std::cout << m_className << "::" << header << ":\n"
2799 << " Global = (" << x << ", " << y << ", " << z << ")\n"
2800 << " Local = (" << t1 << ", " << t2 << ", " << t3 << ", " << t4
2801 << ")\n";
2802 if (m_degenerate[i]) std::cout << " Element is degenerate.\n";
2803 std::cout << " Node x y z V\n";
2804 unsigned int nN = 0;
2806 if (m_degenerate[i]) {
2807 nN = 6;
2808 } else {
2809 nN = 8;
2810 }
2812 nN = 10;
2813 }
2814 const auto& element = m_elements[i];
2815 for (unsigned int ii = 0; ii < nN; ++ii) {
2816 const Node& node = m_nodes[element.emap[ii]];
2817 const double v = pot[element.emap[ii]];
2818 printf(" %-5d %12g %12g %12g %12g\n", element.emap[ii], node.x, node.y,
2819 node.z, v);
2820 }
2821}
2822
2824 const std::string& label, const std::string& labelSource, const double x,
2825 const double y, const double z, const double alpha, const double beta,
2826 const double gamma) {
2827 // Check if a weighting field with the same label already exists.
2828 if (m_wpot.count(label) > 0) {
2829 std::cout << m_className << "::CopyWeightingPotential:\n"
2830 << " Electrode " << label << " exists already.\n";
2831 return;
2832 }
2833 if (m_wfieldCopies.count(label) > 0) {
2834 std::cout << m_className << "::CopyWeightingPotential:\n"
2835 << " A copy named " << label << " exists already.\n";
2836 return;
2837 }
2838
2839 if (m_wpot.count(labelSource) == 0) {
2840 std::cout << m_className << "::CopyWeightingPotential:\n"
2841 << " Source electrode " << labelSource
2842 << " does not exist.\n";
2843 return;
2844 }
2845
2846 WeightingFieldCopy wfieldCopy;
2847 wfieldCopy.source = labelSource;
2848
2849 TMatrixD Rx(3, 3); // Rotation around the y-axis.
2850 Rx(0, 0) = 1;
2851 Rx(1, 1) = TMath::Cos(-alpha);
2852 Rx(1, 2) = -TMath::Sin(-alpha);
2853 Rx(2, 1) = TMath::Sin(-alpha);
2854 Rx(2, 2) = TMath::Cos(-alpha);
2855
2856 TMatrixD Ry(3, 3); // Rotation around the y-axis.
2857 Ry(1, 1) = 1;
2858 Ry(0, 0) = TMath::Cos(-beta);
2859 Ry(2, 0) = -TMath::Sin(-beta);
2860 Ry(0, 2) = TMath::Sin(-beta);
2861 Ry(2, 2) = TMath::Cos(-beta);
2862
2863 TMatrixD Rz(3, 3); // Rotation around the z-axis.
2864 Rz(2, 2) = 1;
2865 Rz(0, 0) = TMath::Cos(-gamma);
2866 Rz(0, 1) = -TMath::Sin(-gamma);
2867 Rz(1, 0) = TMath::Sin(-gamma);
2868 Rz(1, 1) = TMath::Cos(-gamma);
2869
2870 TVectorD trans(3);
2871 trans(0) = -x;
2872 trans(1) = -y;
2873 trans(2) = -z;
2874 wfieldCopy.rot = Rx * Ry * Rz;
2875 wfieldCopy.trans = trans;
2876 m_wfieldCopies[label] = wfieldCopy;
2877
2878 std::cout << m_className << "::CopyWeightingPotential:\n"
2879 << " Copy named " << label << " of weighting potential "
2880 << labelSource << " made.\n";
2881}
2882
2883void ComponentFieldMap::TimeInterpolation(const double t, double& f0,
2884 double& f1, int& i0, int& i1) {
2885 const auto it1 = std::upper_bound(m_wdtimes.cbegin(), m_wdtimes.cend(), t);
2886 const auto it0 = std::prev(it1);
2887
2888 const double dt = t - *it0;
2889 i0 = it0 - m_wdtimes.cbegin();
2890 i1 = it1 - m_wdtimes.cbegin();
2891
2892 f1 = dt / (*it1 - *it0);
2893 f0 = 1. - f1;
2894}
2895
2896} // namespace Garfield
virtual void GetAspectRatio(const size_t i, double &dmin, double &dmax) const
static double Potential3(const std::array< double, 6 > &v, const std::array< double, 3 > &t)
Interpolate the potential in a triangle.
int FindElement5(const double x, const double y, double &t1, double &t2, double &t3, double &t4, double jac[4][4], double &det) const
Find the element for a point in curved quadratic quadrilaterals.
double GetConductivity(const size_t imat) const
Return the conductivity of a field map material.
virtual bool GetNode(const size_t i, double &x, double &y, double &z) const
double DelayedWeightingPotential(double x, double y, double z, const double t, const std::string &label) override
double WeightingPotential(const double x, const double y, const double z, const std::string &label) override
void PrintRange()
Show x, y, z, V and angular ranges.
void PrintMaterials()
List all currently defined materials.
bool SetDefaultDriftMedium()
Find lowest epsilon, check for eps = 0, set default drift media flags.
std::map< std::string, WeightingFieldCopy > m_wfieldCopies
static void Field5(const std::array< double, 8 > &v, const std::array< double, 2 > &t, double jac[4][4], const double det, double &ex, double &ey)
Interpolate the field in a curved quadrilateral.
void TimeInterpolation(const double t, double &f0, double &f1, int &i0, int &i1)
Interpolation of potential between two time slices.
void NotDriftMedium(const size_t imat)
Flag a field map materials as a non-drift medium.
std::array< double, 3 > m_mapamin
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status) override
static double ReadDouble(char *token, double def, bool &error)
double GetPotential(const size_t i) const
virtual double GetElementVolume(const size_t i) const
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) const
Find the element for a point in curved quadratic tetrahedra.
void DriftMedium(const size_t imat)
Flag a field map material as a drift medium.
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.
std::vector< std::array< double, 3 > > m_bbMax
void PrintWarning(const std::string &header)
std::array< double, 3 > m_maxBoundingBox
std::array< double, 3 > m_mapmin
void SetMedium(const size_t imat, Medium *medium)
Associate a field map material with a Medium object.
std::array< double, 3 > m_minBoundingBox
static double ScalingFactor(std::string unit)
double Potential(const double x, const double y, const double z, const std::vector< double > &potentials) const
Compute the electrostatic/weighting potential.
void UpdatePeriodicity() override
Verify periodicities.
double GetPermittivity(const size_t imat) const
Return the relative permittivity of a field map material.
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
static int ReadInteger(char *token, int def, bool &error)
std::vector< Material > m_materials
static void Field3(const std::array< double, 6 > &v, const std::array< double, 3 > &t, double jac[4][4], const double det, double &ex, double &ey)
Interpolate the field in a triangle.
void UnmapFields(double &ex, double &ey, double &ez, const double xpos, const double ypos, const double zpos, const bool xmirrored, const bool ymirrored, const bool zmirrored, const double rcoordinate, const double rotation) const
Move (ex, ey, ez) to global coordinates.
static void Field13(const std::array< double, 10 > &v, const std::array< double, 4 > &t, double jac[4][4], const double det, double &ex, double &ey, double &ez)
Interpolate the field in a curved quadratic tetrahedron.
void WeightingField(const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
static double Potential5(const std::array< double, 8 > &v, const std::array< double, 2 > &t)
Interpolate the potential in a curved quadrilateral.
void CopyWeightingPotential(const std::string &label, const std::string &labelSource, const double x, const double y, const double z, const double alpha, const double beta, const double gamma)
virtual ~ComponentFieldMap()
Destructor.
static double Potential13(const std::array< double, 10 > &v, const std::array< double, 4 > &t)
Interpolate the potential in a curved quadratic tetrahedron.
int Field(const double x, const double y, const double z, double &fx, double &fy, double &fz, int &iel, const std::vector< double > &potentials) const
Compute the electric/weighting field.
std::array< double, 3 > m_mapna
std::vector< std::array< std::array< double, 3 >, 4 > > m_w12
ComponentFieldMap()=delete
Default constructor.
bool GetElement(const size_t i, double &vol, double &dmin, double &dmax) const
Return the volume and aspect ratio of a mesh element.
void PrintCouldNotOpen(const std::string &header, const std::string &filename) const
std::map< std::string, std::vector< double > > m_wpot
bool Check()
Check element aspect ratio.
int FindElementCube(const double x, const double y, const double z, double &t1, double &t2, double &t3, TMatrixD *&jac, std::vector< TMatrixD * > &dN) const
Find the element for a point in a cube.
std::vector< double > m_wdtimes
std::vector< int > m_elementIndices
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
std::array< double, 3 > m_mapamax
std::array< double, 3 > m_cells
std::array< bool, 3 > m_setang
Medium * GetMedium(const size_t imat) const
Return the Medium associated to a field map material.
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 size_t i, const std::vector< double > &potential) const
std::array< double, 3 > m_mapmax
std::map< std::string, std::vector< std::vector< double > > > m_dwpot
void Reset() override
Reset the component.
std::vector< std::array< double, 3 > > m_bbMin
void PrintNotReady(const std::string &header) const
std::array< bool, 3 > m_rotationSymmetric
Rotation symmetry around x-axis, y-axis, z-axis.
Definition Component.hh:380
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
Definition Component.hh:376
bool m_debug
Switch on/off debugging messages.
Definition Component.hh:371
Component()=delete
Default constructor.
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition Component.hh:374
std::string m_className
Class name.
Definition Component.hh:359
bool m_ready
Ready for use?
Definition Component.hh:368
std::array< bool, 3 > m_axiallyPeriodic
Axial periodicity in x, y, z.
Definition Component.hh:378
Abstract base class for media.
Definition Medium.hh:16
const std::string & GetName() const
Get the medium name/identifier.
Definition Medium.hh:26
bool IsDriftable() const
Is charge carrier transport enabled in this medium?
Definition Medium.hh:77
DoubleAc fabs(const DoubleAc &f)
Definition DoubleAc.h:615
DoubleAc sqrt(const DoubleAc &f)
Definition DoubleAc.cpp:314