// 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(lFESpace_.GetFE(0)) && dynamic_cast(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 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 l_dofs, h_dofs, l_vdofs, h_vdofs; DenseMatrix loc_prol; Vector subY, subX; Array 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(&el); MFEM_VERIFY(ltel, "Low order FE space must be tensor product space"); const TensorBasisElement* htel = dynamic_cast(hFESpace.GetFE(0)); MFEM_VERIFY(htel, "High order FE space must be tensor product space"); const Array& 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(elem_restrict_lex_h), "High order element restriction is of unsupported type"); mask.SetSize(localH.Size(), Device::GetMemoryType()); static_cast(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& 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& 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& 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& 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