LCOV - code coverage report
Current view: top level - include/utils - MortarContactUtils.h (source / functions) Hit Total Coverage
Test: idaholab/moose contact: #31405 (292dce) with base fef103 Lines: 103 109 94.5 %
Date: 2025-09-04 07:52:48 Functions: 27 33 81.8 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "FEProblemBase.h"
      13             : #include "ADReal.h"
      14             : #include "RankTwoTensor.h"
      15             : #include "MooseMesh.h"
      16             : 
      17             : #include "libmesh/dof_object.h"
      18             : 
      19             : #include "metaphysicl/parallel_dualnumber.h"
      20             : #include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
      21             : 
      22             : #include "timpi/parallel_sync.h"
      23             : #include "timpi/communicator.h"
      24             : #include "libmesh/data_type.h"
      25             : #include "libmesh/parallel_algebra.h"
      26             : #include "timpi/parallel_sync.h"
      27             : 
      28             : #include <utility>
      29             : #include <array>
      30             : #include <unordered_map>
      31             : #include <vector>
      32             : 
      33             : namespace TIMPI
      34             : {
      35             : 
      36             : template <>
      37             : class StandardType<ADRankTwoTensor> : public DataType
      38             : {
      39             : public:
      40          76 :   explicit StandardType(const ADRankTwoTensor * example = nullptr)
      41          76 :     : DataType(StandardType<ADReal>(example ? &((*example)(0, 0)) : nullptr),
      42         152 :                LIBMESH_DIM * LIBMESH_DIM)
      43             :   {
      44          76 :   }
      45             : 
      46          76 :   inline ~StandardType() { this->free(); }
      47             : 
      48             :   static const bool is_fixed_type = true;
      49             : };
      50             : 
      51             : } // namespace TIMPI
      52             : 
      53             : namespace Moose
      54             : {
      55             : namespace Mortar
      56             : {
      57             : namespace Contact
      58             : {
      59             : 
      60             : /**
      61             :  * This function is used to communicate velocities across processes
      62             :  * @param dof_to_weighted_gap Map from degree of freedom to weighted (weak) gap
      63             :  * @param mesh Mesh used to locate nodes or elements
      64             :  * @param nodal Whether the element has Lagrange interpolation
      65             :  * @param communicator Process communicator
      66             :  * @param send_data_back Whether to send back data to a distributed constraint
      67             :  */
      68             : template <typename T>
      69             : inline void
      70      230126 : communicateVelocities(std::unordered_map<const DofObject *, T> & dof_map,
      71             :                       const MooseMesh & mesh,
      72             :                       const bool nodal,
      73             :                       const Parallel::Communicator & communicator,
      74             :                       const bool send_data_back)
      75             : {
      76             :   libmesh_parallel_only(communicator);
      77             :   const auto our_proc_id = communicator.rank();
      78             : 
      79             :   // We may have weighted velocity information that should go to other processes that own the dofs
      80             :   using Datum = std::pair<dof_id_type, T>;
      81             :   std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
      82             : 
      83     1294854 :   for (auto & pr : dof_map)
      84             :   {
      85     1064728 :     const auto * const dof_object = pr.first;
      86     1064728 :     const auto proc_id = dof_object->processor_id();
      87     1064728 :     if (proc_id == our_proc_id)
      88     1040364 :       continue;
      89             : 
      90       48728 :     push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
      91             :   }
      92             : 
      93      230126 :   const auto & lm_mesh = mesh.getMesh();
      94             :   std::unordered_map<processor_id_type, std::vector<const DofObject *>>
      95             :       pid_to_dof_object_for_sending_back;
      96             : 
      97      230126 :   auto action_functor =
      98       15902 :       [nodal, our_proc_id, &lm_mesh, &dof_map, &pid_to_dof_object_for_sending_back, send_data_back](
      99             :           const processor_id_type pid, const std::vector<Datum> & sent_data)
     100             :   {
     101             :     mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
     102             :     libmesh_ignore(our_proc_id);
     103             : 
     104       40266 :     for (auto & pr : sent_data)
     105             :     {
     106       24364 :       const auto dof_id = pr.first;
     107       24364 :       const auto * const dof_object =
     108       24364 :           nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
     109           0 :                 : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
     110             :       mooseAssert(dof_object, "This should be non-null");
     111             : 
     112       24364 :       if (send_data_back)
     113        5874 :         pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
     114             : 
     115       24364 :       dof_map[dof_object][0] += pr.second[0];
     116       24364 :       dof_map[dof_object][1] += pr.second[1];
     117             :     }
     118             :   };
     119             : 
     120      230126 :   TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
     121             : 
     122             :   // Now send data back if requested
     123      230126 :   if (!send_data_back)
     124             :     return;
     125             : 
     126             :   std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
     127             : 
     128       37350 :   for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
     129             :   {
     130             :     auto & pid_send_data = push_back_data[pid];
     131        3958 :     pid_send_data.reserve(dof_objects.size());
     132        9832 :     for (const DofObject * const dof_object : dof_objects)
     133             :     {
     134        5874 :       const auto & [tangent_one, tangent_two] = libmesh_map_find(dof_map, dof_object);
     135        5874 :       pid_send_data.push_back({dof_object->id(), {tangent_one, tangent_two}});
     136             :     }
     137             :   }
     138             : 
     139       33392 :   auto sent_back_action_functor =
     140        3958 :       [nodal, our_proc_id, &lm_mesh, &dof_map](const processor_id_type libmesh_dbg_var(pid),
     141             :                                                const std::vector<Datum> & sent_data)
     142             :   {
     143             :     mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
     144             :     libmesh_ignore(our_proc_id);
     145             : 
     146        9832 :     for (auto & [dof_id, tangents] : sent_data)
     147             :     {
     148        5874 :       const auto * const dof_object =
     149        5874 :           nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
     150           0 :                 : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
     151             :       mooseAssert(dof_object, "This should be non-null");
     152        5874 :       auto & [our_tangent_one, our_tangent_two] = dof_map[dof_object];
     153        5874 :       our_tangent_one = tangents[0];
     154        5874 :       our_tangent_two = tangents[1];
     155             :     }
     156             :   };
     157             : 
     158       33392 :   TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
     159        5874 : }
     160             : 
     161             : /**
     162             :  * This function is used to communicate velocities across processes
     163             :  * @param dof_map_adr2t Map from degree of freedom to weighted tank two tensor
     164             :  * @param mesh Mesh used to locate nodes or elements
     165             :  * @param nodal Whether the element has Lagrange interpolation
     166             :  * @param communicator Process communicator
     167             :  * @param send_data_back Whether to send back data to a distributed constraint
     168             :  */
     169             : inline void
     170       15360 : communicateR2T(std::unordered_map<const DofObject *, ADRankTwoTensor> & dof_map_adr2t,
     171             :                const MooseMesh & mesh,
     172             :                const bool nodal,
     173             :                const Parallel::Communicator & communicator,
     174             :                const bool send_data_back)
     175             : {
     176             :   libmesh_parallel_only(communicator);
     177             :   const auto our_proc_id = communicator.rank();
     178             : 
     179             :   // We may have weighted velocity information that should go to other processes that own the dofs
     180             :   using Datum = std::pair<dof_id_type, ADRankTwoTensor>;
     181             :   std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
     182             : 
     183       48526 :   for (auto & pr : dof_map_adr2t)
     184             :   {
     185       33166 :     const auto * const dof_object = pr.first;
     186       33166 :     const auto proc_id = dof_object->processor_id();
     187       33166 :     if (proc_id == our_proc_id)
     188       32148 :       continue;
     189             : 
     190        1018 :     push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
     191             :   }
     192             : 
     193       15360 :   const auto & lm_mesh = mesh.getMesh();
     194             :   std::unordered_map<processor_id_type, std::vector<const DofObject *>>
     195             :       pid_to_dof_object_for_sending_back;
     196             : 
     197             :   auto action_functor =
     198        1018 :       [nodal,
     199             :        our_proc_id,
     200             :        &lm_mesh,
     201             :        &dof_map_adr2t,
     202             :        &pid_to_dof_object_for_sending_back,
     203             :        send_data_back](const processor_id_type pid, const std::vector<Datum> & sent_data)
     204             :   {
     205             :     mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
     206             :     libmesh_ignore(our_proc_id);
     207             : 
     208        2036 :     for (auto & pr : sent_data)
     209             :     {
     210        1018 :       const auto dof_id = pr.first;
     211             :       const auto * const dof_object =
     212        1018 :           nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
     213           0 :                 : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
     214             :       mooseAssert(dof_object, "This should be non-null");
     215             : 
     216        1018 :       if (send_data_back)
     217        1018 :         pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
     218             : 
     219        4072 :       for (const auto i : make_range(3))
     220       12216 :         for (const auto j : make_range(3))
     221        9162 :           dof_map_adr2t[dof_object](i, j) += pr.second(i, j);
     222             :     }
     223        1018 :   };
     224             : 
     225       15360 :   TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
     226             : 
     227             :   // Now send data back if requested
     228       15360 :   if (!send_data_back)
     229             :     return;
     230             : 
     231             :   std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
     232             : 
     233       16378 :   for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
     234             :   {
     235             :     auto & pid_send_data = push_back_data[pid];
     236        1018 :     pid_send_data.reserve(dof_objects.size());
     237        2036 :     for (const DofObject * const dof_object : dof_objects)
     238             :     {
     239        1018 :       const auto & r2t = libmesh_map_find(dof_map_adr2t, dof_object);
     240        1018 :       pid_send_data.push_back({dof_object->id(), r2t});
     241             :     }
     242             :   }
     243             : 
     244             :   auto sent_back_action_functor =
     245        1018 :       [nodal, our_proc_id, &lm_mesh, &dof_map_adr2t](const processor_id_type libmesh_dbg_var(pid),
     246             :                                                      const std::vector<Datum> & sent_data)
     247             :   {
     248             :     mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
     249             :     libmesh_ignore(our_proc_id);
     250             : 
     251        2036 :     for (auto & [dof_id, r2t_sent] : sent_data)
     252             :     {
     253             :       const auto * const dof_object =
     254        1018 :           nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
     255           0 :                 : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
     256             :       mooseAssert(dof_object, "This should be non-null");
     257        1018 :       auto & r2t = dof_map_adr2t[dof_object];
     258             :       r2t = r2t_sent;
     259             :     }
     260       16378 :   };
     261             : 
     262       15360 :   TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
     263             : }
     264             : 
     265             : template <typename T>
     266             : void
     267       38400 : communicateRealObject(std::unordered_map<const DofObject *, T> & dof_to_adreal,
     268             :                       const MooseMesh & mesh,
     269             :                       const bool nodal,
     270             :                       const Parallel::Communicator & communicator,
     271             :                       const bool send_data_back)
     272             : {
     273             :   libmesh_parallel_only(communicator);
     274             :   const auto our_proc_id = communicator.rank();
     275             : 
     276             :   // We may have weighted gap information that should go to other processes that own the dofs
     277             :   using Datum = std::tuple<dof_id_type, T>;
     278             :   std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
     279             : 
     280      121315 :   for (auto & pr : dof_to_adreal)
     281             :   {
     282       82915 :     const auto * const dof_object = pr.first;
     283       82915 :     const auto proc_id = dof_object->processor_id();
     284       82915 :     if (proc_id == our_proc_id)
     285       80370 :       continue;
     286             : 
     287        4581 :     push_data[proc_id].push_back(std::make_tuple(dof_object->id(), std::move(pr.second)));
     288             :   }
     289             : 
     290       38400 :   const auto & lm_mesh = mesh.getMesh();
     291             :   std::unordered_map<processor_id_type, std::vector<const DofObject *>>
     292             :       pid_to_dof_object_for_sending_back;
     293             : 
     294       38400 :   auto action_functor =
     295        2545 :       [nodal,
     296             :        our_proc_id,
     297             :        &lm_mesh,
     298             :        &dof_to_adreal,
     299             :        &pid_to_dof_object_for_sending_back,
     300             :        send_data_back](const processor_id_type pid, const std::vector<Datum> & sent_data)
     301             :   {
     302             :     mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
     303             :     libmesh_ignore(our_proc_id);
     304             : 
     305        5090 :     for (auto & [dof_id, weighted_gap] : sent_data)
     306             :     {
     307        2545 :       const auto * const dof_object =
     308        2545 :           nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
     309           0 :                 : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
     310             :       mooseAssert(dof_object, "This should be non-null");
     311        2545 :       if (send_data_back)
     312        2545 :         pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
     313        2545 :       auto & our_adreal = dof_to_adreal[dof_object];
     314        2545 :       our_adreal += weighted_gap;
     315             :     }
     316             :   };
     317             : 
     318       38400 :   TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
     319             : 
     320             :   // Now send data back if requested
     321       38400 :   if (!send_data_back)
     322             :     return;
     323             : 
     324             :   std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
     325             : 
     326       40945 :   for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
     327             :   {
     328             :     auto & pid_send_data = push_back_data[pid];
     329        2545 :     pid_send_data.reserve(dof_objects.size());
     330        5090 :     for (const DofObject * const dof_object : dof_objects)
     331             :     {
     332        2545 :       const auto & our_adreal = libmesh_map_find(dof_to_adreal, dof_object);
     333        2545 :       pid_send_data.push_back(std::make_tuple(dof_object->id(), our_adreal));
     334             :     }
     335             :   }
     336             : 
     337       38400 :   auto sent_back_action_functor =
     338        2545 :       [nodal, our_proc_id, &lm_mesh, &dof_to_adreal](const processor_id_type libmesh_dbg_var(pid),
     339             :                                                      const std::vector<Datum> & sent_data)
     340             :   {
     341             :     mooseAssert(pid != our_proc_id, "We do not send messages to ourself here");
     342             :     libmesh_ignore(our_proc_id);
     343             : 
     344        5090 :     for (auto & [dof_id, adreal] : sent_data)
     345             :     {
     346        2545 :       const auto * const dof_object =
     347        2545 :           nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
     348           0 :                 : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
     349             :       mooseAssert(dof_object, "This should be non-null");
     350        2545 :       auto & our_adreal = dof_to_adreal[dof_object];
     351        2036 :       our_adreal = adreal;
     352             :     }
     353             :   };
     354       38400 :   TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
     355             : }
     356             : 
     357             : /**
     358             :  * This function is used to communicate gaps across processes
     359             :  * @param dof_to_weighted_gap Map from degree of freedom to weighted (weak) gap
     360             :  * @param mesh Mesh used to locate nodes or elements
     361             :  * @param nodal Whether the element has Lagrange interpolation
     362             :  * @param normalize_c Whether to normalize with size the c coefficient in contact constraint
     363             :  * @param communicator Process communicator
     364             :  * @param send_data_back After aggregating data on the owning process, whether to send the aggregate
     365             :  * back to senders. This can be necessary for things like penalty contact in which the constraint is
     366             :  * not enforced by the owner but in a weighted way by the displacement constraints
     367             :  */
     368             : void communicateGaps(
     369             :     std::unordered_map<const DofObject *, std::pair<ADReal, Real>> & dof_to_weighted_gap,
     370             :     const MooseMesh & mesh,
     371             :     bool nodal,
     372             :     bool normalize_c,
     373             :     const Parallel::Communicator & communicator,
     374             :     bool send_data_back);
     375             : }
     376             : }
     377             : }

Generated by: LCOV version 1.14