https://mooseframework.inl.gov
MortarContactUtils.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 "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 <>
40 {
41 public:
42  explicit StandardType(const ADRankTwoTensor * example = nullptr)
43  : DataType(StandardType<ADReal>(example ? &((*example)(0, 0)) : nullptr),
44  LIBMESH_DIM * LIBMESH_DIM)
45  {
46  }
47 
48  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 
70 template <typename T>
71 inline void
72 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  for (auto & pr : dof_map)
86  {
87  const auto * const dof_object = pr.first;
88  const auto proc_id = dof_object->processor_id();
89  if (proc_id == our_proc_id)
90  continue;
91 
92  push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
93  }
94 
95  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  auto action_functor =
100  [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  for (auto & pr : sent_data)
107  {
108  const auto dof_id = pr.first;
109  const auto * const dof_object =
110  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
111  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
112  mooseAssert(dof_object, "This should be non-null");
113 
114  if (send_data_back)
115  pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
116 
117  dof_map[dof_object][0] += pr.second[0];
118  dof_map[dof_object][1] += pr.second[1];
119  }
120  };
121 
122  TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
123 
124  // Now send data back if requested
125  if (!send_data_back)
126  return;
127 
128  std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
129 
130  for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
131  {
132  auto & pid_send_data = push_back_data[pid];
133  pid_send_data.reserve(dof_objects.size());
134  for (const DofObject * const dof_object : dof_objects)
135  {
136  const auto & [tangent_one, tangent_two] = libmesh_map_find(dof_map, dof_object);
137  pid_send_data.push_back({dof_object->id(), {tangent_one, tangent_two}});
138  }
139  }
140 
141  auto sent_back_action_functor =
142  [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  for (auto & [dof_id, tangents] : sent_data)
149  {
150  const auto * const dof_object =
151  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
152  : 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  our_tangent_one = tangents[0];
156  our_tangent_two = tangents[1];
157  }
158  };
159 
160  TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
161 }
162 
171 inline void
172 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  for (auto & pr : dof_map_adr2t)
186  {
187  const auto * const dof_object = pr.first;
188  const auto proc_id = dof_object->processor_id();
189  if (proc_id == our_proc_id)
190  continue;
191 
192  push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
193  }
194 
195  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  [nodal,
201  our_proc_id,
202  &lm_mesh,
203  &dof_map_adr2t,
204  &pid_to_dof_object_for_sending_back,
205  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  for (auto & pr : sent_data)
211  {
212  const auto dof_id = pr.first;
213  const auto * const dof_object =
214  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
215  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
216  mooseAssert(dof_object, "This should be non-null");
217 
218  if (send_data_back)
219  pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
220 
221  for (const auto i : make_range(3))
222  for (const auto j : make_range(3))
223  dof_map_adr2t[dof_object](i, j) += pr.second(i, j);
224  }
225  };
226 
227  TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
228 
229  // Now send data back if requested
230  if (!send_data_back)
231  return;
232 
233  std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
234 
235  for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
236  {
237  auto & pid_send_data = push_back_data[pid];
238  pid_send_data.reserve(dof_objects.size());
239  for (const DofObject * const dof_object : dof_objects)
240  {
241  const auto & r2t = libmesh_map_find(dof_map_adr2t, dof_object);
242  pid_send_data.push_back({dof_object->id(), r2t});
243  }
244  }
245 
246  auto sent_back_action_functor =
247  [nodal, our_proc_id, &lm_mesh, &dof_map_adr2t](const processor_id_type libmesh_dbg_var(pid),
248  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  for (auto & [dof_id, r2t_sent] : sent_data)
254  {
255  const auto * const dof_object =
256  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
257  : 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  };
263 
264  TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
265 }
266 
267 template <typename T>
268 void
269 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  for (auto & pr : dof_to_adreal)
283  {
284  const auto * const dof_object = pr.first;
285  const auto proc_id = dof_object->processor_id();
286  if (proc_id == our_proc_id)
287  continue;
288 
289  push_data[proc_id].push_back(std::make_tuple(dof_object->id(), std::move(pr.second)));
290  }
291 
292  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  auto action_functor =
297  [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  for (auto & [dof_id, weighted_gap] : sent_data)
308  {
309  const auto * const dof_object =
310  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
311  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
312  mooseAssert(dof_object, "This should be non-null");
313  if (send_data_back)
314  pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
315  auto & our_adreal = dof_to_adreal[dof_object];
316  our_adreal += weighted_gap;
317  }
318  };
319 
320  TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
321 
322  // Now send data back if requested
323  if (!send_data_back)
324  return;
325 
326  std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
327 
328  for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
329  {
330  auto & pid_send_data = push_back_data[pid];
331  pid_send_data.reserve(dof_objects.size());
332  for (const DofObject * const dof_object : dof_objects)
333  {
334  const auto & our_adreal = libmesh_map_find(dof_to_adreal, dof_object);
335  pid_send_data.push_back(std::make_tuple(dof_object->id(), our_adreal));
336  }
337  }
338 
339  auto sent_back_action_functor =
340  [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  for (auto & [dof_id, adreal] : sent_data)
347  {
348  const auto * const dof_object =
349  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
350  : 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  our_adreal = adreal;
354  }
355  };
356  TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
357 }
358 
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 }
StandardType(const ADRankTwoTensor *example=nullptr)
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 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)
MeshBase & mesh
DualNumber< Real, DNDerivativeType, true > ADReal
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.
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
uint8_t processor_id_type
void libmesh_ignore(const Args &...)
static const bool is_fixed_type
DIE A HORRIBLE DEATH HERE typedef MPI_Comm communicator
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.
IntRange< T > make_range(T beg, T end)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")