EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
mesh_neighbours.h
1 /*
2 ###############################################################################
3 #
4 # EGSnrc tetrahedral mesh nearest-neighbour algorithm
5 # Copyright (C) 2020 Mevex Corporation
6 #
7 # This file is part of EGSnrc.
8 #
9 # EGSnrc is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU Affero General Public License as published by the
11 # Free Software Foundation, either version 3 of the License, or (at your
12 # option) any later version.
13 #
14 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU Affero General Public License
20 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
21 #
22 ###############################################################################
23 #
24 # Authors: Dave Macrillo, 2020
25 # Matt Ronan,
26 # Nigel Vezeau,
27 # Lou Thompson,
28 # Max Orok
29 #
30 # Contributors:
31 #
32 ###############################################################################
33 */
34 
35 // exclude from doxygen
37 
38 #ifndef MESH_NEIGHBOURS_
39 #define MESH_NEIGHBOURS_
40 
41 #include "egs_mesh.h"
42 
43 #include <algorithm>
44 #include <array>
45 #include <iostream>
46 #include <stdexcept>
47 #include <vector>
48 
49 namespace mesh_neighbours {
50 
51 // Magic number for no neighbour.
52 constexpr int NONE = -1;
53 
54 class Tetrahedron {
55 public:
56  class Face {
57  public:
58  Face() {}
59  Face(int a, int b, int c) {
60  std::array<int, 3> sorted {a, b, c};
61  // sort to ease comparison between faces
62  std::sort(sorted.begin(), sorted.end());
63  nodes_ = sorted;
64  }
65  int node0() const {
66  return nodes_[0];
67  }
68  friend bool operator==(const Face &a, const Face &b);
69  friend bool operator!=(const Face &a, const Face &b);
70  private:
71  std::array<int, 3> nodes_;
72  };
73 
74  // Make a tetrahedron from four nodes.
75  //
76  // Throws std::runtime_error if duplicate node tags are passed in.
77  Tetrahedron(int a, int b, int c, int d)
78  : nodes_({
79  a, b, c, d
80  }) {
81  if (a == b || a == c || a == d) {
82  throw std::runtime_error("duplicate node " + std::to_string(a));
83  }
84  if (b == c || b == d) {
85  throw std::runtime_error("duplicate node " + std::to_string(b));
86  }
87  if (c == d) {
88  throw std::runtime_error("duplicate node " + std::to_string(c));
89  }
90  // Node ordering is important here. Face 0 is missing node 1, Face 1
91  // is missing node 2, etc. This will be used later in the particle
92  // transport methods EGS_Mesh::howfar and EGS_Mesh::hownear.
93  faces_ = {
94  Face(b, c, d),
95  Face(a, c, d),
96  Face(a, b, d),
97  Face(a, b, c)
98  };
99  }
100  std::array<int, 4> nodes() const {
101  return nodes_;
102  }
103  int max_node() const {
104  return *std::max_element(nodes_.begin(), nodes_.end());
105  }
106  std::array<Face, 4> faces() const {
107  return faces_;
108  }
109 
110 private:
111  std::array<int, 4> nodes_;
112  std::array<Face, 4> faces_;
113 };
114 
115 bool operator==(const Tetrahedron::Face &a, const Tetrahedron::Face &b) {
116  return a.nodes_ == b.nodes_;
117 }
118 
119 bool operator!=(const Tetrahedron::Face &a, const Tetrahedron::Face &b) {
120  return a.nodes_ != b.nodes_;
121 }
122 
125 namespace internal {
126 
127 class SharedNodes {
128 public:
129  SharedNodes(std::vector<std::vector<int>> shared_nodes) :
130  shared_nodes(std::move(shared_nodes)) {}
131  const std::vector<int> &elements_around_node(int node) const {
132  return shared_nodes.at(node);
133  }
134 private:
135  std::vector<std::vector<int>> shared_nodes;
136 };
137 
138 // Find the elements around each node.
139 SharedNodes elements_around_nodes(const std::vector<mesh_neighbours::Tetrahedron> &elements) {
140  int max_node = 0;
141  for (const auto &elt: elements) {
142  if (elt.max_node() > max_node) {
143  max_node = elt.max_node();
144  }
145  }
146 
147  // the number of unique nodes is equal to the maximum node number + 1
148  // because the nodes are numbered from 0..=max_node
149  std::vector<std::vector<int>> shared_nodes(max_node + 1);
150  for (std::size_t i = 0; i < elements.size(); i++) {
151  for (auto node: elements[i].nodes()) {
152  shared_nodes.at(node).push_back(i);
153  }
154  }
155  return SharedNodes(shared_nodes);
156 }
157 
158 } // namespace internal
159 
160 // Given a list of tetrahedrons, returns the indices of neighbouring tetrahedrons.
161 std::vector<std::array<int, 4>> tetrahedron_neighbours(
162  const std::vector<mesh_neighbours::Tetrahedron> &elements,
163 egs_mesh::internal::PercentCounter &progress) {
164  progress.start(elements.size());
165  const std::size_t NUM_FACES = 4;
166  const auto shared_nodes = mesh_neighbours::internal::elements_around_nodes(elements);
167 
168  // initialize neighbour element index vector with "no neighbour" constant
169  std::vector<std::array<int, 4>> neighbours(elements.size(), {NONE, NONE, NONE, NONE});
170 
171  for (std::size_t i = 0; i < elements.size(); i++) {
172  auto elt_faces = elements[i].faces();
173  for (std::size_t f = 0; f < NUM_FACES; f++) {
174  // if this face's neighbour was already found, skip it
175  if (neighbours[i][f] != NONE) {
176  continue;
177  }
178  auto face = elt_faces[f];
179  // select a face node and loop through the other elements that share it
180  const auto &elts_sharing_node = shared_nodes.elements_around_node(face.node0());
181  for (auto j: elts_sharing_node) {
182  if (j == static_cast<int>(i)) {
183  // elt can't be a neighbour of itself, skip it
184  continue;
185  }
186  auto other_elt_faces = elements[j].faces();
187  for (std::size_t jf = 0; jf < NUM_FACES; jf++) {
188  if (face == other_elt_faces[jf]) {
189  neighbours[i][f] = j;
190  neighbours[j][jf] = i;
191  break;
192  }
193  }
194  }
195  }
196  progress.step(1);
197  }
198  return neighbours;
199 };
200 
201 } // namespace mesh_neighbours
202 #endif // MESH_NEIGHBOURS_
203 
Tetrahedral mesh geometry: header.