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