https://mooseframework.inl.gov
MortarUtils.h
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 #pragma once
11 
12 #include "Assembly.h"
13 #include "FEProblemBase.h"
14 #include "MaterialBase.h"
15 #include "MaterialWarehouse.h"
16 
17 #include "libmesh/quadrature.h"
18 #include "libmesh/elem.h"
19 #include "libmesh/point.h"
20 
21 namespace Moose
22 {
23 namespace Mortar
24 {
40 void projectQPoints3d(const Elem * msm_elem,
41  const Elem * primal_elem,
42  unsigned int sub_elem_index,
43  const QBase & qrule_msm,
44  std::vector<Point> & q_pts);
45 
67 template <typename Iterators, typename Consumers, typename ActionFunctor>
68 void
70  const Iterators & secondary_elems_to_mortar_segments,
71  Assembly & assembly,
72  SubProblem & subproblem,
73  FEProblemBase & fe_problem,
74  const AutomaticMortarGeneration & amg,
75  const bool displaced,
76  const Consumers & consumers,
77  const THREAD_ID tid,
78  const std::map<SubdomainID, std::deque<MaterialBase *>> & secondary_ip_sub_to_mats,
79  const std::map<SubdomainID, std::deque<MaterialBase *>> & primary_ip_sub_to_mats,
80  const std::deque<MaterialBase *> & secondary_boundary_mats,
81  const ActionFunctor act,
82  const bool reinit_mortar_user_objects)
83 {
84  const auto & primary_secondary_boundary_id_pair = amg.primarySecondaryBoundaryIDPair();
85 
86  const auto primary_boundary_id = primary_secondary_boundary_id_pair.first;
87  const auto secondary_boundary_id = primary_secondary_boundary_id_pair.second;
88 
89  // For 3D mortar get index for retrieving sub-element info
90  unsigned int secondary_sub_elem_index = 0, primary_sub_elem_index = 0;
91  if (amg.dim() == 3)
92  {
93  secondary_sub_elem_index = amg.mortarSegmentMesh().get_elem_integer_index("secondary_sub_elem");
94  primary_sub_elem_index = amg.mortarSegmentMesh().get_elem_integer_index("primary_sub_elem");
95  }
96 
97  // The mortar quadrature rule. Necessary for sizing the number of custom points for re-init'ing
98  // the secondary interior, primary interior, and secondary face elements
99  const auto & qrule_msm = assembly.qRuleMortar();
100 
101  // The element Jacobian times weights
102  const auto & JxW_msm = assembly.jxWMortar();
103 
104  // Set required material properties
105  std::unordered_set<unsigned int> needed_mat_props;
106  for (const auto & consumer : consumers)
107  {
108  const auto & mp_deps = consumer->getMatPropDependencies();
109  needed_mat_props.insert(mp_deps.begin(), mp_deps.end());
110  }
111  fe_problem.setActiveMaterialProperties(needed_mat_props, /*tid=*/tid);
112 
113  // Loop through secondary elements, accumulating quadrature points for all corresponding mortar
114  // segments
115  for (const auto elem_to_msm : secondary_elems_to_mortar_segments)
116  {
117  const Elem * secondary_face_elem = subproblem.mesh().getMesh().elem_ptr(elem_to_msm->first);
118  // Set the secondary interior parent and side ids
119  const Elem * secondary_ip = secondary_face_elem->interior_parent();
120  unsigned int secondary_side_id = secondary_ip->which_side_am_i(secondary_face_elem);
121  const auto & secondary_ip_mats =
122  libmesh_map_find(secondary_ip_sub_to_mats, secondary_ip->subdomain_id());
123 
124  const auto & msm_elems = elem_to_msm->second;
125 
126  // Need to be able to check if there's edge dropping, in 3D we can't just compare
127 
128  // Map mortar segment integration points to primary and secondary sides
129  // Note points for segments will be held contiguously to allow reinit without moving
130  // cleaner way to do this would be with contiguously allocated 2D array but would either
131  // need to move into vector for calling
132  std::vector<Point> secondary_xi_pts, primary_xi_pts;
133 
134  std::vector<Real> JxW;
135 
136 #ifndef NDEBUG
137  unsigned int expected_length = 0;
138 #endif
139 
140  // Loop through contributing msm elements
141  for (const auto msm_elem : msm_elems)
142  {
143  // Initialize mortar segment quadrature and compute JxW
144  subproblem.reinitMortarElem(msm_elem, tid);
145 
146  // Get a reference to the MortarSegmentInfo for this Elem.
147  const MortarSegmentInfo & msinfo = amg.mortarSegmentMeshElemToInfo().at(msm_elem);
148 
149  if (msm_elem->dim() == 1)
150  {
151  for (unsigned int qp = 0; qp < qrule_msm->n_points(); qp++)
152  {
153  const Real eta = qrule_msm->qp(qp)(0);
154 
155  // Map quadrature points to secondary side
156  const Real xi1_eta = 0.5 * (1 - eta) * msinfo.xi1_a + 0.5 * (1 + eta) * msinfo.xi1_b;
157  secondary_xi_pts.push_back(xi1_eta);
158 
159  // Map quadrature points to primary side
160  const Real xi2_eta = 0.5 * (1 - eta) * msinfo.xi2_a + 0.5 * (1 + eta) * msinfo.xi2_b;
161  primary_xi_pts.push_back(xi2_eta);
162  }
163  }
164  else
165  {
166  // Now that has_primary logic gone, could combine these to make more efficient
167  projectQPoints3d(msm_elem,
168  msinfo.secondary_elem,
169  secondary_sub_elem_index,
170  *qrule_msm,
171  secondary_xi_pts);
173  msm_elem, msinfo.primary_elem, primary_sub_elem_index, *qrule_msm, primary_xi_pts);
174  }
175 
176  // If edge dropping case we need JxW on the msm to compute dual shape functions
177  if (assembly.needDual())
178  std::copy(std::begin(JxW_msm), std::end(JxW_msm), std::back_inserter(JxW));
179 
180 #ifndef NDEBUG
181  // Verify that the expected number of quadrature points have been inserted
182  expected_length += qrule_msm->n_points();
183  mooseAssert(secondary_xi_pts.size() == expected_length,
184  "Fewer than expected secondary quadrature points");
185  mooseAssert(primary_xi_pts.size() == expected_length,
186  "Fewer than expected primary quadrature points");
187 
188  if (assembly.needDual())
189  mooseAssert(JxW.size() == expected_length, "Fewer than expected JxW values computed");
190 #endif
191  } // end loop over msm_elems
192 
193  // Reinit dual shape coeffs if dual shape functions needed
194  // lindsayad: is there any need to make sure we do this on both reference and displaced?
195  if (assembly.needDual())
196  assembly.reinitDual(secondary_face_elem, secondary_xi_pts, JxW);
197 
198  unsigned int n_segment = 0;
199 
200  // Loop through contributing msm elements, computing residual and Jacobian this time
201  for (const auto msm_elem : msm_elems)
202  {
203  n_segment++;
204 
205  // These will hold quadrature points for each segment
206  std::vector<Point> xi1_pts, xi2_pts;
207 
208  // Get a reference to the MortarSegmentInfo for this Elem.
209  const MortarSegmentInfo & msinfo = amg.mortarSegmentMeshElemToInfo().at(msm_elem);
210 
211  // Set the primary interior parent and side ids
212  const Elem * primary_ip = msinfo.primary_elem->interior_parent();
213  unsigned int primary_side_id = primary_ip->which_side_am_i(msinfo.primary_elem);
214  const auto & primary_ip_mats =
215  libmesh_map_find(primary_ip_sub_to_mats, primary_ip->subdomain_id());
216 
217  // Compute a JxW for the actual mortar segment element (not the lower dimensional element on
218  // the secondary face!)
219  subproblem.reinitMortarElem(msm_elem, tid);
220 
221  // Extract previously computed mapped quadrature points for secondary and primary face
222  // elements
223  const unsigned int start = (n_segment - 1) * qrule_msm->n_points();
224  const unsigned int end = n_segment * qrule_msm->n_points();
225  xi1_pts.insert(
226  xi1_pts.begin(), secondary_xi_pts.begin() + start, secondary_xi_pts.begin() + end);
227  xi2_pts.insert(xi2_pts.begin(), primary_xi_pts.begin() + start, primary_xi_pts.begin() + end);
228 
229  const Elem * reinit_secondary_elem = secondary_ip;
230 
231  // If we're on the displaced mesh, we need to get the corresponding undisplaced elem before
232  // calling fe_problem.reinitElemFaceRef
233  if (displaced)
234  reinit_secondary_elem = fe_problem.mesh().elemPtr(reinit_secondary_elem->id());
235 
236  // NOTE to future developers: it can be tempting to try and change calls on fe_problem to
237  // calls on subproblem because it seems wasteful to reinit data on both the reference and
238  // displaced problems regardless of whether we are running on a "reference" or displaced
239  // mortar mesh. But making such changes opens a can of worms. For instance, a user may define
240  // constant material properties that are used by mortar constraints and not think about
241  // whether they should set `use_displaced_mesh` for the material (and indeed the user may want
242  // those constant properties usable by both reference and displaced consumer objects). If we
243  // reinit that material and we haven't reinit'd it's assembly member, then we will get things
244  // like segmentation faults. Moreover, one can easily imagine that a material may couple in
245  // variables so we need to make sure those are reinit'd too. So even though it's inefficient,
246  // it's safest to keep making calls on fe_problem instead of subproblem
247 
248  // reinit the variables/residuals/jacobians on the secondary interior
249  fe_problem.reinitElemFaceRef(
250  reinit_secondary_elem, secondary_side_id, TOLERANCE, &xi1_pts, nullptr, tid);
251 
252  const Elem * reinit_primary_elem = primary_ip;
253 
254  // If we're on the displaced mesh, we need to get the corresponding undisplaced elem before
255  // calling fe_problem.reinitElemFaceRef
256  if (displaced)
257  reinit_primary_elem = fe_problem.mesh().elemPtr(reinit_primary_elem->id());
258 
259  // reinit the variables/residuals/jacobians on the primary interior
260  fe_problem.reinitNeighborFaceRef(
261  reinit_primary_elem, primary_side_id, TOLERANCE, &xi2_pts, nullptr, tid);
262 
263  // reinit neighbor materials, but be careful not to execute stateful materials since
264  // conceptually they don't make sense with mortar (they're not interpolary)
265  fe_problem.reinitMaterialsNeighbor(primary_ip->subdomain_id(),
266  /*tid=*/tid,
267  /*swap_stateful=*/false,
268  &primary_ip_mats);
269 
270  // reinit the variables/residuals/jacobians on the lower dimensional element corresponding to
271  // the secondary face. This must be done last after the dof indices have been prepared for the
272  // secondary (element) and primary (neighbor)
273  subproblem.reinitLowerDElem(secondary_face_elem, /*tid=*/tid, &xi1_pts);
274 
275  // All this does currently is sets the neighbor/primary lower dimensional elem in Assembly and
276  // computes its volume for potential use in the MortarConstraints. Solution continuity
277  // stabilization for example relies on being able to access the volume
278  subproblem.reinitNeighborLowerDElem(msinfo.primary_elem, tid);
279 
280  // reinit higher-dimensional secondary face/boundary materials. Do this after we reinit
281  // lower-d variables in case we want to pull the lower-d variable values into the secondary
282  // face/boundary materials. Be careful not to execute stateful materials since conceptually
283  // they don't make sense with mortar (they're not interpolary)
284  fe_problem.reinitMaterialsFace(secondary_ip->subdomain_id(),
285  /*tid=*/tid,
286  /*swap_stateful=*/false,
287  &secondary_ip_mats);
288  fe_problem.reinitMaterialsBoundary(
289  secondary_boundary_id, /*tid=*/tid, /*swap_stateful=*/false, &secondary_boundary_mats);
290 
291  if (reinit_mortar_user_objects)
292  fe_problem.reinitMortarUserObjects(primary_boundary_id, secondary_boundary_id, displaced);
293 
294  act();
295 
296  } // End loop over msm segments on secondary face elem
297  } // End loop over (active) secondary elems
298 }
299 
314 template <typename Consumers>
315 void
316 setupMortarMaterials(const Consumers & consumers,
317  FEProblemBase & fe_problem,
318  const AutomaticMortarGeneration & amg,
319  const THREAD_ID tid,
320  std::map<SubdomainID, std::deque<MaterialBase *>> & secondary_ip_sub_to_mats,
321  std::map<SubdomainID, std::deque<MaterialBase *>> & primary_ip_sub_to_mats,
322  std::deque<MaterialBase *> & secondary_boundary_mats)
323 {
324  secondary_ip_sub_to_mats.clear();
325  primary_ip_sub_to_mats.clear();
326  secondary_boundary_mats.clear();
327 
328  auto & mat_warehouse = fe_problem.getRegularMaterialsWarehouse();
329  auto get_required_sub_mats =
330  [&mat_warehouse, tid, &consumers](
331  const SubdomainID sub_id,
332  const Moose::MaterialDataType mat_data_type) -> std::deque<MaterialBase *>
333  {
334  if (mat_warehouse[mat_data_type].hasActiveBlockObjects(sub_id, tid))
335  {
336  auto & sub_mats = mat_warehouse[mat_data_type].getActiveBlockObjects(sub_id, tid);
337  return MaterialBase::buildRequiredMaterials(consumers, sub_mats, /*allow_stateful=*/false);
338  }
339  else
340  return {};
341  };
342 
343  // Construct secondary *block* materials container
344  const auto & secondary_ip_sub_ids = amg.secondaryIPSubIDs();
345  for (const auto secondary_ip_sub : secondary_ip_sub_ids)
346  secondary_ip_sub_to_mats.emplace(
347  secondary_ip_sub, get_required_sub_mats(secondary_ip_sub, Moose::FACE_MATERIAL_DATA));
348 
349  // Construct primary *block* materials container
350  const auto & primary_ip_sub_ids = amg.primaryIPSubIDs();
351  for (const auto primary_ip_sub : primary_ip_sub_ids)
352  primary_ip_sub_to_mats.emplace(
353  primary_ip_sub, get_required_sub_mats(primary_ip_sub, Moose::NEIGHBOR_MATERIAL_DATA));
354 
355  // Construct secondary *boundary* materials container
356  const auto & boundary_pr = amg.primarySecondaryBoundaryIDPair();
357  const auto secondary_boundary = boundary_pr.second;
358  if (mat_warehouse.hasActiveBoundaryObjects(secondary_boundary, tid))
359  {
360  auto & boundary_mats = mat_warehouse.getActiveBoundaryObjects(secondary_boundary, tid);
361  secondary_boundary_mats =
362  MaterialBase::buildRequiredMaterials(consumers, boundary_mats, /*allow_stateful=*/false);
363  }
364 }
365 }
366 }
virtual MooseMesh & mesh()=0
void setActiveMaterialProperties(const std::unordered_set< unsigned int > &mat_prop_ids, const THREAD_ID tid)
Record and set the material properties required by the current computing thread.
void reinitNeighborLowerDElem(const Elem *elem, const THREAD_ID tid=0)
reinitialize a neighboring lower dimensional element
Definition: SubProblem.C:987
const std::pair< BoundaryID, BoundaryID > & primarySecondaryBoundaryIDPair() const
virtual void reinitLowerDElem(const Elem *lower_d_elem, const THREAD_ID tid, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr)
Definition: SubProblem.C:957
Keeps track of stuff related to assembling.
Definition: Assembly.h:101
const std::map< SubdomainID, std::vector< std::shared_ptr< T > > > & getActiveBlockObjects(THREAD_ID tid=0) const
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:3108
void setupMortarMaterials(const Consumers &consumers, FEProblemBase &fe_problem, const AutomaticMortarGeneration &amg, const THREAD_ID tid, std::map< SubdomainID, std::deque< MaterialBase *>> &secondary_ip_sub_to_mats, std::map< SubdomainID, std::deque< MaterialBase *>> &primary_ip_sub_to_mats, std::deque< MaterialBase *> &secondary_boundary_mats)
This function creates containers of materials necessary to execute the mortar method for a supplied s...
Definition: MortarUtils.h:316
MaterialDataType
MaterialData types.
Definition: MooseTypes.h:691
void reinitMortarUserObjects(BoundaryID primary_boundary_id, BoundaryID secondary_boundary_id, bool displaced)
Call reinit on mortar user objects with matching primary boundary ID, secondary boundary ID...
void reinitMaterialsBoundary(BoundaryID boundary_id, const THREAD_ID tid, bool swap_stateful=true, const std::deque< MaterialBase *> *reinit_mats=nullptr)
reinit materials on a boundary
const libMesh::QBase *const & qRuleMortar() const
Returns a reference to the quadrature rule for the mortar segments.
Definition: Assembly.h:683
const std::set< SubdomainID > & primaryIPSubIDs() const
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
const MaterialWarehouse & getRegularMaterialsWarehouse() const
void reinitMaterialsFace(SubdomainID blk_id, const THREAD_ID tid, bool swap_stateful=true, const std::deque< MaterialBase *> *reinit_mats=nullptr)
reinit materials on element faces
const std::set< SubdomainID > & secondaryIPSubIDs() const
This class is a container/interface for the objects involved in automatic generation of mortar spaces...
virtual void reinitElemFaceRef(const Elem *elem, unsigned int side, Real tolerance, const std::vector< Point > *const pts, const std::vector< Real > *const weights=nullptr, const THREAD_ID tid=0) override
reinitialize FE objects on a given element on a given side at a given set of reference points and the...
const Elem * primary_elem
bool needDual() const
Indicates whether dual shape functions are used (computation is now repeated on each element so expen...
Definition: Assembly.h:584
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3443
void reinitMaterialsNeighbor(SubdomainID blk_id, const THREAD_ID tid, bool swap_stateful=true, const std::deque< MaterialBase *> *reinit_mats=nullptr)
reinit materials on the neighboring element face
virtual void reinitNeighborFaceRef(const Elem *neighbor_elem, unsigned int neighbor_side, Real tolerance, const std::vector< Point > *const pts, const std::vector< Real > *const weights=nullptr, const THREAD_ID tid=0) override
reinitialize FE objects on a given neighbor element on a given side at a given set of reference point...
void loopOverMortarSegments(const Iterators &secondary_elems_to_mortar_segments, Assembly &assembly, SubProblem &subproblem, FEProblemBase &fe_problem, const AutomaticMortarGeneration &amg, const bool displaced, const Consumers &consumers, const THREAD_ID tid, const std::map< SubdomainID, std::deque< MaterialBase *>> &secondary_ip_sub_to_mats, const std::map< SubdomainID, std::deque< MaterialBase *>> &primary_ip_sub_to_mats, const std::deque< MaterialBase *> &secondary_boundary_mats, const ActionFunctor act, const bool reinit_mortar_user_objects)
This method will loop over pairs of secondary elements and their corresponding mortar segments...
Definition: MortarUtils.h:69
const Elem * secondary_elem
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Holds xi^(1), xi^(2), and other data for a given mortar segment.
Generic class for solving transient nonlinear problems.
Definition: SubProblem.h:78
static std::deque< MaterialBase * > buildRequiredMaterials(const Consumers &mat_consumers, const std::vector< std::shared_ptr< MaterialBase >> &mats, const bool allow_stateful)
Build the materials required by a set of consumer objects.
Definition: MaterialBase.h:522
const std::unordered_map< const Elem *, MortarSegmentInfo > & mortarSegmentMeshElemToInfo() const
virtual MooseMesh & mesh() override
const MeshBase & mortarSegmentMesh() const
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
const std::vector< Real > & jxWMortar() const
Returns a reference to JxW for mortar segment elements.
Definition: Assembly.h:678
void reinitMortarElem(const Elem *elem, const THREAD_ID tid=0)
Reinit a mortar element to obtain a valid JxW.
Definition: SubProblem.C:994
unsigned int THREAD_ID
Definition: MooseTypes.h:209
void projectQPoints3d(const Elem *msm_elem, const Elem *primal_elem, unsigned int sub_elem_index, const QBase &qrule_msm, std::vector< Point > &q_pts)
3D projection operator for mapping qpoints on mortar segments to secondary or primary elements ...
Definition: MortarUtils.C:26
void reinitDual(const Elem *elem, const std::vector< Point > &pts, const std::vector< Real > &JxW)
Reintialize dual basis coefficients based on a customized quadrature rule.
Definition: Assembly.C:2275