MFEM  v4.5.2
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 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,
95  const double a) const
96 {
97  MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
98  if (nf==0) { return; }
99  NonconformingTransposeInterpolation(x);
100  H1FaceRestriction::AddMultTranspose(x_interp, y);
101 }
102 
103 void ParNCH1FaceRestriction::AddMultTransposeInPlace(Vector &x, Vector &y) const
104 {
105  if (nf==0) { return; }
106  NonconformingTransposeInterpolationInPlace(x);
107  H1FaceRestriction::AddMultTranspose(x, y);
108 }
109 
110 void ParNCH1FaceRestriction::NonconformingTransposeInterpolation(
111  const Vector& x) const
112 {
113  if (x_interp.Size()==0)
114  {
115  x_interp.SetSize(x.Size());
116  }
117  x_interp = x;
118  NonconformingTransposeInterpolationInPlace(x_interp);
119 }
120 
121 void ParNCH1FaceRestriction::NonconformingTransposeInterpolationInPlace(
122  Vector& x) const
123 {
124  // Assumes all elements have the same number of dofs
125  const int nface_dofs = face_dofs;
126  const int vd = vdim;
127  if ( type==FaceType::Interior )
128  {
129  // Interpolation from slave to master face dofs
130  auto d_x = Reshape(x.ReadWrite(), nface_dofs, vd, nf);
131  auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
132  const int num_nc_faces = nc_interp_config.Size();
133  if ( num_nc_faces == 0 ) { return; }
134  auto interp_config_ptr = nc_interp_config.Read();
135  const int nc_size = interpolations.GetNumInterpolators();
136  auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
137  nface_dofs, nface_dofs, nc_size);
138  static constexpr int max_nd = 1024;
139  MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
140  MFEM_FORALL_3D(nc_face, num_nc_faces, nface_dofs, 1, 1,
141  {
142  MFEM_SHARED double dof_values[max_nd];
143  const NCInterpConfig conf = interp_config_ptr[nc_face];
144  const int master_side = conf.master_side;
145  if ( conf.is_non_conforming && master_side==0 )
146  {
147  const int interp_index = conf.index;
148  const int face = conf.face_index;
149  // Interpolation from fine to coarse
150  for (int c = 0; c < vd; ++c)
151  {
152  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
153  {
154  dof_values[dof] = d_x(dof, c, face);
155  }
156  MFEM_SYNC_THREAD;
157  MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
158  {
159  double res = 0.0;
160  for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
161  {
162  res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
163  }
164  d_x(dof_out, c, face) = res;
165  }
166  MFEM_SYNC_THREAD;
167  }
168  }
169  });
170  }
171 }
172 
173 void ParNCH1FaceRestriction::ComputeScatterIndicesAndOffsets(
174  const ElementDofOrdering ordering,
175  const FaceType face_type)
176 {
177  Mesh &mesh = *fes.GetMesh();
178 
179  // Initialization of the offsets
180  for (int i = 0; i <= ndofs; ++i)
181  {
182  gather_offsets[i] = 0;
183  }
184 
185  // Computation of scatter indices and offsets
186  int f_ind = 0;
187  for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
188  {
189  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
190  if ( face.IsNonconformingCoarse() )
191  {
192  // We skip nonconforming coarse faces as they are treated
193  // by the corresponding nonconforming fine faces.
194  continue;
195  }
196  else if (face_type==FaceType::Interior && face.IsInterior())
197  {
198  if ( face.IsConforming() )
199  {
200  interpolations.RegisterFaceConformingInterpolation(face,f_ind);
201  SetFaceDofsScatterIndices(face, f_ind, ordering);
202  f_ind++;
203  }
204  else // Non-conforming face
205  {
206  SetFaceDofsScatterIndices(face, f_ind, ordering);
207  if ( face.element[0].conformity==Mesh::ElementConformity::Superset )
208  {
209  // In this case the local face is the master (coarse) face, thus
210  // we need to interpolate the values on the slave (fine) face.
211  interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
212  }
213  else
214  {
215  // Treated as a conforming face since we only extract values from
216  // the local slave (fine) face.
217  interpolations.RegisterFaceConformingInterpolation(face,f_ind);
218  }
219  f_ind++;
220  }
221  }
222  else if (face_type==FaceType::Boundary && face.IsBoundary())
223  {
224  SetFaceDofsScatterIndices(face, f_ind, ordering);
225  f_ind++;
226  }
227  }
228  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
229 
230  // Summation of the offsets
231  for (int i = 1; i <= ndofs; ++i)
232  {
233  gather_offsets[i] += gather_offsets[i - 1];
234  }
235 
236  // Transform the interpolation matrix map into a contiguous memory structure.
237  interpolations.LinearizeInterpolatorMapIntoVector();
238  interpolations.InitializeNCInterpConfig();
239 }
240 
241 void ParNCH1FaceRestriction::ComputeGatherIndices(
242  const ElementDofOrdering ordering,
243  const FaceType face_type)
244 {
245  Mesh &mesh = *fes.GetMesh();
246 
247  // Computation of gather_indices
248  int f_ind = 0;
249  for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
250  {
251  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
252  if ( face.IsNonconformingCoarse() )
253  {
254  // We skip nonconforming coarse faces as they are treated
255  // by the corresponding nonconforming fine faces.
256  continue;
257  }
258  else if (face.IsOfFaceType(face_type))
259  {
260  SetFaceDofsGatherIndices(face, f_ind, ordering);
261  f_ind++;
262  }
263  }
264  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
265 
266  // Reset offsets to their correct value
267  for (int i = ndofs; i > 0; --i)
268  {
269  gather_offsets[i] = gather_offsets[i - 1];
270  }
271  gather_offsets[0] = 0;
272 }
273 
274 ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &fes,
275  ElementDofOrdering ordering,
276  FaceType type,
277  L2FaceValues m,
278  bool build)
279  : L2FaceRestriction(fes, ordering, type, m, false)
280 {
281  if (!build) { return; }
282  if (nf==0) { return; }
283 
284  CheckFESpace(ordering);
285 
286  ComputeScatterIndicesAndOffsets(ordering, type);
287 
288  ComputeGatherIndices(ordering, type);
289 }
290 
292  ElementDofOrdering ordering,
293  FaceType type,
294  L2FaceValues m)
295  : ParL2FaceRestriction(fes, ordering, type, m, true)
296 { }
297 
299  const Vector& x, Vector& y) const
300 {
301  MFEM_ASSERT(
303  "This method should be called when m == L2FaceValues::DoubleValued.");
304  const ParFiniteElementSpace &pfes =
305  static_cast<const ParFiniteElementSpace&>(this->fes);
306  ParGridFunction x_gf;
307  x_gf.MakeRef(const_cast<ParFiniteElementSpace*>(&pfes),
308  const_cast<Vector&>(x), 0);
309  x_gf.ExchangeFaceNbrData();
310 
311  // Assumes all elements have the same number of dofs
312  const int nface_dofs = face_dofs;
313  const int vd = vdim;
314  const bool t = byvdim;
315  const int threshold = ndofs;
316  const int nsdofs = pfes.GetFaceNbrVSize();
317  auto d_indices1 = scatter_indices1.Read();
318  auto d_indices2 = scatter_indices2.Read();
319  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
320  auto d_x_shared = Reshape(x_gf.FaceNbrData().Read(),
321  t?vd:nsdofs, t?nsdofs:vd);
322  auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
323  MFEM_FORALL(i, nfdofs,
324  {
325  const int dof = i % nface_dofs;
326  const int face = i / nface_dofs;
327  const int idx1 = d_indices1[i];
328  for (int c = 0; c < vd; ++c)
329  {
330  d_y(dof, c, 0, face) = d_x(t?c:idx1, t?idx1:c);
331  }
332  const int idx2 = d_indices2[i];
333  for (int c = 0; c < vd; ++c)
334  {
335  if (idx2>-1 && idx2<threshold) // interior face
336  {
337  d_y(dof, c, 1, face) = d_x(t?c:idx2, t?idx2:c);
338  }
339  else if (idx2>=threshold) // shared boundary
340  {
341  d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
342  t?(idx2-threshold):c);
343  }
344  else // true boundary
345  {
346  d_y(dof, c, 1, face) = 0.0;
347  }
348  }
349  });
350 }
351 
352 void ParL2FaceRestriction::Mult(const Vector& x, Vector& y) const
353 {
354  if (nf==0) { return; }
356  {
358  }
359  else
360  {
362  }
363 }
364 
365 static MFEM_HOST_DEVICE int AddNnz(const int iE, int *I, const int dofs)
366 {
367  int val = AtomicAdd(I[iE],dofs);
368  return val;
369 }
370 
372  const bool keep_nbr_block) const
373 {
374  if (keep_nbr_block)
375  {
376  return L2FaceRestriction::FillI(mat, keep_nbr_block);
377  }
378  const int nface_dofs = face_dofs;
379  const int Ndofs = ndofs;
380  auto d_indices1 = scatter_indices1.Read();
381  auto d_indices2 = scatter_indices2.Read();
382  auto I = mat.ReadWriteI();
383  MFEM_FORALL(fdof, nf*nface_dofs,
384  {
385  const int f = fdof/nface_dofs;
386  const int iF = fdof%nface_dofs;
387  const int iE1 = d_indices1[f*nface_dofs+iF];
388  if (iE1 < Ndofs)
389  {
390  AddNnz(iE1,I,nface_dofs);
391  }
392  const int iE2 = d_indices2[f*nface_dofs+iF];
393  if (iE2 < Ndofs)
394  {
395  AddNnz(iE2,I,nface_dofs);
396  }
397  });
398 }
399 
401  SparseMatrix &face_mat) const
402 {
403  const int nface_dofs = face_dofs;
404  const int Ndofs = ndofs;
405  auto d_indices1 = scatter_indices1.Read();
406  auto d_indices2 = scatter_indices2.Read();
407  auto I = mat.ReadWriteI();
408  auto I_face = face_mat.ReadWriteI();
409  MFEM_FORALL(i, ne*elem_dofs*vdim+1,
410  {
411  I_face[i] = 0;
412  });
413  MFEM_FORALL(fdof, nf*nface_dofs,
414  {
415  const int f = fdof/nface_dofs;
416  const int iF = fdof%nface_dofs;
417  const int iE1 = d_indices1[f*nface_dofs+iF];
418  if (iE1 < Ndofs)
419  {
420  for (int jF = 0; jF < nface_dofs; jF++)
421  {
422  const int jE2 = d_indices2[f*nface_dofs+jF];
423  if (jE2 < Ndofs)
424  {
425  AddNnz(iE1,I,1);
426  }
427  else
428  {
429  AddNnz(iE1,I_face,1);
430  }
431  }
432  }
433  const int iE2 = d_indices2[f*nface_dofs+iF];
434  if (iE2 < Ndofs)
435  {
436  for (int jF = 0; jF < nface_dofs; jF++)
437  {
438  const int jE1 = d_indices1[f*nface_dofs+jF];
439  if (jE1 < Ndofs)
440  {
441  AddNnz(iE2,I,1);
442  }
443  else
444  {
445  AddNnz(iE2,I_face,1);
446  }
447  }
448  }
449  });
450 }
451 
453  SparseMatrix &mat,
454  const bool keep_nbr_block) const
455 {
456  if (keep_nbr_block)
457  {
458  return L2FaceRestriction::FillJAndData(ea_data, mat, keep_nbr_block);
459  }
460  const int nface_dofs = face_dofs;
461  const int Ndofs = ndofs;
462  auto d_indices1 = scatter_indices1.Read();
463  auto d_indices2 = scatter_indices2.Read();
464  auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
465  auto I = mat.ReadWriteI();
466  auto J = mat.WriteJ();
467  auto Data = mat.WriteData();
468  MFEM_FORALL(fdof, nf*nface_dofs,
469  {
470  const int f = fdof/nface_dofs;
471  const int iF = fdof%nface_dofs;
472  const int iE1 = d_indices1[f*nface_dofs+iF];
473  if (iE1 < Ndofs)
474  {
475  const int offset = AddNnz(iE1,I,nface_dofs);
476  for (int jF = 0; jF < nface_dofs; jF++)
477  {
478  const int jE2 = d_indices2[f*nface_dofs+jF];
479  J[offset+jF] = jE2;
480  Data[offset+jF] = mat_fea(jF,iF,1,f);
481  }
482  }
483  const int iE2 = d_indices2[f*nface_dofs+iF];
484  if (iE2 < Ndofs)
485  {
486  const int offset = AddNnz(iE2,I,nface_dofs);
487  for (int jF = 0; jF < nface_dofs; jF++)
488  {
489  const int jE1 = d_indices1[f*nface_dofs+jF];
490  J[offset+jF] = jE1;
491  Data[offset+jF] = mat_fea(jF,iF,0,f);
492  }
493  }
494  });
495 }
496 
498  SparseMatrix &mat,
499  SparseMatrix &face_mat) const
500 {
501  const int nface_dofs = face_dofs;
502  const int Ndofs = ndofs;
503  auto d_indices1 = scatter_indices1.Read();
504  auto d_indices2 = scatter_indices2.Read();
505  auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
506  auto I = mat.ReadWriteI();
507  auto I_face = face_mat.ReadWriteI();
508  auto J = mat.WriteJ();
509  auto J_face = face_mat.WriteJ();
510  auto Data = mat.WriteData();
511  auto Data_face = face_mat.WriteData();
512  MFEM_FORALL(fdof, nf*nface_dofs,
513  {
514  const int f = fdof/nface_dofs;
515  const int iF = fdof%nface_dofs;
516  const int iE1 = d_indices1[f*nface_dofs+iF];
517  if (iE1 < Ndofs)
518  {
519  for (int jF = 0; jF < nface_dofs; jF++)
520  {
521  const int jE2 = d_indices2[f*nface_dofs+jF];
522  if (jE2 < Ndofs)
523  {
524  const int offset = AddNnz(iE1,I,1);
525  J[offset] = jE2;
526  Data[offset] = mat_fea(jF,iF,1,f);
527  }
528  else
529  {
530  const int offset = AddNnz(iE1,I_face,1);
531  J_face[offset] = jE2-Ndofs;
532  Data_face[offset] = mat_fea(jF,iF,1,f);
533  }
534  }
535  }
536  const int iE2 = d_indices2[f*nface_dofs+iF];
537  if (iE2 < Ndofs)
538  {
539  for (int jF = 0; jF < nface_dofs; jF++)
540  {
541  const int jE1 = d_indices1[f*nface_dofs+jF];
542  if (jE1 < Ndofs)
543  {
544  const int offset = AddNnz(iE2,I,1);
545  J[offset] = jE1;
546  Data[offset] = mat_fea(jF,iF,0,f);
547  }
548  else
549  {
550  const int offset = AddNnz(iE2,I_face,1);
551  J_face[offset] = jE1-Ndofs;
552  Data_face[offset] = mat_fea(jF,iF,0,f);
553  }
554  }
555  }
556  });
557 }
558 
559 void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets(
560  const ElementDofOrdering ordering,
561  const FaceType type)
562 {
563  Mesh &mesh = *fes.GetMesh();
564  const ParFiniteElementSpace &pfes =
565  static_cast<const ParFiniteElementSpace&>(this->fes);
566 
567  // Initialization of the offsets
568  for (int i = 0; i <= ndofs; ++i)
569  {
570  gather_offsets[i] = 0;
571  }
572 
573  // Computation of scatter indices and offsets
574  int f_ind=0;
575  for (int f = 0; f < pfes.GetNF(); ++f)
576  {
577  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
578  if (type==FaceType::Interior && face.IsInterior())
579  {
580  SetFaceDofsScatterIndices1(face,f_ind);
582  {
583  if (face.IsShared())
584  {
586  }
587  else
588  {
590  }
591  }
592  f_ind++;
593  }
594  else if (type==FaceType::Boundary && face.IsBoundary())
595  {
596  SetFaceDofsScatterIndices1(face,f_ind);
598  {
599  SetBoundaryDofsScatterIndices2(face,f_ind);
600  }
601  f_ind++;
602  }
603  }
604  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
605 
606  // Summation of the offsets
607  for (int i = 1; i <= ndofs; ++i)
608  {
609  gather_offsets[i] += gather_offsets[i - 1];
610  }
611 }
612 
613 
614 void ParL2FaceRestriction::ComputeGatherIndices(
615  const ElementDofOrdering ordering,
616  const FaceType type)
617 {
618  Mesh &mesh = *fes.GetMesh();
619 
620  // Computation of gather_indices
621  int f_ind = 0;
622  for (int f = 0; f < fes.GetNF(); ++f)
623  {
624  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
625  if (face.IsOfFaceType(type))
626  {
627  SetFaceDofsGatherIndices1(face,f_ind);
630  face.IsLocal())
631  {
633  }
634  f_ind++;
635  }
636  }
637  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
638 
639  // Reset offsets to their correct value
640  for (int i = ndofs; i > 0; --i)
641  {
642  gather_offsets[i] = gather_offsets[i - 1];
643  }
644  gather_offsets[0] = 0;
645 }
646 
648  ElementDofOrdering ordering,
649  FaceType type,
650  L2FaceValues m)
651  : L2FaceRestriction(fes, ordering, type, m, false),
652  NCL2FaceRestriction(fes, ordering, type, m, false),
653  ParL2FaceRestriction(fes, ordering, type, m, false)
654 {
655  if (nf==0) { return; }
656  x_interp.UseDevice(true);
657 
658  CheckFESpace(ordering);
659 
660  ComputeScatterIndicesAndOffsets(ordering, type);
661 
662  ComputeGatherIndices(ordering, type);
663 }
664 
666  const Vector& x, Vector& y) const
667 {
668  MFEM_ASSERT(
670  "This method should be called when m == L2FaceValues::SingleValued.");
671  // Assumes all elements have the same number of dofs
672  const int nface_dofs = face_dofs;
673  const int vd = vdim;
674  const bool t = byvdim;
675  const int threshold = ndofs;
676  auto d_indices1 = scatter_indices1.Read();
677  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
678  auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
679  auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
680  auto interpolators = interpolations.GetInterpolators().Read();
681  const int nc_size = interpolations.GetNumInterpolators();
682  auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
683  static constexpr int max_nd = 16*16;
684  MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
685  MFEM_FORALL_3D(face, nf, nface_dofs, 1, 1,
686  {
687  MFEM_SHARED double dof_values[max_nd];
688  const InterpConfig conf = interp_config_ptr[face];
689  const int master_side = conf.master_side;
690  const int interp_index = conf.index;
691  const int side = 0;
692  if ( !conf.is_non_conforming || side!=master_side )
693  {
694  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
695  {
696  const int i = face*nface_dofs + dof;
697  const int idx = d_indices1[i];
698  if (idx>-1 && idx<threshold) // interior face
699  {
700  for (int c = 0; c < vd; ++c)
701  {
702  d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
703  }
704  }
705  else // true boundary
706  {
707  for (int c = 0; c < vd; ++c)
708  {
709  d_y(dof, c, face) = 0.0;
710  }
711  }
712  }
713  }
714  else // Interpolation from coarse to fine
715  {
716  for (int c = 0; c < vd; ++c)
717  {
718  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
719  {
720  const int i = face*nface_dofs + dof;
721  const int idx = d_indices1[i];
722  if (idx>-1 && idx<threshold) // interior face
723  {
724  dof_values[dof] = d_x(t?c:idx, t?idx:c);
725  }
726  else // true boundary
727  {
728  dof_values[dof] = 0.0;
729  }
730  }
731  MFEM_SYNC_THREAD;
732  MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
733  {
734  double res = 0.0;
735  for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
736  {
737  res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
738  }
739  d_y(dof_out, c, face) = res;
740  }
741  MFEM_SYNC_THREAD;
742  }
743  }
744  });
745 }
746 
748  const Vector& x, Vector& y) const
749 {
752 }
753 
755 {
756  if (nf==0) { return; }
758  {
760  }
762  {
764  }
766  {
768  }
770  {
772  }
773  else
774  {
775  MFEM_ABORT("Unknown type and multiplicity combination.");
776  }
777 }
778 
780  const double a) const
781 {
782  MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
783  if (nf==0) { return; }
785  {
787  {
790  }
791  else // Single Valued
792  {
795  }
796  }
797  else
798  {
800  {
802  }
803  else // Single valued
804  {
806  }
807  }
808 }
809 
811 {
812  if (nf==0) { return; }
814  {
816  {
819  }
820  else if ( m==L2FaceValues::SingleValued )
821  {
824  }
825  }
826  else
827  {
829  {
831  }
832  else if ( m==L2FaceValues::SingleValued )
833  {
835  }
836  }
837 }
838 
840  const bool keep_nbr_block) const
841 {
842  MFEM_ABORT("Not yet implemented.");
843 }
844 
846  SparseMatrix &face_mat) const
847 {
848  MFEM_ABORT("Not yet implemented.");
849 }
850 
852  SparseMatrix &mat,
853  const bool keep_nbr_block) const
854 {
855  MFEM_ABORT("Not yet implemented.");
856 }
857 
859  SparseMatrix &mat,
860  SparseMatrix &face_mat) const
861 {
862  MFEM_ABORT("Not yet implemented.");
863 }
864 
865 void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets(
866  const ElementDofOrdering ordering,
867  const FaceType type)
868 {
869  Mesh &mesh = *fes.GetMesh();
870 
871  // Initialization of the offsets
872  for (int i = 0; i <= ndofs; ++i)
873  {
874  gather_offsets[i] = 0;
875  }
876 
877  // Computation of scatter and offsets indices
878  int f_ind=0;
879  for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
880  {
881  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
882  if ( face.IsNonconformingCoarse() )
883  {
884  // We skip nonconforming coarse faces as they are treated
885  // by the corresponding nonconforming fine faces.
886  continue;
887  }
888  else if ( type==FaceType::Interior && face.IsInterior() )
889  {
890  if ( face.IsConforming() )
891  {
893  SetFaceDofsScatterIndices1(face,f_ind);
895  {
896  if ( face.IsShared() )
897  {
899  }
900  else
901  {
903  }
904  }
905  }
906  else // Non-conforming face
907  {
909  SetFaceDofsScatterIndices1(face,f_ind);
911  {
912  if ( face.IsShared() )
913  {
915  }
916  else // local nonconforming slave
917  {
919  }
920  }
921  }
922  f_ind++;
923  }
924  else if (type==FaceType::Boundary && face.IsBoundary())
925  {
926  SetFaceDofsScatterIndices1(face,f_ind);
928  {
929  SetBoundaryDofsScatterIndices2(face,f_ind);
930  }
931  f_ind++;
932  }
933  }
934  MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
935  (type==FaceType::Interior? "interior" : "boundary") <<
936  " faces: " << f_ind << " vs " << nf );
937 
938  // Summation of the offsets
939  for (int i = 1; i <= ndofs; ++i)
940  {
941  gather_offsets[i] += gather_offsets[i - 1];
942  }
943 
944  // Transform the interpolation matrix map into a contiguous memory structure.
947 }
948 
949 void ParNCL2FaceRestriction::ComputeGatherIndices(
950  const ElementDofOrdering ordering,
951  const FaceType type)
952 {
953  Mesh &mesh = *fes.GetMesh();
954 
955  // Computation of gather_indices
956  int f_ind = 0;
957  for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
958  {
959  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
960  if ( face.IsNonconformingCoarse() )
961  {
962  // We skip nonconforming coarse faces as they are treated
963  // by the corresponding nonconforming fine faces.
964  continue;
965  }
966  else if ( face.IsOfFaceType(type) )
967  {
968  SetFaceDofsGatherIndices1(face,f_ind);
971  face.IsLocal())
972  {
974  }
975  f_ind++;
976  }
977  }
978  MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
979  (type==FaceType::Interior? "interior" : "boundary") <<
980  " faces: " << f_ind << " vs " << nf );
981 
982  // Switch back offsets to their correct value
983  for (int i = ndofs; i > 0; --i)
984  {
985  gather_offsets[i] = gather_offsets[i - 1];
986  }
987  gather_offsets[0] = 0;
988 }
989 
990 } // namespace mfem
991 
992 #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...
void Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
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 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...
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.
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition: vector.hpp:117
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
int Size() const
Returns the size of the vector.
Definition: vector.hpp:199
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:448
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.
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...
void DoubleValuedNonconformingInterpolation(Vector &x) const
Apply a change of basis from coarse element basis to fine element basis for the coarse face dofs...
virtual void MakeRef(FiniteElementSpace *f, double *v)
Make the ParGridFunction reference external data on a new FiniteElementSpace.
Definition: pgridfunc.cpp:102
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
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
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:456
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
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...
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:620
Array< int > gather_offsets
Mesh * GetMesh() const
Returns the mesh.
Definition: fespace.hpp:441
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
Array< int > scatter_indices1
void CheckFESpace(const ElementDofOrdering ordering)
Verify that L2FaceRestriction is build from an L2 FESpace.
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:464
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:1131
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
int GetNumFacesWithGhost() const
Return the number of faces (3D), edges (2D) or vertices (1D) including ghost faces.
Definition: mesh.cpp:5400
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
const Vector & GetInterpolators() const
Return an mfem::Vector containing the interpolators in the following format: face_dofs x face_dofs x ...
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...