https://mooseframework.inl.gov
Loading...
Searching...
No Matches
Enumerations | Functions
Moose::Mortar::Contact Namespace Reference

Enumerations

enum class  FrictionProjectionDegree { ONE , TWO }
 

Functions

template<typename T >
T augmentedNormalPressure (const T &normal_pressure, const T &scaled_normal_gap)
 Return the augmented normal pressure p_n - C_n g_bar.
 
template<typename T >
T coulombFrictionRadius (const T &friction_coefficient, const T &augmented_normal_pressure)
 Return the nonnegative Coulomb friction radius.
 
template<typename T , std::size_t N>
std::array< T, N > projectToClosedSphere (const std::array< T, N > &vector, const T &radius)
 Project vector onto a closed sphere centered at the origin with radius radius.
 
template<typename T , std::size_t N>
std::array< T, N > alartCurnierFrictionResidual (const std::array< T, N > &tangential_pressure, const std::array< T, N > &augmented_tangential_pressure, const T &radius)
 Return the degree-one Alart-Curnier friction residual p_t - Proj_{B_radius}(q_t).
 
template<typename T , std::size_t N>
std::array< T, N > alartCurnierFrictionResidual (const std::array< T, N > &tangential_pressure, const std::array< T, N > &augmented_tangential_pressure, const T &radius, const T &normal_pressure, const T &epsilon)
 Same as the three-argument alartCurnierFrictionResidual, but additionally short-circuits to the trivial identity residual whenever the raw, unaugmented normal contact pressure normal_pressure falls below epsilon, mirroring the epsilon-gated hueberStadlerWohlmuthFrictionResidual overload below so both friction-residual degrees apply the same active-set transition guard.
 
template<typename T , std::size_t N>
std::array< T, N > hueberStadlerWohlmuthFrictionResidual (const std::array< T, N > &tangential_pressure, const std::array< T, N > &augmented_tangential_pressure, const T &radius)
 Return the degree-two Hueber-Stadler-Wohlmuth friction residual max(radius, ||q_t||) p_t - radius q_t.
 
template<typename T , std::size_t N>
std::array< T, N > hueberStadlerWohlmuthFrictionResidual (const std::array< T, N > &tangential_pressure, const std::array< T, N > &augmented_tangential_pressure, const T &radius, const T &normal_pressure, const T &epsilon)
 Same as the three-argument hueberStadlerWohlmuthFrictionResidual, but additionally short-circuits to the trivial identity residual whenever the raw, unaugmented normal contact pressure normal_pressure falls below epsilon.
 
template<typename T , std::size_t N>
std::array< T, N > frictionalContactResidual (const std::array< T, N > &tangential_pressure, const std::array< T, N > &tangential_velocity, const T &c_t, const T &dt, const T &normal_pressure, const T &scaled_normal_gap, const T &friction_coefficient, const T &epsilon, const FrictionProjectionDegree projection_degree)
 Compute the epsilon-gated frictional residual for a mortar contact node.
 
template<typename T >
void communicateVelocities (std::unordered_map< const DofObject *, T > &dof_map, const MooseMesh &mesh, const bool nodal, const Parallel::Communicator &communicator, const bool send_data_back)
 This function is used to communicate velocities across processes.
 
void communicateR2T (std::unordered_map< const DofObject *, ADRankTwoTensor > &dof_map_adr2t, const MooseMesh &mesh, const bool nodal, const Parallel::Communicator &communicator, const bool send_data_back)
 This function is used to communicate velocities across processes.
 
template<typename T >
void communicateRealObject (std::unordered_map< const DofObject *, T > &dof_to_adreal, const MooseMesh &mesh, const bool nodal, const Parallel::Communicator &communicator, const bool send_data_back)
 
void communicateGaps (std::unordered_map< const DofObject *, std::pair< ADReal, Real > > &dof_to_weighted_gap, const MooseMesh &mesh, bool nodal, bool normalize_c, const Parallel::Communicator &communicator, bool send_data_back)
 This function is used to communicate gaps across processes.
 

