Files
mfem/fem/intrules_cut.cpp
T

1821 lines
50 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.
// Implementation of Surface and Cutcell IntegrationRule(s) classes
#include "fem.hpp"
#include <cmath>
using namespace std;
namespace mfem
{
void CutIntegrationRules::SetOrder(int order)
{
MFEM_VERIFY(order > 0, "Invalid input");
Order = order;
}
void CutIntegrationRules::SetLevelSetProjectionOrder(int order)
{
MFEM_VERIFY(order > 0, "Invalid input");
lsOrder = order;
}
#ifdef MFEM_USE_ALGOIM
void AlgoimIntegrationRules::GetSurfaceIntegrationRule(ElementTransformation
&Tr,
IntegrationRule &result)
{
GenerateLSVector(Tr,LvlSet);
const int dim=pe->GetDim();
int np1d=CutIntegrationRules::Order/2+1;
if (dim==2)
{
LevelSet2D ls(pe,lsvec);
auto q = Algoim::quadGen<2>(ls,Algoim::BoundingBox<real_t,2>(0.0,1.0),
2, -1, np1d);
result.SetSize(q.nodes.size());
result.SetOrder(CutIntegrationRules::Order);
for (size_t i=0; i<q.nodes.size(); i++)
{
IntegrationPoint& ip=result.IntPoint(i);
ip.Set2w(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].w);
}
}
else
{
LevelSet3D ls(pe,lsvec);
auto q = Algoim::quadGen<3>(ls,Algoim::BoundingBox<real_t,3>(0.0,1.0),
3, -1, np1d);
result.SetSize(q.nodes.size());
result.SetOrder(CutIntegrationRules::Order);
for (size_t i=0; i<q.nodes.size(); i++)
{
IntegrationPoint& ip=result.IntPoint(i);
ip.Set(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].x(2),q.nodes[i].w);
}
}
}
void AlgoimIntegrationRules::GetVolumeIntegrationRule(ElementTransformation &Tr,
IntegrationRule &result,
const IntegrationRule *sir)
{
GenerateLSVector(Tr,LvlSet);
const int dim=pe->GetDim();
int np1d=CutIntegrationRules::Order/2+1;
if (dim==2)
{
LevelSet2D ls(pe,lsvec);
auto q = Algoim::quadGen<2>(ls,Algoim::BoundingBox<real_t,2>(0.0,1.0),
-1, -1, np1d);
result.SetSize(q.nodes.size());
result.SetOrder(CutIntegrationRules::Order);
for (size_t i=0; i<q.nodes.size(); i++)
{
IntegrationPoint& ip=result.IntPoint(i);
ip.Set2w(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].w);
}
}
else
{
LevelSet3D ls(pe,lsvec);
auto q = Algoim::quadGen<3>(ls,Algoim::BoundingBox<real_t,3>(0.0,1.0),
-1, -1, np1d);
result.SetSize(q.nodes.size());
result.SetOrder(CutIntegrationRules::Order);
for (size_t i=0; i<q.nodes.size(); i++)
{
IntegrationPoint& ip=result.IntPoint(i);
ip.Set(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].x(2),q.nodes[i].w);
}
}
}
void AlgoimIntegrationRules::GetSurfaceWeights(ElementTransformation &Tr,
const IntegrationRule &sir,
Vector &weights)
{
GenerateLSVector(Tr,LvlSet);
DenseMatrix bmat; // gradients of the shape functions in isoparametric space
DenseMatrix pmat; // gradients of the shape functions in physical space
Vector inormal; // normal to the level set in isoparametric space
Vector tnormal; // normal to the level set in physical space
bmat.SetSize(pe->GetDof(),pe->GetDim());
pmat.SetSize(pe->GetDof(),pe->GetDim());
inormal.SetSize(pe->GetDim());
tnormal.SetSize(pe->GetDim());
weights.SetSize(sir.GetNPoints());
for (int j = 0; j < sir.GetNPoints(); j++)
{
const IntegrationPoint &ip = sir.IntPoint(j);
Tr.SetIntPoint(&ip);
pe->CalcDShape(ip,bmat);
Mult(bmat, Tr.InverseJacobian(), pmat);
// compute the normal to the LS in isoparametric space
bmat.MultTranspose(lsvec,inormal);
// compute the normal to the LS in physical space
pmat.MultTranspose(lsvec,tnormal);
weights[j]= tnormal.Norml2() / inormal.Norml2();
}
}
void AlgoimIntegrationRules::GenerateLSVector(ElementTransformation &Tr,
Coefficient* lvlset)
{
//check if the coefficient is already projected
if (currentElementNo==Tr.ElementNo)
{
if (currentLvlSet==lvlset)
{
if (currentGeometry==Tr.GetGeometryType())
{
return;
}
}
}
currentElementNo=Tr.ElementNo;
if (currentGeometry!=Tr.GetGeometryType())
{
delete le;
delete pe;
currentGeometry=Tr.GetGeometryType();
if (Tr.GetGeometryType()==Geometry::Type::SQUARE)
{
pe=new H1Pos_QuadrilateralElement(lsOrder);
le=new H1_QuadrilateralElement(lsOrder);
}
else if (Tr.GetGeometryType()==Geometry::Type::CUBE)
{
pe=new H1Pos_HexahedronElement(lsOrder);
le=new H1_HexahedronElement(lsOrder);
}
else
{
MFEM_ABORT("Currently MFEM + Algoim supports only quads and hexes.");
}
T.SetSize(pe->GetDof());
pe->Project(*le,Tr,T);
//The transformation matrix depends only on the geometry for change of basis
}
currentLvlSet=lvlset;
const IntegrationRule &ir=le->GetNodes();
lsvec.SetSize(ir.GetNPoints());
lsfun.SetSize(ir.GetNPoints());
for (int i=0; i<ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
Tr.SetIntPoint(&ip);
lsfun(i)=lvlset->Eval(Tr,ip);
}
T.Mult(lsfun,lsvec);
}
#endif
#ifdef MFEM_USE_LAPACK
void MomentFittingIntRules::InitSurface(int order, Coefficient& levelset,
int lsO, ElementTransformation& Tr)
{
Init(order, levelset, lsO);
dim = Tr.GetDimension();
if (Tr.GetDimension() == 1)
{
nBasis = -1;
IntegrationRules irs(0, Quadrature1D::GaussLegendre);
ir = irs.Get(Geometry::SEGMENT, 0);
}
else
{
if (Tr.GetDimension() == 2)
{
nBasis = 2 * (Order + 1) + static_cast<int>(Order * (Order + 1) / 2);
}
else if (Tr.GetDimension() == 3)
{
if (Order== 0) { nBasis = 3; }
else if (Order== 1) { nBasis = 11; }
else if (Order== 2) { nBasis = 26; }
else if (Order== 3) { nBasis = 50; }
else if (Order== 4) { nBasis = 85; }
else if (Order== 5) { nBasis = 133; }
else if (Order== 6) { nBasis = 196; }
else if (Order>= 7) { nBasis = 276; Order = 7; }
}
// compute the quadrature points
int qorder = 0;
IntegrationRules irs(0, Quadrature1D::GaussLegendre);
ir = irs.Get(Tr.GetGeometryType(), qorder);
for (; ir.GetNPoints() <= nBasis; qorder++)
{
ir = irs.Get(Tr.GetGeometryType(), qorder);
}
}
}
void MomentFittingIntRules::InitVolume(int order, Coefficient& levelset,
int lsO, ElementTransformation& Tr)
{
order++;
InitSurface(order, levelset, lsO, Tr);
Order--;
nBasisVolume = 0;
if (Tr.GetDimension() == 1)
{
nBasisVolume = -1;
IntegrationRules irs(0, Quadrature1D::GaussLegendre);
ir = irs.Get(Geometry::SEGMENT, Order);
}
else
{
if (Tr.GetDimension() == 2)
{
nBasisVolume = (int)((Order + 1) * (Order + 2) / 2);
}
else if (Tr.GetDimension() == 3)
{
for (int p = 0; p <= Order; p++)
{
nBasisVolume +=(int)((p + 1) * (p + 2) / 2);
}
}
// assemble the matrix
DenseMatrix Mat(nBasisVolume, ir.GetNPoints());
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
Vector shape;
if (Tr.GetDimension() == 2)
{
Basis2D(ir.IntPoint(ip), shape);
}
else if (Tr.GetDimension() == 3)
{
Basis3D(ir.IntPoint(ip), shape);
}
Mat.SetCol(ip, shape);
}
// compute the SVD for the matrix
VolumeSVD = new DenseMatrixSVD(Mat, 'A', 'A');
VolumeSVD->Eval(Mat);
}
}
void MomentFittingIntRules::ComputeFaceWeights(ElementTransformation& Tr)
{
int elem = Tr.ElementNo;
const Mesh *mesh = Tr.mesh;
if (FaceIP.Size() == 0)
{
FaceWeightsComp.SetSize(mesh->GetNFaces());
FaceWeightsComp = 0.;
}
const Element* me = mesh->GetElement(elem);
IsoparametricTransformation Trafo;
mesh->GetElementTransformation(elem, &Trafo);
Array<int> faces;
Array<int> cor;
mesh->GetElementFaces(elem, faces, cor);
for (int face = 0; face < me->GetNFaces(); face++)
{
if (FaceWeightsComp(faces[face]) == 0.)
{
FaceWeightsComp(faces[face]) = 1.;
Array<int> verts;
mesh->GetFaceVertices(faces[face], verts);
Vector pointA(mesh->SpaceDimension());
Vector pointB(mesh->SpaceDimension());
Vector pointC(mesh->SpaceDimension());
Vector pointD(mesh->SpaceDimension());
for (int d = 0; d < mesh->SpaceDimension(); d++)
{
pointA(d) = (mesh->GetVertex(verts[0]))[d];
pointB(d) = (mesh->GetVertex(verts[1]))[d];
pointC(d) = (mesh->GetVertex(verts[2]))[d];
pointD(d) = (mesh->GetVertex(verts[3]))[d];
}
// TODO - don't we lose the curvature with this local mesh setup?
Mesh local_mesh(2,4,1,0,3);
local_mesh.AddVertex(pointA);
local_mesh.AddVertex(pointB);
local_mesh.AddVertex(pointC);
local_mesh.AddVertex(pointD);
local_mesh.AddQuad(0,1,2,3);
local_mesh.FinalizeQuadMesh(1);
IsoparametricTransformation faceTrafo;
local_mesh.GetElementTransformation(0, &faceTrafo);
// The 3D face integrals are computed as 2D volumetric integrals.
// The 2D face integrals are computed as 1D volumetric integrals.
MomentFittingIntRules FaceRules(Order, *LvlSet, lsOrder);
IntegrationRule FaceRule;
FaceRules.GetVolumeIntegrationRule(faceTrafo, FaceRule);
if (FaceIP.Size() != FaceRule.Size())
{
FaceIP.SetSize(FaceRule.Size());
for (int ip = 0; ip < FaceRule.GetNPoints(); ip++)
{
FaceIP[ip].index = ip;
IntegrationPoint &intp = FaceIP[ip];
intp.x = FaceRule.IntPoint(ip).x;
intp.y = FaceRule.IntPoint(ip).y;
intp.weight = 0.;
}
FaceWeights.SetSize(FaceRule.GetNPoints(), mesh->GetNFaces());
FaceWeights = 0.;
FaceWeightsComp = 0.;
FaceWeightsComp(faces[face]) = 1.;
}
for (int ip = 0; ip < FaceRule.GetNPoints(); ip++)
{
FaceWeights(ip, faces[face]) = FaceRule.IntPoint(ip).weight;
}
}
}
mesh->GetElementTransformation(elem, &Trafo);
}
void MomentFittingIntRules::ComputeSurfaceWeights1D(ElementTransformation& Tr)
{
IntegrationPoint& intp = ir.IntPoint(0);
IntegrationPoint ip0;
ip0.x = 0.;
IntegrationPoint ip1;
ip1.x = 1.;
Tr.SetIntPoint(&ip0);
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip1) < 0.)
{
IntegrationPoint ip2;
ip2.x = .5;
while (LvlSet->Eval(Tr, ip2) > tol_1
|| LvlSet->Eval(Tr, ip2) < -tol_1)
{
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip2) < 0.)
{
ip1.x = ip2.x;
}
else
{
ip0.x = ip2.x;
}
ip2.x = (ip1.x + ip0.x) / 2.;
}
intp.x = ip2.x;
intp.weight = 1. / Tr.Weight();
}
else if (LvlSet->Eval(Tr, ip0) > 0. && LvlSet->Eval(Tr, ip1) <= tol_1)
{
intp.x = 1.;
intp.weight = 1. / Tr.Weight();
}
else if (LvlSet->Eval(Tr, ip1) > 0. && LvlSet->Eval(Tr, ip0) <= tol_1)
{
intp.x = 0.;
intp.weight = 1. / Tr.Weight();
}
else
{
intp.x = .5;
intp.weight = 0.;
}
}
double bisect(ElementTransformation &Tr, Coefficient *LvlSet)
{
IntegrationPoint intp;
IntegrationPoint ip0;
ip0.x = 0.;
IntegrationPoint ip1;
ip1.x = 1.;
Tr.SetIntPoint(&ip0);
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip1) < 0.)
{
IntegrationPoint ip2;
ip2.x = .5;
while (LvlSet->Eval(Tr, ip2) > 1e-12
|| LvlSet->Eval(Tr, ip2) < -1e-12)
{
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip2) < 0.)
{
ip1.x = ip2.x;
}
else
{
ip0.x = ip2.x;
}
ip2.x = (ip1.x + ip0.x) / 2.;
}
intp.x = ip2.x;
intp.weight = 1. / Tr.Weight();
}
else if (LvlSet->Eval(Tr, ip0) > 0. && LvlSet->Eval(Tr, ip1) <= 1e-12)
{
intp.x = 1.;
intp.weight = 1. / Tr.Weight();
}
else if (LvlSet->Eval(Tr, ip1) > 0. && LvlSet->Eval(Tr, ip0) <= 1e-12)
{
intp.x = 0.;
intp.weight = 1. / Tr.Weight();
}
else
{
intp.x = .5;
intp.weight = 0.;
}
return intp.x;
}
void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr)
{
IntegrationRules irs(0, Quadrature1D::GaussLegendre);
IntegrationRule ir2 = irs.Get(Geometry::SEGMENT, ir.GetOrder());
IntegrationPoint ip0;
ip0.x = 0.;
IntegrationPoint ip1;
ip1.x = 1.;
Tr.SetIntPoint(&ip0);
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip1) < 0.)
{
Vector tempX(ir.GetNPoints());
real_t length;
if (LvlSet->Eval(Tr, ip0) > 0.)
{
length = bisect(Tr, LvlSet);
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
IntegrationPoint &intp = ir.IntPoint(ip);
intp.x = ir2.IntPoint(ip).x * length;
intp.weight = ir2.IntPoint(ip).weight * length;
}
}
else
{
length = 1. - bisect(Tr, LvlSet);
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
IntegrationPoint &intp = ir.IntPoint(ip);
intp.x = bisect(Tr, LvlSet) + ir2.IntPoint(ip).x * length;
intp.weight = ir2.IntPoint(ip).weight * length;
}
}
}
else if (LvlSet->Eval(Tr, ip0) <= -tol_1
|| LvlSet->Eval(Tr, ip1) <= -tol_1)
{
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
IntegrationPoint &intp = ir.IntPoint(ip);
intp.x = ir2.IntPoint(ip).x;
intp.weight = 0.;
}
}
else
{
ir = ir2;
}
}
void MomentFittingIntRules::ComputeSurfaceWeights2D(ElementTransformation& Tr)
{
int elem = Tr.ElementNo;
const Mesh* mesh = Tr.mesh;
const Element* me = mesh->GetElement(elem);
IsoparametricTransformation Trafo;
mesh->GetElementTransformation(elem, &Trafo);
DenseMatrix Mat(nBasis, ir.GetNPoints());
Mat = 0.;
Vector RHS(nBasis);
RHS = 0.;
Vector ElemWeights(ir.GetNPoints());
ElemWeights = 0.;
bool element_int = false;
bool interior = true;
Array<bool> edge_int;
DenseMatrix PointA(me->GetNEdges(), Trafo.GetSpaceDim());
DenseMatrix PointB(me->GetNEdges(), Trafo.GetSpaceDim());
Vector edgelength(me->GetNEdges());
Array<int> verts;
mesh->GetElementVertices(elem, verts);
// find the edges that are intersected by the surface and inside the area
for (int edge = 0; edge < me->GetNEdges(); edge++)
{
enum class Layout {inside, intersected, outside};
Layout layout;
const int* vert = me->GetEdgeVertices(edge);
Vector pointA(Trafo.GetSpaceDim());
Vector pointB(Trafo.GetSpaceDim());
for (int d = 0; d < Trafo.GetSpaceDim(); d++)
{
pointA(d) = (Trafo.mesh->GetVertex(verts[vert[0]]))[d];
pointB(d) = (Trafo.mesh->GetVertex(verts[vert[1]]))[d];
}
Vector edgevec(Trafo.GetSpaceDim());
subtract(pointA, pointB, edgevec);
edgelength(edge) = edgevec.Norml2();
IntegrationPoint ipA;
Trafo.TransformBack(pointA, ipA);
IntegrationPoint ipB;
Trafo.TransformBack(pointB, ipB);
if (LvlSet->Eval(Trafo, ipA) < -tol_1
|| LvlSet->Eval(Trafo, ipB) < -tol_1)
{
interior = false;
}
if (LvlSet->Eval(Trafo, ipA) > -tol_1
&& LvlSet->Eval(Trafo, ipB) > -tol_1)
{
layout = Layout::inside;
}
else if (LvlSet->Eval(Trafo, ipA) > tol_2
&& LvlSet->Eval(Trafo, ipB) <= 0.)
{
layout = Layout::intersected;
}
else if (LvlSet->Eval(Trafo, ipA) <= 0.
&& LvlSet->Eval(Trafo, ipB) > tol_2)
{
layout = Layout::intersected;
Vector temp(pointA.Size());
temp = pointA;
pointA = pointB;
pointB = temp;
}
else
{
layout = Layout::outside;
}
// Store the end points of the (1D) intersected edge.
if (layout == Layout::intersected)
{
Vector pointC(pointA.Size());
Vector mid(pointA.Size());
pointC = pointA;
mid = pointC;
mid += pointB;
mid /= 2.;
IntegrationPoint ip;
Trafo.TransformBack(mid, ip);
while (LvlSet->Eval(Trafo, ip) > tol_1
|| LvlSet->Eval(Trafo, ip) < -tol_1)
{
if (LvlSet->Eval(Trafo, ip) > tol_1)
{
pointC = mid;
}
else
{
pointB = mid;
}
mid = pointC;
mid += pointB;
mid /= 2.;
Trafo.TransformBack(mid, ip);
}
pointB = mid;
}
PointA.SetRow(edge, pointA);
PointB.SetRow(edge, pointB);
if ((layout == Layout::inside || layout == Layout::intersected))
{
edge_int.Append(true);
}
else
{
edge_int.Append(false);
}
}
// Integrate over the 1D edges.
for (int edge = 0; edge < me->GetNEdges(); edge++)
{
if (edge_int[edge] && !interior)
{
Vector point0(Trafo.GetSpaceDim());
Vector point1(Trafo.GetSpaceDim());
PointA.GetRow(edge, point0);
PointB.GetRow(edge, point1);
element_int = true;
const IntegrationRule *ir2 = &IntRules.Get(Geometry::SEGMENT,
2*Order+1);
Vector normal(Trafo.GetDimension());
normal = 0.;
if (edge == 0 || edge == 2)
{
normal(1) = 1.;
}
if (edge == 1 || edge == 3)
{
normal(0) = 1.;
}
if (edge == 0 || edge == 3)
{
normal *= -1.;
}
for (int ip = 0; ip < ir2->GetNPoints(); ip++)
{
Vector dist(Trafo.GetSpaceDim());
dist = point1;
dist -= point0;
Vector point(Trafo.GetSpaceDim());
point = dist;
point *= ir2->IntPoint(ip).x;
point += point0;
IntegrationPoint intpoint;
Trafo.TransformBack(point, intpoint);
Trafo.SetIntPoint(&intpoint);
DenseMatrix shapes;
OrthoBasis2D(intpoint, shapes);
Vector grad(Trafo.GetDimension());
for (int dof = 0; dof < nBasis; dof++)
{
shapes.GetRow(dof, grad);
RHS(dof) -= (grad * normal) * ir2->IntPoint(ip).weight
* dist.Norml2() / edgelength(edge);
}
}
}
}
// do integration over the area for integral over interface
if (element_int && !interior)
{
H1_FECollection fec(lsOrder, 2);
FiniteElementSpace fes(const_cast<Mesh*>(Tr.mesh), &fec);
GridFunction LevelSet(&fes);
LevelSet.ProjectCoefficient(*LvlSet);
mesh->GetElementTransformation(elem, &Trafo);
const FiniteElement* fe = fes.GetFE(elem);
Vector normal(Trafo.GetDimension());
Vector gradi(Trafo.GetDimension());
DenseMatrix dshape(fe->GetDof(), Trafo.GetDimension());
Array<int> dofs;
fes.GetElementDofs(elem, dofs);
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
Trafo.SetIntPoint(&(ir.IntPoint(ip)));
normal = 0.;
fe->CalcDShape(ir.IntPoint(ip), dshape);
for (int dof = 0; dof < fe->GetDof(); dof++)
{
dshape.GetRow(dof, gradi);
gradi *= LevelSet(dofs[dof]);
normal += gradi;
}
normal *= (-1. / normal.Norml2());
DenseMatrix shapes;
OrthoBasis2D(ir.IntPoint(ip), shapes);
for (int dof = 0; dof < nBasis; dof++)
{
Vector grad(Trafo.GetSpaceDim());
shapes.GetRow(dof, grad);
Mat(dof, ip) = (grad * normal);
}
}
// solve the underdetermined linear system
Vector temp(nBasis);
Vector temp2(ir.GetNPoints());
DenseMatrixSVD SVD(Mat, 'A', 'A');
SVD.Eval(Mat);
SVD.LeftSingularvectors().MultTranspose(RHS, temp);
temp2 = 0.;
for (int i = 0; i < nBasis; i++)
{
if (SVD.Singularvalue(i) > tol_1)
{
temp2(i) = temp(i) / SVD.Singularvalue(i);
}
}
SVD.RightSingularvectors().MultTranspose(temp2, ElemWeights);
}
// save the weights
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
IntegrationPoint& intp = ir.IntPoint(ip);
intp.weight = ElemWeights(ip);
}
mesh->GetElementTransformation(elem, &Trafo);
}
void MomentFittingIntRules::ComputeVolumeWeights2D(ElementTransformation& Tr,
const IntegrationRule* sir)
{
int elem = Tr.ElementNo;
const Mesh* mesh = Tr.mesh;
const Element* me = mesh->GetElement(elem);
IsoparametricTransformation Trafo;
mesh->GetElementTransformation(elem, &Trafo);
Vector RHS(nBasisVolume);
RHS = 0.;
Vector ElemWeights(ir.GetNPoints());
ElemWeights = 0.;
bool element_int = false;
bool interior = true;
Array<bool> edge_int;
DenseMatrix PointA(me->GetNEdges(), Trafo.GetSpaceDim());
DenseMatrix PointB(me->GetNEdges(), Trafo.GetSpaceDim());
Vector edgelength(me->GetNEdges());
Array<int> verts;
mesh->GetElementVertices(elem, verts);
// find the edges that are intersected by he surface and inside the area
for (int edge = 0; edge < me->GetNEdges(); edge++)
{
enum class Layout {inside, intersected, outside};
Layout layout;
const int* vert = me->GetEdgeVertices(edge);
Vector pointA(Trafo.GetSpaceDim());
Vector pointB(Trafo.GetSpaceDim());
for (int d = 0; d < Trafo.GetSpaceDim(); d++)
{
pointA(d) = (Trafo.mesh->GetVertex(verts[vert[0]]))[d];
pointB(d) = (Trafo.mesh->GetVertex(verts[vert[1]]))[d];
}
Vector edgevec(Trafo.GetSpaceDim());
subtract(pointA, pointB, edgevec);
edgelength(edge) = edgevec.Norml2();
IntegrationPoint ipA;
Trafo.TransformBack(pointA, ipA);
IntegrationPoint ipB;
Trafo.TransformBack(pointB, ipB);
if (LvlSet->Eval(Trafo, ipA) < -tol_1
|| LvlSet->Eval(Trafo, ipB) < -tol_1)
{
interior = false;
}
if (LvlSet->Eval(Trafo, ipA) > -tol_1
&& LvlSet->Eval(Trafo, ipB) > -tol_1)
{
layout = Layout::inside;
}
else if (LvlSet->Eval(Trafo, ipA) > tol_2
&& LvlSet->Eval(Trafo, ipB) <= 0.)
{
layout = Layout::intersected;
}
else if (LvlSet->Eval(Trafo, ipA) <= 0.
&& LvlSet->Eval(Trafo, ipB) > tol_2)
{
layout = Layout::intersected;
Vector temp(pointA.Size());
temp = pointA;
pointA = pointB;
pointB = temp;
}
else
{
layout = Layout::outside;
}
if (layout == Layout::intersected)
{
Vector pointC(pointA.Size());
Vector mid(pointA.Size());
pointC = pointA;
mid = pointC;
mid += pointB;
mid /= 2.;
IntegrationPoint ip;
Trafo.TransformBack(mid, ip);
while (LvlSet->Eval(Trafo, ip) > tol_1
|| LvlSet->Eval(Trafo, ip) < -tol_1)
{
if (LvlSet->Eval(Trafo, ip) > tol_1)
{
pointC = mid;
}
else
{
pointB = mid;
}
mid = pointC;
mid += pointB;
mid /= 2.;
Trafo.TransformBack(mid, ip);
}
pointB = mid;
}
PointA.SetRow(edge, pointA);
PointB.SetRow(edge, pointB);
if ((layout == Layout::inside || layout == Layout::intersected))
{
edge_int.Append(true);
}
else
{
edge_int.Append(false);
}
}
// do the integration over the edges
for (int edge = 0; edge < me->GetNEdges(); edge++)
{
if (edge_int[edge] && !interior)
{
Vector point0(Trafo.GetSpaceDim());
Vector point1(Trafo.GetSpaceDim());
PointA.GetRow(edge, point0);
PointB.GetRow(edge, point1);
element_int = true;
const IntegrationRule *ir2 = &IntRules.Get(Geometry::SEGMENT,
2*Order+1);
Vector normal(Trafo.GetDimension());
normal = 0.;
if (edge == 0 || edge == 2)
{
normal(1) = 1.;
}
if (edge == 1 || edge == 3)
{
normal(0) = 1.;
}
if (edge == 0 || edge == 3)
{
normal *= -1.;
}
for (int ip = 0; ip < ir2->GetNPoints(); ip++)
{
Vector dist(Trafo.GetSpaceDim());
dist = point1;
dist -= point0;
Vector point(Trafo.GetSpaceDim());
point = dist;
point *= ir2->IntPoint(ip).x;
point += point0;
IntegrationPoint intpoint;
Trafo.TransformBack(point, intpoint);
DenseMatrix shapes;
BasisAD2D(intpoint, shapes);
Vector adiv(Trafo.GetDimension());
for (int dof = 0; dof < nBasisVolume; dof++)
{
shapes.GetRow(dof, adiv);
RHS(dof) += (adiv * normal) * ir2->IntPoint(ip).weight
* dist.Norml2() / edgelength(edge);
}
}
}
}
// Integrate over the interface using the already computed surface rule, and
// solve the linear system for the weights.
if (element_int && !interior)
{
H1_FECollection fec(lsOrder, 2);
FiniteElementSpace fes(const_cast<Mesh*>(Tr.mesh), &fec);
GridFunction LevelSet(&fes);
LevelSet.ProjectCoefficient(*LvlSet);
mesh->GetElementTransformation(elem, &Trafo);
const FiniteElement* fe = fes.GetFE(elem);
Vector normal(Trafo.GetDimension());
Vector gradi(Trafo.GetDimension());
DenseMatrix dshape(fe->GetDof(), Trafo.GetDimension());
Array<int> dofs;
fes.GetElementDofs(elem, dofs);
for (int ip = 0; ip < sir->GetNPoints(); ip++)
{
Trafo.SetIntPoint(&(sir->IntPoint(ip)));
normal = 0.;
fe->CalcDShape(sir->IntPoint(ip), dshape);
for (int dof = 0; dof < fe->GetDof(); dof++)
{
dshape.GetRow(dof, gradi);
gradi *= LevelSet(dofs[dof]);
normal += gradi;
}
normal *= (-1. / normal.Norml2());
DenseMatrix shapes;
BasisAD2D(sir->IntPoint(ip), shapes);
for (int dof = 0; dof < nBasisVolume; dof++)
{
Vector adiv(2);
shapes.GetRow(dof, adiv);
RHS(dof) += (adiv * normal) * sir->IntPoint(ip).weight;
}
}
// solve the underdetermined linear system
Vector temp(nBasisVolume);
Vector temp2(ir.GetNPoints());
temp2 = 0.;
VolumeSVD->LeftSingularvectors().MultTranspose(RHS, temp);
for (int i = 0; i < nBasisVolume; i++)
{
if (VolumeSVD->Singularvalue(i) > tol_1)
{
temp2(i) = temp(i) / VolumeSVD->Singularvalue(i);
}
}
VolumeSVD->RightSingularvectors().MultTranspose(temp2, ElemWeights);
}
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
IntegrationPoint& intp = ir.IntPoint(ip);
intp.weight = ElemWeights(ip);
}
if (interior)
{
int qorder = 0;
IntegrationRules irs(0, Quadrature1D::GaussLegendre);
IntegrationRule ir2 = irs.Get(Trafo.GetGeometryType(), qorder);
for (; ir2.GetNPoints() < ir.GetNPoints(); qorder++)
{
ir2 = irs.Get(Trafo.GetGeometryType(), qorder);
}
ir = ir2;
}
mesh->GetElementTransformation(elem, &Trafo);
}
void MomentFittingIntRules::ComputeSurfaceWeights3D(ElementTransformation& Tr)
{
ComputeFaceWeights(Tr);
int elem = Tr.ElementNo;
const Mesh* mesh = Tr.mesh;
const Element* me = mesh->GetElement(elem);
IsoparametricTransformation Trafo;
mesh->GetElementTransformation(elem, &Trafo);
DenseMatrix Mat(nBasis, ir.GetNPoints());
Mat = 0.;
Vector RHS(nBasis);
RHS = 0.;
Vector ElemWeights(ir.GetNPoints());
ElemWeights = 0.;
// Does the element have a positive vertex?
bool element_int = false;
// Are all element vertices positive?
bool interior = true;
Array<int> verts;
mesh->GetElementVertices(elem, verts);
for (int face = 0; face < me->GetNFaces(); face++)
{
const int* vert = me->GetFaceVertices(face);
Vector pointA(Trafo.GetSpaceDim());
Vector pointB(Trafo.GetSpaceDim());
Vector pointC(Trafo.GetSpaceDim());
Vector pointD(Trafo.GetSpaceDim());
for (int d = 0; d < Trafo.GetSpaceDim(); d++)
{
pointA(d) = (Trafo.mesh->GetVertex(verts[vert[0]]))[d];
pointB(d) = (Trafo.mesh->GetVertex(verts[vert[1]]))[d];
pointC(d) = (Trafo.mesh->GetVertex(verts[vert[2]]))[d];
pointD(d) = (Trafo.mesh->GetVertex(verts[vert[3]]))[d];
}
IntegrationPoint ipA;
Trafo.TransformBack(pointA, ipA);
IntegrationPoint ipB;
Trafo.TransformBack(pointB, ipB);
IntegrationPoint ipC;
Trafo.TransformBack(pointC, ipC);
IntegrationPoint ipD;
Trafo.TransformBack(pointD, ipD);
if (LvlSet->Eval(Trafo, ipA) < -tol_1
|| LvlSet->Eval(Trafo, ipB) < -tol_1
|| LvlSet->Eval(Trafo, ipC) < -tol_1
|| LvlSet->Eval(Trafo, ipD) < -tol_1)
{
interior = false;
}
if (LvlSet->Eval(Trafo, ipA) > -tol_1
|| LvlSet->Eval(Trafo, ipB) > -tol_1
|| LvlSet->Eval(Trafo, ipC) > -tol_1
|| LvlSet->Eval(Trafo, ipD) > -tol_1)
{
element_int = true;
}
Array<int> faces;
Array<int> cor;
mesh->GetElementFaces(elem, faces, cor);
IsoparametricTransformation Tr1, Tr2;
FaceElementTransformations FTrans;
Trafo.mesh->GetFaceElementTransformations(faces[face], FTrans, Tr1, Tr2);
FTrans.SetIntPoint(&(FaceIP[0]));
Vector normal(Trafo.GetDimension());
normal = 0.;
if (face == 0 || face == 5)
{
normal(2) = 1.;
}
if (face == 1 || face == 3)
{
normal(1) = 1.;
}
if (face == 2 || face == 4)
{
normal(0) = 1.;
}
if (face == 0 || face == 1 || face == 4)
{
normal *= -1.;
}
for (int ip = 0; ip < FaceIP.Size(); ip++)
{
DenseMatrix shape;
Vector point(3);
IntegrationPoint ipoint;
FTrans.Transform(FaceIP[ip], point);
Trafo.TransformBack(point, ipoint);
OrthoBasis3D(ipoint, shape);
for (int dof = 0; dof < nBasis; dof++)
{
Vector grad(Trafo.GetSpaceDim());
shape.GetRow(dof, grad);
RHS(dof) -= (grad * normal) * FaceWeights(ip, faces[face]);
}
}
}
// If the element is intersected, form the matrix and solve for the weights.
if (element_int && !interior)
{
H1_FECollection fec(lsOrder, 3);
FiniteElementSpace fes(const_cast<Mesh*>(Tr.mesh), &fec);
GridFunction LevelSet(&fes);
LevelSet.ProjectCoefficient(*LvlSet);
mesh->GetElementTransformation(elem, &Trafo);
const FiniteElement* fe = fes.GetFE(elem);
Vector normal(Trafo.GetDimension());
Vector gradi(Trafo.GetDimension());
DenseMatrix dshape(fe->GetDof(), Trafo.GetDimension());
Array<int> dofs;
fes.GetElementDofs(elem, dofs);
// Form the matrix.
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
Trafo.SetIntPoint(&(ir.IntPoint(ip)));
normal = 0.;
fe->CalcDShape(ir.IntPoint(ip), dshape);
for (int dof = 0; dof < fe->GetDof(); dof++)
{
dshape.GetRow(dof, gradi);
gradi *= LevelSet(dofs[dof]);
normal += gradi;
}
normal *= (-1. / normal.Norml2());
DenseMatrix shapes;
OrthoBasis3D(ir.IntPoint(ip), shapes);
for (int dof = 0; dof < nBasis; dof++)
{
Vector grad(Trafo.GetSpaceDim());
shapes.GetRow(dof, grad);
Mat(dof, ip) = (grad * normal);
}
}
// solve the underdetermined linear system
Vector temp(nBasis);
Vector temp2(ir.GetNPoints());
DenseMatrixSVD SVD(Mat, 'A', 'A');
SVD.Eval(Mat);
SVD.LeftSingularvectors().MultTranspose(RHS, temp);
temp2 = 0.;
for (int i = 0; i < nBasis; i++)
{
if (SVD.Singularvalue(i) > tol_1)
{
temp2(i) = temp(i) / SVD.Singularvalue(i);
}
}
SVD.RightSingularvectors().MultTranspose(temp2, ElemWeights);
}
// scale the weights
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
IntegrationPoint& intp = ir.IntPoint(ip);
intp.weight = ElemWeights(ip);
}
mesh->GetElementTransformation(elem, &Trafo);
}
void MomentFittingIntRules::ComputeVolumeWeights3D(ElementTransformation& Tr,
const IntegrationRule* sir)
{
Order++;
ComputeFaceWeights(Tr);
Order--;
int elem = Tr.ElementNo;
const Mesh* mesh = Tr.mesh;
const Element* me = mesh->GetElement(elem);
IsoparametricTransformation Trafo;
mesh->GetElementTransformation(elem, &Trafo);
Vector RHS(nBasisVolume);
RHS = 0.;
Vector ElemWeights(ir.GetNPoints());
ElemWeights = 0.;
// Does the element have a positive vertex?
bool element_int = false;
// Are all element vertices positive?
bool interior = true;
Array<int> verts;
mesh->GetElementVertices(elem, verts);
for (int face = 0; face < me->GetNFaces(); face++)
{
const int* vert = me->GetFaceVertices(face);
Vector pointA(Trafo.GetSpaceDim());
Vector pointB(Trafo.GetSpaceDim());
Vector pointC(Trafo.GetSpaceDim());
Vector pointD(Trafo.GetSpaceDim());
for (int d = 0; d < Trafo.GetSpaceDim(); d++)
{
pointA(d) = (Trafo.mesh->GetVertex(verts[vert[0]]))[d];
pointB(d) = (Trafo.mesh->GetVertex(verts[vert[1]]))[d];
pointC(d) = (Trafo.mesh->GetVertex(verts[vert[2]]))[d];
pointD(d) = (Trafo.mesh->GetVertex(verts[vert[3]]))[d];
}
IntegrationPoint ipA;
Trafo.TransformBack(pointA, ipA);
IntegrationPoint ipB;
Trafo.TransformBack(pointB, ipB);
IntegrationPoint ipC;
Trafo.TransformBack(pointC, ipC);
IntegrationPoint ipD;
Trafo.TransformBack(pointD, ipD);
if (LvlSet->Eval(Trafo, ipA) < -tol_1
|| LvlSet->Eval(Trafo, ipB) < -tol_1
|| LvlSet->Eval(Trafo, ipC) < -tol_1
|| LvlSet->Eval(Trafo, ipD) < -tol_1)
{
interior = false;
}
if (LvlSet->Eval(Trafo, ipA) > -tol_1
|| LvlSet->Eval(Trafo, ipB) > -tol_1
|| LvlSet->Eval(Trafo, ipC) > -tol_1
|| LvlSet->Eval(Trafo, ipD) > -tol_1)
{
element_int = true;
}
Array<int> faces;
Array<int> cor;
mesh->GetElementFaces(elem, faces, cor);
IsoparametricTransformation Tr1, Tr2;
FaceElementTransformations FTrans;
Trafo.mesh->GetFaceElementTransformations(faces[face], FTrans, Tr1, Tr2);
FTrans.SetIntPoint(&(FaceIP[0]));
Vector normal(Trafo.GetDimension());
normal = 0.;
if (face == 0 || face == 5)
{
normal(2) = 1.;
}
if (face == 1 || face == 3)
{
normal(1) = 1.;
}
if (face == 2 || face == 4)
{
normal(0) = 1.;
}
if (face == 0 || face == 1 || face == 4)
{
normal *= -1.;
}
for (int ip = 0; ip < FaceIP.Size(); ip++)
{
DenseMatrix shape;
Vector point(3);
IntegrationPoint ipoint;
FTrans.Transform(FaceIP[ip], point);
Trafo.TransformBack(point, ipoint);
BasisAD3D(ipoint, shape);
for (int dof = 0; dof < nBasisVolume; dof++)
{
Vector adiv(Trafo.GetSpaceDim());
shape.GetRow(dof, adiv);
RHS(dof) += (adiv * normal) * FaceWeights(ip, faces[face]);
}
}
}
// If the element is intersected, integrate over the cut surface (using the
// already computed rule) and solve the matrix for the weights.
if (element_int && !interior)
{
H1_FECollection fec(lsOrder, 3);
FiniteElementSpace fes(const_cast<Mesh*>(Tr.mesh), &fec);
GridFunction LevelSet(&fes);
LevelSet.ProjectCoefficient(*LvlSet);
mesh->GetElementTransformation(elem, &Trafo);
const FiniteElement* fe = fes.GetFE(elem);
Vector normal(Trafo.GetDimension());
Vector gradi(Trafo.GetDimension());
DenseMatrix dshape(fe->GetDof(), Trafo.GetDimension());
Array<int> dofs;
fes.GetElementDofs(elem, dofs);
// Integrate over the cut surface using the already computed rule.
for (int ip = 0; ip < sir->GetNPoints(); ip++)
{
Trafo.SetIntPoint(&(sir->IntPoint(ip)));
normal = 0.;
fe->CalcDShape(sir->IntPoint(ip), dshape);
for (int dof = 0; dof < fe->GetDof(); dof++)
{
dshape.GetRow(dof, gradi);
gradi *= LevelSet(dofs[dof]);
normal += gradi;
}
normal *= (-1. / normal.Norml2());
DenseMatrix shapes;
BasisAD3D(sir->IntPoint(ip), shapes);
for (int dof = 0; dof < nBasisVolume; dof++)
{
Vector adiv(Trafo.GetSpaceDim());
shapes.GetRow(dof, adiv);
RHS(dof) += (adiv * normal) * sir->IntPoint(ip).weight;
}
}
// solve the underdetermined linear system
Vector temp(nBasisVolume);
Vector temp2(ir.GetNPoints());
VolumeSVD->LeftSingularvectors().MultTranspose(RHS, temp);
temp2 = 0.;
for (int i = 0; i < nBasisVolume; i++)
if (VolumeSVD->Singularvalue(i) > tol_1)
{
temp2(i) = temp(i) / VolumeSVD->Singularvalue(i);
}
VolumeSVD->RightSingularvectors().MultTranspose(temp2, ElemWeights);
}
// scale the weights
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
IntegrationPoint& intp = ir.IntPoint(ip);
intp.weight = ElemWeights(ip);
}
// Fully inside the subdomain -> standard integration.
if (interior)
{
int qorder = 0;
IntegrationRules irs(0, Quadrature1D::GaussLegendre);
IntegrationRule ir2 = irs.Get(Trafo.GetGeometryType(), qorder);
for (; ir2.GetNPoints() < ir.GetNPoints(); qorder++)
{
ir2 = irs.Get(Trafo.GetGeometryType(), qorder);
}
ir = ir2;
}
mesh->GetElementTransformation(elem, &Trafo);
}
void MomentFittingIntRules::DivFreeBasis2D(const IntegrationPoint& ip,
DenseMatrix& shape)
{
shape.SetSize(nBasis, 2);
Vector X(2);
X(0) = -1. + 2. * ip.x;
X(1) = -1. + 2. * ip.y;
for (int c = 0; c <= Order; c++)
{
Vector a(2);
a = 0.;
a(1) = pow(X(0), (real_t)(c));
Vector b(2);
b = 0.;
b(0) = pow(X(1), (real_t)(c));
shape.SetRow(2 * c, a);
shape.SetRow(2 * c + 1, b);
}
Poly_1D poly;
int count = 2 * Order+ 2;
for (int c = 1; c <= Order; c++)
{
const int* factorial = poly.Binom(c);
for (int expo = c; expo > 0; expo--)
{
Vector a(2);
a(0) = (real_t)(factorial[expo]) * pow(X(0), (real_t)(expo))
* pow(X(1), (real_t)(c - expo));
a(1) = -1. * (real_t)(factorial[expo - 1])
* pow(X(0), (real_t)(expo - 1))
* pow(X(1), (real_t)(c - expo + 1));
shape.SetRow(count, a);
count++;
}
}
}
void MomentFittingIntRules::OrthoBasis2D(const IntegrationPoint& ip,
DenseMatrix& shape)
{
const IntegrationRule *ir_ = &IntRules.Get(Geometry::SQUARE, 2*Order+1);
shape.SetSize(nBasis, 2);
// evaluate basis in the point
DenseMatrix preshape(nBasis, 2);
DivFreeBasis2D(ip, shape);
// evaluate basis for quadrature points
DenseTensor shapeMFN(nBasis, 2, ir_->GetNPoints());
for (int p = 0; p < ir_->GetNPoints(); p++)
{
DenseMatrix shapeN(nBasis, 2);
DivFreeBasis2D(ir_->IntPoint(p), shapeN);
for (int i = 0; i < nBasis; i++)
for (int j = 0; j < 2; j++)
{
shapeMFN(i, j, p) = shapeN(i, j);
}
}
// do modified Gram-Schmidt orthogonalization
for (int count = 1; count < nBasis; count++)
{
mGSStep(shape, shapeMFN, count);
}
}
void MomentFittingIntRules::OrthoBasis3D(const IntegrationPoint& ip,
DenseMatrix& shape)
{
Vector X(3);
X(0) = -1. + 2. * ip.x;
X(1) = -1. + 2. * ip.y;
X(2) = -1. + 2. * ip.z;
DivFreeBasis::GetDivFree3DBasis(X, shape, Order);
}
void MomentFittingIntRules::mGSStep(DenseMatrix& shape, DenseTensor& shapeMFN,
int step)
{
const IntegrationRule *ir_ = &IntRules.Get(Geometry::SQUARE, 2*Order+1);
for (int count = step; count < shape.Height(); count++)
{
real_t den = 0.;
real_t num = 0.;
for (int ip = 0; ip < ir_->GetNPoints(); ip++)
{
Vector u(2);
Vector v(2);
shapeMFN(ip).GetRow(count, u);
shapeMFN(ip).GetRow(step - 1, v);
den += v * v * ir_->IntPoint(ip).weight;
num += u * v * ir_->IntPoint(ip).weight;
}
real_t coeff = num / den;
Vector s(2);
Vector t(2);
shape.GetRow(step - 1, s);
shape.GetRow(count, t);
s *= coeff;
t += s;
shape.SetRow(count, t);
for (int ip = 0; ip < ir_->GetNPoints(); ip++)
{
shapeMFN(ip).GetRow(step - 1, s);
shapeMFN(ip).GetRow(count, t);
s *= coeff;
t += s;
shapeMFN(ip).SetRow(count, t);
}
}
}
void MomentFittingIntRules::Basis2D(const IntegrationPoint& ip, Vector& shape)
{
shape.SetSize(nBasisVolume);
Vector X(2);
X(0) = -1. + 2. * ip.x;
X(1) = -1. + 2. * ip.y;
int count = 0;
for (int c = 0; c <= Order; c++)
{
for (int expo = 0; expo <= c; expo++)
{
shape(count) = pow(X(0), (real_t)(expo))
* pow(X(1), (real_t)(c - expo));
count++;
}
}
}
void MomentFittingIntRules::BasisAD2D(const IntegrationPoint& ip,
DenseMatrix& shape)
{
shape.SetSize(nBasisVolume, 2);
Vector X(2);
X(0) = -1. + 2. * ip.x;
X(1) = -1. + 2. * ip.y;
int count = 0;
for (int c = 0; c <= Order; c++)
{
for (int expo = 0; expo <= c; expo++)
{
shape(count, 0) = .25 * pow(X(0), (real_t)(expo + 1))
* pow(X(1), (real_t)(c - expo))
/ (real_t)(expo + 1);
shape(count, 1) = .25 * pow(X(0), (real_t)(expo))
* pow(X(1), (real_t)(c - expo + 1))
/ (real_t)(c - expo + 1);
count++;
}
}
}
void MomentFittingIntRules::Basis3D(const IntegrationPoint& ip, Vector& shape)
{
shape.SetSize(nBasisVolume);
Vector X(3);
X(0) = -1. + 2. * ip.x;
X(1) = -1. + 2. * ip.y;
X(2) = -1. + 2. * ip.z;
int count = 0;
for (int c = 0; c <= Order; c++)
for (int expo = 0; expo <= c; expo++)
for (int expo2 = 0; expo2 <= c - expo; expo2++)
{
shape(count) = pow(X(0), (real_t)(expo))
* pow(X(1), (real_t)(expo2))
* pow(X(2), (real_t)(c - expo - expo2));
count++;
}
}
void MomentFittingIntRules::BasisAD3D(const IntegrationPoint& ip,
DenseMatrix& shape)
{
shape.SetSize(nBasisVolume, 3);
Vector X(3);
X(0) = -1. + 2. * ip.x;
X(1) = -1. + 2. * ip.y;
X(2) = -1. + 2. * ip.z;
int count = 0;
for (int c = 0; c <= Order; c++)
for (int expo = 0; expo <= c; expo++)
for (int expo2 = 0; expo2 <= c - expo; expo2++)
{
shape(count, 0) = pow(X(0), (real_t)(expo + 1))
* pow(X(1), (real_t)(expo2))
* pow(X(2), (real_t)(c - expo - expo2))
/ (6. * (real_t)(expo + 1));
shape(count, 1) = pow(X(0), (real_t)(expo))
* pow(X(1), (real_t)(expo2 + 1))
* pow(X(2), (real_t)(c - expo - expo2))
/ (6. * (real_t)(expo2 + 1));;
shape(count, 2) = pow(X(0), (real_t)(expo))
* pow(X(1), (real_t)(expo2))
* pow(X(2), (real_t)(c - expo - expo2 + 1))
/ (6. * (real_t)(c - expo + expo2 + 1));;
count++;
}
}
void MomentFittingIntRules::Clear()
{
dim = -1;
nBasis = -1;
nBasisVolume = -1;
delete VolumeSVD;
VolumeSVD = NULL;
FaceIP.DeleteAll();
FaceWeights = 0.;
FaceWeightsComp = 0.;
}
void MomentFittingIntRules::SetOrder(int order)
{
if (order != Order) { Clear(); }
Order = order;
}
void MomentFittingIntRules::GetSurfaceIntegrationRule(ElementTransformation& Tr,
IntegrationRule& result)
{
if (nBasis == -1 || dim != Tr.GetDimension())
{
Clear();
InitSurface(Order, *LvlSet, lsOrder, Tr);
}
if (Tr.GetDimension() == 3)
{
FaceIP.DeleteAll();
FaceWeights = 0.;
FaceWeightsComp = 0.;
}
if (Tr.GetDimension() == 1)
{
ComputeSurfaceWeights1D(Tr);
}
else if (Tr.GetDimension() == 2)
{
ComputeSurfaceWeights2D(Tr);
}
else if (Tr.GetDimension() == 3)
{
ComputeSurfaceWeights3D(Tr);
}
result.SetSize(ir.GetNPoints());
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
result.IntPoint(ip).index = ip;
IntegrationPoint &intp = result.IntPoint(ip);
intp.x = ir.IntPoint(ip).x;
intp.y = ir.IntPoint(ip).y;
intp.z = ir.IntPoint(ip).z;
intp.weight = ir.IntPoint(ip).weight;
}
}
void MomentFittingIntRules::GetVolumeIntegrationRule(ElementTransformation& Tr,
IntegrationRule& result,
const IntegrationRule* sir)
{
if (nBasis == -1 || nBasisVolume == -1 || dim != Tr.GetDimension())
{
Clear();
InitVolume(Order, *LvlSet, lsOrder, Tr);
}
if (Tr.GetDimension() == 3)
{
FaceIP.DeleteAll();
FaceWeights = 0.;
FaceWeightsComp = 0.;
}
IntegrationRule SIR;
if (Tr.GetDimension() == 1)
{
Clear();
InitVolume(Order, *LvlSet, lsOrder, Tr);
}
else if (sir == NULL)
{
Order++;
GetSurfaceIntegrationRule(Tr, SIR);
Order--;
}
else if (sir->GetOrder() - 1 != ir.GetOrder())
{
Order++;
GetSurfaceIntegrationRule(Tr, SIR);
Order--;
}
else { SIR = *sir; }
if (Tr.GetDimension() == 1)
{
ComputeVolumeWeights1D(Tr);
}
else if (Tr.GetDimension() == 2)
{
ComputeVolumeWeights2D(Tr, &SIR);
}
else if (Tr.GetDimension() == 3)
{
ComputeVolumeWeights3D(Tr, &SIR);
}
result.SetSize(ir.GetNPoints());
for (int ip = 0; ip < ir.GetNPoints(); ip++)
{
result.IntPoint(ip).index = ip;
IntegrationPoint &intp = result.IntPoint(ip);
intp.x = ir.IntPoint(ip).x;
intp.y = ir.IntPoint(ip).y;
intp.z = ir.IntPoint(ip).z;
intp.weight = ir.IntPoint(ip).weight;
}
}
void MomentFittingIntRules::GetSurfaceWeights(ElementTransformation& Tr,
const IntegrationRule &sir,
Vector &weights)
{
if (nBasis == -1 || dim != Tr.GetDimension())
{
Clear();
InitSurface(Order, *LvlSet, lsOrder, Tr);
}
weights.SetSize(sir.GetNPoints());
weights = 0.0;
bool computeweights = false;
for (int ip = 0; ip < sir.GetNPoints(); ip++)
{
if (sir.IntPoint(ip).weight != 0.)
{
computeweights = true;
}
}
if (Tr.GetDimension() > 1 && computeweights)
{
int elem = Tr.ElementNo;
const Mesh* mesh = Tr.mesh;
H1_FECollection fec(lsOrder, Tr.GetDimension());
FiniteElementSpace fes(const_cast<Mesh*>(Tr.mesh), &fec);
GridFunction LevelSet(&fes);
LevelSet.ProjectCoefficient(*LvlSet);
IsoparametricTransformation Trafo;
mesh->GetElementTransformation(elem, &Trafo);
const FiniteElement* fe = fes.GetFE(elem);
Vector normal(Tr.GetDimension());
Vector normal2(Tr.GetSpaceDim());
Vector gradi(Tr.GetDimension());
DenseMatrix dshape(fe->GetDof(), Tr.GetDimension());
Array<int> dofs;
fes.GetElementDofs(elem, dofs);
for (int ip = 0; ip < sir.GetNPoints(); ip++)
{
Trafo.SetIntPoint(&(sir.IntPoint(ip)));
LevelSet.GetGradient(Trafo, normal2);
real_t normphys = normal2.Norml2();
normal = 0.;
fe->CalcDShape(sir.IntPoint(ip), dshape);
for (int dof = 0; dof < fe->GetDof(); dof++)
{
dshape.GetRow(dof, gradi);
gradi *= LevelSet(dofs[dof]);
normal += gradi;
}
real_t normref = normal.Norml2();
normal *= (-1. / normal.Norml2());
weights(ip) = normphys / normref;
}
}
}
#endif // MFEM_USE_LAPACK
}