https://mooseframework.inl.gov
AdvancedConcentricCircleGenerator.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 
12 
13 // C++ includes
14 #include <cmath>
15 
17 
20 {
22 
23  params.makeParamRequired<std::vector<Real>>("ring_radii");
24  params.makeParamRequired<std::vector<unsigned int>>("ring_intervals");
25 
26  params.addRangeCheckedParam<unsigned int>(
27  "num_sectors",
28  "num_sectors>2",
29  "Number of azimuthal sectors of the circular mesh to be generated.");
30  params.addRangeCheckedParam<std::vector<Real>>(
31  "customized_azimuthal_angles",
32  "customized_azimuthal_angles>=0&customized_azimuthal_angles<360",
33  "List of the user-specified azimuthal angles of the nodes.");
34 
35  params.addParamNamesToGroup("num_sectors customized_azimuthal_angles", "Azimuthal Mesh Density");
36 
37  params.addClassDescription("This AdvancedConcentricCircleGenerator object is designed to mesh a "
38  "concentric circular geometry.");
39 
40  return params;
41 }
42 
44  const InputParameters & parameters)
45  : ConcentricCircleGeneratorBase(parameters),
46  _azimuthal_angles(isParamValid("customized_azimuthal_angles")
47  ? getParam<std::vector<Real>>("customized_azimuthal_angles")
48  : std::vector<Real>()),
49  _num_sectors(isParamValid("num_sectors") ? getParam<unsigned int>("num_sectors")
50  : _azimuthal_angles.size())
51 {
52  const unsigned short tri_order = _tri_elem_type == TRI_ELEM_TYPE::TRI3 ? 1 : 2;
53  const unsigned short quad_order = _quad_elem_type == QUAD_ELEM_TYPE::QUAD4 ? 1 : 2;
54  // 1. If the generated mesh has only one ring layer of triangular elements, then no
55  // quad elements are generated;
56  // 2. Otherwise, both types of elements are generated.
57  _order = tri_order;
58  if (_ring_radii.size() == 1 && _ring_intervals.front() == 1 &&
61  {
62  if (tri_order != quad_order)
64  }
65  else if (tri_order != quad_order)
66  paramError("tri_element_type",
67  "the element types of triangular and quadrilateral elements must be compatible if "
68  "both types of elements are generated.");
69 
70  if (_num_sectors == 0)
71  paramError(
72  "num_sectors",
73  "this parameter must be specified if 'customized_azimuthal_angles' is not provided.");
74  if (_azimuthal_angles.empty())
75  {
76  for (unsigned int i = 0; i < _num_sectors; i++)
77  {
78  _azimuthal_angles.push_back((Real)i * 360.0 / (Real)_num_sectors);
80  }
81  }
82  else
83  {
84  if (_num_sectors != _azimuthal_angles.size())
85  paramError("num_sectors",
86  "this parameter must be equal to the size of 'customized_azimuthal_angles' if "
87  "both parameters are provided.");
88  for (unsigned int i = 0; i < _azimuthal_angles.size(); i++)
89  {
90  const Real azi_angle_interval = i == _azimuthal_angles.size() - 1
91  ? 360.0 + _azimuthal_angles[0] - _azimuthal_angles[i]
93  if (azi_angle_interval <= 0.0)
94  paramError("customized_azimuthal_angles",
95  "the azimuthal angles provided must be strictly increasing.");
96  else if (azi_angle_interval >= 120.0)
97  paramError("customized_azimuthal_angles",
98  "please make sure the circle azimuthal discretization angles are less than "
99  "120.0 to avert awkward polygonization.");
100  _virtual_nums_sectors.push_back(360.0 / azi_angle_interval);
101  }
102  }
103  // Customized interface boundary id/name related error messages
104  if (_inward_interface_boundary_names.size() > 0 &&
105  _inward_interface_boundary_names.size() != _ring_radii.size() - 1)
106  paramError("inward_interface_boundary_names",
107  "If provided, the length of this parameter must be identical to the total number of "
108  "interfaces.");
109  if (_outward_interface_boundary_names.size() > 0 &&
110  _outward_interface_boundary_names.size() != _ring_radii.size() - 1)
111  paramError("outward_interface_boundary_names",
112  "If provided, the length of this parameter must be identical to the total number of "
113  "interfaces.");
114 
115  const unsigned int num_innermost_ring_layers =
118  if (!_ring_block_ids.empty() &&
119  _ring_block_ids.size() !=
120  (_ring_intervals.size() + (unsigned int)(num_innermost_ring_layers != 1)))
121  paramError("ring_block_ids",
122  "This parameter must have the appropriate size if it is provided. The size should "
123  "be the same as the size of 'ring_intervals' if the innermost ring interval "
124  "(including boundary layers) is unity; otherwise the size should be greater than "
125  "the size of 'ring_intervals' by one. If 'quad_center_elements' is true, it is "
126  "optional to only provide this parameter with the same size as 'ring_intervals'");
127  if (!_ring_block_names.empty() &&
128  _ring_block_names.size() !=
129  (_ring_intervals.size() + (unsigned int)(num_innermost_ring_layers != 1)))
130  paramError("ring_block_names",
131  "This parameter must have the appropriate size if it is set. The size should be the "
132  "same as the size of 'ring_intervals' if the innermost ring interval (including "
133  "boundary layers) is unity; otherwise the size should be greater than the size of "
134  "'ring_intervals' by one. If 'quad_center_elements' is true, it is optional to only "
135  "provide this parameter with the same size as 'ring_intervals'");
136  for (unsigned int i = 0; i < _ring_radii.size(); i++)
137  {
138  const Real layer_width = _ring_radii[i] - (i == 0 ? 0.0 : _ring_radii[i - 1]);
140  _ring_inner_boundary_layer_params.widths[i] / layer_width);
142  _ring_outer_boundary_layer_params.widths[i] / layer_width);
143  }
144  for (unsigned int i = 0; i < _ring_inner_boundary_layer_params.fractions.size(); i++)
147  paramError("ring_inner_boundary_layer_intervals",
148  "Ring inner boundary layer must have zero interval if its thickness is zero.");
150  0.0) &&
152  paramError(
153  "ring_inner_boundary_layer_intervals",
154  "Ring inner boundary layer must have non-zero interval if its thickness is not zero.");
155  for (unsigned int i = 0; i < _ring_outer_boundary_layer_params.fractions.size(); i++)
156  {
159  paramError("ring_outer_boundary_layer_intervals",
160  "Ring outer boundary layer must have zero interval if its thickness is zero.");
162  0.0) &&
164  paramError(
165  "ring_outer_boundary_layer_intervals",
166  "Ring outer boundary layer must have non-zero interval if its thickness is not zero.");
169  1.0)
170  paramError("ring_inner_boundary_layer_widths",
171  "Summation of ring_inner_boundary_layer_widths and "
172  "ring_outer_boundary_layer_widths cannot exceeds the ring layer width.");
173  }
174 
175  for (unsigned int i = 0; i < _ring_radii.size(); i++)
176  {
177  const Real layer_width = _ring_radii[i] - (i == 0 ? 0.0 : _ring_radii[i - 1]);
179  _ring_inner_boundary_layer_params.widths[i] / layer_width);
181  _ring_outer_boundary_layer_params.widths[i] / layer_width);
182  }
183 }
184 
185 std::unique_ptr<MeshBase>
187 {
188  std::vector<Real> ring_radii_corr;
189  std::vector<Real> mod_azimuthal_angles;
190 
191  for (unsigned int i = 1; i < _azimuthal_angles.size(); i++)
192  {
193  mod_azimuthal_angles.push_back(_azimuthal_angles[i - 1]);
194  if (_order == 2)
195  mod_azimuthal_angles.push_back((_azimuthal_angles[i - 1] + _azimuthal_angles[i]) / 2.0);
196  }
197  mod_azimuthal_angles.push_back(_azimuthal_angles.back());
198  if (_order == 2)
199  mod_azimuthal_angles.push_back((_azimuthal_angles.back() + _azimuthal_angles.front() + 360.0) /
200  2.0);
201 
202  const Real corr_factor =
204  ? PolygonalMeshGenerationUtils::radiusCorrectionFactor(mod_azimuthal_angles, true, _order)
205  : 1.0;
206 
207  for (const auto & ring_radius : _ring_radii)
208  ring_radii_corr.push_back(ring_radius * corr_factor);
209 
210  const multiBdryLayerParams empty_params = {
211  std::vector<Real>(), std::vector<Real>(), std::vector<unsigned int>(), std::vector<Real>()};
212  const singleBdryLayerParams empty_param = {0.0, 0.0, 0, 1.0};
213 
214  // A dummy pitch number is needed for callind buildSlice()
215  // Any value larger than twice of the largest ring radius will work
216  const Real dummy_pitch = _ring_radii.back() * 3.0;
217  const unsigned int num_sectors_per_side = 1;
218  const unsigned int background_intervals = 0;
219  const Real background_radial_bias = 1.0;
220 
221  dof_id_type node_id_background_meta;
222 
223  auto mesh = buildSlice(ring_radii_corr,
228  std::vector<Real>(),
229  std::vector<unsigned int>(),
230  std::vector<Real>(),
231  empty_params,
232  empty_params,
233  dummy_pitch,
234  num_sectors_per_side,
235  background_intervals,
236  background_radial_bias,
237  empty_param,
238  empty_param,
239  node_id_background_meta,
241  /*side_index*/ 1,
242  std::vector<Real>(),
244  /* quad_center_elements */ false,
245  /* center_quad_factor */ 0.0,
249  1.0,
250  true,
253  MeshTools::Modification::rotate(*mesh, -_azimuthal_angles[0], 0, 0);
254 
255  for (unsigned int i = 1; i < _num_sectors; i++)
256  {
257  auto mesh_tmp = buildSlice(ring_radii_corr,
262  std::vector<Real>(),
263  std::vector<unsigned int>(),
264  std::vector<Real>(),
265  empty_params,
266  empty_params,
267  dummy_pitch,
268  num_sectors_per_side,
269  background_intervals,
270  background_radial_bias,
271  empty_param,
272  empty_param,
273  node_id_background_meta,
275  /*side_index*/ i + 1,
276  std::vector<Real>(),
278  /* quad_center_elements */ false,
279  /* center_quad_factor */ 0.0,
283  1.0,
284  true,
287 
288  ReplicatedMesh other_mesh(*mesh_tmp);
289  MeshTools::Modification::rotate(other_mesh, -_azimuthal_angles[i], 0, 0);
290  mesh->prepare_for_use();
291  other_mesh.prepare_for_use();
292  // As we rotate the mesh in the negative direction (see "-" before _azimuthal_angles[i]),
293  // the order of SLICE_END and SLICE_BEGIN should be reversed compared to the similar call in
294  // PolygonConcentricCircleMeshGeneratorBase.C
295  mesh->stitch_meshes(other_mesh, SLICE_END, SLICE_BEGIN, TOLERANCE, true, false);
296  other_mesh.clear();
297  }
298 
300  for (unsigned int i = 0; i < _num_sectors; i++)
301  mesh->get_boundary_info().remove_id(i + 1 + OUTER_SIDESET_ID_ALT);
302 
303  // An extra step to stich the first and last slices together
304  mesh->stitch_surfaces(SLICE_END, SLICE_BEGIN, TOLERANCE, true, false);
305 
306  mesh->prepare_for_use();
307 
308  // Set up customized Block Names and/or IDs
309  unsigned int block_it = 0;
310  unsigned int ring_block_num = 0;
311  std::vector<subdomain_id_type> block_ids_old;
312  std::vector<subdomain_id_type> block_ids_new;
313  std::vector<SubdomainName> block_names;
314 
315  ringBlockIdsNamesPreparer(block_it, ring_block_num, block_ids_old, block_ids_new, block_names);
316 
318  *mesh, block_ids_old, block_ids_new, block_names, "AdvancedConcentricCircleGenerator");
319 
320  // Customized boundary ids and names
321  if (_external_boundary_id > 0)
323  else
325  if (!_external_boundary_name.empty())
326  {
327  mesh->get_boundary_info().sideset_name(_external_boundary_id > 0 ? _external_boundary_id : 0) =
329  mesh->get_boundary_info().nodeset_name(_external_boundary_id > 0 ? _external_boundary_id : 0) =
331  }
332 
334 
335  mesh->set_isnt_prepared();
336  return dynamic_pointer_cast<MeshBase>(mesh);
337 }
const std::vector< unsigned int > _ring_intervals
Number of rings in each circle or in the enclosing square.
const boundary_id_type _interface_boundary_id_shift
Shift in default boundary IDs of interfaces to avert potential conflicts.
const std::vector< std::string > _outward_interface_boundary_names
Boundary Names of the mesh&#39;s outward interface boundaries.
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
const subdomain_id_type _block_id_shift
Shift in default subdomain IDs to avert potential conflicts with other meshes.
QUAD_ELEM_TYPE _quad_elem_type
Type of quadrilateral elements to be generated.
const bool _preserve_volumes
Volume preserving function is optional.
const boundary_id_type _external_boundary_id
Boundary ID of the mesh&#39;s external boundary.
Contains multiple blocks&#39;s boundary layer related parameters.
std::unique_ptr< ReplicatedMesh > buildSlice(std::vector< Real > ring_radii, const std::vector< unsigned int > ring_layers, const std::vector< Real > ring_radial_biases, const multiBdryLayerParams &ring_inner_boundary_layer_params, const multiBdryLayerParams &ring_outer_boundary_layer_params, std::vector< Real > ducts_center_dist, const std::vector< unsigned int > ducts_layers, const std::vector< Real > duct_radial_biases, const multiBdryLayerParams &duct_inner_boundary_layer_params, const multiBdryLayerParams &duct_outer_boundary_layer_params, const Real pitch, const unsigned int num_sectors_per_side, const unsigned int background_intervals, const Real background_radial_bias, const singleBdryLayerParams &background_inner_boundary_layer_params, const singleBdryLayerParams &background_outer_boundary_layer_params, dof_id_type &node_id_background_meta, const Real virtual_side_number, const unsigned int side_index, const std::vector< Real > azimuthal_tangent=std::vector< Real >(), const subdomain_id_type block_id_shift=0, const bool quad_center_elements=false, const Real center_quad_factor=0.0, const bool create_inward_interface_boundaries=false, const bool create_outward_interface_boundaries=true, const boundary_id_type boundary_id_shift=0, const Real pitch_scale_factor=1.0, const bool generate_side_specific_boundaries=true, const TRI_ELEM_TYPE tri_elem_type=TRI_ELEM_TYPE::TRI3, const QUAD_ELEM_TYPE quad_elem_type=QUAD_ELEM_TYPE::QUAD4)
Generates a mesh of a polygon slice, which is the foundation of both buildGeneralSlice and buildSimpl...
MeshBase & mesh
multiBdryLayerParams _ring_inner_boundary_layer_params
Widths, fractions, radial sectors and growth factors of the inner boundary layers of the ring regions...
const std::vector< Real > _ring_radii
Radii of concentric circles.
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
AdvancedConcentricCircleGenerator(const InputParameters &parameters)
std::unique_ptr< MeshBase > generate() override
void assignBlockIdsNames(ReplicatedMesh &mesh, std::vector< subdomain_id_type > &block_ids_old, std::vector< subdomain_id_type > &block_ids_new, std::vector< SubdomainName > &block_names, const std::string &generator_name) const
Assign block IDs and names to the mesh if applicable.
void ringBlockIdsNamesPreparer(unsigned int &block_counter, unsigned int &ring_block_num, std::vector< subdomain_id_type > &block_ids_old, std::vector< subdomain_id_type > &block_ids_new, std::vector< SubdomainName > &block_names) const
Prepare user-defined ring block IDs and names to replace the default ones.
Contains a single block&#39;s boundary layer related parameters.
void changeBoundaryId(const boundary_id_type old_id, const boundary_id_type new_id, bool delete_prev)
registerMooseObject("ReactorApp", AdvancedConcentricCircleGenerator)
const bool _create_outward_interface_boundaries
Whether outward interface boundaries are created.
const std::vector< std::string > _inward_interface_boundary_names
Boundary Names of the mesh&#39;s inward interface boundaries.
multiBdryLayerParams _ring_outer_boundary_layer_params
Widths, fractions, radial sectors and growth factors of the outer boundary layers of the ring regions...
const bool _create_inward_interface_boundaries
Whether inward interface boundaries are created.
const std::vector< Real > _ring_radial_biases
Bias values used to induce biasing to radial meshing in ring regions.
const unsigned int _num_sectors
Number of azimuthal sectors of the circular mesh to be generated.
void paramError(const std::string &param, Args... args) const
std::vector< Real > _virtual_nums_sectors
A virtual sector number list which are 360.0 times inverse of the azimuthal intervals.
std::vector< SubdomainName > _ring_block_names
Subdomain Names of the ring regions.
std::vector< Real > _azimuthal_angles
List of the azimuthal angles of the nodes.
const BoundaryName _external_boundary_name
Boundary Name of the mesh&#39;s external boundary.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< subdomain_id_type > _ring_block_ids
Subdomain IDs of the ring regions.
void makeParamRequired(const std::string &name)
void addClassDescription(const std::string &doc_string)
This AdvancedConcentricCircleGenerator object is designed to mesh a concentric circular geometry...
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
This ConcentricCircleGeneratorBase object is a base class to be inherited for mesh generators that in...
TRI_ELEM_TYPE _tri_elem_type
Type of triangular elements to be generated.
const bool _generate_side_specific_boundaries
Whether the side-specific external boundaries are generated or not.
unsigned short _order
Order of the elements to be generated.
Real radiusCorrectionFactor(const std::vector< Real > &azimuthal_list, const bool full_circle=true, const unsigned int order=1, const bool is_first_value_vertex=true)
Makes radial correction to preserve ring area.
void ErrorVector unsigned int
bool absoluteFuzzyGreaterThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
void assignInterfaceBoundaryNames(ReplicatedMesh &mesh) const
Assign interface boundary names to the mesh if applicable.
uint8_t dof_id_type
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)