Enumeration Type Documentation

◆ FrictionProjectionDegree

Function Documentation

◆ alartCurnierFrictionResidual() [1/2]

template<typename T , std::size_t N>
std::array< T, N > Moose::Mortar::Contact::alartCurnierFrictionResidual ( const std::array< T, N > &  tangential_pressure,
const std::array< T, N > &  augmented_tangential_pressure,
const T radius 
)

Return the degree-one Alart-Curnier friction residual p_t - Proj_{B_radius}(q_t).

Parameters
tangential_pressureThe tangential contact pressure p_t
augmented_tangential_pressureThe velocity-augmented tangential pressure q_t (see frictionalContactResidual)
radiusThe Coulomb friction radius (see coulombFrictionRadius)
Returns
The degree-one Alart-Curnier friction residual

Definition at line 127 of file MortarContactUtils.h.

130{
131 const auto projection = projectToClosedSphere(augmented_tangential_pressure, radius);
132 std::array<T, N> residual;
133 for (const auto i : index_range(residual))
134 residual[i] = tangential_pressure[i] - projection[i];
135 return residual;
136}
std::array< T, N > projectToClosedSphere(const std::array< T, N > &vector, const T &radius)
Project vector onto a closed sphere centered at the origin with radius radius.

Referenced by alartCurnierFrictionResidual(), frictionalContactResidual(), TYPED_TEST(), TYPED_TEST(), TYPED_TEST(), TYPED_TEST(), and TYPED_TEST().

◆ alartCurnierFrictionResidual() [2/2]

template<typename T , std::size_t N>
std::array< T, N > Moose::Mortar::Contact::alartCurnierFrictionResidual ( const std::array< T, N > &  tangential_pressure,
const std::array< T, N > &  augmented_tangential_pressure,
const T radius,
const T normal_pressure,
const T epsilon 
)

Same as the three-argument alartCurnierFrictionResidual, but additionally short-circuits to the trivial identity residual whenever the raw, unaugmented normal contact pressure normal_pressure falls below epsilon, mirroring the epsilon-gated hueberStadlerWohlmuthFrictionResidual overload below so both friction-residual degrees apply the same active-set transition guard.

Parameters
tangential_pressureThe tangential contact pressure p_t
augmented_tangential_pressureThe velocity-augmented tangential pressure q_t (see frictionalContactResidual)
radiusThe Coulomb friction radius (see coulombFrictionRadius)
normal_pressureThe raw, unaugmented normal contact pressure
epsilonThe minimum normal contact pressure required to enable frictional enforcement
Returns
The epsilon-gated, degree-one Alart-Curnier friction residual

Definition at line 154 of file MortarContactUtils.h.

159{
160 if (normal_pressure < epsilon)
161 return tangential_pressure;
162
163 return alartCurnierFrictionResidual(tangential_pressure, augmented_tangential_pressure, radius);
164}
std::array< T, N > alartCurnierFrictionResidual(const std::array< T, N > &tangential_pressure, const std::array< T, N > &augmented_tangential_pressure, const T &radius)
Return the degree-one Alart-Curnier friction residual p_t - Proj_{B_radius}(q_t).

◆ augmentedNormalPressure()

template<typename T >
T Moose::Mortar::Contact::augmentedNormalPressure ( const T normal_pressure,
const T scaled_normal_gap 
)

Return the augmented normal pressure p_n - C_n g_bar.

Parameters
normal_pressureThe normal contact pressure p_n
scaled_normal_gapThe normal gap g_bar scaled by a factor C_n, intended to bring it onto the order of the contact pressure for a well-conditioned complementarity function
Returns
The augmented normal pressure

Definition at line 77 of file MortarContactUtils.h.

78{
79 return normal_pressure - scaled_normal_gap;
80}

Referenced by frictionalContactResidual(), and TYPED_TEST().

◆ communicateGaps()

