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 "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 <>
38 {
39 public:
40  explicit StandardType(const ADRankTwoTensor * example = nullptr)
41  : DataType(StandardType<ADReal>(example ? &((*example)(0, 0)) : nullptr),
42  LIBMESH_DIM * LIBMESH_DIM)
43  {
44  }
45 
46  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 
68 template <typename T>
69 inline void
70 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  for (auto & pr : dof_map)
84  {
85  const auto * const dof_object = pr.first;
86  const auto proc_id = dof_object->processor_id();
87  if (proc_id == our_proc_id)
88  continue;
89 
90  push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
91  }
92 
93  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  auto action_functor =
98  [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  for (auto & pr : sent_data)
105  {
106  const auto dof_id = pr.first;
107  const auto * const dof_object =
108  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
109  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
110  mooseAssert(dof_object, "This should be non-null");
111 
112  if (send_data_back)
113  pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
114 
115  dof_map[dof_object][0] += pr.second[0];
116  dof_map[dof_object][1] += pr.second[1];
117  }
118  };
119 
120  TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
121 
122  // Now send data back if requested
123  if (!send_data_back)
124  return;
125 
126  std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
127 
128  for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
129  {
130  auto & pid_send_data = push_back_data[pid];
131  pid_send_data.reserve(dof_objects.size());
132  for (const DofObject * const dof_object : dof_objects)
133  {
134  const auto & [tangent_one, tangent_two] = libmesh_map_find(dof_map, dof_object);
135  pid_send_data.push_back({dof_object->id(), {tangent_one, tangent_two}});
136  }
137  }
138 
139  auto sent_back_action_functor =
140  [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  for (auto & [dof_id, tangents] : sent_data)
147  {
148  const auto * const dof_object =
149  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
150  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
151  mooseAssert(dof_object, "This should be non-null");
152  auto & [our_tangent_one, our_tangent_two] = dof_map[dof_object];
153  our_tangent_one = tangents[0];
154  our_tangent_two = tangents[1];
155  }
156  };
157 
158  TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
159 }
160 
169 inline void
170 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  for (auto & pr : dof_map_adr2t)
184  {
185  const auto * const dof_object = pr.first;
186  const auto proc_id = dof_object->processor_id();
187  if (proc_id == our_proc_id)
188  continue;
189 
190  push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
191  }
192 
193  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  [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  for (auto & pr : sent_data)
209  {
210  const auto dof_id = pr.first;
211  const auto * const dof_object =
212  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
213  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
214  mooseAssert(dof_object, "This should be non-null");
215 
216  if (send_data_back)
217  pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
218 
219  for (const auto i : make_range(3))
220  for (const auto j : make_range(3))
221  dof_map_adr2t[dof_object](i, j) += pr.second(i, j);
222  }
223  };
224 
225  TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
226 
227  // Now send data back if requested
228  if (!send_data_back)
229  return;
230 
231  std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
232 
233  for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
234  {
235  auto & pid_send_data = push_back_data[pid];
236  pid_send_data.reserve(dof_objects.size());
237  for (const DofObject * const dof_object : dof_objects)
238  {
239  const auto & r2t = libmesh_map_find(dof_map_adr2t, dof_object);
240  pid_send_data.push_back({dof_object->id(), r2t});
241  }
242  }
243 
244  auto sent_back_action_functor =
245  [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  for (auto & [dof_id, r2t_sent] : sent_data)
252  {
253  const auto * const dof_object =
254  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
255  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
256  mooseAssert(dof_object, "This should be non-null");
257  auto & r2t = dof_map_adr2t[dof_object];
258  r2t = r2t_sent;
259  }
260  };
261 
262  TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
263 }
264 
265 template <typename T>
266 void
267 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  for (auto & pr : dof_to_adreal)
281  {
282  const auto * const dof_object = pr.first;
283  const auto proc_id = dof_object->processor_id();
284  if (proc_id == our_proc_id)
285  continue;
286 
287  push_data[proc_id].push_back(std::make_tuple(dof_object->id(), std::move(pr.second)));
288  }
289 
290  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  auto action_functor =
295  [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  for (auto & [dof_id, weighted_gap] : sent_data)
306  {
307  const auto * const dof_object =
308  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
309  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
310  mooseAssert(dof_object, "This should be non-null");
311  if (send_data_back)
312  pid_to_dof_object_for_sending_back[pid].push_back(dof_object);
313  auto & our_adreal = dof_to_adreal[dof_object];
314  our_adreal += weighted_gap;
315  }
316  };
317 
318  TIMPI::push_parallel_vector_data(communicator, push_data, action_functor);
319 
320  // Now send data back if requested
321  if (!send_data_back)
322  return;
323 
324  std::unordered_map<processor_id_type, std::vector<Datum>> push_back_data;
325 
326  for (const auto & [pid, dof_objects] : pid_to_dof_object_for_sending_back)
327  {
328  auto & pid_send_data = push_back_data[pid];
329  pid_send_data.reserve(dof_objects.size());
330  for (const DofObject * const dof_object : dof_objects)
331  {
332  const auto & our_adreal = libmesh_map_find(dof_to_adreal, dof_object);
333  pid_send_data.push_back(std::make_tuple(dof_object->id(), our_adreal));
334  }
335  }
336 
337  auto sent_back_action_functor =
338  [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  for (auto & [dof_id, adreal] : sent_data)
345  {
346  const auto * const dof_object =
347  nodal ? static_cast<const DofObject *>(lm_mesh.node_ptr(dof_id))
348  : static_cast<const DofObject *>(lm_mesh.elem_ptr(dof_id));
349  mooseAssert(dof_object, "This should be non-null");
350  auto & our_adreal = dof_to_adreal[dof_object];
351  our_adreal = adreal;
352  }
353  };
354  TIMPI::push_parallel_vector_data(communicator, push_back_data, sent_back_action_functor);
355 }
356 
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 }
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")