www.mooseframework.org
SideSetsAroundSubdomainGenerator.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 "MooseMeshUtils.h"
14 #include "CastUniquePointer.h"
15 
16 #include "libmesh/mesh.h"
17 #include "libmesh/remote_elem.h"
18 #include "libmesh/point.h"
19 #include "libmesh/fe_base.h"
20 
22 
23 template <>
26 {
28 
29  params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
30  params.addRequiredParam<std::vector<BoundaryName>>(
31  "new_boundary", "The list of boundary IDs to create on the supplied subdomain");
32  params.addRequiredParam<std::vector<SubdomainName>>("block",
33  "The blocks around which to create sidesets");
34  params.addParam<Point>("normal",
35  "If supplied, only faces with normal equal to this, up to "
36  "normal_tol, will be added to the sidesets specified");
37  params.addRangeCheckedParam<Real>("normal_tol",
38  0.1,
39  "normal_tol>=0 & normal_tol<=2",
40  "If normal is supplied then faces are "
41  "only added if face_normal.normal_hat >= "
42  "1 - normal_tol, where normal_hat = "
43  "normal/|normal|");
44 
45  params.addClassDescription(
46  "Adds element faces that are on the exterior of the given block to the sidesets specified");
47 
48  return params;
49 }
50 
52  const InputParameters & parameters)
53  : SideSetsGeneratorBase(parameters),
54  _input(getMesh("input")),
55  _boundary_names(getParam<std::vector<BoundaryName>>("new_boundary")),
56  _using_normal(isParamValid("normal")),
57  _normal_tol(getParam<Real>("normal_tol")),
58  _normal(_using_normal ? getParam<Point>("normal") : Point())
59 {
60  if (_using_normal)
61  {
62  // normalize
63  mooseAssert(_normal.norm() >= 1E-5, "Normal is zero");
64  _normal /= _normal.norm();
65  }
66 }
67 
68 std::unique_ptr<MeshBase>
70 {
71  std::unique_ptr<MeshBase> mesh = std::move(_input);
72 
73  // Extract the block ID
74  /*std::vector<subdomain_id_type> blocks;
75  std::vector<SubdomainName> block_names = getParam<std::vector<SubdomainName>>("block");
76  blocks.resize(block_names.size());
77  for (unsigned int i = 0; i < block_names.size(); i++)
78  blocks[i] = mesh->get_id_by_name(block_names[i]);*/
79  auto blocks =
80  MooseMeshUtils::getSubdomainIDs(*mesh, getParam<std::vector<SubdomainName>>("block"));
81  std::set<subdomain_id_type> block_ids(blocks.begin(), blocks.end());
82 
83  // Create the boundary IDs from the list of names provided (the true flag creates ids from unknown
84  // names)
85  std::vector<boundary_id_type> boundary_ids =
87 
88  // construct the FE object so we can compute normals of faces
89  setup(*mesh);
90  Point face_normal;
91  bool add_to_bdy = true;
92 
93  // Get a reference to our BoundaryInfo object for later use
94  BoundaryInfo & boundary_info = mesh->get_boundary_info();
95 
96  // Prepare to query about sides adjacent to remote elements if we're
97  // on a distributed mesh
98  const processor_id_type my_n_proc = mesh->n_processors();
99  const processor_id_type my_proc_id = mesh->processor_id();
100  typedef std::vector<std::pair<dof_id_type, unsigned int>> vec_type;
101  std::vector<vec_type> queries(my_n_proc);
102 
103  // Loop over the elements
104  for (const auto & elem : mesh->active_element_ptr_range())
105  {
106  subdomain_id_type curr_subdomain = elem->subdomain_id();
107 
108  // We only need to loop over elements in the source subdomain
109  if (block_ids.count(curr_subdomain) == 0)
110  continue;
111 
112  for (unsigned int side = 0; side < elem->n_sides(); ++side)
113  {
114  const Elem * neighbor = elem->neighbor_ptr(side);
115 
116  // On a replicated mesh, we add all subdomain sides ourselves.
117  // On a distributed mesh, we may have missed sides which
118  // neighbor remote elements. We should query any such cases.
119  if (neighbor == remote_elem)
120  {
121  queries[elem->processor_id()].push_back(std::make_pair(elem->id(), side));
122  }
123  else if (neighbor == nullptr || // element on boundary OR
124  block_ids.count(neighbor->subdomain_id()) ==
125  0) // neighboring element is on a different subdomain
126  {
127  if (_using_normal)
128  {
129  _fe_face->reinit(elem, side);
130  face_normal = _fe_face->get_normals()[0];
131  add_to_bdy = (_normal * face_normal >= 1.0 - _normal_tol);
132  }
133 
134  // Add the boundaries, if appropriate
135  if (add_to_bdy)
136  for (const auto & boundary_id : boundary_ids)
137  boundary_info.add_side(elem, side, boundary_id);
138  }
139  }
140  }
141 
142  if (!mesh->is_serial())
143  {
144  Parallel::MessageTag queries_tag = mesh->comm().get_unique_tag(867),
145  replies_tag = mesh->comm().get_unique_tag(5309);
146 
147  std::vector<Parallel::Request> side_requests(my_n_proc - 1), reply_requests(my_n_proc - 1);
148 
149  // Make all requests
150  for (processor_id_type p = 0; p != my_n_proc; ++p)
151  {
152  if (p == my_proc_id)
153  continue;
154 
155  Parallel::Request & request = side_requests[p - (p > my_proc_id)];
156 
157  mesh->comm().send(p, queries[p], request, queries_tag);
158  }
159 
160  // Reply to all requests
161  std::vector<vec_type> responses(my_n_proc - 1);
162 
163  for (processor_id_type p = 1; p != my_n_proc; ++p)
164  {
165  vec_type query;
166 
167  Parallel::Status status(mesh->comm().probe(Parallel::any_source, queries_tag));
168  const processor_id_type source_pid = cast_int<processor_id_type>(status.source());
169 
170  mesh->comm().receive(source_pid, query, queries_tag);
171 
172  Parallel::Request & request = reply_requests[p - 1];
173 
174  for (const auto & q : query)
175  {
176  const Elem * elem = mesh->elem_ptr(q.first);
177  const unsigned int side = q.second;
178  const Elem * neighbor = elem->neighbor_ptr(side);
179 
180  if (neighbor == nullptr || // element on boundary OR
181  block_ids.count(neighbor->subdomain_id()) ==
182  0) // neighboring element is on a different subdomain
183  {
184  if (_using_normal)
185  {
186  _fe_face->reinit(elem, side);
187  face_normal = _fe_face->get_normals()[0];
188  add_to_bdy = (_normal * face_normal >= 1.0 - _normal_tol);
189  }
190 
191  // Add the boundaries, if appropriate
192  if (add_to_bdy)
193  responses[p - 1].push_back(std::make_pair(elem->id(), side));
194  }
195  }
196 
197  mesh->comm().send(source_pid, responses[p - 1], request, replies_tag);
198  }
199 
200  // Process all incoming replies
201  for (processor_id_type p = 1; p != my_n_proc; ++p)
202  {
203  Parallel::Status status(this->comm().probe(Parallel::any_source, replies_tag));
204  const processor_id_type source_pid = cast_int<processor_id_type>(status.source());
205 
206  vec_type response;
207 
208  this->comm().receive(source_pid, response, replies_tag);
209 
210  for (const auto & r : response)
211  {
212  const Elem * elem = mesh->elem_ptr(r.first);
213  const unsigned int side = r.second;
214 
215  for (const auto & boundary_id : boundary_ids)
216  boundary_info.add_side(elem, side, boundary_id);
217  }
218  }
219 
220  Parallel::wait(side_requests);
221  Parallel::wait(reply_requests);
222  }
223 
224  // Assign the supplied names to the newly created side sets
225  for (unsigned int i = 0; i < boundary_ids.size(); ++i)
226  boundary_info.sideset_name(boundary_ids[i]) = _boundary_names[i];
227 
228  return dynamic_pointer_cast<MeshBase>(mesh);
229 }
SideSetsAroundSubdomainGenerator(const InputParameters &parameters)
std::vector< BoundaryName > _boundary_names
names of the sidesets to which the faces will be added
InputParameters validParams< SideSetsAroundSubdomainGenerator >()
Point _normal
if specified, then faces are only added if their normal is close to this
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
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< subdomain_id_type > getSubdomainIDs(const libMesh::MeshBase &mesh, const std::vector< SubdomainName > &subdomain_name)
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
Definition: MooseObject.h:191
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.
Real _normal_tol
if normal is specified, then faces are only added if face_normal.normal_hat <= 1 - normal_tol where n...
std::unique_ptr< FEBase > _fe_face
InputParameters validParams< SideSetsGeneratorBase >()
std::vector< libMesh::boundary_id_type > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown)
MPI_Comm comm
registerMooseObject("MooseApp", SideSetsAroundSubdomainGenerator)
bool _using_normal
true if only faces close to "normal" will be added
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...
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
Adds the faces on the boundary of given block to the sidesets specified by "boundary" Optionally...
void setup(MeshBase &mesh)
This method is used to construct the FE object so we can compute normals of faces.