void Moose::Mortar::Contact::communicateGaps ( std::unordered_map< const DofObject *, std::pair< ADReal, Real > > &  dof_to_weighted_gap,
const MooseMesh mesh,
bool  nodal,
bool  normalize_c,
const Parallel::Communicator &  communicator,
bool  send_data_back 
)

This function is used to communicate gaps across processes.

Parameters
dof_to_weighted_gapMap from degree of freedom to weighted (weak) gap
meshMesh used to locate nodes or elements
nodalWhether the element has Lagrange interpolation
normalize_cWhether to normalize with size the c coefficient in contact constraint
communicatorProcess communicator
send_data_backAfter aggregating data on the owning process, whether to send the aggregate back to senders. This can be necessary for things like penalty contact in which the constraint is not enforced by the owner but in a weighted way by the displacement constraints

Definition at line 21 of file MortarContactUtils.C.

28{
29 libmesh_parallel_only(communicator);
30 const auto our_proc_id = communicator.rank();
31
32 // We may have weighted gap information that should go to other processes that own the dofs
33 using Datum = std::tuple<dof_id_type, ADReal, Real>;
34 std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
35
36 for (auto & pr : dof_to_weighted_gap)
37 {
38 const auto * const dof_object = pr.first;
39 const auto proc_id = dof_object->processor_id();
40 if (proc_id == our_proc_id)
41 continue;
42
43 push_data[proc_id].push_back(
44 std::make_tuple(dof_object->id(), std::move(pr.second.first), pr.second.second));
45 }
46
47 const auto & lm_mesh = mesh.getMesh();
48 std::unordered_map<processor_id_type, std::vector<const DofObject *>>
49 pid_to_dof_object_for_sending_back;
50
51 auto action_functor =
52 [nodal,
53 our_proc_id,
54 &lm_mesh,
55 &dof_to_weighted_gap,
56 &normalize_c,
57 &pid_to_dof_object_for_sending_back,
58 send_data_back](const processor_id_type pid, const std::vector<Datum> & sent_data)
59 {
60 mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
61 libmesh_ignore(our_proc_id);
62
63 for (auto & [dof_id, weighted_gap, normalization] : sent_data)
64 {
65 const auto * const dof_object = nodal ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
66 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
67 mooseAssert(dof_object, "This should be non-null");
68 if (send_data_back)
69 pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
70 auto & [our_weighted_gap, our_normalization] = dof_to_weighted_gap[dof_object];
71 our_weighted_gap += weighted_gap;
72 if (normalize_c)
73 our_normalization += normalization;
74 }
75 };
76
77 TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
78
79 // Now send data back if requested
80 if (!send_data_back)
81 return;
82
83 std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
84
85 for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
86 {
87 auto & pid_send_data = push_back_data[pid];
88 pid_send_data.reserve(dof_objects.size());
89 for (const DofObject * const dof_object : dof_objects)
90 {
91 const auto & [our_weighted_gap, our_normalization] =
92 libmesh_map_find(dof_to_weighted_gap, dof_object);
93 pid_send_data.push_back(
94 std::make_tuple(dof_object->id(), our_weighted_gap, our_normalization));
95 }
96 }
97
98 auto sent_back_action_functor =
99 [nodal, our_proc_id, &lm_mesh, &dof_to_weighted_gap, &normalize_c](
100 const processor_id_type libmesh_dbg_var(pid), const std::vector<Datum> & sent_data)
101 {
102 mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
103 libmesh_ignore(our_proc_id);
104
105 for (auto & [dof_id, weighted_gap, normalization] : sent_data)
106 {
107 const auto * const dof_object = nodal ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
108 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
109 mooseAssert(dof_object, "This should be non-null");
110 auto & [our_weighted_gap, our_normalization] = dof_to_weighted_gap[dof_object];
111 our_weighted_gap = weighted_gap;
112 if (normalize_c)
113 our_normalization = normalization;
114 }
115 };
116 TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
117}
MeshBase & mesh
uint8_t processor_id_type
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
void libmesh_ignore(const Args &...)
Tnew cast_ptr(Told *oldvar)

