www.mooseframework.org
LowerDBlockFromSidesetGenerator.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 "InputParameters.h"
12 #include "MooseTypes.h"
13 #include "CastUniquePointer.h"
14 #include "MooseMeshUtils.h"
15 
16 #include "libmesh/distributed_mesh.h"
17 #include "libmesh/elem.h"
18 #include "libmesh/parallel_elem.h"
19 #include "libmesh/parallel_node.h"
20 #include "libmesh/compare_elems_by_level.h"
21 #include "libmesh/mesh_communication.h"
22 
23 #include "timpi/parallel_sync.h"
24 
25 #include <set>
26 #include <typeinfo>
27 
29 
32 {
34 
35  params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
36  params.addParam<SubdomainID>("new_block_id", "The lower dimensional block id to create");
37  params.addParam<SubdomainName>("new_block_name",
38  "The lower dimensional block name to create (optional)");
39  params.addRequiredParam<std::vector<BoundaryName>>(
40  "sidesets", "The sidesets from which to create the new block");
41 
42  params.addClassDescription("Adds lower dimensional elements on the specified sidesets.");
43 
44  return params;
45 }
46 
48  : MeshGenerator(parameters),
49  _input(getMesh("input")),
50  _sideset_names(getParam<std::vector<BoundaryName>>("sidesets"))
51 {
52 }
53 
54 // Used to temporarily store information about which lower-dimensional
55 // sides to add and what subdomain id to use for the added sides.
57 {
58  ElemSideDouble(Elem * elem_in, unsigned short int side_in) : elem(elem_in), side(side_in) {}
59 
60  Elem * elem;
61  unsigned short int side;
62 };
63 
64 std::unique_ptr<MeshBase>
66 {
67  std::unique_ptr<MeshBase> mesh = std::move(_input);
68 
69  // Generate a new block id if one isn't supplied.
70  SubdomainID new_block_id = isParamValid("new_block_id")
71  ? getParam<SubdomainID>("new_block_id")
73 
74  // Make sure our boundary info and parallel counts are setup
75  if (!mesh->is_prepared())
76  {
77  const bool allow_remote_element_removal = mesh->allow_remote_element_removal();
78  // We want all of our boundary elements available, so avoid removing them if they haven't
79  // already been so
80  mesh->allow_remote_element_removal(false);
81  mesh->prepare_for_use();
82  mesh->allow_remote_element_removal(allow_remote_element_removal);
83  }
84 
85  auto sideset_ids = MooseMeshUtils::getBoundaryIDs(*mesh, _sideset_names, true);
86  std::set<boundary_id_type> sidesets(sideset_ids.begin(), sideset_ids.end());
87 
88  auto side_list = mesh->get_boundary_info().build_side_list();
89  if (!mesh->is_serial() && mesh->comm().size() > 1)
90  {
91  std::vector<Elem *> elements_to_send;
92  unsigned short i_need_boundary_elems = 0;
93  for (const auto & [elem_id, side, bc_id] : side_list)
94  {
95  libmesh_ignore(side);
96  if (sidesets.count(bc_id))
97  {
98  // Whether we have this boundary information through our locally owned element or a ghosted
99  // element, we'll need the boundary elements for parallel consistent addition
100  i_need_boundary_elems = 1;
101  auto * elem = mesh->elem_ptr(elem_id);
102  if (elem->processor_id() == mesh->processor_id())
103  elements_to_send.push_back(elem);
104  }
105  }
106 
107  std::set<const Elem *, CompareElemIdsByLevel> connected_elements(elements_to_send.begin(),
108  elements_to_send.end());
109  std::set<const Node *> connected_nodes;
110  reconnect_nodes(connected_elements, connected_nodes);
111  std::set<dof_id_type> connected_node_ids;
112  for (auto * nd : connected_nodes)
113  connected_node_ids.insert(nd->id());
114 
115  std::vector<unsigned short> need_boundary_elems(mesh->comm().size());
116  mesh->comm().allgather(i_need_boundary_elems, need_boundary_elems);
117  std::unordered_map<processor_id_type, decltype(elements_to_send)> push_element_data;
118  std::unordered_map<processor_id_type, decltype(connected_nodes)> push_node_data;
119 
120  for (const auto pid : index_range(mesh->comm()))
121  // Don't need to send to self
122  if (pid != mesh->processor_id() && need_boundary_elems[pid])
123  {
124  if (elements_to_send.size())
125  push_element_data[pid] = elements_to_send;
126  if (connected_nodes.size())
127  push_node_data[pid] = connected_nodes;
128  }
129 
130  auto node_action_functor = [](processor_id_type, const auto &)
131  {
132  // Node packing specialization already has unpacked node into mesh, so nothing to do
133  };
134  Parallel::push_parallel_packed_range(
135  mesh->comm(), push_node_data, mesh.get(), node_action_functor);
136  auto elem_action_functor = [](processor_id_type, const auto &)
137  {
138  // Elem packing specialization already has unpacked elem into mesh, so nothing to do
139  };
141  mesh->comm(), push_element_data, mesh.get(), elem_action_functor);
142 
143  // now that we've gathered everything, we need to rebuild the side list
144  side_list = mesh->get_boundary_info().build_side_list();
145  }
146 
147  std::vector<std::pair<dof_id_type, ElemSideDouble>> element_sides_on_boundary;
148  dof_id_type counter = 0;
149  for (const auto & triple : side_list)
150  if (sidesets.count(std::get<2>(triple)))
151  {
152  if (auto elem = mesh->query_elem_ptr(std::get<0>(triple)))
153  {
154  if (!elem->active())
155  mooseError(
156  "Only active, level 0 elements can be made interior parents of new level 0 lower-d "
157  "elements. Make sure that ",
158  type(),
159  "s are run before any refinement generators");
160  element_sides_on_boundary.push_back(
161  std::make_pair(counter, ElemSideDouble(elem, std::get<1>(triple))));
162  }
163  ++counter;
164  }
165 
166  dof_id_type max_elem_id = mesh->max_elem_id();
167  unique_id_type max_unique_id = mesh->parallel_max_unique_id();
168 
169  // Making an important assumption that at least our boundary elements are the same on all
170  // processes even in distributed mesh mode (this is reliant on the correct ghosting functors
171  // existing on the mesh)
172  for (auto & [i, elem_side] : element_sides_on_boundary)
173  {
174  Elem * elem = elem_side.elem;
175 
176  const auto side = elem_side.side;
177 
178  // Build a non-proxy element from this side.
179  std::unique_ptr<Elem> side_elem(elem->build_side_ptr(side, /*proxy=*/false));
180 
181  // The side will be added with the same processor id as the parent.
182  side_elem->processor_id() = elem->processor_id();
183 
184  // Add subdomain ID
185  side_elem->subdomain_id() = new_block_id;
186 
187  // Also assign the side's interior parent, so it is always
188  // easy to figure out the Elem we came from.
189  side_elem->set_interior_parent(elem);
190 
191  // Add id
192  side_elem->set_id(max_elem_id + i);
193  side_elem->set_unique_id(max_unique_id + i);
194 
195  // Finally, add the lower-dimensional element to the Mesh.
196  mesh->add_elem(side_elem.release());
197  };
198 
199  // Assign block name, if provided
200  if (isParamValid("new_block_name"))
201  mesh->subdomain_name(new_block_id) = getParam<SubdomainName>("new_block_name");
202 
203  const bool skip_partitioning_old = mesh->skip_partitioning();
204  mesh->skip_partitioning(true);
205  mesh->prepare_for_use();
206  mesh->skip_partitioning(skip_partitioning_old);
207 
208  return mesh;
209 }
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
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::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
uint8_t processor_id_type
void libmesh_ignore(const Args &...)
registerMooseObject("MooseApp", LowerDBlockFromSidesetGenerator)
void push_parallel_packed_range(const Communicator &comm, MapToContainers &&data, Context *context, const ActionFunctor &act_on_data)
LowerDBlockFromSidesetGenerator(const InputParameters &parameters)
const std::string & type() const
Get the type of this class.
Definition: MooseBase.h:50
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
Gets the boundary IDs with their names.
static InputParameters validParams()
Definition: MeshGenerator.C:23
void reconnect_nodes(const std::set< const Elem *, CompareElemIdsByLevel > &connected_elements, connected_node_set_type &connected_nodes)
ElemSideDouble(Elem *elem_in, unsigned short int side_in)
Creates lower-dimensional elements on the specified sidesets.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
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...
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...
SubdomainID getNextFreeSubdomainID(MeshBase &input_mesh)
Checks input mesh and returns max(block ID) + 1, which represents a block ID that is not currently in...
const std::vector< BoundaryName > _sideset_names
a vector of the names of the sidesets to add the lower-D elements to
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:32
uint8_t unique_id_type
auto index_range(const T &sizable)
uint8_t dof_id_type