Files
mfem/fem/fe/fe_base.cpp
T

2825 lines
76 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.
// Finite Element Base classes
#include "fe_base.hpp"
#include "face_map_utils.hpp"
#include "../coefficient.hpp"
namespace mfem
{
using namespace std;
DofToQuad DofToQuad::Abs() const
{
DofToQuad d2q(*this);
d2q.B.Abs();
d2q.Bt.Abs();
d2q.G.Abs();
d2q.Gt.Abs();
return d2q;
}
FiniteElement::FiniteElement(int D, Geometry::Type G,
int Do, int O, int F)
: Nodes(Do)
{
dim = D ; geom_type = G ; dof = Do ; order = O ; func_space = F;
vdim = 0 ; cdim = 0;
range_type = SCALAR;
map_type = VALUE;
deriv_type = NONE;
deriv_range_type = SCALAR;
deriv_map_type = VALUE;
for (int i = 0; i < Geometry::MaxDim; i++) { orders[i] = -1; }
#ifndef MFEM_THREAD_SAFE
vshape.SetSize(dof, dim);
#endif
}
void FiniteElement::CalcVShape(
const IntegrationPoint &ip, DenseMatrix &shape) const
{
MFEM_ABORT("method is not implemented for this class");
}
void FiniteElement::CalcVShape(
ElementTransformation &Trans, DenseMatrix &shape) const
{
MFEM_ABORT("method is not implemented for this class");
}
void FiniteElement::CalcDivShape(
const IntegrationPoint &ip, Vector &divshape) const
{
MFEM_ABORT("method is not implemented for this class");
}
void FiniteElement::CalcPhysDivShape(
ElementTransformation &Trans, Vector &div_shape) const
{
CalcDivShape(Trans.GetIntPoint(), div_shape);
div_shape *= (1.0 / Trans.Weight());
}
void FiniteElement::CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const
{
MFEM_ABORT("method is not implemented for this class");
}
void FiniteElement::CalcPhysCurlShape(ElementTransformation &Trans,
DenseMatrix &curl_shape) const
{
switch (dim)
{
case 3:
{
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
CalcCurlShape(Trans.GetIntPoint(), vshape);
MultABt(vshape, Trans.Jacobian(), curl_shape);
curl_shape *= (1.0 / Trans.Weight());
break;
}
case 2:
// This is valid for both 2x2 and 3x2 Jacobians
CalcCurlShape(Trans.GetIntPoint(), curl_shape);
curl_shape *= (1.0 / Trans.Weight());
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
}
void FiniteElement::GetFaceDofs(int face, int **dofs, int *ndofs) const
{
MFEM_ABORT("method is not overloaded");
}
void FiniteElement::CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const
{
MFEM_ABORT("method is not overloaded");
}
void FiniteElement::GetLocalInterpolation(ElementTransformation &Trans,
DenseMatrix &I) const
{
MFEM_ABORT("method is not overloaded");
}
void FiniteElement::GetLocalRestriction(ElementTransformation &,
DenseMatrix &) const
{
MFEM_ABORT("method is not overloaded");
}
void FiniteElement::GetTransferMatrix(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &I) const
{
MFEM_ABORT("method is not overloaded");
}
void FiniteElement::Project(
Coefficient &coeff, ElementTransformation &Trans, Vector &dofs) const
{
MFEM_ABORT("method is not overloaded");
}
void FiniteElement::Project(
VectorCoefficient &vc, ElementTransformation &Trans, Vector &dofs) const
{
MFEM_ABORT("method is not overloaded");
}
void FiniteElement::ProjectFromNodes(Vector &vc, ElementTransformation &Trans,
Vector &dofs) const
{
mfem_error("FiniteElement::ProjectFromNodes() (vector) is not overloaded!");
}
void FiniteElement::ProjectMatrixCoefficient(
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
{
MFEM_ABORT("method is not overloaded");
}
void FiniteElement::ProjectDelta(int vertex, Vector &dofs) const
{
MFEM_ABORT("method is not implemented for this element");
}
void FiniteElement::Project(
const FiniteElement &fe, ElementTransformation &Trans, DenseMatrix &I) const
{
MFEM_ABORT("method is not implemented for this element");
}
void FiniteElement::ProjectGrad(
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &grad) const
{
MFEM_ABORT("method is not implemented for this element");
}
void FiniteElement::ProjectCurl(
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const
{
MFEM_ABORT("method is not implemented for this element");
}
void FiniteElement::ProjectDiv(
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &div) const
{
MFEM_ABORT("method is not implemented for this element");
}
void FiniteElement::CalcPhysShape(ElementTransformation &Trans,
Vector &shape) const
{
CalcShape(Trans.GetIntPoint(), shape);
if (map_type == INTEGRAL)
{
shape /= Trans.Weight();
}
}
void FiniteElement::CalcPhysDShape(ElementTransformation &Trans,
DenseMatrix &dshape) const
{
MFEM_ASSERT(map_type == VALUE, "");
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
CalcDShape(Trans.GetIntPoint(), vshape);
Mult(vshape, Trans.InverseJacobian(), dshape);
}
void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
Vector &Laplacian) const
{
MFEM_ASSERT(map_type == VALUE, "");
// Simpler routine if mapping is affine
if (Trans.Hessian().FNorm2() < 1e-20)
{
CalcPhysLinLaplacian(Trans, Laplacian);
return;
}
// Compute full Hessian first if non-affine
int size = (dim*(dim+1))/2;
DenseMatrix hess(dof, size);
CalcPhysHessian(Trans,hess);
if (dim == 3)
{
for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = hess(nd,0) + hess(nd,3) + hess(nd,5);
}
}
else if (dim == 2)
{
for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = hess(nd,0) + hess(nd,2);
}
}
else
{
for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = hess(nd,0);
}
}
}
// Assume a linear mapping
void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
Vector &Laplacian) const
{
MFEM_ASSERT(map_type == VALUE, "");
int size = (dim*(dim+1))/2;
DenseMatrix hess(dof, size);
DenseMatrix Gij(dim,dim);
Vector scale(size);
CalcHessian(Trans.GetIntPoint(), hess);
MultAAt(Trans.InverseJacobian(), Gij);
if (dim == 3)
{
scale[0] = Gij(0,0);
scale[1] = 2*Gij(0,1);
scale[2] = 2*Gij(0,2);
scale[3] = Gij(1,1);
scale[4] = 2*Gij(1,2);
scale[5] = Gij(2,2);
}
else if (dim == 2)
{
scale[0] = Gij(0,0);
scale[1] = 2*Gij(0,1);
scale[2] = Gij(1,1);
}
else
{
scale[0] = Gij(0,0);
}
for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = 0.0;
for (int ii = 0; ii < size; ii++)
{
Laplacian[nd] += hess(nd,ii)*scale[ii];
}
}
}
void FiniteElement::CalcPhysHessian(ElementTransformation &Trans,
DenseMatrix& Hessian) const
{
MFEM_ASSERT(map_type == VALUE, "");
// Roll 2-Tensors in vectors and 4-Tensor in Matrix, exploiting symmetry
Array<int> map(dim*dim);
if (dim == 3)
{
map[0] = 0;
map[1] = 1;
map[2] = 2;
map[3] = 1;
map[4] = 3;
map[5] = 4;
map[6] = 2;
map[7] = 4;
map[8] = 5;
}
else if (dim == 2)
{
map[0] = 0;
map[1] = 1;
map[2] = 1;
map[3] = 2;
}
else
{
map[0] = 0;
}
// Hessian in ref coords
int size = (dim*(dim+1))/2;
DenseMatrix hess(dof, size);
CalcHessian(Trans.GetIntPoint(), hess);
// Gradient in physical coords
if (Trans.Hessian().FNorm2() > 1e-10)
{
DenseMatrix grad(dof, dim);
CalcPhysDShape(Trans, grad);
DenseMatrix gmap(dof, size);
Mult(grad,Trans.Hessian(),gmap);
hess -= gmap;
}
// LHM
DenseMatrix lhm(size,size);
DenseMatrix invJ = Trans.Jacobian();
lhm = 0.0;
for (int i = 0; i < dim; i++)
{
for (int j = 0; j < dim; j++)
{
for (int k = 0; k < dim; k++)
{
for (int l = 0; l < dim; l++)
{
lhm(map[i*dim+j],map[k*dim+l]) += invJ(i,k)*invJ(j,l);
}
}
}
}
// Correct multiplicity
Vector mult(size);
mult = 0.0;
for (int i = 0; i < dim*dim; i++) { mult[map[i]]++; }
lhm.InvRightScaling(mult);
// Hessian in physical coords
lhm.Invert();
Mult(hess, lhm, Hessian);
}
const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const
{
DofToQuad *d2q = nullptr;
MFEM_VERIFY(mode == DofToQuad::FULL, "invalid mode requested");
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
if (!d2q)
{
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
d2q = new DofToQuad;
const int nqpt = ir.GetNPoints();
d2q->FE = this;
d2q->IntRule = &ir;
d2q->mode = mode;
d2q->ndof = dof;
d2q->nqpt = nqpt;
switch (range_type)
{
case SCALAR:
{
d2q->B.SetSize(nqpt*dof);
d2q->Bt.SetSize(dof*nqpt);
Vector shape;
vshape.GetColumnReference(0, shape);
for (int i = 0; i < nqpt; i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
CalcShape(ip, shape);
for (int j = 0; j < dof; j++)
{
d2q->B[i+nqpt*j] = d2q->Bt[j+dof*i] = shape(j);
}
}
break;
}
case VECTOR:
{
d2q->B.SetSize(nqpt*dim*dof);
d2q->Bt.SetSize(dof*nqpt*dim);
for (int i = 0; i < nqpt; i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
CalcVShape(ip, vshape);
for (int d = 0; d < dim; d++)
{
for (int j = 0; j < dof; j++)
{
d2q->B[i+nqpt*(d+dim*j)] =
d2q->Bt[j+dof*(i+nqpt*d)] = vshape(j, d);
}
}
}
break;
}
case UNKNOWN_RANGE_TYPE:
// Skip B and Bt for unknown range type
break;
}
switch (deriv_type)
{
case GRAD:
{
d2q->G.SetSize(nqpt*dim*dof);
d2q->Gt.SetSize(dof*nqpt*dim);
for (int i = 0; i < nqpt; i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
CalcDShape(ip, vshape);
for (int d = 0; d < dim; d++)
{
for (int j = 0; j < dof; j++)
{
d2q->G[i+nqpt*(d+dim*j)] =
d2q->Gt[j+dof*(i+nqpt*d)] = vshape(j, d);
}
}
}
break;
}
case DIV:
{
d2q->G.SetSize(nqpt*dof);
d2q->Gt.SetSize(dof*nqpt);
Vector divshape;
vshape.GetColumnReference(0, divshape);
for (int i = 0; i < nqpt; i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
CalcDivShape(ip, divshape);
for (int j = 0; j < dof; j++)
{
d2q->G[i+nqpt*j] = d2q->Gt[j+dof*i] = divshape(j);
}
}
break;
}
case CURL:
{
d2q->G.SetSize(nqpt*cdim*dof);
d2q->Gt.SetSize(dof*nqpt*cdim);
DenseMatrix curlshape(vshape.GetData(), dof, cdim); // cdim <= dim
for (int i = 0; i < nqpt; i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
CalcCurlShape(ip, curlshape);
for (int d = 0; d < cdim; d++)
{
for (int j = 0; j < dof; j++)
{
d2q->G[i+nqpt*(d+cdim*j)] =
d2q->Gt[j+dof*(i+nqpt*d)] = curlshape(j, d);
}
}
}
break;
}
case NONE:
// Skip G and Gt for unknown derivative type
break;
}
dof2quad_array.Append(d2q);
}
}
return *d2q;
}
void FiniteElement::GetFaceMap(const int face_id,
Array<int> &face_map) const
{
MFEM_ABORT("method is not implemented for this element");
}
FiniteElement::~FiniteElement()
{
for (int i = 0; i < dof2quad_array.Size(); i++)
{
delete dof2quad_array[i];
}
}
void ScalarFiniteElement::NodalLocalInterpolation(
ElementTransformation &Trans, DenseMatrix &I,
const ScalarFiniteElement &fine_fe) const
{
real_t v[Geometry::MaxDim];
Vector vv(v, dim);
IntegrationPoint f_ip;
#ifdef MFEM_THREAD_SAFE
Vector shape(dof);
#else
Vector shape;
vshape.GetColumnReference(0, shape);
#endif
MFEM_ASSERT(map_type == fine_fe.GetMapType(), "");
I.SetSize(fine_fe.dof, dof);
for (int i = 0; i < fine_fe.dof; i++)
{
Trans.Transform(fine_fe.Nodes.IntPoint(i), vv);
f_ip.Set(v, dim);
CalcShape(f_ip, shape);
for (int j = 0; j < dof; j++)
{
if (fabs(I(i,j) = shape(j)) < 1.0e-12)
{
I(i,j) = 0.0;
}
}
}
if (map_type == INTEGRAL)
{
// assuming Trans is linear; this should be ok for all refinement types
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
I *= Trans.Weight();
}
}
void ScalarFiniteElement::ScalarLocalInterpolation(
ElementTransformation &Trans, DenseMatrix &I,
const ScalarFiniteElement &fine_fe) const
{
// General "interpolation", defined by L2 projection
real_t v[Geometry::MaxDim];
Vector vv(v, dim);
IntegrationPoint f_ip;
const int fs = fine_fe.GetDof(), cs = this->GetDof();
I.SetSize(fs, cs);
Vector fine_shape(fs), coarse_shape(cs);
DenseMatrix fine_mass(fs), fine_coarse_mass(fs, cs); // initialized with 0
const int ir_order =
std::max(GetOrder(), fine_fe.GetOrder()) + fine_fe.GetOrder();
const IntegrationRule &ir = IntRules.Get(fine_fe.GetGeomType(), ir_order);
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
fine_fe.CalcShape(ip, fine_shape);
Trans.Transform(ip, vv);
f_ip.Set(v, dim);
this->CalcShape(f_ip, coarse_shape);
AddMult_a_VVt(ip.weight, fine_shape, fine_mass);
AddMult_a_VWt(ip.weight, fine_shape, coarse_shape, fine_coarse_mass);
}
DenseMatrixInverse fine_mass_inv(fine_mass);
fine_mass_inv.Mult(fine_coarse_mass, I);
if (map_type == INTEGRAL)
{
// assuming Trans is linear; this should be ok for all refinement types
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
I *= Trans.Weight();
}
}
void ScalarFiniteElement::ScalarLocalL2Restriction(
ElementTransformation &Trans, DenseMatrix &R,
const ScalarFiniteElement &coarse_fe) const
{
// General "restriction", defined by L2 projection
real_t v[Geometry::MaxDim];
Vector vv(v, dim);
const int cs = coarse_fe.GetDof(), fs = this->GetDof();
R.SetSize(cs, fs);
Vector fine_shape(fs), coarse_shape(cs);
DenseMatrix coarse_mass(cs), coarse_fine_mass(cs, fs); // initialized with 0
const int ir_order = GetOrder() + coarse_fe.GetOrder();
const IntegrationRule &ir = IntRules.Get(coarse_fe.GetGeomType(), ir_order);
// integrate coarse_mass in the coarse space
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &c_ip = ir.IntPoint(i);
coarse_fe.CalcShape(c_ip, coarse_shape);
AddMult_a_VVt(c_ip.weight, coarse_shape, coarse_mass);
}
// integrate coarse_fine_mass in the fine space
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &f_ip = ir.IntPoint(i);
this->CalcShape(f_ip, fine_shape);
Trans.Transform(f_ip, vv);
IntegrationPoint c_ip;
c_ip.Set(v, dim);
coarse_fe.CalcShape(c_ip, coarse_shape);
AddMult_a_VWt(f_ip.weight*Trans.Weight(), coarse_shape, fine_shape,
coarse_fine_mass);
}
DenseMatrixInverse coarse_mass_inv(coarse_mass);
coarse_mass_inv.Mult(coarse_fine_mass, R);
if (map_type == INTEGRAL)
{
// assuming Trans is linear; this should be ok for all refinement types
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
R *= 1.0 / Trans.Weight();
}
}
void NodalFiniteElement::CreateLexicographicFullMap(const IntegrationRule &ir)
const
{
// Get the FULL version of the map. This call contains omp critical region,
// so it is done before the critical region below.
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
// Do not run if the new Dof2Quad is already present, e.g. added in a
// previous call or added by another omp thread.
if (DofToQuad::SearchArray(dof2quad_array, ir,
DofToQuad::LEXICOGRAPHIC_FULL) == nullptr)
{
// Undo the native ordering which is what FiniteElement::GetDofToQuad
// returns.
auto *d2q_new = new DofToQuad(d2q);
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
const int nqpt = ir.GetNPoints();
const int b_dim = (range_type == VECTOR) ? dim : 1;
for (int i = 0; i < nqpt; i++)
{
for (int d = 0; d < b_dim; d++)
{
for (int j = 0; j < dof; j++)
{
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
}
}
}
const int g_dim = [this]()
{
switch (deriv_type)
{
case GRAD: return dim;
case DIV: return 1;
case CURL: return cdim;
default: return 0;
}
}();
for (int i = 0; i < nqpt; i++)
{
for (int d = 0; d < g_dim; d++)
{
for (int j = 0; j < dof; j++)
{
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
}
}
}
dof2quad_array.Append(d2q_new);
}
}
}
const DofToQuad &NodalFiniteElement::GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const
{
DofToQuad *d2q = nullptr;
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
}
if (d2q) { return *d2q; }
if (mode != DofToQuad::LEXICOGRAPHIC_FULL)
{
return FiniteElement::GetDofToQuad(ir, mode);
}
else
{
CreateLexicographicFullMap(ir);
return NodalFiniteElement::GetDofToQuad(ir, mode);
}
}
void NodalFiniteElement::ProjectCurl_2D(
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const
{
DenseMatrix curl_shape(fe.GetDof(), 1);
curl.SetSize(dof, fe.GetDof());
for (int i = 0; i < dof; i++)
{
fe.CalcCurlShape(Nodes.IntPoint(i), curl_shape);
real_t w = 1.0;
if (GetMapType() == FiniteElement::VALUE)
{
Trans.SetIntPoint(&Nodes.IntPoint(i));
w /= Trans.Weight();
}
for (int j = 0; j < fe.GetDof(); j++)
{
curl(i,j) = w * curl_shape(j,0);
}
}
}
void InvertLinearTrans(ElementTransformation &trans,
const IntegrationPoint &pt, Vector &x)
{
// invert a linear transform with one Newton step
IntegrationPoint p0;
p0.Set3(0, 0, 0);
trans.Transform(p0, x);
real_t store[3];
Vector v(store, x.Size());
pt.Get(store, x.Size());
v -= x;
trans.InverseJacobian().Mult(v, x);
}
void NodalFiniteElement::GetLocalRestriction(ElementTransformation &Trans,
DenseMatrix &R) const
{
IntegrationPoint ipt;
Vector pt(&ipt.x, dim);
#ifdef MFEM_THREAD_SAFE
Vector shape(dof);
#else
Vector shape;
vshape.GetColumnReference(0, shape);
#endif
Trans.SetIntPoint(&Nodes[0]);
for (int j = 0; j < dof; j++)
{
InvertLinearTrans(Trans, Nodes[j], pt);
if (Geometries.CheckPoint(geom_type, ipt)) // do we need an epsilon here?
{
CalcShape(ipt, shape);
R.SetRow(j, shape);
}
else
{
// Set the whole row to avoid valgrind warnings in R.Threshold().
R.SetRow(j, infinity());
}
}
R.Threshold(1e-12);
}
void NodalFiniteElement::Project(
Coefficient &coeff, ElementTransformation &Trans, Vector &dofs) const
{
for (int i = 0; i < dof; i++)
{
const IntegrationPoint &ip = Nodes.IntPoint(i);
// some coefficients expect that Trans.IntPoint is the same
// as the second argument of Eval
Trans.SetIntPoint(&ip);
dofs(i) = coeff.Eval(Trans, ip);
if (map_type == INTEGRAL)
{
dofs(i) *= Trans.Weight();
}
}
}
void NodalFiniteElement::Project(
VectorCoefficient &vc, ElementTransformation &Trans, Vector &dofs) const
{
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
Vector x(vc.GetVDim());
for (int i = 0; i < dof; i++)
{
const IntegrationPoint &ip = Nodes.IntPoint(i);
Trans.SetIntPoint(&ip);
vc.Eval (x, Trans, ip);
if (map_type == INTEGRAL)
{
x *= Trans.Weight();
}
for (int j = 0; j < x.Size(); j++)
{
dofs(dof*j+i) = x(j);
}
}
}
void NodalFiniteElement::ProjectMatrixCoefficient(
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
{
// (mc.height x mc.width) @ DOFs -> (dof x mc.width x mc.height) in dofs
MFEM_ASSERT(dofs.Size() == mc.GetHeight()*mc.GetWidth()*dof, "");
DenseMatrix MQ(mc.GetHeight(), mc.GetWidth());
for (int k = 0; k < dof; k++)
{
T.SetIntPoint(&Nodes.IntPoint(k));
mc.Eval(MQ, T, Nodes.IntPoint(k));
if (map_type == INTEGRAL) { MQ *= T.Weight(); }
for (int r = 0; r < MQ.Height(); r++)
{
for (int d = 0; d < MQ.Width(); d++)
{
dofs(k+dof*(d+MQ.Width()*r)) = MQ(r,d);
}
}
}
}
void NodalFiniteElement::Project(
const FiniteElement &fe, ElementTransformation &Trans, DenseMatrix &I) const
{
if (fe.GetRangeType() == SCALAR)
{
Vector shape(fe.GetDof());
I.SetSize(dof, fe.GetDof());
if (map_type == fe.GetMapType())
{
for (int k = 0; k < dof; k++)
{
fe.CalcShape(Nodes.IntPoint(k), shape);
for (int j = 0; j < shape.Size(); j++)
{
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
}
}
}
else
{
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint(&Nodes.IntPoint(k));
fe.CalcPhysShape(Trans, shape);
if (map_type == INTEGRAL)
{
shape *= Trans.Weight();
}
for (int j = 0; j < shape.Size(); j++)
{
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
}
}
}
}
else
{
DenseMatrix vshape(fe.GetDof(), std::max(Trans.GetSpaceDim(),
fe.GetRangeDim()));
I.SetSize(vshape.Width()*dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint(&Nodes.IntPoint(k));
fe.CalcVShape(Trans, vshape);
if (map_type == INTEGRAL)
{
vshape *= Trans.Weight();
}
for (int j = 0; j < vshape.Height(); j++)
for (int d = 0; d < vshape.Width(); d++)
{
I(k+d*dof,j) = vshape(j,d);
}
}
}
}
void NodalFiniteElement::ProjectGrad(
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &grad) const
{
MFEM_ASSERT(fe.GetMapType() == VALUE, "");
MFEM_ASSERT(Trans.GetSpaceDim() == dim, "")
DenseMatrix dshape(fe.GetDof(), dim), grad_k(fe.GetDof(), dim), Jinv(dim);
grad.SetSize(dim*dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
fe.CalcDShape(ip, dshape);
Trans.SetIntPoint(&ip);
CalcInverse(Trans.Jacobian(), Jinv);
Mult(dshape, Jinv, grad_k);
if (map_type == INTEGRAL)
{
grad_k *= Trans.Weight();
}
for (int j = 0; j < grad_k.Height(); j++)
for (int d = 0; d < dim; d++)
{
grad(k+d*dof,j) = grad_k(j,d);
}
}
}
void NodalFiniteElement::ProjectDiv(
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &div) const
{
real_t detJ;
Vector div_shape(fe.GetDof());
div.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
fe.CalcDivShape(ip, div_shape);
if (map_type == VALUE)
{
Trans.SetIntPoint(&ip);
detJ = Trans.Weight();
for (int j = 0; j < div_shape.Size(); j++)
{
div(k,j) = (fabs(div_shape(j)) < 1e-12) ? 0.0 : div_shape(j)/detJ;
}
}
else
{
for (int j = 0; j < div_shape.Size(); j++)
{
div(k,j) = (fabs(div_shape(j)) < 1e-12) ? 0.0 : div_shape(j);
}
}
}
}
void NodalFiniteElement::ReorderLexToNative(int ncomp,
Vector &dofs) const
{
MFEM_ASSERT(lex_ordering.Size() == dof, "Permutation is not defined by FE.");
MFEM_ASSERT(dofs.Size() == ncomp * dof, "Wrong input size.");
Vector dofs_native(ncomp * dof);
for (int i = 0; i < dof; i++)
{
for (int c = 0; c < ncomp; c++)
{
dofs_native(c*dof + lex_ordering[i]) = dofs(c*dof + i);
}
}
dofs = dofs_native;
}
VectorFiniteElement::VectorFiniteElement(int D, Geometry::Type G,
int Do, int O, int M, int F)
: FiniteElement(D, G, Do, O, F)
{
range_type = VECTOR;
map_type = M;
SetDerivMembers();
is_nodal = true;
vdim = dim;
if (map_type == H_CURL)
{
cdim = (dim == 3) ? 3 : 1;
}
}
void VectorFiniteElement::CalcShape(
const IntegrationPoint &ip, Vector &shape) const
{
mfem_error("Error: Cannot use scalar CalcShape(...) function with\n"
" VectorFiniteElements!");
}
void VectorFiniteElement::CalcDShape(
const IntegrationPoint &ip, DenseMatrix &dshape) const
{
mfem_error("Error: Cannot use scalar CalcDShape(...) function with\n"
" VectorFiniteElements!");
}
void VectorFiniteElement::SetDerivMembers()
{
switch (map_type)
{
case H_DIV:
switch (dim)
{
case 3: // div: 3D H_DIV -> 3D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
case 2: // div: 2D H_DIV -> 2D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_DIV_R2D:
switch (dim)
{
case 2: // div: 2D H_DIV_R2D -> 2D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
case 1: // div: 1D H_DIV_R2D -> 1D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_DIV_R1D:
switch (dim)
{
case 1: // div: 1D H_DIV_R1D -> 1D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL:
switch (dim)
{
case 3: // curl: 3D H_CURL -> 3D H_DIV
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV;
break;
case 2:
// curl: 2D H_CURL -> INTEGRAL
deriv_type = CURL;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
case 1:
deriv_type = NONE;
deriv_range_type = UNKNOWN_RANGE_TYPE;
deriv_map_type = UNKNOWN_MAP_TYPE;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL_R2D:
switch (dim)
{
case 2:
// curl: 2D H_CURL_R2D -> H_DIV_R2D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R2D;
break;
case 1:
// curl: 1D H_CURL_R2D -> H_DIV_R2D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R2D;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL_R1D:
switch (dim)
{
case 1:
// curl: 1D H_CURL_R1D -> H_DIV_R1D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R1D;
break;
case 0:
deriv_type = NONE;
deriv_range_type = UNKNOWN_RANGE_TYPE;
deriv_map_type = UNKNOWN_MAP_TYPE;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
default:
MFEM_ABORT("Invalid MapType = " << map_type);
}
}
void VectorFiniteElement::CalcVShape_RT(
ElementTransformation &Trans, DenseMatrix &shape) const
{
MFEM_ASSERT(map_type == H_DIV, "");
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
CalcVShape(Trans.GetIntPoint(), vshape);
MultABt(vshape, Trans.Jacobian(), shape);
shape *= (1.0 / Trans.Weight());
}
void VectorFiniteElement::CalcVShape_ND(
ElementTransformation &Trans, DenseMatrix &shape) const
{
MFEM_ASSERT(map_type == H_CURL, "");
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
CalcVShape(Trans.GetIntPoint(), vshape);
Mult(vshape, Trans.InverseJacobian(), shape);
}
void VectorFiniteElement::Project_RT(
const real_t *nk, const Array<int> &d2n,
VectorCoefficient &vc, ElementTransformation &Trans, Vector &dofs) const
{
real_t vk[Geometry::MaxDim];
const int sdim = Trans.GetSpaceDim();
MFEM_ASSERT(vc.GetVDim() == sdim, "");
Vector xk(vk, sdim);
const bool square_J = (dim == sdim);
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint(&Nodes.IntPoint(k));
vc.Eval(xk, Trans, Nodes.IntPoint(k));
// dof_k = nk^t adj(J) xk
dofs(k) = Trans.AdjugateJacobian().InnerProduct(vk, nk + d2n[k]*dim);
if (!square_J) { dofs(k) /= Trans.Weight(); }
}
}
void VectorFiniteElement::Project_RT(
const real_t *nk, const Array<int> &d2n,
Vector &vc, ElementTransformation &Trans, Vector &dofs) const
{
const int sdim = Trans.GetSpaceDim();
const bool square_J = (dim == sdim);
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint(&Nodes.IntPoint(k));
// dof_k = nk^t adj(J) xk
dofs(k) = Trans.AdjugateJacobian().InnerProduct(
&vc[k*sdim], nk + d2n[k]*dim);
if (!square_J) { dofs(k) /= Trans.Weight(); }
}
}
void VectorFiniteElement::ProjectMatrixCoefficient_RT(
const real_t *nk, const Array<int> &d2n,
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
{
// project the rows of the matrix coefficient in an RT space
const int sdim = T.GetSpaceDim();
MFEM_ASSERT(mc.GetWidth() == sdim, "");
const bool square_J = (dim == sdim);
DenseMatrix MQ(mc.GetHeight(), mc.GetWidth());
Vector nk_phys(sdim), dofs_k(MQ.Height());
MFEM_ASSERT(dofs.Size() == dof*MQ.Height(), "");
for (int k = 0; k < dof; k++)
{
T.SetIntPoint(&Nodes.IntPoint(k));
mc.Eval(MQ, T, Nodes.IntPoint(k));
// nk_phys = adj(J)^t nk
T.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, nk_phys);
if (!square_J) { nk_phys /= T.Weight(); }
MQ.Mult(nk_phys, dofs_k);
for (int r = 0; r < MQ.Height(); r++)
{
dofs(k+dof*r) = dofs_k(r);
}
}
}
void VectorFiniteElement::Project_RT(
const real_t *nk, const Array<int> &d2n, const FiniteElement &fe,
ElementTransformation &Trans, DenseMatrix &I) const
{
if (fe.GetRangeType() == SCALAR)
{
real_t vk[Geometry::MaxDim];
Vector shape(fe.GetDof());
int sdim = Trans.GetSpaceDim();
I.SetSize(dof, sdim*fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
fe.CalcShape(ip, shape);
Trans.SetIntPoint(&ip);
// Transform RT face normals from reference to physical space
// vk = adj(J)^T nk
Trans.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, vk);
if (fe.GetMapType() == INTEGRAL)
{
real_t w = 1.0/Trans.Weight();
for (int d = 0; d < dim; d++)
{
vk[d] *= w;
}
}
for (int j = 0; j < shape.Size(); j++)
{
real_t s = shape(j);
if (fabs(s) < 1e-12)
{
s = 0.0;
}
// Project scalar basis function multiplied by each coordinate
// direction onto the transformed face normals
for (int d = 0; d < sdim; d++)
{
I(k,j+d*shape.Size()) = s*vk[d];
}
}
}
}
else
{
int sdim = Trans.GetSpaceDim();
real_t vk[Geometry::MaxDim];
DenseMatrix vshape(fe.GetDof(), sdim);
Vector vshapenk(fe.GetDof());
const bool square_J = (dim == sdim);
I.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
Trans.SetIntPoint(&ip);
// Transform RT face normals from reference to physical space
// vk = adj(J)^T nk
Trans.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, vk);
// Compute fe basis functions in physical space
fe.CalcVShape(Trans, vshape);
// Project fe basis functions onto transformed face normals
vshape.Mult(vk, vshapenk);
if (!square_J) { vshapenk /= Trans.Weight(); }
for (int j=0; j<vshapenk.Size(); j++)
{
I(k,j) = vshapenk(j);
}
}
}
}
void VectorFiniteElement::ProjectGrad_RT(
const real_t *nk, const Array<int> &d2n, const FiniteElement &fe,
ElementTransformation &Trans, DenseMatrix &grad) const
{
if (dim != 2)
{
mfem_error("VectorFiniteElement::ProjectGrad_RT works only in 2D!");
}
DenseMatrix dshape(fe.GetDof(), fe.GetDim());
Vector grad_k(fe.GetDof());
real_t tk[2];
grad.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
fe.CalcDShape(Nodes.IntPoint(k), dshape);
tk[0] = nk[d2n[k]*dim+1];
tk[1] = -nk[d2n[k]*dim];
dshape.Mult(tk, grad_k);
for (int j = 0; j < grad_k.Size(); j++)
{
grad(k,j) = (fabs(grad_k(j)) < 1e-12) ? 0.0 : grad_k(j);
}
}
}
void VectorFiniteElement::ProjectCurl_ND(
const real_t *tk, const Array<int> &d2t, const FiniteElement &fe,
ElementTransformation &Trans, DenseMatrix &curl) const
{
#ifdef MFEM_THREAD_SAFE
DenseMatrix curlshape(fe.GetDof(), dim);
DenseMatrix curlshape_J(fe.GetDof(), dim);
DenseMatrix JtJ(dim, dim);
#else
curlshape.SetSize(fe.GetDof(), dim);
curlshape_J.SetSize(fe.GetDof(), dim);
JtJ.SetSize(dim, dim);
#endif
Vector curl_k(fe.GetDof());
curl.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
// calculate J^t * J / |J|
Trans.SetIntPoint(&ip);
MultAtB(Trans.Jacobian(), Trans.Jacobian(), JtJ);
JtJ *= 1.0 / Trans.Weight();
// transform curl of shapes (rows) by J^t * J / |J|
fe.CalcCurlShape(ip, curlshape);
Mult(curlshape, JtJ, curlshape_J);
curlshape_J.Mult(tk + d2t[k]*dim, curl_k);
for (int j = 0; j < curl_k.Size(); j++)
{
curl(k,j) = (fabs(curl_k(j)) < 1e-12) ? 0.0 : curl_k(j);
}
}
}
void VectorFiniteElement::ProjectCurl_RT(
const real_t *nk, const Array<int> &d2n, const FiniteElement &fe,
ElementTransformation &Trans, DenseMatrix &curl) const
{
DenseMatrix curl_shape(fe.GetDof(), dim);
Vector curl_k(fe.GetDof());
curl.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
fe.CalcCurlShape(Nodes.IntPoint(k), curl_shape);
curl_shape.Mult(nk + d2n[k]*dim, curl_k);
for (int j = 0; j < curl_k.Size(); j++)
{
curl(k,j) = (fabs(curl_k(j)) < 1e-12) ? 0.0 : curl_k(j);
}
}
}
void VectorFiniteElement::Project_ND(
const real_t *tk, const Array<int> &d2t,
VectorCoefficient &vc, ElementTransformation &Trans, Vector &dofs) const
{
real_t vk[Geometry::MaxDim];
Vector xk(vk, vc.GetVDim());
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint(&Nodes.IntPoint(k));
vc.Eval(xk, Trans, Nodes.IntPoint(k));
// dof_k = xk^t J tk
dofs(k) = Trans.Jacobian().InnerProduct(tk + d2t[k]*dim, vk);
}
}
void VectorFiniteElement::Project_ND(
const real_t *tk, const Array<int> &d2t,
Vector &vc, ElementTransformation &Trans, Vector &dofs) const
{
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint(&Nodes.IntPoint(k));
// dof_k = xk^t J tk
dofs(k) = Trans.Jacobian().InnerProduct(tk + d2t[k]*dim, &vc[k*dim]);
}
}
void VectorFiniteElement::ProjectMatrixCoefficient_ND(
const real_t *tk, const Array<int> &d2t,
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
{
// project the rows of the matrix coefficient in an ND space
const int sdim = T.GetSpaceDim();
MFEM_ASSERT(mc.GetWidth() == sdim, "");
DenseMatrix MQ(mc.GetHeight(), mc.GetWidth());
Vector tk_phys(sdim), dofs_k(MQ.Height());
MFEM_ASSERT(dofs.Size() == dof*MQ.Height(), "");
for (int k = 0; k < dof; k++)
{
T.SetIntPoint(&Nodes.IntPoint(k));
mc.Eval(MQ, T, Nodes.IntPoint(k));
// tk_phys = J tk
T.Jacobian().Mult(tk + d2t[k]*dim, tk_phys);
MQ.Mult(tk_phys, dofs_k);
for (int r = 0; r < MQ.Height(); r++)
{
dofs(k+dof*r) = dofs_k(r);
}
}
}
void VectorFiniteElement::Project_ND(
const real_t *tk, const Array<int> &d2t, const FiniteElement &fe,
ElementTransformation &Trans, DenseMatrix &I) const
{
if (fe.GetRangeType() == SCALAR)
{
int sdim = Trans.GetSpaceDim();
real_t vk[Geometry::MaxDim];
Vector shape(fe.GetDof());
I.SetSize(dof, sdim*fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
fe.CalcShape(ip, shape);
Trans.SetIntPoint(&ip);
// Transform ND edge tengents from reference to physical space
// vk = J tk
Trans.Jacobian().Mult(tk + d2t[k]*dim, vk);
if (fe.GetMapType() == INTEGRAL)
{
real_t w = 1.0/Trans.Weight();
for (int d = 0; d < sdim; d++)
{
vk[d] *= w;
}
}
for (int j = 0; j < shape.Size(); j++)
{
real_t s = shape(j);
if (fabs(s) < 1e-12)
{
s = 0.0;
}
// Project scalar basis function multiplied by each coordinate
// direction onto the transformed edge tangents
for (int d = 0; d < sdim; d++)
{
I(k, j + d*shape.Size()) = s*vk[d];
}
}
}
}
else
{
int sdim = Trans.GetSpaceDim();
real_t vk[Geometry::MaxDim];
DenseMatrix vshape(fe.GetDof(), sdim);
Vector vshapetk(fe.GetDof());
I.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
Trans.SetIntPoint(&ip);
// Transform ND edge tangents from reference to physical space
// vk = J tk
Trans.Jacobian().Mult(tk + d2t[k]*dim, vk);
// Compute fe basis functions in physical space
fe.CalcVShape(Trans, vshape);
// Project fe basis functions onto transformed edge tangents
vshape.Mult(vk, vshapetk);
for (int j=0; j<vshapetk.Size(); j++)
{
I(k, j) = vshapetk(j);
}
}
}
}
void VectorFiniteElement::ProjectGrad_ND(
const real_t *tk, const Array<int> &d2t, const FiniteElement &fe,
ElementTransformation &Trans, DenseMatrix &grad) const
{
MFEM_ASSERT(fe.GetMapType() == VALUE, "");
DenseMatrix dshape(fe.GetDof(), fe.GetDim());
Vector grad_k(fe.GetDof());
grad.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
fe.CalcDShape(Nodes.IntPoint(k), dshape);
dshape.Mult(tk + d2t[k]*dim, grad_k);
for (int j = 0; j < grad_k.Size(); j++)
{
grad(k,j) = (fabs(grad_k(j)) < 1e-12) ? 0.0 : grad_k(j);
}
}
}
void VectorFiniteElement::LocalL2Projection_RT(
const VectorFiniteElement &cfe, ElementTransformation &Trans,
DenseMatrix &I) const
{
Vector v(dim);
IntegrationPoint tr_ip;
const int fs = dof, cs = cfe.GetDof();
I.SetSize(fs, cs);
DenseMatrix fine_shape(fs, dim), coarse_shape(cs, cfe.GetDim());
DenseMatrix fine_mass(fs), fine_coarse_mass(fs, cs); // initialized with 0
const int ir_order =
std::max(GetOrder(), this->GetOrder()) + this->GetOrder();
const IntegrationRule &ir = IntRules.Get(this->GetGeomType(), ir_order);
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
const DenseMatrix &adjJ = Trans.AdjugateJacobian();
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
real_t w = ip.weight;
this->CalcVShape(ip, fine_shape);
Trans.Transform(ip, v);
tr_ip.Set(v.GetData(), dim);
cfe.CalcVShape(tr_ip, coarse_shape);
AddMult_a_AAt(w, fine_shape, fine_mass);
for (int k=0; k<fs; ++k)
{
for (int j=0; j<cs; ++j)
{
real_t Mkj = 0.0;
for (int d1=0; d1<dim; ++d1)
{
for (int d2=0; d2<dim; ++d2)
{
Mkj += w*fine_shape(k,d1)*adjJ(d2,d1)*coarse_shape(j,d2);
}
}
fine_coarse_mass(k,j) += (fabs(Mkj) < 1e-12) ? 0.0 : Mkj;
}
}
}
DenseMatrixInverse fine_mass_inv(fine_mass);
fine_mass_inv.Mult(fine_coarse_mass, I);
}
void VectorFiniteElement::LocalInterpolation_RT(
const VectorFiniteElement &cfe, const real_t *nk, const Array<int> &d2n,
ElementTransformation &Trans, DenseMatrix &I) const
{
MFEM_ASSERT(map_type == cfe.GetMapType(), "");
if (!is_nodal) { return LocalL2Projection_RT(cfe, Trans, I); }
real_t vk[Geometry::MaxDim];
Vector xk(vk, dim);
IntegrationPoint ip;
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(cfe.GetDof(), cfe.GetDim());
#else
DenseMatrix vshape(cfe.vshape.Data(), cfe.GetDof(), cfe.GetDim());
#endif
I.SetSize(dof, vshape.Height());
// assuming Trans is linear; this should be ok for all refinement types
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
const DenseMatrix &adjJ = Trans.AdjugateJacobian();
for (int k = 0; k < dof; k++)
{
Trans.Transform(Nodes.IntPoint(k), xk);
ip.Set3(vk);
cfe.CalcVShape(ip, vshape);
// xk = |J| J^{-t} n_k
adjJ.MultTranspose(nk + d2n[k]*dim, vk);
// I_k = vshape_k.adj(J)^t.n_k, k=1,...,dof
for (int j = 0; j < vshape.Height(); j++)
{
real_t Ikj = 0.;
for (int i = 0; i < dim; i++)
{
Ikj += vshape(j, i) * vk[i];
}
I(k, j) = (fabs(Ikj) < 1e-12) ? 0.0 : Ikj;
}
}
}
void VectorFiniteElement::LocalL2Projection_ND(
const VectorFiniteElement &cfe,
ElementTransformation &Trans, DenseMatrix &I) const
{
Vector v(dim);
IntegrationPoint tr_ip;
const int fs = dof, cs = cfe.GetDof();
I.SetSize(fs, cs);
DenseMatrix fine_shape(fs, dim), coarse_shape(cs, cfe.GetDim());
DenseMatrix fine_mass(fs), fine_coarse_mass(fs, cs); // initialized with 0
const int ir_order =
std::max(GetOrder(), this->GetOrder()) + this->GetOrder();
const IntegrationRule &ir = IntRules.Get(this->GetGeomType(), ir_order);
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
const DenseMatrix &J = Trans.Jacobian();
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
this->CalcVShape(ip, fine_shape);
Trans.Transform(ip, v);
tr_ip.Set(v.GetData(), dim);
cfe.CalcVShape(tr_ip, coarse_shape);
AddMult_a_AAt(ip.weight, fine_shape, fine_mass);
for (int k=0; k<fs; ++k)
{
for (int j=0; j<cs; ++j)
{
real_t Mkj = 0.0;
for (int d1=0; d1<dim; ++d1)
{
for (int d2=0; d2<dim; ++d2)
{
Mkj += ip.weight*fine_shape(k,d1)*J(d1,d2)*coarse_shape(j,d2);
}
}
fine_coarse_mass(k,j) += (fabs(Mkj) < 1e-12) ? 0.0 : Mkj;
}
}
}
DenseMatrixInverse fine_mass_inv(fine_mass);
fine_mass_inv.Mult(fine_coarse_mass, I);
}
void VectorFiniteElement::LocalInterpolation_ND(
const VectorFiniteElement &cfe, const real_t *tk, const Array<int> &d2t,
ElementTransformation &Trans, DenseMatrix &I) const
{
if (!is_nodal) { return LocalL2Projection_ND(cfe, Trans, I); }
real_t vk[Geometry::MaxDim];
Vector xk(vk, dim);
IntegrationPoint ip;
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(cfe.GetDof(), cfe.GetDim());
#else
DenseMatrix vshape(cfe.vshape.Data(), cfe.GetDof(), cfe.GetDim());
#endif
I.SetSize(dof, vshape.Height());
// assuming Trans is linear; this should be ok for all refinement types
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
const DenseMatrix &J = Trans.Jacobian();
for (int k = 0; k < dof; k++)
{
Trans.Transform(Nodes.IntPoint(k), xk);
ip.Set3(vk);
cfe.CalcVShape(ip, vshape);
// xk = J t_k
J.Mult(tk + d2t[k]*dim, vk);
// I_k = vshape_k.J.t_k, k=1,...,Dof
for (int j = 0; j < vshape.Height(); j++)
{
real_t Ikj = 0.;
for (int i = 0; i < dim; i++)
{
Ikj += vshape(j, i) * vk[i];
}
I(k, j) = (fabs(Ikj) < 1e-12) ? 0.0 : Ikj;
}
}
}
void VectorFiniteElement::LocalRestriction_RT(
const real_t *nk, const Array<int> &d2n, ElementTransformation &Trans,
DenseMatrix &R) const
{
real_t pt_data[Geometry::MaxDim];
IntegrationPoint ip;
Vector pt(pt_data, dim);
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
const DenseMatrix &J = Trans.Jacobian();
const real_t weight = Trans.Weight();
for (int j = 0; j < dof; j++)
{
InvertLinearTrans(Trans, Nodes.IntPoint(j), pt);
ip.Set(pt_data, dim);
if (Geometries.CheckPoint(geom_type, ip)) // do we need an epsilon here?
{
CalcVShape(ip, vshape);
J.MultTranspose(nk+dim*d2n[j], pt_data);
pt /= weight;
for (int k = 0; k < dof; k++)
{
real_t R_jk = 0.0;
for (int d = 0; d < dim; d++)
{
R_jk += vshape(k,d)*pt_data[d];
}
R(j,k) = R_jk;
}
}
else
{
// Set the whole row to avoid valgrind warnings in R.Threshold().
R.SetRow(j, infinity());
}
}
R.Threshold(1e-12);
}
void VectorFiniteElement::LocalRestriction_ND(
const real_t *tk, const Array<int> &d2t, ElementTransformation &Trans,
DenseMatrix &R) const
{
real_t pt_data[Geometry::MaxDim];
IntegrationPoint ip;
Vector pt(pt_data, dim);
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
const DenseMatrix &Jinv = Trans.InverseJacobian();
for (int j = 0; j < dof; j++)
{
InvertLinearTrans(Trans, Nodes.IntPoint(j), pt);
ip.Set(pt_data, dim);
if (Geometries.CheckPoint(geom_type, ip)) // do we need an epsilon here?
{
CalcVShape(ip, vshape);
Jinv.Mult(tk+dim*d2t[j], pt_data);
for (int k = 0; k < dof; k++)
{
real_t R_jk = 0.0;
for (int d = 0; d < dim; d++)
{
R_jk += vshape(k,d)*pt_data[d];
}
R(j,k) = R_jk;
}
}
else
{
// Set the whole row to avoid valgrind warnings in R.Threshold().
R.SetRow(j, infinity());
}
}
R.Threshold(1e-12);
}
Poly_1D::Basis::Basis(const int p, const real_t *nodes, EvalType etype)
: etype(etype), auxiliary_basis(NULL), scale_integrated(false)
{
switch (etype)
{
case ChangeOfBasis:
{
x.SetSize(p + 1);
w.SetSize(p + 1);
DenseMatrix A(p + 1);
for (int i = 0; i <= p; i++)
{
CalcBasis(p, nodes[i], A.GetColumn(i));
}
Ai.Factor(A);
// mfem::out << "Poly_1D::Basis(" << p << ",...) : "; Ai.TestInversion();
break;
}
case Barycentric:
{
x.SetSize(p + 1);
w.SetSize(p + 1);
x = nodes;
w = 1.0;
for (int i = 0; i <= p; i++)
{
for (int j = 0; j < i; j++)
{
real_t xij = x(i) - x(j);
w(i) *= xij;
w(j) *= -xij;
}
}
for (int i = 0; i <= p; i++)
{
w(i) = 1.0/w(i);
}
#ifdef MFEM_DEBUG
// Make sure the nodes are increasing
for (int i = 0; i < p; i++)
{
if (x(i) >= x(i+1))
{
mfem_error("Poly_1D::Basis::Basis : nodes are not increasing!");
}
}
#endif
break;
}
case Positive:
x.SetDataAndSize(NULL, p + 1); // use x to store (p + 1)
break;
case Integrated:
auxiliary_basis = new Basis(
p+1, poly1d.GetPoints(p+1, BasisType::GaussLobatto), Barycentric);
u_aux.SetSize(p+2);
d_aux.SetSize(p+2);
d2_aux.SetSize(p+2);
break;
default: break;
}
}
void Poly_1D::Basis::Eval(const real_t y, Vector &u) const
{
switch (etype)
{
case ChangeOfBasis:
{
CalcBasis(Ai.Width() - 1, y, x);
Ai.Mult(x, u);
break;
}
case Barycentric:
{
int i, k, p = x.Size() - 1;
real_t l, lk;
if (p == 0)
{
u(0) = 1.0;
return;
}
lk = 1.0;
for (k = 0; k < p; k++)
{
if (y >= (x(k) + x(k+1))/2)
{
lk *= y - x(k);
}
else
{
for (i = k+1; i <= p; i++)
{
lk *= y - x(i);
}
break;
}
}
l = lk * (y - x(k));
for (i = 0; i < k; i++)
{
u(i) = l * w(i) / (y - x(i));
}
u(k) = lk * w(k);
for (i++; i <= p; i++)
{
u(i) = l * w(i) / (y - x(i));
}
break;
}
case Positive:
CalcBernstein(x.Size() - 1, y, u);
break;
case Integrated:
auxiliary_basis->Eval(y, u_aux, d_aux);
EvalIntegrated(d_aux, u);
break;
default: break;
}
}
void Poly_1D::Basis::Eval(const real_t y, Vector &u, Vector &d) const
{
switch (etype)
{
case ChangeOfBasis:
{
CalcBasis(Ai.Width() - 1, y, x, w);
Ai.Mult(x, u);
Ai.Mult(w, d);
break;
}
case Barycentric:
{
int i, k, p = x.Size() - 1;
real_t l, lp, lk, sk, si;
if (p == 0)
{
u(0) = 1.0;
d(0) = 0.0;
return;
}
lk = 1.0;
for (k = 0; k < p; k++)
{
if (y >= (x(k) + x(k+1))/2)
{
lk *= y - x(k);
}
else
{
for (i = k+1; i <= p; i++)
{
lk *= y - x(i);
}
break;
}
}
l = lk * (y - x(k));
sk = 0.0;
for (i = 0; i < k; i++)
{
si = 1.0/(y - x(i));
sk += si;
u(i) = l * si * w(i);
}
u(k) = lk * w(k);
for (i++; i <= p; i++)
{
si = 1.0/(y - x(i));
sk += si;
u(i) = l * si * w(i);
}
lp = l * sk + lk;
for (i = 0; i < k; i++)
{
d(i) = (lp * w(i) - u(i))/(y - x(i));
}
d(k) = sk * u(k);
for (i++; i <= p; i++)
{
d(i) = (lp * w(i) - u(i))/(y - x(i));
}
break;
}
case Positive:
CalcBernstein(x.Size() - 1, y, u, d);
break;
case Integrated:
auxiliary_basis->Eval(y, u_aux, d_aux, d2_aux);
EvalIntegrated(d_aux,u);
EvalIntegrated(d2_aux,d);
break;
default: break;
}
}
void Poly_1D::Basis::Eval(const real_t y, Vector &u, Vector &d,
Vector &d2) const
{
MFEM_VERIFY(etype == Barycentric,
"Basis::Eval with second order derivatives not implemented for"
" etype = " << etype);
switch (etype)
{
case ChangeOfBasis:
{
CalcBasis(Ai.Width() - 1, y, x, w);
Ai.Mult(x, u);
Ai.Mult(w, d);
// set d2 (not implemented yet)
break;
}
case Barycentric:
{
int i, k, p = x.Size() - 1;
real_t l, lp, lp2, lk, sk, si, sk2;
if (p == 0)
{
u(0) = 1.0;
d(0) = 0.0;
d2(0) = 0.0;
return;
}
lk = 1.0;
for (k = 0; k < p; k++)
{
if (y >= (x(k) + x(k+1))/2)
{
lk *= y - x(k);
}
else
{
for (i = k+1; i <= p; i++)
{
lk *= y - x(i);
}
break;
}
}
l = lk * (y - x(k));
sk = 0.0;
sk2 = 0.0;
for (i = 0; i < k; i++)
{
si = 1.0/(y - x(i));
sk += si;
sk2 -= si * si;
u(i) = l * si * w(i);
}
u(k) = lk * w(k);
for (i++; i <= p; i++)
{
si = 1.0/(y - x(i));
sk += si;
sk2 -= si * si;
u(i) = l * si * w(i);
}
lp = l * sk + lk;
lp2 = lp * sk + l * sk2 + sk * lk;
for (i = 0; i < k; i++)
{
d(i) = (lp * w(i) - u(i))/(y - x(i));
d2(i) = (lp2 * w(i) - 2 * d(i))/(y - x(i));
}
d(k) = sk * u(k);
d2(k) = sk2 * u(k) + sk * d(k);
for (i++; i <= p; i++)
{
d(i) = (lp * w(i) - u(i))/(y - x(i));
d2(i) = (lp2 * w(i) - 2 * d(i))/(y - x(i));
}
break;
}
case Positive:
CalcBernstein(x.Size() - 1, y, u, d);
break;
case Integrated:
MFEM_ABORT("Integrated basis must be evaluated with EvalIntegrated");
break;
default: break;
}
}
void Poly_1D::Basis::EvalIntegrated(const Vector &d_aux_, Vector &u) const
{
MFEM_VERIFY(etype == Integrated,
"EvalIntegrated is only valid for Integrated basis type");
int p = d_aux_.Size() - 1;
// See Gerritsma, M. (2010). "Edge functions for spectral element methods",
// in Lecture Notes in Computational Science and Engineering, 199--207.
u[0] = -d_aux_[0];
for (int j=1; j<p; ++j)
{
u[j] = u[j-1] - d_aux_[j];
}
// If scale_integrated is true, the degrees of freedom represent mean values,
// otherwise they represent subcell integrals. Generally, scale_integrated
// should be true for MapType::VALUE, and false for other map types.
if (scale_integrated)
{
Vector &aux_nodes = auxiliary_basis->x;
for (int j=0; j<aux_nodes.Size()-1; ++j)
{
u[j] *= aux_nodes[j+1] - aux_nodes[j];
}
}
}
void Poly_1D::Basis::ScaleIntegrated(bool scale_integrated_)
{
scale_integrated = scale_integrated_;
}
Poly_1D::Basis::~Basis()
{
delete auxiliary_basis;
}
const int *Poly_1D::Binom(const int p)
{
if (binom.NumCols() <= p)
{
binom.SetSize(p + 1, p + 1);
for (int i = 0; i <= p; i++)
{
binom(i,0) = binom(i,i) = 1;
for (int j = 1; j < i; j++)
{
binom(i,j) = binom(i-1,j) + binom(i-1,j-1);
}
}
}
return binom[p];
}
void Poly_1D::ChebyshevPoints(const int p, real_t *x)
{
for (int i = 0; i <= p; i++)
{
// x[i] = 0.5*(1. + cos(M_PI*(p - i + 0.5)/(p + 1)));
real_t s = sin(M_PI_2*(i + 0.5)/(p + 1));
x[i] = s*s;
}
}
void Poly_1D::CalcMono(const int p, const real_t x, real_t *u)
{
real_t xn;
u[0] = xn = 1.;
for (int n = 1; n <= p; n++)
{
u[n] = (xn *= x);
}
}
void Poly_1D::CalcMono(const int p, const real_t x, real_t *u, real_t *d)
{
real_t xn;
u[0] = xn = 1.;
d[0] = 0.;
for (int n = 1; n <= p; n++)
{
d[n] = n * xn;
u[n] = (xn *= x);
}
}
void Poly_1D::CalcBinomTerms(const int p, const real_t x, const real_t y,
real_t *u)
{
if (p == 0)
{
u[0] = 1.;
}
else
{
int i;
const int *b = Binom(p);
real_t z = x;
for (i = 1; i < p; i++)
{
u[i] = b[i]*z;
z *= x;
}
u[p] = z;
z = y;
for (i--; i > 0; i--)
{
u[i] *= z;
z *= y;
}
u[0] = z;
}
}
void Poly_1D::CalcBinomTerms(const int p, const real_t x, const real_t y,
real_t *u, real_t *d)
{
if (p == 0)
{
u[0] = 1.;
d[0] = 0.;
}
else
{
int i;
const int *b = Binom(p);
const real_t xpy = x + y, ptx = p*x;
real_t z = 1.;
for (i = 1; i < p; i++)
{
d[i] = b[i]*z*(i*xpy - ptx);
z *= x;
u[i] = b[i]*z;
}
d[p] = p*z;
u[p] = z*x;
z = 1.;
for (i--; i > 0; i--)
{
d[i] *= z;
z *= y;
u[i] *= z;
}
d[0] = -p*z;
u[0] = z*y;
}
}
void Poly_1D::CalcDBinomTerms(const int p, const real_t x, const real_t y,
real_t *d)
{
if (p == 0)
{
d[0] = 0.;
}
else
{
int i;
const int *b = Binom(p);
const real_t xpy = x + y, ptx = p*x;
real_t z = 1.;
for (i = 1; i < p; i++)
{
d[i] = b[i]*z*(i*xpy - ptx);
z *= x;
}
d[p] = p*z;
z = 1.;
for (i--; i > 0; i--)
{
d[i] *= z;
z *= y;
}
d[0] = -p*z;
}
}
void Poly_1D::CalcDxBinomTerms(const int p, const real_t x, const real_t y,
real_t *u)
{
if (p == 0)
{
u[0] = 0.;
}
else
{
int i;
const int *b = Binom(p);
real_t z = 1.;
for (i = 1; i < p; i++)
{
u[i] = i * b[i]*z;
z *= x;
}
u[p] = i * z;
z = y;
for (i--; i > 0; i--)
{
u[i] *= z;
z *= y;
}
u[0] = 0;
}
}
void Poly_1D::CalcDyBinomTerms(const int p, const real_t x, const real_t y,
real_t *u)
{
if (p == 0)
{
u[0] = 0.;
}
else
{
int i;
const int *b = Binom(p);
real_t z = x;
for (i = 1; i < p; i++)
{
u[i] = b[i]*z;
z *= x;
}
u[p] = 0.;
z = 1.;
for (i--; i > 0; i--)
{
u[i] *= (p - i) * z;
z *= y;
}
u[0] = p * z;
}
}
void Poly_1D::CalcLegendre(const int p, const real_t x, real_t *u)
{
// use the recursive definition for [-1,1]:
// (n+1)*P_{n+1}(z) = (2*n+1)*z*P_n(z)-n*P_{n-1}(z)
real_t z;
u[0] = 1.;
if (p == 0) { return; }
u[1] = z = 2.*x - 1.;
for (int n = 1; n < p; n++)
{
u[n+1] = ((2*n + 1)*z*u[n] - n*u[n-1])/(n + 1);
}
}
void Poly_1D::CalcLegendre(const int p, const real_t x, real_t *u, real_t *d)
{
// use the recursive definition for [-1,1]:
// (n+1)*P_{n+1}(z) = (2*n+1)*z*P_n(z)-n*P_{n-1}(z)
// for the derivative use, z in [-1,1]:
// P'_{n+1}(z) = (2*n+1)*P_n(z)+P'_{n-1}(z)
real_t z;
u[0] = 1.;
d[0] = 0.;
if (p == 0) { return; }
u[1] = z = 2.*x - 1.;
d[1] = 2.;
for (int n = 1; n < p; n++)
{
u[n+1] = ((2*n + 1)*z*u[n] - n*u[n-1])/(n + 1);
d[n+1] = (4*n + 2)*u[n] + d[n-1];
}
}
void Poly_1D::CalcChebyshev(const int p, const real_t x, real_t *u)
{
// recursive definition, z in [-1,1]
// T_0(z) = 1, T_1(z) = z
// T_{n+1}(z) = 2*z*T_n(z) - T_{n-1}(z)
real_t z;
u[0] = 1.;
if (p == 0) { return; }
u[1] = z = 2.*x - 1.;
for (int n = 1; n < p; n++)
{
u[n+1] = 2*z*u[n] - u[n-1];
}
}
void Poly_1D::CalcChebyshev(const int p, const real_t x, real_t *u, real_t *d)
{
// recursive definition, z in [-1,1]
// T_0(z) = 1, T_1(z) = z
// T_{n+1}(z) = 2*z*T_n(z) - T_{n-1}(z)
// T'_n(z) = n*U_{n-1}(z)
// U_0(z) = 1 U_1(z) = 2*z
// U_{n+1}(z) = 2*z*U_n(z) - U_{n-1}(z)
// U_n(z) = z*U_{n-1}(z) + T_n(z) = z*T'_n(z)/n + T_n(z)
// T'_{n+1}(z) = (n + 1)*(z*T'_n(z)/n + T_n(z))
real_t z;
u[0] = 1.;
d[0] = 0.;
if (p == 0) { return; }
u[1] = z = 2.*x - 1.;
d[1] = 2.;
for (int n = 1; n < p; n++)
{
u[n+1] = 2*z*u[n] - u[n-1];
d[n+1] = (n + 1)*(z*d[n]/n + 2*u[n]);
}
}
void Poly_1D::CalcChebyshev(const int p, const real_t x, real_t *u, real_t *d,
real_t *dd)
{
// recursive definition, z in [-1,1]
// T_0(z) = 1, T_1(z) = z
// T_{n+1}(z) = 2*z*T_n(z) - T_{n-1}(z)
// T'_n(z) = n*U_{n-1}(z)
// U_0(z) = 1 U_1(z) = 2*z
// U_{n+1}(z) = 2*z*U_n(z) - U_{n-1}(z)
// U_n(z) = z*U_{n-1}(z) + T_n(z) = z*T'_n(z)/n + T_n(z)
// T'_{n+1}(z) = (n + 1)*(z*T'_n(z)/n + T_n(z))
// T''_{n+1}(z) = (n + 1)*(2*(n + 1)*T'_n(z) + z*T''_n(z)) / n
real_t z;
u[0] = 1.;
d[0] = 0.;
dd[0]= 0.;
if (p == 0) { return; }
u[1] = z = 2.*x - 1.;
d[1] = 2.;
dd[1] = 0;
for (int n = 1; n < p; n++)
{
u[n+1] = 2*z*u[n] - u[n-1];
d[n+1] = (n + 1)*(z*d[n]/n + 2*u[n]);
dd[n+1] = (n + 1)*(2.*(n + 1)*d[n] + z*dd[n])/n;
}
}
const Array<real_t>* Poly_1D::GetPointsArray(const int p, const int btype)
{
Array<real_t> *val;
BasisType::Check(btype);
const int qtype = BasisType::GetQuadrature1D(btype);
if (qtype == Quadrature1D::Invalid) { return nullptr; }
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (Poly1DGetPoints)
#endif
{
std::pair<int, int> key(btype, p);
auto it = points_container.find(key);
if (it == points_container.end())
{
it = points_container.emplace(key, new Array<real_t>(p + 1, h_mt)).first;
val = it->second.get();
real_t* hptr = val->HostWrite();
quad_func.GivePolyPoints(p + 1, hptr, qtype);
}
else
{
val = it->second.get();
}
}
return val;
}
Poly_1D::Basis &Poly_1D::GetBasis(const int p, const int btype)
{
BasisType::Check(btype);
Basis* val;
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (Poly1DGetBasis)
#endif
{
std::pair<int, int> key(btype, p);
auto it = bases_container.find(key);
if (it == bases_container.end())
{
EvalType etype;
if (btype == BasisType::Positive) { etype = Positive; }
else if (btype == BasisType::IntegratedGLL) { etype = Integrated; }
else { etype = Barycentric; }
it = bases_container
.emplace(key, new Basis(p, GetPoints(p, btype), etype))
.first;
}
val = it->second.get();
}
return *val;
}
TensorBasisElement::TensorBasisElement(const int dims, const int p,
const int btype, const DofMapType dmtype)
: b_type(btype),
basis1d(poly1d.GetBasis(p, b_type))
{
if (dmtype == H1_DOF_MAP || dmtype == Sr_DOF_MAP)
{
switch (dims)
{
case 1:
{
dof_map.SetSize(p + 1);
dof_map[0] = 0;
dof_map[p] = 1;
for (int i = 1; i < p; i++)
{
dof_map[i] = i+1;
}
break;
}
case 2:
{
const int p1 = p + 1;
dof_map.SetSize(p1*p1);
// vertices
dof_map[0 + 0*p1] = 0;
dof_map[p + 0*p1] = 1;
dof_map[p + p*p1] = 2;
dof_map[0 + p*p1] = 3;
// edges
int o = 4;
for (int i = 1; i < p; i++)
{
dof_map[i + 0*p1] = o++;
}
for (int i = 1; i < p; i++)
{
dof_map[p + i*p1] = o++;
}
for (int i = 1; i < p; i++)
{
dof_map[(p-i) + p*p1] = o++;
}
for (int i = 1; i < p; i++)
{
dof_map[0 + (p-i)*p1] = o++;
}
// interior
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
dof_map[i + j*p1] = o++;
}
}
break;
}
case 3:
{
const int p1 = p + 1;
dof_map.SetSize(p1*p1*p1);
// vertices
dof_map[0 + (0 + 0*p1)*p1] = 0;
dof_map[p + (0 + 0*p1)*p1] = 1;
dof_map[p + (p + 0*p1)*p1] = 2;
dof_map[0 + (p + 0*p1)*p1] = 3;
dof_map[0 + (0 + p*p1)*p1] = 4;
dof_map[p + (0 + p*p1)*p1] = 5;
dof_map[p + (p + p*p1)*p1] = 6;
dof_map[0 + (p + p*p1)*p1] = 7;
// edges (see Hexahedron::edges in mesh/hexahedron.cpp).
// edges (see Constants<Geometry::CUBE>::Edges in fem/geom.cpp).
int o = 8;
for (int i = 1; i < p; i++)
{
dof_map[i + (0 + 0*p1)*p1] = o++; // (0,1)
}
for (int i = 1; i < p; i++)
{
dof_map[p + (i + 0*p1)*p1] = o++; // (1,2)
}
for (int i = 1; i < p; i++)
{
dof_map[i + (p + 0*p1)*p1] = o++; // (3,2)
}
for (int i = 1; i < p; i++)
{
dof_map[0 + (i + 0*p1)*p1] = o++; // (0,3)
}
for (int i = 1; i < p; i++)
{
dof_map[i + (0 + p*p1)*p1] = o++; // (4,5)
}
for (int i = 1; i < p; i++)
{
dof_map[p + (i + p*p1)*p1] = o++; // (5,6)
}
for (int i = 1; i < p; i++)
{
dof_map[i + (p + p*p1)*p1] = o++; // (7,6)
}
for (int i = 1; i < p; i++)
{
dof_map[0 + (i + p*p1)*p1] = o++; // (4,7)
}
for (int i = 1; i < p; i++)
{
dof_map[0 + (0 + i*p1)*p1] = o++; // (0,4)
}
for (int i = 1; i < p; i++)
{
dof_map[p + (0 + i*p1)*p1] = o++; // (1,5)
}
for (int i = 1; i < p; i++)
{
dof_map[p + (p + i*p1)*p1] = o++; // (2,6)
}
for (int i = 1; i < p; i++)
{
dof_map[0 + (p + i*p1)*p1] = o++; // (3,7)
}
// faces (see Mesh::GenerateFaces in mesh/mesh.cpp)
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
dof_map[i + ((p-j) + 0*p1)*p1] = o++; // (3,2,1,0)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
dof_map[i + (0 + j*p1)*p1] = o++; // (0,1,5,4)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
dof_map[p + (i + j*p1)*p1] = o++; // (1,2,6,5)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
dof_map[(p-i) + (p + j*p1)*p1] = o++; // (2,3,7,6)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
dof_map[0 + ((p-i) + j*p1)*p1] = o++; // (3,0,4,7)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
dof_map[i + (j + p*p1)*p1] = o++; // (4,5,6,7)
}
}
// interior
for (int k = 1; k < p; k++)
{
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
dof_map[i + (j + k*p1)*p1] = o++;
}
}
}
break;
}
default:
MFEM_ABORT("invalid dimension: " << dims);
break;
}
}
else if (dmtype == L2_DOF_MAP)
{
// leave dof_map empty, indicating that the dofs are ordered
// lexicographically, i.e. the dof_map is identity
}
else
{
MFEM_ABORT("invalid DofMapType: " << dmtype);
}
}
const DofToQuad &TensorBasisElement::GetTensorDofToQuad(
const FiniteElement &fe, const IntegrationRule &ir,
DofToQuad::Mode mode, const Poly_1D::Basis &basis, bool closed,
Array<DofToQuad*> &dof2quad_array)
{
DofToQuad *d2q = nullptr;
MFEM_VERIFY(mode == DofToQuad::TENSOR, "invalid mode requested");
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
if (!d2q)
{
d2q = new DofToQuad;
const int ndof = closed ? fe.GetOrder() + 1 : fe.GetOrder();
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/fe.GetDim()) + 0.5);
d2q->FE = &fe;
d2q->IntRule = &ir;
d2q->mode = mode;
d2q->ndof = ndof;
d2q->nqpt = nqpt;
d2q->B.SetSize(nqpt*ndof);
d2q->Bt.SetSize(ndof*nqpt);
d2q->G.SetSize(nqpt*ndof);
d2q->Gt.SetSize(ndof*nqpt);
Vector val(ndof), grad(ndof);
for (int i = 0; i < nqpt; i++)
{
// The first 'nqpt' points in 'ir' have the same x-coordinates as those
// of the 1D rule.
basis.Eval(ir.IntPoint(i).x, val, grad);
for (int j = 0; j < ndof; j++)
{
d2q->B[i+nqpt*j] = d2q->Bt[j+ndof*i] = val(j);
d2q->G[i+nqpt*j] = d2q->Gt[j+ndof*i] = grad(j);
}
}
dof2quad_array.Append(d2q);
}
}
return *d2q;
}
NodalTensorFiniteElement::NodalTensorFiniteElement(const int dims,
const int p,
const int btype,
const DofMapType dmtype)
: NodalFiniteElement(dims, GetTensorProductGeometry(dims), Pow(p + 1, dims),
p, dims > 1 ? FunctionSpace::Qk : FunctionSpace::Pk),
TensorBasisElement(dims, p, btype, dmtype)
{
lex_ordering = dof_map;
}
void NodalTensorFiniteElement::SetMapType(const int map_type)
{
ScalarFiniteElement::SetMapType(map_type);
// If we are using the "integrated" basis, the basis functions should be
// scaled for MapType::VALUE, and not scaled for MapType::INTEGRAL. This
// ensures spectral equivalence of the mass matrix with its low-order-refined
// counterpart (cf. LORDiscretization)
if (basis1d.IsIntegratedType())
{
basis1d.ScaleIntegrated(map_type == VALUE);
}
}
const DofToQuad &NodalTensorFiniteElement::GetDofToQuad(
const IntegrationRule &ir,
DofToQuad::Mode mode) const
{
if (mode != DofToQuad::TENSOR)
{
return NodalFiniteElement::GetDofToQuad(ir, mode);
}
else
{
return GetTensorDofToQuad(*this, ir, mode, basis1d, true, dof2quad_array);
}
}
void NodalTensorFiniteElement::GetFaceMap(const int face_id,
Array<int> &face_map) const
{
internal::GetTensorFaceMap(dim, order, face_id, face_map);
}
VectorTensorFiniteElement::VectorTensorFiniteElement(const int dims,
const int d,
const int p,
const int cbtype,
const int obtype,
const int M,
const DofMapType dmtype)
: VectorFiniteElement(dims, GetTensorProductGeometry(dims), d,
p, M, FunctionSpace::Qk),
TensorBasisElement(dims, p, VerifyNodal(VerifyClosed(cbtype)), dmtype),
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(obtype)))
{
MFEM_VERIFY(dims > 1, "Constructor for VectorTensorFiniteElement with both "
"open and closed bases is not valid for 1D elements.");
}
VectorTensorFiniteElement::VectorTensorFiniteElement(const int dims,
const int d,
const int p,
const int obtype,
const int M,
const DofMapType dmtype)
: VectorFiniteElement(dims, GetTensorProductGeometry(dims), d,
p, M, FunctionSpace::Pk),
TensorBasisElement(dims, p, VerifyOpen(obtype), dmtype),
obasis1d(poly1d.GetBasis(p, VerifyOpen(obtype)))
{
MFEM_VERIFY(dims == 1, "Constructor for VectorTensorFiniteElement without "
"closed basis is only valid for 1D elements.");
}
VectorTensorFiniteElement::~VectorTensorFiniteElement()
{
for (int i = 0; i < dof2quad_array_open.Size(); i++)
{
delete dof2quad_array_open[i];
}
}
}