Referenced by WeightedGapUserObject::finalize(), ComputeDynamicWeightedGapLMMechanicalContact::incorrectEdgeDroppingPost(), ComputeFrictionalForceCartesianLMMechanicalContact::incorrectEdgeDroppingPost(), ComputeWeightedGapCartesianLMMechanicalContact::incorrectEdgeDroppingPost(), ComputeDynamicWeightedGapLMMechanicalContact::post(), ComputeFrictionalForceCartesianLMMechanicalContact::post(), and ComputeWeightedGapCartesianLMMechanicalContact::post().

◆ communicateR2T()

void Moose::Mortar::Contact::communicateR2T ( std::unordered_map< const DofObject *, ADRankTwoTensor > &  dof_map_adr2t,
const MooseMesh mesh,
const bool  nodal,
const Parallel::Communicator &  communicator,
const bool  send_data_back 
)
inline

This function is used to communicate velocities across processes.

Parameters
dof_map_adr2tMap from degree of freedom to weighted tank two tensor
meshMesh used to locate nodes or elements
nodalWhether the element has Lagrange interpolation
communicatorProcess communicator
send_data_backWhether to send back data to a distributed constraint

Definition at line 396 of file MortarContactUtils.h.

401{
402 libmesh_parallel_only(communicator);
403 const auto our_proc_id = communicator.rank();
404
405 // We may have weighted velocity information that should go to other processes that own the dofs
406 using Datum = std::pair<dof_id_type, ADRankTwoTensor>;
407 std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
408
409 for (auto & pr : dof_map_adr2t)
410 {
411 const auto * const dof_object = pr.first;
412 const auto proc_id = dof_object->processor_id();
413 if (proc_id == our_proc_id)
414 continue;
415
416 push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
417 }
418
419 const auto & lm_mesh = mesh.getMesh();
420 std::unordered_map<processor_id_type, std::vector<const DofObject *>>
421 pid_to_dof_object_for_sending_back;
422
423 auto action_functor =
424 [nodal,
425 our_proc_id,
426 &lm_mesh,
427 &dof_map_adr2t,
428 &pid_to_dof_object_for_sending_back,
429 send_data_back](const processor_id_type pid, const std::vector<Datum> & sent_data)
430 {
431 mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
432 libmesh_ignore(our_proc_id);
433
434 for (auto & pr : sent_data)
435 {
436 const auto dof_id = pr.first;
437 const auto * const dof_object = nodal ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
438 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
439 mooseAssert(dof_object, "This should be non-null");
440
441 if (send_data_back)
442 pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
443
444 for (const auto i : make_range(3))
445 for (const auto j : make_range(3))
446 dof_map_adr2t[dof_object](i, j) += pr.second(i, j);
447 }
448 };
449
450 TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
451
452 // Now send data back if requested
453 if (!send_data_back)
454 return;
455
456 std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
457
458 for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
459 {
460 auto & pid_send_data = push_back_data[pid];
461 pid_send_data.reserve(dof_objects.size());
462 for (const DofObject * const dof_object : dof_objects)
463 {
464 const auto & r2t = libmesh_map_find(dof_map_adr2t, dof_object);
465 pid_send_data.push_back({dof_object->id(), r2t});
466 }
467 }
468
469 auto sent_back_action_functor =
470 [nodal, our_proc_id, &lm_mesh, &dof_map_adr2t](const processor_id_type libmesh_dbg_var(pid),
471 const std::vector<Datum> & sent_data)
472 {
473 mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
474 libmesh_ignore(our_proc_id);
475
476 for (auto & [dof_id, r2t_sent] : sent_data)
477 {
478 const auto * const dof_object = nodal ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
479 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
480 mooseAssert(dof_object, "This should be non-null");
481 auto & r2t = dof_map_adr2t[dof_object];
482 r2t = r2t_sent;
483 }
484 };
485
486 TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
487}
for(PetscInt i=0;i< nvars;++i)

Referenced by CohesiveZoneModelBase::finalize().

