57 #include "mesh_neighbours.h"
58 #include "msh_parser.h"
59 #include "tetgen_parser.h"
66 #include <unordered_map>
77 EGS_Mesh::~EGS_Mesh() =
default;
82 std::size_t n_max = std::numeric_limits<int>::max();
83 if (this->
elements.size() >= n_max) {
84 throw std::runtime_error(
"maximum number of elements (" +
85 std::to_string(n_max) +
") exceeded (" +
86 std::to_string(this->
elements.size()) +
")");
88 if (this->
nodes.size() >= n_max) {
89 throw std::runtime_error(
"maximum number of nodes (" +
90 std::to_string(n_max) +
") exceeded (" +
91 std::to_string(this->
nodes.size()) +
")");
100 PercentCounter::PercentCounter(
EGS_InfoFunction info,
const std::string &msg)
101 : info_(info), msg_(msg) {}
103 void PercentCounter::start(EGS_Float goal) {
105 t_start_ = std::chrono::system_clock::now();
107 interactive_ = isatty(STDOUT_FILENO);
109 interactive_ = _isatty(STDOUT_FILENO);
111 if (!info_ || !interactive_) {
114 info_(
"\r%s (0%%)", msg_.c_str());
118 void PercentCounter::step(EGS_Float delta) {
119 if (!info_ || !interactive_) {
123 const int percent =
static_cast<int>((progress_ / goal_) * 100.0);
124 if (percent > old_percent_) {
125 info_(
"\r%s (%d%%)", msg_.c_str(), percent);
126 old_percent_ = percent;
130 void PercentCounter::finish(
const std::string &end_msg) {
134 const auto t_end = std::chrono::system_clock::now();
135 const std::chrono::duration<float> elapsed = t_end - t_start_;
137 info_(
"%s in %0.3fs\n", end_msg.c_str(), elapsed.count());
142 info_(
"\r%s in %0.3fs\n", end_msg.c_str(), elapsed.count());
153 const EGS_Float eps = 1e-8;
155 inline bool approx_eq(
double a,
double b,
double e = eps) {
156 return (std::abs(a - b) <= e * (std::abs(a) + std::abs(b) + 1.0));
160 return approx_eq(0.0, v.length(), eps);
163 inline EGS_Float min3(EGS_Float a, EGS_Float b, EGS_Float c) {
164 return std::min(std::min(a, b), c);
167 inline EGS_Float max3(EGS_Float a, EGS_Float b, EGS_Float c) {
168 return std::max(std::max(a, b), c);
180 return (x - y).length2();
184 return std::sqrt(distance2(x, y));
193 EGS_Float d1 = dot(ab, ao);
194 EGS_Float d2 = dot(ac, ao);
195 if (d1 <= 0.0 && d2 <= 0.0) {
201 EGS_Float d3 = dot(ab, bo);
202 EGS_Float d4 = dot(ac, bo);
203 if (d3 >= 0.0 && d4 <= d3) {
208 EGS_Float vc = d1 * d4 - d3 * d2;
209 if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) {
210 EGS_Float v = d1 / (d1 - d3);
216 EGS_Float d5 = dot(ab, co);
217 EGS_Float d6 = dot(ac, co);
218 if (d6 >= 0.0 && d5 <= d6) {
223 EGS_Float vb = d5 * d2 - d1 * d6;
224 if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) {
225 EGS_Float w = d2 / (d2 - d6);
230 EGS_Float va = d3 * d6 - d5 * d4;
231 if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) {
232 EGS_Float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
233 return B + w * (C - B);
237 EGS_Float denom = 1.0 / (va + vb + vc);
238 EGS_Float v = vb * denom;
239 EGS_Float w = vc * denom;
240 return A + v * ab + w * ac;
246 return dot(P - A, cross(B - A, C - A)) * dot(D - A, cross(B - A, C - A)) < 0.0;
251 EGS_Float min = std::numeric_limits<EGS_Float>::max();
254 EGS_Vector q = closest_point_triangle(P, A, B, C);
255 EGS_Float dis = distance2(q, P);
262 if (point_outside_of_plane(P, A, B, C, D)) {
263 maybe_update_min_point(A, B, C);
266 if (point_outside_of_plane(P, A, C, D, B)) {
267 maybe_update_min_point(A, C, D);
270 if (point_outside_of_plane(P, A, B, D, C)) {
271 maybe_update_min_point(A, B, D);
273 if (point_outside_of_plane(P, B, D, C, A)) {
274 maybe_update_min_point(B, D, C);
294 int exterior_triangle_ray_intersection(
const EGS_Vector &p,
297 const EGS_Float eps = 1e-10;
302 EGS_Float det = dot(ab, pvec);
304 if (det > -eps && det < eps) {
307 EGS_Float inv_det = 1.0 / det;
309 EGS_Float u = dot(tvec, pvec) * inv_det;
310 if (u < 0.0 || u > 1.0) {
314 EGS_Float v = dot(v_norm, qvec) * inv_det;
315 if (v < 0.0 || u + v > 1.0) {
319 dist = dot(ac, qvec) * inv_det;
330 class EGS_Mesh_Octree {
333 return std::min(n.A.
x, std::min(n.B.
x, std::min(n.C.
x, n.D.
x)));
336 return std::max(n.A.
x, std::max(n.B.
x, std::max(n.C.
x, n.D.
x)));
339 return std::min(n.A.
y, std::min(n.B.
y, std::min(n.C.
y, n.D.
y)));
342 return std::max(n.A.
y, std::max(n.B.
y, std::max(n.C.
y, n.D.
y)));
345 return std::min(n.A.
z, std::min(n.B.
z, std::min(n.C.
z, n.D.
z)));
348 return std::max(n.A.
z, std::max(n.B.
z, std::max(n.C.
z, n.D.
z)));
359 BoundingBox() =
default;
360 BoundingBox(
double min_x,
double max_x,
double min_y,
double max_y,
361 double min_z,
double max_z) : min_x(min_x), max_x(max_x),
362 min_y(min_y), max_y(max_y), min_z(min_z), max_z(max_z) {}
363 double mid_x()
const {
364 return (min_x + max_x) / 2.0;
366 double mid_y()
const {
367 return (min_y + max_y) / 2.0;
369 double mid_z()
const {
370 return (min_z + max_z) / 2.0;
372 double volume()
const {
373 return (max_x - min_x) * (max_y - min_y) * (max_z - min_z);
375 void expand(
double delta) {
383 void print(std::ostream &out = std::cout)
const {
385 std::setprecision(std::numeric_limits<double>::max_digits10) <<
386 "min_x: " << min_x <<
"\n";
388 std::setprecision(std::numeric_limits<double>::max_digits10) <<
389 "max_x: " << max_x <<
"\n";
391 std::setprecision(std::numeric_limits<double>::max_digits10) <<
392 "min_y: " << min_y <<
"\n";
394 std::setprecision(std::numeric_limits<double>::max_digits10) <<
395 "max_y: " << max_y <<
"\n";
397 std::setprecision(std::numeric_limits<double>::max_digits10) <<
398 "min_z: " << min_z <<
"\n";
400 std::setprecision(std::numeric_limits<double>::max_digits10) <<
401 "max_z: " << max_z <<
"\n";
425 if (min3(a.
x, b.
x, c.
x) >= max_x ||
426 min3(a.
y, b.
y, c.
y) >= max_y ||
427 min3(a.
z, b.
z, c.
z) >= max_z ||
428 max3(a.
x, b.
x, c.
x) <= min_x ||
429 max3(a.
y, b.
y, c.
y) <= min_y ||
430 max3(a.
z, b.
z, c.
z) <= min_z) {
436 EGS_Float ex = (max_x - min_x) / 2.0;
437 EGS_Float ey = (max_y - min_y) / 2.0;
438 EGS_Float ez = (max_z - min_z) / 2.0;
446 const std::array<EGS_Vector, 3> edge_vecs { v1-v0, v2-v1, v0-v2 };
450 const EGS_Vector ux {1, 0, 0}, uy {0, 1, 0}, uz {0, 0, 1};
451 const std::array<EGS_Vector, 3> unit_vecs { ux, uy, uz};
462 const EGS_Float r = ex * std::abs(dot(ux, a)) +
463 ey * std::abs(dot(uy, a)) + ez * std::abs(dot(uz, a));
465 const EGS_Float p0 = dot(v0, a);
466 const EGS_Float p1 = dot(v1, a);
467 const EGS_Float p2 = dot(v2, a);
468 if (std::max(-max3(p0, p1, p2), min3(p0, p1, p2)) + eps > r) {
474 if (max3(v0.
x, v1.
x, v2.
x) <= -ex || min3(v0.
x, v1.
x, v2.
x) >= ex ||
475 max3(v0.
y, v1.
y, v2.
y) <= -ey || min3(v0.
y, v1.
y, v2.
y) >= ey ||
476 max3(v0.
z, v1.
z, v2.
z) <= -ez || min3(v0.
z, v1.
z, v2.
z) >= ez) {
485 const EGS_Vector n = cross(edge_vecs[0], edge_vecs[1]);
487 const EGS_Float r = ex * std::abs(n.
x) + ey * std::abs(n.
y) +
494 const EGS_Float s = dot(n, centre) - dot(n, a);
496 return std::abs(s) <= r;
500 return intersects_triangle(tet.A, tet.B, tet.C) ||
501 intersects_triangle(tet.A, tet.C, tet.D) ||
502 intersects_triangle(tet.A, tet.B, tet.D) ||
503 intersects_triangle(tet.B, tet.C, tet.D);
515 EGS_Float tmin = 0.0;
516 EGS_Float tmax = std::numeric_limits<EGS_Float>::max();
517 std::array<EGS_Float, 3> p_vec {p.
x, p.
y, p.
z};
518 std::array<EGS_Float, 3> v_vec {v.
x, v.
y, v.
z};
519 std::array<EGS_Float, 3> mins {min_x, min_y, min_z};
520 std::array<EGS_Float, 3> maxs {max_x, max_y, max_z};
521 for (std::size_t i = 0; i < 3; i++) {
524 if (std::abs(v_vec[i]) < eps) {
526 if (p_vec[i] < mins[i] || p_vec[i] > maxs[i]) {
532 EGS_Float inv_vel = 1.0 / v_vec[i];
533 EGS_Float t1 = (mins[i] - p_vec[i]) * inv_vel;
534 EGS_Float t2 = (maxs[i] - p_vec[i]) * inv_vel;
539 tmin = std::max(tmin, t1);
540 tmax = std::min(tmax, t2);
556 EGS_Float min_interior_distance(
const EGS_Vector &point)
const {
557 return std::min(point.
x - min_x, std::min(point.
y - min_y,
558 std::min(point.
z - min_z, std::min(max_x - point.
x,
559 std::min(max_y - point.
y, max_z - point.
z)))));
569 std::array<EGS_Float, 3> p = {point.
x, point.
y, point.
z};
570 std::array<EGS_Float, 3> mins = {min_x, min_y, min_z};
571 std::array<EGS_Float, 3> maxs = {max_x, max_y, max_z};
573 std::array<EGS_Float, 3> q = p;
574 for (
int i = 0; i < 3; i++) {
575 if (p[i] < mins[i]) {
578 if (p[i] > maxs[i]) {
585 bool contains(
const EGS_Vector &point)
const {
595 return point.
x >= min_x && point.
x < max_x &&
596 point.
y >= min_y && point.
y < max_y &&
597 point.
z >= min_z && point.
z < max_z;
600 bool is_indivisible()
const {
602 return approx_eq(min_x, mid_x()) ||
603 approx_eq(max_x, mid_x()) ||
604 approx_eq(min_y, mid_y()) ||
605 approx_eq(max_y, mid_y()) ||
606 approx_eq(min_z, mid_z()) ||
607 approx_eq(max_z, mid_z());
621 std::array<BoundingBox, 8> divide8()
const {
667 std::vector<int> elts_;
668 std::vector<Node> children_;
672 Node(
const std::vector<int> &elts,
const BoundingBox &bbox,
673 std::size_t n_max,
const EGS_Mesh &mesh,
674 egs_mesh::internal::PercentCounter &progress) : bbox_(bbox) {
676 if (bbox_.is_indivisible() || elts.size() < n_max) {
678 progress.step(bbox_.volume());
682 std::array<std::vector<int>, 8> octants;
683 std::array<BoundingBox, 8> bbs = bbox_.divide8();
686 for (
const auto &e : elts) {
687 for (
int i = 0; i < 8; i++) {
689 octants[i].push_back(e);
693 for (
int i = 0; i < 8; i++) {
694 children_.push_back(Node(
695 std::move(octants[i]), bbs[i], n_max, mesh, progress
700 bool isLeaf()
const {
701 return children_.empty();
704 void print(std::ostream &out,
int level)
const {
705 out <<
"Level " << level <<
"\n";
707 if (children_.empty()) {
708 out <<
"num_elts: " << elts_.size() <<
"\n";
709 for (
const auto &e: elts_) {
715 for (
int i = 0; i < 8; i++) {
716 children_.at(i).print(out, level + 1);
725 std::size_t octant = 0;
726 if (p.
x >= bbox_.mid_x()) {
729 if (p.
y >= bbox_.mid_y()) {
732 if (p.
z >= bbox_.mid_z()) {
739 std::vector<int> findOtherIntersectedOctants(
const EGS_Vector &p,
740 const EGS_Vector &v,
int exclude_octant)
const {
742 throw std::runtime_error(
743 "findOtherIntersectedOctants called on leaf node");
749 std::vector<std::pair<EGS_Float, int>> intersections;
750 for (
int i = 0; i < 8; i++) {
751 if (i == exclude_octant) {
756 if (children_[i].bbox_.ray_intersection(p, v, dist, intersection)) {
757 intersections.push_back({dist, i});
760 std::sort(intersections.begin(), intersections.end());
761 std::vector<int> octants;
762 for (
const auto &i : intersections) {
763 octants.push_back(i.second);
771 const EGS_Float best_dist = bbox_.min_interior_distance(p);
775 EGS_Float best_dist2 = best_dist * best_dist;
776 for (
const auto &e: elts_) {
778 best_dist2 = std::min(best_dist2, distance2(p,
779 closest_point_tetrahedron(p, n.A, n.B, n.C, n.D)));
781 return std::sqrt(best_dist2);
788 return hownear_leaf_search(p, mesh);
791 const auto octant = findOctant(p);
792 return children_[octant].hownear_exterior(p, mesh);
800 for (
const auto &e: elts_) {
809 return children_[findOctant(p)].isWhere(p, mesh);
814 const EGS_Float &max_dist, EGS_Float &t,
EGS_Mesh &mesh)
817 EGS_Float min_dist = std::numeric_limits<EGS_Float>::max();
820 for (
const auto &e: elts_) {
827 auto intersection = mesh.closest_boundary_face(e, p, v);
828 if (intersection.dist < min_dist) {
830 min_dist = intersection.dist;
839 auto hit = bbox_.ray_intersection(p, v, dist, intersection);
846 auto octant = findOctant(intersection);
847 auto elt = children_[octant].howfar_exterior(
848 p, v, max_dist, t, mesh
857 for (
const auto &o : findOtherIntersectedOctants(p, v, octant)) {
858 auto elt = children_[o].howfar_exterior(
859 p, v, max_dist, t, mesh
872 EGS_Mesh_Octree() =
default;
873 EGS_Mesh_Octree(
const std::vector<int> &elts, std::size_t n_max,
874 const EGS_Mesh &mesh, egs_mesh::internal::PercentCounter &progress) {
876 throw std::runtime_error(
"EGS_Mesh_Octree: empty elements vector");
878 if (elts.size() > std::numeric_limits<int>::max()) {
879 throw std::runtime_error(
"EGS_Mesh_Octree: num elts must fit into an int");
882 const EGS_Float INF = std::numeric_limits<EGS_Float>::infinity();
883 BoundingBox g_bounds(INF, -INF, INF, -INF, INF, -INF);
884 for (
const auto &e : elts) {
886 g_bounds.min_x = std::min(g_bounds.min_x, tet_min_x(nodes));
887 g_bounds.max_x = std::max(g_bounds.max_x, tet_max_x(nodes));
888 g_bounds.min_y = std::min(g_bounds.min_y, tet_min_y(nodes));
889 g_bounds.max_y = std::max(g_bounds.max_y, tet_max_y(nodes));
890 g_bounds.min_z = std::min(g_bounds.min_z, tet_min_z(nodes));
891 g_bounds.max_z = std::max(g_bounds.max_z, tet_max_z(nodes));
895 g_bounds.expand(1e-8);
898 progress.start(g_bounds.volume());
899 root_ = Node(elts, g_bounds, n_max, mesh, progress);
903 if (!root_.bbox_.contains(p)) {
906 return root_.isWhere(p, mesh);
909 void print(std::ostream &out)
const {
914 const EGS_Float &max_dist, EGS_Float &t,
EGS_Mesh &mesh)
const {
917 auto hit = root_.bbox_.ray_intersection(p, v, dist, intersection);
918 if (!hit || dist > max_dist) {
921 return root_.howfar_exterior(p, v, max_dist, t, mesh);
935 if (!root_.bbox_.contains(p)) {
936 return distance(root_.bbox_.closest_point(p), p);
939 return root_.hownear_exterior(p, mesh);
947 initializeElements(std::move(spec.
elements), std::move(spec.
nodes),
948 std::move(spec.
media));
949 initializeNeighbours();
954 void EGS_Mesh::initializeElements(
955 std::vector<EGS_MeshSpec::Tetrahedron> elements,
956 std::vector<EGS_MeshSpec::Node> nodes,
957 std::vector<EGS_MeshSpec::Medium> materials) {
960 elt_tags_.reserve(elements.size());
961 elt_node_indices_.reserve(elements.size());
962 nodes_.reserve(nodes.size());
964 std::unordered_map<int, int> node_map;
965 node_map.reserve(nodes.size());
966 for (
int i = 0; i < static_cast<int>(nodes.size()); i++) {
967 const auto &n = nodes[i];
968 node_map.insert({n.tag, i});
971 if (node_map.size() != nodes.size()) {
972 throw std::runtime_error(
"duplicate nodes in node list");
975 auto find_node = [&](
int node_tag) ->
int {
976 auto node_it = node_map.find(node_tag);
977 if (node_it == node_map.end()) {
978 throw std::runtime_error(
"No mesh node with tag: " + std::to_string(node_tag));
980 return node_it->second;
982 for (
int i = 0; i < static_cast<int>(elements.size()); i++) {
983 const auto &e = elements[i];
984 elt_tags_.push_back(e.tag);
985 elt_node_indices_.push_back({
986 find_node(e.a), find_node(e.b), find_node(e.c), find_node(e.d)
990 initializeMedia(std::move(elements), std::move(materials));
993 void EGS_Mesh::initializeMedia(std::vector<EGS_MeshSpec::Tetrahedron> elements,
994 std::vector<EGS_MeshSpec::Medium> materials) {
995 std::unordered_map<int, int> medium_offsets;
996 for (
const auto &m : materials) {
1000 bool inserted = medium_offsets.insert({m.tag, media_offset}).second;
1002 throw std::runtime_error(
"duplicate medium tag: "
1003 + std::to_string(m.tag));
1007 medium_indices_.reserve(elements.size());
1008 for (
const auto &e: elements) {
1009 medium_indices_.push_back(medium_offsets.at(e.medium_tag));
1013 void EGS_Mesh::initializeNeighbours() {
1014 std::vector<mesh_neighbours::Tetrahedron> neighbour_elts;
1016 for (
const auto &e: elt_node_indices_) {
1017 neighbour_elts.emplace_back(mesh_neighbours::Tetrahedron(e[0], e[1], e[2], e[3]));
1020 egs_mesh::internal::PercentCounter progress(get_logger(),
1021 "EGS_Mesh: finding element neighbours");
1023 neighbours_ = mesh_neighbours::tetrahedron_neighbours(
1024 neighbour_elts, progress);
1026 progress.finish(
"EGS_Mesh: found element neighbours");
1029 for (
const auto &ns: neighbours_) {
1030 for (
const auto &n: ns) {
1031 boundary_faces_.push_back(n == mesh_neighbours::NONE);
1036 void EGS_Mesh::initializeNormals() {
1038 for (
int i = 0; i < static_cast<int>(
num_elements()); i++) {
1043 if (dot(normal, d-a) < 0) {
1049 face_normals_.push_back({
1050 get_normal(n.B, n.C, n.D, n.A),
1051 get_normal(n.A, n.C, n.D, n.B),
1052 get_normal(n.A, n.B, n.D, n.C),
1053 get_normal(n.A, n.B, n.C, n.D)
1058 void EGS_Mesh::initializeOctrees() {
1059 std::vector<int> elts;
1060 std::vector<int> boundary_elts;
1065 boundary_elts.push_back(i);
1069 std::size_t n_vol = 200;
1070 egs_mesh::internal::PercentCounter vol_progress(get_logger(),
1071 "EGS_Mesh: building volume octree");
1072 volume_tree_ = std::unique_ptr<EGS_Mesh_Octree>(
1073 new EGS_Mesh_Octree(elts, n_vol, *
this, vol_progress)
1075 vol_progress.finish(
"EGS_Mesh: built volume octree");
1077 std::size_t n_surf = 100;
1078 egs_mesh::internal::PercentCounter surf_progress(get_logger(),
1079 "EGS_Mesh: building surface octree");
1080 surface_tree_ = std::unique_ptr<EGS_Mesh_Octree>(
1081 new EGS_Mesh_Octree(boundary_elts, n_surf, *
this, surf_progress)
1083 surf_progress.finish(
"EGS_Mesh: built surface octree");
1086 bool EGS_Mesh::isInside(
const EGS_Vector &x) {
1087 return isWhere(x) != -1;
1094 int EGS_Mesh::medium(
int ireg)
const {
1095 return medium_indices_.at(ireg);
1100 if (point_outside_of_plane(x, n.A, n.B, n.C, n.D)) {
1103 if (point_outside_of_plane(x, n.A, n.C, n.D, n.B)) {
1106 if (point_outside_of_plane(x, n.A, n.B, n.D, n.C)) {
1109 if (point_outside_of_plane(x, n.B, n.C, n.D, n.A)) {
1116 return volume_tree_->isWhere(x, *
this);
1119 EGS_Float EGS_Mesh::hownear(
int ireg,
const EGS_Vector &x) {
1122 return min_interior_face_dist(ireg, x);
1125 return min_exterior_face_dist(x);
1130 EGS_Float distance_to_plane(
const EGS_Vector &x,
1132 return std::abs(dot(unit_plane_normal, x - plane_point));
1135 EGS_Float EGS_Mesh::min_interior_face_dist(
int ireg,
const EGS_Vector &x) {
1139 EGS_Float min_dist = distance_to_plane(x, face_normals_[ireg][0], n.B);
1140 min_dist = std::min(min_dist,
1141 distance_to_plane(x, face_normals_[ireg][1], n.A));
1142 min_dist = std::min(min_dist,
1143 distance_to_plane(x, face_normals_[ireg][2], n.A));
1144 min_dist = std::min(min_dist,
1145 distance_to_plane(x, face_normals_[ireg][3], n.A));
1150 EGS_Float EGS_Mesh::min_exterior_face_dist(
const EGS_Vector &x) {
1151 return surface_tree_->hownear_exterior(x, *
this);
1155 EGS_Float &t,
int *newmed ,
EGS_Vector *normal ) {
1157 return howfar_exterior(x, u, t, newmed, normal);
1163 EGS_Float distance_to_boundary =
veryFar;
1164 auto new_reg = howfar_interior(
1165 ireg, x, u, distance_to_boundary, newmed, normal);
1167 if (distance_to_boundary < t) {
1168 t = distance_to_boundary;
1185 EGS_Float &t,
int *newmed,
EGS_Vector *normal) {
1222 std::array<EGS_Vector, 4> face_points {n.B, n.A, n.A, n.A};
1223 std::array<PointLocation, 4> intersect_tests {};
1224 for (
int i = 0; i < 4; ++i) {
1225 intersect_tests[i] = find_point_location(
1226 x, u, face_points[i], face_normals_[ireg][i]);
1230 if (intersect_tests[0].signed_distance < 0.0 ||
1231 intersect_tests[1].signed_distance < 0.0 ||
1232 intersect_tests[2].signed_distance < 0.0 ||
1233 intersect_tests[3].signed_distance < 0.0) {
1234 return howfar_interior_thick_plane(intersect_tests, ireg, x, u, t,
1240 auto ix = find_interior_intersection(intersect_tests);
1241 if (ix.dist < 0.0 || ix.face_index == -1) {
1242 egsWarning(
"\nEGS_Mesh warning: bad interior intersection t = %.17g, face_index = %d in region %d: "
1243 "x=(%.17g,%.17g,%.17g) u=(%.17g,%.17g,%.17g)\n", ix.dist, ix.face_index,
1244 ireg, x.
x, x.
y, x.
z, u.
x, u.
y, u.
z);
1250 if (ix.dist < EGS_Mesh::min_step_size) {
1251 ix.dist = EGS_Mesh::min_step_size;
1254 int new_reg = neighbours_[ireg].at(ix.face_index);
1255 update_medium(new_reg, newmed);
1256 update_normal(face_normals_[ireg].at(ix.face_index), u, normal);
1261 EGS_Mesh::PointLocation EGS_Mesh::find_point_location(
const EGS_Vector &x,
1270 EGS_Float direction_dot_normal = dot(u, plane_normal);
1284 EGS_Float signed_distance = dot(plane_normal, x - plane_point);
1285 return PointLocation(direction_dot_normal, signed_distance);
1292 EGS_Mesh::Intersection EGS_Mesh::find_interior_intersection(
1293 const std::array<PointLocation, 4> &ixs) {
1295 int min_face_index = -1;
1296 for (
int i = 0; i < 4; ++i) {
1299 if (ixs[i].direction_dot_normal >= 0.0) {
1302 EGS_Float t_i = -ixs[i].signed_distance / ixs[i].direction_dot_normal;
1309 return Intersection(t_min, min_face_index);
1313 int EGS_Mesh::howfar_interior_thick_plane(
const std::array<PointLocation, 4> &
1315 EGS_Float &t,
int *newmed,
EGS_Vector *normal) {
1331 EGS_Float max_neg_dist =
veryFar;
1332 int face_index = -1;
1333 for (
int i = 0; i < 4; ++i) {
1334 if (intersect_tests[i].signed_distance < max_neg_dist) {
1335 max_neg_dist = intersect_tests[i].signed_distance;
1339 if (face_index == -1) {
1340 egsWarning(
"\nEGS_Mesh warning: howfar_interior_thick_plane face_index %d in region %d: "
1341 "x=(%.17g,%.17g,%.17g) u=(%.17g,%.17g,%.17g)\n",
1342 face_index, ireg, x.
x, x.
y, x.
z, u.
x, u.
y, u.
z);
1353 constexpr EGS_Float thick_plane_bounds = EGS_Mesh::min_step_size;
1354 if (max_neg_dist < -thick_plane_bounds ||
1355 intersect_tests.at(face_index).direction_dot_normal < -thick_plane_bounds) {
1356 return howfar_interior_recover_lost_particle(ireg, x, u, t, newmed);
1370 int min_face_index = -1;
1371 for (
int i = 0; i < 4; ++i) {
1374 if (intersect_tests[i].direction_dot_normal >= 0.0) {
1378 EGS_Float t_i = -intersect_tests[i].signed_distance
1379 / intersect_tests[i].direction_dot_normal;
1390 if (t_min < EGS_Mesh::min_step_size) {
1391 return howfar_interior_recover_lost_particle(ireg, x, u, t, newmed);
1393 if (min_face_index == -1) {
1394 egsWarning(
"\nEGS_Mesh warning: face_index %d in region %d: "
1395 "x=(%.17g,%.17g,%.17g) u=(%.17g,%.17g,%.17g)\n",
1396 min_face_index, ireg, x.
x, x.
y, x.
z, u.
x, u.
y, u.
z);
1401 int new_reg = neighbours_[ireg].at(min_face_index);
1402 update_medium(new_reg, newmed);
1403 update_normal(face_normals_[ireg].at(min_face_index), u, normal);
1423 int EGS_Mesh::howfar_interior_recover_lost_particle(
int ireg,
1425 t = EGS_Mesh::min_step_size;
1429 for (
int i = 0; i < 4; ++i) {
1430 const auto neighbour = neighbours_[ireg][i];
1431 if (neighbour == -1) {
1435 update_medium(neighbour, newmed);
1442 auto actual_elt = isWhere(new_pos);
1443 update_medium(actual_elt, newmed);
1452 EGS_Mesh::Intersection EGS_Mesh::closest_boundary_face(
int ireg,
const EGS_Vector &x,
1455 EGS_Float min_dist = std::numeric_limits<EGS_Float>::max();
1457 auto dist = min_dist;
1458 auto closest_face = -1;
1462 if (boundary_faces_[4*ireg + face] &&
1465 point_outside_of_plane(x, A, B, C, D) &&
1466 dot(face_normals_[ireg][face], u) > 0.0 &&
1467 exterior_triangle_ray_intersection(x, u, A, B, C, dist) &&
1470 closest_face = face;
1476 check_face_intersection(0, n.B, n.C, n.D, n.A);
1477 check_face_intersection(1, n.A, n.C, n.D, n.B);
1478 check_face_intersection(2, n.A, n.B, n.D, n.C);
1479 check_face_intersection(3, n.A, n.B, n.C, n.D);
1481 return EGS_Mesh::Intersection(min_dist, closest_face);
1486 EGS_Float &t,
int *newmed,
EGS_Vector *normal) {
1487 EGS_Float min_dist = 1e30;
1488 auto min_reg = surface_tree_->howfar_exterior(x, u, t, min_dist, *
this);
1491 if (min_dist > t || min_reg == -1) {
1498 *newmed = medium(min_reg);
1501 auto intersection = closest_boundary_face(min_reg, x, u);
1502 EGS_Vector tmp_normal = face_normals_[min_reg]
1503 .at(intersection.face_index);
1505 if (dot(tmp_normal, u) > 0) {
1506 tmp_normal = -1.0 * tmp_normal;
1508 *normal = tmp_normal;
1513 const std::string EGS_Mesh::type =
"EGS_Mesh";
1515 void EGS_Mesh::printInfo()
const {
1517 std::ostringstream oss;
1529 static EGS_MeshSpec parse_mesh_file(
const std::string &mesh_file) {
1531 auto ends_with = [](
const std::string& str,
const std::string& suffix)
1533 if (suffix.size() > str.size()) {
1536 return str.compare(str.size() - suffix.size(), str.size(), suffix) == 0;
1539 if (ends_with(mesh_file,
".msh")) {
1540 std::ifstream file_stream(mesh_file);
1542 throw std::runtime_error(std::string(
"mesh file: `") + mesh_file
1543 +
"` does not exist or is not readable");
1548 catch (
const std::runtime_error &e) {
1549 throw std::runtime_error(std::string(
"Gmsh msh file parsing failed")
1550 +
"\nerror: " + e.what() +
"\n");
1554 if (ends_with(mesh_file,
".ele")) {
1555 return tetgen_parser::parse_tetgen_files(mesh_file,
1559 if (ends_with(mesh_file,
".node")) {
1560 return tetgen_parser::parse_tetgen_files(mesh_file,
1564 throw std::runtime_error(std::string(
"unknown extension for mesh file `")
1565 + mesh_file +
"`, supported extensions are msh, ele, node");
1571 egsWarning(
"createGeometry(EGS_Mesh): null input\n");
1574 std::string mesh_file;
1575 int err = input->
getInput(
"file", mesh_file);
1577 egsWarning(
"createGeometry(EGS_Mesh): no mesh file key `file` in input\n");
1583 mesh_spec = parse_mesh_file(mesh_file);
1585 catch (
const std::runtime_error &e) {
1586 std::string error_msg = std::string(
"createGeometry(EGS_Mesh): ") +
1592 EGS_Float scale = 0.0;
1593 err = input->
getInput(
"scale", scale);
1596 mesh_spec.
scale(scale);
1599 egsFatal(
"createGeometry(EGS_Mesh): invalid scale value (%g), "
1600 "expected a positive number\n", scale);
1606 mesh =
new EGS_Mesh(std::move(mesh_spec));
1608 catch (
const std::runtime_error &e) {
1609 std::string error_msg = std::string(
"createGeometry(EGS_Mesh): ") +
1610 "bad input to EGS_Mesh\nerror: " + e.what() +
"\n";
virtual void printInfo() const
Print information about this geometry.
A container for raw unstructured tetrahedral mesh data.
EGS_Vector methods for the manipulation of 3D vectors in cartesian co-ordinates.
void scale(EGS_Float factor)
Multiply all node coordinates by a constant factor.
Tetrahedral mesh geometry: header.
A class representing 3D vectors.
int setLabels(EGS_Input *input)
Set the labels from an input block.
void printElement(int i, std::ostream &elt_info=std::cout) const
Print information about element i to the stream elt_info.
const EGS_Float veryFar
A very large float.
EGS_GLIB_EXPORT EGS_BaseGeometry * createGeometry(EGS_Input *input)
Base geometry class. Every geometry class must be derived from EGS_BaseGeometry.
void setBoundaryTolerance(EGS_Input *inp)
Set the value of the boundary tolerance from the input inp.
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
A tetrahedral mesh geometry.
static int addMedium(const string &medname)
Add a medium or get the index of an existing medium.
EGS_InfoFunction EGS_EXPORT egsInformation
Always use this function for reporting the progress of a simulation and any other type of information...
EGS_Mesh(EGS_MeshSpec spec)
void setName(EGS_Input *inp)
Set the name of the geometry from the input inp.
Nodes element_nodes(int element) const
Given an element offset, return the element's node coordinates.
std::vector< EGS_MeshSpec::Tetrahedron > elements
Unique mesh elements.
std::vector< EGS_MeshSpec::Node > nodes
Unique nodes.
bool is_boundary(int reg) const
bool insideElement(int i, const EGS_Vector &x)
Check if a point x is inside element i.
static int error_flag
Set to non-zero status if a geometry problem is encountered.
void(* EGS_InfoFunction)(const char *,...)
Defines a function printf-like prototype for functions to be used to report info, warnings...
int nreg
Number of local regions in this geometry.
int num_elements() const
Returns the number of mesh elements.
std::vector< EGS_MeshSpec::Medium > media
Unique medium information.
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.