https://mooseframework.inl.gov
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");
54  params.addClassDescription(
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.
100  if (_has_outer_square)
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 
106 std::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 
139  if (_preserve_volumes)
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 
168  if (_has_outer_square)
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 
217  if (_has_outer_square)
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 
307  if (_has_outer_square)
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);
405  if (!_has_outer_square)
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 
434  if (_has_outer_square)
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 
689  if (!_has_outer_square)
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 
703  if (!_has_outer_square)
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 
717  if (!_has_outer_square)
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 
731  if (!_has_outer_square)
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);
745  if (_has_outer_square)
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);
784  if (_has_outer_square)
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);
823  if (_has_outer_square)
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);
871  if (_has_outer_square)
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 
920  if (_has_outer_square)
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->subdomain_name(0) = getParam<SubdomainName>("subdomain_name");
990 
991  mesh->prepare_for_use();
992  return dynamic_pointer_cast<MeshBase>(mesh);
993 }
unsigned int _smoothing_max_it
Iteration number for Laplace smoothing.
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sin(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tan
registerMooseObject("MooseApp", ConcentricCircleMeshGenerator)
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
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
bool _preserve_volumes
Volume preserving function is optional.
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.
virtual void smooth() override
std::vector< Real > _radii
Radii of concentric circles.
Generates a mesh based on concentric circles, given all the parameters.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
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...
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.
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template cos(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(cos
MooseEnum _portion
Control of which portion of mesh will be developed.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:54
static InputParameters validParams()
Definition: MeshGenerator.C:23
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
ConcentricCircleMeshGenerator(const InputParameters &parameters)
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...
unsigned int _num_sectors
Number of sectors in one quadrant.
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 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
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:33
void ErrorVector unsigned int
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template pow< 2 >(tan(_arg))+1.0) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(sqrt