◆ communicateRealObject()

template<typename T >
void Moose::Mortar::Contact::communicateRealObject ( std::unordered_map< const DofObject *, T > &  dof_to_adreal,
const MooseMesh mesh,
const bool  nodal,
const Parallel::Communicator &  communicator,
const bool  send_data_back 
)

Definition at line 491 of file MortarContactUtils.h.

496{
497 libmesh_parallel_only(communicator);
498 const auto our_proc_id = communicator.rank();
499
500 // We may have weighted gap information that should go to other processes that own the dofs
501 using Datum = std::tuple<dof_id_type, T>;
502 std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
503
504 for (auto & pr : dof_to_adreal)
505 {
506 const auto * const dof_object = pr.first;
507 const auto proc_id = dof_object->processor_id();
508 if (proc_id == our_proc_id)
509 continue;
510
511 push_data[proc_id].push_back(std::make_tuple(dof_object->id(), std::move(pr.second)));
512 }
513
514 const auto & lm_mesh = mesh.getMesh();
515 std::unordered_map<processor_id_type, std::vector<const DofObject *>>
516 pid_to_dof_object_for_sending_back;
517
518 auto action_functor =
519 [nodal,
520 our_proc_id,
521 &lm_mesh,
522 &dof_to_adreal,
523 &pid_to_dof_object_for_sending_back,
524 send_data_back](const processor_id_type pid, const std::vector<Datum> & sent_data)
525 {
526 mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
527 libmesh_ignore(our_proc_id);
528
529 for (auto & [dof_id, weighted_gap] : sent_data)
530 {
531 const auto * const dof_object = nodal ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
532 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
533 mooseAssert(dof_object, "This should be non-null");
534 if (send_data_back)
535 pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
536 auto & our_adreal = dof_to_adreal[dof_object];
537 our_adreal += weighted_gap;
538 }
539 };
540
541 TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
542
543 // Now send data back if requested
544 if (!send_data_back)
545 return;
546
547 std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
548
549 for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
550 {
551 auto & pid_send_data = push_back_data[pid];
552 pid_send_data.reserve(dof_objects.size());
553 for (const DofObject * const dof_object : dof_objects)
554 {
555 const auto & our_adreal = libmesh_map_find(dof_to_adreal, dof_object);
556 pid_send_data.push_back(std::make_tuple(dof_object->id(), our_adreal));
557 }
558 }
559
560 auto sent_back_action_functor =
561 [nodal, our_proc_id, &lm_mesh, &dof_to_adreal](const processor_id_type libmesh_dbg_var(pid),
562 const std::vector<Datum> & sent_data)
563 {
564 mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
565 libmesh_ignore(our_proc_id);
566
567 for (auto & [dof_id, adreal] : sent_data)
568 {
569 const auto * const dof_object = nodal ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
570 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
571 mooseAssert(dof_object, "This should be non-null");
572 auto & our_adreal = dof_to_adreal[dof_object];
573 our_adreal = adreal;
574 }
575 };
576 TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
577}

Referenced by BilinearMixedModeCohesiveZoneModel::finalize(), CohesiveZoneModelBase::finalize(), and LMWeightedGapUserObject::finalizeNodalScaling().

◆ communicateVelocities()

template<typename T >
void Moose::Mortar::Contact::communicateVelocities ( std::unordered_map< const DofObject *, T > &  dof_map,
const MooseMesh mesh,
const bool  nodal,
const Parallel::Communicator &  communicator,
const bool  send_data_back 
)
inline

This function is used to communicate velocities across processes.

Parameters
dof_to_weighted_gapMap from degree of freedom to weighted (weak) gap
meshMesh used to locate nodes or elements
nodalWhether the element has Lagrange interpolation
communicatorProcess communicator
send_data_backWhether to send back data to a distributed constraint

Definition at line 298 of file MortarContactUtils.h.

