https://mooseframework.inl.gov
Loading...
Searching...
No Matches
TransfiniteMeshGenerator.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#include "CastUniquePointer.h"
12#include "Conversion.h"
13#include "MooseMeshUtils.h"
14
15#include "libmesh/parsed_function.h"
16#include "libmesh/replicated_mesh.h"
17#include "libmesh/face_quad4.h"
18#include "libmesh/fparser_ad.hh"
19#include "libmesh/elem.h"
20
22
25{
28
29 MooseEnum edge_type("LINE=1 CIRCARC=2 DISCRETE=3 PARSED=4");
30
31 params.addRequiredParam<std::vector<Point>>("corners", "The x,y,z positions of the nodes");
32
33 // Define edge types
34 params.addRequiredParam<MooseEnum>("bottom_type", edge_type, "type of the bottom (y) boundary");
35 params.addRequiredParam<MooseEnum>("top_type", edge_type, "type of the top (y) boundary");
36 params.addRequiredParam<MooseEnum>("left_type", edge_type, "type of the left (x) boundary");
37 params.addRequiredParam<MooseEnum>("right_type", edge_type, "type of the right (x) boundary");
38
39 // We need to know the number of points on opposite sides, and if no other parameter is available
40 // we shall assume them to be equally distributed
41 params.addRequiredParam<unsigned int>("nx",
42 "Number of nodes on horizontal edges, including corners");
43 params.addRequiredParam<unsigned int>("ny",
44 "Number of Nodes on vertical edges, including corners");
45
46 // each edge has a different parameter according to its type
47 params.addParam<std::string>("bottom_parameter", "", "Bottom side support parameter");
48 params.addParam<std::string>("top_parameter", "", "Top side support parameter");
49 params.addParam<std::string>("left_parameter", "", "Left side support parameter");
50 params.addParam<std::string>("right_parameter", "", "Right side support parameter");
51
52 params.addRangeCheckedParam<Real>(
53 "bias_x",
54 1.,
55 "bias_x>=1.0 & bias_x<=2",
56 "The amount by which to grow (or shrink) the cells in the x-direction.");
57 params.addRangeCheckedParam<Real>(
58 "bias_y",
59 1.,
60 "bias_y>=1.0 & bias_y<=2",
61 "The amount by which to grow (or shrink) the cells in the y-direction.");
62
64 "Creates a QUAD4 mesh given a set of corner vertices and edge types. "
65 "The edge type can be either LINE, CIRCARC, DISCRETE or PARSED, with "
66 "LINE as the default option. "
67 "For the non-default options the user needs to specify additional "
68 "parameters via the edge_parameter option "
69 "as follows: for CIRCARC the deviation of the midpoint from an "
70 "arccircle, for DISCRETE a set of points, or "
71 "a paramterization via the PARSED option. Opposite edges may have "
72 "different distributions s long as the "
73 "number of points is identical. Along opposite edges a different point "
74 "distribution can be prescribed "
75 "via the options bias_x or bias_y for opposing edges.");
76
77 params.addParamNamesToGroup("bottom_type left_type top_type right_type", "Edge type");
78 params.addParamNamesToGroup("bottom_parameter left_parameter top_parameter right_parameter",
79 "Edge");
80 params.addParamNamesToGroup("nx ny bias_x bias_y", "Number and distribution of points");
81
82 return params;
83}
84
86 : MeshGenerator(parameters),
87 FunctionParserUtils<false>(parameters),
88 _corners(getParam<std::vector<Point>>("corners")),
89 _nx(getParam<unsigned int>("nx")),
90 _ny(getParam<unsigned int>("ny")),
91 _bottom_type(getParam<MooseEnum>("bottom_type")),
92 _top_type(getParam<MooseEnum>("top_type")),
93 _left_type(getParam<MooseEnum>("left_type")),
94 _right_type(getParam<MooseEnum>("right_type")),
95 _bottom_parameter(getParam<std::string>("bottom_parameter")),
96 _top_parameter(getParam<std::string>("top_parameter")),
97 _left_parameter(getParam<std::string>("left_parameter")),
98 _right_parameter(getParam<std::string>("right_parameter")),
99 _bias_x(getParam<Real>("bias_x")),
100 _bias_y(getParam<Real>("bias_y"))
101{
102 // initialize parsed function
103 _parsed_func = std::make_shared<SymFunction>();
105 _parsed_func->AddConstant("pi", libMesh::pi);
106 _func_params.resize(1);
107
108 mooseAssert((_nx > 1) && (_ny > 1),
109 "A minimum of 2 points is needed on each edge, i.e. the user needs to consider edge "
110 "vertices as well.");
111}
112std::unique_ptr<MeshBase>
114{
115 auto mesh = buildMeshBaseObject();
116
117 mesh->set_mesh_dimension(2);
118 mesh->set_spatial_dimension(2);
119 BoundaryInfo & boundary_info = mesh->get_boundary_info();
120
121 // explicitly assign corners since they will be used extensively and the ordering may be confusing
122 const Point V00 = _corners[0];
123 const Point V10 = _corners[1];
124 const Point V11 = _corners[2];
125 const Point V01 = _corners[3];
126
127 // we construct a vector that mimics the normal only to account for inward and outward arcircles
128 // note we do not need to define inward directions since they would multiply the sign of the
129 // arc circle parameter, which if negative would give inward vectors
130 std::vector<Point> outward_vec(4);
131 outward_vec[0] = Point(0.0, -1.0, 0.0);
132 outward_vec[1] = Point(0.0, 1.0, 0.0);
133 outward_vec[2] = Point(-1.0, 0.0, 0.0);
134 outward_vec[3] = Point(1.0, 0.0, 0.0);
135
136 const unsigned long int total_nodes = _nx * _ny;
137
138 // we take [0,1] as the reference interval and we need to set the biases upfront
139 Real edge_length = 1.0;
140 std::vector<Real> param_x_dir = getPointsDistribution(edge_length, _nx, _bias_x);
141 std::vector<Real> param_y_dir = getPointsDistribution(edge_length, _ny, _bias_y);
142
143 std::vector<Point> edge_bottom =
144 getEdge(V00, V10, _nx, _bottom_type, _bottom_parameter, outward_vec[0], param_x_dir);
145 std::vector<Point> edge_top =
146 getEdge(V01, V11, _nx, _top_type, _top_parameter, outward_vec[1], param_x_dir);
147 std::vector<Point> edge_left =
148 getEdge(V00, V01, _ny, _left_type, _left_parameter, outward_vec[2], param_y_dir);
149 std::vector<Point> edge_right =
150 getEdge(V10, V11, _ny, _right_type, _right_parameter, outward_vec[3], param_y_dir);
151
152 // Used for the parametrization on edge pairs, provided by the point distribution according to
153 // biases
154 Real rx_coord, sy_coord;
155
156 std::vector<Node *> nodes(total_nodes);
157 unsigned int node_id = 0;
158
159 Point newPt;
160 Real r1_basis, r2_basis, s1_basis, s2_basis;
161
162 for (unsigned int idx = 0; idx < _nx; idx++)
163 {
164 rx_coord = param_x_dir[idx];
165 r1_basis = 1 - rx_coord;
166 r2_basis = rx_coord;
167
168 for (unsigned int idy = 0; idy < _ny; idy++)
169 {
170 sy_coord = param_y_dir[idy];
171 s1_basis = 1 - sy_coord;
172 s2_basis = sy_coord;
173
174 // this is the core of the algorithm and generates every internal point
175 newPt = r2_basis * edge_right[idy] + r1_basis * edge_left[idy] + s1_basis * edge_bottom[idx] +
176 s2_basis * edge_top[idx] - r1_basis * s1_basis * V00 - r1_basis * s2_basis * V01 -
177 r2_basis * s1_basis * V10 - r2_basis * s2_basis * V11;
178
179 nodes[node_id] = mesh->add_point(newPt, node_id);
180 node_id++;
181 }
182 }
183
184 for (unsigned int idx = 0; idx < _nx - 1; idx++)
185 {
186 for (unsigned int idy = 0; idy < _ny - 1; idy++)
187 {
188 Elem * elem = mesh->add_elem(new Quad4);
189 elem->set_node(0, nodes[idy + idx * _ny]);
190 elem->set_node(1, nodes[idy + (idx + 1) * _ny]);
191 elem->set_node(2, nodes[idy + 1 + (idx + 1) * _ny]);
192 elem->set_node(3, nodes[idy + 1 + idx * _ny]);
193
194 if (idy == 0) // add bottom boundary (boundary_id = 0)
195 boundary_info.add_side(elem, 0, 0);
196
197 if (idx == _nx - 2) // add right boundary (boundary_id = 1)
198 boundary_info.add_side(elem, 1, 1);
199
200 if (idy == _ny - 2) // add top boundary (boundary_id = 2)
201 boundary_info.add_side(elem, 2, 2);
202
203 if (idx == 0) // add left boundary (boundary_id = 3)
204 boundary_info.add_side(elem, 3, 3);
205 }
206 }
207
208 boundary_info.sideset_name(0) = "bottom";
209 boundary_info.nodeset_name(0) = "bottom";
210
211 boundary_info.sideset_name(1) = "right";
212 boundary_info.nodeset_name(1) = "right";
213
214 boundary_info.sideset_name(2) = "top";
215 boundary_info.nodeset_name(2) = "top";
216
217 boundary_info.sideset_name(3) = "left";
218 boundary_info.nodeset_name(3) = "left";
219
220 mesh->prepare_for_use();
221 return dynamic_pointer_cast<MeshBase>(mesh);
222}
223
224std::vector<Point>
226 const Point & P2,
227 const unsigned int np,
228 const MooseEnum & type,
229 const std::string & parameter,
230 const Point & outward,
231 const std::vector<Real> & param_vec)
232{
233 std::vector<Point> edge;
234
235 switch (type)
236 {
237 case 1:
238 {
239 edge = getLineEdge(P1, P2, param_vec);
240 mooseAssert(MooseUtils::absoluteFuzzyEqual((edge[0] - P1).norm(), 0.0),
241 "The line does not fit the first vertex on the edge.");
242 mooseAssert(MooseUtils::absoluteFuzzyEqual((edge[np - 1] - P2).norm(), 0.0),
243 "The line does not fit the end vertex on the edge.");
244 }
245 break;
246 case 2:
247 {
248 edge = getCircarcEdge(P1, P2, parameter, outward, param_vec);
249 mooseAssert(MooseUtils::absoluteFuzzyEqual((edge[0] - P1).norm(), 0.0),
250 "No arccircle parametrization can be found to fit the first vertex on the edge.");
251 mooseAssert(MooseUtils::absoluteFuzzyEqual((edge[np - 1] - P2).norm(), 0.0),
252 "No arccircle parametrization can be found to fit the end vertex on the edge.");
253 }
254 break;
255 case 3:
256 {
257 edge = getDiscreteEdge(np, parameter);
258 mooseAssert(MooseUtils::absoluteFuzzyEqual((edge[0] - P1).norm(), 0.0),
259 "The first discrete point does not fit the corresponding edge vertex."
260 "Note: discrete points need to replicate the edge corners.");
261 mooseAssert(MooseUtils::absoluteFuzzyEqual((edge[np - 1] - P2).norm(), 0.0),
262 "The last discrete point does not fit the corresponding edge vertex."
263 "Note: discrete points need to replicate the edge corners.");
264 }
265 break;
266 case 4:
267 {
268 edge = getParsedEdge(parameter, param_vec);
269 mooseAssert(MooseUtils::absoluteFuzzyEqual((edge[0] - P1).norm(), 0.0),
270 "The parametrization does not fit the first vertex on the edge.");
271 mooseAssert(MooseUtils::absoluteFuzzyEqual((edge[np - 1] - P2).norm(), 0.0),
272 "The parametrization does not fit the end vertex on the edge.");
273 }
274 break;
275 }
276 if (edge.size() != np)
277 mooseError("The generated edge does not match the number of points on the"
278 "opposite edge.");
279 return edge;
280}
281
282std::vector<Point>
284 const Point & P2,
285 const std::vector<Real> & param_vec)
286{
287 std::vector<Point> edge(param_vec.size());
288 auto it = 0;
289
290 for (auto rx : param_vec)
291 {
292 edge[it] = P1 * (1.0 - rx) + P2 * rx;
293 it++;
294 };
295 return edge;
296}
297
298std::vector<Point>
299TransfiniteMeshGenerator::getParsedEdge(const std::string & parameter,
300 const std::vector<Real> & param_vec)
301{
302 std::vector<Point> edge(param_vec.size());
303 Real x_coord, y_coord;
304
305 std::vector<std::string> param_coords;
306 MooseUtils::tokenize(parameter, param_coords, 1, ";");
307
308 auto it = 0;
309 for (auto rx : param_vec)
310 {
311 _parsed_func->Parse(param_coords[0], "r");
312 x_coord = _parsed_func->Eval(&rx);
313 _parsed_func->Parse(param_coords[1], "r");
314 y_coord = _parsed_func->Eval(&rx);
315 edge[it] = Point(x_coord, y_coord, 0.0);
316 it++;
317 }
318
319 return edge;
320}
321
322std::vector<Point>
323TransfiniteMeshGenerator::getDiscreteEdge(const unsigned int np, const std::string & parameter)
324{
325 std::vector<Point> edge(np);
326 std::vector<std::string> string_points;
327 MooseUtils::tokenize(parameter, string_points, 1, "\n");
328 if (string_points.size() != np)
329 mooseError("DISCRETE: the number of discrete points does not match the number of points on the"
330 "opposite edge.");
331
332 auto it = 0;
333 for (unsigned int iter = 0; iter < string_points.size(); iter++)
334 {
335 std::vector<Real> point_vals;
336 MooseUtils::tokenizeAndConvert(string_points[iter], point_vals, " ");
337 edge[it] = Point(point_vals[0], point_vals[1], point_vals[2]);
338 it++;
339 }
340
341 return edge;
342}
343
344std::vector<Point>
346 const Point & P2,
347 const std::string & parameter,
348 const Point & outward,
349 const std::vector<Real> & param_vec)
350{
351 std::vector<Point> edge(param_vec.size());
352
353 std::vector<Real> param_coords;
354 MooseUtils::tokenizeAndConvert(parameter, param_coords, ";");
355 Point P3;
356 if (param_coords.size() == 1)
357 {
358 P3 = computeMidPoint(P1, P2, param_coords[0], outward);
359 }
360 else
361 {
362 P3 = Point(param_coords[0], param_coords[1], param_coords[2]);
363 }
364
365 Real rad = computeRadius(P1, P2, P3);
366 Point P0 = computeOrigin(P1, P2, P3);
367
368 // need to shift to center of coordinates to find the corresponding radians
369 Point x0 = (P1 - P0);
370 Point x1 = (P2 - P0);
371
372 // The case when the edge spans quadrants 1 and 4 requires special treatment
373 // to periodically switch we compute the angle that needs added to one edge
374 // to identify the entire edge span
375 mooseAssert(x0.norm() > 0.0 && x1.norm() > 0.0,
376 "The point provided cannot generate an arc circle on the edge specified");
377
378 Real arclength = std::acos((x0 * x1) / x0.norm() / x1.norm());
379
380 Real a = std::atan2(x0(1), x0(0));
381 Real b = std::atan2(x1(1), x1(0));
382
383 if (a < 0)
384 a = a + 2 * M_PI;
385 if (std::abs(b - a) > M_PI)
386 b = a + arclength;
387
388 auto it = 0;
389 for (auto rx : param_vec)
390 {
391 Real interval = getMapInterval(rx, 0.0, 1.0, a, b);
392 Real x = P0(0) + rad * std::cos(interval);
393 Real y = P0(1) + rad * std::sin(interval);
394 edge[it] = Point(x, y, 0.0);
395 it++;
396 };
397
398 return edge;
399}
400
401Real
403 const Real xab, const Real a, const Real b, const Real c, const Real d) const
404// this routine maps a point x\in[a, b] to the corresponding point in the interval [c, d]
405{
406 mooseAssert(std::abs(b - a) > 0.0,
407 "The input interval [a, b] is empty, check that a and b are not identical.");
408 Real xcd = c + (d - c) / (b - a) * (xab - a);
409
410 return xcd;
411}
412
413std::vector<Real>
415 const unsigned int np,
416 const Real bias) const
417{
418 std::vector<Real> param_vec;
419 Real rx = 0.0;
420
421 if (bias != 1.0)
422 {
423 Real step = 0.0;
424 Real factor = edge_length * (1.0 - std::abs(bias)) / (1.0 - std::pow(std::abs(bias), np - 1));
425 param_vec.push_back(rx);
426 for (unsigned int iter = 1; iter < np; iter++)
427 {
428 step = step + factor * std::pow(bias, Real(iter - 1));
429 rx = getMapInterval(step, 0.0, edge_length, 0.0, 1.0);
430 param_vec.push_back(rx);
431 }
432 }
433 else
434 {
435 const Real interval = edge_length / Real(np - 1);
436 for (unsigned int iter = 0; iter < np; iter++)
437 {
438 rx = getMapInterval(Real(iter) * interval, 0.0, edge_length, 0.0, 1.0);
439 param_vec.push_back(rx);
440 }
441 }
442 return param_vec;
443}
444
445Real
446TransfiniteMeshGenerator::computeRadius(const Point & P1, const Point & P2, const Point & P3) const
447{
448 Point temp1 = P1 - P2;
449 Real a2 = temp1.norm_sq(); // a is the distance from P1 to P2, but we only need it squared
450 Point temp2 = P3 - P1;
451 Real b2 = temp2.norm_sq();
452 Real dr = std::sqrt(b2 - a2 / 4.0);
453 const Real rad = a2 / 8.0 / dr + dr / 2.0;
454 return rad;
455}
456
457Point
458TransfiniteMeshGenerator::computeOrigin(const Point & P1, const Point & P2, const Point & P3) const
459{
460 // define interim quantities
461 const Real a1 = (2 * P3(0) - 2 * P1(0));
462 const Real a2 = (2 * P3(1) - 2 * P1(1));
463 const Real A = P3(1) * P3(1) - P1(0) * P1(0) + P3(0) * P3(0) - P1(1) * P1(1);
464 const Real b1 = (2 * P3(0) - 2 * P2(0));
465 const Real b2 = (2 * P3(1) - 2 * P2(1));
466 const Real B = P3(1) * P3(1) - P2(0) * P2(0) + P3(0) * P3(0) - P2(1) * P2(1);
467 mooseAssert(std::abs(b2) > 0.0 && std::abs(a1) > 0.0 && std::abs(a1 * b2 - a2 * b1) > 0.0,
468 "The point provided cannot generate an arc circle on the edge specified."
469 "The origin of the corresponding circle cannot be computed.");
470
471 const Real y0 = (a1 * B - A * b1) / (a1 * b2 - a2 * b1);
472 const Real x0 = (A - y0 * a2) / a1;
473
474 // fill in and return the origin point
475 Point P0 = Point(x0, y0, 0.0);
476 return P0;
477}
478
479Point
481 const Point & P2,
482 const Real height,
483 const Point & outward) const
484{
485 const Real xm = (P1(0) + P2(0)) / 2;
486 const Real ym = (P1(1) + P2(1)) / 2;
487 // The arc can be inverted into the domain (concave) or outward (convex)
488 // we use the convention that if the height is given as negative we seek a concave arc
489 // if "height" is positive then we seek a convex arc.
490 // The outward vector is sufficient to determine the direction of the arc.
491 const Real dist = std::abs(height);
492 const int orient = (height >= 0) ? 1 : -1;
493 Point MidPoint;
494
495 // this accounts for lines aligned with the system of coordinates
496 if ((std::abs(P2(0) - P1(0)) < 1e-15) || (std::abs(P2(1) - P1(1)) < 1e-15))
497 MidPoint = Point(xm, ym, 0.0) + dist * orient * outward;
498
499 // some of the cases are not covered by this strategy and we need to use normals info
500 // this is implemented and tested in the prototype and is soon to be updated
501 if (std::abs(P2(0) - P1(0)) > 1e-15 && std::abs(P2(1) - P1(1)) > 1e-15)
502 {
503 // m is the slope of the line orthogonal to the midpoint
504 const Real m = -(P2(0) - P1(0)) / (P2(1) - P1(1));
505 // The equation to determine allows two solutions
506 const Real x_temp = dist * sqrt((m * m + 1)) / (m * m + 1);
507 const Real factor = orient * outward(0) + m * orient * outward(1);
508 int direction = (factor >= 0) ? 1 : -1;
509
510 MidPoint = Point(direction * x_temp + xm, direction * x_temp * m + ym, 0.0);
511 }
512 return MidPoint;
513}
registerMooseObject("MooseApp", TransfiniteMeshGenerator)
void ErrorVector unsigned int
std::vector< GenericReal< is_ad > > _func_params
Array to stage the parameters passed to the functions when calling Eval.
void setParserFeatureFlags(SymFunctionPtr &) const
apply input parameters to internal feature flags of the parser object
static InputParameters validParams()
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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...
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.
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()
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.
const std::string & type() const
Get the type of this class.
Definition MooseBase.h:93
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
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
Generates an quadrilateral given all the parameters.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
std::vector< Point > getEdge(const Point &P1, const Point &P2, const unsigned int np, const MooseEnum &type, const std::string &parameter, const Point &outward, const std::vector< Real > &param_vec)
Real computeRadius(const Point &P1, const Point &P2, const Point &P3) const
std::vector< Point > getCircarcEdge(const Point &P1, const Point &P2, const std::string &parameter, const Point &outward, const std::vector< Real > &param_vec)
static InputParameters validParams()
Real getMapInterval(const Real xab, const Real a, const Real b, const Real c, const Real d) const
Point computeOrigin(const Point &P1, const Point &P2, const Point &P3) const
const std::vector< Point > & _corners
std::vector< Point > getLineEdge(const Point &P1, const Point &P2, const std::vector< Real > &param_vec)
Point computeMidPoint(const Point &P1, const Point &P2, const Real dist, const Point &outward) const
std::vector< Point > getParsedEdge(const std::string &parameter, const std::vector< Real > &param_vec)
std::vector< Real > getPointsDistribution(const Real edge_length, const unsigned int np, const Real bias) const
SymFunctionPtr _parsed_func
function parser object describing the combinatorial geometry
TransfiniteMeshGenerator(const InputParameters &parameters)
std::vector< Point > getDiscreteEdge(const unsigned int np, const std::string &parameter)
MeshBase & mesh
bool tokenizeAndConvert(const std::string &str, std::vector< T > &tokenized_vector, const std::string &delimiter=" \t\n\v\f\r")
tokenizeAndConvert splits a string using delimiter and then converts to type T.
void tokenize(const std::string &str, std::vector< T > &elements, unsigned int min_len=1, const std::string &delims="/")
This function will split the passed in string on a set of delimiters appending the substrings to the ...
const Real pi
MooseUnits pow(const MooseUnits &, int)
Definition Units.C:537