https://mooseframework.inl.gov
PolyLineMeshFollowingNodeSetGenerator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
11 
12 #include "CastUniquePointer.h"
13 #include "MooseMeshUtils.h"
14 #include "MooseUtils.h"
15 
16 #include "libmesh/elem.h"
17 #include "libmesh/int_range.h"
18 #include "libmesh/unstructured_mesh.h"
19 
21 
24 {
26 
27  // Path parameters
28  params.addRequiredParam<Point>("starting_point", "Starting point for the polyline");
29  params.addRequiredParam<Point>("starting_direction",
30  "Starting value for the direction of the line");
31  params.addRequiredParam<MeshGeneratorName>("input", "The mesh we get the sideset from");
32  params.addRequiredParam<BoundaryName>("nodeset", "Nodeset to follow to form the polyline");
33  params.addRequiredParam<Real>("search_radius",
34  "Radius of the sphere used to find points in the nodeset");
35  params.addParam<bool>(
36  "ignore_nodes_behind",
37  false,
38  "Ignore nodes in the nodeset that are behind the current point in the polyline");
39  params.addParam<bool>("loop", false, "Whether edges should form a closed loop");
40 
41  // Discretization parameters
42  // NOTE: we could have another dx as a path search parameter, and decouple the two options
44  "dx",
45  "dx>0",
46  "Approximate size of the edge elements (before any refinement with num_edges_between_points) "
47  "and approximate step to advance by to search for the next point in the polyline");
48  params.addParam<unsigned int>(
49  "max_edges", 1000, "Maximum number of edges. Serves as a stopping criterion");
50  params.addParam<unsigned int>(
51  "num_edges_between_points", 1, "How many Edge elements to build between each point pair");
52 
53  // Naming of result parameters
54  params.addParam<SubdomainName>("line_subdomain", "line", "Subdomain name for the line");
55  params.addParam<BoundaryName>(
56  "start_boundary", "start", "Boundary to assign to (non-looped) polyline start");
57  params.addParam<BoundaryName>(
58  "end_boundary", "end", "Boundary to assign to (non-looped) polyline end");
59 
60  params.addParam<bool>(
61  "verbose",
62  false,
63  "whether to output additional information to console during the line generation");
64 
65  params.addClassDescription(
66  "Generates a polyline (open ended or looped) of Edge elements by marching along a nodeeset "
67  "and trying to be as close as possible to the nodes of the nodeset");
68 
69  return params;
70 }
71 
73  const InputParameters & parameters)
74  : MeshGenerator(parameters),
75  _input(getMesh("input")),
76  _starting_point(getParam<Point>("starting_point")),
77  _starting_direction(getParam<Point>("starting_direction")),
78  _ignore_nodes_behind(getParam<bool>("ignore_nodes_behind")),
79  _loop(getParam<bool>("loop")),
80  _line_subdomain(getParam<SubdomainName>("line_subdomain")),
81  _start_boundary(getParam<BoundaryName>("start_boundary")),
82  _end_boundary(getParam<BoundaryName>("end_boundary")),
83  _dx(getParam<Real>("dx")),
84  _num_edges_between_points(getParam<unsigned int>("num_edges_between_points")),
85  _verbose(getParam<bool>("verbose"))
86 {
87  if (_loop && (isParamSetByUser("start_boundary") || isParamSetByUser("end_boundary")))
88  paramError("loop",
89  "Loop does not have a start or end boundary. These parameters must not be passed.");
90 }
91 
92 std::unique_ptr<MeshBase>
94 {
95  auto uptr_mesh = buildMeshBaseObject();
96  MeshBase & mesh = *uptr_mesh;
97  std::unique_ptr<MeshBase> base_mesh = std::move(_input);
98  if (!base_mesh->is_serial())
99  paramError("input", "Input mesh must not be distributed");
100 
101  // We may rely on boundary info caches later
102  if (!base_mesh->preparation().has_boundary_id_sets)
103  base_mesh->get_boundary_info().regenerate_id_sets();
104 
105  // Get nodeset ID in input mesh
106  const auto nodeset_id =
107  MooseMeshUtils::getBoundaryID(getParam<BoundaryName>("nodeset"), *base_mesh);
108 
109  const auto search_radius_sq = libMesh::Utility::pow<2>(getParam<Real>("search_radius"));
110  const auto n_points = getParam<unsigned int>("max_edges");
111  Point current_point = _starting_point;
112  Point previous_direction = _starting_direction;
113  mesh.add_point(_starting_point, 0);
114 
115  // Pre-find all points in the nodeset to follow
116  // TODO: Build a KNN tree to speed up the search later on
117  const auto all_nodeset_tuples = base_mesh->get_boundary_info().build_node_list(
119  std::vector<dof_id_type> nodeset_nodes;
120  nodeset_nodes.reserve(all_nodeset_tuples.size() /
121  base_mesh->get_boundary_info().n_boundary_ids());
122  for (const auto & tup : all_nodeset_tuples)
123  if (BoundaryID(std::get<1>(tup)) == nodeset_id)
124  nodeset_nodes.push_back(std::get<0>(tup));
125  if (_verbose)
126  _console << "Total number of nodes in nodeset " << getParam<BoundaryName>("nodeset") << ": "
127  << nodeset_nodes.size() << std::endl;
128 
129  unsigned int n_segments = 0;
130  for (const auto i : make_range(n_points))
131  {
132  // Move the point forward in the search direction
133  const auto previous_point = current_point;
134  current_point += previous_direction * _dx;
135 
136  // Draw a sphere to find the next point as the barycenter of the nodes from the nodeset inside
137  // the sphere.
138  // NOTE: there are many heuristics we could use here. We could try to fit a cylinder
139  // to the nodeset if we know the nodeset represents a cylinder for example.
140  Point barycenter(0);
141  unsigned int n_sum = 0;
142  for (const auto n_id : nodeset_nodes)
143  if ((current_point - base_mesh->node_ref(n_id)).norm_sq() < search_radius_sq)
144  {
145  if (!_ignore_nodes_behind ||
146  ((base_mesh->node_ref(n_id) - current_point) * previous_direction >= 0))
147  {
148  barycenter += base_mesh->node_ref(n_id);
149  n_sum++;
150  }
151  }
152  if (n_sum > 0)
153  barycenter /= n_sum;
154  else if (!_ignore_nodes_behind)
155  mooseError("Did not find any nodes in the nodeset near the current point at: ",
156  current_point);
157  else
158  barycenter = previous_point;
159 
160  if (MooseUtils::absoluteFuzzyEqual((barycenter - previous_point).norm_sq(), 0))
161  {
162  mooseInfo("Barycenter did not move from ", barycenter, ". Returning!");
163  goto done_drawing;
164  }
165 
166  // Compute new direction
167  const auto new_direction = (barycenter - previous_point).unit();
168  previous_direction = new_direction;
169 
170  // Set the new point towards the barycenter
171  n_segments++;
172  // Note: this dx could be different than the dx used to search the barycenter
173  current_point = previous_point + _dx * new_direction;
174  mesh.add_point(current_point, (i + 1) * _num_edges_between_points);
175  if (_verbose)
176  _console << i << ": new point: " << current_point << " new direction " << previous_direction
177  << std::endl;
178 
179  // Add the additional edges in between if requested
181  {
182  auto p = previous_point;
183  const Point pvec = (current_point - previous_point) / _num_edges_between_points;
184  for (auto j : make_range(1u, _num_edges_between_points))
185  {
186  p += pvec;
187  mesh.add_point(p, i * _num_edges_between_points + j);
188  }
189  }
190  }
191 
192 done_drawing:
193 
194  const auto n_elem = n_segments * _num_edges_between_points - 1;
195  const auto max_nodes = n_segments * _num_edges_between_points - 1;
196  for (auto i : make_range(n_elem + _loop))
197  {
198  const auto ip1 = _loop ? (i + 1) % max_nodes : (i + 1);
199  auto elem = Elem::build(EDGE2);
200  elem->set_node(0, mesh.node_ptr(i));
201  elem->set_node(1, mesh.node_ptr(ip1));
202  elem->set_id() = i;
203  mesh.add_elem(std::move(elem));
204  }
205 
206  // Add the starting and end boundary
207  if (!_loop)
208  {
209  BoundaryInfo & bi = mesh.get_boundary_info();
210  std::vector<BoundaryName> bdy_names{_start_boundary, _end_boundary};
211  std::vector<boundary_id_type> ids = MooseMeshUtils::getBoundaryIDs(mesh, bdy_names, true);
212  bi.add_side(mesh.elem_ptr(0), 0, ids[0]);
213  bi.add_side(mesh.elem_ptr(n_elem - 1), 1, ids[1]);
214  }
215 
216  mesh.prepare_for_use();
217 
218  return uptr_mesh;
219 }
void mooseInfo(Args &&... args) const
Definition: MooseBase.h:344
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
These methods add an range checked parameters.
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition: MooseBase.h:467
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
auto norm_sq(const T &a)
const Point _starting_direction
Starting direction for the polyline.
PolyLineMeshFollowingNodeSetGenerator(const InputParameters &parameters)
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
const bool _verbose
Whether to output to console the mesh generation process.
const Real _dx
Approximate spacing between nodes.
const bool _ignore_nodes_behind
Whether to only look in front of the polyline for nodes in the nodeset.
const bool _loop
Whether edges should form a closed loop. Will error if the nodeset does not loop back on itself...
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
const BoundaryName & _start_boundary
Boundary names to assign to (non-looped) polyline start and end.
boundary_id_type BoundaryID
Generates a polyline (open ended or looped) of Edge elements by marching along a nodeeset and trying ...
const unsigned int _num_edges_between_points
How many Edge elements to build between each point pair.
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
Gets the boundary IDs with their names.
static InputParameters validParams()
Definition: MeshGenerator.C:23
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
EDGE2
const Point _starting_point
Starting point of the polyline.
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:281
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
Build a MeshBase object whose underlying type will be determined by the Mesh input file block...
bool isParamSetByUser(const std::string &name) const
Test if the supplied parameter is set by a user, as opposed to not set or set to default.
Definition: MooseBase.h:215
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:33
void ErrorVector unsigned int
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template pow< 2 >(tan(_arg))+1.0) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(sqrt
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
registerMooseObject("MooseApp", PolyLineMeshFollowingNodeSetGenerator)