https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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"
17
18#include "libmesh/quadrature.h"
19#include "libmesh/elem.h"
20#include "libmesh/point.h"
21
22namespace Moose
23{
24namespace Mortar
25{
29std::vector<unsigned int> getMortarSubElementNodeIndices(const Elem & parent_elem,
30 unsigned int sub_elem);
31
47void projectQPoints3d(const Elem * msm_elem,
48 const Elem * primal_elem,
49 unsigned int sub_elem_index,
50 const QBase & qrule_msm,
51 std::vector<Point> & q_pts);
52
62void mapQPoints3dFromReference(const Elem & mortar_segment_elem,
63 const MortarSegmentReferencePoints & reference_points,
64 const QBase & qrule_msm,
65 std::vector<Point> & secondary_q_pts,
66 std::vector<Point> & primary_q_pts);
67
89template <typename Iterators, typename Consumers, typename ActionFunctor>
90void
92 const Iterators & secondary_elems_to_mortar_segments,
93 Assembly & assembly,
94 SubProblem & subproblem,
95 FEProblemBase & fe_problem,
96 const AutomaticMortarGeneration & amg,
97 const bool displaced,
98 const Consumers & consumers,
99 const THREAD_ID tid,
100 const std::map<SubdomainID, std::deque<MaterialBase *>> & secondary_ip_sub_to_mats,
101 const std::map<SubdomainID, std::deque<MaterialBase *>> & primary_ip_sub_to_mats,
102 const std::deque<MaterialBase *> & secondary_boundary_mats,
103 const ActionFunctor act,
104 const bool reinit_mortar_user_objects)
105{
106 const auto & primary_secondary_boundary_id_pair = amg.primarySecondaryBoundaryIDPair();
107
108 const auto primary_boundary_id = primary_secondary_boundary_id_pair.first;
109 const auto secondary_boundary_id = primary_secondary_boundary_id_pair.second;
110
111 // For 3D mortar get index for retrieving sub-element info
112 unsigned int secondary_sub_elem_index = 0, primary_sub_elem_index = 0;
113 if (amg.dim() == 3)
114 {
115 secondary_sub_elem_index = amg.mortarSegmentMesh().get_elem_integer_index("secondary_sub_elem");
116 primary_sub_elem_index = amg.mortarSegmentMesh().get_elem_integer_index("primary_sub_elem");
117 }
118
119 // The mortar quadrature rule. Necessary for sizing the number of custom points for re-init'ing
120 // the secondary interior, primary interior, and secondary face elements
121 const auto & qrule_msm = assembly.qRuleMortar();
122
123 // The element Jacobian times weights
124 const auto & JxW_msm = assembly.jxWMortar();
125
126 // Set required material properties
127 std::unordered_set<unsigned int> needed_mat_props;
128 for (const auto & consumer : consumers)
129 {
130 const auto & mp_deps = consumer->getMatPropDependencies();
131 needed_mat_props.insert(mp_deps.begin(), mp_deps.end());
132 }
133 fe_problem.setActiveMaterialProperties(needed_mat_props, /*tid=*/tid);
134
135 // Loop through secondary elements, accumulating quadrature points for all corresponding mortar
136 // segments
137 for (const auto elem_to_msm : secondary_elems_to_mortar_segments)
138 {
139 const Elem * secondary_face_elem = subproblem.mesh().getMesh().elem_ptr(elem_to_msm->first);
140 // Set the secondary interior parent and side ids
141 const Elem * secondary_ip = secondary_face_elem->interior_parent();
142 unsigned int secondary_side_id = secondary_ip->which_side_am_i(secondary_face_elem);
143 const auto & secondary_ip_mats =
144 libmesh_map_find(secondary_ip_sub_to_mats, secondary_ip->subdomain_id());
145
146 const auto & msm_elems = elem_to_msm->second;
147
148 // Need to be able to check if there's edge dropping, in 3D we can't just compare
149
150 // Map mortar segment integration points to primary and secondary sides
151 // Note points for segments will be held contiguously to allow reinit without moving
152 // cleaner way to do this would be with contiguously allocated 2D array but would either
153 // need to move into vector for calling
154 std::vector<Point> secondary_xi_pts, primary_xi_pts;
155
156 std::vector<Real> JxW;
157
158#ifndef NDEBUG
159 unsigned int expected_length = 0;
160#endif
161
162 // Loop through contributing msm elements
163 for (const auto msm_elem : msm_elems)
164 {
165 // Initialize mortar segment quadrature and compute JxW
166 subproblem.reinitMortarElem(msm_elem, tid);
167
168 // Get a reference to the MortarSegmentInfo for this Elem.
169 const MortarSegmentInfo & msinfo = amg.mortarSegmentMeshElemToInfo().at(msm_elem);
170
171 if (msm_elem->dim() == 1)
172 {
173 for (unsigned int qp = 0; qp < qrule_msm->n_points(); qp++)
174 {
175 const Real eta = qrule_msm->qp(qp)(0);
176
177 // Map quadrature points to secondary side
178 const Real xi1_eta = 0.5 * (1 - eta) * msinfo.xi1_a + 0.5 * (1 + eta) * msinfo.xi1_b;
179 secondary_xi_pts.push_back(xi1_eta);
180
181 // Map quadrature points to primary side
182 const Real xi2_eta = 0.5 * (1 - eta) * msinfo.xi2_a + 0.5 * (1 + eta) * msinfo.xi2_b;
183 primary_xi_pts.push_back(xi2_eta);
184 }
185 }
186 else
187 {
188 if (amg.mortar3DQpMapping() == Mortar3DQuadraturePointMapping::REFERENCE_INTERPOLATION)
190 amg.mortarSegmentReferencePoints(*msm_elem),
191 *qrule_msm,
192 secondary_xi_pts,
193 primary_xi_pts);
194 else
195 {
196 // Map independently because the parent-face linearizations differ.
197 projectQPoints3d(msm_elem,
198 msinfo.secondary_elem,
199 secondary_sub_elem_index,
200 *qrule_msm,
201 secondary_xi_pts);
203 msm_elem, msinfo.primary_elem, primary_sub_elem_index, *qrule_msm, primary_xi_pts);
204 }
205 }
206
207 // If edge dropping case we need the mortar integration weights to compute dual shape
208 // functions
209 if (assembly.needDual())
210 {
211 const auto & coord_msm = assembly.mortarCoordTransformation();
212 for (const auto qp : make_range(qrule_msm->n_points()))
213 JxW.push_back(JxW_msm[qp] * coord_msm[qp]);
214 }
215
216#ifndef NDEBUG
217 // Verify that the expected number of quadrature points have been inserted
218 expected_length += qrule_msm->n_points();
219 mooseAssert(secondary_xi_pts.size() == expected_length,
220 "Fewer than expected secondary quadrature points");
221 mooseAssert(primary_xi_pts.size() == expected_length,
222 "Fewer than expected primary quadrature points");
223
224 if (assembly.needDual())
225 mooseAssert(JxW.size() == expected_length, "Fewer than expected JxW values computed");
226#endif
227 } // end loop over msm_elems
228
229 // Reinit dual shape coeffs if dual shape functions needed
230 // lindsayad: is there any need to make sure we do this on both reference and displaced?
231 if (assembly.needDual())
232 assembly.reinitDual(secondary_face_elem, secondary_xi_pts, JxW);
233
234 unsigned int n_segment = 0;
235
236 // Loop through contributing msm elements, computing residual and Jacobian this time
237 for (const auto msm_elem : msm_elems)
238 {
239 n_segment++;
240
241 // These will hold quadrature points for each segment
242 std::vector<Point> xi1_pts, xi2_pts;
243
244 // Get a reference to the MortarSegmentInfo for this Elem.
245 const MortarSegmentInfo & msinfo = amg.mortarSegmentMeshElemToInfo().at(msm_elem);
246
247 // Set the primary interior parent and side ids
248 const Elem * primary_ip = msinfo.primary_elem->interior_parent();
249 unsigned int primary_side_id = primary_ip->which_side_am_i(msinfo.primary_elem);
250 const auto & primary_ip_mats =
251 libmesh_map_find(primary_ip_sub_to_mats, primary_ip->subdomain_id());
252
253 // Compute a JxW for the actual mortar segment element (not the lower dimensional element on
254 // the secondary face!)
255 subproblem.reinitMortarElem(msm_elem, tid);
256
257 // Extract previously computed mapped quadrature points for secondary and primary face
258 // elements
259 const unsigned int start = (n_segment - 1) * qrule_msm->n_points();
260 const unsigned int end = n_segment * qrule_msm->n_points();
261 xi1_pts.insert(
262 xi1_pts.begin(), secondary_xi_pts.begin() + start, secondary_xi_pts.begin() + end);
263 xi2_pts.insert(xi2_pts.begin(), primary_xi_pts.begin() + start, primary_xi_pts.begin() + end);
264
265 const Elem * reinit_secondary_elem = secondary_ip;
266
267 // If we're on the displaced mesh, we need to get the corresponding undisplaced elem before
268 // calling fe_problem.reinitElemFaceRef
269 if (displaced)
270 reinit_secondary_elem = fe_problem.mesh().elemPtr(reinit_secondary_elem->id());
271
272 // NOTE to future developers: it can be tempting to try and change calls on fe_problem to
273 // calls on subproblem because it seems wasteful to reinit data on both the reference and
274 // displaced problems regardless of whether we are running on a "reference" or displaced
275 // mortar mesh. But making such changes opens a can of worms. For instance, a user may define
276 // constant material properties that are used by mortar constraints and not think about
277 // whether they should set `use_displaced_mesh` for the material (and indeed the user may want
278 // those constant properties usable by both reference and displaced consumer objects). If we
279 // reinit that material and we haven't reinit'd it's assembly member, then we will get things
280 // like segmentation faults. Moreover, one can easily imagine that a material may couple in
281 // variables so we need to make sure those are reinit'd too. So even though it's inefficient,
282 // it's safest to keep making calls on fe_problem instead of subproblem
283
284 // reinit the variables/residuals/jacobians on the secondary interior
285 fe_problem.reinitElemFaceRef(
286 reinit_secondary_elem, secondary_side_id, TOLERANCE, &xi1_pts, nullptr, tid);
287
288 const Elem * reinit_primary_elem = primary_ip;
289
290 // If we're on the displaced mesh, we need to get the corresponding undisplaced elem before
291 // calling fe_problem.reinitElemFaceRef
292 if (displaced)
293 reinit_primary_elem = fe_problem.mesh().elemPtr(reinit_primary_elem->id());
294
295 // reinit the variables/residuals/jacobians on the primary interior
296 fe_problem.reinitNeighborFaceRef(
297 reinit_primary_elem, primary_side_id, TOLERANCE, &xi2_pts, nullptr, tid);
298
299 // reinit neighbor materials, but be careful not to execute stateful materials since
300 // conceptually they don't make sense with mortar (they're not interpolary)
301 fe_problem.reinitMaterialsNeighbor(primary_ip->subdomain_id(),
302 /*tid=*/tid,
303 /*swap_stateful=*/false,
304 &primary_ip_mats);
305
306 // reinit the variables/residuals/jacobians on the lower dimensional element corresponding to
307 // the secondary face. This must be done last after the dof indices have been prepared for the
308 // secondary (element) and primary (neighbor)
309 subproblem.reinitLowerDElem(secondary_face_elem, /*tid=*/tid, &xi1_pts);
310
311 // All this does currently is sets the neighbor/primary lower dimensional elem in Assembly and
312 // computes its volume for potential use in the MortarConstraints. Solution continuity
313 // stabilization for example relies on being able to access the volume
314 subproblem.reinitNeighborLowerDElem(msinfo.primary_elem, tid);
315
316 // reinit higher-dimensional secondary face/boundary materials. Do this after we reinit
317 // lower-d variables in case we want to pull the lower-d variable values into the secondary
318 // face/boundary materials. Be careful not to execute stateful materials since conceptually
319 // they don't make sense with mortar (they're not interpolary)
320 fe_problem.reinitMaterialsFace(secondary_ip->subdomain_id(),
321 /*tid=*/tid,
322 /*swap_stateful=*/false,
323 &secondary_ip_mats);
324 fe_problem.reinitMaterialsBoundary(
325 secondary_boundary_id, /*tid=*/tid, /*swap_stateful=*/false, &secondary_boundary_mats);
326
327 if (reinit_mortar_user_objects)
328 fe_problem.reinitMortarUserObjects(primary_boundary_id, secondary_boundary_id, displaced);
329
330 act();
331
332 } // End loop over msm segments on secondary face elem
333 } // End loop over (active) secondary elems
334}
335
350template <typename Consumers>
351void
352setupMortarMaterials(const Consumers & consumers,
353 FEProblemBase & fe_problem,
354 const AutomaticMortarGeneration & amg,
355 const THREAD_ID tid,
356 std::map<SubdomainID, std::deque<MaterialBase *>> & secondary_ip_sub_to_mats,
357 std::map<SubdomainID, std::deque<MaterialBase *>> & primary_ip_sub_to_mats,
358 std::deque<MaterialBase *> & secondary_boundary_mats)
359{
360 secondary_ip_sub_to_mats.clear();
361 primary_ip_sub_to_mats.clear();
362 secondary_boundary_mats.clear();
363
364 auto & mat_warehouse = fe_problem.getRegularMaterialsWarehouse();
365 auto get_required_sub_mats =
366 [&mat_warehouse, tid, &consumers](
367 const SubdomainID sub_id,
368 const Moose::MaterialDataType mat_data_type) -> std::deque<MaterialBase *>
369 {
370 if (mat_warehouse[mat_data_type].hasActiveBlockObjects(sub_id, tid))
371 {
372 auto & sub_mats = mat_warehouse[mat_data_type].getActiveBlockObjects(sub_id, tid);
373 return MaterialBase::buildRequiredMaterials(consumers, sub_mats, /*allow_stateful=*/false);
374 }
375 else
376 return {};
377 };
378
379 // Construct secondary *block* materials container
380 const auto & secondary_ip_sub_ids = amg.secondaryIPSubIDs();
381 for (const auto secondary_ip_sub : secondary_ip_sub_ids)
382 secondary_ip_sub_to_mats.emplace(
383 secondary_ip_sub, get_required_sub_mats(secondary_ip_sub, Moose::FACE_MATERIAL_DATA));
384
385 // Construct primary *block* materials container
386 const auto & primary_ip_sub_ids = amg.primaryIPSubIDs();
387 for (const auto primary_ip_sub : primary_ip_sub_ids)
388 primary_ip_sub_to_mats.emplace(
389 primary_ip_sub, get_required_sub_mats(primary_ip_sub, Moose::NEIGHBOR_MATERIAL_DATA));
390
391 // Construct secondary *boundary* materials container
392 const auto & boundary_pr = amg.primarySecondaryBoundaryIDPair();
393 const auto secondary_boundary = boundary_pr.second;
394 if (mat_warehouse.hasActiveBoundaryObjects(secondary_boundary, tid))
395 {
396 auto & boundary_mats = mat_warehouse.getActiveBoundaryObjects(secondary_boundary, tid);
397 secondary_boundary_mats =
398 MaterialBase::buildRequiredMaterials(consumers, boundary_mats, /*allow_stateful=*/false);
399 }
400}
401}
402}
subdomain_id_type SubdomainID
unsigned int THREAD_ID
Definition MooseTypes.h:237
Point eta
Definition MortarUtils.C:60
Keeps track of stuff related to assembling.
Definition Assembly.h:101
const libMesh::QBase *const & qRuleMortar() const
Returns a reference to the quadrature rule for the mortar segments.
Definition Assembly.h:703
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:2309
const std::vector< Real > & jxWMortar() const
Returns a reference to JxW for mortar segment elements.
Definition Assembly.h:698
bool needDual() const
Indicates whether dual shape functions are used (computation is now repeated on each element so expen...
Definition Assembly.h:614
const MooseArray< Real > & mortarCoordTransformation() const
Returns the reference to the coordinate transformation coefficients on the mortar segment mesh.
Definition Assembly.h:285
This class is a container/interface for the objects involved in automatic generation of mortar spaces...
const MortarSegmentReferencePoints & mortarSegmentReferencePoints(const Elem &mortar_segment_elem) const
Return the parent-face reference coordinates for a mortar segment.
Mortar3DQuadraturePointMapping mortar3DQpMapping() const
Return the 3D mortar quadrature-point mapping method.
const std::pair< BoundaryID, BoundaryID > & primarySecondaryBoundaryIDPair() const
const std::set< SubdomainID > & secondaryIPSubIDs() const
const MeshBase & mortarSegmentMesh() const
const std::set< SubdomainID > & primaryIPSubIDs() const
const std::unordered_map< const Elem *, MortarSegmentInfo > & mortarSegmentMeshElemToInfo() const
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
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...
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 MaterialWarehouse & getRegularMaterialsWarehouse() const
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 MooseMesh & mesh() override
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.
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 reinitMaterialsBoundary(BoundaryID boundary_id, const THREAD_ID tid, bool swap_stateful=true, const std::deque< MaterialBase * > *reinit_mats=nullptr)
reinit materials on a boundary
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,...
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.
virtual Elem * elemPtr(const dof_id_type i)
Definition MooseMesh.C:3177
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition MooseMesh.C:3512
const std::map< SubdomainID, std::vector< std::shared_ptr< T > > > & getActiveBlockObjects(THREAD_ID tid=0) const
Generic class for solving transient nonlinear problems.
Definition SubProblem.h:79
virtual MooseMesh & mesh()=0
void reinitNeighborLowerDElem(const Elem *elem, const THREAD_ID tid=0)
reinitialize a neighboring lower dimensional element
Definition SubProblem.C:992
void reinitMortarElem(const Elem *elem, const THREAD_ID tid=0)
Reinit a mortar element to obtain a valid JxW.
Definition SubProblem.C:999
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:946
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
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:91
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...
std::vector< unsigned int > getMortarSubElementNodeIndices(const Elem &parent_elem, unsigned int sub_elem)
Return the node indices for a first-order sub-element of a parent face.
Definition MortarUtils.C:67
void mapQPoints3dFromReference(const Elem &mortar_segment_elem, const MortarSegmentReferencePoints &reference_points, const QBase &qrule_msm, std::vector< Point > &secondary_q_pts, std::vector< Point > &primary_q_pts)
3D mapping operator that interpolates stored parent reference points on each triangular mortar segmen...
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
MaterialDataType
MaterialData types.
Definition MooseTypes.h:746
@ NEIGHBOR_MATERIAL_DATA
Definition MooseTypes.h:750
@ FACE_MATERIAL_DATA
Definition MooseTypes.h:749
Holds xi^(1), xi^(2), and other data for a given mortar segment.
const Elem * primary_elem
const Elem * secondary_elem
Parent-face reference coordinates associated with the vertices of one triangular mortar segment.