www.mooseframework.org
PatchSidesetGenerator.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 
10 #include "PatchSidesetGenerator.h"
11 #include "InputParameters.h"
12 #include "MooseTypes.h"
13 #include "CastUniquePointer.h"
14 #include "MooseUtils.h"
15 #include "MooseMeshUtils.h"
16 
17 #include "libmesh/distributed_mesh.h"
18 #include "libmesh/elem.h"
19 #include "libmesh/linear_partitioner.h"
20 #include "libmesh/centroid_partitioner.h"
21 #include "libmesh/parmetis_partitioner.h"
22 #include "libmesh/hilbert_sfc_partitioner.h"
23 #include "libmesh/morton_sfc_partitioner.h"
24 #include "libmesh/enum_elem_type.h"
25 
26 // libmesh elem types
27 #include "libmesh/edge_edge2.h"
28 #include "libmesh/edge_edge3.h"
29 #include "libmesh/edge_edge4.h"
30 #include "libmesh/face_tri3.h"
31 #include "libmesh/face_tri6.h"
32 #include "libmesh/face_quad4.h"
33 #include "libmesh/face_quad8.h"
34 #include "libmesh/face_quad9.h"
35 
36 #include <set>
37 #include <limits>
38 
39 registerMooseObject("HeatConductionApp", PatchSidesetGenerator);
40 
42 
43 InputParameters
45 {
46  InputParameters params = MeshGenerator::validParams();
47 
48  params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
49  params.addRequiredParam<boundary_id_type>("sideset",
50  "The sideset that will be divided into patches");
51  params.addRequiredRangeCheckedParam<unsigned int>(
52  "n_patches", "n_patches>0", "Number of patches");
53 
54  MooseEnum partitioning("default=-3 metis=-2 parmetis=-1 linear=0 centroid hilbert_sfc morton_sfc",
55  "default");
56  params.addParam<MooseEnum>(
57  "partitioner",
58  partitioning,
59  "Specifies a mesh partitioner to use when splitting the mesh for a parallel computation.");
60  MooseEnum direction("x y z radial");
61  params.addParam<MooseEnum>("centroid_partitioner_direction",
62  direction,
63  "Specifies the sort direction if using the centroid partitioner. "
64  "Available options: x, y, z, radial");
65 
66  params.addParamNamesToGroup("partitioner centroid_partitioner_direction", "Partitioning");
67 
68  params.addClassDescription(
69  "Divides the given sideset into smaller patches of roughly equal size.");
70 
71  return params;
72 }
73 
74 PatchSidesetGenerator::PatchSidesetGenerator(const InputParameters & parameters)
75  : MeshGenerator(parameters),
76  _input(getMesh("input")),
77  _n_patches(getParam<unsigned int>("n_patches")),
78  _sideset(getParam<boundary_id_type>("sideset")),
79  _partitioner_name(getParam<MooseEnum>("partitioner"))
80 {
81 }
82 
83 std::unique_ptr<MeshBase>
85 {
86  std::unique_ptr<MeshBase> mesh = std::move(_input);
87 
88  _mesh->errorIfDistributedMesh("PatchSidesetGenerator");
89 
90  // Get a reference to our BoundaryInfo object for later use
91  BoundaryInfo & boundary_info = mesh->get_boundary_info();
92 
93  // get a list of all sides; vector of tuples (elem, loc_side, side_set)
94  auto side_list = boundary_info.build_active_side_list();
95 
96  // create a dim - 1 dimensional mesh
97  auto boundary_mesh =
98  libmesh_make_unique<libMesh::ReplicatedMesh>(comm(), mesh->mesh_dimension() - 1);
99  boundary_mesh->set_mesh_dimension(mesh->mesh_dimension() - 1);
100  boundary_mesh->set_spatial_dimension(mesh->mesh_dimension());
101 
102  // nodes in the new mesh by boundary_node_id (index)
103  std::vector<Node *> boundary_nodes;
104  // a map from the node numbering on the volumetric mesh to the numbering
105  // on the boundary_mesh
106  std::map<dof_id_type, dof_id_type> mesh_node_id_to_boundary_node_id;
107  // a local counter keeping track of how many entries have been added to boundary_nodes
108  dof_id_type boundary_node_id = 0;
109  // a map from new element id in the boundary mesh to the element id/side/sideset
110  // tuple it came from
111  std::map<dof_id_type, std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
112  boundary_elem_to_mesh_elem;
113  for (auto & side : side_list)
114  {
115  if (std::get<2>(side) == _sideset)
116  {
117  // the original volumetric mesh element
118  const Elem * elem = mesh->elem_ptr(std::get<0>(side));
119 
120  // the boundary element
121  std::unique_ptr<const Elem> boundary_elem = elem->side_ptr(std::get<1>(side));
122 
123  // an array that saves the boundary node ids of this elem in the right order
124  std::vector<dof_id_type> bnd_elem_node_ids(boundary_elem->n_nodes());
125 
126  // loop through the nodes in boundary_elem
127  for (unsigned int j = 0; j < boundary_elem->n_nodes(); ++j)
128  {
129  const Node * node = boundary_elem->node_ptr(j);
130 
131  // Is this node a new node?
132  if (mesh_node_id_to_boundary_node_id.find(node->id()) ==
133  mesh_node_id_to_boundary_node_id.end())
134  {
135  // yes, it is new, need to add it to the mesh_node_id_to_boundary_node_id map
136  mesh_node_id_to_boundary_node_id.insert(
137  std::pair<dof_id_type, dof_id_type>(node->id(), boundary_node_id));
138 
139  // this adds this node to the boundary mesh and puts it at the right position
140  // in the boundary_nodes array
141  Point pt(*node);
142  boundary_nodes.push_back(boundary_mesh->add_point(pt, boundary_node_id));
143 
144  // keep track of the boundary node for setting up the element
145  bnd_elem_node_ids[j] = boundary_node_id;
146 
147  // increment the boundary_node_id counter
148  ++boundary_node_id;
149  }
150  else
151  bnd_elem_node_ids[j] = mesh_node_id_to_boundary_node_id.find(node->id())->second;
152  }
153 
154  // all nodes for this element have been added, so we can add the element to the
155  // boundary mesh
156  Elem * new_bnd_elem = boundaryElementHelper(*boundary_mesh, boundary_elem->type());
157 
158  // keep track of these new boundary elements in boundary_elem_to_mesh_elem
159  boundary_elem_to_mesh_elem.insert(
160  std::pair<dof_id_type, std::tuple<dof_id_type, unsigned short int, boundary_id_type>>(
161  new_bnd_elem->id(), side));
162 
163  // set the nodes & subdomain_id of the new element by looping over the
164  // boundary_elem and then inserting its nodes into new_bnd_elem in the
165  // same order
166  for (unsigned int j = 0; j < boundary_elem->n_nodes(); ++j)
167  {
168  dof_id_type old_node_id = boundary_elem->node_ptr(j)->id();
169  if (mesh_node_id_to_boundary_node_id.find(old_node_id) ==
170  mesh_node_id_to_boundary_node_id.end())
171  mooseError("Node id", old_node_id, " not linked to new node id.");
172  dof_id_type new_node_id = mesh_node_id_to_boundary_node_id.find(old_node_id)->second;
173  new_bnd_elem->set_node(j) = boundary_nodes[new_node_id];
174  }
175  }
176  }
177 
178  // partition the boundary mesh
179  boundary_mesh->prepare_for_use();
180  MooseMesh::setPartitioner(*boundary_mesh, _partitioner_name, false, _pars, *this);
181  boundary_mesh->partition(_n_patches);
182 
183  // prepare sideset names and boundary_ids added to mesh
184  std::vector<BoundaryName> sideset_names =
185  sidesetNameHelper(boundary_info.get_sideset_name(_sideset));
186 
187  std::vector<boundary_id_type> boundary_ids =
188  MooseMeshUtils::getBoundaryIDs(*mesh, sideset_names, true);
189 
190  mooseAssert(sideset_names.size() == _n_patches,
191  "sideset_names must have as many entries as user-requested number of patches.");
192  mooseAssert(boundary_ids.size() == _n_patches,
193  "boundary_ids must have as many entries as user-requested number of patches.");
194 
195  // loop through all elements in the boundary mesh and assign the side of
196  // the _original_ element to the new sideset
197  for (const auto & elem : boundary_mesh->active_element_ptr_range())
198  {
199  if (boundary_elem_to_mesh_elem.find(elem->id()) == boundary_elem_to_mesh_elem.end())
200  mooseError("Element in the boundary mesh with id ",
201  elem->id(),
202  " not found in boundary_elem_to_mesh_elem.");
203 
204  auto side = boundary_elem_to_mesh_elem.find(elem->id())->second;
205 
206  mooseAssert(elem->processor_id() < boundary_ids.size(),
207  "Processor id larger than number of patches.");
208  boundary_info.add_side(
209  std::get<0>(side), std::get<1>(side), boundary_ids[elem->processor_id()]);
210  }
211 
212  // make sure new boundary names are set
213  for (unsigned int j = 0; j < boundary_ids.size(); ++j)
214  {
215  boundary_info.sideset_name(boundary_ids[j]) = sideset_names[j];
216  boundary_info.nodeset_name(boundary_ids[j]) = sideset_names[j];
217  }
218 
219  return mesh;
220 }
221 
222 std::vector<BoundaryName>
223 PatchSidesetGenerator::sidesetNameHelper(const std::string & base_name) const
224 {
225  std::vector<BoundaryName> rv;
226  for (unsigned int j = 0; j < _n_patches; ++j)
227  {
228  std::stringstream ss;
229  ss << base_name << "_" << j;
230  rv.push_back(ss.str());
231  }
232  return rv;
233 }
234 
235 Elem *
236 PatchSidesetGenerator::boundaryElementHelper(MeshBase & mesh, libMesh::ElemType type) const
237 {
238  switch (type)
239  {
240  case 0:
241  return mesh.add_elem(new libMesh::Edge2);
242  case 1:
243  return mesh.add_elem(new libMesh::Edge3);
244  case 2:
245  return mesh.add_elem(new libMesh::Edge4);
246  case 3:
247  return mesh.add_elem(new libMesh::Tri3);
248  case 4:
249  return mesh.add_elem(new libMesh::Tri6);
250  case 5:
251  return mesh.add_elem(new libMesh::Quad4);
252  case 6:
253  return mesh.add_elem(new libMesh::Quad8);
254  case 7:
255  return mesh.add_elem(new libMesh::Quad9);
256  default:
257  mooseError("Unsupported element type (libMesh elem_type enum): ", type);
258  }
259 }
PatchSidesetGenerator::_n_patches
unsigned int _n_patches
the number of patches that this sideset generator divides _sideset into
Definition: PatchSidesetGenerator.h:43
PatchSidesetGenerator::boundaryElementHelper
Elem * boundaryElementHelper(MeshBase &mesh, libMesh::ElemType type) const
Definition: PatchSidesetGenerator.C:236
PatchSidesetGenerator::_partitioner_name
MooseEnum _partitioner_name
the name of the partitioner being used
Definition: PatchSidesetGenerator.h:49
PatchSidesetGenerator
Subdivides a sidesets into smaller patches each of which is going to be a new patch.
Definition: PatchSidesetGenerator.h:25
PatchSidesetGenerator::validParams
static InputParameters validParams()
Definition: PatchSidesetGenerator.C:44
PatchSidesetGenerator::sidesetNameHelper
std::vector< BoundaryName > sidesetNameHelper(const std::string &base_name) const
returns the name of the _n_patches subdivisions derived from _sideset
Definition: PatchSidesetGenerator.C:223
validParams
InputParameters validParams()
PatchSidesetGenerator::_sideset
const boundary_id_type & _sideset
The sideset that will be subdivided.
Definition: PatchSidesetGenerator.h:46
registerMooseObject
registerMooseObject("HeatConductionApp", PatchSidesetGenerator)
PatchSidesetGenerator::_input
std::unique_ptr< MeshBase > & _input
Definition: PatchSidesetGenerator.h:40
PatchSidesetGenerator::PatchSidesetGenerator
PatchSidesetGenerator(const InputParameters &parameters)
Definition: PatchSidesetGenerator.C:74
defineLegacyParams
defineLegacyParams(PatchSidesetGenerator)
PatchSidesetGenerator.h
PatchSidesetGenerator::generate
std::unique_ptr< MeshBase > generate() override
Definition: PatchSidesetGenerator.C:84