Files
mfem/fem/transfer.cpp
T

556 lines
16 KiB
C++

// Copyright (c) 2010-2020, 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 "transfer.hpp"
#include "../general/forall.hpp"
namespace mfem
{
TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_)
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize())
{
if (lFESpace_.FEColl() == hFESpace_.FEColl())
{
OperatorPtr P(Operator::ANY_TYPE);
hFESpace_.GetTransferOperator(lFESpace_, P);
P.SetOperatorOwner(false);
opr = P.Ptr();
}
else if (lFESpace_.GetMesh()->GetNE() > 0
&& hFESpace_.GetMesh()->GetNE() > 0
&& dynamic_cast<const TensorBasisElement*>(lFESpace_.GetFE(0))
&& dynamic_cast<const TensorBasisElement*>(hFESpace_.GetFE(0)))
{
opr = new TensorProductPRefinementTransferOperator(lFESpace_, hFESpace_);
}
else
{
opr = new PRefinementTransferOperator(lFESpace_, hFESpace_);
}
}
TransferOperator::~TransferOperator() { delete opr; }
void TransferOperator::Mult(const Vector& x, Vector& y) const
{
opr->Mult(x, y);
}
void TransferOperator::MultTranspose(const Vector& x, Vector& y) const
{
opr->MultTranspose(x, y);
}
PRefinementTransferOperator::PRefinementTransferOperator(
const FiniteElementSpace& lFESpace_, const FiniteElementSpace& hFESpace_)
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize()), lFESpace(lFESpace_),
hFESpace(hFESpace_)
{
}
PRefinementTransferOperator::~PRefinementTransferOperator() {}
void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
{
Mesh* mesh = hFESpace.GetMesh();
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
Vector subY, subX;
Geometry::Type cached_geom = Geometry::INVALID;
const FiniteElement* h_fe = NULL;
const FiniteElement* l_fe = NULL;
IsoparametricTransformation T;
int vdim = lFESpace.GetVDim();
for (int i = 0; i < mesh->GetNE(); i++)
{
hFESpace.GetElementDofs(i, h_dofs);
lFESpace.GetElementDofs(i, l_dofs);
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
if (geom != cached_geom)
{
h_fe = hFESpace.GetFE(i);
l_fe = lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
subY.SetSize(loc_prol.Height());
cached_geom = geom;
}
for (int vd = 0; vd < vdim; vd++)
{
l_dofs.Copy(l_vdofs);
lFESpace.DofsToVDofs(vd, l_vdofs);
h_dofs.Copy(h_vdofs);
hFESpace.DofsToVDofs(vd, h_vdofs);
x.GetSubVector(l_vdofs, subX);
loc_prol.Mult(subX, subY);
y.SetSubVector(h_vdofs, subY);
}
}
}
void PRefinementTransferOperator::MultTranspose(const Vector& x,
Vector& y) const
{
y = 0.0;
Mesh* mesh = hFESpace.GetMesh();
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
Vector subY, subX;
Array<char> processed(hFESpace.GetVSize());
processed = 0;
Geometry::Type cached_geom = Geometry::INVALID;
const FiniteElement* h_fe = NULL;
const FiniteElement* l_fe = NULL;
IsoparametricTransformation T;
int vdim = lFESpace.GetVDim();
for (int i = 0; i < mesh->GetNE(); i++)
{
hFESpace.GetElementDofs(i, h_dofs);
lFESpace.GetElementDofs(i, l_dofs);
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
if (geom != cached_geom)
{
h_fe = hFESpace.GetFE(i);
l_fe = lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
loc_prol.Transpose();
subY.SetSize(loc_prol.Height());
cached_geom = geom;
}
for (int vd = 0; vd < vdim; vd++)
{
l_dofs.Copy(l_vdofs);
lFESpace.DofsToVDofs(vd, l_vdofs);
h_dofs.Copy(h_vdofs);
hFESpace.DofsToVDofs(vd, h_vdofs);
x.GetSubVector(h_vdofs, subX);
for (int p = 0; p < h_dofs.Size(); ++p)
{
if (processed[lFESpace.DecodeDof(h_dofs[p])])
{
subX[p] = 0.0;
}
}
loc_prol.Mult(subX, subY);
y.AddElementVector(l_vdofs, subY);
}
for (int p = 0; p < h_dofs.Size(); ++p)
{
processed[lFESpace.DecodeDof(h_dofs[p])] = 1;
}
}
}
TensorProductPRefinementTransferOperator::
TensorProductPRefinementTransferOperator(
const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_)
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize()), lFESpace(lFESpace_),
hFESpace(hFESpace_)
{
// Assuming the same element type
Mesh* mesh = lFESpace.GetMesh();
dim = mesh->Dimension();
if (mesh->GetNE() == 0)
{
return;
}
const FiniteElement& el = *lFESpace.GetFE(0);
const TensorBasisElement* ltel =
dynamic_cast<const TensorBasisElement*>(&el);
MFEM_VERIFY(ltel, "Low order FE space must be tensor product space");
const TensorBasisElement* htel =
dynamic_cast<const TensorBasisElement*>(hFESpace.GetFE(0));
MFEM_VERIFY(htel, "High order FE space must be tensor product space");
const Array<int>& hdofmap = htel->GetDofMap();
const IntegrationRule& ir = hFESpace.GetFE(0)->GetNodes();
IntegrationRule irLex = ir;
// The quadrature points, or equivalently, the dofs of the high order space
// must be sorted in lexicographical order
for (int i = 0; i < ir.GetNPoints(); ++i)
{
irLex.IntPoint(i) = ir.IntPoint(hdofmap[i]);
}
NE = lFESpace.GetNE();
const DofToQuad& maps = el.GetDofToQuad(irLex, DofToQuad::TENSOR);
D1D = maps.ndof;
Q1D = maps.nqpt;
B = maps.B;
Bt = maps.Bt;
elem_restrict_lex_l =
lFESpace.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
MFEM_VERIFY(elem_restrict_lex_l,
"Low order ElementRestriction not available");
elem_restrict_lex_h =
hFESpace.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
MFEM_VERIFY(elem_restrict_lex_h,
"High order ElementRestriction not available");
localL.SetSize(elem_restrict_lex_l->Height(), Device::GetMemoryType());
localH.SetSize(elem_restrict_lex_h->Height(), Device::GetMemoryType());
localL.UseDevice(true);
localH.UseDevice(true);
MFEM_VERIFY(dynamic_cast<const ElementRestriction*>(elem_restrict_lex_h),
"High order element restriction is of unsupported type");
mask.SetSize(localH.Size(), Device::GetMemoryType());
static_cast<const ElementRestriction*>(elem_restrict_lex_h)
->BooleanMask(mask);
mask.UseDevice(true);
}
namespace TransferKernels
{
void Prolongation2D(const int NE, const int D1D, const int Q1D,
const Vector& localL, Vector& localH,
const Array<double>& B, const Vector& mask)
{
auto x_ = Reshape(localL.Read(), D1D, D1D, NE);
auto y_ = Reshape(localH.ReadWrite(), Q1D, Q1D, NE);
auto B_ = Reshape(B.Read(), Q1D, D1D);
auto m_ = Reshape(mask.Read(), Q1D, Q1D, NE);
localH = 0.0;
MFEM_FORALL(e, NE,
{
for (int dy = 0; dy < D1D; ++dy)
{
double sol_x[MAX_Q1D];
for (int qy = 0; qy < Q1D; ++qy)
{
sol_x[qy] = 0.0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const double s = x_(dx, dy, e);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] += B_(qx, dx) * s;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double d2q = B_(qy, dy);
for (int qx = 0; qx < Q1D; ++qx)
{
y_(qx, qy, e) += d2q * sol_x[qx];
}
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
y_(qx, qy, e) *= m_(qx, qy, e);
}
}
});
}
void Prolongation3D(const int NE, const int D1D, const int Q1D,
const Vector& localL, Vector& localH,
const Array<double>& B, const Vector& mask)
{
auto x_ = Reshape(localL.Read(), D1D, D1D, D1D, NE);
auto y_ = Reshape(localH.ReadWrite(), Q1D, Q1D, Q1D, NE);
auto B_ = Reshape(B.Read(), Q1D, D1D);
auto m_ = Reshape(mask.Read(), Q1D, Q1D, Q1D, NE);
localH = 0.0;
MFEM_FORALL(e, NE,
{
for (int dz = 0; dz < D1D; ++dz)
{
double sol_xy[MAX_Q1D][MAX_Q1D];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
double sol_x[MAX_Q1D];
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] = 0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const double s = x_(dx, dy, dz, e);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] += B_(qx, dx) * s;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double wy = B_(qy, dy);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] += wy * sol_x[qx];
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
const double wz = B_(qz, dz);
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
y_(qx, qy, qz, e) += wz * sol_xy[qy][qx];
}
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
y_(qx, qy, qz, e) *= m_(qx, qy, qz, e);
}
}
}
});
}
void Restriction2D(const int NE, const int D1D, const int Q1D,
const Vector& localH, Vector& localL,
const Array<double>& Bt, const Vector& mask)
{
auto x_ = Reshape(localH.Read(), Q1D, Q1D, NE);
auto y_ = Reshape(localL.ReadWrite(), D1D, D1D, NE);
auto Bt_ = Reshape(Bt.Read(), D1D, Q1D);
auto m_ = Reshape(mask.Read(), Q1D, Q1D, NE);
localL = 0.0;
MFEM_FORALL(e, NE,
{
for (int qy = 0; qy < Q1D; ++qy)
{
double sol_x[MAX_D1D];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] = 0.0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const double s = m_(qx, qy, e) * x_(qx, qy, e);
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] += Bt_(dx, qx) * s;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const double q2d = Bt_(dy, qy);
for (int dx = 0; dx < D1D; ++dx)
{
y_(dx, dy, e) += q2d * sol_x[dx];
}
}
}
});
}
void Restriction3D(const int NE, const int D1D, const int Q1D,
const Vector& localH, Vector& localL,
const Array<double>& Bt, const Vector& mask)
{
auto x_ = Reshape(localH.Read(), Q1D, Q1D, Q1D, NE);
auto y_ = Reshape(localL.ReadWrite(), D1D, D1D, D1D, NE);
auto Bt_ = Reshape(Bt.Read(), D1D, Q1D);
auto m_ = Reshape(mask.Read(), Q1D, Q1D, Q1D, NE);
localL = 0.0;
MFEM_FORALL(e, NE,
{
for (int qz = 0; qz < Q1D; ++qz)
{
double sol_xy[MAX_D1D][MAX_D1D];
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
sol_xy[dy][dx] = 0;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
double sol_x[MAX_D1D];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] = 0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const double s = m_(qx, qy, qz, e) * x_(qx, qy, qz, e);
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] += Bt_(dx, qx) * s;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const double wy = Bt_(dy, qy);
for (int dx = 0; dx < D1D; ++dx)
{
sol_xy[dy][dx] += wy * sol_x[dx];
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
const double wz = Bt_(dz, qz);
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
y_(dx, dy, dz, e) += wz * sol_xy[dy][dx];
}
}
}
}
});
}
} // namespace TransferKernels
TensorProductPRefinementTransferOperator::
~TensorProductPRefinementTransferOperator()
{
}
void TensorProductPRefinementTransferOperator::Mult(const Vector& x,
Vector& y) const
{
if (lFESpace.GetMesh()->GetNE() == 0)
{
return;
}
elem_restrict_lex_l->Mult(x, localL);
if (dim == 2)
{
TransferKernels::Prolongation2D(NE, D1D, Q1D, localL, localH, B, mask);
}
else if (dim == 3)
{
TransferKernels::Prolongation3D(NE, D1D, Q1D, localL, localH, B, mask);
}
else
{
MFEM_ABORT("TensorProductPRefinementTransferOperator::Mult not "
"implemented for dim = "
<< dim);
}
elem_restrict_lex_h->MultTranspose(localH, y);
}
void TensorProductPRefinementTransferOperator::MultTranspose(const Vector& x,
Vector& y) const
{
if (lFESpace.GetMesh()->GetNE() == 0)
{
return;
}
elem_restrict_lex_h->Mult(x, localH);
if (dim == 2)
{
TransferKernels::Restriction2D(NE, D1D, Q1D, localH, localL, Bt, mask);
}
else if (dim == 3)
{
TransferKernels::Restriction3D(NE, D1D, Q1D, localH, localL, Bt, mask);
}
else
{
MFEM_ABORT("TensorProductPRefinementTransferOperator::MultTranspose not "
"implemented for dim = "
<< dim);
}
elem_restrict_lex_l->MultTranspose(localL, y);
}
#ifdef MFEM_USE_MPI
TrueTransferOperator::TrueTransferOperator(const
ParFiniteElementSpace& lFESpace_,
const ParFiniteElementSpace& hFESpace_)
: Operator(hFESpace_.GetTrueVSize(), lFESpace_.GetTrueVSize()),
lFESpace(lFESpace_),
hFESpace(hFESpace_)
{
localTransferOperator = new TransferOperator(lFESpace_, hFESpace_);
tmpL.SetSize(lFESpace_.GetVSize());
tmpH.SetSize(hFESpace_.GetVSize());
hFESpace.GetRestrictionMatrix()->BuildTranspose();
}
TrueTransferOperator::~TrueTransferOperator()
{
delete localTransferOperator;
}
void TrueTransferOperator::Mult(const Vector& x, Vector& y) const
{
lFESpace.GetProlongationMatrix()->Mult(x, tmpL);
localTransferOperator->Mult(tmpL, tmpH);
hFESpace.GetRestrictionMatrix()->Mult(tmpH, y);
}
void TrueTransferOperator::MultTranspose(const Vector& x, Vector& y) const
{
hFESpace.GetRestrictionMatrix()->MultTranspose(x, tmpH);
localTransferOperator->MultTranspose(tmpH, tmpL);
lFESpace.GetProlongationMatrix()->MultTranspose(tmpL, y);
}
#endif
} // namespace mfem