www.mooseframework.org
AugmentSparsityOnInterface.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 // App includes
12 #include "Executioner.h"
13 #include "FEProblemBase.h"
14 #include "MooseApp.h"
15 
16 // libMesh includes
17 #include "libmesh/elem.h"
18 #include "libmesh/mesh_base.h"
19 #include "libmesh/boundary_info.h"
20 
22 
23 using namespace libMesh;
24 
26 AugmentSparsityOnInterface::validParams()
27 {
29  params.addRequiredParam<BoundaryName>("primary_boundary",
30  "The name of the primary boundary sideset.");
31  params.addRequiredParam<BoundaryName>("secondary_boundary",
32  "The name of the secondary boundary sideset.");
33  params.addRequiredParam<SubdomainName>("primary_subdomain",
34  "The name of the primary lower dimensional subdomain.");
35  params.addRequiredParam<SubdomainName>("secondary_subdomain",
36  "The name of the secondary lower dimensional subdomain.");
37  params.addParam<bool>(
38  "ghost_point_neighbors",
39  false,
40  "Whether we should ghost point neighbors of secondary lower-dimensional elements and "
41  "also their mortar interface couples for applications such as mortar nodal auxiliary "
42  "kernels.");
43  params.addParam<bool>(
44  "ghost_higher_d_neighbors",
45  false,
46  "Whether we should ghost higher-dimensional neighbors. This is necessary when we are doing "
47  "second order mortar with finite volume primal variables, because in order for the method to "
48  "be second order we must use cell gradients, which couples in the neighbor cells.");
49 
50  // We want to wait until our mortar mesh has been built before trying to delete remote elements.
51  // And our mortar mesh cannot be built until the entire mesh has been generated. By setting this
52  // parameter to false we will make sure that any prepare_for_use calls during the mesh generation
53  // phase will not delete remote elements *and* we will set a flag on the moose mesh saying that we
54  // need to delete remote elements after the addition of late geometric ghosting functors
55  // (including this ghosting functor)
56  params.set<bool>("attach_geometric_early") = false;
57  return params;
58 }
59 
61  : RelationshipManager(params),
62  _primary_boundary_name(getParam<BoundaryName>("primary_boundary")),
63  _secondary_boundary_name(getParam<BoundaryName>("secondary_boundary")),
64  _primary_subdomain_name(getParam<SubdomainName>("primary_subdomain")),
65  _secondary_subdomain_name(getParam<SubdomainName>("secondary_subdomain")),
66  _is_coupling_functor(isType(Moose::RelationshipManagerType::COUPLING)),
67  _ghost_point_neighbors(getParam<bool>("ghost_point_neighbors")),
68  _ghost_higher_d_neighbors(getParam<bool>("ghost_higher_d_neighbors"))
69 {
70 }
71 
73  : RelationshipManager(other),
74  _primary_boundary_name(other._primary_boundary_name),
75  _secondary_boundary_name(other._secondary_boundary_name),
76  _primary_subdomain_name(other._primary_subdomain_name),
77  _secondary_subdomain_name(other._secondary_subdomain_name),
78  _is_coupling_functor(other._is_coupling_functor),
79  _ghost_point_neighbors(other._ghost_point_neighbors),
80  _ghost_higher_d_neighbors(other._ghost_higher_d_neighbors)
81 {
82 }
83 
84 void
85 AugmentSparsityOnInterface::internalInitWithMesh(const MeshBase &)
86 {
87 }
88 
89 std::string
90 AugmentSparsityOnInterface::getInfo() const
91 {
92  std::ostringstream oss;
93  oss << "AugmentSparsityOnInterface";
94  return oss.str();
95 }
96 
97 void
98 AugmentSparsityOnInterface::ghostMortarInterfaceCouplings(
99  const processor_id_type p,
100  const Elem * const elem,
101  map_type & coupled_elements,
102  const AutomaticMortarGeneration & amg) const
103 {
104  // Look up elem in the mortar_interface_coupling data structure.
105  const auto & mic = amg.mortarInterfaceCoupling();
106  auto find_it = mic.find(elem->id());
107  if (find_it == mic.end())
108  return;
109 
110  const auto & coupled_set = find_it->second;
111 
112  for (const auto coupled_elem_id : coupled_set)
113  {
114  const Elem * coupled_elem = _mesh->elem_ptr(coupled_elem_id);
115  mooseAssert(coupled_elem,
116  "The coupled element with id " << coupled_elem_id << " doesn't exist!");
117 
118  if (coupled_elem->processor_id() != p)
119  coupled_elements.emplace(coupled_elem, _null_mat);
120  }
121 }
122 
123 void
124 AugmentSparsityOnInterface::ghostLowerDSecondaryElemPointNeighbors(
125  const processor_id_type p,
126  const Elem * const query_elem,
127  map_type & coupled_elements,
128  const BoundaryID secondary_boundary_id,
129  const SubdomainID secondary_subdomain_id,
130  const AutomaticMortarGeneration & amg) const
131 {
132  // I hypothesize that node processor ids are tied to higher dimensional element processor
133  // ids over lower dimensional element processor ids based on debugging experience.
134  // Consequently we need to be checking for higher dimensional elements and whether they are
135  // along our secondary boundary. From there we will query the AMG object's
136  // mortar-interface-coupling container to get the secondary lower-dimensional element, and
137  // then we will ghost it's point neighbors and their mortar interface couples
138 
139  // It's possible that one higher-dimensional element could have multiple lower-dimensional
140  // elements from multiple sides. Morever, even if there is only one lower-d element per
141  // higher-d element, the unordered_multimap that holds the coupling information can have
142  // duplicate key-value pairs if there are multiple mortar segments per secondary face. So to
143  // prevent attempting to insert into the coupled elements map multiple times with the same
144  // element, we'll keep track of the elements we've handled. We're going to use a tree-based
145  // set here since the number of lower-d elements handled should never exceed the number of
146  // element sides (which is small)
147  std::set<dof_id_type> secondary_lower_elems_handled;
148  const BoundaryInfo & binfo = _mesh->get_boundary_info();
149  for (auto side : query_elem->side_index_range())
150  {
151  if (!binfo.has_boundary_id(query_elem, side, secondary_boundary_id))
152  // We're not a higher-dimensional element along the secondary face, or at least this
153  // side isn't
154  continue;
155 
156  const auto & mic = amg.mortarInterfaceCoupling();
157  auto find_it = mic.find(query_elem->id());
158  if (find_it == mic.end())
159  continue;
160 
161  const auto & coupled_set = find_it->second;
162  for (const auto coupled_elem_id : coupled_set)
163  {
164  auto * const coupled_elem = _mesh->elem_ptr(coupled_elem_id);
165 
166  if (coupled_elem->subdomain_id() != secondary_subdomain_id)
167  {
168  // We support higher-d-secondary to higher-d-primary coupling now, e.g.
169  // if we get here, coupled_elem is not actually a secondary lower elem; it's a
170  // primary higher-d elem
171  mooseAssert(coupled_elem->dim() == query_elem->dim(), "These should be matching dim");
172  continue;
173  }
174 
175  auto insert_pr = secondary_lower_elems_handled.insert(coupled_elem_id);
176 
177  // If insertion didn't happen, then we've already handled this element
178  if (!insert_pr.second)
179  continue;
180 
181  // We've already ghosted the secondary lower-d element itself if it needed to be
182  // outside of the _ghost_point_neighbors logic. But now we must make sure to ghost the
183  // point neighbors of the secondary lower-d element and their mortar interface
184  // couplings
185  std::set<const Elem *> secondary_lower_elem_point_neighbors;
186  coupled_elem->find_point_neighbors(secondary_lower_elem_point_neighbors);
187 
188  for (const Elem * const neigh : secondary_lower_elem_point_neighbors)
189  {
190  if (neigh->processor_id() != p)
191  coupled_elements.emplace(neigh, _null_mat);
192 
193  ghostMortarInterfaceCouplings(p, neigh, coupled_elements, amg);
194  }
195  } // end iteration over mortar interface couplings
196 
197  // We actually should have added all the lower-dimensional elements associated with the
198  // higher-dimensional element, so we can stop iterating over sides
199  return;
200 
201  } // end for side_index_range
202 }
203 
204 void
205 AugmentSparsityOnInterface::ghostHigherDNeighbors(const processor_id_type p,
206  const Elem * const query_elem,
207  map_type & coupled_elements,
208  const BoundaryID secondary_boundary_id,
209  const SubdomainID secondary_subdomain_id,
210  const AutomaticMortarGeneration & amg) const
211 {
212  // The coupling is this for second order FV: secondary lower dimensional elem dofs will depend on
213  // higher-d secondary elem neighbor primal dofs and higher-d primary elem neighbor primal dofs.
214  // Higher dimensional primal dofs only depend on the secondary lower dimensional LM dofs for the
215  // current FV gap heat transfer use cases
216 
217  if (query_elem->subdomain_id() != secondary_subdomain_id)
218  return;
219 
220  const BoundaryInfo & binfo = _mesh->get_boundary_info();
221  const auto which_side = query_elem->interior_parent()->which_side_am_i(query_elem);
222  if (!binfo.has_boundary_id(query_elem->interior_parent(), which_side, secondary_boundary_id))
223  return;
224 
225  const auto & mic = amg.mortarInterfaceCoupling();
226  auto find_it = mic.find(query_elem->id());
227  if (find_it == mic.end())
228  // Perhaps no projection onto primary
229  return;
230 
231  const auto & lower_d_coupled_set = find_it->second;
232  for (const auto coupled_elem_id : lower_d_coupled_set)
233  {
234  auto * const coupled_elem = _mesh->elem_ptr(coupled_elem_id);
235  if (coupled_elem->dim() == query_elem->dim())
236  // lower-d-elem
237  continue;
238 
239  std::vector<const Elem *> active_neighbors;
240 
241  for (auto s : coupled_elem->side_index_range())
242  {
243  const Elem * const neigh = coupled_elem->neighbor_ptr(s);
244 
245  if (!neigh || neigh == remote_elem)
246  continue;
247 
248  // With any kind of neighbor, we need to couple to all the
249  // active descendants on our side.
250  neigh->active_family_tree_by_neighbor(active_neighbors, coupled_elem);
251 
252  for (const auto & neighbor : active_neighbors)
253  if (neighbor->processor_id() != p)
254  {
255  mooseAssert(
256  neighbor->subdomain_id() != secondary_subdomain_id,
257  "Ensure that we aren't missing potential erasures from the secondary-to-msms map");
258  coupled_elements.emplace(neighbor, _null_mat);
259  }
260  }
261  }
262 }
263 
264 void
265 AugmentSparsityOnInterface::operator()(const MeshBase::const_element_iterator & range_begin,
266  const MeshBase::const_element_iterator & range_end,
267  const processor_id_type p,
268  map_type & coupled_elements)
269 {
270  // We ask the user to pass boundary names instead of ids to our constraint object. However, We
271  // are unable to get the boundary ids from boundary names until we've attached the MeshBase object
272  // to the MooseMesh
273  const bool generating_mesh = !_moose_mesh->getMeshPtr();
274  const auto primary_boundary_id = generating_mesh
276  : _moose_mesh->getBoundaryID(_primary_boundary_name);
277  const auto secondary_boundary_id = generating_mesh
279  : _moose_mesh->getBoundaryID(_secondary_boundary_name);
280  const auto primary_subdomain_id = generating_mesh
282  : _moose_mesh->getSubdomainID(_primary_subdomain_name);
283  const auto secondary_subdomain_id = generating_mesh
285  : _moose_mesh->getSubdomainID(_secondary_subdomain_name);
286 
287  const AutomaticMortarGeneration * const amg =
289  std::make_pair(primary_boundary_id, secondary_boundary_id),
290  std::make_pair(primary_subdomain_id, secondary_subdomain_id),
292  : nullptr;
293 
294  // If we're on a dynamic mesh or we have not yet constructed the mortar mesh, we need to ghost the
295  // entire interface because we don't know a priori what elements will project onto what. We *do
296  // not* add the whole interface if we are a coupling functor because it is very expensive. This is
297  // because when building the sparsity pattern, we call through to the ghosting functors with one
298  // element at a time (and then below we do a loop over all the mesh's active elements). It's
299  // perhaps faster in this case to deal with mallocs coming out of MatSetValues, especially if the
300  // mesh displacements are relatively small
301  if ((!amg || _use_displaced_mesh) && !_is_coupling_functor)
302  {
303  for (const Elem * const elem : _mesh->active_element_ptr_range())
304  {
305  if (generating_mesh)
306  {
307  // We are still generating the mesh, so it's possible we don't even have the right boundary
308  // ids created yet! So we actually ghost all boundary elements and all lower dimensional
309  // elements who have parents on a boundary
310  if (elem->on_boundary())
311  coupled_elements.insert(std::make_pair(elem, _null_mat));
312  else if (const Elem * const ip = elem->interior_parent())
313  {
314  if (ip->on_boundary())
315  coupled_elements.insert(std::make_pair(elem, _null_mat));
316  }
317  }
318  else
319  {
320  // We've finished generating our mesh so we can be selective and only ghost elements lying
321  // in our lower-dimensional subdomains and their interior parents
322 
323  mooseAssert(primary_boundary_id != Moose::INVALID_BOUNDARY_ID,
324  "Primary boundary id should exist by now. If you're using a MeshModifier "
325  "please use the corresponding MeshGenerator instead");
326  mooseAssert(secondary_boundary_id != Moose::INVALID_BOUNDARY_ID,
327  "Secondary boundary id should exist by now. If you're using a MeshModifier "
328  "please use the corresponding MeshGenerator instead");
329  mooseAssert(primary_subdomain_id != Moose::INVALID_BLOCK_ID,
330  "Primary subdomain id should exist by now. If you're using a MeshModifier "
331  "please use the corresponding MeshGenerator instead");
332  mooseAssert(secondary_subdomain_id != Moose::INVALID_BLOCK_ID,
333  "Secondary subdomain id should exist by now. If you're using a MeshModifier "
334  "please use the corresponding MeshGenerator instead");
335 
336  // Higher-dimensional boundary elements
337  const BoundaryInfo & binfo = _mesh->get_boundary_info();
338 
339  for (auto side : elem->side_index_range())
340  if ((elem->processor_id() != p) &&
341  (binfo.has_boundary_id(elem, side, primary_boundary_id) ||
342  binfo.has_boundary_id(elem, side, secondary_boundary_id)))
343  coupled_elements.insert(std::make_pair(elem, _null_mat));
344 
345  // Lower dimensional subdomain elements
346  if ((elem->processor_id() != p) && (elem->subdomain_id() == primary_subdomain_id ||
347  elem->subdomain_id() == secondary_subdomain_id))
348  {
349  coupled_elements.insert(std::make_pair(elem, _null_mat));
350 
351 #ifndef NDEBUG
352  // let's do some safety checks
353  const Elem * const ip = elem->interior_parent();
354  mooseAssert(ip,
355  "We should have set interior parents for all of our lower-dimensional mortar "
356  "subdomains");
357  auto side = ip->which_side_am_i(elem);
358  auto bnd_id = elem->subdomain_id() == primary_subdomain_id ? primary_boundary_id
359  : secondary_boundary_id;
360  mooseAssert(_mesh->get_boundary_info().has_boundary_id(ip, side, bnd_id),
361  "The interior parent for the lower-dimensional element does not lie on the "
362  "boundary");
363 #endif
364  }
365  }
366  }
367  }
368  // For a static mesh (or for determining a sparsity pattern approximation on a displaced mesh) we
369  // can just ghost the coupled elements determined during mortar mesh generation
370  else if (amg)
371  {
372  for (const Elem * const elem : as_range(range_begin, range_end))
373  {
374  ghostMortarInterfaceCouplings(p, elem, coupled_elements, *amg);
375 
376  if (_ghost_point_neighbors)
377  ghostLowerDSecondaryElemPointNeighbors(
378  p, elem, coupled_elements, secondary_boundary_id, secondary_subdomain_id, *amg);
379  if (_ghost_higher_d_neighbors)
380  ghostHigherDNeighbors(
381  p, elem, coupled_elements, secondary_boundary_id, secondary_subdomain_id, *amg);
382  } // end for loop over input range
383  } // end if amg
384 }
385 
386 bool
387 AugmentSparsityOnInterface::operator>=(const RelationshipManager & other) const
388 {
389  if (auto asoi = dynamic_cast<const AugmentSparsityOnInterface *>(&other))
390  {
391  if (_primary_boundary_name == asoi->_primary_boundary_name &&
392  _secondary_boundary_name == asoi->_secondary_boundary_name &&
393  _primary_subdomain_name == asoi->_primary_subdomain_name &&
394  _secondary_subdomain_name == asoi->_secondary_subdomain_name &&
395  _ghost_point_neighbors >= asoi->_ghost_point_neighbors &&
396  _ghost_higher_d_neighbors >= asoi->_ghost_higher_d_neighbors && baseGreaterEqual(*asoi))
397  return true;
398  }
399  return false;
400 }
401 
402 std::unique_ptr<GhostingFunctor>
404 {
405  return _app.getFactory().copyConstruct(*this);
406 }
bool has_boundary_id(const Node *const node, const boundary_id_type id) const
RelationshipManagerType
Main types of Relationship Managers.
Definition: MooseTypes.h:876
const Elem * interior_parent() const
IntRange< unsigned short > side_index_range() const
const BoundaryID INVALID_BOUNDARY_ID
Definition: MooseTypes.C:24
unsigned int which_side_am_i(const Elem *e) const
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
FEProblemBase & feProblem()
Return a reference to this Executioner&#39;s FEProblemBase instance.
Definition: Executioner.C:112
MooseMesh * _moose_mesh
Pointer to the MooseMesh object.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
const BoundaryInfo & get_boundary_info() const
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...
Factory & getFactory()
Retrieve a writable reference to the Factory associated with this App.
Definition: MooseApp.h:396
This class is a container/interface for the objects involved in automatic generation of mortar spaces...
const SubdomainID INVALID_BLOCK_ID
Definition: MooseTypes.C:22
const MeshBase * getMeshPtr() const
Definition: MooseMesh.C:3193
uint8_t processor_id_type
AugmentSparsityOnInterface(MeshBase &mesh, boundary_id_type crack_boundary_lower, boundary_id_type crack_boundary_upper)
dof_id_type id() const
boundary_id_type BoundaryID
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
void find_point_neighbors(const Point &p, std::set< const Elem * > &neighbor_set) const
const std::unordered_map< dof_id_type, std::unordered_set< dof_id_type > > & mortarInterfaceCoupling() const
MooseApp & _app
The MOOSE application this is associated with.
Definition: MooseBase.h:84
virtual bool baseGreaterEqual(const RelationshipManager &rhs) const
Whether the base class provides more or the same amount and type of ghosting as the rhs...
Executioner * getExecutioner() const
Retrieve the Executioner for this App.
Definition: MooseApp.C:1483
bool on_boundary() const
void active_family_tree_by_neighbor(std::vector< const Elem * > &family, const Elem *neighbor, bool reset=true) const
virtual const Elem * elem_ptr(const dof_id_type i) const=0
const Elem * neighbor_ptr(unsigned int i) const
const bool _use_displaced_mesh
Which system this should go to (undisplaced or displaced)
RelationshipManagers are used for describing what kinds of non-local resources are needed for an obje...
subdomain_id_type subdomain_id() const
virtual unsigned short dim() const=0
const AutomaticMortarGeneration & getMortarInterface(const std::pair< BoundaryID, BoundaryID > &primary_secondary_boundary_pair, const std::pair< SubdomainID, SubdomainID > &primary_secondary_subdomain_pair, bool on_displaced) const
Return the undisplaced or displaced mortar generation object associated with the provided boundaries ...
static InputParameters validParams()
registerMooseObject("MooseApp", AugmentSparsityOnInterface)
virtual void operator()(const MeshBase::const_element_iterator &range_begin, const MeshBase::const_element_iterator &range_end, processor_id_type p, map_type &coupled_elements) override
virtual std::unique_ptr< GhostingFunctor > clone() const
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...
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
processor_id_type processor_id() const
BoundaryID getBoundaryID(const BoundaryName &boundary_name) const
Get the associated BoundaryID for the boundary name.
Definition: MooseMesh.C:1475
SubdomainID getSubdomainID(const SubdomainName &subdomain_name) const
Get the associated subdomain ID for the subdomain name.
Definition: MooseMesh.C:1514