MFEM  v4.5.1
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
prestriction.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "../config/config.hpp"
13 
14 #ifdef MFEM_USE_MPI
15 
16 #include "restriction.hpp"
17 #include "prestriction.hpp"
18 #include "pgridfunc.hpp"
19 #include "pfespace.hpp"
20 #include "fespace.hpp"
21 #include "../general/forall.hpp"
22 
23 namespace mfem
24 {
25 
27  ElementDofOrdering ordering,
28  FaceType type)
29  : H1FaceRestriction(fes, ordering, type, false),
30  type(type),
31  interpolations(fes, ordering, type)
32 {
33  if (nf==0) { return; }
34  x_interp.UseDevice(true);
35 
36  CheckFESpace(ordering);
37 
38  ComputeScatterIndicesAndOffsets(ordering, type);
39 
40  ComputeGatherIndices(ordering, type);
41 }
42 
43 void ParNCH1FaceRestriction::Mult(const Vector &x, Vector &y) const
44 {
47 }
48 
50 {
51  // Assumes all elements have the same number of dofs
52  const int nface_dofs = face_dofs;
53  const int vd = vdim;
54  auto d_y = Reshape(y.ReadWrite(), nface_dofs, vd, nf);
55  auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
56  const int num_nc_faces = nc_interp_config.Size();
57  if ( num_nc_faces == 0 ) { return; }
58  auto interp_config_ptr = nc_interp_config.Read();
59  const int nc_size = interpolations.GetNumInterpolators();
60  auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
61  nface_dofs, nface_dofs, nc_size);
62  static constexpr int max_nd = 16*16;
63  MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
64  MFEM_FORALL_3D(nc_face, num_nc_faces, nface_dofs, 1, 1,
65  {
66  MFEM_SHARED double dof_values[max_nd];
67  const NCInterpConfig conf = interp_config_ptr[nc_face];
68  if ( conf.is_non_conforming && conf.master_side == 0 )
69  {
70  const int interp_index = conf.index;
71  const int face = conf.face_index;
72  for (int c = 0; c < vd; ++c)
73  {
74  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
75  {
76  dof_values[dof] = d_y(dof, c, face);
77  }
78  MFEM_SYNC_THREAD;
79  MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
80  {
81  double res = 0.0;
82  for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
83  {
84  res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
85  }
86  d_y(dof_out, c, face) = res;
87  }
88  MFEM_SYNC_THREAD;
89  }
90  }
91  });
92 }
93 
94 void ParNCH1FaceRestriction::AddMultTranspose(const Vector &x, Vector &y) const
95 {
96  if (nf==0) { return; }
97  NonconformingTransposeInterpolation(x);
98  H1FaceRestriction::AddMultTranspose(x_interp, y);
99 }
100 
101 void ParNCH1FaceRestriction::AddMultTransposeInPlace(Vector &x, Vector &y) const
102 {
103  if (nf==0) { return; }
104  NonconformingTransposeInterpolationInPlace(x);
105  H1FaceRestriction::AddMultTranspose(x, y);
106 }
107 
108 void ParNCH1FaceRestriction::NonconformingTransposeInterpolation(
109  const Vector& x) const
110 {
111  if (x_interp.Size()==0)
112  {
113  x_interp.SetSize(x.Size());
114  }
115  x_interp = x;
116  NonconformingTransposeInterpolationInPlace(x_interp);
117 }
118 
119 void ParNCH1FaceRestriction::NonconformingTransposeInterpolationInPlace(
120  Vector& x) const
121 {
122  // Assumes all elements have the same number of dofs
123  const int nface_dofs = face_dofs;
124  const int vd = vdim;
125  if ( type==FaceType::Interior )
126  {
127  // Interpolation from slave to master face dofs
128  auto d_x = Reshape(x.ReadWrite(), nface_dofs, vd, nf);
129  auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
130  const int num_nc_faces = nc_interp_config.Size();
131  if ( num_nc_faces == 0 ) { return; }
132  auto interp_config_ptr = nc_interp_config.Read();
133  const int nc_size = interpolations.GetNumInterpolators();
134  auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
135  nface_dofs, nface_dofs, nc_size);
136  static constexpr int max_nd = 1024;
137  MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
138  MFEM_FORALL_3D(nc_face, num_nc_faces, nface_dofs, 1, 1,
139  {
140  MFEM_SHARED double dof_values[max_nd];
141  const NCInterpConfig conf = interp_config_ptr[nc_face];
142  const int master_side = conf.master_side;
143  if ( conf.is_non_conforming && master_side==0 )
144  {
145  const int interp_index = conf.index;
146  const int face = conf.face_index;
147  // Interpolation from fine to coarse
148  for (int c = 0; c < vd; ++c)
149  {
150  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
151  {
152  dof_values[dof] = d_x(dof, c, face);
153  }
154  MFEM_SYNC_THREAD;
155  MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
156  {
157  double res = 0.0;
158  for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
159  {
160  res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
161  }
162  d_x(dof_out, c, face) = res;
163  }
164  MFEM_SYNC_THREAD;
165  }
166  }
167  });
168  }
169 }
170 
171 void ParNCH1FaceRestriction::ComputeScatterIndicesAndOffsets(
172  const ElementDofOrdering ordering,
173  const FaceType face_type)
174 {
175  Mesh &mesh = *fes.GetMesh();
176 
177  // Initialization of the offsets
178  for (int i = 0; i <= ndofs; ++i)
179  {
180  gather_offsets[i] = 0;
181  }
182 
183  // Computation of scatter indices and offsets
184  int f_ind = 0;
185  for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
186  {
187  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
188  if ( face.IsNonconformingCoarse() )
189  {
190  // We skip nonconforming coarse faces as they are treated
191  // by the corresponding nonconforming fine faces.
192  continue;
193  }
194  else if (face_type==FaceType::Interior && face.IsInterior())
195  {
196  if ( face.IsConforming() )
197  {
198  interpolations.RegisterFaceConformingInterpolation(face,f_ind);
199  SetFaceDofsScatterIndices(face, f_ind, ordering);
200  f_ind++;
201  }
202  else // Non-conforming face
203  {
204  SetFaceDofsScatterIndices(face, f_ind, ordering);
205  if ( face.element[0].conformity==Mesh::ElementConformity::Superset )
206  {
207  // In this case the local face is the master (coarse) face, thus
208  // we need to interpolate the values on the slave (fine) face.
209  interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
210  }
211  else
212  {
213  // Treated as a conforming face since we only extract values from
214  // the local slave (fine) face.
215  interpolations.RegisterFaceConformingInterpolation(face,f_ind);
216  }
217  f_ind++;
218  }
219  }
220  else if (face_type==FaceType::Boundary && face.IsBoundary())
221  {
222  SetFaceDofsScatterIndices(face, f_ind, ordering);
223  f_ind++;
224  }
225  }
226  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
227 
228  // Summation of the offsets
229  for (int i = 1; i <= ndofs; ++i)
230  {
231  gather_offsets[i] += gather_offsets[i - 1];
232  }
233 
234  // Transform the interpolation matrix map into a contiguous memory structure.
235  interpolations.LinearizeInterpolatorMapIntoVector();
236  interpolations.InitializeNCInterpConfig();
237 }
238 
239 void ParNCH1FaceRestriction::ComputeGatherIndices(
240  const ElementDofOrdering ordering,
241  const FaceType face_type)
242 {
243  Mesh &mesh = *fes.GetMesh();
244 
245  // Computation of gather_indices
246  int f_ind = 0;
247  for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
248  {
249  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
250  if ( face.IsNonconformingCoarse() )
251  {
252  // We skip nonconforming coarse faces as they are treated
253  // by the corresponding nonconforming fine faces.
254  continue;
255  }
256  else if (face.IsOfFaceType(face_type))
257  {
258  SetFaceDofsGatherIndices(face, f_ind, ordering);
259  f_ind++;
260  }
261  }
262  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
263 
264  // Reset offsets to their correct value
265  for (int i = ndofs; i > 0; --i)
266  {
267  gather_offsets[i] = gather_offsets[i - 1];
268  }
269  gather_offsets[0] = 0;
270 }
271 
272 ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &fes,
273  ElementDofOrdering ordering,
274  FaceType type,
275  L2FaceValues m,
276  bool build)
277  : L2FaceRestriction(fes, ordering, type, m, false)
278 {
279  if (!build) { return; }
280  if (nf==0) { return; }
281 
282  CheckFESpace(ordering);
283 
284  ComputeScatterIndicesAndOffsets(ordering, type);
285 
286  ComputeGatherIndices(ordering, type);
287 }
288 
290  ElementDofOrdering ordering,
291  FaceType type,
292  L2FaceValues m)
293  : ParL2FaceRestriction(fes, ordering, type, m, true)
294 { }
295 
297  const Vector& x, Vector& y) const
298 {
299  MFEM_ASSERT(
301  "This method should be called when m == L2FaceValues::DoubleValued.");
302  const ParFiniteElementSpace &pfes =
303  static_cast<const ParFiniteElementSpace&>(this->fes);
304  ParGridFunction x_gf;
305  x_gf.MakeRef(const_cast<ParFiniteElementSpace*>(&pfes),
306  const_cast<Vector&>(x), 0);
307  x_gf.ExchangeFaceNbrData();
308 
309  // Assumes all elements have the same number of dofs
310  const int nface_dofs = face_dofs;
311  const int vd = vdim;
312  const bool t = byvdim;
313  const int threshold = ndofs;
314  const int nsdofs = pfes.GetFaceNbrVSize();
315  auto d_indices1 = scatter_indices1.Read();
316  auto d_indices2 = scatter_indices2.Read();
317  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
318  auto d_x_shared = Reshape(x_gf.FaceNbrData().Read(),
319  t?vd:nsdofs, t?nsdofs:vd);
320  auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
321  MFEM_FORALL(i, nfdofs,
322  {
323  const int dof = i % nface_dofs;
324  const int face = i / nface_dofs;
325  const int idx1 = d_indices1[i];
326  for (int c = 0; c < vd; ++c)
327  {
328  d_y(dof, c, 0, face) = d_x(t?c:idx1, t?idx1:c);
329  }
330  const int idx2 = d_indices2[i];
331  for (int c = 0; c < vd; ++c)
332  {
333  if (idx2>-1 && idx2<threshold) // interior face
334  {
335  d_y(dof, c, 1, face) = d_x(t?c:idx2, t?idx2:c);
336  }
337  else if (idx2>=threshold) // shared boundary
338  {
339  d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
340  t?(idx2-threshold):c);
341  }
342  else // true boundary
343  {
344  d_y(dof, c, 1, face) = 0.0;
345  }
346  }
347  });
348 }
349 
350 void ParL2FaceRestriction::Mult(const Vector& x, Vector& y) const
351 {
352  if (nf==0) { return; }
354  {
356  }
357  else
358  {
360  }
361 }
362 
363 static MFEM_HOST_DEVICE int AddNnz(const int iE, int *I, const int dofs)
364 {
365  int val = AtomicAdd(I[iE],dofs);
366  return val;
367 }
368 
370  const bool keep_nbr_block) const
371 {
372  if (keep_nbr_block)
373  {
374  return L2FaceRestriction::FillI(mat, keep_nbr_block);
375  }
376  const int nface_dofs = face_dofs;
377  const int Ndofs = ndofs;
378  auto d_indices1 = scatter_indices1.Read();
379  auto d_indices2 = scatter_indices2.Read();
380  auto I = mat.ReadWriteI();
381  MFEM_FORALL(fdof, nf*nface_dofs,
382  {
383  const int f = fdof/nface_dofs;
384  const int iF = fdof%nface_dofs;
385  const int iE1 = d_indices1[f*nface_dofs+iF];
386  if (iE1 < Ndofs)
387  {
388  AddNnz(iE1,I,nface_dofs);
389  }
390  const int iE2 = d_indices2[f*nface_dofs+iF];
391  if (iE2 < Ndofs)
392  {
393  AddNnz(iE2,I,nface_dofs);
394  }
395  });
396 }
397 
399  SparseMatrix &face_mat) const
400 {
401  const int nface_dofs = face_dofs;
402  const int Ndofs = ndofs;
403  auto d_indices1 = scatter_indices1.Read();
404  auto d_indices2 = scatter_indices2.Read();
405  auto I = mat.ReadWriteI();
406  auto I_face = face_mat.ReadWriteI();
407  MFEM_FORALL(i, ne*elem_dofs*vdim+1,
408  {
409  I_face[i] = 0;
410  });
411  MFEM_FORALL(fdof, nf*nface_dofs,
412  {
413  const int f = fdof/nface_dofs;
414  const int iF = fdof%nface_dofs;
415  const int iE1 = d_indices1[f*nface_dofs+iF];
416  if (iE1 < Ndofs)
417  {
418  for (int jF = 0; jF < nface_dofs; jF++)
419  {
420  const int jE2 = d_indices2[f*nface_dofs+jF];
421  if (jE2 < Ndofs)
422  {
423  AddNnz(iE1,I,1);
424  }
425  else
426  {
427  AddNnz(iE1,I_face,1);
428  }
429  }
430  }
431  const int iE2 = d_indices2[f*nface_dofs+iF];
432  if (iE2 < Ndofs)
433  {
434  for (int jF = 0; jF < nface_dofs; jF++)
435  {
436  const int jE1 = d_indices1[f*nface_dofs+jF];
437  if (jE1 < Ndofs)
438  {
439  AddNnz(iE2,I,1);
440  }
441  else
442  {
443  AddNnz(iE2,I_face,1);
444  }
445  }
446  }
447  });
448 }
449 
451  SparseMatrix &mat,
452  const bool keep_nbr_block) const
453 {
454  if (keep_nbr_block)
455  {
456  return L2FaceRestriction::FillJAndData(ea_data, mat, keep_nbr_block);
457  }
458  const int nface_dofs = face_dofs;
459  const int Ndofs = ndofs;
460  auto d_indices1 = scatter_indices1.Read();
461  auto d_indices2 = scatter_indices2.Read();
462  auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
463  auto I = mat.ReadWriteI();
464  auto J = mat.WriteJ();
465  auto Data = mat.WriteData();
466  MFEM_FORALL(fdof, nf*nface_dofs,
467  {
468  const int f = fdof/nface_dofs;
469  const int iF = fdof%nface_dofs;
470  const int iE1 = d_indices1[f*nface_dofs+iF];
471  if (iE1 < Ndofs)
472  {
473  const int offset = AddNnz(iE1,I,nface_dofs);
474  for (int jF = 0; jF < nface_dofs; jF++)
475  {
476  const int jE2 = d_indices2[f*nface_dofs+jF];
477  J[offset+jF] = jE2;
478  Data[offset+jF] = mat_fea(jF,iF,1,f);
479  }
480  }
481  const int iE2 = d_indices2[f*nface_dofs+iF];
482  if (iE2 < Ndofs)
483  {
484  const int offset = AddNnz(iE2,I,nface_dofs);
485  for (int jF = 0; jF < nface_dofs; jF++)
486  {
487  const int jE1 = d_indices1[f*nface_dofs+jF];
488  J[offset+jF] = jE1;
489  Data[offset+jF] = mat_fea(jF,iF,0,f);
490  }
491  }
492  });
493 }
494 
496  SparseMatrix &mat,
497  SparseMatrix &face_mat) const
498 {
499  const int nface_dofs = face_dofs;
500  const int Ndofs = ndofs;
501  auto d_indices1 = scatter_indices1.Read();
502  auto d_indices2 = scatter_indices2.Read();
503  auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
504  auto I = mat.ReadWriteI();
505  auto I_face = face_mat.ReadWriteI();
506  auto J = mat.WriteJ();
507  auto J_face = face_mat.WriteJ();
508  auto Data = mat.WriteData();
509  auto Data_face = face_mat.WriteData();
510  MFEM_FORALL(fdof, nf*nface_dofs,
511  {
512  const int f = fdof/nface_dofs;
513  const int iF = fdof%nface_dofs;
514  const int iE1 = d_indices1[f*nface_dofs+iF];
515  if (iE1 < Ndofs)
516  {
517  for (int jF = 0; jF < nface_dofs; jF++)
518  {
519  const int jE2 = d_indices2[f*nface_dofs+jF];
520  if (jE2 < Ndofs)
521  {
522  const int offset = AddNnz(iE1,I,1);
523  J[offset] = jE2;
524  Data[offset] = mat_fea(jF,iF,1,f);
525  }
526  else
527  {
528  const int offset = AddNnz(iE1,I_face,1);
529  J_face[offset] = jE2-Ndofs;
530  Data_face[offset] = mat_fea(jF,iF,1,f);
531  }
532  }
533  }
534  const int iE2 = d_indices2[f*nface_dofs+iF];
535  if (iE2 < Ndofs)
536  {
537  for (int jF = 0; jF < nface_dofs; jF++)
538  {
539  const int jE1 = d_indices1[f*nface_dofs+jF];
540  if (jE1 < Ndofs)
541  {
542  const int offset = AddNnz(iE2,I,1);
543  J[offset] = jE1;
544  Data[offset] = mat_fea(jF,iF,0,f);
545  }
546  else
547  {
548  const int offset = AddNnz(iE2,I_face,1);
549  J_face[offset] = jE1-Ndofs;
550  Data_face[offset] = mat_fea(jF,iF,0,f);
551  }
552  }
553  }
554  });
555 }
556 
557 void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets(
558  const ElementDofOrdering ordering,
559  const FaceType type)
560 {
561  Mesh &mesh = *fes.GetMesh();
562  const ParFiniteElementSpace &pfes =
563  static_cast<const ParFiniteElementSpace&>(this->fes);
564 
565  // Initialization of the offsets
566  for (int i = 0; i <= ndofs; ++i)
567  {
568  gather_offsets[i] = 0;
569  }
570 
571  // Computation of scatter indices and offsets
572  int f_ind=0;
573  for (int f = 0; f < pfes.GetNF(); ++f)
574  {
575  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
576  if (type==FaceType::Interior && face.IsInterior())
577  {
578  SetFaceDofsScatterIndices1(face,f_ind);
580  {
581  if (face.IsShared())
582  {
584  }
585  else
586  {
588  }
589  }
590  f_ind++;
591  }
592  else if (type==FaceType::Boundary && face.IsBoundary())
593  {
594  SetFaceDofsScatterIndices1(face,f_ind);
596  {
597  SetBoundaryDofsScatterIndices2(face,f_ind);
598  }
599  f_ind++;
600  }
601  }
602  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
603 
604  // Summation of the offsets
605  for (int i = 1; i <= ndofs; ++i)
606  {
607  gather_offsets[i] += gather_offsets[i - 1];
608  }
609 }
610 
611 
612 void ParL2FaceRestriction::ComputeGatherIndices(
613  const ElementDofOrdering ordering,
614  const FaceType type)
615 {
616  Mesh &mesh = *fes.GetMesh();
617 
618  // Computation of gather_indices
619  int f_ind = 0;
620  for (int f = 0; f < fes.GetNF(); ++f)
621  {
622  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
623  if (face.IsOfFaceType(type))
624  {
625  SetFaceDofsGatherIndices1(face,f_ind);
627  type==FaceType::Interior &&
628  face.IsLocal())
629  {
631  }
632  f_ind++;
633  }
634  }
635  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
636 
637  // Reset offsets to their correct value
638  for (int i = ndofs; i > 0; --i)
639  {
640  gather_offsets[i] = gather_offsets[i - 1];
641  }
642  gather_offsets[0] = 0;
643 }
644 
646  ElementDofOrdering ordering,
647  FaceType type,
648  L2FaceValues m)
649  : L2FaceRestriction(fes, ordering, type, m, false),
650  NCL2FaceRestriction(fes, ordering, type, m, false),
651  ParL2FaceRestriction(fes, ordering, type, m, false)
652 {
653  if (nf==0) { return; }
654  x_interp.UseDevice(true);
655 
656  CheckFESpace(ordering);
657 
658  ComputeScatterIndicesAndOffsets(ordering, type);
659 
660  ComputeGatherIndices(ordering, type);
661 }
662 
664  const Vector& x, Vector& y) const
665 {
666  MFEM_ASSERT(
668  "This method should be called when m == L2FaceValues::SingleValued.");
669  // Assumes all elements have the same number of dofs
670  const int nface_dofs = face_dofs;
671  const int vd = vdim;
672  const bool t = byvdim;
673  const int threshold = ndofs;
674  auto d_indices1 = scatter_indices1.Read();
675  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
676  auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
677  auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
678  auto interpolators = interpolations.GetInterpolators().Read();
679  const int nc_size = interpolations.GetNumInterpolators();
680  auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
681  static constexpr int max_nd = 16*16;
682  MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
683  MFEM_FORALL_3D(face, nf, nface_dofs, 1, 1,
684  {
685  MFEM_SHARED double dof_values[max_nd];
686  const InterpConfig conf = interp_config_ptr[face];
687  const int master_side = conf.master_side;
688  const int interp_index = conf.index;
689  const int side = 0;
690  if ( !conf.is_non_conforming || side!=master_side )
691  {
692  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
693  {
694  const int i = face*nface_dofs + dof;
695  const int idx = d_indices1[i];
696  if (idx>-1 && idx<threshold) // interior face
697  {
698  for (int c = 0; c < vd; ++c)
699  {
700  d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
701  }
702  }
703  else // true boundary
704  {
705  for (int c = 0; c < vd; ++c)
706  {
707  d_y(dof, c, face) = 0.0;
708  }
709  }
710  }
711  }
712  else // Interpolation from coarse to fine
713  {
714  for (int c = 0; c < vd; ++c)
715  {
716  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
717  {
718  const int i = face*nface_dofs + dof;
719  const int idx = d_indices1[i];
720  if (idx>-1 && idx<threshold) // interior face
721  {
722  dof_values[dof] = d_x(t?c:idx, t?idx:c);
723  }
724  else // true boundary
725  {
726  dof_values[dof] = 0.0;
727  }
728  }
729  MFEM_SYNC_THREAD;
730  MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
731  {
732  double res = 0.0;
733  for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
734  {
735  res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
736  }
737  d_y(dof_out, c, face) = res;
738  }
739  MFEM_SYNC_THREAD;
740  }
741  }
742  });
743 }
744 
746  const Vector& x, Vector& y) const
747 {
750 }
751 
753 {
754  if (nf==0) { return; }
756  {
758  }
759  else if ( type==FaceType::Boundary && m==L2FaceValues::DoubleValued )
760  {
762  }
763  else if ( type==FaceType::Interior && m==L2FaceValues::SingleValued )
764  {
766  }
767  else if ( type==FaceType::Boundary && m==L2FaceValues::SingleValued )
768  {
770  }
771  else
772  {
773  MFEM_ABORT("Unknown type and multiplicity combination.");
774  }
775 }
776 
778 {
779  if (nf==0) { return; }
780  if (type==FaceType::Interior)
781  {
783  {
786  }
787  else // Single Valued
788  {
791  }
792  }
793  else
794  {
796  {
798  }
799  else // Single valued
800  {
802  }
803  }
804 }
805 
807 {
808  if (nf==0) { return; }
809  if (type==FaceType::Interior)
810  {
812  {
815  }
816  else if ( m==L2FaceValues::SingleValued )
817  {
820  }
821  }
822  else
823  {
825  {
827  }
828  else if ( m==L2FaceValues::SingleValued )
829  {
831  }
832  }
833 }
834 
836  const bool keep_nbr_block) const
837 {
838  MFEM_ABORT("Not yet implemented.");
839 }
840 
842  SparseMatrix &face_mat) const
843 {
844  MFEM_ABORT("Not yet implemented.");
845 }
846 
848  SparseMatrix &mat,
849  const bool keep_nbr_block) const
850 {
851  MFEM_ABORT("Not yet implemented.");
852 }
853 
855  SparseMatrix &mat,
856  SparseMatrix &face_mat) const
857 {
858  MFEM_ABORT("Not yet implemented.");
859 }
860 
861 void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets(
862  const ElementDofOrdering ordering,
863  const FaceType type)
864 {
865  Mesh &mesh = *fes.GetMesh();
866 
867  // Initialization of the offsets
868  for (int i = 0; i <= ndofs; ++i)
869  {
870  gather_offsets[i] = 0;
871  }
872 
873  // Computation of scatter and offsets indices
874  int f_ind=0;
875  for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
876  {
877  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
878  if ( face.IsNonconformingCoarse() )
879  {
880  // We skip nonconforming coarse faces as they are treated
881  // by the corresponding nonconforming fine faces.
882  continue;
883  }
884  else if ( type==FaceType::Interior && face.IsInterior() )
885  {
886  if ( face.IsConforming() )
887  {
889  SetFaceDofsScatterIndices1(face,f_ind);
891  {
892  if ( face.IsShared() )
893  {
895  }
896  else
897  {
899  }
900  }
901  }
902  else // Non-conforming face
903  {
905  SetFaceDofsScatterIndices1(face,f_ind);
907  {
908  if ( face.IsShared() )
909  {
911  }
912  else // local nonconforming slave
913  {
915  }
916  }
917  }
918  f_ind++;
919  }
920  else if (type==FaceType::Boundary && face.IsBoundary())
921  {
922  SetFaceDofsScatterIndices1(face,f_ind);
924  {
925  SetBoundaryDofsScatterIndices2(face,f_ind);
926  }
927  f_ind++;
928  }
929  }
930  MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
931  (type==FaceType::Interior? "interior" : "boundary") <<
932  " faces: " << f_ind << " vs " << nf );
933 
934  // Summation of the offsets
935  for (int i = 1; i <= ndofs; ++i)
936  {
937  gather_offsets[i] += gather_offsets[i - 1];
938  }
939 
940  // Transform the interpolation matrix map into a contiguous memory structure.
943 }
944 
945 void ParNCL2FaceRestriction::ComputeGatherIndices(
946  const ElementDofOrdering ordering,
947  const FaceType type)
948 {
949  Mesh &mesh = *fes.GetMesh();
950 
951  // Computation of gather_indices
952  int f_ind = 0;
953  for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
954  {
955  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
956  if ( face.IsNonconformingCoarse() )
957  {
958  // We skip nonconforming coarse faces as they are treated
959  // by the corresponding nonconforming fine faces.
960  continue;
961  }
962  else if ( face.IsOfFaceType(type) )
963  {
964  SetFaceDofsGatherIndices1(face,f_ind);
966  type==FaceType::Interior &&
967  face.IsLocal())
968  {
970  }
971  f_ind++;
972  }
973  }
974  MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
975  (type==FaceType::Interior? "interior" : "boundary") <<
976  " faces: " << f_ind << " vs " << nf );
977 
978  // Switch back offsets to their correct value
979  for (int i = ndofs; i > 0; --i)
980  {
981  gather_offsets[i] = gather_offsets[i - 1];
982  }
983  gather_offsets[0] = 0;
984 }
985 
986 } // namespace mfem
987 
988 #endif
void DoubleValuedNonconformingTransposeInterpolationInPlace(Vector &x) const
Apply a change of basis from fine element basis to coarse element basis for the coarse face dofs...
void Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
void Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
FaceInformation GetFaceInformation(int f) const
Definition: mesh.cpp:1131
Operator that extracts Face degrees of freedom in parallel.
void DoubleValuedNonconformingMult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with no...
void PermuteAndSetSharedFaceDofsScatterIndices2(const Mesh::FaceInformation &face, const int face_index)
Permute and set the scattering indices of elem2 for the shared face described by the face...
Operator that extracts Face degrees of freedom for H1 FiniteElementSpaces.
void Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
ParL2FaceRestriction(const ParFiniteElementSpace &fes, ElementDofOrdering ordering, FaceType type, L2FaceValues m, bool build)
Constructs an ParL2FaceRestriction.
ParNCH1FaceRestriction(const ParFiniteElementSpace &fes, ElementDofOrdering ordering, FaceType type)
Constructs an ParNCH1FaceRestriction.
void SetBoundaryDofsScatterIndices2(const Mesh::FaceInformation &face, const int face_index)
Set the scattering indices of elem2 for the boundary face described by the face.
MFEM_HOST_DEVICE T AtomicAdd(T &add, const T val)
Definition: backends.hpp:84
InterpolationManager interpolations
int Size() const
Returns the size of the vector.
Definition: vector.hpp:200
Operator that extracts face degrees of freedom for L2 nonconforming spaces.
const Array< NCInterpConfig > & GetNCFaceInterpConfig() const
Return an array containing the interpolation configuration for each face registered with RegisterFace...
Abstract parallel finite element space.
Definition: pfespace.hpp:28
void SetFaceDofsGatherIndices1(const Mesh::FaceInformation &face, const int face_index)
Set the gathering indices of elem1 for the interior face described by the face.
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition: vector.hpp:118
void DoubleValuedNonconformingInterpolation(Vector &x) const
Apply a change of basis from coarse element basis to fine element basis for the coarse face dofs...
const L2FaceValues m
void Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
void DoubleValuedConformingAddMultTranspose(const Vector &x, Vector &y) const
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector. Should only be used with con...
ParNCL2FaceRestriction(const ParFiniteElementSpace &fes, ElementDofOrdering ordering, FaceType type, L2FaceValues m=L2FaceValues::DoubleValued)
Constructs an ParNCL2FaceRestriction.
void PermuteAndSetFaceDofsGatherIndices2(const Mesh::FaceInformation &face, const int face_index)
Permute and set the gathering indices of elem2 for the interior face described by the face...
virtual void MakeRef(FiniteElementSpace *f, double *v)
Make the ParGridFunction reference external data on a new FiniteElementSpace.
Definition: pgridfunc.cpp:102
void AddMultTransposeInPlace(Vector &x, Vector &y) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector.
FaceType
Definition: mesh.hpp:45
void RegisterFaceConformingInterpolation(const Mesh::FaceInformation &face, int face_index)
Register the face with face and index face_index as a conforming face for the interpolation of the de...
const FiniteElementSpace & fes
double f(const Vector &xvec)
Definition: lor_mms.hpp:32
void PermuteAndSetFaceDofsScatterIndices2(const Mesh::FaceInformation &face, const int face_index)
Permute and set the scattering indices of elem2, and increment the offsets for the face described by ...
Data type sparse matrix.
Definition: sparsemat.hpp:50
void SingleValuedConformingMult(const Vector &x, Vector &y) const
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with co...
Mesh * GetMesh() const
Returns the mesh.
Definition: fespace.hpp:441
Operator that extracts Face degrees of freedom for L2 spaces.
virtual double * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:457
void CheckFESpace(const ElementDofOrdering ordering)
Verify that H1FaceRestriction is build from an H1 FESpace.
void LinearizeInterpolatorMapIntoVector()
Transform the interpolation matrix map into a contiguous memory structure.
int * ReadWriteI(bool on_dev=true)
Definition: sparsemat.hpp:243
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition: array.hpp:304
void FillJAndData(const Vector &fea_data, SparseMatrix &mat, SparseMatrix &face_mat) const
int GetNF() const
Returns number of faces (i.e. co-dimension 1 entities) in the mesh.
Definition: fespace.hpp:620
Vector & FaceNbrData()
Definition: pgridfunc.hpp:208
Array< int > gather_offsets
void DoubleValuedConformingMult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with co...
void SingleValuedNonconformingMult(const Vector &x, Vector &y) const
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with no...
int GetNumInterpolators() const
Return the total number of interpolators.
int * WriteJ(bool on_dev=true)
Definition: sparsemat.hpp:257
void SingleValuedConformingAddMultTranspose(const Vector &x, Vector &y) const
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector. Should only be used with con...
Array< int > scatter_indices1
void CheckFESpace(const ElementDofOrdering ordering)
Verify that L2FaceRestriction is build from an L2 FESpace.
virtual double * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:465
int GetNumFacesWithGhost() const
Return the number of faces (3D), edges (2D) or vertices (1D) including ghost faces.
Definition: mesh.cpp:5387
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition: fespace.hpp:74
int GetFaceNbrVSize() const
Definition: pfespace.hpp:394
RefCoord t[3]
void SingleValuedNonconformingTransposeInterpolation(const Vector &x) const
Apply a change of basis from fine element basis to coarse element basis for the coarse face dofs...
void SingleValuedNonconformingTransposeInterpolationInPlace(Vector &x) const
Apply a change of basis from fine element basis to coarse element basis for the coarse face dofs...
Vector data type.
Definition: vector.hpp:60
void DoubleValuedNonconformingTransposeInterpolation(const Vector &x) const
Apply a change of basis from fine element basis to coarse element basis for the coarse face dofs...
void FillJAndData(const Vector &ea_data, SparseMatrix &mat, SparseMatrix &face_mat) const
void NonconformingInterpolation(Vector &x) const
Apply a change of basis from coarse element basis to fine element basis for the coarse face dofs...
virtual void FillJAndData(const Vector &fea_data, SparseMatrix &mat, const bool keep_nbr_block=false) const
Fill the J and Data arrays of the SparseMatrix corresponding to the sparsity pattern given by this L2...
void SetFaceDofsScatterIndices1(const Mesh::FaceInformation &face, const int face_index)
Set the scattering indices of elem1, and increment the offsets for the face described by the face...
void RegisterFaceCoarseToFineInterpolation(const Mesh::FaceInformation &face, int face_index)
Register the face with face and index face_index as a conforming face for the interpolation of the de...
Class for parallel grid function.
Definition: pgridfunc.hpp:32
uint32_t is_non_conforming
const Array< InterpConfig > & GetFaceInterpConfig() const
Return an array containing the interpolation configuration for each face registered with RegisterFace...
void AddMultTranspose(const Vector &x, Vector &y) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector.
virtual const double * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:449
InterpolationManager interpolations
void FillI(SparseMatrix &mat, const bool keep_nbr_block=false) const override
Array< int > scatter_indices2
virtual void FillI(SparseMatrix &mat, const bool keep_nbr_block=false) const
Fill the I array of SparseMatrix corresponding to the sparsity pattern given by this L2FaceRestrictio...
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims...dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
Definition: dtensor.hpp:131
double f(const Vector &p)
double * WriteData(bool on_dev=true)
Definition: sparsemat.hpp:273
void FillI(SparseMatrix &mat, const bool keep_nbr_block=false) const override
Fill the I array of SparseMatrix corresponding to the sparsity pattern given by this ParNCL2FaceRestr...
const Vector & GetInterpolators() const
Return an mfem::Vector containing the interpolators in the following format: face_dofs x face_dofs x ...