EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_mesh.h
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ mesh geometry library headers.
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: Pascal Michaud
31 # Reid Townson
32 #
33 ###############################################################################
34 #
35 # The original EGS_Mesh geometry started as a product of many people's hard
36 # work at Mevex Corporation. Thank you to the entire team. Special thanks to
37 # Dave Brown and Emily Craven for their support in releasing this work
38 # under a public licence.
39 #
40 # The EGS_Mesh library was then extended and refactored significantly by
41 # Max Orok, as the subject of his Masters degree in Applied Science at the
42 # University of Ottawa, in the department of Mechanical Engineering and under
43 # the supervision of Professor James McDonald.
44 #
45 ###############################################################################
46 */
47 
52 #ifndef EGS_MESH
53 #define EGS_MESH
54 
55 #include <algorithm>
56 #include <chrono>
57 #include <cstdint>
58 #include <iostream>
59 #include <iomanip>
60 #include <memory>
61 #include <sstream>
62 #include <vector>
63 #include <array>
64 
65 #include "egs_base_geometry.h"
66 #include "egs_vector.h"
67 
68 #ifdef WIN32
69 
70  #ifdef BUILD_EGS_MESH_DLL
71  #define EGS_MESH_EXPORT __declspec(dllexport)
72  #else
73  #define EGS_MESH_EXPORT __declspec(dllimport)
74  #endif
75  #define EGS_MESH_LOCAL
76 
77 #else
78 
79  #ifdef HAVE_VISIBILITY
80  #define EGS_MESH_EXPORT __attribute__ ((visibility ("default")))
81  #define EGS_MESH_LOCAL __attribute__ ((visibility ("hidden")))
82  #else
83  #define EGS_MESH_EXPORT
84  #define EGS_MESH_LOCAL
85  #endif
86 
87 #endif
88 
89 // exclude from doxygen
91 class EGS_Mesh_Octree;
93 
95 class EGS_MESH_EXPORT EGS_MeshSpec {
96 public:
97 
99  struct Tetrahedron {
100  Tetrahedron(int tag, int medium_tag, int a, int b, int c, int d) :
101  tag(tag), medium_tag(medium_tag), a(a), b(b), c(c), d(d) {}
102  int tag = -1;
103  int medium_tag = -1;
104  // nodes
105  int a = -1;
106  int b = -1;
107  int c = -1;
108  int d = -1;
109  };
110 
112  struct Node {
113  Node(int tag, double x, double y, double z) :
114  tag(tag), x(x), y(y), z(z) {}
115  int tag = -1;
116  double x = 0.0;
117  double y = 0.0;
118  double z = 0.0;
119  };
120 
122  struct Medium {
123  Medium(int tag, std::string medium_name) :
124  tag(tag), medium_name(medium_name) {}
125  int tag = -1;
126  std::string medium_name;
127  };
128 
129  EGS_MeshSpec() = default;
130  EGS_MeshSpec(std::vector<Tetrahedron> elements, std::vector<Node> nodes,
131  std::vector<Medium> media) : elements(std::move(elements)),
132  nodes(std::move(nodes)), media(std::move(media)) {}
133  EGS_MeshSpec(const EGS_MeshSpec &) = delete;
134  EGS_MeshSpec &operator=(const EGS_MeshSpec &) = delete;
135  // EGS_MeshSpec is move-only
136  EGS_MeshSpec(EGS_MeshSpec &&) = default;
137  EGS_MeshSpec &operator=(EGS_MeshSpec &&) = default;
138  ~EGS_MeshSpec() = default;
139 
142  void checkValid() const;
143 
145  void scale(EGS_Float factor) {
146  for (auto &node: nodes) {
147  node.x *= factor;
148  node.y *= factor;
149  node.z *= factor;
150  }
151  }
152 
153  // Public members
154 
156  std::vector<EGS_MeshSpec::Tetrahedron> elements;
158  std::vector<EGS_MeshSpec::Node> nodes;
160  std::vector<EGS_MeshSpec::Medium> media;
161 };
162 
243 class EGS_MESH_EXPORT EGS_Mesh : public EGS_BaseGeometry {
244 public:
247  explicit EGS_Mesh(EGS_MeshSpec spec);
248 
249  // EGS_Mesh is move-only
250  EGS_Mesh(const EGS_Mesh &) = delete;
251  EGS_Mesh &operator=(const EGS_Mesh &) = delete;
252 
253  // Declare move constructor, move assignment, and destructor without
254  // defining them. We can't define them yet because of the unique_ptr to
255  // forward declared EGS_Mesh_Octree members.
256  EGS_Mesh(EGS_Mesh &&);
257  EGS_Mesh &operator=(EGS_Mesh &&);
258  ~EGS_Mesh();
259 
261  int num_elements() const {
262  return elt_tags_.size();
263  }
264 
266  int num_nodes() const {
267  return nodes_.size();
268  }
269 
272  const std::array<int, 4> &element_neighbours(int i) const {
273  return neighbours_.at(i);
274  }
275 
277  EGS_Float element_volume(int i) const {
278  const auto &n = element_nodes(i);
279  return std::abs((n.A - n.D) * ((n.B - n.D) % (n.C - n.D))) / 6.0;
280  }
281 
283  EGS_Float element_density(int i) const {
284  return EGS_BaseGeometry::getMediumRho(medium_indices_.at(i));
285  }
286 
289  int element_tag(int i) const {
290  return elt_tags_.at(i);
291  }
292 
296  inline bool is_boundary(int reg) const {
297  return boundary_faces_[4*reg] || boundary_faces_[4*reg + 1] ||
298  boundary_faces_[4*reg + 2] || boundary_faces_[4*reg + 3];
299  }
300 
302  void printElement(int i, std::ostream &elt_info = std::cout) const {
303  const auto &n = element_nodes(i);
304  elt_info << " Tetrahedron " << i << ":\n"
305  << " \tNode coordinates (cm):\n"
306  << " \t0: " << n.A.x << " " << n.A.y << " " << n.A.z << "\n"
307  << " \t1: " << n.B.x << " " << n.B.y << " " << n.B.z << "\n"
308  << " \t2: " << n.C.x << " " << n.C.y << " " << n.C.z << "\n"
309  << " \t3: " << n.D.x << " " << n.C.y << " " << n.C.z << "\n"
310  << " \tNeighbour elements:\n"
311  << " \t\tOn face 0: " << neighbours_[i][0] << "\n"
312  << " \t\tOn face 1: " << neighbours_[i][1] << "\n"
313  << " \t\tOn face 2: " << neighbours_[i][2] << "\n"
314  << " \t\tOn face 3: " << neighbours_[i][3] << "\n"
315  << std::boolalpha
316  << " \tBoundary element: " << is_boundary(i) << "\n"
317  << " \tMedia index: "<< medium_indices_[i] << "\n";
318  }
319 
320  // EGS_BaseGeometry interface
321  const std::string &getType() const override {
322  return type;
323  }
324  bool isInside(const EGS_Vector &x) override;
325  int inside(const EGS_Vector &x) override;
326  int medium(int ireg) const override;
327  int isWhere(const EGS_Vector &x) override;
328  int howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u,
329  EGS_Float &t, int *newmed=0, EGS_Vector *normal=0) override;
330  EGS_Float hownear(int ireg, const EGS_Vector &x) override;
331  void printInfo() const override;
332 
334  bool insideElement(int i, const EGS_Vector &x);
335 
336  // exclude from doxygen, should be private but used by EGS_Mesh_Octree::Node
338  struct Intersection {
339  Intersection(EGS_Float dist, int face_index)
340  : dist(dist), face_index(face_index) {}
342  EGS_Float dist;
344  int face_index;
345  };
346 
347  // `howfar` helper: Determine the closest boundary face intersection
348  Intersection closest_boundary_face(int ireg, const EGS_Vector &x, const EGS_Vector &u);
350 
357  struct Nodes {
358  const EGS_Vector &A;
359  const EGS_Vector &B;
360  const EGS_Vector &C;
361  const EGS_Vector &D;
362  };
363 
365  Nodes element_nodes(int element) const {
366  const auto &node_indices = elt_node_indices_.at(element);
367  return Nodes {
368  nodes_.at(node_indices[0]),
369  nodes_.at(node_indices[1]),
370  nodes_.at(node_indices[2]),
371  nodes_.at(node_indices[3])
372  };
373  }
374 
377  const EGS_Vector &node_coordinates(int node_offset) const {
378  return nodes_.at(node_offset);
379  }
380 
382  const std::array<int, 4> &element_node_offsets(int element) const {
383  return elt_node_indices_.at(element);
384  }
385 
387  static EGS_Float get_min_step_size() {
388  return EGS_Mesh::min_step_size;
389  }
390 
391 private:
392  // `hownear` helper method
393  // Given a tetrahedron ireg, find the minimum distance to a face in any direction.
394  EGS_Float min_interior_face_dist(int ireg, const EGS_Vector &x);
395 
396  // `hownear` helper method
397  // Outside the mesh, find the minimum distance to the mesh in any direction (ireg = -1)
398  EGS_Float min_exterior_face_dist(const EGS_Vector &x);
399 
400  // `howfar` helper method inside a given tetrahedron
401  int howfar_interior(int ireg, const EGS_Vector &x, const EGS_Vector &u,
402  EGS_Float &t, int *newmed, EGS_Vector *normal);
403 
404  // `howfar_interior` helper methods
405 
406  struct PointLocation {
407  PointLocation() = default;
408  PointLocation(EGS_Float direction_dot_normal, EGS_Float signed_distance)
409  : direction_dot_normal(direction_dot_normal), signed_distance(
410  signed_distance) {}
411 
412  EGS_Float direction_dot_normal = 0.0;
413  EGS_Float signed_distance = 0.0;
414  };
415 
416  PointLocation find_point_location(const EGS_Vector &x, const
417  EGS_Vector &u, const EGS_Vector &plane_point, const EGS_Vector &
418  plane_normal);
419 
420  Intersection find_interior_intersection(
421  const std::array<PointLocation, 4> &ixs);
422 
423  int howfar_interior_thick_plane(
424  const std::array<PointLocation, 4> &intersect_tests, int ireg,
425  const EGS_Vector &x, const EGS_Vector &u, EGS_Float &t, int *newmed,
426  EGS_Vector *normal);
427 
428  // If the particle is lost, try to recover transport by shifting the
429  // particle along a small step.
430  int howfar_interior_recover_lost_particle(int ireg, const EGS_Vector &x,
431  const EGS_Vector &u, EGS_Float &t, int *newmed);
432 
433  // `howfar` helper method outside the mesh
434  int howfar_exterior(const EGS_Vector &x, const EGS_Vector &u, EGS_Float &t,
435  int *newmed, EGS_Vector *normal);
436 
437  inline void update_medium(int newreg, int *newmed) const {
438  if (!newmed) {
439  return;
440  }
441  if (newreg == -1) {
442  // vacuum
443  *newmed = -1;
444  }
445  else {
446  *newmed = medium(newreg);
447  }
448  }
449 
450  inline void update_normal(const EGS_Vector &face_normal,
451  const EGS_Vector &u_norm, EGS_Vector *normal) const {
452  if (!normal) {
453  return;
454  }
455  // egs++ convention is normal pointing opposite view ray
456  if (face_normal * u_norm > 0.0) {
457  *normal = -1.0 * face_normal;
458  }
459  else {
460  *normal = face_normal;
461  }
462  }
463 
464  // Can only be called after initializeElements (for num_elements())
465  EGS_InfoFunction get_logger() const {
466  if (num_elements() < 50000) {
467  // don't log for small meshes since loading is near instantaneous
468  return nullptr;
469  }
470  return egsInformation;
471  }
472 
473  // Constructor helper methods:
474  //
475  // Each logical piece is a separate function to help reduce peak memory
476  // usage. For large meshes, having temporaries around until the end of the
477  // constructor, including during intense operations like constructing the
478  // octrees, etc. can raise the peak memory usage far above the steady state
479  // requirements.
480 
481  // Initialize the mesh element information in the EGS_Mesh constructor.
482  // Must be called before any other initialize functions. After this method
483  // is called, the following member data is initialized:
484  // * EGS_Mesh::nodes_
485  // * EGS_Mesh::elt_tags_
486  // * EGS_Mesh::elt_node_indices_
487  // * EGS_BaseGeometry::nreg
488  // and member functions that depend on this data, like num_elements().
489  void initializeElements(std::vector<EGS_MeshSpec::Tetrahedron> elements,
490  std::vector<EGS_MeshSpec::Node> nodes,
491  std::vector<EGS_MeshSpec::Medium> materials);
492 
493  // Initialize mesh element medium offsets, adding any previously undefined
494  // media to the EGS_BaseGeometry media list. Responsible for initializing:
495  // * EGS_Mesh::medium_indices_
496  void initializeMedia(std::vector<EGS_MeshSpec::Tetrahedron> elements,
497  std::vector<EGS_MeshSpec::Medium> materials);
498 
499  // Initialize neigbhour and boundary information. Must be called after
500  // initializeElements. Responsible for initializing:
501  // * EGS_Mesh::neighbours_
502  // * EGS_Mesh::boundary_faces_
503  void initializeNeighbours();
504 
505  // Initialize the two octrees used to accelerate transport. Both
506  // initializeElements and initializeNeighbours must be called to properly
507  // set up the octrees. Responsible for initializing:
508  // * EGS_Mesh::volume_tree_
509  // * EGS_Mesh::surface_tree_
510  void initializeOctrees();
511 
512  // Initialize the tetrahedron face normals. Must be called after
513  // initializeElements. Responsible for initializing EGS_Mesh:face_normals_.
514  void initializeNormals();
515 
516  std::vector<EGS_Vector> nodes_;
517  std::vector<int> elt_tags_;
518  std::vector<std::array<int, 4>> elt_node_indices_;
519  // 4 * num_elts of which faces are boundaries
520  // TODO: try vec<array<bool, 4>>
521  std::vector<bool> boundary_faces_;
522  // TODO if memory is an issue, could try storing tets as sets of faces,
523  // faces as sets of edges, etc.
524  std::vector<std::array<EGS_Vector, 4>> face_normals_;
525  std::vector<int> medium_indices_;
526 
527  std::unique_ptr<EGS_Mesh_Octree> volume_tree_;
528  std::unique_ptr<EGS_Mesh_Octree> surface_tree_;
529 
530  std::vector<std::array<int, 4>> neighbours_;
531  static const std::string type;
532 
533  static constexpr EGS_Float min_step_size = 1e-10;
534 };
535 
536 // exclude from doxygen
538 
539 namespace egs_mesh {
540 
541 // The egs_mesh::internal namespace is for internal API functions and may change
542 // without warning.
543 namespace internal {
544 
546 class EGS_MESH_EXPORT PercentCounter {
547 public:
548  // Create a new counter. This doesn't log anything or start the timer yet.
549  // The counter must be activated later using `start`.
550  PercentCounter(EGS_InfoFunction info, const std::string &msg);
551 
552  // Start the counter's progress toward the provided goal. The goal must be
553  // a positive number and fit into an int. Logging and timing begin at this
554  // point.
555  void start(EGS_Float goal);
556 
557  // Advance the counter by a fraction of the goal value. Assumes delta is
558  // positive.
559  void step(EGS_Float delta);
560 
561  // After the task is finished, print a new message and the elapsed time.
562  void finish(const std::string &end_msg);
563 
564 private:
565  // egsInformation-like function pointer.
566  EGS_InfoFunction info_;
567  // Progress message.
568  std::string msg_;
569  // Goal value.
570  double goal_ = 0.0;
571  // Progress value, percent complete = progress_ / goal_.
572  double progress_ = 0.0;
573  // The last progress percentage that was displayed, used to avoid redundant
574  // I/O calls.
575  int old_percent_ = 0;
576  // The time when PercentCounter::start was called.
577  std::chrono::time_point<std::chrono::system_clock> t_start_;
578  // Whether to update the percentage in real time (true) or just report when
579  // the task is finished (false).
580  bool interactive_ = false;
581 };
582 
583 } // namespace internal
584 
585 } // namespace egs_mesh
586 
588 
589 #endif // EGS_MESH
Base geometry class. Every geometry class must be derived from EGS_BaseGeometry.
A container for raw unstructured tetrahedral mesh data.
Definition: egs_mesh.h:95
std::vector< EGS_MeshSpec::Tetrahedron > elements
Unique mesh elements.
Definition: egs_mesh.h:156
void scale(EGS_Float factor)
Multiply all node coordinates by a constant factor.
Definition: egs_mesh.h:145
std::vector< EGS_MeshSpec::Node > nodes
Unique nodes.
Definition: egs_mesh.h:158
std::vector< EGS_MeshSpec::Medium > media
Unique medium information.
Definition: egs_mesh.h:160
A tetrahedral mesh geometry.
Definition: egs_mesh.h:243
EGS_Float element_density(int i) const
Returns the density in g/cm3 of element i.
Definition: egs_mesh.h:283
const std::array< int, 4 > & element_neighbours(int i) const
Definition: egs_mesh.h:272
void printElement(int i, std::ostream &elt_info=std::cout) const
Print information about element i to the stream elt_info.
Definition: egs_mesh.h:302
static EGS_Float get_min_step_size()
Returns the minimum step size (i.e. the fuzzy plane tolerance).
Definition: egs_mesh.h:387
int num_elements() const
Returns the number of mesh elements.
Definition: egs_mesh.h:261
Nodes element_nodes(int element) const
Given an element offset, return the element's node coordinates.
Definition: egs_mesh.h:365
int num_nodes() const
Returns the number of unique mesh nodes.
Definition: egs_mesh.h:266
EGS_Float element_volume(int i) const
Returns the volume in cm3 of element i.
Definition: egs_mesh.h:277
bool is_boundary(int reg) const
Definition: egs_mesh.h:296
const EGS_Vector & node_coordinates(int node_offset) const
Definition: egs_mesh.h:377
int element_tag(int i) const
Definition: egs_mesh.h:289
const std::array< int, 4 > & element_node_offsets(int element) const
Given an element offset, return its four node offsets.
Definition: egs_mesh.h:382
A class representing 3D vectors.
Definition: egs_vector.h:57
EGS_BaseGeometry class header file.
void(* EGS_InfoFunction)(const char *,...)
Defines a function printf-like prototype for functions to be used to report info, warnings,...
EGS_Vector methods for the manipulation of 3D vectors in cartesian co-ordinates.
EGS_InfoFunction EGS_EXPORT egsInformation
Always use this function for reporting the progress of a simulation and any other type of information...
A medium. The medium name must match an EGSnrc medium name.
Definition: egs_mesh.h:122
A 3D point. Units are cm.
Definition: egs_mesh.h:112
A tetrahedral mesh element.
Definition: egs_mesh.h:99