https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
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
92std::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(
118 libMesh::BoundaryInfo::NodeBCTupleSortBy::BOUNDARY_ID);
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 {
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
192done_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}
boundary_id_type BoundaryID
registerMooseObject("MooseApp", PolyLineMeshFollowingNodeSetGenerator)
void ErrorVector unsigned int
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
These methods add an range checked parameters.
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.
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...
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.
MeshGenerators are objects that can modify or add to an existing mesh.
static InputParameters validParams()
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.
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:457
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:205
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:271
void mooseInfo(Args &&... args) const
Definition MooseBase.h:334
Generates a polyline (open ended or looped) of Edge elements by marching along a nodeeset and trying ...
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.
const Point _starting_direction
Starting direction for the polyline.
const BoundaryName & _start_boundary
Boundary names to assign to (non-looped) polyline start and end.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
PolyLineMeshFollowingNodeSetGenerator(const InputParameters &parameters)
const unsigned int _num_edges_between_points
How many Edge elements to build between each point pair.
const bool _verbose
Whether to output to console the mesh generation process.
const Point _starting_point
Starting point of the polyline.
MeshBase & mesh
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.
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.