303{
304 libmesh_parallel_only(communicator);
305 const auto our_proc_id = communicator.rank();
306
307 // We may have weighted velocity information that should go to other processes that own the dofs
308 using Datum = std::pair<dof_id_type, T>;
309 std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
310
311 for (auto & pr : dof_map)
312 {
313 const auto * const dof_object = pr.first;
314 const auto proc_id = dof_object->processor_id();
315 if (proc_id == our_proc_id)
316 continue;
317
318 push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
319 }
320
321 const auto & lm_mesh = mesh.getMesh();
322 std::unordered_map<processor_id_type, std::vector<const DofObject *>>
323 pid_to_dof_object_for_sending_back;
324
325 auto action_functor =
326 [nodal, our_proc_id, &lm_mesh, &dof_map, &pid_to_dof_object_for_sending_back, send_data_back](
327 const processor_id_type pid, const std::vector<Datum> & sent_data)
328 {
329 mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
330 libmesh_ignore(our_proc_id);
331
332 for (auto & pr : sent_data)
333 {
334 const auto dof_id = pr.first;
335 const auto * const dof_object = nodal ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
336 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
337 mooseAssert(dof_object, "This should be non-null");
338
339 if (send_data_back)
340 pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
341
342 dof_map[dof_object][0] += pr.second[0];
343 dof_map[dof_object][1] += pr.second[1];
344 }
345 };
346
347 TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
348
349 // Now send data back if requested
350 if (!send_data_back)
351 return;
352
353 std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
354
355 for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
356 {
357 auto & pid_send_data = push_back_data[pid];
358 pid_send_data.reserve(dof_objects.size());
359 for (const DofObject * const dof_object : dof_objects)
360 {
361 const auto & [tangent_one, tangent_two] = libmesh_map_find(dof_map, dof_object);
362 pid_send_data.push_back({dof_object->id(), {tangent_one, tangent_two}});
363 }
364 }
365
366 auto sent_back_action_functor =
367 [nodal, our_proc_id, &lm_mesh, &dof_map](const processor_id_type libmesh_dbg_var(pid),
368 const std::vector<Datum> & sent_data)
369 {
370 mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
371 libmesh_ignore(our_proc_id);
372
373 for (auto & [dof_id, tangents] : sent_data)
374 {
375 const auto * const dof_object = nodal ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
376 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
377 mooseAssert(dof_object, "This should be non-null");
378 auto & [our_tangent_one, our_tangent_two] = dof_map[dof_object];
379 our_tangent_one = tangents[0];
380 our_tangent_two = tangents[1];
381 }
382 };
383
384 TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
385}

Referenced by WeightedVelocitiesUserObject::finalize(), ComputeDynamicFrictionalForceLMMechanicalContact::incorrectEdgeDroppingPost(), ComputeFrictionalForceCartesianLMMechanicalContact::incorrectEdgeDroppingPost(), ComputeDynamicFrictionalForceLMMechanicalContact::post(), and ComputeFrictionalForceCartesianLMMechanicalContact::post().

◆ coulombFrictionRadius()

template<typename T >
T Moose::Mortar::Contact::coulombFrictionRadius ( const T friction_coefficient,
const T augmented_normal_pressure 
)

Return the nonnegative Coulomb friction radius.

Parameters
friction_coefficientThe Coulomb friction coefficient
augmented_normal_pressureThe augmented normal pressure (see augmentedNormalPressure)
Returns
The Coulomb friction radius, clamped to be nonnegative

Definition at line 90 of file MortarContactUtils.h.

91{
92 return friction_coefficient * std::max(T(0), augmented_normal_pressure);
93}
const double T

Referenced by frictionalContactResidual(), and TYPED_TEST().

◆ frictionalContactResidual()

template<typename T , std::size_t N>
std::array< T, N > Moose::Mortar::Contact::frictionalContactResidual ( const std::array< T, N > &  tangential_pressure,
const std::array< T, N > &  tangential_velocity,
const T c_t,
const T dt,
const T normal_pressure,
const T scaled_normal_gap,
const T friction_coefficient,
const T epsilon,
const FrictionProjectionDegree  projection_degree 
)

