https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MoveBoundaryNodesToCurveGenerator.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
12#include "MooseMeshUtils.h"
13
14#include "libmesh/boundary_info.h"
15#include "libmesh/elem.h"
16#include "libmesh/mesh_base.h"
17#include "libmesh/node.h"
18
19// C++ includes
20#include <algorithm>
21#include <cmath>
22#include <limits>
23#include <set>
24
26
29{
31
32 params.addRequiredParam<MeshGeneratorName>(
33 "input", "The input mesh whose boundary nodes are snapped onto the curve.");
34 params.addRequiredParam<BoundaryName>(
35 "boundary", "The name of the boundary whose nodes are snapped onto the curve.");
36 params.addRequiredParam<MeshGeneratorName>(
37 "parsed_curve_generator",
38 "The ParsedCurveGenerator that defines the curve to snap the nodes onto.");
39 params.addRangeCheckedParam<unsigned int>(
40 "samples_per_section",
41 50,
42 "samples_per_section>=2",
43 "Number of uniformly spaced samples of each section of the curve that are used to bracket "
44 "the closest point of the curve.");
45
47 "Snaps the nodes of a boundary onto the parametric curve of a ParsedCurveGenerator to "
48 "recover the geometry that the straight element edges of the input mesh approximate.");
49
50 return params;
51}
52
54 const InputParameters & parameters)
55 : MeshGenerator(parameters),
56 _input(getMesh("input")),
57 // The mesh of the curve is not used by this generator. It is requested so that the mesh
58 // generator system builds the ParsedCurveGenerator before this generator, which reads the
59 // curve definition from that generator below and evaluates its curve.
60 _curve_mesh(getMesh("parsed_curve_generator")),
61 _curve_generator(curveGenerator()),
62 _boundary_name(getParam<BoundaryName>("boundary")),
63 _samples_per_section(getParam<unsigned int>("samples_per_section")),
64 _section_bounding_t_values(_curve_generator.sectionBoundingTValues()),
65 _is_closed_loop(_curve_generator.isClosedLoop())
66{
67 if (_section_bounding_t_values.size() < 2)
68 paramError("parsed_curve_generator",
69 "The ParsedCurveGenerator '",
70 getParam<MeshGeneratorName>("parsed_curve_generator"),
71 "' must have at least two 'section_bounding_t_values' to define a curve.");
72
73 // Sample each section of the curve uniformly, leaving out the end of the section as it is the
74 // start of the next one
75 for (const auto i : make_range(_section_bounding_t_values.size() - 1))
76 {
77 const Real t_start = _section_bounding_t_values[i];
78 const Real t_end = _section_bounding_t_values[i + 1];
79 for (const auto j : make_range(_samples_per_section))
80 _t_samples.push_back(t_start +
81 (t_end - t_start) * static_cast<Real>(j) / _samples_per_section);
82 }
83 // The end of the last section is the start of the first one on a closed loop, so it is only
84 // sampled for an open curve
85 if (!_is_closed_loop)
86 _t_samples.push_back(_section_bounding_t_values.back());
87}
88
89std::unique_ptr<MeshBase>
91{
92 // The mesh generator system requires that every requested mesh is released here, and the mesh of
93 // the curve is only requested for the dependency it creates
94 _curve_mesh.reset();
95
96 std::unique_ptr<MeshBase> mesh = std::move(_input);
97
98 if (!mesh->is_serial())
99 paramError("input", "Input mesh must not be distributed");
100
102 paramError("boundary", "The boundary '", _boundary_name, "' does not exist in the input mesh.");
103 const auto boundary_id = MooseMeshUtils::getBoundaryID(_boundary_name, *mesh);
104
105 // The nodes of the boundary can be defined by its sides as well as by its nodeset entries
106 const BoundaryInfo & boundary_info = mesh->get_boundary_info();
107 const auto side_list = boundary_info.build_side_list();
108 const auto node_list = boundary_info.build_node_list();
109 std::set<dof_id_type> boundary_node_ids;
110 for (const auto & [elem_id, side, side_boundary_id] : side_list)
111 if (side_boundary_id == boundary_id)
112 {
113 const Elem & elem = mesh->elem_ref(elem_id);
114 for (const auto local_node_id : elem.nodes_on_side(side))
115 boundary_node_ids.insert(elem.node_id(local_node_id));
116 }
117 for (const auto & [node_id, node_boundary_id] : node_list)
118 if (node_boundary_id == boundary_id)
119 boundary_node_ids.insert(node_id);
120
121 // The sampled curve points do not depend on the node being snapped, so they are evaluated once
122 // here instead of once per node
123 _sample_points.reserve(_t_samples.size());
124 for (const auto t_sample : _t_samples)
125 _sample_points.push_back(curvePoint(t_sample));
126
127 for (const auto node_id : boundary_node_ids)
128 {
129 Node & node = mesh->node_ref(node_id);
130 const Point snapped_point = curvePoint(closestParameter(node));
131 // The curve is defined in the XY plane, so the out-of-plane coordinate is left alone
132 node(0) = snapped_point(0);
133 node(1) = snapped_point(1);
134 }
135
136 mesh->unset_has_cached_elem_data();
137 mesh->clear_point_locator();
138
139 return mesh;
140}
141
144{
145 const MeshGenerator & curve_generator =
146 _app.getMeshGenerator(getParam<MeshGeneratorName>("parsed_curve_generator"));
147
148 const auto parsed_curve_generator = dynamic_cast<const ParsedCurveGenerator *>(&curve_generator);
149 if (!parsed_curve_generator)
150 paramError("parsed_curve_generator",
151 "The mesh generator '",
152 curve_generator.name(),
153 "' is of type '",
154 curve_generator.type(),
155 "', but a ParsedCurveGenerator is required to define the curve.");
156
157 // The mesh generator system only hands out const generators, and evaluating the curve stages the
158 // parameter in the parser of the generator that owns it, the same const_cast that
159 // MeshGeneratorSystem::getMeshGeneratorInternal() makes for the same reason
160 return const_cast<ParsedCurveGenerator &>(*parsed_curve_generator);
161}
162
163Point
168
169Real
170MoveBoundaryNodesToCurveGenerator::squaredDistance(const Real t_param, const Point & point)
171{
172 return squaredDistance(curvePoint(t_param), point);
173}
174
175Real
177 const Point & point) const
178{
179 const Real dx = curve_point(0) - point(0);
180 const Real dy = curve_point(1) - point(1);
181 return dx * dx + dy * dy;
182}
183
184Real
186{
187 // Sampling the whole curve brackets the closest point globally, so that the refinement below
188 // does not converge onto a closest point of another part of the curve
189 std::size_t closest_sample = 0;
190 Real closest_squared_distance = std::numeric_limits<Real>::max();
191 for (const auto i : index_range(_t_samples))
192 {
193 const Real sample_squared_distance = squaredDistance(_sample_points[i], point);
194 if (sample_squared_distance < closest_squared_distance)
195 {
196 closest_squared_distance = sample_squared_distance;
197 closest_sample = i;
198 }
199 }
200
201 // The closest point of the curve lies between the two samples that neighbor the closest sample.
202 // On a closed loop, the sample before the first one and the sample after the last one are found
203 // across the seam, one period below and above the sampled t values.
204 const Real t_period = _section_bounding_t_values.back() - _section_bounding_t_values.front();
205 Real t_before;
206 if (closest_sample > 0)
207 t_before = _t_samples[closest_sample - 1];
208 else
209 t_before = _is_closed_loop ? _t_samples.back() - t_period : _t_samples.front();
210 Real t_after;
211 if (closest_sample + 1 < _t_samples.size())
212 t_after = _t_samples[closest_sample + 1];
213 else
214 t_after = _is_closed_loop ? _t_samples.front() + t_period : _t_samples.back();
215
216 return goldenSectionSearch(std::min(t_before, t_after), std::max(t_before, t_after), point);
217}
218
219Real
221 const Real t_upper,
222 const Point & point)
223{
224 // Inverse of the golden ratio, which is the factor the bracket shrinks by in each iteration
225 const Real inv_golden_ratio = (std::sqrt(5.0) - 1.0) / 2.0;
226 // A fixed number of iterations makes the search identical for every node. It shrinks the bracket
227 // to about 1e-12 of its initial size, which is well below the size of an element.
228 constexpr unsigned int num_iterations = 60;
229
230 Real lower = t_lower;
231 Real upper = t_upper;
232 Real t_1 = upper - inv_golden_ratio * (upper - lower);
233 Real t_2 = lower + inv_golden_ratio * (upper - lower);
234 Real squared_distance_1 = squaredDistance(t_1, point);
235 Real squared_distance_2 = squaredDistance(t_2, point);
236
237 for ([[maybe_unused]] const auto i : make_range(num_iterations))
238 {
239 if (squared_distance_1 < squared_distance_2)
240 {
241 upper = t_2;
242 t_2 = t_1;
243 squared_distance_2 = squared_distance_1;
244 t_1 = upper - inv_golden_ratio * (upper - lower);
245 squared_distance_1 = squaredDistance(t_1, point);
246 }
247 else
248 {
249 lower = t_1;
250 t_1 = t_2;
251 squared_distance_1 = squared_distance_2;
252 t_2 = lower + inv_golden_ratio * (upper - lower);
253 squared_distance_2 = squaredDistance(t_2, point);
254 }
255 }
256
257 return (lower + upper) / 2.0;
258}
259
260Real
262{
263 const Real t_min =
265 const Real t_max =
267 const Real t_range = t_max - t_min;
268
269 return t_param - t_range * std::floor((t_param - t_min) / t_range);
270}
registerMooseObject("MooseApp", MoveBoundaryNodesToCurveGenerator)
void ErrorVector unsigned int
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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.
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
MeshGenerators are objects that can modify or add to an existing mesh.
static InputParameters validParams()
const MeshGenerator & getMeshGenerator(const std::string &name) const
Definition MooseApp.h:920
const std::string & type() const
Get the type of this class.
Definition MooseBase.h:93
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
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
MooseApp & _app
The MOOSE application this is associated with.
Definition MooseBase.h:375
Snaps the nodes of a boundary onto the parametric curve of a ParsedCurveGenerator.
const unsigned int _samples_per_section
Number of uniform samples of each curve section used to bracket the closest curve point.
std::unique_ptr< MeshBase > & _curve_mesh
Reference to the mesh pointer of the curve generator, which is requested for its dependency.
ParsedCurveGenerator & _curve_generator
The ParsedCurveGenerator that defines the curve to snap the nodes onto, and evaluates it.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
const bool _is_closed_loop
Whether the curve is a closed loop, in which case the parameter wraps at the bounding values.
Point curvePoint(const Real t_param)
Evaluates the curve through the generator that defines it.
Real goldenSectionSearch(const Real t_lower, const Real t_upper, const Point &point)
Refines a bracket of the closest curve point with a golden-section search.
Real squaredDistance(const Real t_param, const Point &point)
Calculates the squared in-plane distance between a given point and a point of the curve.
std::vector< Point > _sample_points
Curve points at the sampled t values, evaluated once in generate() and reused for every node.
std::unique_ptr< MeshBase > & _input
Reference to the input mesh pointer, whose boundary nodes are snapped onto the curve.
ParsedCurveGenerator & curveGenerator() const
Gets the generator that defines the curve, after checking that it is a ParsedCurveGenerator.
Real boundedParameter(const Real t_param) const
Bounds a parameter t into the bounding t values of a closed loop, which is where the formulas of the ...
const BoundaryName _boundary_name
Name of the boundary whose nodes are snapped onto the curve.
Real closestParameter(const Point &point)
Finds the parameter t of the curve point that is the closest to a given point.
std::vector< Real > _t_samples
Sampled t values used to bracket the closest curve point.
MoveBoundaryNodesToCurveGenerator(const InputParameters &parameters)
const std::vector< Real > _section_bounding_t_values
t values that bound the sections of the curve
his ParsedCurveGenerator object is designed to generate a mesh of a curve that consists of EDGE2,...
Point pointCalculator(const Real t_param)
Calculates the point coordinates {x(t), y(t), z(t)} based on parameter t.
const boundary_id_type node_boundary_id
MeshBase & mesh
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
bool hasBoundaryNameOrID(const MeshBase &mesh, const BoundaryName &name_or_id)
Whether a particular boundary name or ID exists in the mesh.