12 #include "../config/config.hpp"
21 #include "../general/forall.hpp"
31 interpolations(fes, ordering, type)
33 if (
nf==0) {
return; }
38 ComputeScatterIndicesAndOffsets(ordering, type);
40 ComputeGatherIndices(ordering, type);
45 if (
nf==0) {
return; }
58 const int dof = i % nface_dofs;
59 const int face = i / nface_dofs;
60 const int idx = d_indices[i];
61 for (
int c = 0; c < vd; ++c)
63 d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
75 auto d_interp =
Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
76 static constexpr
int max_nd = 1024;
77 MFEM_VERIFY(nface_dofs<=max_nd,
"Too many degrees of freedom.");
78 MFEM_FORALL_3D(face,
nf, nface_dofs, 1, 1,
80 MFEM_SHARED
double dof_values[max_nd];
83 const int interp_index = conf.
index;
87 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
89 const int i = face*nface_dofs + dof;
90 const int idx = d_indices[i];
91 for (
int c = 0; c < vd; ++c)
93 d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
99 for (
int c = 0; c < vd; ++c)
102 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
104 const int i = face*nface_dofs + dof;
105 const int idx = d_indices[i];
106 dof_values[dof] = d_x(t?c:idx, t?idx:c);
110 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
113 for (
int dof_in = 0; dof_in<nface_dofs; dof_in++)
115 res += d_interp(dof_out, dof_in, interp_index)*
118 d_y(dof_out, c, face) = res;
127 void ParNCH1FaceRestriction::AddMultTranspose(
const Vector &x,
Vector &y)
const
129 if (nf==0) {
return; }
130 if (x_interp.Size()==0)
132 x_interp.SetSize(x.
Size());
136 const int nface_dofs = face_dofs;
138 const bool t = byvdim;
139 if ( type==FaceType::Interior )
142 auto d_x =
Reshape(x_interp.ReadWrite(), nface_dofs, vd, nf);
143 auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
144 auto interpolators = interpolations.GetInterpolators().Read();
145 const int nc_size = interpolations.GetNumInterpolators();
146 auto d_interp =
Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
147 static constexpr
int max_nd = 1024;
148 MFEM_VERIFY(nface_dofs<=max_nd,
"Too many degrees of freedom.");
149 MFEM_FORALL_3D(face, nf, nface_dofs, 1, 1,
151 MFEM_SHARED
double dof_values[max_nd];
154 const int interp_index = conf.
index;
158 for (
int c = 0; c < vd; ++c)
160 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
162 dof_values[dof] = d_x(dof, c, face);
165 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
168 for (
int dof_in = 0; dof_in<nface_dofs; dof_in++)
170 res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
172 d_x(dof_out, c, face) = res;
181 auto d_offsets = gather_offsets.Read();
182 auto d_indices = gather_indices.Read();
183 auto d_x =
Reshape(x_interp.Read(), nface_dofs, vd, nf);
184 auto d_y =
Reshape(y.ReadWrite(),
t?vd:ndofs,
t?ndofs:vd);
185 MFEM_FORALL(i, ndofs,
187 const int offset = d_offsets[i];
188 const int next_offset = d_offsets[i + 1];
189 for (
int c = 0; c < vd; ++c)
191 double dof_value = 0;
192 for (
int j = offset; j < next_offset; ++j)
194 int idx_j = d_indices[j];
195 dof_value += d_x(idx_j % nface_dofs, c, idx_j / nface_dofs);
197 d_y(
t?c:i,
t?i:c) += dof_value;
202 void ParNCH1FaceRestriction::ComputeScatterIndicesAndOffsets(
206 Mesh &mesh = *fes.GetMesh();
209 for (
int i = 0; i <= ndofs; ++i)
211 gather_offsets[i] = 0;
216 for (
int f = 0;
f < mesh.GetNumFacesWithGhost(); ++
f)
218 Mesh::FaceInformation face = mesh.GetFaceInformation(
f);
219 if ( face.IsNonconformingCoarse() )
225 else if (face_type==FaceType::Interior && face.IsInterior())
227 if ( face.IsConforming() )
229 interpolations.RegisterFaceConformingInterpolation(face,f_ind);
230 SetFaceDofsScatterIndices(face, f_ind, ordering);
235 SetFaceDofsScatterIndices(face, f_ind, ordering);
236 if ( face.element[0].conformity==Mesh::ElementConformity::Superset )
240 interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
246 interpolations.RegisterFaceConformingInterpolation(face,f_ind);
251 else if (face_type==FaceType::Boundary && face.IsBoundary())
253 SetFaceDofsScatterIndices(face, f_ind, ordering);
257 MFEM_VERIFY(f_ind==nf,
"Unexpected number of faces.");
260 for (
int i = 1; i <= ndofs; ++i)
262 gather_offsets[i] += gather_offsets[i - 1];
266 interpolations.LinearizeInterpolatorMapIntoVector();
269 void ParNCH1FaceRestriction::ComputeGatherIndices(
273 Mesh &mesh = *fes.GetMesh();
277 for (
int f = 0;
f < mesh.GetNumFacesWithGhost(); ++
f)
279 Mesh::FaceInformation face = mesh.GetFaceInformation(
f);
280 if ( face.IsNonconformingCoarse() )
286 else if (face.IsOfFaceType(face_type))
288 SetFaceDofsGatherIndices(face, f_ind, ordering);
292 MFEM_VERIFY(f_ind==nf,
"Unexpected number of faces.");
295 for (
int i = ndofs; i > 0; --i)
297 gather_offsets[i] = gather_offsets[i - 1];
299 gather_offsets[0] = 0;
309 if (!build) {
return; }
310 if (
nf==0) {
return; }
314 ComputeScatterIndicesAndOffsets(ordering, type);
316 ComputeGatherIndices(ordering, type);
331 "This method should be called when m == L2FaceValues::DoubleValued.");
335 x_gf.
MakeRef(const_cast<ParFiniteElementSpace*>(&pfes),
336 const_cast<Vector&>(x), 0);
343 const int threshold =
ndofs;
349 t?vd:nsdofs, t?nsdofs:vd);
353 const int dof = i % nface_dofs;
354 const int face = i / nface_dofs;
355 const int idx1 = d_indices1[i];
356 for (
int c = 0; c < vd; ++c)
358 d_y(dof, c, 0, face) = d_x(t?c:idx1, t?idx1:c);
360 const int idx2 = d_indices2[i];
361 for (
int c = 0; c < vd; ++c)
363 if (idx2>-1 && idx2<threshold)
365 d_y(dof, c, 1, face) = d_x(t?c:idx2, t?idx2:c);
367 else if (idx2>=threshold)
369 d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
370 t?(idx2-threshold):c);
374 d_y(dof, c, 1, face) = 0.0;
382 if (
nf==0) {
return; }
393 static MFEM_HOST_DEVICE
int AddNnz(
const int iE,
int *I,
const int dofs)
400 const bool keep_nbr_block)
const
407 const int Ndofs =
ndofs;
411 MFEM_FORALL(fdof,
nf*nface_dofs,
413 const int f = fdof/nface_dofs;
414 const int iF = fdof%nface_dofs;
415 const int iE1 = d_indices1[f*nface_dofs+iF];
418 AddNnz(iE1,I,nface_dofs);
420 const int iE2 = d_indices2[f*nface_dofs+iF];
423 AddNnz(iE2,I,nface_dofs);
432 const int Ndofs =
ndofs;
441 MFEM_FORALL(fdof,
nf*nface_dofs,
443 const int f = fdof/nface_dofs;
444 const int iF = fdof%nface_dofs;
445 const int iE1 = d_indices1[f*nface_dofs+iF];
448 for (
int jF = 0; jF < nface_dofs; jF++)
450 const int jE2 = d_indices2[f*nface_dofs+jF];
457 AddNnz(iE1,I_face,1);
461 const int iE2 = d_indices2[f*nface_dofs+iF];
464 for (
int jF = 0; jF < nface_dofs; jF++)
466 const int jE1 = d_indices1[f*nface_dofs+jF];
473 AddNnz(iE2,I_face,1);
482 const bool keep_nbr_block)
const
489 const int Ndofs =
ndofs;
492 auto mat_fea =
Reshape(ea_data.
Read(), nface_dofs, nface_dofs, 2,
nf);
496 MFEM_FORALL(fdof,
nf*nface_dofs,
498 const int f = fdof/nface_dofs;
499 const int iF = fdof%nface_dofs;
500 const int iE1 = d_indices1[f*nface_dofs+iF];
503 const int offset = AddNnz(iE1,I,nface_dofs);
504 for (
int jF = 0; jF < nface_dofs; jF++)
506 const int jE2 = d_indices2[f*nface_dofs+jF];
508 Data[offset+jF] = mat_fea(jF,iF,1,f);
511 const int iE2 = d_indices2[f*nface_dofs+iF];
514 const int offset = AddNnz(iE2,I,nface_dofs);
515 for (
int jF = 0; jF < nface_dofs; jF++)
517 const int jE1 = d_indices1[f*nface_dofs+jF];
519 Data[offset+jF] = mat_fea(jF,iF,0,f);
530 const int Ndofs =
ndofs;
533 auto mat_fea =
Reshape(ea_data.
Read(), nface_dofs, nface_dofs, 2,
nf);
537 auto J_face = face_mat.
WriteJ();
540 MFEM_FORALL(fdof,
nf*nface_dofs,
542 const int f = fdof/nface_dofs;
543 const int iF = fdof%nface_dofs;
544 const int iE1 = d_indices1[f*nface_dofs+iF];
547 for (
int jF = 0; jF < nface_dofs; jF++)
549 const int jE2 = d_indices2[f*nface_dofs+jF];
552 const int offset = AddNnz(iE1,I,1);
554 Data[offset] = mat_fea(jF,iF,1,f);
558 const int offset = AddNnz(iE1,I_face,1);
559 J_face[offset] = jE2-Ndofs;
560 Data_face[offset] = mat_fea(jF,iF,1,f);
564 const int iE2 = d_indices2[f*nface_dofs+iF];
567 for (
int jF = 0; jF < nface_dofs; jF++)
569 const int jE1 = d_indices1[f*nface_dofs+jF];
572 const int offset = AddNnz(iE2,I,1);
574 Data[offset] = mat_fea(jF,iF,0,f);
578 const int offset = AddNnz(iE2,I_face,1);
579 J_face[offset] = jE1-Ndofs;
580 Data_face[offset] = mat_fea(jF,iF,0,f);
587 void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets(
596 for (
int i = 0; i <=
ndofs; ++i)
603 for (
int f = 0;
f < pfes.
GetNF(); ++
f)
632 MFEM_VERIFY(f_ind==
nf,
"Unexpected number of faces.");
635 for (
int i = 1; i <=
ndofs; ++i)
642 void ParL2FaceRestriction::ComputeGatherIndices(
653 if (face.IsOfFaceType(type))
665 MFEM_VERIFY(f_ind==
nf,
"Unexpected number of faces.");
668 for (
int i =
ndofs; i > 0; --i)
683 if (
nf==0) {
return; }
688 ComputeScatterIndicesAndOffsets(ordering, type);
690 ComputeGatherIndices(ordering, type);
698 "This method should be called when m == L2FaceValues::SingleValued.");
703 const int threshold =
ndofs;
710 auto d_interp =
Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
711 static constexpr
int max_nd = 16*16;
712 MFEM_VERIFY(nface_dofs<=max_nd,
"Too many degrees of freedom.");
713 MFEM_FORALL_3D(face,
nf, nface_dofs, 1, 1,
715 MFEM_SHARED
double dof_values[max_nd];
718 const int interp_index = conf.
index;
722 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
724 const int i = face*nface_dofs + dof;
725 const int idx = d_indices1[i];
726 if (idx>-1 && idx<threshold)
728 for (
int c = 0; c < vd; ++c)
730 d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
735 for (
int c = 0; c < vd; ++c)
737 d_y(dof, c, face) = 0.0;
744 for (
int c = 0; c < vd; ++c)
746 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
748 const int i = face*nface_dofs + dof;
749 const int idx = d_indices1[i];
750 if (idx>-1 && idx<threshold)
752 dof_values[dof] = d_x(t?c:idx, t?idx:c);
756 dof_values[dof] = 0.0;
760 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
763 for (
int dof_in = 0; dof_in<nface_dofs; dof_in++)
765 res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
767 d_y(dof_out, c, face) = res;
780 "This method should be called when m == L2FaceValues::DoubleValued.");
784 x_gf.
MakeRef(const_cast<ParFiniteElementSpace*>(&pfes),
785 const_cast<Vector&>(x), 0);
792 const int threshold =
ndofs;
798 t?vd:nsdofs, t?nsdofs:vd);
803 auto d_interp =
Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
804 static constexpr
int max_nd = 1024;
805 MFEM_VERIFY(nface_dofs<=max_nd,
"Too many degrees of freedom.");
806 MFEM_FORALL_3D(face,
nf, nface_dofs, 1, 1,
808 MFEM_SHARED
double dof_values[max_nd];
811 const int interp_index = conf.
index;
812 for (
int side = 0; side < 2; side++)
817 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
819 const int i = face*nface_dofs + dof;
820 const int idx = side==0 ? d_indices1[i] : d_indices2[i];
821 if (idx>-1 && idx<threshold)
823 for (
int c = 0; c < vd; ++c)
825 d_y(dof, c, side, face) = d_x(t?c:idx, t?idx:c);
828 else if (idx>=threshold)
830 const int sidx = idx-threshold;
831 for (
int c = 0; c < vd; ++c)
833 d_y(dof, c, side, face) = d_x_shared(t?c:sidx, t?sidx:c);
838 for (
int c = 0; c < vd; ++c)
840 d_y(dof, c, side, face) = 0.0;
847 for (
int c = 0; c < vd; ++c)
849 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
851 const int i = face*nface_dofs + dof;
852 const int idx = side==0 ? d_indices1[i] : d_indices2[i];
853 if (idx>-1 && idx<threshold)
855 dof_values[dof] = d_x(t?c:idx, t?idx:c);
857 else if (idx>=threshold)
859 const int sidx = idx-threshold;
860 dof_values[dof] = d_x_shared(t?c:sidx, t?sidx:c);
864 dof_values[dof] = 0.0;
868 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
871 for (
int dof_in = 0; dof_in<nface_dofs; dof_in++)
873 res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
875 d_y(dof_out, c, side, face) = res;
886 if (
nf==0) {
return; }
905 MFEM_ABORT(
"Unknown type and multiplicity combination.");
911 if (
nf==0) {
return; }
939 const bool keep_nbr_block)
const
941 MFEM_ABORT(
"Not yet implemented.");
947 MFEM_ABORT(
"Not yet implemented.");
952 const bool keep_nbr_block)
const
954 MFEM_ABORT(
"Not yet implemented.");
961 MFEM_ABORT(
"Not yet implemented.");
964 void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets(
971 for (
int i = 0; i <=
ndofs; ++i)
981 if ( face.IsNonconformingCoarse() )
989 if ( face.IsConforming() )
995 if ( face.IsShared() )
1011 if ( face.IsShared() )
1033 MFEM_VERIFY(f_ind==
nf,
"Unexpected number of " <<
1035 " faces: " << f_ind <<
" vs " <<
nf );
1038 for (
int i = 1; i <=
ndofs; ++i)
1047 void ParNCL2FaceRestriction::ComputeGatherIndices(
1055 for (
int f = 0;
f < mesh.GetNumFacesWithGhost(); ++
f)
1058 if ( face.IsNonconformingCoarse() )
1064 else if ( face.IsOfFaceType(type) )
1076 MFEM_VERIFY(f_ind==
nf,
"Unexpected number of " <<
1078 " faces: " << f_ind <<
" vs " <<
nf );
1081 for (
int i =
ndofs; i > 0; --i)
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
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 ExchangeFaceNbrData()
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)
InterpolationManager interpolations
int Size() const
Returns the size of the vector.
Operator that extracts face degrees of freedom for L2 nonconforming spaces.
Abstract parallel finite element space.
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.
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...
DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims...dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
virtual void MakeRef(FiniteElementSpace *f, double *v)
Make the ParGridFunction reference external data on a new FiniteElementSpace.
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)
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 ...
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.
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).
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)
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
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.
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)
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.
int GetNumFacesWithGhost() const
Return the number of faces (3D), edges (2D) or vertices (1D) including ghost faces.
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
int GetFaceNbrVSize() const
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 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
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.
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).
Array< int > scatter_indices
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...
double f(const Vector &p)
double * WriteData(bool on_dev=true)
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 ...