Compute the epsilon-gated frictional residual for a mortar contact node.

This composes the tangential pressure augmentation tangential_pressure + c_t * tangential_velocity * dt with augmentedNormalPressure, coulombFrictionRadius, and the epsilon-gated, degree-selected (projection_degree) friction residual (alartCurnierFrictionResidual for FrictionProjectionDegree::ONE, hueberStadlerWohlmuthFrictionResidual for FrictionProjectionDegree::TWO), the composition shared by the normal, dynamic, and Cartesian mortar frictional contact constraints.

Parameters
tangential_pressureThe tangential contact pressure p_t
tangential_velocityThe relative (slip) velocity between the secondary and primary surfaces, resolved into the local tangential direction(s) at this node
c_tNumerical factor used in the tangential constraints for convergence purposes
dtThe current time step size
normal_pressureThe raw, unaugmented normal contact pressure
scaled_normal_gapThe normal gap g_bar scaled by a factor C_n, intended to bring it onto the order of the contact pressure for a well-conditioned complementarity function
friction_coefficientThe Coulomb friction coefficient
epsilonThe minimum normal contact pressure required to enable frictional enforcement
projection_degreeWhich friction-residual projection degree to use
Returns
The epsilon-gated frictional residual

Definition at line 258 of file MortarContactUtils.h.

267{
268 std::array<T, N> augmented_tangential_pressure;
269 for (const auto i : index_range(augmented_tangential_pressure))
270 augmented_tangential_pressure[i] = tangential_pressure[i] + c_t * tangential_velocity[i] * dt;
271
272 const auto radius = coulombFrictionRadius(
273 friction_coefficient, augmentedNormalPressure(normal_pressure, scaled_normal_gap));
274
275 switch (projection_degree)
276 {
277 case FrictionProjectionDegree::ONE:
279 tangential_pressure, augmented_tangential_pressure, radius, normal_pressure, epsilon);
280 case FrictionProjectionDegree::TWO:
282 tangential_pressure, augmented_tangential_pressure, radius, normal_pressure, epsilon);
283 default:
284 mooseError("Unhandled projection degree");
285 }
286}
void mooseError(Args &&... args)
T coulombFrictionRadius(const T &friction_coefficient, const T &augmented_normal_pressure)
Return the nonnegative Coulomb friction radius.
std::array< T, N > hueberStadlerWohlmuthFrictionResidual(const std::array< T, N > &tangential_pressure, const std::array< T, N > &augmented_tangential_pressure, const T &radius)
Return the degree-two Hueber-Stadler-Wohlmuth friction residual max(radius, ||q_t||) p_t - radius q_t...
T augmentedNormalPressure(const T &normal_pressure, const T &scaled_normal_gap)
Return the augmented normal pressure p_n - C_n g_bar.
const Real radius

Referenced by ComputeDynamicFrictionalForceLMMechanicalContact::enforceConstraintOnDof(), ComputeFrictionalForceCartesianLMMechanicalContact::enforceConstraintOnDof(), ComputeFrictionalForceLMMechanicalContact::enforceConstraintOnDof(), ComputeDynamicFrictionalForceLMMechanicalContact::enforceConstraintOnDof3d(), and ComputeFrictionalForceLMMechanicalContact::enforceConstraintOnDof3d().

◆ hueberStadlerWohlmuthFrictionResidual() [1/2]

template<typename T , std::size_t N>
std::array< T, N > Moose::Mortar::Contact::hueberStadlerWohlmuthFrictionResidual ( const std::array< T, N > &  tangential_pressure,
const std::array< T, N > &  augmented_tangential_pressure,
const T radius 
)

Return the degree-two Hueber-Stadler-Wohlmuth friction residual max(radius, ||q_t||) p_t - radius q_t.

At radius = ||q_t|| = 0 the degree-two expression vanishes for every p_t. Returning p_t in that state preserves the Coulomb solution set during separation.

