Files
mfem/fem/prestriction.cpp
T
Will Pazner 985dfe2749 Cache InterpolationManager in FiniteElementSpace
The InterpolationManager used in ParNCH1FaceRestriction is still a bit
different.
2025-08-08 10:37:24 -07:00

1090 lines
33 KiB
C++

// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "restriction.hpp"
#include "prestriction.hpp"
#include "pgridfunc.hpp"
#include "pfespace.hpp"
#include "fespace.hpp"
#include "fe/face_map_utils.hpp"
#include "../general/forall.hpp"
namespace mfem
{
ParNCH1FaceRestriction::ParNCH1FaceRestriction(const ParFiniteElementSpace &fes,
ElementDofOrdering f_ordering,
FaceType type)
: H1FaceRestriction(fes, f_ordering, type, false),
type(type),
interpolations(fes, f_ordering, type)
{
if (nf==0) { return; }
x_interp.UseDevice(true);
// Check that the space is H1 (not currently implemented for ND or RT spaces)
const bool is_h1 = dynamic_cast<const H1_FECollection*>(fes.FEColl());
MFEM_VERIFY(is_h1, "ParNCH1FaceRestriction is only implemented for H1 spaces.")
CheckFESpace(f_ordering);
ComputeScatterIndicesAndOffsets(f_ordering, type);
ComputeGatherIndices(f_ordering, type);
}
void ParNCH1FaceRestriction::Mult(const Vector &x, Vector &y) const
{
H1FaceRestriction::Mult(x, y);
NonconformingInterpolation(y);
}
void ParNCH1FaceRestriction::NonconformingInterpolation(Vector& y) const
{
// Assumes all elements have the same number of dofs
const int nface_dofs = face_dofs;
const int vd = vdim;
auto d_y = Reshape(y.ReadWrite(), nface_dofs, vd, nf);
auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
const int num_nc_faces = nc_interp_config.Size();
if ( num_nc_faces == 0 ) { return; }
auto interp_config_ptr = nc_interp_config.Read();
const int nc_size = interpolations.GetNumInterpolators();
auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
nface_dofs, nface_dofs, nc_size);
static constexpr int max_nd = 16*16;
MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
mfem::forall_2D(num_nc_faces, nface_dofs, 1, [=] MFEM_HOST_DEVICE (int nc_face)
{
MFEM_SHARED real_t dof_values[max_nd];
const NCInterpConfig conf = interp_config_ptr[nc_face];
if ( conf.is_non_conforming && conf.master_side == 0 )
{
const int interp_index = conf.index;
const int face = conf.face_index;
for (int c = 0; c < vd; ++c)
{
MFEM_FOREACH_THREAD(dof,x,nface_dofs)
{
dof_values[dof] = d_y(dof, c, face);
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
{
real_t res = 0.0;
for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
{
res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
}
d_y(dof_out, c, face) = res;
}
MFEM_SYNC_THREAD;
}
}
});
}
void ParNCH1FaceRestriction::AddMultTranspose(const Vector &x, Vector &y,
const real_t a) const
{
MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
if (nf==0) { return; }
NonconformingTransposeInterpolation(x);
H1FaceRestriction::AddMultTranspose(x_interp, y);
}
void ParNCH1FaceRestriction::AddMultTransposeInPlace(Vector &x, Vector &y) const
{
if (nf==0) { return; }
NonconformingTransposeInterpolationInPlace(x);
H1FaceRestriction::AddMultTranspose(x, y);
}
void ParNCH1FaceRestriction::NonconformingTransposeInterpolation(
const Vector& x) const
{
if (x_interp.Size()==0)
{
x_interp.SetSize(x.Size());
}
x_interp = x;
NonconformingTransposeInterpolationInPlace(x_interp);
}
void ParNCH1FaceRestriction::NonconformingTransposeInterpolationInPlace(
Vector& x) const
{
// Assumes all elements have the same number of dofs
const int nface_dofs = face_dofs;
const int vd = vdim;
if ( type==FaceType::Interior )
{
// Interpolation from slave to master face dofs
auto d_x = Reshape(x.ReadWrite(), nface_dofs, vd, nf);
auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
const int num_nc_faces = nc_interp_config.Size();
if ( num_nc_faces == 0 ) { return; }
auto interp_config_ptr = nc_interp_config.Read();
const int nc_size = interpolations.GetNumInterpolators();
auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
nface_dofs, nface_dofs, nc_size);
static constexpr int max_nd = 1024;
MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
mfem::forall_2D(num_nc_faces, nface_dofs, 1,
[=] MFEM_HOST_DEVICE (int nc_face)
{
MFEM_SHARED real_t dof_values[max_nd];
const NCInterpConfig conf = interp_config_ptr[nc_face];
const int master_side = conf.master_side;
if ( conf.is_non_conforming && master_side==0 )
{
const int interp_index = conf.index;
const int face = conf.face_index;
// Interpolation from fine to coarse
for (int c = 0; c < vd; ++c)
{
MFEM_FOREACH_THREAD(dof,x,nface_dofs)
{
dof_values[dof] = d_x(dof, c, face);
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
{
real_t res = 0.0;
for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
{
res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
}
d_x(dof_out, c, face) = res;
}
MFEM_SYNC_THREAD;
}
}
});
}
}
void ParNCH1FaceRestriction::ComputeScatterIndicesAndOffsets(
const ElementDofOrdering f_ordering,
const FaceType face_type)
{
Mesh &mesh = *fes.GetMesh();
// Initialization of the offsets
for (int i = 0; i <= ndofs; ++i)
{
gather_offsets[i] = 0;
}
// Computation of scatter indices and offsets
int f_ind = 0;
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
{
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
if ( face.IsNonconformingCoarse() )
{
// We skip nonconforming coarse faces as they are treated
// by the corresponding nonconforming fine faces.
continue;
}
else if (face_type==FaceType::Interior && face.IsInterior())
{
if ( face.IsConforming() )
{
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
SetFaceDofsScatterIndices(face, f_ind, f_ordering);
f_ind++;
}
else // Non-conforming face
{
SetFaceDofsScatterIndices(face, f_ind, f_ordering);
if ( face.element[0].conformity==Mesh::ElementConformity::Superset )
{
// In this case the local face is the master (coarse) face, thus
// we need to interpolate the values on the slave (fine) face.
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
}
else
{
// Treated as a conforming face since we only extract values from
// the local slave (fine) face.
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
}
f_ind++;
}
}
else if (face_type==FaceType::Boundary && face.IsBoundary())
{
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
SetFaceDofsScatterIndices(face, f_ind, f_ordering);
f_ind++;
}
}
MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
// Summation of the offsets
for (int i = 1; i <= ndofs; ++i)
{
gather_offsets[i] += gather_offsets[i - 1];
}
// Transform the interpolation matrix map into a contiguous memory structure.
interpolations.LinearizeInterpolatorMapIntoVector();
interpolations.InitializeNCInterpConfig();
}
void ParNCH1FaceRestriction::ComputeGatherIndices(
const ElementDofOrdering f_ordering,
const FaceType face_type)
{
Mesh &mesh = *fes.GetMesh();
// Computation of gather_indices
int f_ind = 0;
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
{
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
if ( face.IsNonconformingCoarse() )
{
// We skip nonconforming coarse faces as they are treated
// by the corresponding nonconforming fine faces.
continue;
}
else if (face.IsOfFaceType(face_type))
{
SetFaceDofsGatherIndices(face, f_ind, f_ordering);
f_ind++;
}
}
MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
// Reset offsets to their correct value
for (int i = ndofs; i > 0; --i)
{
gather_offsets[i] = gather_offsets[i - 1];
}
gather_offsets[0] = 0;
}
ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &pfes_,
ElementDofOrdering f_ordering,
FaceType type,
L2FaceValues m,
bool build)
: L2FaceRestriction(pfes_, f_ordering, type, m, false),
pfes(pfes_)
{
if (!build) { return; }
if (nf==0) { return; }
CheckFESpace();
ComputeScatterIndicesAndOffsets();
ComputeGatherIndices();
}
ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &fes,
ElementDofOrdering f_ordering,
FaceType type,
L2FaceValues m)
: ParL2FaceRestriction(fes, f_ordering, type, m, true)
{ }
void ParL2FaceRestriction::DoubleValuedConformingMult(
const Vector& x, Vector& y) const
{
MFEM_ASSERT(
m == L2FaceValues::DoubleValued,
"This method should be called when m == L2FaceValues::DoubleValued.");
Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, type);
// Early return only after calling ParGridFunction::ExchangeFaceNbrData,
// otherwise MPI communication can hang.
if (nf == 0) { return; }
// Assumes all elements have the same number of dofs
const int nface_dofs = face_dofs;
const int vd = vdim;
const bool t = byvdim;
const int threshold = ndofs;
const int nsdofs = pfes.GetFaceNbrVSize();
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
auto d_x_shared = Reshape(face_nbr_data.Read(),
t?vd:nsdofs, t?nsdofs:vd);
auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
{
const int dof = i % nface_dofs;
const int face = i / nface_dofs;
const int idx1 = d_indices1[i];
for (int c = 0; c < vd; ++c)
{
d_y(dof, c, 0, face) = d_x(t?c:idx1, t?idx1:c);
}
const int idx2 = d_indices2[i];
for (int c = 0; c < vd; ++c)
{
if (idx2>-1 && idx2<threshold) // interior face
{
d_y(dof, c, 1, face) = d_x(t?c:idx2, t?idx2:c);
}
else if (idx2>=threshold) // shared boundary
{
d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
t?(idx2-threshold):c);
}
else // true boundary
{
d_y(dof, c, 1, face) = 0.0;
}
}
});
}
void ParL2FaceRestriction::Mult(const Vector& x, Vector& y) const
{
if (m==L2FaceValues::DoubleValued)
{
DoubleValuedConformingMult(x, y);
}
else
{
SingleValuedConformingMult(x, y);
}
}
static MFEM_HOST_DEVICE int AddNnz(const int iE, int *I, const int dofs)
{
int val = AtomicAdd(I[iE],dofs);
return val;
}
void ParL2FaceRestriction::FillI(SparseMatrix &mat,
const bool keep_nbr_block) const
{
if (keep_nbr_block)
{
return L2FaceRestriction::FillI(mat, keep_nbr_block);
}
const int nface_dofs = face_dofs;
const int Ndofs = ndofs;
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto I = mat.ReadWriteI();
mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
{
const int f = fdof/nface_dofs;
const int iF = fdof%nface_dofs;
const int iE1 = d_indices1[f*nface_dofs+iF];
if (iE1 < Ndofs)
{
AddNnz(iE1,I,nface_dofs);
}
const int iE2 = d_indices2[f*nface_dofs+iF];
if (iE2 < Ndofs)
{
AddNnz(iE2,I,nface_dofs);
}
});
}
void ParL2FaceRestriction::FillI(SparseMatrix &mat,
SparseMatrix &face_mat) const
{
const int nface_dofs = face_dofs;
const int Ndofs = ndofs;
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto I = mat.ReadWriteI();
auto I_face = face_mat.ReadWriteI();
mfem::forall(ne*elem_dofs*vdim+1, [=] MFEM_HOST_DEVICE (int i)
{
I_face[i] = 0;
});
mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
{
const int f = fdof/nface_dofs;
const int iF = fdof%nface_dofs;
const int iE1 = d_indices1[f*nface_dofs+iF];
if (iE1 < Ndofs)
{
for (int jF = 0; jF < nface_dofs; jF++)
{
const int jE2 = d_indices2[f*nface_dofs+jF];
if (jE2 < Ndofs)
{
AddNnz(iE1,I,1);
}
else
{
AddNnz(iE1,I_face,1);
}
}
}
const int iE2 = d_indices2[f*nface_dofs+iF];
if (iE2 < Ndofs)
{
for (int jF = 0; jF < nface_dofs; jF++)
{
const int jE1 = d_indices1[f*nface_dofs+jF];
if (jE1 < Ndofs)
{
AddNnz(iE2,I,1);
}
else
{
AddNnz(iE2,I_face,1);
}
}
}
});
}
void ParL2FaceRestriction::FillJAndData(const Vector &ea_data,
SparseMatrix &mat,
const bool keep_nbr_block) const
{
if (keep_nbr_block)
{
return L2FaceRestriction::FillJAndData(ea_data, mat, keep_nbr_block);
}
const int nface_dofs = face_dofs;
const int Ndofs = ndofs;
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
auto I = mat.ReadWriteI();
auto J = mat.WriteJ();
auto Data = mat.WriteData();
mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
{
const int f = fdof/nface_dofs;
const int iF = fdof%nface_dofs;
const int iE1 = d_indices1[f*nface_dofs+iF];
if (iE1 < Ndofs)
{
const int offset = AddNnz(iE1,I,nface_dofs);
for (int jF = 0; jF < nface_dofs; jF++)
{
const int jE2 = d_indices2[f*nface_dofs+jF];
J[offset+jF] = jE2;
Data[offset+jF] = mat_fea(jF,iF,1,f);
}
}
const int iE2 = d_indices2[f*nface_dofs+iF];
if (iE2 < Ndofs)
{
const int offset = AddNnz(iE2,I,nface_dofs);
for (int jF = 0; jF < nface_dofs; jF++)
{
const int jE1 = d_indices1[f*nface_dofs+jF];
J[offset+jF] = jE1;
Data[offset+jF] = mat_fea(jF,iF,0,f);
}
}
});
}
void ParL2FaceRestriction::FillJAndData(const Vector &ea_data,
SparseMatrix &mat,
SparseMatrix &face_mat) const
{
const int nface_dofs = face_dofs;
const int Ndofs = ndofs;
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
auto I = mat.ReadWriteI();
auto I_face = face_mat.ReadWriteI();
auto J = mat.WriteJ();
auto J_face = face_mat.WriteJ();
auto Data = mat.WriteData();
auto Data_face = face_mat.WriteData();
mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
{
const int f = fdof/nface_dofs;
const int iF = fdof%nface_dofs;
const int iE1 = d_indices1[f*nface_dofs+iF];
if (iE1 < Ndofs)
{
for (int jF = 0; jF < nface_dofs; jF++)
{
const int jE2 = d_indices2[f*nface_dofs+jF];
if (jE2 < Ndofs)
{
const int offset = AddNnz(iE1,I,1);
J[offset] = jE2;
Data[offset] = mat_fea(jF,iF,1,f);
}
else
{
const int offset = AddNnz(iE1,I_face,1);
J_face[offset] = jE2-Ndofs;
Data_face[offset] = mat_fea(jF,iF,1,f);
}
}
}
const int iE2 = d_indices2[f*nface_dofs+iF];
if (iE2 < Ndofs)
{
for (int jF = 0; jF < nface_dofs; jF++)
{
const int jE1 = d_indices1[f*nface_dofs+jF];
if (jE1 < Ndofs)
{
const int offset = AddNnz(iE2,I,1);
J[offset] = jE1;
Data[offset] = mat_fea(jF,iF,0,f);
}
else
{
const int offset = AddNnz(iE2,I_face,1);
J_face[offset] = jE1-Ndofs;
Data_face[offset] = mat_fea(jF,iF,0,f);
}
}
}
});
}
void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
Mesh &mesh = *fes.GetMesh();
// Initialization of the offsets
for (int i = 0; i <= ndofs; ++i)
{
gather_offsets[i] = 0;
}
// Computation of scatter indices and offsets
int f_ind=0;
for (int f = 0; f < pfes.GetNF(); ++f)
{
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
if (type==FaceType::Interior && face.IsInterior())
{
SetFaceDofsScatterIndices1(face,f_ind);
if (m==L2FaceValues::DoubleValued)
{
if (face.IsShared())
{
PermuteAndSetSharedFaceDofsScatterIndices2(face,f_ind);
}
else
{
PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
}
}
f_ind++;
}
else if (type==FaceType::Boundary && face.IsBoundary())
{
SetFaceDofsScatterIndices1(face,f_ind);
if (m==L2FaceValues::DoubleValued)
{
SetBoundaryDofsScatterIndices2(face,f_ind);
}
f_ind++;
}
}
MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
// Summation of the offsets
for (int i = 1; i <= ndofs; ++i)
{
gather_offsets[i] += gather_offsets[i - 1];
}
}
void ParL2FaceRestriction::ComputeGatherIndices()
{
Mesh &mesh = *fes.GetMesh();
// Computation of gather_indices
int f_ind = 0;
for (int f = 0; f < fes.GetNF(); ++f)
{
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
if (face.IsOfFaceType(type))
{
SetFaceDofsGatherIndices1(face,f_ind);
if (m==L2FaceValues::DoubleValued &&
type==FaceType::Interior &&
face.IsLocal())
{
PermuteAndSetFaceDofsGatherIndices2(face,f_ind);
}
f_ind++;
}
}
MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
// Reset offsets to their correct value
for (int i = ndofs; i > 0; --i)
{
gather_offsets[i] = gather_offsets[i - 1];
}
gather_offsets[0] = 0;
}
ParNCL2FaceRestriction::ParNCL2FaceRestriction(const ParFiniteElementSpace &fes,
ElementDofOrdering f_ordering,
FaceType type,
L2FaceValues m)
: L2FaceRestriction(fes, f_ordering, type, m, false),
NCL2FaceRestriction(fes, f_ordering, type, m, false),
ParL2FaceRestriction(fes, f_ordering, type, m, false)
{
if (nf==0) { return; }
x_interp.UseDevice(true);
CheckFESpace();
ComputeScatterIndicesAndOffsets();
ComputeGatherIndices();
}
void ParNCL2FaceRestriction::SingleValuedNonconformingMult(
const Vector& x, Vector& y) const
{
if (nf == 0) { return; }
MFEM_ASSERT(
m == L2FaceValues::SingleValued,
"This method should be called when m == L2FaceValues::SingleValued.");
// Assumes all elements have the same number of dofs
const int nface_dofs = face_dofs;
const int vd = vdim;
const bool t = byvdim;
const int threshold = ndofs;
auto d_indices1 = scatter_indices1.Read();
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
auto interpolators = interpolations.GetInterpolators().Read();
const int nc_size = interpolations.GetNumInterpolators();
auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
static constexpr int max_nd = 16*16;
MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
mfem::forall_2D(nf, nface_dofs, 1, [=] MFEM_HOST_DEVICE (int face)
{
MFEM_SHARED real_t dof_values[max_nd];
const InterpConfig conf = interp_config_ptr[face];
const int master_side = conf.master_side;
const int interp_index = conf.index;
const int side = 0;
if ( !conf.is_non_conforming || side!=master_side )
{
MFEM_FOREACH_THREAD(dof,x,nface_dofs)
{
const int i = face*nface_dofs + dof;
const int idx = d_indices1[i];
if (idx>-1 && idx<threshold) // interior face
{
for (int c = 0; c < vd; ++c)
{
d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
}
}
else // true boundary
{
for (int c = 0; c < vd; ++c)
{
d_y(dof, c, face) = 0.0;
}
}
}
}
else // Interpolation from coarse to fine
{
for (int c = 0; c < vd; ++c)
{
MFEM_FOREACH_THREAD(dof,x,nface_dofs)
{
const int i = face*nface_dofs + dof;
const int idx = d_indices1[i];
if (idx>-1 && idx<threshold) // interior face
{
dof_values[dof] = d_x(t?c:idx, t?idx:c);
}
else // true boundary
{
dof_values[dof] = 0.0;
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
{
real_t res = 0.0;
for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
{
res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
}
d_y(dof_out, c, face) = res;
}
MFEM_SYNC_THREAD;
}
}
});
}
void ParNCL2FaceRestriction::DoubleValuedNonconformingMult(
const Vector& x, Vector& y) const
{
ParL2FaceRestriction::DoubleValuedConformingMult(x, y);
NCL2FaceRestriction::DoubleValuedNonconformingInterpolation(y);
}
void ParNCL2FaceRestriction::Mult(const Vector& x, Vector& y) const
{
if ( type==FaceType::Interior && m==L2FaceValues::DoubleValued )
{
DoubleValuedNonconformingMult(x, y);
}
else if ( type==FaceType::Boundary && m==L2FaceValues::DoubleValued )
{
DoubleValuedConformingMult(x, y);
}
else if ( type==FaceType::Interior && m==L2FaceValues::SingleValued )
{
SingleValuedNonconformingMult(x, y);
}
else if ( type==FaceType::Boundary && m==L2FaceValues::SingleValued )
{
SingleValuedConformingMult(x, y);
}
else
{
MFEM_ABORT("Unknown type and multiplicity combination.");
}
}
void ParNCL2FaceRestriction::AddMultTranspose(const Vector &x, Vector &y,
const real_t a) const
{
MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
if (nf==0) { return; }
if (type==FaceType::Interior)
{
if ( m==L2FaceValues::DoubleValued )
{
DoubleValuedNonconformingTransposeInterpolation(x);
DoubleValuedConformingAddMultTranspose(x_interp, y);
}
else // Single Valued
{
SingleValuedNonconformingTransposeInterpolation(x);
SingleValuedConformingAddMultTranspose(x_interp, y);
}
}
else
{
if ( m==L2FaceValues::DoubleValued )
{
DoubleValuedConformingAddMultTranspose(x, y);
}
else // Single valued
{
SingleValuedConformingAddMultTranspose(x, y);
}
}
}
void ParNCL2FaceRestriction::AddMultTransposeInPlace(Vector& x, Vector& y) const
{
if (nf==0) { return; }
if (type==FaceType::Interior)
{
if ( m==L2FaceValues::DoubleValued )
{
DoubleValuedNonconformingTransposeInterpolationInPlace(x);
DoubleValuedConformingAddMultTranspose(x, y);
}
else if ( m==L2FaceValues::SingleValued )
{
SingleValuedNonconformingTransposeInterpolationInPlace(x);
SingleValuedConformingAddMultTranspose(x, y);
}
}
else
{
if ( m==L2FaceValues::DoubleValued )
{
DoubleValuedConformingAddMultTranspose(x, y);
}
else if ( m==L2FaceValues::SingleValued )
{
SingleValuedConformingAddMultTranspose(x, y);
}
}
}
void ParNCL2FaceRestriction::FillI(SparseMatrix &mat,
const bool keep_nbr_block) const
{
if (keep_nbr_block)
{
return NCL2FaceRestriction::FillI(mat, keep_nbr_block);
}
const int nface_dofs = face_dofs;
const int Ndofs = ndofs;
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto I = mat.ReadWriteI();
mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
{
const int f = fdof/nface_dofs;
const int iF = fdof%nface_dofs;
const int iE1 = d_indices1[f*nface_dofs+iF];
if (iE1 < Ndofs)
{
AddNnz(iE1,I,nface_dofs);
}
const int iE2 = d_indices2[f*nface_dofs+iF];
if (iE2 < Ndofs)
{
AddNnz(iE2,I,nface_dofs);
}
});
}
void ParNCL2FaceRestriction::FillI(SparseMatrix &mat,
SparseMatrix &face_mat) const
{
MFEM_ABORT("Not yet implemented.");
}
void ParNCL2FaceRestriction::FillJAndData(const Vector &fea_data,
SparseMatrix &mat,
const bool keep_nbr_block) const
{
if (keep_nbr_block)
{
return NCL2FaceRestriction::FillJAndData(fea_data, mat, keep_nbr_block);
}
const int nface_dofs = face_dofs;
const int Ndofs = ndofs;
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto I = mat.ReadWriteI();
auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
auto J = mat.WriteJ();
auto Data = mat.WriteData();
auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
auto interpolators = interpolations.GetInterpolators().Read();
const int nc_size = interpolations.GetNumInterpolators();
auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
{
const int f = fdof/nface_dofs;
const InterpConfig conf = interp_config_ptr[f];
const int master_side = conf.master_side;
const int interp_index = conf.index;
const int iF = fdof%nface_dofs;
const int iE1 = d_indices1[f*nface_dofs+iF];
if (iE1 < Ndofs)
{
const int offset1 = AddNnz(iE1,I,nface_dofs);
for (int jF = 0; jF < nface_dofs; jF++)
{
const int jE2 = d_indices2[f*nface_dofs+jF];
J[offset1+jF] = jE2;
real_t val2 = 0.0;
if ( conf.is_non_conforming && master_side==0 )
{
for (int kF = 0; kF < nface_dofs; kF++)
{
val2 += d_interp(kF, iF, interp_index) * mat_fea(jF,kF,1,f);
}
}
else if ( conf.is_non_conforming && master_side==1 )
{
for (int kF = 0; kF < nface_dofs; kF++)
{
val2 += mat_fea(kF,iF,1,f) * d_interp(kF, jF, interp_index);
}
}
else
{
val2 = mat_fea(jF,iF,1,f);
}
Data[offset1+jF] = val2;
}
}
const int iE2 = d_indices2[f*nface_dofs+iF];
if (iE2 < Ndofs)
{
const int offset2 = AddNnz(iE2,I,nface_dofs);
for (int jF = 0; jF < nface_dofs; jF++)
{
const int jE1 = d_indices1[f*nface_dofs+jF];
J[offset2+jF] = jE1;
real_t val1 = 0.0;
if ( conf.is_non_conforming && master_side==0 )
{
for (int kF = 0; kF < nface_dofs; kF++)
{
val1 += mat_fea(kF,iF,0,f) * d_interp(kF, jF, interp_index);
}
}
else if ( conf.is_non_conforming && master_side==1 )
{
for (int kF = 0; kF < nface_dofs; kF++)
{
val1 += d_interp(kF, iF, interp_index) * mat_fea(jF,kF,0,f);
}
}
else
{
val1 = mat_fea(jF,iF,0,f);
}
Data[offset2+jF] = val1;
}
}
});
}
void ParNCL2FaceRestriction::FillJAndData(const Vector &ea_data,
SparseMatrix &mat,
SparseMatrix &face_mat) const
{
MFEM_ABORT("Not yet implemented.");
}
void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
Mesh &mesh = *fes.GetMesh();
// Initialization of the offsets
for (int i = 0; i <= ndofs; ++i)
{
gather_offsets[i] = 0;
}
// Computation of scatter and offsets indices
int f_ind=0;
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
{
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
if ( face.IsNonconformingCoarse() )
{
// We skip nonconforming coarse faces as they are treated
// by the corresponding nonconforming fine faces.
continue;
}
else if ( type==FaceType::Interior && face.IsInterior() )
{
if ( face.IsConforming() )
{
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
if ( face.IsShared() )
{
PermuteAndSetSharedFaceDofsScatterIndices2(face,f_ind);
}
else
{
PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
}
}
}
else // Non-conforming face
{
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
if ( face.IsShared() )
{
PermuteAndSetSharedFaceDofsScatterIndices2(face,f_ind);
}
else // local nonconforming slave
{
PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
}
}
}
f_ind++;
}
else if (type==FaceType::Boundary && face.IsBoundary())
{
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
SetBoundaryDofsScatterIndices2(face,f_ind);
}
f_ind++;
}
}
MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
(type==FaceType::Interior? "interior" : "boundary") <<
" faces: " << f_ind << " vs " << nf );
// Summation of the offsets
for (int i = 1; i <= ndofs; ++i)
{
gather_offsets[i] += gather_offsets[i - 1];
}
}
void ParNCL2FaceRestriction::ComputeGatherIndices()
{
Mesh &mesh = *fes.GetMesh();
// Computation of gather_indices
int f_ind = 0;
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
{
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
if ( face.IsNonconformingCoarse() )
{
// We skip nonconforming coarse faces as they are treated
// by the corresponding nonconforming fine faces.
continue;
}
else if ( face.IsOfFaceType(type) )
{
SetFaceDofsGatherIndices1(face,f_ind);
if (m==L2FaceValues::DoubleValued &&
type==FaceType::Interior &&
face.IsLocal())
{
PermuteAndSetFaceDofsGatherIndices2(face,f_ind);
}
f_ind++;
}
}
MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
(type==FaceType::Interior? "interior" : "boundary") <<
" faces: " << f_ind << " vs " << nf );
// Switch back offsets to their correct value
for (int i = ndofs; i > 0; --i)
{
gather_offsets[i] = gather_offsets[i - 1];
}
gather_offsets[0] = 0;
}
} // namespace mfem
#endif