Line data Source code
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 :
23 : registerMooseObject("MooseApp", BSplineCurveGenerator);
24 :
25 : InputParameters
26 3632 : BSplineCurveGenerator::validParams()
27 : {
28 3632 : InputParameters params = MeshGenerator::validParams();
29 14528 : MooseEnum edge_elem_type("EDGE2 EDGE3 EDGE4", "EDGE2");
30 :
31 : // Convenience parameters
32 10896 : params.addParam<SubdomainID>(
33 7264 : "new_subdomain_id", 1, "Subdomain ID to assign to the curve elements");
34 14528 : params.addParam<SubdomainName>("new_subdomain_name",
35 : "Subdomain name to assign to the curve elements");
36 :
37 : // Geometry parameters
38 14528 : params.addParam<Point>("start_point", "Starting (x,y,z) point for curve.");
39 14528 : params.addParam<Point>("end_point", "Ending (x,y,z) point for curve.");
40 14528 : params.addParam<RealVectorValue>("start_direction", "Direction vector of curve at start point.");
41 14528 : params.addParam<RealVectorValue>("end_direction", "Direction vector of curve at end point.");
42 14528 : params.addParamNamesToGroup("start_point end_point start_direction end_direction",
43 : "Curve extremities input");
44 :
45 : // Alternative to start / end point
46 14528 : params.addParam<MeshGeneratorName>("start_mesh",
47 : "Meshgenerator providing the mesh to start spline from.");
48 14528 : 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 14528 : params.addParam<MeshGeneratorName>("end_mesh",
54 : "Meshgenerator providing the mesh to end splne on.");
55 14528 : 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 14528 : 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 14528 : params.addParam<unsigned int>("degree", 3, "Degree of interpolating polynomial.");
66 18160 : params.addRangeCheckedParam<libMesh::Real>(
67 7264 : "sharpness", 0.6, "sharpness>0 & sharpness<=1", "Sharpness of curve bend.");
68 10896 : params.addParam<unsigned int>(
69 : "num_cps",
70 7264 : 6,
71 : "Number of control points used to draw the curve. Minimum of degree+1 points are required.");
72 14528 : params.addParamNamesToGroup("degree sharpness num_cps", "Spline");
73 :
74 : // Discretization parameters
75 14528 : params.addParam<MooseEnum>(
76 : "edge_element_type", edge_elem_type, "Type of the EDGE elements to be generated.");
77 21792 : params.addRequiredRangeCheckedParam<unsigned int>(
78 : "num_elements", "num_elements>=1", "Numer of elements to be drawn. Must be at least 1.");
79 14528 : params.addParamNamesToGroup("edge_element_type num_elements", "Discretization");
80 :
81 7264 : 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 7264 : params.addParam<std::vector<BoundaryName>>("edge_nodesets",
85 7264 : std::vector<BoundaryName>(),
86 : "Nodeset name to give each edge of the spline curve");
87 :
88 7264 : return params;
89 3632 : }
90 :
91 287 : BSplineCurveGenerator::BSplineCurveGenerator(const InputParameters & parameters)
92 : : MeshGenerator(parameters),
93 287 : _new_subdomain_id(getParam<SubdomainID>("new_subdomain_id")),
94 574 : _degree(getParam<unsigned int>("degree")),
95 574 : _sharpness(getParam<libMesh::Real>("sharpness")),
96 574 : _num_cps(getParam<unsigned int>("num_cps")),
97 574 : _order((unsigned int)(getParam<MooseEnum>("edge_element_type")) + 1),
98 574 : _num_elements(getParam<unsigned int>("num_elements")),
99 574 : _node_set_boundaries(getParam<std::vector<BoundaryName>>("edge_nodesets")),
100 574 : _start_mesh_input(getMesh("start_mesh", true)),
101 1148 : _end_mesh_input(getMesh("end_mesh", true))
102 : {
103 287 : if (_num_cps < _degree + 1)
104 6 : paramError("num_cps", "Number of control points must be at least degree+1.");
105 :
106 : // Check input parameters
107 852 : if (!isParamValid("start_point"))
108 : {
109 228 : if (!isParamValid("boundary_providing_start_point"))
110 0 : paramError("boundary_providing_start_point",
111 : "boundary_providing_start_point must be specified if start_point is not");
112 228 : else if (!isParamValid("start_mesh"))
113 0 : paramError("start_mesh", "start_mesh must be specified if start_point is not.");
114 : }
115 : else
116 : {
117 1040 : if (isParamValid("boundary_providing_start_point") || isParamValid("start_mesh"))
118 0 : paramError("start_point",
119 : "start_point and boundary_providing_start_point or start_mesh cannot be "
120 : "simultaneously specified!");
121 624 : if (!isParamValid("start_direction"))
122 0 : paramError("start_direction",
123 : "Starting direction must be specified if the 'start_point' is specified");
124 : }
125 852 : if (!isParamValid("end_point"))
126 : {
127 228 : if (!isParamValid("boundary_providing_end_point"))
128 0 : paramError("boundary_providing_end_point",
129 : "boundary_providing_end_point must be specified if start_point is not");
130 228 : else if (!isParamValid("end_mesh"))
131 0 : paramError("end_mesh", "end_mesh must be specified if start_point is not.");
132 : }
133 : else
134 : {
135 1040 : if (isParamValid("boundary_providing_end_point") || isParamValid("end_mesh"))
136 0 : paramError("end_point",
137 : "end_point and boundary_providing_end_point or end_mesh cannot be "
138 : "simultaneously specified!");
139 624 : if (!isParamValid("end_direction"))
140 0 : paramError("end_direction",
141 : "Ending direction must be specified if the 'end_point' is specified");
142 : }
143 284 : if (_node_set_boundaries.size() != 0 && _node_set_boundaries.size() != 2)
144 0 : paramError("edge_nodesets", "If specified, edge_nodesets must have exactly 2 entries.");
145 284 : }
146 :
147 : std::unique_ptr<MeshBase>
148 272 : BSplineCurveGenerator::generate()
149 : {
150 272 : if (_start_mesh_input)
151 76 : _start_mesh = std::move(_start_mesh_input);
152 272 : if (_end_mesh_input)
153 76 : _end_mesh = std::move(_end_mesh_input);
154 :
155 272 : const auto start_point = startPoint();
156 272 : const auto end_point = endPoint();
157 272 : const auto start_dir = startDirection();
158 272 : const auto end_dir = endDirection();
159 :
160 272 : auto mesh = buildReplicatedMesh(2);
161 :
162 : // determine number of control points needed
163 : unsigned int half_cps;
164 272 : if (_num_cps % 2 == 0)
165 272 : half_cps = _num_cps / 2;
166 : else
167 : {
168 0 : half_cps = (_num_cps - 1) / 2;
169 0 : 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 272 : start_point, end_point, start_dir, end_dir, half_cps, _sharpness);
175 :
176 : // initialize BSpline class
177 : Moose::BSpline b_spline(
178 266 : _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 266 : const auto n_ts = _num_elements * _order + 1;
182 266 : std::vector<Node *> nodes(n_ts);
183 266 : std::vector<Point> eval_points;
184 6238 : for (const auto i : make_range(n_ts))
185 : {
186 : // n_ts-1 because max(t_current) must be 1.0
187 5972 : const auto t_current = ((Real)i / (Real)(n_ts - 1));
188 5972 : eval_points.push_back(b_spline.getPoint(t_current));
189 5972 : nodes[i] = mesh->add_point(eval_points.back(), i);
190 : }
191 :
192 : // create elements from points
193 5222 : for (const auto i : make_range(_num_elements))
194 : {
195 4956 : std::unique_ptr<Elem> new_elem;
196 4956 : switch (_order)
197 : {
198 4456 : case 1:
199 4456 : new_elem = std::make_unique<Edge2>();
200 4456 : break;
201 250 : case 2:
202 250 : new_elem = std::make_unique<Edge3>();
203 250 : new_elem->set_node(2, nodes[i * _order + 1]);
204 250 : break;
205 250 : default:
206 : {
207 250 : new_elem = std::make_unique<Edge4>();
208 250 : new_elem->set_node(2, nodes[i * _order + 1]);
209 250 : new_elem->set_node(3, nodes[i * _order + 2]);
210 : }
211 : }
212 :
213 4956 : new_elem->set_node(0, nodes[i * _order]);
214 : mooseAssert((i + 1) * _order < nodes.size(), "Out of bounds in nodes array");
215 4956 : new_elem->set_node(1, nodes[((i + 1) * _order)]);
216 :
217 4956 : new_elem->subdomain_id() = _new_subdomain_id;
218 4956 : mesh->add_elem(std::move(new_elem));
219 4956 : }
220 :
221 : // Add subdomain name if needed
222 798 : if (isParamValid("new_subdomain_name"))
223 24 : mesh->subdomain_name(_new_subdomain_id) = getParam<SubdomainName>("new_subdomain_name");
224 :
225 266 : if (_node_set_boundaries.size())
226 : {
227 : // Add boundary nodesets to boundary info
228 40 : BoundaryInfo & boundary_info = mesh->get_boundary_info();
229 40 : int i = 0;
230 120 : for (auto & side_name : _node_set_boundaries)
231 80 : boundary_info.nodeset_name(i++) = side_name;
232 :
233 40 : boundary_info.add_node(*nodes.begin(), boundary_info.get_id_by_name(_node_set_boundaries[0]));
234 40 : boundary_info.add_node(*(nodes.end() - 1),
235 40 : boundary_info.get_id_by_name(_node_set_boundaries[1]));
236 : }
237 532 : return dynamic_pointer_cast<MeshBase>(mesh);
238 266 : }
239 :
240 : Point
241 272 : BSplineCurveGenerator::startPoint() const
242 : {
243 816 : if (isParamValid("start_point"))
244 588 : return getParam<Point>("start_point");
245 : else
246 304 : return MooseMeshUtils::boundaryCentroidCalculator(
247 152 : getParam<BoundaryName>("boundary_providing_start_point"), *_start_mesh);
248 : }
249 :
250 : Point
251 272 : BSplineCurveGenerator::endPoint() const
252 : {
253 816 : if (isParamValid("end_point"))
254 588 : return getParam<Point>("end_point");
255 : else
256 304 : return MooseMeshUtils::boundaryCentroidCalculator(
257 152 : getParam<BoundaryName>("boundary_providing_end_point"), *_end_mesh);
258 : }
259 :
260 : RealVectorValue
261 272 : BSplineCurveGenerator::startDirection() const
262 : {
263 816 : if (isParamValid("start_direction"))
264 786 : return getParam<RealVectorValue>("start_direction");
265 : else
266 40 : return MooseMeshUtils::boundaryWeightedNormal(
267 20 : getParam<BoundaryName>("boundary_providing_start_point"), *_start_mesh);
268 : }
269 :
270 : RealVectorValue
271 272 : BSplineCurveGenerator::endDirection() const
272 : {
273 816 : if (isParamValid("end_direction"))
274 786 : return getParam<RealVectorValue>("end_direction");
275 : else
276 40 : return MooseMeshUtils::boundaryWeightedNormal(
277 20 : getParam<BoundaryName>("boundary_providing_end_point"), *_end_mesh);
278 : }
|