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