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  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, nodes[index + (_num_sectors / 2 + 1) + counter * (_num_sectors / 2 + 2) + 1]);
366  elem->set_node(3, nodes[index - _num_sectors / 2 - 1]);
367  elem->subdomain_id() = subdomainIDs[0];
368 
369  if (index == standard + 1)
370  boundary_info.add_side(elem, 2, 1);
371 
372  index = index - _num_sectors / 2 - 1;
373  ++counter;
374  }
375 
376  counter = 0;
377  // adding elements for other concentric circles
378  index = Utility::pow<2>(standard / 2 + 1);
379  limit = Utility::pow<2>(standard / 2 + 1) +
380  (_num_sectors + 1) * (total_concentric_circles.size() - 1);
381 
382  int num_nodes_boundary = Utility::pow<2>(standard / 2 + 1) + standard + 1;
383 
384  while (index < limit)
385  {
386  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
387  elem->set_node(0, nodes[index]);
388  elem->set_node(1, nodes[index + standard + 1]);
389  elem->set_node(2, nodes[index + standard + 2]);
390  elem->set_node(3, nodes[index + 1]);
391 
392  for (int i = 0; i < static_cast<int>(subdomainIDs.size() - 1); ++i)
393  if (index < limit - (standard + 1) * i && index >= limit - (standard + 1) * (i + 1))
394  elem->subdomain_id() = subdomainIDs[subdomainIDs.size() - 1 - i];
395 
396  const int initial = Utility::pow<2>(standard / 2 + 1);
397  const int final = Utility::pow<2>(standard / 2 + 1) + standard - 1;
398 
399  if ((index - initial) % (standard + 1) == 0)
400  boundary_info.add_side(elem, 0, 2);
401  if ((index - final) % (standard + 1) == 0)
402  boundary_info.add_side(elem, 2, 1);
403  if (!_has_outer_square)
404  if (index >= limit - (standard + 1))
405  boundary_info.add_side(elem, 1, 3);
406 
407  // index increment is for adding nodes for a next element.
408  ++index;
409 
410  // increment by 2 indices may be necessary depending on where the index points to.
411  // this varies based on the algorithms provided for the specific element and node placement.
412  if (index == (num_nodes_boundary + counter * (standard + 1)) - 1)
413  {
414  ++index;
415  ++counter;
416  }
417  }
418 
419  // Enclosing square sections
420  // ABCA
421  // C B
422  // B C
423  // ACBA
424 
425  // adding elements for the enclosing square. (top left)
426  int initial =
427  Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size();
428 
429  int initial2 =
430  Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size();
431 
432  if (_has_outer_square)
433  {
434  if (_rings.back() != 0) // this must be condition up front.
435  {
436  index = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size();
437  limit = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
438  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 3 -
439  _rings.back() * (_rings.back() + 2) - (_rings.back() + 1);
440  while (index <= limit)
441  {
442  // outer square sector C
443  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
444  elem->set_node(0, nodes[index]);
445  elem->set_node(1, nodes[index + 1]);
446  elem->set_node(2, nodes[index + 1 + _rings.back() + 1]);
447  elem->set_node(3, nodes[index + 1 + _rings.back()]);
448  elem->subdomain_id() = subdomainIDs.back() + 1;
449 
450  if (index < (initial2 + static_cast<int>(_rings.back())))
451  boundary_info.add_side(elem, 0, 1);
452 
453  if (index == initial)
454  boundary_info.add_side(elem, 3, 4);
455 
456  ++index;
457 
458  // As mentioned before, increment by 2 indices may be required depending on where the index
459  // points to.
460  if ((index - initial) % static_cast<int>(_rings.back()) == 0)
461  {
462  ++index;
463  initial = initial + (static_cast<int>(_rings.back()) + 1);
464  }
465  }
466 
467  // adding elements for the enclosing square. (top right)
468  initial = Utility::pow<2>(standard / 2 + 1) +
469  (standard + 1) * total_concentric_circles.size() +
470  (_rings.back() + 1) * (standard / 2 + 1);
471 
472  limit = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
473  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 3 -
474  (_rings.back() + 2);
475 
476  while (index <= limit)
477  {
478  // outer square sector A
479  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
480  elem->set_node(3, nodes[index]);
481  elem->set_node(2, nodes[index + _rings.back() + 2]);
482  elem->set_node(1, nodes[index + _rings.back() + 3]);
483  elem->set_node(0, nodes[index + 1]);
484  elem->subdomain_id() = subdomainIDs.back() + 1;
485 
486  if (index >= static_cast<int>(limit - (_rings.back() + 1)))
487  boundary_info.add_side(elem, 1, 3);
488 
489  if ((index - initial) % static_cast<int>(_rings.back() + 2) == 0)
490  boundary_info.add_side(elem, 2, 4);
491 
492  ++index;
493 
494  if ((index - initial) % static_cast<int>(_rings.back() + 1) == 0)
495  {
496  ++index;
497  initial = initial + (static_cast<int>(_rings.back()) + 2);
498  }
499  }
500 
501  // adding elements for the enclosing square. (one center quad)
502  int index1 = Utility::pow<2>(standard / 2 + 1) +
503  (standard + 1) * (total_concentric_circles.size() - 1) + standard / 2;
504 
505  int index2 = Utility::pow<2>(standard / 2 + 1) +
506  (standard + 1) * total_concentric_circles.size() +
507  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 3 -
508  _rings.back() * (_rings.back() + 2) - (_rings.back() + 1);
509 
510  // pointy tips of the A sectors, touching the inner circle
511  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
512  elem->set_node(3, nodes[index1]);
513  elem->set_node(2, nodes[index2]);
514  elem->set_node(1, nodes[index2 + _rings.back() + 1]);
515  elem->set_node(0, nodes[index2 + _rings.back() + 2]);
516  elem->subdomain_id() = subdomainIDs.back() + 1;
517 
518  // adding elements for the left mid part.
519  index = Utility::pow<2>(standard / 2 + 1) + standard / 2 +
520  (standard + 1) * (total_concentric_circles.size() - 1);
521  limit = index + standard / 2 - 1;
522 
523  while (index <= limit)
524  {
525  // outer square elements in sector C touching the inner circle
526  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
527  elem->set_node(3, nodes[index]);
528  elem->set_node(2, nodes[index + 1]);
529  elem->set_node(1, nodes[index2 - _rings.back() - 1]);
530  elem->set_node(0, nodes[index2]);
531  elem->subdomain_id() = subdomainIDs.back() + 1;
532 
533  if (index == limit)
534  boundary_info.add_side(elem, 1, 1);
535 
536  ++index;
537 
538  // two different indices are used to add nodes for an element.
539  index2 = index2 - _rings.back() - 1;
540  }
541 
542  // adding elements for the right mid part.
543  index1 = Utility::pow<2>(standard / 2 + 1) + standard / 2 +
544  (standard + 1) * (total_concentric_circles.size() - 1);
545  index2 = Utility::pow<2>(standard / 2 + 1) +
546  (standard + 1) * total_concentric_circles.size() +
547  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 2 +
548  (_rings.back() + 1);
549  int index3 =
550  Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
551  (_rings.back() + 1) * (standard / 2) - 1 + (_rings.back() + 1) + (_rings.back() + 2);
552 
553  // elements clockwise from the A sector tips
554  elem = mesh->add_elem(std::make_unique<Quad4>());
555  elem->set_node(0, nodes[index1]);
556  elem->set_node(1, nodes[index1 - 1]);
557  elem->set_node(2, nodes[index2]);
558  elem->set_node(3, nodes[index3]);
559  elem->subdomain_id() = subdomainIDs.back() + 1;
560 
561  if (standard == 2)
562  boundary_info.add_side(elem, 1, 2);
563 
564  // adding elements for the right mid bottom part.
565 
566  index = Utility::pow<2>(standard / 2 + 1) + standard / 2 +
567  (standard + 1) * (total_concentric_circles.size() - 1) - 2;
568  index1 = Utility::pow<2>(standard / 2 + 1) +
569  (standard + 1) * total_concentric_circles.size() +
570  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 2 +
571  (_rings.back() + 1) * 2;
572 
573  limit = Utility::pow<2>(standard / 2 + 1) + standard / 2 +
574  (standard + 1) * (total_concentric_circles.size() - 1) - standard / 2;
575 
576  if (standard != 2)
577  {
578  while (index >= limit)
579  {
580  // outer square elements in sector B touching the inner circle
581  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
582  elem->set_node(0, nodes[index]);
583  elem->set_node(1, nodes[index1]);
584  elem->set_node(2, nodes[index1 - (_rings.back() + 1)]);
585  elem->set_node(3, nodes[index + 1]);
586  elem->subdomain_id() = subdomainIDs.back() + 1;
587 
588  if (index == limit)
589  boundary_info.add_side(elem, 0, 2);
590  --index;
591  index1 = index1 + (_rings.back() + 1);
592  }
593  }
594 
595  // adding elements for the right low part.
596  index = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
597  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 2;
598 
599  index1 = index - (_rings.back() + 2);
600  // dummy condition for elem definition
601  if (standard >= 2)
602  {
603  // single elements between A and B on the outside of the square
604  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
605  elem->set_node(3, nodes[index]);
606  elem->set_node(2, nodes[index + 1]);
607  elem->set_node(1, nodes[index + 2]);
608  elem->set_node(0, nodes[index1]);
609  elem->subdomain_id() = subdomainIDs.back() + 1;
610 
611  boundary_info.add_side(elem, 2, 3);
612 
613  if (standard == 2)
614  boundary_info.add_side(elem, 1, 2);
615  }
616 
617  index = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
618  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 2 -
619  (_rings.back() + 2);
620 
621  limit = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
622  (_rings.back() + 1) * (standard / 2) + (_rings.back() + 2) * 2 - 2;
623 
624  int k = 1;
625  while (index > limit)
626  {
627  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
628  elem->set_node(3, nodes[index]);
629  elem->set_node(2, nodes[index + (_rings.back() + 2) * k + k + 1]);
630  elem->set_node(1, nodes[index + (_rings.back() + 2) * k + k + 2]);
631  elem->set_node(0, nodes[index - _rings.back() - 2]);
632  elem->subdomain_id() = subdomainIDs.back() + 1;
633  index = index - (_rings.back() + 2);
634  ++k;
635 
636  if (standard == 2)
637  boundary_info.add_side(elem, 1, 2);
638  }
639 
640  index = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
641  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 1;
642  initial = Utility::pow<2>(standard / 2 + 1) +
643  (standard + 1) * total_concentric_circles.size() +
644  (_rings.back() + 1) * (standard / 2) + Utility::pow<2>(_rings.back() + 2) - 1;
645  limit = Utility::pow<2>(standard / 2 + 1) + (standard + 1) * total_concentric_circles.size() +
646  (_rings.back() + 1) * (standard / 2) * 2 + Utility::pow<2>(_rings.back() + 2) - 2 -
647  _rings.back() - 1;
648 
649  initial2 = Utility::pow<2>(standard / 2 + 1) +
650  (standard + 1) * total_concentric_circles.size() +
651  (_rings.back() + 1) * (standard / 2) * 2 + Utility::pow<2>(_rings.back() + 2) - 2 -
652  (_rings.back() + 1) * 2;
653 
654  if (standard > 2)
655  {
656  while (index < limit)
657  {
658  Elem * elem = mesh->add_elem(std::make_unique<Quad4>());
659  elem->set_node(0, nodes[index]);
660  elem->set_node(1, nodes[index + 1]);
661  elem->set_node(2, nodes[index + 1 + _rings.back() + 1]);
662  elem->set_node(3, nodes[index + 1 + _rings.back()]);
663  elem->subdomain_id() = subdomainIDs.back() + 1;
664 
665  if (index > initial2)
666  boundary_info.add_side(elem, 2, 2);
667 
668  if ((index - initial) == 0)
669  boundary_info.add_side(elem, 3, 3);
670 
671  ++index;
672 
673  if ((index - initial) % static_cast<int>(_rings.back()) == 0)
674  {
675  ++index;
676  initial = initial + (static_cast<int>(_rings.back()) + 1);
677  }
678  }
679  }
680  }
681  }
682 
683  // This is to set boundary names.
684  boundary_info.sideset_name(1) = "left";
685  boundary_info.sideset_name(2) = "bottom";
686 
687  if (!_has_outer_square)
688  boundary_info.sideset_name(3) = "outer";
689  else
690  {
691  boundary_info.sideset_name(3) = "right";
692  boundary_info.sideset_name(4) = "top";
693  }
694 
695  if (_portion == "top_left")
696  {
697  MeshTools::Modification::rotate(*mesh, 90, 0, 0);
698  boundary_info.sideset_name(1) = "bottom";
699  boundary_info.sideset_name(2) = "right";
700 
701  if (!_has_outer_square)
702  boundary_info.sideset_name(3) = "outer";
703  else
704  {
705  boundary_info.sideset_name(3) = "top";
706  boundary_info.sideset_name(4) = "left";
707  }
708  }
709  else if (_portion == "bottom_left")
710  {
711  MeshTools::Modification::rotate(*mesh, 180, 0, 0);
712  boundary_info.sideset_name(1) = "right";
713  boundary_info.sideset_name(2) = "top";
714 
715  if (!_has_outer_square)
716  boundary_info.sideset_name(3) = "outer";
717  else
718  {
719  boundary_info.sideset_name(3) = "left";
720  boundary_info.sideset_name(4) = "bottom";
721  }
722  }
723  else if (_portion == "bottom_right")
724  {
725  MeshTools::Modification::rotate(*mesh, 270, 0, 0);
726  boundary_info.sideset_name(1) = "top";
727  boundary_info.sideset_name(2) = "left";
728 
729  if (!_has_outer_square)
730  boundary_info.sideset_name(3) = "outer";
731  else
732  {
733  boundary_info.sideset_name(3) = "bottom";
734  boundary_info.sideset_name(4) = "right";
735  }
736  }
737 
738  else if (_portion == "top_half")
739  {
740  ReplicatedMesh other_mesh(*mesh);
741  // This is to rotate the mesh and also to reset boundary IDs.
742  MeshTools::Modification::rotate(other_mesh, 90, 0, 0);
743  if (_has_outer_square)
744  {
745  MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
746  MeshTools::Modification::change_boundary_id(other_mesh, 2, 6);
747  MeshTools::Modification::change_boundary_id(other_mesh, 3, 7);
748  MeshTools::Modification::change_boundary_id(other_mesh, 4, 1);
749  MeshTools::Modification::change_boundary_id(other_mesh, 5, 2);
750  MeshTools::Modification::change_boundary_id(other_mesh, 6, 3);
751  MeshTools::Modification::change_boundary_id(other_mesh, 7, 4);
752  mesh->prepare_for_use();
753  other_mesh.prepare_for_use();
754  mesh->stitch_meshes(other_mesh, 1, 3, TOLERANCE, true, /*verbose=*/false);
755  mesh->get_boundary_info().sideset_name(1) = "left";
756  mesh->get_boundary_info().sideset_name(2) = "bottom";
757  mesh->get_boundary_info().sideset_name(3) = "right";
758  mesh->get_boundary_info().sideset_name(4) = "top";
759  }
760  else
761  {
762  MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
763  MeshTools::Modification::change_boundary_id(other_mesh, 2, 1);
764  MeshTools::Modification::change_boundary_id(other_mesh, 5, 2);
765  mesh->prepare_for_use();
766  other_mesh.prepare_for_use();
767  mesh->stitch_meshes(other_mesh, 1, 1, TOLERANCE, true, /*verbose=*/false);
768 
769  MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
770  MeshTools::Modification::change_boundary_id(*mesh, 3, 2);
771  mesh->get_boundary_info().sideset_name(1) = "bottom";
772  mesh->get_boundary_info().sideset_name(2) = "outer";
773  }
774  other_mesh.clear();
775  }
776 
777  else if (_portion == "right_half")
778  {
779  ReplicatedMesh other_mesh(*mesh);
780  // This is to rotate the mesh and also to reset boundary IDs.
781  MeshTools::Modification::rotate(other_mesh, 270, 0, 0);
782  if (_has_outer_square)
783  {
784  MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
785  MeshTools::Modification::change_boundary_id(other_mesh, 2, 6);
786  MeshTools::Modification::change_boundary_id(other_mesh, 3, 7);
787  MeshTools::Modification::change_boundary_id(other_mesh, 4, 3);
788  MeshTools::Modification::change_boundary_id(other_mesh, 5, 4);
789  MeshTools::Modification::change_boundary_id(other_mesh, 6, 1);
790  MeshTools::Modification::change_boundary_id(other_mesh, 7, 2);
791  mesh->prepare_for_use();
792  other_mesh.prepare_for_use();
793  mesh->stitch_meshes(other_mesh, 2, 4, TOLERANCE, true, /*verbose=*/false);
794  mesh->get_boundary_info().sideset_name(1) = "left";
795  mesh->get_boundary_info().sideset_name(2) = "bottom";
796  mesh->get_boundary_info().sideset_name(3) = "right";
797  mesh->get_boundary_info().sideset_name(4) = "top";
798  }
799  else
800  {
801  MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
802  MeshTools::Modification::change_boundary_id(other_mesh, 2, 1);
803  MeshTools::Modification::change_boundary_id(other_mesh, 5, 2);
804  mesh->prepare_for_use();
805  other_mesh.prepare_for_use();
806  mesh->stitch_meshes(other_mesh, 2, 2, TOLERANCE, true, /*verbose=*/false);
807 
808  MeshTools::Modification::change_boundary_id(*mesh, 3, 2);
809  mesh->get_boundary_info().sideset_name(1) = "left";
810  mesh->get_boundary_info().sideset_name(2) = "outer";
811  }
812  other_mesh.clear();
813  }
814  else if (_portion == "left_half")
815  {
816  ReplicatedMesh other_mesh(*mesh);
817 
818  // This is to rotate the mesh and to reset boundary IDs.
819  MeshTools::Modification::rotate(other_mesh, 90, 0, 0);
820  MeshTools::Modification::rotate(*mesh, 180, 0, 0);
821  if (_has_outer_square)
822  {
823  // The other mesh is created by rotating the original mesh about 90 degrees.
824  MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
825  MeshTools::Modification::change_boundary_id(other_mesh, 2, 6);
826  MeshTools::Modification::change_boundary_id(other_mesh, 3, 7);
827  MeshTools::Modification::change_boundary_id(other_mesh, 4, 1);
828  MeshTools::Modification::change_boundary_id(other_mesh, 5, 2);
829  MeshTools::Modification::change_boundary_id(other_mesh, 6, 3);
830  MeshTools::Modification::change_boundary_id(other_mesh, 7, 4);
831  // The original mesh is then rotated about 180 degrees.
832  MeshTools::Modification::change_boundary_id(*mesh, 1, 5);
833  MeshTools::Modification::change_boundary_id(*mesh, 2, 6);
834  MeshTools::Modification::change_boundary_id(*mesh, 3, 7);
835  MeshTools::Modification::change_boundary_id(*mesh, 4, 2);
836  MeshTools::Modification::change_boundary_id(*mesh, 5, 3);
837  MeshTools::Modification::change_boundary_id(*mesh, 6, 4);
838  MeshTools::Modification::change_boundary_id(*mesh, 7, 1);
839  mesh->prepare_for_use();
840  other_mesh.prepare_for_use();
841  mesh->stitch_meshes(other_mesh, 4, 2, TOLERANCE, true, /*verbose=*/false);
842  mesh->get_boundary_info().sideset_name(1) = "left";
843  mesh->get_boundary_info().sideset_name(2) = "bottom";
844  mesh->get_boundary_info().sideset_name(3) = "right";
845  mesh->get_boundary_info().sideset_name(4) = "top";
846  }
847  else
848  {
849  MeshTools::Modification::change_boundary_id(*mesh, 1, 5);
850  MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
851  MeshTools::Modification::change_boundary_id(*mesh, 5, 2);
852  mesh->prepare_for_use();
853  other_mesh.prepare_for_use();
854  mesh->stitch_meshes(other_mesh, 1, 1, TOLERANCE, true, /*verbose=*/false);
855 
856  MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
857  MeshTools::Modification::change_boundary_id(*mesh, 3, 2);
858  mesh->get_boundary_info().sideset_name(1) = "right";
859  mesh->get_boundary_info().sideset_name(2) = "outer";
860  }
861  other_mesh.clear();
862  }
863  else if (_portion == "bottom_half")
864  {
865  ReplicatedMesh other_mesh(*mesh);
866  // This is to rotate the mesh and also to reset boundary IDs.
867  MeshTools::Modification::rotate(other_mesh, 180, 0, 0);
868  MeshTools::Modification::rotate(*mesh, 270, 0, 0);
869  if (_has_outer_square)
870  {
871  // The other mesh is created by rotating the original mesh about 180 degrees.
872  MeshTools::Modification::change_boundary_id(other_mesh, 1, 5);
873  MeshTools::Modification::change_boundary_id(other_mesh, 2, 6);
874  MeshTools::Modification::change_boundary_id(other_mesh, 3, 7);
875  MeshTools::Modification::change_boundary_id(other_mesh, 4, 2);
876  MeshTools::Modification::change_boundary_id(other_mesh, 5, 3);
877  MeshTools::Modification::change_boundary_id(other_mesh, 6, 4);
878  MeshTools::Modification::change_boundary_id(other_mesh, 7, 1);
879  // The original mesh is rotated about 270 degrees.
880  MeshTools::Modification::change_boundary_id(*mesh, 1, 5);
881  MeshTools::Modification::change_boundary_id(*mesh, 2, 6);
882  MeshTools::Modification::change_boundary_id(*mesh, 3, 7);
883  MeshTools::Modification::change_boundary_id(*mesh, 4, 3);
884  MeshTools::Modification::change_boundary_id(*mesh, 5, 4);
885  MeshTools::Modification::change_boundary_id(*mesh, 6, 1);
886  MeshTools::Modification::change_boundary_id(*mesh, 7, 2);
887  mesh->prepare_for_use();
888  other_mesh.prepare_for_use();
889  mesh->stitch_meshes(other_mesh, 1, 3, TOLERANCE, true, /*verbose=*/false);
890  mesh->get_boundary_info().sideset_name(1) = "left";
891  mesh->get_boundary_info().sideset_name(2) = "bottom";
892  mesh->get_boundary_info().sideset_name(3) = "right";
893  mesh->get_boundary_info().sideset_name(4) = "top";
894  }
895  else
896  {
897  MeshTools::Modification::change_boundary_id(*mesh, 1, 5);
898  MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
899  MeshTools::Modification::change_boundary_id(*mesh, 5, 2);
900  mesh->prepare_for_use();
901  other_mesh.prepare_for_use();
902  mesh->stitch_meshes(other_mesh, 1, 1, TOLERANCE, true, /*verbose=*/false);
903 
904  MeshTools::Modification::change_boundary_id(*mesh, 2, 1);
905  MeshTools::Modification::change_boundary_id(*mesh, 3, 2);
906  mesh->get_boundary_info().sideset_name(1) = "top";
907  mesh->get_boundary_info().sideset_name(2) = "outer";
908  }
909  other_mesh.clear();
910  }
911  else if (_portion == "full")
912  {
913  ReplicatedMesh portion_two(*mesh);
914 
915  // This is to rotate the mesh and also to reset boundary IDs.
916  MeshTools::Modification::rotate(portion_two, 90, 0, 0);
917 
918  if (_has_outer_square)
919  {
920  // Portion 2: 2nd quadrant
921  MeshTools::Modification::change_boundary_id(portion_two, 1, 5);
922  MeshTools::Modification::change_boundary_id(portion_two, 2, 6);
923  MeshTools::Modification::change_boundary_id(portion_two, 3, 7);
924  MeshTools::Modification::change_boundary_id(portion_two, 4, 1);
925  MeshTools::Modification::change_boundary_id(portion_two, 5, 2);
926  MeshTools::Modification::change_boundary_id(portion_two, 6, 3);
927  MeshTools::Modification::change_boundary_id(portion_two, 7, 4);
928  mesh->prepare_for_use();
929  portion_two.prepare_for_use();
930  // 'top_half'
931  mesh->stitch_meshes(portion_two, 1, 3, TOLERANCE, true, /*verbose=*/false);
932 
933  // 'bottom_half'
934  ReplicatedMesh portion_bottom(*mesh);
935  MeshTools::Modification::rotate(portion_bottom, 180, 0, 0);
936  MeshTools::Modification::change_boundary_id(portion_bottom, 1, 5);
937  MeshTools::Modification::change_boundary_id(portion_bottom, 2, 6);
938  MeshTools::Modification::change_boundary_id(portion_bottom, 3, 7);
939  MeshTools::Modification::change_boundary_id(portion_bottom, 4, 2);
940  MeshTools::Modification::change_boundary_id(portion_bottom, 5, 3);
941  MeshTools::Modification::change_boundary_id(portion_bottom, 6, 4);
942  MeshTools::Modification::change_boundary_id(portion_bottom, 7, 1);
943  mesh->prepare_for_use();
944  portion_bottom.prepare_for_use();
945  // 'full'
946  mesh->stitch_meshes(portion_bottom, 2, 4, TOLERANCE, true, /*verbose=*/false);
947 
948  mesh->get_boundary_info().sideset_name(1) = "left";
949  mesh->get_boundary_info().sideset_name(2) = "bottom";
950  mesh->get_boundary_info().sideset_name(3) = "right";
951  mesh->get_boundary_info().sideset_name(4) = "top";
952  portion_bottom.clear();
953  }
954  else
955  {
956  MeshTools::Modification::change_boundary_id(portion_two, 1, 5);
957  MeshTools::Modification::change_boundary_id(portion_two, 2, 1);
958  MeshTools::Modification::change_boundary_id(portion_two, 5, 2);
959  // 'top half'
960  mesh->prepare_for_use();
961  portion_two.prepare_for_use();
962  mesh->stitch_meshes(portion_two, 1, 1, TOLERANCE, true, /*verbose=*/false);
963  // 'bottom half'
964  ReplicatedMesh portion_bottom(*mesh);
965  MeshTools::Modification::rotate(portion_bottom, 180, 0, 0);
966  // 'full'
967  mesh->prepare_for_use();
968  portion_bottom.prepare_for_use();
969  mesh->stitch_meshes(portion_bottom, 2, 2, TOLERANCE, true, /*verbose=*/false);
970  MeshTools::Modification::change_boundary_id(*mesh, 3, 1);
971  mesh->get_boundary_info().sideset_name(1) = "outer";
972  portion_bottom.clear();
973  }
974  portion_two.clear();
975  }
976 
977  if (_portion != "top_half" && _portion != "right_half" && _portion != "left_half" &&
978  _portion != "bottom_half" && _portion != "full")
979  mesh->prepare_for_use();
980 
981  // Laplace smoothing
984 
985  mesh->prepare_for_use();
986  return dynamic_pointer_cast<MeshBase>(mesh);
987 }
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)
virtual void smooth() override
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.
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:33
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
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)
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