Parameters
tangential_pressureThe tangential contact pressure p_t
augmented_tangential_pressureThe velocity-augmented tangential pressure q_t (see frictionalContactResidual)
radiusThe Coulomb friction radius (see coulombFrictionRadius)
Returns
The degree-two Hueber-Stadler-Wohlmuth friction residual

Definition at line 180 of file MortarContactUtils.h.

183{
184 const T augmented_norm = MathUtils::norm(augmented_tangential_pressure);
185 const T weight = std::max(radius, augmented_norm);
186 if (weight == 0)
187 return tangential_pressure;
188
189 std::array<T, N> residual;
190 for (const auto i : index_range(residual))
191 residual[i] = weight * tangential_pressure[i] - radius * augmented_tangential_pressure[i];
192 return residual;
193}
auto norm(const T &value)

Referenced by frictionalContactResidual(), hueberStadlerWohlmuthFrictionResidual(), TYPED_TEST(), TYPED_TEST(), TYPED_TEST(), TYPED_TEST(), and TYPED_TEST().

◆ hueberStadlerWohlmuthFrictionResidual() [2/2]

template<typename T , std::size_t N>
std::array< T, N > Moose::Mortar::Contact::hueberStadlerWohlmuthFrictionResidual ( const std::array< T, N > &  tangential_pressure,
const std::array< T, N > &  augmented_tangential_pressure,
const T radius,
const T normal_pressure,
const T epsilon 
)

Same as the three-argument hueberStadlerWohlmuthFrictionResidual, but additionally short-circuits to the trivial identity residual whenever the raw, unaugmented normal contact pressure normal_pressure falls below epsilon.

The pure weight == 0 condition in the three-argument overload is an exact mathematical criterion, but it is a poor numerical proxy for "this dof is separated" during Newton iteration: augmented_tangential_pressure includes a c_t-scaled tangential velocity term that is essentially never exactly zero in floating point, so the full weight/radius expression stays active even for dofs whose normal contact state is still swinging between contact and separation as the active set settles. Because that expression is only piecewise smooth across the active-set boundary, evaluating it on a dof that has not yet settled can inject a large, poorly conditioned residual and Jacobian row into that Newton step. Gating on the raw normal Lagrange multiplier instead is a coarser but cheaper and more conservative criterion: it forces such transitioning dofs onto the trivial identity residual (a well-conditioned, constant-derivative Jacobian row) until the normal contact state has clearly resolved, trading a small amount of formulation fidelity for solver robustness during that transient.

Parameters
tangential_pressureThe tangential contact pressure p_t
augmented_tangential_pressureThe velocity-augmented tangential pressure q_t (see frictionalContactResidual)
radiusThe Coulomb friction radius (see coulombFrictionRadius)
normal_pressureThe raw, unaugmented normal contact pressure
epsilonThe minimum normal contact pressure required to enable frictional enforcement
Returns
The epsilon-gated, degree-two Hueber-Stadler-Wohlmuth friction residual

Definition at line 222 of file MortarContactUtils.h.

227{
228 if (normal_pressure < epsilon)
229 return tangential_pressure;
230
232 tangential_pressure, augmented_tangential_pressure, radius);
233}

◆ projectToClosedSphere()

template<typename T , std::size_t N>
std::array< T, N > Moose::Mortar::Contact::projectToClosedSphere ( const std::array< T, N > &  vector,
const T radius 
)

Project vector onto a closed sphere centered at the origin with radius radius.

Parameters
vectorThe vector to project
radiusThe radius of the closed sphere to project onto
Returns
vector unchanged if its norm is already within radius, otherwise vector scaled down to have norm radius

Definition at line 104 of file MortarContactUtils.h.

105{
106 const T norm = MathUtils::norm(vector);
107 if (norm <= radius)
108 return vector;
109
110 std::array<T, N> projection;
111 for (const auto i : index_range(projection))
112 projection[i] = radius * vector[i] / norm;
113 return projection;
114}

Referenced by alartCurnierFrictionResidual(), and TYPED_TEST().