https://mooseframework.inl.gov
BSplineCurveGenerator.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 // MOOSE includes
11 #include "BSplineCurveGenerator.h"
12 #include "CastUniquePointer.h"
13 #include "MooseMeshUtils.h"
14 #include "BSpline.h"
15 #include "SplineUtils.h"
16 
17 #include "libmesh/edge_edge2.h"
18 #include "libmesh/edge_edge3.h"
19 #include "libmesh/edge_edge4.h"
20 
21 using namespace libMesh;
22 
24 
27 {
29  MooseEnum edge_elem_type("EDGE2 EDGE3 EDGE4", "EDGE2");
30 
31  // Convenience parameters
32  params.addParam<SubdomainID>(
33  "new_subdomain_id", 1, "Subdomain ID to assign to the curve elements");
34  params.addParam<SubdomainName>("new_subdomain_name",
35  "Subdomain name to assign to the curve elements");
36 
37  // Geometry parameters
38  params.addParam<Point>("start_point", "Starting (x,y,z) point for curve.");
39  params.addParam<Point>("end_point", "Ending (x,y,z) point for curve.");
40  params.addParam<RealVectorValue>("start_direction", "Direction vector of curve at start point.");
41  params.addParam<RealVectorValue>("end_direction", "Direction vector of curve at end point.");
42  params.addParamNamesToGroup("start_point end_point start_direction end_direction",
43  "Curve extremities input");
44 
45  // Alternative to start / end point
46  params.addParam<MeshGeneratorName>("start_mesh",
47  "Meshgenerator providing the mesh to start spline from.");
48  params.addParam<BoundaryName>(
49  "boundary_providing_start_point",
50  "Boundary at whose centroid the spline should start. If the start_direction is not set, the "
51  "starting direction is computed from a side-volume average of the side-vertex-average "
52  "normals of the boundary sides");
53  params.addParam<MeshGeneratorName>("end_mesh",
54  "Meshgenerator providing the mesh to end splne on.");
55  params.addParam<BoundaryName>(
56  "boundary_providing_end_point",
57  "Boundary at whose centroid the spline should end. If the end_direction is not set, the "
58  "ending direction is computed from a side-volume average of the side-vertex-average normals "
59  "of the boundary sides");
60  params.addParamNamesToGroup(
61  "start_mesh end_mesh boundary_providing_start_point boundary_providing_end_point",
62  "Curve extremities input");
63 
64  // Spline shape parameters
65  params.addParam<unsigned int>("degree", 3, "Degree of interpolating polynomial.");
67  "sharpness", 0.6, "sharpness>0 & sharpness<=1", "Sharpness of curve bend.");
68  params.addParam<unsigned int>(
69  "num_cps",
70  6,
71  "Number of control points used to draw the curve. Minimum of degree+1 points are required.");
72  params.addParamNamesToGroup("degree sharpness num_cps", "Spline");
73 
74  // Discretization parameters
75  params.addParam<MooseEnum>(
76  "edge_element_type", edge_elem_type, "Type of the EDGE elements to be generated.");
77  params.addRequiredRangeCheckedParam<unsigned int>(
78  "num_elements", "num_elements>=1", "Numer of elements to be drawn. Must be at least 1.");
79  params.addParamNamesToGroup("edge_element_type num_elements", "Discretization");
80 
81  params.addClassDescription(
82  "This BSplineMeshGenerator object is designed to generate a mesh of a curve that consists of "
83  "EDGE2, EDGE3, or EDGE4 elements drawn using an open uniform B-Spline.");
84  params.addParam<std::vector<BoundaryName>>("edge_nodesets",
85  std::vector<BoundaryName>(),
86  "Nodeset name to give each edge of the spline curve");
87 
88  return params;
89 }
90 
92  : MeshGenerator(parameters),
93  _new_subdomain_id(getParam<SubdomainID>("new_subdomain_id")),
94  _degree(getParam<unsigned int>("degree")),
95  _sharpness(getParam<libMesh::Real>("sharpness")),
96  _num_cps(getParam<unsigned int>("num_cps")),
97  _order((unsigned int)(getParam<MooseEnum>("edge_element_type")) + 1),
98  _num_elements(getParam<unsigned int>("num_elements")),
99  _node_set_boundaries(getParam<std::vector<BoundaryName>>("edge_nodesets")),
100  _start_mesh_input(getMesh("start_mesh", true)),
101  _end_mesh_input(getMesh("end_mesh", true))
102 {
103  if (_num_cps < _degree + 1)
104  paramError("num_cps", "Number of control points must be at least degree+1.");
105 
106  // Check input parameters
107  if (!isParamValid("start_point"))
108  {
109  if (!isParamValid("boundary_providing_start_point"))
110  paramError("boundary_providing_start_point",
111  "boundary_providing_start_point must be specified if start_point is not");
112  else if (!isParamValid("start_mesh"))
113  paramError("start_mesh", "start_mesh must be specified if start_point is not.");
114  }
115  else
116  {
117  if (isParamValid("boundary_providing_start_point") || isParamValid("start_mesh"))
118  paramError("start_point",
119  "start_point and boundary_providing_start_point or start_mesh cannot be "
120  "simultaneously specified!");
121  if (!isParamValid("start_direction"))
122  paramError("start_direction",
123  "Starting direction must be specified if the 'start_point' is specified");
124  }
125  if (!isParamValid("end_point"))
126  {
127  if (!isParamValid("boundary_providing_end_point"))
128  paramError("boundary_providing_end_point",
129  "boundary_providing_end_point must be specified if start_point is not");
130  else if (!isParamValid("end_mesh"))
131  paramError("end_mesh", "end_mesh must be specified if start_point is not.");
132  }
133  else
134  {
135  if (isParamValid("boundary_providing_end_point") || isParamValid("end_mesh"))
136  paramError("end_point",
137  "end_point and boundary_providing_end_point or end_mesh cannot be "
138  "simultaneously specified!");
139  if (!isParamValid("end_direction"))
140  paramError("end_direction",
141  "Ending direction must be specified if the 'end_point' is specified");
142  }
143  if (_node_set_boundaries.size() != 0 && _node_set_boundaries.size() != 2)
144  paramError("edge_nodesets", "If specified, edge_nodesets must have exactly 2 entries.");
145 }
146 
147 std::unique_ptr<MeshBase>
149 {
150  if (_start_mesh_input)
151  _start_mesh = std::move(_start_mesh_input);
152  if (_end_mesh_input)
153  _end_mesh = std::move(_end_mesh_input);
154 
155  const auto start_point = startPoint();
156  const auto end_point = endPoint();
157  const auto start_dir = startDirection();
158  const auto end_dir = endDirection();
159 
160  auto mesh = buildReplicatedMesh(2);
161 
162  // determine number of control points needed
163  unsigned int half_cps;
164  if (_num_cps % 2 == 0)
165  half_cps = _num_cps / 2;
166  else
167  {
168  half_cps = (_num_cps - 1) / 2;
169  mooseWarning("Need an even number of control points. `num_cps` has been decreased by 1.");
170  }
171 
172  // generate points using BSpline functions/class
173  std::vector<Point> control_points = SplineUtils::bSplineControlPoints(
174  start_point, end_point, start_dir, end_dir, half_cps, _sharpness);
175 
176  // initialize BSpline class
177  Moose::BSpline b_spline(
178  _degree, start_point, end_point, start_dir, end_dir, half_cps, _sharpness);
179 
180  // discretize t and evaluate points, assemble into nodes inside loop
181  const auto n_ts = _num_elements * _order + 1;
182  std::vector<Node *> nodes(n_ts);
183  std::vector<Point> eval_points;
184  for (const auto i : make_range(n_ts))
185  {
186  // n_ts-1 because max(t_current) must be 1.0
187  const auto t_current = ((Real)i / (Real)(n_ts - 1));
188  eval_points.push_back(b_spline.getPoint(t_current));
189  nodes[i] = mesh->add_point(eval_points.back(), i);
190  }
191 
192  // create elements from points
193  for (const auto i : make_range(_num_elements))
194  {
195  std::unique_ptr<Elem> new_elem;
196  switch (_order)
197  {
198  case 1:
199  new_elem = std::make_unique<Edge2>();
200  break;
201  case 2:
202  new_elem = std::make_unique<Edge3>();
203  new_elem->set_node(2, nodes[i * _order + 1]);
204  break;
205  default:
206  {
207  new_elem = std::make_unique<Edge4>();
208  new_elem->set_node(2, nodes[i * _order + 1]);
209  new_elem->set_node(3, nodes[i * _order + 2]);
210  }
211  }
212 
213  new_elem->set_node(0, nodes[i * _order]);
214  mooseAssert((i + 1) * _order < nodes.size(), "Out of bounds in nodes array");
215  new_elem->set_node(1, nodes[((i + 1) * _order)]);
216 
217  new_elem->subdomain_id() = _new_subdomain_id;
218  mesh->add_elem(std::move(new_elem));
219  }
220 
221  // Add subdomain name if needed
222  if (isParamValid("new_subdomain_name"))
223  mesh->subdomain_name(_new_subdomain_id) = getParam<SubdomainName>("new_subdomain_name");
224 
225  if (_node_set_boundaries.size())
226  {
227  // Add boundary nodesets to boundary info
228  BoundaryInfo & boundary_info = mesh->get_boundary_info();
229  int i = 0;
230  for (auto & side_name : _node_set_boundaries)
231  boundary_info.nodeset_name(i++) = side_name;
232 
233  boundary_info.add_node(*nodes.begin(), boundary_info.get_id_by_name(_node_set_boundaries[0]));
234  boundary_info.add_node(*(nodes.end() - 1),
235  boundary_info.get_id_by_name(_node_set_boundaries[1]));
236  }
238 }
239 
240 Point
242 {
243  if (isParamValid("start_point"))
244  return getParam<Point>("start_point");
245  else
247  getParam<BoundaryName>("boundary_providing_start_point"), *_start_mesh);
248 }
249 
250 Point
252 {
253  if (isParamValid("end_point"))
254  return getParam<Point>("end_point");
255  else
257  getParam<BoundaryName>("boundary_providing_end_point"), *_end_mesh);
258 }
259 
262 {
263  if (isParamValid("start_direction"))
264  return getParam<RealVectorValue>("start_direction");
265  else
267  getParam<BoundaryName>("boundary_providing_start_point"), *_start_mesh);
268 }
269 
272 {
273  if (isParamValid("end_direction"))
274  return getParam<RealVectorValue>("end_direction");
275  else
277  getParam<BoundaryName>("boundary_providing_end_point"), *_end_mesh);
278 }
std::vector< BoundaryName > _node_set_boundaries
vector of the names of the boundaries at the ends of the spline curve
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
These methods add an range checked parameters.
std::string & nodeset_name(boundary_id_type id)
RealVectorValue endDirection() const
Return the ending direction of the spline.
RealVectorValue boundaryWeightedNormal(const BoundaryName &boundary, MeshBase &mesh)
Calculates the side-volume weighted (side-vertex) average normal of a boundary on a mesh...
std::unique_ptr< ReplicatedMesh > buildReplicatedMesh(unsigned int dim=libMesh::invalid_uint)
Build a replicated mesh.
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
const unsigned int _num_elements
number of edge elements on the curve
std::unique_ptr< MeshBase > _end_mesh
If &#39;end_mesh&#39; parameter is set, mesh providing the ending boundary.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
const unsigned int _order
order of the EDGE elements to be generated
const unsigned int _num_cps
number of control points to be generated
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.
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
const BoundaryInfo & get_boundary_info() const
RealVectorValue startDirection() const
Return the starting direction of the spline.
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
registerMooseObject("MooseApp", BSplineCurveGenerator)
std::unique_ptr< MeshBase > & _end_mesh_input
If &#39;end_mesh&#39; parameter is set, reference to input mesh providing the ending boundary.
boundary_id_type get_id_by_name(std::string_view name) const
void mooseWarning(Args &&... args) const
void add_node(const Node *node, const boundary_id_type id)
static InputParameters validParams()
const Real _sharpness
sharpness of curve (measure of how close it is to the curve with three orthogonal segments) ...
virtual Elem * add_elem(Elem *e)=0
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:54
Point endPoint() const
Return the ending point of the spline.
std::string & subdomain_name(subdomain_id_type id)
static InputParameters validParams()
Definition: MeshGenerator.C:23
BSplineCurveGenerator(const InputParameters &parameters)
std::vector< Point > bSplineControlPoints(const libMesh::Point &start_point, const libMesh::Point &end_point, const libMesh::RealVectorValue &start_direction, const libMesh::RealVectorValue &end_direction, const unsigned int cps_per_half, const libMesh::Real sharpness)
Creates control points for an open uniform BSpline.
Definition: SplineUtils.C:67
libMesh::Point getPoint(const libMesh::Real t) const
Evaluate the BSpline interpolation at given value of t.
Definition: BSpline.C:40
const unsigned int _degree
degree of interpolating spline
const SubdomainID _new_subdomain_id
Subdomain ID for the elements created.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Class implementing a uniform clamped B-Spline curve.
Definition: BSpline.h:25
Point boundaryCentroidCalculator(const BoundaryName &boundary, MeshBase &mesh)
Calculates the centroid of a boundary on a mesh.
std::unique_ptr< MeshBase > _start_mesh
If &#39;start_mesh&#39; parameter is set, mesh providing the starting boundary.
Point startPoint() const
Return the starting point of the spline.
IntRange< T > make_range(T beg, T end)
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...
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition: MooseBase.h:209
Mesh generator to create a 1D B-spline curve mesh in 3D space.
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:33
void ErrorVector unsigned int
std::unique_ptr< MeshBase > & _start_mesh_input
If &#39;start_mesh&#39; parameter is set, reference to input mesh providing the starting boundary.
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...