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

Generated by: LCOV version 1.14