https://mooseframework.inl.gov
Loading...
Searching...
No Matches
AugmentSparsityOnInterface.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
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
24AugmentSparsityOnInterface::validParams()
25{
27 params.addRequiredParam<BoundaryName>("primary_boundary",
28 "The name of the primary boundary sideset.");
29 params.addRequiredParam<BoundaryName>("secondary_boundary",
30 "The name of the secondary boundary sideset.");
31 params.addRequiredParam<SubdomainName>("primary_subdomain",
32 "The name of the primary lower dimensional subdomain.");
33 params.addRequiredParam<SubdomainName>("secondary_subdomain",
34 "The name of the secondary lower dimensional subdomain.");
35 params.addParam<bool>(
36 "ghost_point_neighbors",
37 false,
38 "Whether we should ghost point neighbors of secondary lower-dimensional elements and "
39 "also their interior parents and mortar interface couples for applications such as mortar "
40 "nodal auxiliary kernels and nodal-normal contact Jacobians.");
41 params.addParam<bool>(
42 "ghost_higher_d_neighbors",
43 false,
44 "Whether we should ghost higher-dimensional neighbors. This is necessary when we are doing "
45 "second order mortar with finite volume primal variables, because in order for the method to "
46 "be second order we must use cell gradients, which couples in the neighbor cells.");
47
48 // We want to wait until our mortar mesh has been built before trying to delete remote elements.
49 // And our mortar mesh cannot be built until the entire mesh has been generated. By setting this
50 // parameter to false we will make sure that any prepare_for_use calls during the mesh generation
51 // phase will not delete remote elements *and* we will set a flag on the moose mesh saying that we
52 // need to delete remote elements after the addition of late geometric ghosting functors
53 // (including this ghosting functor)
54 params.set<bool>("attach_geometric_early") = false;
55 return params;
56}
57
59 : RelationshipManager(params),
60 _primary_boundary_name(getParam<BoundaryName>("primary_boundary")),
61 _secondary_boundary_name(getParam<BoundaryName>("secondary_boundary")),
62 _primary_subdomain_name(getParam<SubdomainName>("primary_subdomain")),
63 _secondary_subdomain_name(getParam<SubdomainName>("secondary_subdomain")),
64 _is_coupling_functor(isType(Moose::RelationshipManagerType::COUPLING)),
65 _ghost_point_neighbors(getParam<bool>("ghost_point_neighbors")),
66 _ghost_higher_d_neighbors(getParam<bool>("ghost_higher_d_neighbors"))
67{
68}
69
71 : RelationshipManager(other),
72 _primary_boundary_name(other._primary_boundary_name),
73 _secondary_boundary_name(other._secondary_boundary_name),
74 _primary_subdomain_name(other._primary_subdomain_name),
75 _secondary_subdomain_name(other._secondary_subdomain_name),
76 _is_coupling_functor(other._is_coupling_functor),
77 _ghost_point_neighbors(other._ghost_point_neighbors),
78 _ghost_higher_d_neighbors(other._ghost_higher_d_neighbors)
79{
80}
81
82void
83AugmentSparsityOnInterface::internalInitWithMesh(const MeshBase &)
84{
85}
86
87std::string
88AugmentSparsityOnInterface::getInfo() const
89{
90 std::ostringstream oss;
91 oss << "AugmentSparsityOnInterface";
92 return oss.str();
93}
94
95void
96AugmentSparsityOnInterface::ghostMortarInterfaceCouplings(
97 const processor_id_type p,
98 const Elem * const elem,
99 map_type & coupled_elements,
100 const AutomaticMortarGeneration & amg) const
101{
102 // Look up elem in the mortar_interface_coupling data structure.
103 const auto & mic = amg.mortarInterfaceCoupling();
104 auto find_it = mic.find(elem->id());
105 if (find_it == mic.end())
106 return;
107
108 const auto & coupled_set = find_it->second;
109
110 for (const auto coupled_elem_id : coupled_set)
111 {
112 // During adaptivity, the coupling map can temporarily contain element ids absent from the
113 // current mesh. Mortar regeneration rebuilds the map and reinitializes ghosting.
114 const Elem * const coupled_elem = _mesh->query_elem_ptr(coupled_elem_id);
115 if (coupled_elem && coupled_elem->processor_id() != p)
116 coupled_elements.emplace(coupled_elem, _null_mat);
117 }
118}
119
120void
121AugmentSparsityOnInterface::ghostLowerDSecondaryElemPointNeighbors(
122 const processor_id_type p,
123 const Elem * const query_elem,
124 map_type & coupled_elements,
125 const SubdomainID secondary_subdomain_id,
126 const AutomaticMortarGeneration & amg) const
127{
128 // The query may be a secondary face, one of its volume parents, or a coupled primary parent.
129 // Start from every associated secondary face so the ghosting graph contains the complete
130 // nodal-normal support in either row direction.
131 std::vector<const Elem *> secondary_lower_elem_candidates;
132 if (query_elem->subdomain_id() == secondary_subdomain_id)
133 secondary_lower_elem_candidates.push_back(query_elem);
134 else
135 // The relationship manager's mesh supplies the current face topology during mesh refinement.
136 for (const auto side : query_elem->side_index_range())
137 if (const Elem * const lower_elem = _moose_mesh->getLowerDElem(query_elem, side);
138 lower_elem && lower_elem->subdomain_id() == secondary_subdomain_id)
139 secondary_lower_elem_candidates.push_back(lower_elem);
140
141 const auto & mic = amg.mortarInterfaceCoupling();
142 if (const auto find_it = mic.find(query_elem->id()); find_it != mic.end())
143 for (const auto coupled_elem_id : find_it->second)
144 if (const Elem * const coupled_elem = _mesh->query_elem_ptr(coupled_elem_id);
145 coupled_elem && coupled_elem->subdomain_id() == secondary_subdomain_id)
146 secondary_lower_elem_candidates.push_back(coupled_elem);
147
148 // Candidate sources can overlap, and mortar coupling can repeat a face for several segments.
149 // Track each face so it is ghosted once.
150 std::set<dof_id_type> secondary_lower_elems_handled;
151 for (const Elem * const candidate : secondary_lower_elem_candidates)
152 {
153 if (candidate->subactive())
154 continue;
155
156 // Resolve inactive ancestors to the active faces used by the current coupling graph.
157 std::vector<const Elem *> active_secondary_lower_elems;
158 candidate->active_family_tree(active_secondary_lower_elems);
159 for (const Elem * const secondary_lower_elem : active_secondary_lower_elems)
160 {
161 // Coupling entries can identify higher-dimensional primary elements. Only lower-dimensional
162 // elements in the secondary subdomain contribute to the secondary face star.
163 if (secondary_lower_elem->subdomain_id() != secondary_subdomain_id ||
164 !secondary_lower_elems_handled.insert(secondary_lower_elem->id()).second)
165 continue;
166
167 std::set<const Elem *> secondary_lower_elem_point_neighbors;
168 secondary_lower_elem->find_point_neighbors(secondary_lower_elem_point_neighbors);
169 secondary_lower_elem_point_neighbors.insert(secondary_lower_elem);
170
171 for (const Elem * const neigh : secondary_lower_elem_point_neighbors)
172 {
173 if (!neigh->active() || neigh->subdomain_id() != secondary_subdomain_id)
174 continue;
175
176 // Ghosting functors return only elements not owned by the requesting processor.
177 if (neigh->processor_id() != p)
178 coupled_elements.emplace(neigh, _null_mat);
179
180 // Every point-neighbor face contributes to the smoothed nodal normal, even when it has no
181 // mortar segment. Its interior parent therefore contributes displacement derivatives.
182 const Elem * const interior_parent = neigh->interior_parent();
183 mooseAssert(interior_parent,
184 "A lower-dimensional secondary mortar element must have an interior parent.");
185 if (interior_parent->processor_id() != p)
186 coupled_elements.emplace(interior_parent, _null_mat);
187
188 ghostMortarInterfaceCouplings(p, neigh, coupled_elements, amg);
189 }
190 }
191 }
192}
193
194void
195AugmentSparsityOnInterface::ghostHigherDNeighbors(const processor_id_type p,
196 const Elem * const query_elem,
197 map_type & coupled_elements,
198 const BoundaryID secondary_boundary_id,
199 const SubdomainID secondary_subdomain_id,
200 const AutomaticMortarGeneration & amg) const
201{
202 // The coupling is this for second order FV: secondary lower dimensional elem dofs will depend on
203 // higher-d secondary elem neighbor primal dofs and higher-d primary elem neighbor primal dofs.
204 // Higher dimensional primal dofs only depend on the secondary lower dimensional LM dofs for the
205 // current FV gap heat transfer use cases
206
207 if (query_elem->subdomain_id() != secondary_subdomain_id)
208 return;
209
210 const BoundaryInfo & binfo = _mesh->get_boundary_info();
211 const auto which_side = query_elem->interior_parent()->which_side_am_i(query_elem);
212 if (!binfo.has_boundary_id(query_elem->interior_parent(), which_side, secondary_boundary_id))
213 return;
214
215 const auto & mic = amg.mortarInterfaceCoupling();
216 auto find_it = mic.find(query_elem->id());
217 if (find_it == mic.end())
218 // Perhaps no projection onto primary
219 return;
220
221 const auto & lower_d_coupled_set = find_it->second;
222 for (const auto coupled_elem_id : lower_d_coupled_set)
223 {
224 auto * const coupled_elem = _mesh->elem_ptr(coupled_elem_id);
225 if (coupled_elem->dim() == query_elem->dim())
226 // lower-d-elem
227 continue;
228
229 std::vector<const Elem *> active_neighbors;
230
231 for (auto s : coupled_elem->side_index_range())
232 {
233 const Elem * const neigh = coupled_elem->neighbor_ptr(s);
234
235 if (!neigh || neigh == remote_elem)
236 continue;
237
238 // With any kind of neighbor, we need to couple to all the
239 // active descendants on our side.
240 neigh->active_family_tree_by_neighbor(active_neighbors, coupled_elem);
241
242 for (const auto & neighbor : active_neighbors)
243 if (neighbor->processor_id() != p)
244 {
245 mooseAssert(
246 neighbor->subdomain_id() != secondary_subdomain_id,
247 "Ensure that we aren't missing potential erasures from the secondary-to-msms map");
248 coupled_elements.emplace(neighbor, _null_mat);
249 }
250 }
251 }
252}
253
254void
255AugmentSparsityOnInterface::operator()(const MeshBase::const_element_iterator & range_begin,
256 const MeshBase::const_element_iterator & range_end,
257 const processor_id_type p,
258 map_type & coupled_elements)
259{
260 // We ask the user to pass boundary names instead of ids to our constraint object. However, We
261 // are unable to get the boundary ids from boundary names until we've attached the MeshBase object
262 // to the MooseMesh
263 const bool generating_mesh = !_moose_mesh->getMeshPtr();
264 const auto primary_boundary_id = generating_mesh
266 : _moose_mesh->getBoundaryID(_primary_boundary_name);
267 const auto secondary_boundary_id = generating_mesh
269 : _moose_mesh->getBoundaryID(_secondary_boundary_name);
270 const auto primary_subdomain_id = generating_mesh
272 : _moose_mesh->getSubdomainID(_primary_subdomain_name);
273 const auto secondary_subdomain_id = generating_mesh
275 : _moose_mesh->getSubdomainID(_secondary_subdomain_name);
276
277 const AutomaticMortarGeneration * const amg =
279 std::make_pair(primary_boundary_id, secondary_boundary_id),
280 std::make_pair(primary_subdomain_id, secondary_subdomain_id),
282 : nullptr;
283
284 // If we're on a dynamic mesh or we have not yet constructed the mortar mesh, we need to ghost the
285 // entire interface because we don't know a priori what elements will project onto what. We *do
286 // not* add the whole interface if we are a coupling functor because it is very expensive. This is
287 // because when building the sparsity pattern, we call through to the ghosting functors with one
288 // element at a time (and then below we do a loop over all the mesh's active elements). It's
289 // perhaps faster in this case to deal with mallocs coming out of MatSetValues, especially if the
290 // mesh displacements are relatively small
291 if ((!amg || _use_displaced_mesh) && !_is_coupling_functor)
292 {
293 for (const Elem * const elem : _mesh->active_element_ptr_range())
294 {
295 if (generating_mesh)
296 {
297 // We are still generating the mesh, so it's possible we don't even have the right boundary
298 // ids created yet! So we actually ghost all boundary elements and all lower dimensional
299 // elements who have parents on a boundary
300 if (elem->on_boundary())
301 coupled_elements.insert(std::make_pair(elem, _null_mat));
302 else if (const Elem * const ip = elem->interior_parent())
303 {
304 if (ip->on_boundary())
305 coupled_elements.insert(std::make_pair(elem, _null_mat));
306 }
307 }
308 else
309 {
310 // We've finished generating our mesh so we can be selective and only ghost elements lying
311 // in our lower-dimensional subdomains and their interior parents
312
313 mooseAssert(primary_boundary_id != Moose::INVALID_BOUNDARY_ID,
314 "Primary boundary id should exist by now.");
315 mooseAssert(secondary_boundary_id != Moose::INVALID_BOUNDARY_ID,
316 "Secondary boundary id should exist by now.");
317 mooseAssert(primary_subdomain_id != Moose::INVALID_BLOCK_ID,
318 "Primary subdomain id should exist by now.");
319 mooseAssert(secondary_subdomain_id != Moose::INVALID_BLOCK_ID,
320 "Secondary subdomain id should exist by now.");
321
322 // Higher-dimensional boundary elements
323 const BoundaryInfo & binfo = _mesh->get_boundary_info();
324
325 for (auto side : elem->side_index_range())
326 if ((elem->processor_id() != p) &&
327 (binfo.has_boundary_id(elem, side, primary_boundary_id) ||
328 binfo.has_boundary_id(elem, side, secondary_boundary_id)))
329 coupled_elements.insert(std::make_pair(elem, _null_mat));
330
331 // Lower dimensional subdomain elements
332 if ((elem->processor_id() != p) && (elem->subdomain_id() == primary_subdomain_id ||
333 elem->subdomain_id() == secondary_subdomain_id))
334 {
335 coupled_elements.insert(std::make_pair(elem, _null_mat));
336
337#ifndef NDEBUG
338 // let's do some safety checks
339 const Elem * const ip = elem->interior_parent();
340 mooseAssert(ip,
341 "We should have set interior parents for all of our lower-dimensional mortar "
342 "subdomains");
343 auto side = ip->which_side_am_i(elem);
344 auto bnd_id = elem->subdomain_id() == primary_subdomain_id ? primary_boundary_id
345 : secondary_boundary_id;
346 mooseAssert(_mesh->get_boundary_info().has_boundary_id(ip, side, bnd_id),
347 "The interior parent for the lower-dimensional element does not lie on the "
348 "boundary");
349#endif
350 }
351 }
352 }
353 }
354 // For a static mesh (or for determining a sparsity pattern approximation on a displaced mesh) we
355 // can just ghost the coupled elements determined during mortar mesh generation
356 else if (amg)
357 {
358 for (const Elem * const elem : as_range(range_begin, range_end))
359 {
360 ghostMortarInterfaceCouplings(p, elem, coupled_elements, *amg);
361
362 if (_ghost_point_neighbors)
363 ghostLowerDSecondaryElemPointNeighbors(
364 p, elem, coupled_elements, secondary_subdomain_id, *amg);
365 if (_ghost_higher_d_neighbors)
366 ghostHigherDNeighbors(
367 p, elem, coupled_elements, secondary_boundary_id, secondary_subdomain_id, *amg);
368 } // end for loop over input range
369 } // end if amg
370}
371
372bool
373AugmentSparsityOnInterface::operator>=(const RelationshipManager & other) const
374{
375 if (auto asoi = dynamic_cast<const AugmentSparsityOnInterface *>(&other))
376 {
377 if (_primary_boundary_name == asoi->_primary_boundary_name &&
378 _secondary_boundary_name == asoi->_secondary_boundary_name &&
379 _primary_subdomain_name == asoi->_primary_subdomain_name &&
380 _secondary_subdomain_name == asoi->_secondary_subdomain_name &&
381 _ghost_point_neighbors >= asoi->_ghost_point_neighbors &&
382 _ghost_higher_d_neighbors >= asoi->_ghost_higher_d_neighbors && baseGreaterEqual(*asoi))
383 return true;
384 }
385 return false;
386}
387
388std::unique_ptr<GhostingFunctor>
390{
391 return _app.getFactory().copyConstruct(*this);
392}
registerMooseObject("MooseApp", AugmentSparsityOnInterface)
boundary_id_type BoundaryID
subdomain_id_type SubdomainID
if(!dmm->_nl) SETERRQ(PETSC_COMM_WORLD
AugmentSparsityOnInterface(MeshBase &mesh, boundary_id_type crack_boundary_lower, boundary_id_type crack_boundary_upper)
virtual std::unique_ptr< GhostingFunctor > clone() const override
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
This class is a container/interface for the objects involved in automatic generation of mortar spaces...
const std::unordered_map< dof_id_type, std::unordered_set< dof_id_type > > & mortarInterfaceCoupling() const
FEProblemBase & feProblem()
Return a reference to this Executioner's FEProblemBase instance.
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 ...
std::unique_ptr< T > copyConstruct(const T &object)
Copy constructs the object object.
Definition Factory.h:358
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
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...
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
Executioner * getExecutioner() const
Retrieve the Executioner for this App.
Definition MooseApp.C:2024
Factory & getFactory()
Retrieve a writable reference to the Factory associated with this App.
Definition MooseApp.h:404
const MeshBase * getMeshPtr() const
Definition MooseMesh.C:3506
SubdomainID getSubdomainID(const SubdomainName &subdomain_name) const
Get the associated subdomain ID for the subdomain name.
Definition MooseMesh.C:1678
BoundaryID getBoundaryID(const BoundaryName &boundary_name) const
Get the associated BoundaryID for the boundary name.
Definition MooseMesh.C:1639
MooseApp & _app
The MOOSE application this is associated with.
Definition MooseBase.h:375
RelationshipManagers are used for describing what kinds of non-local resources are needed for an obje...
const bool _use_displaced_mesh
Which system this should go to (undisplaced or displaced)
virtual bool baseGreaterEqual(const RelationshipManager &rhs) const
Whether the base class provides more or the same amount and type of ghosting as the rhs.
static InputParameters validParams()
MooseMesh * _moose_mesh
Pointer to the MooseMesh object.
processor_id_type processor_id() const
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
RelationshipManagerType
Main types of Relationship Managers.
const BoundaryID INVALID_BOUNDARY_ID
Definition MooseTypes.C:22
const SubdomainID INVALID_BLOCK_ID
Definition MooseTypes.C:20
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)