https://mooseframework.inl.gov
ParsedCurveGenerator.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 
10 #include "ParsedCurveGenerator.h"
11 #include "LinearInterpolation.h"
12 #include "MooseUtils.h"
13 #include "CastUniquePointer.h"
14 #include "MooseMeshUtils.h"
15 
16 #include "libmesh/fparser_ad.hh"
17 #include "libmesh/edge_edge2.h"
18 #include "libmesh/edge_edge3.h"
19 #include "libmesh/edge_edge4.h"
20 
21 // C++ includes
22 #include <cmath>
23 
25 
28 {
31 
32  MooseEnum edge_elem_type("EDGE2 EDGE3 EDGE4", "EDGE2");
33 
34  params.addRequiredParam<std::string>("x_formula", "Function expression of x(t)");
35  params.addRequiredParam<std::string>("y_formula", "Function expression of y(t)");
36  params.addParam<std::string>("z_formula", "0", "Function expression of z(t)");
37  params.addRequiredRangeCheckedParam<std::vector<unsigned int>>(
38  "nums_segments",
39  "nums_segments>=1",
40  "Numbers of segments (EDGE elements) of each section of the curve to be generated. The "
41  "number of entries in this parameter should be equal to one less than the number of entries "
42  "in 'section_bounding_t_values'");
43  params.addRequiredParam<std::vector<Real>>(
44  "section_bounding_t_values",
45  "The 't' values that bound the sections of the curve. Start and end points must be included. "
46  "The number of entries in 'nums_segments' should be equal to one less than the number of "
47  "entries in this parameter.");
48  params.addParam<std::vector<std::string>>(
49  "constant_names", "Vector of constants used in the parsed function (use this for kB etc.)");
50  params.addParam<std::vector<std::string>>(
51  "constant_expressions",
52  "Vector of values for the constants in constant_names (can be an FParser expression)");
53  params.addParam<bool>("is_closed_loop", false, "Whether the curve is closed or not.");
54  params.addRangeCheckedParam<Real>("point_overlapping_tolerance",
56  "point_overlapping_tolerance>0.0",
57  "The point-to-point distance tolerance that is used to "
58  "determine whether the two points are overlapped.");
59  params.addRangeCheckedParam<unsigned int>(
60  "forced_closing_num_segments",
61  "forced_closing_num_segments>1",
62  "Number of segments (EDGE elements) of the curve section that is generated to forcefully "
63  "close the loop.");
64  params.addRangeCheckedParam<Real>("oversample_factor",
65  10.0,
66  "oversample_factor>2.0",
67  "Oversample factor to help make node distance nearly uniform.");
68  params.addRangeCheckedParam<unsigned int>(
69  "max_oversample_number_factor",
70  1000,
71  "max_oversample_number_factor>100",
72  "For each section of the curve, the maximum number of oversampling points is the product of "
73  "this factor and the number of nodes on the curve.");
74  params.addParam<std::vector<BoundaryName>>(
75  "edge_nodesets", std::vector<BoundaryName>(), "Nodesets on both edges of the parsed curves");
76  params.addParam<MooseEnum>(
77  "edge_element_type", edge_elem_type, "Type of the EDGE elements to be generated.");
78  params.addClassDescription("This ParsedCurveGenerator object is designed to generate a mesh of a "
79  "curve that consists of EDGE2, EDGE3, or EDGE4 elements.");
80 
81  return params;
82 }
83 
85  : MeshGenerator(parameters),
86  FunctionParserUtils<false>(parameters),
87  _function_x(getParam<std::string>("x_formula")),
88  _function_y(getParam<std::string>("y_formula")),
89  _function_z(getParam<std::string>("z_formula")),
90  _nums_segments(getParam<std::vector<unsigned int>>("nums_segments")),
91  _section_bounding_t_values(getParam<std::vector<Real>>("section_bounding_t_values")),
92  _is_closed_loop(getParam<bool>("is_closed_loop")),
93  _point_overlapping_tolerance(getParam<Real>("point_overlapping_tolerance")),
94  _forced_closing_num_segments(isParamValid("forced_closing_num_segments")
95  ? getParam<unsigned int>("forced_closing_num_segments")
96  : 0),
97  _oversample_factor(getParam<Real>("oversample_factor")),
98  _max_oversample_number_factor(getParam<unsigned int>("max_oversample_number_factor")),
99  _node_set_boundaries(getParam<std::vector<BoundaryName>>("edge_nodesets")),
100  _order(getParam<MooseEnum>("edge_element_type") == "EDGE2"
101  ? 1
102  : (getParam<MooseEnum>("edge_element_type") == "EDGE3" ? 2 : 3))
103 {
104  if (std::adjacent_find(_section_bounding_t_values.begin(), _section_bounding_t_values.end()) !=
106  paramError("section_bounding_t_values", "elements must be unique.");
109  paramError("section_bounding_t_values", "elements must change monotonically.");
110  if (_nums_segments.size() != _section_bounding_t_values.size() - 1)
111  paramError(
112  "nums_segments",
113  "The size of this parameter must be one less than size of section_bounding_t_values.");
114  _func_Fx = std::make_shared<SymFunction>();
115  _func_Fy = std::make_shared<SymFunction>();
116  _func_Fz = std::make_shared<SymFunction>();
117 
118  // set FParser internal feature flags
122 
123  // add the constant expressions; note that the three functions share one set of constants
124  if (isParamValid("constant_names") && isParamValid("constant_expressions"))
125  {
127  getParam<std::vector<std::string>>("constant_names"),
128  getParam<std::vector<std::string>>("constant_expressions"));
130  getParam<std::vector<std::string>>("constant_names"),
131  getParam<std::vector<std::string>>("constant_expressions"));
133  getParam<std::vector<std::string>>("constant_names"),
134  getParam<std::vector<std::string>>("constant_expressions"));
135  }
136 
137  // parse functions
138  if (_func_Fx->Parse(_function_x, "t") >= 0)
139  mooseError("Invalid function x(t)\n",
140  _function_x,
141  "\nin ParsedCurveGenerator ",
142  name(),
143  ".\n",
144  _func_Fx->ErrorMsg());
145  if (_func_Fy->Parse(_function_y, "t") >= 0)
146  mooseError("Invalid function y(t)\n",
147  _function_y,
148  "\nin ParsedCurveGenerator ",
149  name(),
150  ".\n",
151  _func_Fy->ErrorMsg());
152  if (_func_Fz->Parse(_function_z, "t") >= 0)
153  mooseError("Invalid function z(t)\n",
154  _function_z,
155  "\nin ParsedCurveGenerator ",
156  name(),
157  ".\n",
158  _func_Fz->ErrorMsg());
159 
160  _func_params.resize(1);
161 
163  paramError("forced_closing_num_segments",
164  "this parameter is not needed if the curve to be generated is not a closed loop.");
165 
166  if (_node_set_boundaries.size() > 1 && _is_closed_loop)
167  paramError("edge_nodesets", "Cannot add more than one edge nodeset on a closed loop");
168  if (_node_set_boundaries.size() && _node_set_boundaries.size() != 2 && !_is_closed_loop)
169  paramError("edge_nodesets", "Exactly two edges in an open loop");
170 }
171 
172 std::unique_ptr<MeshBase>
174 {
175  auto mesh = buildReplicatedMesh(2);
176 
177  // Do oversampling for each section of the curve as defined by "section_bounding_t_values"
178  for (unsigned int i = 0; i < _nums_segments.size(); i++)
179  {
180  std::vector<Real> t_sect_space;
181  std::vector<Real> dis_sect_space;
184  t_sect_space,
185  dis_sect_space,
186  _nums_segments[i] * _order,
187  _is_closed_loop && _nums_segments.size() == 1,
189  if (i > 0)
190  {
191  // Remove the last t value and distance value of previous section to avert overlapping
192  _t_space.pop_back();
193  // Add the last distance value as this is a cumulative distance
194  for (auto & dis_elem : dis_sect_space)
195  dis_elem += _dis_space.back();
196  _dis_space.pop_back();
197  }
198  _t_space.insert(_t_space.end(), t_sect_space.begin(), t_sect_space.end());
199  _dis_space.insert(_dis_space.end(), dis_sect_space.begin(), dis_sect_space.end());
200  }
201 
202  // Use linear interpolation to help achieve nearly uniform intervals for each section
203  std::unique_ptr<LinearInterpolation> linear_t;
204  linear_t = std::make_unique<LinearInterpolation>(_dis_space, _t_space);
205 
206  std::vector<Node *> nodes(
207  std::accumulate(_nums_segments.begin(), _nums_segments.end(), 0) * _order + 1);
208  for (unsigned int i = 0; i < nodes.size(); i++)
209  {
210  _func_params[0] = linear_t->sample((Real)i);
211  Point side_p = Point(evaluate(_func_Fx), evaluate(_func_Fy), evaluate(_func_Fz));
212  nodes[i] = mesh->add_point(side_p, i);
213  }
214 
215  // For a closed loop, need to check if the first and last Points are overlapped
216  if (_is_closed_loop)
217  {
219  (*nodes.back() - *nodes.front()).norm(), 0.0, _point_overlapping_tolerance))
220  {
221  // Remove the overlapped nodes for a closed loop
222  mesh->delete_node(nodes.back());
223  nodes.resize(nodes.size() - 1);
225  paramError("forced_closing_num_segments",
226  "this parameter is not needed if the first and last points of the curve to be "
227  "generated are overlapped.");
228  }
229  else if (_forced_closing_num_segments * _order > 1)
230  {
231  // Add extra nodes on the curve section used to forcefully close the loop
232  const Point start_pt(*nodes.back());
233  const Point end_pt(*nodes.front());
234  const unsigned int num_nodes_0(nodes.size());
235  for (unsigned int i = 1; i < _forced_closing_num_segments * _order; i++)
236  {
237  Point side_p = start_pt + (end_pt - start_pt) * (Real)i /
239  nodes.push_back(mesh->add_point(side_p, num_nodes_0 + i - 1));
240  }
241  }
242  }
243 
244  const unsigned int num_elems = (nodes.size() - !_is_closed_loop) / _order;
245  for (unsigned int i = 0; i < num_elems; i++)
246  {
247  std::unique_ptr<Elem> new_elem;
248  new_elem = std::make_unique<Edge2>();
249  if (_order > 1)
250  {
251  new_elem = std::make_unique<Edge3>();
252  if (_order == 3)
253  {
254  new_elem = std::make_unique<Edge4>();
255  new_elem->set_node(3, nodes[i * _order + 2]);
256  }
257  new_elem->set_node(2, nodes[i * _order + 1]);
258  }
259  new_elem->set_node(0, nodes[i * _order]);
260  new_elem->set_node(1, nodes[((i + 1) * _order) % nodes.size()]);
261 
262  new_elem->subdomain_id() = 1;
263  mesh->add_elem(std::move(new_elem));
264  }
265 
266  if (_node_set_boundaries.size())
267  {
268  // Add boundary nodesets to boundary info
269  BoundaryInfo & boundary_info = mesh->get_boundary_info();
270  int i = 0;
271  for (auto & side_name : _node_set_boundaries)
272  boundary_info.nodeset_name(i++) = side_name;
273 
274  boundary_info.add_node(*nodes.begin(), boundary_info.get_id_by_name(_node_set_boundaries[0]));
275  if (_node_set_boundaries.size() > 1)
276  boundary_info.add_node(*(nodes.end() - 1),
277  boundary_info.get_id_by_name(_node_set_boundaries[1]));
278  }
279 
280  return dynamic_pointer_cast<MeshBase>(mesh);
281 }
282 
283 void
285  const Real t_end,
286  std::vector<Real> & t_sect_space,
287  std::vector<Real> & dis_sect_space,
288  unsigned int num_segments,
289  const bool is_closed_loop,
290  const Real oversample_factor)
291 {
292  std::vector<Point> pt_sect_space;
293  t_sect_space.push_back(t_start);
294  t_sect_space.push_back(t_end);
295  pt_sect_space.push_back(pointCalculator(t_start));
296  pt_sect_space.push_back(pointCalculator(t_end));
297  const Real total_distance =
298  is_closed_loop
299  ? euclideanDistance(pt_sect_space.front(), pointCalculator((t_start + t_end) / 2.0))
300  : euclideanDistance(pt_sect_space.front(), pt_sect_space.back());
301  if (MooseUtils::absoluteFuzzyEqual(total_distance, 0.0))
302  paramError("section_bounding_t_values",
303  "The curve has at least one cross, which is not supported.");
304  const Real target_fine_interval = total_distance / (Real)num_segments / oversample_factor;
305  unsigned int t_space_id = 0;
306  dis_sect_space.push_back(0.0);
307  // Use a maximum oversampling points number and a counter to avert infinite loop
308  const unsigned int max_binary_search = _max_oversample_number_factor * (num_segments + 1);
309  unsigned int binary_search_counter(0);
310  // Use binary algorithm to make all the intervals smaller than the target value
311  while (t_space_id + 1 < t_sect_space.size() && binary_search_counter <= max_binary_search)
312  {
313  binary_search_counter++;
314  if (euclideanDistance(pt_sect_space[t_space_id], pt_sect_space[t_space_id + 1]) >
315  target_fine_interval)
316  {
317  const Real new_t_value = (t_sect_space[t_space_id] + t_sect_space[t_space_id + 1]) / 2.0;
318  t_sect_space.insert(t_sect_space.begin() + t_space_id + 1, new_t_value);
319  pt_sect_space.insert(pt_sect_space.begin() + t_space_id + 1, pointCalculator(new_t_value));
320  }
321  else
322  {
323  dis_sect_space.push_back(
324  dis_sect_space.back() +
325  euclideanDistance(pt_sect_space[t_space_id], pt_sect_space[t_space_id + 1]));
326  t_space_id++;
327  }
328  }
329  if (binary_search_counter > max_binary_search)
330  paramError(
331  "max_oversample_number_factor",
332  "Maximum oversampling points number has been exceeded. Please consider adding more t "
333  "values into 'section_bounding_t_values' or increase 'max_oversample_number_factor'.");
334  const Real total_dis_seg = dis_sect_space.back();
335  // Normalization to make the normalized length of the curve equals to number of segments
336  for (auto & dist : dis_sect_space)
337  dist = dist / total_dis_seg * (Real)num_segments;
338 }
339 
340 Point
342 {
343  _func_params[0] = t_param;
344  return Point(evaluate(_func_Fx), evaluate(_func_Fy), evaluate(_func_Fz));
345 }
346 
347 Real
348 ParsedCurveGenerator::euclideanDistance(const Point p1, const Point p2)
349 {
350  return (p1 - p2).norm();
351 }
GenericReal< is_ad > evaluate(SymFunctionPtr &, const std::string &object_name="")
Evaluate FParser object and check EvalError.
void addFParserConstants(SymFunctionPtr &parser, const std::vector< std::string > &constant_names, const std::vector< std::string > &constant_expressions) const
add constants (which can be complex expressions) to the parser object
Real euclideanDistance(const Point p1, const Point p2)
Calculates the Euclidean distance between two given points.
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
These methods add an range checked parameters.
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
std::unique_ptr< ReplicatedMesh > buildReplicatedMesh(unsigned int dim=libMesh::invalid_uint)
Build a replicated mesh.
SymFunctionPtr _func_Fy
function parser object describing the y(t)
static constexpr Real TOLERANCE
const std::string _function_x
function expression for x(t)
const bool _is_closed_loop
whether the curve is a closed loop or not
ParsedCurveGenerator(const InputParameters &parameters)
const Real _oversample_factor
Oversampling factor to help make node distance nearly uniform.
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
SymFunctionPtr _func_Fz
function parser object describing the z(t)
const Real _point_overlapping_tolerance
the point-to-point distance tolerance that is used to determine whether the two points are overlapped...
virtual const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:57
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...
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
const unsigned int _order
order of the EDGE elements to be generated
static InputParameters validParams()
const std::string _function_z
function expression for z(t)
const std::vector< Real > _section_bounding_t_values
critical t values that define the sections of the curve
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
std::vector< Real > _t_space
t values that are sampled for curve points
constexpr bool is_sorted()
Check if the given index sequence is sorted ()internal function)
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 ...
const unsigned int _forced_closing_num_segments
Number of segments of the curve section that is generated to forcefully close the loop...
auto norm(const T &a) -> decltype(std::abs(a))
his ParsedCurveGenerator object is designed to generate a mesh of a curve that consists of EDGE2...
static InputParameters validParams()
Definition: MeshGenerator.C:23
Point pointCalculator(const Real t_param)
Calculates the point coordinates {x(t), y(t), 0.0} based on parameter t.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const unsigned int _max_oversample_number_factor
A factor used to calculate the maximum oversampling points number in each section.
const std::vector< unsigned int > _nums_segments
numbers of side segments of each section defined by section_bounding_t_values
std::vector< GenericReal< is_ad > > _func_params
Array to stage the parameters passed to the functions when calling Eval.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
SymFunctionPtr _func_Fx
function parser object describing the x(t)
std::vector< Real > _dis_space
cumulative distances of the curve points from the starting ppint
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...
static InputParameters validParams()
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 addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
registerMooseObject("MooseApp", ParsedCurveGenerator)
void tSectionSpaceDefiner(const Real t_start, const Real t_end, std::vector< Real > &t_sect_space, std::vector< Real > &dis_sect_space, unsigned int num_segments, const bool is_closed_loop, const Real oversample_factor)
Calculates the oversampled parameter t series and corresponding cumulative distances from the startin...
const std::string _function_y
function expression for y(t)
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:32
void ErrorVector unsigned int
std::vector< BoundaryName > _node_set_boundaries
vector of the names of the boundaries at the ends of the parsed curve
void setParserFeatureFlags(SymFunctionPtr &) const
apply input parameters to internal feature flags of the parser object