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