https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ConcentricCircleMeshGenerator.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 "libmesh/replicated_mesh.h"
13#include "libmesh/face_quad4.h"
14#include "libmesh/mesh_modification.h"
15#include "libmesh/serial_mesh.h"
16#include "libmesh/boundary_info.h"
17#include "libmesh/utility.h"
18#include "libmesh/mesh_smoother_laplace.h"
19
20// C++ includes
21#include <cmath> // provides round, not std::round (see http://www.cplusplus.com/reference/cmath/round/)
22
24
27{
29 params.addParam<SubdomainName>("subdomain_name",
30 "Name of the subdomain assigned to all the elements");
31 MooseEnum portion(
32 "full top_right top_left bottom_left bottom_right right_half left_half top_half bottom_half",
33 "full");
34 params.addRequiredParam<unsigned int>("num_sectors",
35 "num_sectors % 2 = 0, num_sectors > 0"
36 "Number of azimuthal sectors in each quadrant"
37 "'num_sectors' must be an even number.");
38 params.addRequiredParam<std::vector<Real>>("radii", "Radii of major concentric circles");
39 params.addRequiredParam<std::vector<unsigned int>>(
40 "rings", "Number of rings in each circle or in the enclosing square");
41 params.addRequiredParam<bool>(
42 "has_outer_square",
43 "It determines if meshes for a outer square are added to concentric circle meshes.");
44 params.addRangeCheckedParam<Real>(
45 "pitch",
46 0.0,
47 "pitch>=0.0",
48 "The enclosing square can be added to the completed concentric circle mesh."
49 "Elements are quad meshes.");
50 params.addParam<MooseEnum>("portion", portion, "Control of which part of mesh is created");
51 params.addRequiredParam<bool>(
52 "preserve_volumes", "Volume of concentric circles can be preserved using this function.");
53 params.addParam<unsigned int>("smoothing_max_it", 1, "Number of Laplacian smoothing iterations");
55 "This ConcentricCircleMeshGenerator source code is to generate concentric "
56 "circle meshes.");
57
58 return params;
59}
60
62 : MeshGenerator(parameters),
63 _num_sectors(getParam<unsigned int>("num_sectors")),
64 _radii(getParam<std::vector<Real>>("radii")),
65 _rings(getParam<std::vector<unsigned int>>("rings")),
66 _has_outer_square(getParam<bool>("has_outer_square")),
67 _pitch(getParam<Real>("pitch")),
68 _preserve_volumes(getParam<bool>("preserve_volumes")),
69 _smoothing_max_it(getParam<unsigned int>("smoothing_max_it")),
70 _portion(getParam<MooseEnum>("portion"))
71{
72 declareMeshProperty("use_distributed_mesh", false);
73
74 if (_num_sectors % 2 != 0)
75 paramError("num_sectors", "must be an even number.");
76
77 // radii data check
78 for (unsigned i = 0; i < _radii.size() - 1; ++i)
79 {
80 if (_radii[i] == 0.0)
81 paramError("radii", "must be positive numbers.");
82 if (_radii[i] > _radii[i + 1])
83 paramError("radii",
84 "must be provided in order by starting with the smallest radius providing the "
85 "following gradual radii.");
86 }
87
88 // size of 'rings' check
90 {
91 if (_rings.size() != _radii.size() + 1)
92 paramError("rings", "The size of 'rings' must be equal to the size of 'radii' plus 1.");
93 }
94 else
95 {
96 if (_rings.size() != _radii.size())
97 paramError("rings", "The size of 'rings' must be equal to the size of 'radii'.");
98 }
99 // pitch / 2 must be bigger than any raddi.
101 for (unsigned i = 0; i < _radii.size(); ++i)
102 if (_pitch / 2 < _radii[i])
103 paramError("pitch", "The pitch / 2 must be larger than any radii.");
104}
105
106std::unique_ptr<MeshBase>
108{
109 auto mesh = buildReplicatedMesh(2);
110 BoundaryInfo & boundary_info = mesh->get_boundary_info();
111
112 // Creating real mesh concentric circles
113 // i: index for _rings, j: index for _radii
114 std::vector<Real> total_concentric_circles;
115 unsigned int j = 0;
116 while (j < _radii.size())
117 {
118 unsigned int i = 0;
119 if (j == 0)
120 while (i < _rings[j])
121 {
122 total_concentric_circles.push_back(_radii[j] / (_num_sectors / 2 + _rings[j]) *
123 (i + _num_sectors / 2 + 1));
124 ++i;
125 }
126 else
127 while (i < _rings[j])
128 {
129 total_concentric_circles.push_back(_radii[j - 1] +
130 (_radii[j] - _radii[j - 1]) / _rings[j] * (i + 1));
131 ++i;
132 }
133 ++j;
134 }
135
136 // volume preserving function is used to conserve volume.
137 const Real d_angle = M_PI / 2 / _num_sectors;
138
140 {
141 Real original_radius = 0.0;
142 for (unsigned i = 0; i < total_concentric_circles.size(); ++i)
143 {
144 // volume preserving function for the center circle
145 if (i == 0)
146 {
147 const Real target_area = M_PI * Utility::pow<2>(total_concentric_circles[i]);
148 Real modified_radius = std::sqrt(2 * target_area / std::sin(d_angle) / _num_sectors / 4);
149 original_radius = total_concentric_circles[i];
150 total_concentric_circles[i] = modified_radius;
151 }
152 else
153 {
154 // volume preserving functions for outer circles
155 const Real target_area = M_PI * (Utility::pow<2>(total_concentric_circles[i]) -
156 Utility::pow<2>(original_radius));
157 Real modified_radius = std::sqrt(target_area / std::sin(d_angle) / _num_sectors / 2 +
158 Utility::pow<2>(total_concentric_circles[i - 1]));
159 original_radius = total_concentric_circles[i];
160 total_concentric_circles[i] = modified_radius;
161 }
162 }
163 }
164
165 // number of total nodes
166 unsigned num_total_nodes = 0;
167
169 num_total_nodes = Utility::pow<2>(_num_sectors / 2 + 1) +
170 (_num_sectors + 1) * total_concentric_circles.size() +
171 Utility::pow<2>(_rings.back() + 2) + _num_sectors * (_rings.back() + 1) - 1;
172 else
173 num_total_nodes = Utility::pow<2>(_num_sectors / 2 + 1) +
174 (_num_sectors + 1) * total_concentric_circles.size();
175
176 std::vector<Node *> nodes(num_total_nodes);
177
178 unsigned node_id = 0;
179
180 // for adding nodes for the square at the center of the circle
181 Real xx = total_concentric_circles[0] / (_num_sectors / 2 + 1) * _num_sectors / 2;
182 Point p1 = Point(xx, 0, 0);
183 Point p2 = Point(0, xx, 0);
184 Point p3 = Point(xx * std::sqrt(2.0) / 2, xx * std::sqrt(2.0) / 2, 0);
185 for (unsigned i = 0; i <= _num_sectors / 2; ++i)
186 {
187 Real fx = i / (_num_sectors / 2.0);
188 for (unsigned j = 0; j <= _num_sectors / 2; ++j)
189 {
190 Real fy = j / (_num_sectors / 2.0);
191 Point p = p1 * fx * (1 - fy) + p2 * fy * (1 - fx) + p3 * fx * fy;
192 nodes[node_id] = mesh->add_point(p, node_id);
193 ++node_id;
194 }
195 }
196
197 // for adding the outer layers of the square
198 Real current_radius = total_concentric_circles[0];
199
200 // for adding the outer circles of the square.
201 for (unsigned layers = 0; layers < total_concentric_circles.size(); ++layers)
202 {
203 current_radius = total_concentric_circles[layers];
204 for (unsigned num_outer_nodes = 0; num_outer_nodes <= _num_sectors; ++num_outer_nodes)
205 {
206 const Real x = current_radius * std::cos(num_outer_nodes * d_angle);
207 const Real y = current_radius * std::sin(num_outer_nodes * d_angle);
208 nodes[node_id] = mesh->add_point(Point(x, y, 0.0), node_id);
209 ++node_id;
210 }
211 }
212
213 // adding nodes for the enclosing square.
214 // adding nodes for the left edge of the square.
215 // applying the method for partitioning the line segments.
216
218 {
219 // putting nodes for the left side of the enclosing square.
220 for (unsigned i = 0; i <= _num_sectors / 2; ++i)
221 {
222 const Real a1 = (_pitch / 2) * i / (_num_sectors / 2 + _rings.back() + 1);
223 const Real b1 = _pitch / 2;
224
225 const Real a2 = total_concentric_circles.back() * std::cos(M_PI / 2 - i * d_angle);
226 const Real b2 = total_concentric_circles.back() * std::sin(M_PI / 2 - i * d_angle);
227
228 for (unsigned j = 0; j <= _rings.back(); ++j)
229 {
230 Real x = ((_rings.back() + 1 - j) * a1 + j * a2) / (_rings.back() + 1);
231 Real y = ((_rings.back() + 1 - j) * b1 + j * b2) / (_rings.back() + 1);
232 nodes[node_id] = mesh->add_point(Point(x, y, 0.0), node_id);
233 ++node_id;
234 }
235 }
236 // putting nodes for the center part of the enclosing square.
237 unsigned k = 1;
238 for (unsigned i = 1; i <= _rings.back() + 1; ++i)
239 {
240 const Real a1 =
241 (_pitch / 2) * (i + _num_sectors / 2) / (_num_sectors / 2 + _rings.back() + 1);
242 const Real b1 = _pitch / 2;
243
244 const Real a2 = _pitch / 2;
245 const Real b2 = (_pitch / 2) * (_num_sectors / 2) / (_num_sectors / 2 + _rings.back() + 1);
246
247 const Real a3 = total_concentric_circles.back() * std::cos(M_PI / 4);
248 const Real b3 = total_concentric_circles.back() * std::sin(M_PI / 4);
249
250 const Real a4 = ((_rings.back() + 1 - k) * a3 + k * a2) / (_rings.back() + 1);
251 const Real b4 = ((_rings.back() + 1 - k) * b3 + k * b2) / (_rings.back() + 1);
252
253 for (unsigned j = 0; j <= _rings.back() + 1; ++j)
254 {
255 Real x = ((_rings.back() + 1 - j) * a1 + j * a4) / (_rings.back() + 1);
256 Real y = ((_rings.back() + 1 - j) * b1 + j * b4) / (_rings.back() + 1);
257 nodes[node_id] = mesh->add_point(Point(x, y, 0.0), node_id);
258 ++node_id;
259 }
260 ++k;
261 }
262
263 // putting nodes for the right part of the enclosing square.
264 for (unsigned i = 1; i <= _num_sectors / 2; ++i)
265 {
266 const Real a1 = _pitch / 2;
267 const Real b1 =
268 (_pitch / 2) * (_num_sectors / 2 - i) / (_num_sectors / 2 + _rings.back() + 1);
269
270 const Real a2 = total_concentric_circles.back() * std::cos(M_PI / 4 - i * d_angle);
271 const Real b2 = total_concentric_circles.back() * std::sin(M_PI / 4 - i * d_angle);
272
273 for (unsigned j = 0; j <= _rings.back(); ++j)
274 {
275 Real x = ((_rings.back() + 1 - j) * a1 + j * a2) / (_rings.back() + 1);
276 Real y = ((_rings.back() + 1 - j) * b1 + j * b2) / (_rings.back() + 1);
277 nodes[node_id] = mesh->add_point(Point(x, y, 0.0), node_id);
278 ++node_id;
279 }
280 }
281 }
282
283 // Currently, index, limit, counter variables use the int type because of the 'modulo' function.
284 // adding elements
285 int index = 0;
286 int limit = 0;
287 int standard = static_cast<int>(_num_sectors);
288
289 // This is to set the limit for the index
290 if (standard > 4)
291 {
292 int additional_term = 0;
293 int counter = standard;
294 while (counter > 4)
295 {
296 counter = counter - 2;
297 additional_term = additional_term + counter;
298 }
299 limit = standard + additional_term;
300 }
301 else if (standard == 4)
302 limit = standard;
303
304 // SubdomainIDs set up
305 std::vector<unsigned int> subdomainIDs;
306
308 for (unsigned int i = 0; i < _rings.size() - 1; ++i)
309 for (unsigned int j = 0; j < _rings[i]; ++j)
310 subdomainIDs.push_back(i + 1);
311 else
312 for (unsigned int i = 0; i < _rings.size(); ++i)
313 for (unsigned int j = 0; j < _rings[i]; ++j)
314 subdomainIDs.push_back(i + 1);
315
316 // adding elements in the square
317 while (index <= limit)
318 {
319 // inner circle area (polygonal core)
320 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
321 elem->set_node(0, nodes[index]);
322 elem->set_node(1, nodes[index + _num_sectors / 2 + 1]);
323 elem->set_node(2, nodes[index + _num_sectors / 2 + 2]);
324 elem->set_node(3, nodes[index + 1]);
325 elem->subdomain_id() = subdomainIDs[0];
326
327 if (index < standard / 2)
328 boundary_info.add_side(elem, 3, 1);
329 if (index % (standard / 2 + 1) == 0)
330 boundary_info.add_side(elem, 0, 2);
331
332 ++index;
333
334 // increment by 2 indices is necessary depending on where the index points to.
335 if ((index - standard / 2) % (standard / 2 + 1) == 0)
336 ++index;
337 }
338
339 index = (_num_sectors / 2 + 1) * (_num_sectors / 2);
340 limit = (_num_sectors / 2) * (_num_sectors / 2 + 2);
341
342 // adding elements in one outer layer of the square (right side)
343 while (index < limit)
344 {
345 // inner circle elements touching B
346 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
347 elem->set_node(0, nodes[index]);
348 elem->set_node(1, nodes[index + _num_sectors / 2 + 1]);
349 elem->set_node(2, nodes[index + _num_sectors / 2 + 2]);
350 elem->set_node(3, nodes[index + 1]);
351 elem->subdomain_id() = subdomainIDs[0];
352
353 if (index == (standard / 2 + 1) * (standard / 2))
354 boundary_info.add_side(elem, 0, 2);
355
356 ++index;
357 }
358
359 // adding elements in one outer layer of the square (left side)
360 int counter = 0;
361 while (index != standard / 2)
362 {
363 // inner circle elements touching C
364 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
365 elem->set_node(0, nodes[index]);
366 elem->set_node(1, nodes[index + (_num_sectors / 2 + 1) + counter * (_num_sectors / 2 + 2)]);
367 elem->set_node(2, nodes[index + (_num_sectors / 2 + 1) + counter * (_num_sectors / 2 + 2) + 1]);
368 elem->set_node(3, nodes[index - _num_sectors / 2 - 1]);
369 elem->subdomain_id() = subdomainIDs[0];
370
371 if (index == standard + 1)
372 boundary_info.add_side(elem, 2, 1);
373
374 index = index - _num_sectors / 2 - 1;
375 ++counter;
376 }
377
378 counter = 0;
379 // adding elements for other concentric circles
380 index = Utility::pow<2>(standard / 2 + 1);
381 limit = Utility::pow<2>(standard / 2 + 1) +
382 (_num_sectors + 1) * (total_concentric_circles.size() - 1);
383
384 int num_nodes_boundary = Utility::pow<2>(standard / 2 + 1) + standard + 1;
385
386 while (index < limit)
387 {
388 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
389 elem->set_node(0, nodes[index]);
390 elem->set_node(1, nodes[index + standard + 1]);
391 elem->set_node(2, nodes[index + standard + 2]);
392 elem->set_node(3, nodes[index + 1]);
393
394 for (int i = 0; i < static_cast<int>(subdomainIDs.size() - 1); ++i)
395 if (index < limit - (standard + 1) * i && index >= limit - (standard + 1) * (i + 1))
396 elem->subdomain_id() = subdomainIDs[subdomainIDs.size() - 1 - i];
397
398 const int initial = Utility::pow<2>(standard / 2 + 1);
399 const int final = Utility::pow<2>(standard / 2 + 1) + standard - 1;
400
401 if ((index - initial) % (standard + 1) == 0)
402 boundary_info.add_side(elem, 0, 2);
403 if ((index - final) % (standard + 1) == 0)
404 boundary_info.add_side(elem, 2, 1);
406 if (index >= limit - (standard + 1))
407 boundary_info.add_side(elem, 1, 3);
408
409 // index increment is for adding nodes for a next element.
410 ++index;
411
412 // increment by 2 indices may be necessary depending on where the index points to.
413 // this varies based on the algorithms provided for the specific element and node placement.
414 if (index == (num_nodes_boundary + counter * (standard + 1)) - 1)
415 {
416 ++index;
417 ++counter;
418 }
419 }
420
421 // Enclosing square sections
422 // ABCA
423 // C B
424 // B C
425 // ACBA
426
427 // adding elements for the enclosing square. (top left)
428 int initial =
429 Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size();
430
431 int initial2 =
432 Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size();
433
435 {
436 if (_rings.back() != 0) // this must be condition up front.
437 {
438 index = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size();
439 limit = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
440 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 3 -
441 _rings.back() * (_rings.back() + 2) - (_rings.back() + 1);
442 while (index <= limit)
443 {
444 // outer square sector C
445 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
446 elem->set_node(0, nodes[index]);
447 elem->set_node(1, nodes[index + 1]);
448 elem->set_node(2, nodes[index + 1 + _rings.back() + 1]);
449 elem->set_node(3, nodes[index + 1 + _rings.back()]);
450 elem->subdomain_id() = subdomainIDs.back() + 1;
451
452 if (index < (initial2 + static_cast<int>(_rings.back())))
453 boundary_info.add_side(elem, 0, 1);
454
455 if (index == initial)
456 boundary_info.add_side(elem, 3, 4);
457
458 ++index;
459
460 // As mentioned before, increment by 2 indices may be required depending on where the index
461 // points to.
462 if ((index - initial) % static_cast<int>(_rings.back()) == 0)
463 {
464 ++index;
465 initial = initial + (static_cast<int>(_rings.back()) + 1);
466 }
467 }
468
469 // adding elements for the enclosing square. (top right)
470 initial = Utility::pow<2>(standard / 2 + 1) +
471 (standard + 1) * total_concentric_circles.size() +
472 (_rings.back() + 1) * (standard / 2 + 1);
473
474 limit = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
475 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 3 -
476 (_rings.back() + 2);
477
478 while (index <= limit)
479 {
480 // outer square sector A
481 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
482 elem->set_node(3, nodes[index]);
483 elem->set_node(2, nodes[index + _rings.back() + 2]);
484 elem->set_node(1, nodes[index + _rings.back() + 3]);
485 elem->set_node(0, nodes[index + 1]);
486 elem->subdomain_id() = subdomainIDs.back() + 1;
487
488 if (index >= static_cast<int>(limit - (_rings.back() + 1)))
489 boundary_info.add_side(elem, 1, 3);
490
491 if ((index - initial) % static_cast<int>(_rings.back() + 2) == 0)
492 boundary_info.add_side(elem, 2, 4);
493
494 ++index;
495
496 if ((index - initial) % static_cast<int>(_rings.back() + 1) == 0)
497 {
498 ++index;
499 initial = initial + (static_cast<int>(_rings.back()) + 2);
500 }
501 }
502
503 // adding elements for the enclosing square. (one center quad)
504 int index1 = Utility::pow<2>(standard / 2 + 1) +
505 (standard + 1) * (total_concentric_circles.size() - 1) + standard / 2;
506
507 int index2 = Utility::pow<2>(standard / 2 + 1) +
508 (standard + 1) * total_concentric_circles.size() +
509 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 3 -
510 _rings.back() * (_rings.back() + 2) - (_rings.back() + 1);
511
512 // pointy tips of the A sectors, touching the inner circle
513 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
514 elem->set_node(3, nodes[index1]);
515 elem->set_node(2, nodes[index2]);
516 elem->set_node(1, nodes[index2 + _rings.back() + 1]);
517 elem->set_node(0, nodes[index2 + _rings.back() + 2]);
518 elem->subdomain_id() = subdomainIDs.back() + 1;
519
520 // adding elements for the left mid part.
521 index = Utility::pow<2>(standard / 2 + 1) + standard / 2 +
522 (standard + 1) * (total_concentric_circles.size() - 1);
523 limit = index + standard / 2 - 1;
524
525 while (index <= limit)
526 {
527 // outer square elements in sector C touching the inner circle
528 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
529 elem->set_node(3, nodes[index]);
530 elem->set_node(2, nodes[index + 1]);
531 elem->set_node(1, nodes[index2 - _rings.back() - 1]);
532 elem->set_node(0, nodes[index2]);
533 elem->subdomain_id() = subdomainIDs.back() + 1;
534
535 if (index == limit)
536 boundary_info.add_side(elem, 1, 1);
537
538 ++index;
539
540 // two different indices are used to add nodes for an element.
541 index2 = index2 - _rings.back() - 1;
542 }
543
544 // adding elements for the right mid part.
545 index1 = Utility::pow<2>(standard / 2 + 1) + standard / 2 +
546 (standard + 1) * (total_concentric_circles.size() - 1);
547 index2 = Utility::pow<2>(standard / 2 + 1) +
548 (standard + 1) * total_concentric_circles.size() +
549 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 2 +
550 (_rings.back() + 1);
551 int index3 =
552 Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
553 (_rings.back() + 1) * (standard / 2) - 1 + (_rings.back() + 1) + (_rings.back() + 2);
554
555 // elements clockwise from the A sector tips
556 elem = mesh->add_elem(std::make_unique<Quad4>());
557 elem->set_node(0, nodes[index1]);
558 elem->set_node(1, nodes[index1 - 1]);
559 elem->set_node(2, nodes[index2]);
560 elem->set_node(3, nodes[index3]);
561 elem->subdomain_id() = subdomainIDs.back() + 1;
562
563 if (standard == 2)
564 boundary_info.add_side(elem, 1, 2);
565
566 // adding elements for the right mid bottom part.
567
568 index = Utility::pow<2>(standard / 2 + 1) + standard / 2 +
569 (standard + 1) * (total_concentric_circles.size() - 1) - 2;
570 index1 = Utility::pow<2>(standard / 2 + 1) +
571 (standard + 1) * total_concentric_circles.size() +
572 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 2 +
573 (_rings.back() + 1) * 2;
574
575 limit = Utility::pow<2>(standard / 2 + 1) + standard / 2 +
576 (standard + 1) * (total_concentric_circles.size() - 1) - standard / 2;
577
578 if (standard != 2)
579 {
580 while (index >= limit)
581 {
582 // outer square elements in sector B touching the inner circle
583 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
584 elem->set_node(0, nodes[index]);
585 elem->set_node(1, nodes[index1]);
586 elem->set_node(2, nodes[index1 - (_rings.back() + 1)]);
587 elem->set_node(3, nodes[index + 1]);
588 elem->subdomain_id() = subdomainIDs.back() + 1;
589
590 if (index == limit)
591 boundary_info.add_side(elem, 0, 2);
592 --index;
593 index1 = index1 + (_rings.back() + 1);
594 }
595 }
596
597 // adding elements for the right low part.
598 index = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
599 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 2;
600
601 index1 = index - (_rings.back() + 2);
602 // dummy condition for elem definition
603 if (standard >= 2)
604 {
605 // single elements between A and B on the outside of the square
606 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
607 elem->set_node(3, nodes[index]);
608 elem->set_node(2, nodes[index + 1]);
609 elem->set_node(1, nodes[index + 2]);
610 elem->set_node(0, nodes[index1]);
611 elem->subdomain_id() = subdomainIDs.back() + 1;
612
613 boundary_info.add_side(elem, 2, 3);
614
615 if (standard == 2)
616 boundary_info.add_side(elem, 1, 2);
617 }
618
619 index = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
620 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 2 -
621 (_rings.back() + 2);
622
623 limit = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
624 (_rings.back() + 1) * (standard / 2) + (_rings.back() + 2) * 2 - 2;
625
626 int k = 1;
627 while (index > limit)
628 {
629 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
630 elem->set_node(3, nodes[index]);
631 elem->set_node(2, nodes[index + (_rings.back() + 2) * k + k + 1]);
632 elem->set_node(1, nodes[index + (_rings.back() + 2) * k + k + 2]);
633 elem->set_node(0, nodes[index - _rings.back() - 2]);
634 elem->subdomain_id() = subdomainIDs.back() + 1;
635 index = index - (_rings.back() + 2);
636 ++k;
637
638 if (standard == 2)
639 boundary_info.add_side(elem, 1, 2);
640 }
641
642 index = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
643 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 1;
644 initial = Utility::pow<2>(standard / 2 + 1) +
645 (standard + 1) * total_concentric_circles.size() +
646 (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 1;
647 limit = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
648 (_rings.back() + 1) * (standard / 2) * 2 + Utility::pow<2>(_rings.back() + 2) - 2 -
649 _rings.back() - 1;
650
651 initial2 = Utility::pow<2>(standard / 2 + 1) +
652 (standard + 1) * total_concentric_circles.size() +
653 (_rings.back() + 1) * (standard / 2) * 2 + Utility::pow<2>(_rings.back() + 2) - 2 -
654 (_rings.back() + 1) * 2;
655
656 if (standard > 2)
657 {
658 while (index < limit)
659 {
660 Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
661 elem->set_node(0, nodes[index]);
662 elem->set_node(1, nodes[index + 1]);
663 elem->set_node(2, nodes[index + 1 + _rings.back() + 1]);
664 elem->set_node(3, nodes[index + 1 + _rings.back()]);
665 elem->subdomain_id() = subdomainIDs.back() + 1;
666
667 if (index > initial2)
668 boundary_info.add_side(elem, 2, 2);
669
670 if ((index - initial) == 0)
671 boundary_info.add_side(elem, 3, 3);
672
673 ++index;
674
675 if ((index - initial) % static_cast<int>(_rings.back()) == 0)
676 {
677 ++index;
678 initial = initial + (static_cast<int>(_rings.back()) + 1);
679 }
680 }
681 }
682 }
683 }
684
685 // This is to set boundary names.
686 boundary_info.sideset_name(1) = "left";
687 boundary_info.sideset_name(2) = "bottom";
688
690 boundary_info.sideset_name(3) = "outer";
691 else
692 {
693 boundary_info.sideset_name(3) = "right";
694 boundary_info.sideset_name(4) = "top";
695 }
696
697 if (_portion == "top_left")
698 {
699 MeshTools::Modification::rotate(*mesh, 90, 0, 0);
700 boundary_info.sideset_name(1) = "bottom";
701 boundary_info.sideset_name(2) = "right";
702
704 boundary_info.sideset_name(3) = "outer";
705 else
706 {
707 boundary_info.sideset_name(3) = "top";
708 boundary_info.sideset_name(4) = "left";
709 }
710 }
711 else if (_portion == "bottom_left")
712 {
713 MeshTools::Modification::rotate(*mesh, 180, 0, 0);
714 boundary_info.sideset_name(1) = "right";
715 boundary_info.sideset_name(2) = "top";
716
718 boundary_info.sideset_name(3) = "outer";
719 else
720 {
721 boundary_info.sideset_name(3) = "left";
722 boundary_info.sideset_name(4) = "bottom";
723 }
724 }
725 else if (_portion == "bottom_right")
726 {
727 MeshTools::Modification::rotate(*mesh, 270, 0, 0);
728 boundary_info.sideset_name(1) = "top";
729 boundary_info.sideset_name(2) = "left";
730
732 boundary_info.sideset_name(3) = "outer";
733 else
734 {
735 boundary_info.sideset_name(3) = "bottom";
736 boundary_info.sideset_name(4) = "right";
737 }
738 }
739
740 else if (_portion == "top_half")
741 {
742 ReplicatedMesh other_mesh(*mesh);
743 // This is to rotate the mesh and also to reset boundary IDs.
744 MeshTools::Modification::rotate(other_mesh, 90, 0, 0);
746 {
747 MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
748 MeshTools::Modification::change_boundary_id(other_mesh, 2, 6);
749 MeshTools::Modification::change_boundary_id(other_mesh, 3, 7);
750 MeshTools::Modification::change_boundary_id(other_mesh, 4, 1);
751 MeshTools::Modification::change_boundary_id(other_mesh, 5, 2);
752 MeshTools::Modification::change_boundary_id(other_mesh, 6, 3);
753 MeshTools::Modification::change_boundary_id(other_mesh, 7, 4);
754 mesh->prepare_for_use();
755 other_mesh.prepare_for_use();
756 mesh->stitch_meshes(other_mesh, 1, 3, TOLERANCE, true, /*verbose=*/false);
757 mesh->get_boundary_info().sideset_name(1) = "left";
758 mesh->get_boundary_info().sideset_name(2) = "bottom";
759 mesh->get_boundary_info().sideset_name(3) = "right";
760 mesh->get_boundary_info().sideset_name(4) = "top";
761 }
762 else
763 {
764 MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
765 MeshTools::Modification::change_boundary_id(other_mesh, 2, 1);
766 MeshTools::Modification::change_boundary_id(other_mesh, 5, 2);
767 mesh->prepare_for_use();
768 other_mesh.prepare_for_use();
769 mesh->stitch_meshes(other_mesh, 1, 1, TOLERANCE, true, /*verbose=*/false);
770
771 MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
772 MeshTools::Modification::change_boundary_id(*mesh, 3, 2);
773 mesh->get_boundary_info().sideset_name(1) = "bottom";
774 mesh->get_boundary_info().sideset_name(2) = "outer";
775 }
776 other_mesh.clear();
777 }
778
779 else if (_portion == "right_half")
780 {
781 ReplicatedMesh other_mesh(*mesh);
782 // This is to rotate the mesh and also to reset boundary IDs.
783 MeshTools::Modification::rotate(other_mesh, 270, 0, 0);
785 {
786 MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
787 MeshTools::Modification::change_boundary_id(other_mesh, 2, 6);
788 MeshTools::Modification::change_boundary_id(other_mesh, 3, 7);
789 MeshTools::Modification::change_boundary_id(other_mesh, 4, 3);
790 MeshTools::Modification::change_boundary_id(other_mesh, 5, 4);
791 MeshTools::Modification::change_boundary_id(other_mesh, 6, 1);
792 MeshTools::Modification::change_boundary_id(other_mesh, 7, 2);
793 mesh->prepare_for_use();
794 other_mesh.prepare_for_use();
795 mesh->stitch_meshes(other_mesh, 2, 4, TOLERANCE, true, /*verbose=*/false);
796 mesh->get_boundary_info().sideset_name(1) = "left";
797 mesh->get_boundary_info().sideset_name(2) = "bottom";
798 mesh->get_boundary_info().sideset_name(3) = "right";
799 mesh->get_boundary_info().sideset_name(4) = "top";
800 }
801 else
802 {
803 MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
804 MeshTools::Modification::change_boundary_id(other_mesh, 2, 1);
805 MeshTools::Modification::change_boundary_id(other_mesh, 5, 2);
806 mesh->prepare_for_use();
807 other_mesh.prepare_for_use();
808 mesh->stitch_meshes(other_mesh, 2, 2, TOLERANCE, true, /*verbose=*/false);
809
810 MeshTools::Modification::change_boundary_id(*mesh, 3, 2);
811 mesh->get_boundary_info().sideset_name(1) = "left";
812 mesh->get_boundary_info().sideset_name(2) = "outer";
813 }
814 other_mesh.clear();
815 }
816 else if (_portion == "left_half")
817 {
818 ReplicatedMesh other_mesh(*mesh);
819
820 // This is to rotate the mesh and to reset boundary IDs.
821 MeshTools::Modification::rotate(other_mesh, 90, 0, 0);
822 MeshTools::Modification::rotate(*mesh, 180, 0, 0);
824 {
825 // The other mesh is created by rotating the original mesh about 90 degrees.
826 MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
827 MeshTools::Modification::change_boundary_id(other_mesh, 2, 6);
828 MeshTools::Modification::change_boundary_id(other_mesh, 3, 7);
829 MeshTools::Modification::change_boundary_id(other_mesh, 4, 1);
830 MeshTools::Modification::change_boundary_id(other_mesh, 5, 2);
831 MeshTools::Modification::change_boundary_id(other_mesh, 6, 3);
832 MeshTools::Modification::change_boundary_id(other_mesh, 7, 4);
833 // The original mesh is then rotated about 180 degrees.
834 MeshTools::Modification::change_boundary_id(*mesh, 1, 5);
835 MeshTools::Modification::change_boundary_id(*mesh, 2, 6);
836 MeshTools::Modification::change_boundary_id(*mesh, 3, 7);
837 MeshTools::Modification::change_boundary_id(*mesh, 4, 2);
838 MeshTools::Modification::change_boundary_id(*mesh, 5, 3);
839 MeshTools::Modification::change_boundary_id(*mesh, 6, 4);
840 MeshTools::Modification::change_boundary_id(*mesh, 7, 1);
841 mesh->prepare_for_use();
842 other_mesh.prepare_for_use();
843 mesh->stitch_meshes(other_mesh, 4, 2, TOLERANCE, true, /*verbose=*/false);
844 mesh->get_boundary_info().sideset_name(1) = "left";
845 mesh->get_boundary_info().sideset_name(2) = "bottom";
846 mesh->get_boundary_info().sideset_name(3) = "right";
847 mesh->get_boundary_info().sideset_name(4) = "top";
848 }
849 else
850 {
851 MeshTools::Modification::change_boundary_id(*mesh, 1, 5);
852 MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
853 MeshTools::Modification::change_boundary_id(*mesh, 5, 2);
854 mesh->prepare_for_use();
855 other_mesh.prepare_for_use();
856 mesh->stitch_meshes(other_mesh, 1, 1, TOLERANCE, true, /*verbose=*/false);
857
858 MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
859 MeshTools::Modification::change_boundary_id(*mesh, 3, 2);
860 mesh->get_boundary_info().sideset_name(1) = "right";
861 mesh->get_boundary_info().sideset_name(2) = "outer";
862 }
863 other_mesh.clear();
864 }
865 else if (_portion == "bottom_half")
866 {
867 ReplicatedMesh other_mesh(*mesh);
868 // This is to rotate the mesh and also to reset boundary IDs.
869 MeshTools::Modification::rotate(other_mesh, 180, 0, 0);
870 MeshTools::Modification::rotate(*mesh, 270, 0, 0);
872 {
873 // The other mesh is created by rotating the original mesh about 180 degrees.
874 MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
875 MeshTools::Modification::change_boundary_id(other_mesh, 2, 6);
876 MeshTools::Modification::change_boundary_id(other_mesh, 3, 7);
877 MeshTools::Modification::change_boundary_id(other_mesh, 4, 2);
878 MeshTools::Modification::change_boundary_id(other_mesh, 5, 3);
879 MeshTools::Modification::change_boundary_id(other_mesh, 6, 4);
880 MeshTools::Modification::change_boundary_id(other_mesh, 7, 1);
881 // The original mesh is rotated about 270 degrees.
882 MeshTools::Modification::change_boundary_id(*mesh, 1, 5);
883 MeshTools::Modification::change_boundary_id(*mesh, 2, 6);
884 MeshTools::Modification::change_boundary_id(*mesh, 3, 7);
885 MeshTools::Modification::change_boundary_id(*mesh, 4, 3);
886 MeshTools::Modification::change_boundary_id(*mesh, 5, 4);
887 MeshTools::Modification::change_boundary_id(*mesh, 6, 1);
888 MeshTools::Modification::change_boundary_id(*mesh, 7, 2);
889 mesh->prepare_for_use();
890 other_mesh.prepare_for_use();
891 mesh->stitch_meshes(other_mesh, 1, 3, TOLERANCE, true, /*verbose=*/false);
892 mesh->get_boundary_info().sideset_name(1) = "left";
893 mesh->get_boundary_info().sideset_name(2) = "bottom";
894 mesh->get_boundary_info().sideset_name(3) = "right";
895 mesh->get_boundary_info().sideset_name(4) = "top";
896 }
897 else
898 {
899 MeshTools::Modification::change_boundary_id(*mesh, 1, 5);
900 MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
901 MeshTools::Modification::change_boundary_id(*mesh, 5, 2);
902 mesh->prepare_for_use();
903 other_mesh.prepare_for_use();
904 mesh->stitch_meshes(other_mesh, 1, 1, TOLERANCE, true, /*verbose=*/false);
905
906 MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
907 MeshTools::Modification::change_boundary_id(*mesh, 3, 2);
908 mesh->get_boundary_info().sideset_name(1) = "top";
909 mesh->get_boundary_info().sideset_name(2) = "outer";
910 }
911 other_mesh.clear();
912 }
913 else if (_portion == "full")
914 {
915 ReplicatedMesh portion_two(*mesh);
916
917 // This is to rotate the mesh and also to reset boundary IDs.
918 MeshTools::Modification::rotate(portion_two, 90, 0, 0);
919
921 {
922 // Portion 2: 2nd quadrant
923 MeshTools::Modification::change_boundary_id(portion_two, 1, 5);
924 MeshTools::Modification::change_boundary_id(portion_two, 2, 6);
925 MeshTools::Modification::change_boundary_id(portion_two, 3, 7);
926 MeshTools::Modification::change_boundary_id(portion_two, 4, 1);
927 MeshTools::Modification::change_boundary_id(portion_two, 5, 2);
928 MeshTools::Modification::change_boundary_id(portion_two, 6, 3);
929 MeshTools::Modification::change_boundary_id(portion_two, 7, 4);
930 mesh->prepare_for_use();
931 portion_two.prepare_for_use();
932 // 'top_half'
933 mesh->stitch_meshes(portion_two, 1, 3, TOLERANCE, true, /*verbose=*/false);
934
935 // 'bottom_half'
936 ReplicatedMesh portion_bottom(*mesh);
937 MeshTools::Modification::rotate(portion_bottom, 180, 0, 0);
938 MeshTools::Modification::change_boundary_id(portion_bottom, 1, 5);
939 MeshTools::Modification::change_boundary_id(portion_bottom, 2, 6);
940 MeshTools::Modification::change_boundary_id(portion_bottom, 3, 7);
941 MeshTools::Modification::change_boundary_id(portion_bottom, 4, 2);
942 MeshTools::Modification::change_boundary_id(portion_bottom, 5, 3);
943 MeshTools::Modification::change_boundary_id(portion_bottom, 6, 4);
944 MeshTools::Modification::change_boundary_id(portion_bottom, 7, 1);
945 mesh->prepare_for_use();
946 portion_bottom.prepare_for_use();
947 // 'full'
948 mesh->stitch_meshes(portion_bottom, 2, 4, TOLERANCE, true, /*verbose=*/false);
949
950 mesh->get_boundary_info().sideset_name(1) = "left";
951 mesh->get_boundary_info().sideset_name(2) = "bottom";
952 mesh->get_boundary_info().sideset_name(3) = "right";
953 mesh->get_boundary_info().sideset_name(4) = "top";
954 portion_bottom.clear();
955 }
956 else
957 {
958 MeshTools::Modification::change_boundary_id(portion_two, 1, 5);
959 MeshTools::Modification::change_boundary_id(portion_two, 2, 1);
960 MeshTools::Modification::change_boundary_id(portion_two, 5, 2);
961 // 'top half'
962 mesh->prepare_for_use();
963 portion_two.prepare_for_use();
964 mesh->stitch_meshes(portion_two, 1, 1, TOLERANCE, true, /*verbose=*/false);
965 // 'bottom half'
966 ReplicatedMesh portion_bottom(*mesh);
967 MeshTools::Modification::rotate(portion_bottom, 180, 0, 0);
968 // 'full'
969 mesh->prepare_for_use();
970 portion_bottom.prepare_for_use();
971 mesh->stitch_meshes(portion_bottom, 2, 2, TOLERANCE, true, /*verbose=*/false);
972 MeshTools::Modification::change_boundary_id(*mesh, 3, 1);
973 mesh->get_boundary_info().sideset_name(1) = "outer";
974 portion_bottom.clear();
975 }
976 portion_two.clear();
977 }
978
979 if (_portion != "top_half" && _portion != "right_half" && _portion != "left_half" &&
980 _portion != "bottom_half" && _portion != "full")
981 mesh->prepare_for_use();
982
983 // Laplace smoothing
985 lms.smooth();
986
987 // Add subdomain name
988 if (isParamValid("subdomain_name"))
989 mesh->set_subdomain_name(0, getParam<SubdomainName>("subdomain_name"), true);
990
991 mesh->prepare_for_use();
992 return dynamic_pointer_cast<MeshBase>(mesh);
993}
registerMooseObject("MooseApp", ConcentricCircleMeshGenerator)
void ErrorVector unsigned int
Generates a mesh based on concentric circles, given all the parameters.
MooseEnum _portion
Control of which portion of mesh will be developed.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
ConcentricCircleMeshGenerator(const InputParameters &parameters)
std::vector< unsigned int > _rings
Number of rings in each circle or in the enclosing square.
bool _has_outer_square
Adding the enclosing square is optional.
std::vector< Real > _radii
Radii of concentric circles.
bool _preserve_volumes
Volume preserving function is optional.
unsigned int _num_sectors
Number of sectors in one quadrant.
unsigned int _smoothing_max_it
Iteration number for Laplace smoothing.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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.
std::unique_ptr< ReplicatedMesh > buildReplicatedMesh(unsigned int dim=libMesh::invalid_uint)
Build a replicated mesh.
static InputParameters validParams()
T & declareMeshProperty(const std::string &data_name, Args &&... args)
Methods for writing out attributes to the mesh meta-data store, which can be retrieved from most othe...
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
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
virtual void smooth() override
MeshBase & mesh