Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e8625cf89 | ||
|
|
3f7a432e74 | ||
|
|
a57fd02a4c | ||
|
|
2ae97ff2da | ||
|
|
2b712207c6 | ||
|
|
62e95fe7bc | ||
|
|
c62681aaf8 | ||
|
|
a25138e611 | ||
|
|
cc3a4475d2 | ||
|
|
8e00ae03e4 | ||
|
|
2930c1477f | ||
|
|
bf3423dcba | ||
|
|
dd23ccddb3 | ||
|
|
3c5dc275a4 | ||
|
|
6604ba702c | ||
|
|
395e75f69a | ||
|
|
1875c34055 | ||
|
|
f58decb421 | ||
|
|
635ac55e77 | ||
|
|
6beb0decd7 | ||
|
|
253aebb734 | ||
|
|
7de36b40bb | ||
|
|
bf54fc6b3f | ||
|
|
fb3b943cf0 | ||
|
|
df3d0b51c2 | ||
|
|
4c4aeaeec3 | ||
|
|
cadb9778a4 | ||
|
|
32bdcb6ef2 | ||
|
|
e74d501ac3 | ||
|
|
fcd2f0ceb2 | ||
|
|
c0879aaf76 | ||
|
|
e024ac0f9c | ||
|
|
b3ee631aa6 | ||
|
|
daf2fdecec | ||
|
|
d3a0d0a181 | ||
|
|
07853b9c62 | ||
|
|
20089dd62b | ||
|
|
a8ebdf16bd | ||
|
|
99372de933 | ||
|
|
70d84e441d | ||
|
|
f6bc0f8988 | ||
|
|
ef54519a29 | ||
|
|
49a31c0cf7 | ||
|
|
ba9b251007 | ||
|
|
5ce2fa9ab9 | ||
|
|
78c93de6ce | ||
|
|
3d4aa157cb | ||
|
|
a7f1c177c5 | ||
|
|
2d4e3cf77e |
+17
-2
@@ -125,6 +125,7 @@ MFEM_USE_GINKGO = NO
|
||||
MFEM_USE_GNUTLS = NO
|
||||
MFEM_USE_NETCDF = NO
|
||||
MFEM_USE_PETSC = NO
|
||||
MFEM_USE_SLEPC = NO
|
||||
MFEM_USE_MPFR = NO
|
||||
MFEM_USE_SIDRE = NO
|
||||
MFEM_USE_CONDUIT = NO
|
||||
@@ -276,6 +277,20 @@ ifeq ($(PETSC_FOUND),YES)
|
||||
-L$(abspath $(PETSC_DIR))/lib -lpetsc $(PETSC_LIB)
|
||||
endif
|
||||
|
||||
SLEPC_DIR := $(MFEM_DIR)/../slepc
|
||||
SLEPC_VARS := $(SLEPC_DIR)/lib/slepc/conf/slepc_variables
|
||||
SLEPC_FOUND := $(if $(wildcard $(SLEPC_VARS)),YES,)
|
||||
SLEPC_INC_VAR = SLEPC_INCLUDE
|
||||
SLEPC_LIB_VAR = SLEPC_EXTERNAL_LIB
|
||||
ifeq ($(SLEPC_FOUND),YES)
|
||||
SLEPC_OPT := $(shell sed -n "s/$(SLEPC_INC_VAR) *= *//p" $(SLEPC_VARS))
|
||||
# Some additional external libraries might be defined in this file
|
||||
-include ${SLEPC_DIR}/${PETSC_ARCH}/lib/slepc/conf/slepcvariables
|
||||
SLEPC_LIB := $(shell sed -n "s/$(SLEPC_LIB_VAR) *= *//p" $(SLEPC_VARS))
|
||||
SLEPC_LIB := -Wl,-rpath,$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib\
|
||||
-L$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib -lslepc $(SLEPC_LIB)
|
||||
endif
|
||||
|
||||
# MPFR library configuration
|
||||
MPFR_OPT =
|
||||
MPFR_LIB = -lmpfr
|
||||
@@ -324,9 +339,9 @@ GSLIB_DIR = @MFEM_DIR@/../gslib/build
|
||||
GSLIB_OPT = -I$(GSLIB_DIR)/include
|
||||
GSLIB_LIB = -L$(GSLIB_DIR)/lib -lgs
|
||||
|
||||
# CUDA library configuration (currently not needed)
|
||||
# CUDA library configuration
|
||||
CUDA_OPT =
|
||||
CUDA_LIB =
|
||||
CUDA_LIB = -lcusparse
|
||||
|
||||
# HIP library configuration (currently not needed)
|
||||
HIP_OPT =
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-square.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// mpirun -np 4 ex9p -m ../data/disc-nurbs.mesh -p 2 -rp 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 3 -rp 2 -dt 0.0025 -tf 9 -vs 20
|
||||
// mpirun -np 4 ex9p -m ../data/periodic-cube.mesh -p 0 -o 2 -rp 1 -dt 0.01 -tf 8
|
||||
// mpirun -np 3 ex9p -m ../data/amr-hex.mesh -p 1 -rs 1 -rp 0 -dt 0.005 -tf 0.5
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex9p -pa
|
||||
|
||||
@@ -176,42 +176,41 @@ static void PADiffusionSetup3D(const int Q1D,
|
||||
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
|
||||
auto C = const_c ? Reshape(c.Read(), 1, 1) : Reshape(c.Read(), NQ, NE);
|
||||
auto D = Reshape(d.Write(), NQ, 6, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
MFEM_FORALL(eq, NE*NQ,
|
||||
{
|
||||
for (int q = 0; q < NQ; ++q)
|
||||
{
|
||||
const double J11 = J(q,0,0,e);
|
||||
const double J21 = J(q,1,0,e);
|
||||
const double J31 = J(q,2,0,e);
|
||||
const double J12 = J(q,0,1,e);
|
||||
const double J22 = J(q,1,1,e);
|
||||
const double J32 = J(q,2,1,e);
|
||||
const double J13 = J(q,0,2,e);
|
||||
const double J23 = J(q,1,2,e);
|
||||
const double J33 = J(q,2,2,e);
|
||||
const double detJ = J11 * (J22 * J33 - J32 * J23) -
|
||||
/* */ J21 * (J12 * J33 - J32 * J13) +
|
||||
/* */ J31 * (J12 * J23 - J22 * J13);
|
||||
const double coeff = const_c ? C(0,0) : C(q,e);
|
||||
const double c_detJ = W[q] * coeff / detJ;
|
||||
// adj(J)
|
||||
const double A11 = (J22 * J33) - (J23 * J32);
|
||||
const double A12 = (J32 * J13) - (J12 * J33);
|
||||
const double A13 = (J12 * J23) - (J22 * J13);
|
||||
const double A21 = (J31 * J23) - (J21 * J33);
|
||||
const double A22 = (J11 * J33) - (J13 * J31);
|
||||
const double A23 = (J21 * J13) - (J11 * J23);
|
||||
const double A31 = (J21 * J32) - (J31 * J22);
|
||||
const double A32 = (J31 * J12) - (J11 * J32);
|
||||
const double A33 = (J11 * J22) - (J12 * J21);
|
||||
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
|
||||
D(q,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
|
||||
D(q,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
|
||||
D(q,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
|
||||
D(q,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
|
||||
D(q,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
|
||||
D(q,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
|
||||
}
|
||||
const int e = eq / NQ;
|
||||
const int q = eq % NQ;
|
||||
const double J11 = J(q,0,0,e);
|
||||
const double J21 = J(q,1,0,e);
|
||||
const double J31 = J(q,2,0,e);
|
||||
const double J12 = J(q,0,1,e);
|
||||
const double J22 = J(q,1,1,e);
|
||||
const double J32 = J(q,2,1,e);
|
||||
const double J13 = J(q,0,2,e);
|
||||
const double J23 = J(q,1,2,e);
|
||||
const double J33 = J(q,2,2,e);
|
||||
const double detJ = J11 * (J22 * J33 - J32 * J23) -
|
||||
/* */ J21 * (J12 * J33 - J32 * J13) +
|
||||
/* */ J31 * (J12 * J23 - J22 * J13);
|
||||
const double coeff = const_c ? C(0,0) : C(q,e);
|
||||
const double c_detJ = W[q] * coeff / detJ;
|
||||
// adj(J)
|
||||
const double A11 = (J22 * J33) - (J23 * J32);
|
||||
const double A12 = (J32 * J13) - (J12 * J33);
|
||||
const double A13 = (J12 * J23) - (J22 * J13);
|
||||
const double A21 = (J31 * J23) - (J21 * J33);
|
||||
const double A22 = (J11 * J33) - (J13 * J31);
|
||||
const double A23 = (J21 * J13) - (J11 * J23);
|
||||
const double A31 = (J21 * J32) - (J31 * J22);
|
||||
const double A32 = (J31 * J12) - (J11 * J32);
|
||||
const double A33 = (J11 * J22) - (J12 * J21);
|
||||
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
|
||||
D(q,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
|
||||
D(q,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
|
||||
D(q,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
|
||||
D(q,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
|
||||
D(q,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
|
||||
D(q,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+23
-11
@@ -25,6 +25,7 @@ namespace mfem
|
||||
|
||||
void MassIntegrator::SetupPA(const FiniteElementSpace &fes, const bool force)
|
||||
{
|
||||
|
||||
// Assuming the same element type
|
||||
fespace = &fes;
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
@@ -51,21 +52,30 @@ void MassIntegrator::SetupPA(const FiniteElementSpace &fes, const bool force)
|
||||
dofs1D = maps->ndof;
|
||||
quad1D = maps->nqpt;
|
||||
pa_data.SetSize(ne*nq, Device::GetDeviceMemoryType());
|
||||
Vector coeff;
|
||||
Vector *coeff{nullptr};
|
||||
bool own_coeff{true};
|
||||
if (Q == nullptr)
|
||||
{
|
||||
coeff.SetSize(1);
|
||||
coeff(0) = 1.0;
|
||||
coeff = new Vector;
|
||||
coeff->SetSize(1);
|
||||
(*coeff)(0) = 1.0;
|
||||
}
|
||||
else if (ConstantCoefficient* cQ = dynamic_cast<ConstantCoefficient*>(Q))
|
||||
{
|
||||
coeff.SetSize(1);
|
||||
coeff(0) = cQ->constant;
|
||||
coeff = new Vector;
|
||||
coeff->SetSize(1);
|
||||
(*coeff)(0) = 1.0;
|
||||
}
|
||||
else if (QuadratureCoefficient* cQ = dynamic_cast<QuadratureCoefficient*>(Q))
|
||||
{
|
||||
coeff = cQ->Data();
|
||||
own_coeff = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
coeff.SetSize(nq * ne);
|
||||
auto C = Reshape(coeff.HostWrite(), nq, ne);
|
||||
coeff = new Vector;
|
||||
coeff->SetSize(nq * ne);
|
||||
auto C = Reshape(coeff->HostWrite(), nq, ne);
|
||||
for (int e = 0; e < ne; ++e)
|
||||
{
|
||||
ElementTransformation& T = *fes.GetElementTransformation(e);
|
||||
@@ -80,11 +90,11 @@ void MassIntegrator::SetupPA(const FiniteElementSpace &fes, const bool force)
|
||||
{
|
||||
const int NE = ne;
|
||||
const int NQ = nq;
|
||||
const bool const_c = coeff.Size() == 1;
|
||||
const bool const_c = coeff->Size() == 1;
|
||||
auto w = ir->GetWeights().Read();
|
||||
auto J = Reshape(geom->J.Read(), NQ,2,2,NE);
|
||||
auto C =
|
||||
const_c ? Reshape(coeff.Read(), 1,1) : Reshape(coeff.Read(), NQ,NE);
|
||||
const_c ? Reshape(coeff->Read(), 1,1) : Reshape(coeff->Read(), NQ,NE);
|
||||
auto v = Reshape(pa_data.Write(), NQ, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
@@ -104,11 +114,11 @@ void MassIntegrator::SetupPA(const FiniteElementSpace &fes, const bool force)
|
||||
{
|
||||
const int NE = ne;
|
||||
const int NQ = nq;
|
||||
const bool const_c = coeff.Size() == 1;
|
||||
const bool const_c = coeff->Size() == 1;
|
||||
auto W = ir->GetWeights().Read();
|
||||
auto J = Reshape(geom->J.Read(), NQ,3,3,NE);
|
||||
auto C =
|
||||
const_c ? Reshape(coeff.Read(), 1,1) : Reshape(coeff.Read(), NQ,NE);
|
||||
const_c ? Reshape(coeff->Read(), 1,1) : Reshape(coeff->Read(), NQ,NE);
|
||||
auto v = Reshape(pa_data.Write(), NQ,NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
@@ -125,6 +135,8 @@ void MassIntegrator::SetupPA(const FiniteElementSpace &fes, const bool force)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (own_coeff) { delete coeff; }
|
||||
}
|
||||
|
||||
void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
|
||||
+109
-19
@@ -12,6 +12,7 @@
|
||||
// Implementation of Coefficient class
|
||||
|
||||
#include "fem.hpp"
|
||||
#include "../linalg/dtensor.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
@@ -21,6 +22,13 @@ namespace mfem
|
||||
|
||||
using namespace std;
|
||||
|
||||
double QuadratureCoefficient::Eval(ElementTransformation & T,
|
||||
const IntegrationPoint & ip)
|
||||
{
|
||||
auto coeff = mfem::Reshape(qData->HostRead(), nip, NE);
|
||||
return coeff(ip.index, T.ElementNo);
|
||||
}
|
||||
|
||||
double PWConstCoefficient::Eval(ElementTransformation & T,
|
||||
const IntegrationPoint & ip)
|
||||
{
|
||||
@@ -416,13 +424,43 @@ double DeterminantCoefficient::Eval(ElementTransformation &T,
|
||||
return ma.Det();
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &A,
|
||||
VectorCoefficient &B,
|
||||
double _alpha, double _beta)
|
||||
: VectorCoefficient(A.GetVDim()), a(&A), b(&B), alpha(_alpha), beta(_beta),
|
||||
va(A.GetVDim())
|
||||
VectorSumCoefficient::VectorSumCoefficient(int dim)
|
||||
: VectorCoefficient(dim),
|
||||
ACoef(NULL), BCoef(NULL),
|
||||
A(dim), B(dim),
|
||||
alphaCoef(NULL), betaCoef(NULL),
|
||||
alpha(1.0), beta(1.0)
|
||||
{
|
||||
MFEM_ASSERT(A.GetVDim() == B.GetVDim(),
|
||||
A = 0.0; B = 0.0;
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &_A,
|
||||
VectorCoefficient &_B,
|
||||
double _alpha, double _beta)
|
||||
: VectorCoefficient(_A.GetVDim()),
|
||||
ACoef(&_A), BCoef(&_B),
|
||||
A(_A.GetVDim()), B(_A.GetVDim()),
|
||||
alphaCoef(NULL), betaCoef(NULL),
|
||||
alpha(_alpha), beta(_beta)
|
||||
{
|
||||
MFEM_ASSERT(_A.GetVDim() == _B.GetVDim(),
|
||||
"VectorSumCoefficient: "
|
||||
"Arguments must have the same dimension.");
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &_A,
|
||||
VectorCoefficient &_B,
|
||||
Coefficient &_alpha,
|
||||
Coefficient &_beta)
|
||||
: VectorCoefficient(_A.GetVDim()),
|
||||
ACoef(&_A), BCoef(&_B),
|
||||
A(_A.GetVDim()),
|
||||
B(_A.GetVDim()),
|
||||
alphaCoef(&_alpha),
|
||||
betaCoef(&_beta),
|
||||
alpha(0.0), beta(0.0)
|
||||
{
|
||||
MFEM_ASSERT(_A.GetVDim() == _B.GetVDim(),
|
||||
"VectorSumCoefficient: "
|
||||
"Arguments must have the same dimension.");
|
||||
}
|
||||
@@ -430,26 +468,47 @@ VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &A,
|
||||
void VectorSumCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
b->Eval(V, T, ip);
|
||||
if ( beta != 1.0 ) { V *= beta; }
|
||||
a->Eval(va, T, ip);
|
||||
V.Add(alpha, va);
|
||||
V.SetSize(A.Size());
|
||||
if ( ACoef) { ACoef->Eval(A, T, ip); }
|
||||
if ( BCoef) { BCoef->Eval(B, T, ip); }
|
||||
if (alphaCoef) { alpha = alphaCoef->Eval(T, ip); }
|
||||
if ( betaCoef) { beta = betaCoef->Eval(T, ip); }
|
||||
add(alpha, A, beta, B, V);
|
||||
}
|
||||
|
||||
ScalarVectorProductCoefficient::ScalarVectorProductCoefficient(
|
||||
double A,
|
||||
VectorCoefficient &B)
|
||||
: VectorCoefficient(B.GetVDim()), aConst(A), a(NULL), b(&B)
|
||||
{}
|
||||
|
||||
ScalarVectorProductCoefficient::ScalarVectorProductCoefficient(
|
||||
Coefficient &A,
|
||||
VectorCoefficient &B)
|
||||
: VectorCoefficient(B.GetVDim()), a(&A), b(&B)
|
||||
: VectorCoefficient(B.GetVDim()), aConst(0.0), a(&A), b(&B)
|
||||
{}
|
||||
|
||||
void ScalarVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
double sa = a->Eval(T, ip);
|
||||
double sa = (a == NULL) ? aConst : a->Eval(T, ip);
|
||||
b->Eval(V, T, ip);
|
||||
V *= sa;
|
||||
}
|
||||
|
||||
NormalizedVectorCoefficient::NormalizedVectorCoefficient(VectorCoefficient &A,
|
||||
double _tol)
|
||||
: VectorCoefficient(A.GetVDim()), a(&A), tol(_tol)
|
||||
{}
|
||||
|
||||
void NormalizedVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(V, T, ip);
|
||||
double nv = V.Norml2();
|
||||
V *= (nv > tol) ? (1.0/nv) : 0.0;
|
||||
}
|
||||
|
||||
VectorCrossProductCoefficient::VectorCrossProductCoefficient(
|
||||
VectorCoefficient &A,
|
||||
VectorCoefficient &B)
|
||||
@@ -471,17 +530,18 @@ void VectorCrossProductCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
V[2] = va[0] * vb[1] - va[1] * vb[0];
|
||||
}
|
||||
|
||||
MatVecCoefficient::MatVecCoefficient(MatrixCoefficient &A,
|
||||
VectorCoefficient &B)
|
||||
MatrixVectorProductCoefficient::MatrixVectorProductCoefficient(
|
||||
MatrixCoefficient &A, VectorCoefficient &B)
|
||||
: VectorCoefficient(A.GetHeight()), a(&A), b(&B),
|
||||
ma(A.GetHeight(), A.GetWidth()), vb(B.GetVDim())
|
||||
{
|
||||
MFEM_ASSERT(A.GetWidth() == B.GetVDim(),
|
||||
"MatVecCoefficient: Arguments have incompatible dimensions.");
|
||||
"MatrixVectorProductCoefficient: "
|
||||
"Arguments have incompatible dimensions.");
|
||||
}
|
||||
|
||||
void MatVecCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
void MatrixVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(ma, T, ip);
|
||||
b->Eval(vb, T, ip);
|
||||
@@ -517,17 +577,23 @@ void MatrixSumCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
M.Add(alpha, ma);
|
||||
}
|
||||
|
||||
ScalarMatrixProductCoefficient::ScalarMatrixProductCoefficient(
|
||||
double A,
|
||||
MatrixCoefficient &B)
|
||||
: MatrixCoefficient(B.GetHeight(), B.GetWidth()), aConst(A), a(NULL), b(&B)
|
||||
{}
|
||||
|
||||
ScalarMatrixProductCoefficient::ScalarMatrixProductCoefficient(
|
||||
Coefficient &A,
|
||||
MatrixCoefficient &B)
|
||||
: MatrixCoefficient(B.GetHeight(), B.GetWidth()), a(&A), b(&B)
|
||||
: MatrixCoefficient(B.GetHeight(), B.GetWidth()), aConst(0.0), a(&A), b(&B)
|
||||
{}
|
||||
|
||||
void ScalarMatrixProductCoefficient::Eval(DenseMatrix &M,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
double sa = a->Eval(T, ip);
|
||||
double sa = (a == NULL) ? aConst : a->Eval(T, ip);
|
||||
b->Eval(M, T, ip);
|
||||
M *= sa;
|
||||
}
|
||||
@@ -581,6 +647,30 @@ void OuterProductCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
CrossCrossCoefficient::CrossCrossCoefficient(Coefficient &A,
|
||||
VectorCoefficient &K)
|
||||
: MatrixCoefficient(K.GetVDim(), K.GetVDim()), aConst(0.0), a(&A), k(&K),
|
||||
vk(K.GetVDim())
|
||||
{}
|
||||
|
||||
void CrossCrossCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
k->Eval(vk, T, ip);
|
||||
M.SetSize(vk.Size(), vk.Size());
|
||||
M = 0.0;
|
||||
double k2 = vk*vk;
|
||||
for (int i=0; i<vk.Size(); i++)
|
||||
{
|
||||
M(i, i) = k2;
|
||||
for (int j=0; j<vk.Size(); j++)
|
||||
{
|
||||
M(i, j) -= vk[i] * vk[j];
|
||||
}
|
||||
}
|
||||
M *= ((a == NULL ) ? aConst : a->Eval(T, ip) );
|
||||
}
|
||||
|
||||
double LpNormLoop(double p, Coefficient &coeff, Mesh &mesh,
|
||||
const IntegrationRule *irs[])
|
||||
{
|
||||
|
||||
+418
-21
@@ -84,6 +84,33 @@ public:
|
||||
{ return (constant); }
|
||||
};
|
||||
|
||||
|
||||
/// class for quadrature coefficient
|
||||
class QuadratureCoefficient : public Coefficient
|
||||
{
|
||||
|
||||
private:
|
||||
const int nip;
|
||||
const int NE;
|
||||
public:
|
||||
Vector *qData{nullptr};
|
||||
|
||||
//Set external data
|
||||
QuadratureCoefficient(Vector *Data, int in_nip, int in_NE)
|
||||
: qData(Data), nip(in_nip), NE(in_NE)
|
||||
{ }
|
||||
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
Vector *Data()
|
||||
{
|
||||
return qData;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// class for piecewise constant coefficient
|
||||
/** @brief A piecewise constant coefficient with the constants keyed
|
||||
off the element attribute numbers. */
|
||||
class PWConstCoefficient : public Coefficient
|
||||
@@ -852,12 +879,14 @@ public:
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Coefficients based on sums and products of other coefficients
|
||||
|
||||
/// Scalar coefficient defined as the sum of two scalar coefficients
|
||||
/// Coefficients based on sums, products, or other functions of coefficients.
|
||||
///@{
|
||||
/** Scalar coefficient defined as the linear combination of two scalar
|
||||
coefficients or a scalar and a scalar coefficient */
|
||||
class SumCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
double aConst;
|
||||
Coefficient * a;
|
||||
Coefficient * b;
|
||||
|
||||
@@ -865,33 +894,141 @@ private:
|
||||
double beta;
|
||||
|
||||
public:
|
||||
/// Construct with the two coefficients. Result is _alpha * A + _beta * B.
|
||||
/// Constructor with one coefficient. Result is _alpha * A + _beta * B
|
||||
SumCoefficient(double A, Coefficient &B,
|
||||
double _alpha = 1.0, double _beta = 1.0)
|
||||
: aConst(A), a(NULL), b(&B), alpha(_alpha), beta(_beta) { }
|
||||
|
||||
/// Constructor with two coefficients. Result is _alpha * A + _beta * B.
|
||||
SumCoefficient(Coefficient &A, Coefficient &B,
|
||||
double _alpha = 1.0, double _beta = 1.0)
|
||||
: a(&A), b(&B), alpha(_alpha), beta(_beta) { }
|
||||
: aConst(0.0), a(&A), b(&B), alpha(_alpha), beta(_beta) { }
|
||||
|
||||
/// Reset the first term in the linear combination as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the first term in the linear combination
|
||||
double GetAConst() const { return aConst; }
|
||||
|
||||
/// Reset the first term in the linear combination
|
||||
void SetACoef(Coefficient &A) { a = &A; }
|
||||
/// Return the first term in the linear combination
|
||||
Coefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the second term in the linear combination
|
||||
void SetBCoef(Coefficient &B) { b = &B; }
|
||||
/// Return the second term in the linear combination
|
||||
Coefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Reset the factor in front of the first term in the linear combination
|
||||
void SetAlpha(double _alpha) { alpha = _alpha; }
|
||||
/// Return the factor in front of the first term in the linear combination
|
||||
double GetAlpha() const { return alpha; }
|
||||
|
||||
/// Reset the factor in front of the second term in the linear combination
|
||||
void SetBeta(double _beta) { beta = _beta; }
|
||||
/// Return the factor in front of the second term in the linear combination
|
||||
double GetBeta() const { return beta; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{ return alpha * a->Eval(T, ip) + beta * b->Eval(T, ip); }
|
||||
{
|
||||
return alpha * ((a == NULL ) ? aConst : a->Eval(T, ip) )
|
||||
+ beta * b->Eval(T, ip);
|
||||
}
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as the product of two scalar coefficients
|
||||
/** Scalar coefficient defined as the product of two scalar coefficients or
|
||||
a scalar and a scalar coefficient. */
|
||||
class ProductCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
double aConst;
|
||||
Coefficient * a;
|
||||
Coefficient * b;
|
||||
|
||||
public:
|
||||
/// Construct with the two coefficients. Result is A * B.
|
||||
/// Constructor with one coefficient. Result is A * B.
|
||||
ProductCoefficient(double A, Coefficient &B)
|
||||
: aConst(A), a(NULL), b(&B) { }
|
||||
|
||||
/// Constructor with two coefficients. Result is A * B.
|
||||
ProductCoefficient(Coefficient &A, Coefficient &B)
|
||||
: a(&A), b(&B) { }
|
||||
: aConst(0.0), a(&A), b(&B) { }
|
||||
|
||||
/// Reset the first term in the product as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the first term in the product
|
||||
double GetAConst() const { return aConst; }
|
||||
|
||||
/// Reset the first term in the product
|
||||
void SetACoef(Coefficient &A) { a = &A; }
|
||||
/// Return the first term in the product
|
||||
Coefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the second term in the product
|
||||
void SetBCoef(Coefficient &B) { b = &B; }
|
||||
/// Return the second term in the product
|
||||
Coefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{ return a->Eval(T, ip) * b->Eval(T, ip); }
|
||||
{ return ((a == NULL ) ? aConst : a->Eval(T, ip) ) * b->Eval(T, ip); }
|
||||
};
|
||||
|
||||
/** Scalar coefficient defined as the ratio of two scalars where one or both
|
||||
scalars are scalar coefficients. */
|
||||
class RatioCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
double aConst;
|
||||
double bConst;
|
||||
Coefficient * a;
|
||||
Coefficient * b;
|
||||
|
||||
public:
|
||||
/** Initialize a coefficient which returns A / B where @a A is a
|
||||
constant and @a B is a scalar coefficient */
|
||||
RatioCoefficient(double A, Coefficient &B)
|
||||
: aConst(A), bConst(1.0), a(NULL), b(&B) { }
|
||||
/** Initialize a coefficient which returns A / B where @a A and @a B are both
|
||||
scalar coefficients */
|
||||
RatioCoefficient(Coefficient &A, Coefficient &B)
|
||||
: aConst(0.0), bConst(1.0), a(&A), b(&B) { }
|
||||
/** Initialize a coefficient which returns A / B where @a A is a
|
||||
scalar coefficient and @a B is a constant */
|
||||
RatioCoefficient(Coefficient &A, double B)
|
||||
: aConst(0.0), bConst(B), a(&A), b(NULL) { }
|
||||
|
||||
/// Reset the numerator in the ratio as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the numerator of the ratio
|
||||
double GetAConst() const { return aConst; }
|
||||
|
||||
/// Reset the denominator in the ratio as a constant
|
||||
void SetBConst(double B) { b = NULL; bConst = B; }
|
||||
/// Return the denominator of the ratio
|
||||
double GetBConst() const { return bConst; }
|
||||
|
||||
/// Reset the numerator in the ratio
|
||||
void SetACoef(Coefficient &A) { a = &A; }
|
||||
/// Return the numerator of the ratio
|
||||
Coefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the denominator in the ratio
|
||||
void SetBCoef(Coefficient &B) { b = &B; }
|
||||
/// Return the denominator of the ratio
|
||||
Coefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the coefficient
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
double den = (b == NULL ) ? bConst : b->Eval(T, ip);
|
||||
MFEM_ASSERT(den != 0.0, "Division by zero in RatioCoefficient");
|
||||
return ((a == NULL ) ? aConst : a->Eval(T, ip) ) / den;
|
||||
}
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as a scalar raised to a power
|
||||
@@ -907,6 +1044,16 @@ public:
|
||||
PowerCoefficient(Coefficient &A, double _p)
|
||||
: a(&A), p(_p) { }
|
||||
|
||||
/// Reset the base coefficient
|
||||
void SetACoef(Coefficient &A) { a = &A; }
|
||||
/// Return the base coefficient
|
||||
Coefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the exponent
|
||||
void SetExponent(double _p) { p = _p; }
|
||||
/// Return the exponent
|
||||
double GetExponent() const { return p; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
@@ -927,6 +1074,16 @@ public:
|
||||
/// Construct with the two vector coefficients. Result is \f$ A \cdot B \f$.
|
||||
InnerProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Reset the first vector in the inner product
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the first vector coefficient in the inner product
|
||||
VectorCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the second vector in the inner product
|
||||
void SetBCoef(VectorCoefficient &B) { b = &B; }
|
||||
/// Return the second vector coefficient in the inner product
|
||||
VectorCoefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -943,9 +1100,19 @@ private:
|
||||
mutable Vector vb;
|
||||
|
||||
public:
|
||||
/// Construct with the two vector coefficients. Result is \f$ A_x B_y - A_y * B_x; \f$.
|
||||
/// Constructor with two vector coefficients. Result is \f$ A_x B_y - A_y * B_x; \f$.
|
||||
VectorRotProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Reset the first vector in the product
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the first vector of the product
|
||||
VectorCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the second vector in the product
|
||||
void SetBCoef(VectorCoefficient &B) { b = &B; }
|
||||
/// Return the second vector of the product
|
||||
VectorCoefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -963,17 +1130,28 @@ public:
|
||||
/// Construct with the matrix.
|
||||
DeterminantCoefficient(MatrixCoefficient &A);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Evaluate the determinant coefficient at @a ip.
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Vector coefficient defined as the sum of two vector coefficients
|
||||
/// Vector coefficient defined as the linear combination of two vectors
|
||||
class VectorSumCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
VectorCoefficient * a;
|
||||
VectorCoefficient * b;
|
||||
VectorCoefficient * ACoef;
|
||||
VectorCoefficient * BCoef;
|
||||
|
||||
Vector A;
|
||||
Vector B;
|
||||
|
||||
Coefficient * alphaCoef;
|
||||
Coefficient * betaCoef;
|
||||
|
||||
double alpha;
|
||||
double beta;
|
||||
@@ -981,10 +1159,60 @@ private:
|
||||
mutable Vector va;
|
||||
|
||||
public:
|
||||
/// Construct with the two vector coefficients. Result is _alpha * A + _beta * B.
|
||||
/** Constructor with no coefficients.
|
||||
To be used with the various "Set" methods */
|
||||
VectorSumCoefficient(int dim);
|
||||
|
||||
/** Constructor with two vector coefficients.
|
||||
Result is _alpha * A + _beta * B */
|
||||
VectorSumCoefficient(VectorCoefficient &A, VectorCoefficient &B,
|
||||
double _alpha = 1.0, double _beta = 1.0);
|
||||
|
||||
/** Constructor with scalar coefficients.
|
||||
Result is _alpha * _A + _beta * _B */
|
||||
VectorSumCoefficient(VectorCoefficient &_A, VectorCoefficient &_B,
|
||||
Coefficient &_alpha, Coefficient &_beta);
|
||||
|
||||
/// Reset the first vector coefficient
|
||||
void SetACoef(VectorCoefficient &A) { ACoef = &A; }
|
||||
/// Return the first vector coefficient
|
||||
VectorCoefficient * GetACoef() const { return ACoef; }
|
||||
|
||||
/// Reset the second vector coefficient
|
||||
void SetBCoef(VectorCoefficient &B) { BCoef = &B; }
|
||||
/// Return the second vector coefficient
|
||||
VectorCoefficient * GetBCoef() const { return BCoef; }
|
||||
|
||||
/// Reset the factor in front of the first vector coefficient
|
||||
void SetAlphaCoef(Coefficient &A) { alphaCoef = &A; }
|
||||
/// Return the factor in front of the first vector coefficient
|
||||
Coefficient * GetAlphaCoef() const { return alphaCoef; }
|
||||
|
||||
/// Reset the factor in front of the second vector coefficient
|
||||
void SetBetaCoef(Coefficient &B) { betaCoef = &B; }
|
||||
/// Return the factor in front of the second vector coefficient
|
||||
Coefficient * GetBetaCoef() const { return betaCoef; }
|
||||
|
||||
/// Reset the first vector as a constant
|
||||
void SetA(const Vector &_A) { A = _A; ACoef = NULL; }
|
||||
/// Return the first vector constant
|
||||
const Vector & GetA() const { return A; }
|
||||
|
||||
/// Reset the second vector as a constant
|
||||
void SetB(const Vector &_B) { B = _B; BCoef = NULL; }
|
||||
/// Return the second vector constant
|
||||
const Vector & GetB() const { return B; }
|
||||
|
||||
/// Reset the factor in front of the first vector coefficient as a constant
|
||||
void SetAlpha(double _alpha) { alpha = _alpha; alphaCoef = NULL; }
|
||||
/// Return the factor in front of the first vector coefficient
|
||||
double GetAlpha() const { return alpha; }
|
||||
|
||||
/// Reset the factor in front of the second vector coefficient as a constant
|
||||
void SetBeta(double _beta) { beta = _beta; betaCoef = NULL; }
|
||||
/// Return the factor in front of the second vector coefficient
|
||||
double GetBeta() const { return beta; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -995,13 +1223,60 @@ public:
|
||||
class ScalarVectorProductCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
double aConst;
|
||||
Coefficient * a;
|
||||
VectorCoefficient * b;
|
||||
|
||||
public:
|
||||
/// Construct with the two coefficients. Result is A * B.
|
||||
/// Constructor with constant and vector coefficient. Result is A * B.
|
||||
ScalarVectorProductCoefficient(double A, VectorCoefficient &B);
|
||||
|
||||
/// Constructor with two coefficients. Result is A * B.
|
||||
ScalarVectorProductCoefficient(Coefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Reset the scalar factor as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the scalar factor
|
||||
double GetAConst() const { return aConst; }
|
||||
|
||||
/// Reset the scalar factor
|
||||
void SetACoef(Coefficient &A) { a = &A; }
|
||||
/// Return the scalar factor
|
||||
Coefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the vector factor
|
||||
void SetBCoef(VectorCoefficient &B) { b = &B; }
|
||||
/// Return the vector factor
|
||||
VectorCoefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
using VectorCoefficient::Eval;
|
||||
};
|
||||
|
||||
/// Vector coefficient defined as a normalized vector field (returns v/|v|)
|
||||
class NormalizedVectorCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
VectorCoefficient * a;
|
||||
|
||||
double tol;
|
||||
|
||||
public:
|
||||
/** @brief Return a vector normalized to a length of one
|
||||
|
||||
This class evaluates the vector coefficient @a A and, if |A| > @a tol,
|
||||
returns the normalized vector A / |A|. If |A| <= @a tol, the zero
|
||||
vector is returned.
|
||||
*/
|
||||
NormalizedVectorCoefficient(VectorCoefficient &A, double tol = 1e-6);
|
||||
|
||||
/// Reset the vector coefficient
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the vector coefficient
|
||||
VectorCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -1022,6 +1297,16 @@ public:
|
||||
/// Construct with the two coefficients. Result is A x B.
|
||||
VectorCrossProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Reset the first term in the product
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the first term in the product
|
||||
VectorCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the second term in the product
|
||||
void SetBCoef(VectorCoefficient &B) { b = &B; }
|
||||
/// Return the second term in the product
|
||||
VectorCoefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -1030,7 +1315,7 @@ public:
|
||||
|
||||
/** @brief Vector coefficient defined as a product of a matrix coefficient and
|
||||
a vector coefficient. */
|
||||
class MatVecCoefficient : public VectorCoefficient
|
||||
class MatrixVectorProductCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
MatrixCoefficient * a;
|
||||
@@ -1040,8 +1325,18 @@ private:
|
||||
mutable Vector vb;
|
||||
|
||||
public:
|
||||
/// Construct with the two coefficients. Result is A*B.
|
||||
MatVecCoefficient(MatrixCoefficient &A, VectorCoefficient &B);
|
||||
/// Constructor with two coefficients. Result is A*B.
|
||||
MatrixVectorProductCoefficient(MatrixCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the vector coefficient
|
||||
void SetBCoef(VectorCoefficient &B) { b = &B; }
|
||||
/// Return the vector coefficient
|
||||
VectorCoefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the vector coefficient at @a ip.
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
@@ -1049,6 +1344,9 @@ public:
|
||||
using VectorCoefficient::Eval;
|
||||
};
|
||||
|
||||
/// Convenient alias for the MatrixVectorProductCoefficient
|
||||
typedef MatrixVectorProductCoefficient MatVecCoefficient;
|
||||
|
||||
/// Constant matrix coefficient defined as the identity of dimension d
|
||||
class IdentityMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
@@ -1065,7 +1363,7 @@ public:
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Matrix coefficient defined as the sum of two matrix coefficients.
|
||||
/// Matrix coefficient defined as the linear combination of two matrices
|
||||
class MatrixSumCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
@@ -1082,6 +1380,26 @@ public:
|
||||
MatrixSumCoefficient(MatrixCoefficient &A, MatrixCoefficient &B,
|
||||
double _alpha = 1.0, double _beta = 1.0);
|
||||
|
||||
/// Reset the first matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the first matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the second matrix coefficient
|
||||
void SetBCoef(MatrixCoefficient &B) { b = &B; }
|
||||
/// Return the second matrix coefficient
|
||||
MatrixCoefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Reset the factor in front of the first matrix coefficient
|
||||
void SetAlpha(double _alpha) { alpha = _alpha; }
|
||||
/// Return the factor in front of the first matrix coefficient
|
||||
double GetAlpha() const { return alpha; }
|
||||
|
||||
/// Reset the factor in front of the second matrix coefficient
|
||||
void SetBeta(double _beta) { beta = _beta; }
|
||||
/// Return the factor in front of the second matrix coefficient
|
||||
double GetBeta() const { return beta; }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -1092,13 +1410,32 @@ public:
|
||||
class ScalarMatrixProductCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
double aConst;
|
||||
Coefficient * a;
|
||||
MatrixCoefficient * b;
|
||||
|
||||
public:
|
||||
/// Construct with the two coefficients. Result is A*B.
|
||||
/// Constructor with one coefficient. Result is A*B.
|
||||
ScalarMatrixProductCoefficient(double A, MatrixCoefficient &B);
|
||||
|
||||
/// Constructor with two coefficients. Result is A*B.
|
||||
ScalarMatrixProductCoefficient(Coefficient &A, MatrixCoefficient &B);
|
||||
|
||||
/// Reset the scalar factor as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the scalar factor
|
||||
double GetAConst() const { return aConst; }
|
||||
|
||||
/// Reset the scalar factor
|
||||
void SetACoef(Coefficient &A) { a = &A; }
|
||||
/// Return the scalar factor
|
||||
Coefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the matrix factor
|
||||
void SetBCoef(MatrixCoefficient &B) { b = &B; }
|
||||
/// Return the matrix factor
|
||||
MatrixCoefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -1114,6 +1451,11 @@ public:
|
||||
/// Construct with the matrix coefficient. Result is \f$ A^T \f$.
|
||||
TransposeMatrixCoefficient(MatrixCoefficient &A);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -1129,6 +1471,11 @@ public:
|
||||
/// Construct with the matrix coefficient. Result is \f$ A^{-1} \f$.
|
||||
InverseMatrixCoefficient(MatrixCoefficient &A);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
@@ -1148,11 +1495,61 @@ public:
|
||||
/// Construct with two vector coefficients. Result is \f$ A B^T \f$.
|
||||
OuterProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
|
||||
|
||||
/// Reset the first vector in the outer product
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
/// Return the first vector coefficient in the outer product
|
||||
VectorCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the second vector in the outer product
|
||||
void SetBCoef(VectorCoefficient &B) { b = &B; }
|
||||
/// Return the second vector coefficient in the outer product
|
||||
VectorCoefficient * GetBCoef() const { return b; }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/** @brief Matrix coefficient defined as -a k x k x, for a vector k and scalar a
|
||||
|
||||
This coefficient returns \f$a * (|k|^2 I - k \otimes k)\f$, where I is
|
||||
the identity matrix and \f$\otimes\f$ indicates the outer product. This
|
||||
can be evaluated for vectors of any dimension but in three
|
||||
dimensions it corresponds to computing the cross product with k twice.
|
||||
*/
|
||||
class CrossCrossCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
double aConst;
|
||||
Coefficient * a;
|
||||
VectorCoefficient * k;
|
||||
|
||||
mutable Vector vk;
|
||||
|
||||
public:
|
||||
CrossCrossCoefficient(double A, VectorCoefficient &K);
|
||||
CrossCrossCoefficient(Coefficient &A, VectorCoefficient &K);
|
||||
|
||||
/// Reset the scalar factor as a constant
|
||||
void SetAConst(double A) { a = NULL; aConst = A; }
|
||||
/// Return the scalar factor
|
||||
double GetAConst() const { return aConst; }
|
||||
|
||||
/// Reset the scalar factor
|
||||
void SetACoef(Coefficient &A) { a = &A; }
|
||||
/// Return the scalar factor
|
||||
Coefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the vector factor
|
||||
void SetKCoef(VectorCoefficient &K) { k = &K; }
|
||||
/// Return the vector factor
|
||||
VectorCoefficient * GetKCoef() const { return k; }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
///@}
|
||||
|
||||
class QuadratureFunction;
|
||||
|
||||
|
||||
@@ -624,4 +624,77 @@ void FaceElementTransformations::Transform(const DenseMatrix &matrix,
|
||||
IsoparametricTransformation::Transform(matrix, result);
|
||||
}
|
||||
|
||||
double FaceElementTransformations::CheckConsistency(int print_level,
|
||||
std::ostream &out)
|
||||
{
|
||||
// Check that the face vertices are mapped to the same physical location
|
||||
// when using the following three transformations:
|
||||
// - the face transformation, *this
|
||||
// - Loc1 + Elem1
|
||||
// - Loc2 + Elem2, if present.
|
||||
|
||||
const bool have_face = (mask & 16);
|
||||
const bool have_el1 = (mask & 1) && (mask & 4);
|
||||
const bool have_el2 = (mask & 2) && (mask & 8) && (Elem2No >= 0);
|
||||
if (int(have_face) + int(have_el1) + int(have_el2) < 2)
|
||||
{
|
||||
// need at least two different transformations to perform a check
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const IntegrationRule &v_ir = *Geometries.GetVertices(GetGeometryType());
|
||||
|
||||
double max_dist = 0.0;
|
||||
Vector dist(v_ir.GetNPoints());
|
||||
DenseMatrix coords_base, coords_el;
|
||||
IntegrationRule v_eir(v_ir.GetNPoints());
|
||||
if (have_face)
|
||||
{
|
||||
Transform(v_ir, coords_base);
|
||||
if (print_level > 0)
|
||||
{
|
||||
out << "\nface vertex coordinates (from face transform):\n"
|
||||
<< "----------------------------------------------\n";
|
||||
coords_base.PrintT(out, coords_base.Height());
|
||||
}
|
||||
}
|
||||
if (have_el1)
|
||||
{
|
||||
Loc1.Transform(v_ir, v_eir);
|
||||
Elem1->Transform(v_eir, coords_el);
|
||||
if (print_level > 0)
|
||||
{
|
||||
out << "\nface vertex coordinates (from element 1 transform):\n"
|
||||
<< "---------------------------------------------------\n";
|
||||
coords_el.PrintT(out, coords_el.Height());
|
||||
}
|
||||
if (have_face)
|
||||
{
|
||||
coords_el -= coords_base;
|
||||
coords_el.Norm2(dist);
|
||||
max_dist = std::max(max_dist, dist.Normlinf());
|
||||
}
|
||||
else
|
||||
{
|
||||
coords_base = coords_el;
|
||||
}
|
||||
}
|
||||
if (have_el2)
|
||||
{
|
||||
Loc2.Transform(v_ir, v_eir);
|
||||
Elem2->Transform(v_eir, coords_el);
|
||||
if (print_level > 0)
|
||||
{
|
||||
out << "\nface vertex coordinates (from element 2 transform):\n"
|
||||
<< "---------------------------------------------------\n";
|
||||
coords_el.PrintT(out, coords_el.Height());
|
||||
}
|
||||
coords_el -= coords_base;
|
||||
coords_el.Norm2(dist);
|
||||
max_dist = std::max(max_dist, dist.Normlinf());
|
||||
}
|
||||
|
||||
return max_dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -492,6 +492,26 @@ public:
|
||||
ElementTransformation & GetElement2Transformation();
|
||||
IntegrationPointTransformation & GetIntPoint1Transformation();
|
||||
IntegrationPointTransformation & GetIntPoint2Transformation();
|
||||
|
||||
/** @brief Check for self-consistency: compares the result of mapping the
|
||||
reference face vertices to physical coordinates using the three
|
||||
transformations: face, element 1, and element 2.
|
||||
|
||||
@param[in] print_level If set to a positive number, print the physical
|
||||
coordinates of the face vertices computed through
|
||||
all available transformations: face, element 1,
|
||||
and/or element 2.
|
||||
@param[in,out] out The output stream to use for printing.
|
||||
|
||||
@returns A maximal distance between physical coordinates of face vertices
|
||||
that should coincide. A successful check should return a small
|
||||
number relative to the mesh extents. If less than 2 of the three
|
||||
transformations are set, returns 0.
|
||||
|
||||
@warning This check will generally fail on periodic boundary faces.
|
||||
*/
|
||||
double CheckConsistency(int print_level = 0,
|
||||
std::ostream &out = mfem::out);
|
||||
};
|
||||
|
||||
/** Elem1(Loc1(x)) = Face(x) = Elem2(Loc2(x))
|
||||
|
||||
+2
-2
@@ -1031,13 +1031,13 @@ int GridFunction::GetFaceVectorValues(
|
||||
}
|
||||
if (di == 0)
|
||||
{
|
||||
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 4);
|
||||
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 5);
|
||||
Transf->Loc1.Transform(ir, eir);
|
||||
GetVectorValues(*Transf->Elem1, eir, vals, &tr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 8);
|
||||
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 10);
|
||||
Transf->Loc2.Transform(ir, eir);
|
||||
GetVectorValues(*Transf->Elem2, eir, vals, &tr);
|
||||
}
|
||||
|
||||
+166
-7
@@ -63,6 +63,53 @@ void DomainLFIntegrator::AssembleDeltaElementVect(
|
||||
elvect *= delta->EvalDelta(Trans, Trans.GetIntPoint());
|
||||
}
|
||||
|
||||
void DomainLFGradIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
int dof = el.GetDof();
|
||||
int spaceDim = Tr.GetSpaceDim();
|
||||
|
||||
dshape.SetSize(dof, spaceDim);
|
||||
|
||||
elvect.SetSize(dof);
|
||||
elvect = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = 2 * el.GetOrder();
|
||||
ir = &IntRules.Get(el.GetGeomType(), intorder);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
Tr.SetIntPoint(&ip);
|
||||
el.CalcPhysDShape(Tr, dshape);
|
||||
|
||||
Q.Eval(Qvec, Tr, ip);
|
||||
Qvec *= ip.weight * Tr.Weight();
|
||||
|
||||
dshape.AddMult(Qvec, elvect);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainLFGradIntegrator::AssembleDeltaElementVect(
|
||||
const FiniteElement &fe, ElementTransformation &Trans, Vector &elvect)
|
||||
{
|
||||
MFEM_ASSERT(vec_delta != NULL,"coefficient must be VectorDeltaCoefficient");
|
||||
int dof = fe.GetDof();
|
||||
int spaceDim = Trans.GetSpaceDim();
|
||||
|
||||
dshape.SetSize(dof, spaceDim);
|
||||
fe.CalcPhysDShape(Trans, dshape);
|
||||
|
||||
vec_delta->EvalDelta(Qvec, Trans, Trans.GetIntPoint());
|
||||
|
||||
elvect.SetSize(dof);
|
||||
dshape.Mult(Qvec, elvect);
|
||||
}
|
||||
|
||||
void BoundaryLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
@@ -255,7 +302,6 @@ void VectorDomainLFIntegrator::AssembleDeltaElementVect(
|
||||
MultVWt(shape, Qvec, elvec_as_mat);
|
||||
}
|
||||
|
||||
|
||||
void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
@@ -332,7 +378,6 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VectorFEDomainLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
@@ -362,7 +407,6 @@ void VectorFEDomainLFIntegrator::AssembleRHSElementVect(
|
||||
|
||||
QF.Eval (vec, Tr, ip);
|
||||
vec *= ip.weight * Tr.Weight();
|
||||
|
||||
vshape.AddMult (vec, elvect);
|
||||
}
|
||||
}
|
||||
@@ -383,6 +427,125 @@ void VectorFEDomainLFIntegrator::AssembleDeltaElementVect(
|
||||
vshape.Mult(vec, elvect);
|
||||
}
|
||||
|
||||
void VectorFEDomainLFCurlIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
int dof = el.GetDof();
|
||||
int spaceDim = Tr.GetSpaceDim();
|
||||
int n=(spaceDim == 3)? spaceDim : 1;
|
||||
curlshape.SetSize(dof,n);
|
||||
vec.SetSize(n);
|
||||
|
||||
elvect.SetSize(dof);
|
||||
elvect = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = 2*el.GetOrder();
|
||||
ir = &IntRules.Get(el.GetGeomType(), intorder);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
Tr.SetIntPoint (&ip);
|
||||
el.CalcPhysCurlShape(Tr, curlshape);
|
||||
|
||||
switch (spaceDim)
|
||||
{
|
||||
case 3:
|
||||
MFEM_VERIFY(QF, "VectorFunctionCoefficient not provided");
|
||||
QF->Eval(vec, Tr, ip);
|
||||
break;
|
||||
case 2:
|
||||
MFEM_VERIFY(Q, "FunctionCoefficient (Scalar) not provided");
|
||||
vec[0] = Q->Eval(Tr, ip);
|
||||
break;
|
||||
default:
|
||||
break; // This should be unreachable
|
||||
}
|
||||
vec *= ip.weight * Tr.Weight();
|
||||
curlshape.AddMult (vec, elvect);
|
||||
}
|
||||
}
|
||||
|
||||
void VectorFEDomainLFCurlIntegrator::AssembleDeltaElementVect(
|
||||
const FiniteElement &fe, ElementTransformation &Trans, Vector &elvect)
|
||||
{
|
||||
int spaceDim = Trans.GetSpaceDim();
|
||||
switch (spaceDim)
|
||||
{
|
||||
case 3:
|
||||
MFEM_ASSERT(vec_delta != NULL,
|
||||
"coefficient must be VectorDeltaCoefficient");
|
||||
break;
|
||||
case 2:
|
||||
MFEM_ASSERT(delta != NULL,
|
||||
"coefficient must be DeltaCoefficient");
|
||||
break;
|
||||
default:
|
||||
break; // This should be unreachable
|
||||
}
|
||||
int dof = fe.GetDof();
|
||||
int n=(spaceDim == 3)? spaceDim : 1;
|
||||
curlshape.SetSize(dof, n);
|
||||
elvect.SetSize(dof);
|
||||
fe.CalcPhysCurlShape(Trans, curlshape);
|
||||
|
||||
switch (spaceDim)
|
||||
{
|
||||
case 3:
|
||||
vec_delta->EvalDelta(vec, Trans, Trans.GetIntPoint());
|
||||
curlshape.Mult(vec, elvect);
|
||||
break;
|
||||
case 2:
|
||||
curlshape.GetColumn(0,elvect);
|
||||
elvect *= delta->EvalDelta(Trans, Trans.GetIntPoint());
|
||||
break;
|
||||
default:
|
||||
break; // This should be unreachable
|
||||
}
|
||||
}
|
||||
|
||||
void VectorFEDomainLFDivIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
int dof = el.GetDof();
|
||||
|
||||
divshape.SetSize(dof); // vector of size dof
|
||||
elvect.SetSize(dof);
|
||||
elvect = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = 2 * el.GetOrder();
|
||||
ir = &IntRules.Get(el.GetGeomType(), intorder);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
Tr.SetIntPoint (&ip);
|
||||
double val = Tr.Weight() * Q.Eval(Tr, ip);
|
||||
el.CalcPhysDivShape(Tr, divshape);
|
||||
|
||||
add(elvect, ip.weight * val, divshape, elvect);
|
||||
}
|
||||
}
|
||||
|
||||
void VectorFEDomainLFDivIntegrator::AssembleDeltaElementVect(
|
||||
const FiniteElement &fe, ElementTransformation &Trans, Vector &elvect)
|
||||
{
|
||||
MFEM_ASSERT(delta != NULL, "coefficient must be DeltaCoefficient");
|
||||
elvect.SetSize(fe.GetDof());
|
||||
fe.CalcPhysDivShape(Trans, elvect);
|
||||
elvect *= delta->EvalDelta(Trans, Trans.GetIntPoint());
|
||||
}
|
||||
|
||||
void VectorBoundaryFluxLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
@@ -448,7 +611,6 @@ void VectorFEBoundaryFluxLFIntegrator::AssembleRHSElementVect(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VectorFEBoundaryTangentLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
@@ -483,7 +645,6 @@ void VectorFEBoundaryTangentLFIntegrator::AssembleRHSElementVect(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BoundaryFlowIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
@@ -548,7 +709,6 @@ void BoundaryFlowIntegrator::AssembleRHSElementVect(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DGDirichletLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
@@ -637,7 +797,6 @@ void DGDirichletLFIntegrator::AssembleRHSElementVect(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DGElasticityDirichletLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
|
||||
+78
-1
@@ -119,6 +119,33 @@ public:
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
/// Class for domain integrator L(v) := (f, grad v)
|
||||
class DomainLFGradIntegrator : public DeltaLFIntegrator
|
||||
{
|
||||
private:
|
||||
Vector shape, Qvec;
|
||||
VectorCoefficient &Q;
|
||||
DenseMatrix dshape;
|
||||
|
||||
public:
|
||||
/// Constructs the domain integrator (Q, grad v)
|
||||
DomainLFGradIntegrator(VectorCoefficient &QF)
|
||||
: DeltaLFIntegrator(QF), Q(QF) { }
|
||||
|
||||
/** Given a particular Finite Element and a transformation (Tr)
|
||||
computes the element right hand side element vector, elvect. */
|
||||
virtual void AssembleRHSElementVect(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
Vector &elvect);
|
||||
|
||||
virtual void AssembleDeltaElementVect(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
Vector &elvect);
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
|
||||
/// Class for boundary integration L(v) := (g, v)
|
||||
class BoundaryLFIntegrator : public LinearFormIntegrator
|
||||
{
|
||||
@@ -252,6 +279,56 @@ public:
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
/// \f$ (Q, curl v)_{\Omega} \f$ for Nedelec Elements)
|
||||
class VectorFEDomainLFCurlIntegrator : public DeltaLFIntegrator
|
||||
{
|
||||
private:
|
||||
VectorCoefficient *QF=nullptr;
|
||||
Coefficient *Q=nullptr;
|
||||
DenseMatrix curlshape;
|
||||
Vector vec;
|
||||
|
||||
public:
|
||||
/// Constructs the domain integrator (Q, curl v)
|
||||
VectorFEDomainLFCurlIntegrator(VectorCoefficient &F)
|
||||
: DeltaLFIntegrator(F), QF(&F) { }
|
||||
VectorFEDomainLFCurlIntegrator(Coefficient &F)
|
||||
: DeltaLFIntegrator(F), Q(&F) { }
|
||||
|
||||
virtual void AssembleRHSElementVect(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
Vector &elvect);
|
||||
|
||||
virtual void AssembleDeltaElementVect(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
Vector &elvect);
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
/// \f$ (Q, div v)_{\Omega} \f$ for RT Elements)
|
||||
class VectorFEDomainLFDivIntegrator : public DeltaLFIntegrator
|
||||
{
|
||||
private:
|
||||
Vector divshape;
|
||||
Coefficient &Q;
|
||||
public:
|
||||
/// Constructs the domain integrator (Q, div v)
|
||||
VectorFEDomainLFDivIntegrator(Coefficient &QF)
|
||||
: DeltaLFIntegrator(QF), Q(QF) { }
|
||||
|
||||
/** Given a particular Finite Element and a transformation (Tr)
|
||||
computes the element right hand side element vector, elvect. */
|
||||
virtual void AssembleRHSElementVect(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
Vector &elvect);
|
||||
|
||||
virtual void AssembleDeltaElementVect(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
Vector &elvect);
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
/** \f$ (f, v \cdot n)_{\partial\Omega} \f$ for vector test function
|
||||
v=(v1,...,vn) where all vi are in the same scalar FE space and f is a
|
||||
@@ -283,7 +360,7 @@ class VectorFEBoundaryFluxLFIntegrator : public LinearFormIntegrator
|
||||
private:
|
||||
Coefficient *F;
|
||||
Vector shape;
|
||||
int oa, ob; // these contol the quadrature order, see DomainLFIntegrator
|
||||
int oa, ob; // these control the quadrature order, see DomainLFIntegrator
|
||||
|
||||
public:
|
||||
VectorFEBoundaryFluxLFIntegrator(int a = 1, int b = -1)
|
||||
|
||||
+7
-4
@@ -1172,7 +1172,7 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
// Works for NC mesh where 'i' is an index returned by
|
||||
// ParMesh::GetSharedFace() such that i >= Mesh::GetNumFaces(), i.e. 'i' is
|
||||
// the index of a ghost.
|
||||
// the index of a ghost face.
|
||||
MFEM_ASSERT(Nonconforming() && i >= pmesh->GetNumFaces(), "");
|
||||
int el1, el2, inf1, inf2;
|
||||
pmesh->GetFaceElements(i, &el1, &el2);
|
||||
@@ -1212,11 +1212,14 @@ const FiniteElement *ParFiniteElementSpace::GetFaceNbrFE(int i) const
|
||||
|
||||
const FiniteElement *ParFiniteElementSpace::GetFaceNbrFaceFE(int i) const
|
||||
{
|
||||
// Works for NC mesh where 'i' is an index returned by
|
||||
// ParMesh::GetSharedFace() such that i >= Mesh::GetNumFaces(), i.e. 'i' is
|
||||
// the index of a ghost face.
|
||||
// Works in tandem with GetFaceNbrFaceVDofs() defined above.
|
||||
|
||||
MFEM_ASSERT(Nonconforming() && !NURBSext, "");
|
||||
Geometry::Type geom = (pmesh->Dimension() == 2) ?
|
||||
Geometry::SEGMENT : Geometry::SQUARE;
|
||||
return fec->FiniteElementForGeometry(geom);
|
||||
Geometry::Type face_geom = pmesh->GetFaceGeometryType(i);
|
||||
return fec->FiniteElementForGeometry(face_geom);
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::Lose_Dof_TrueDof_Matrix()
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ void ParGridFunction::ExchangeFaceNbrData()
|
||||
ParMesh *pmesh = pfes->GetParMesh();
|
||||
|
||||
face_nbr_data.SetSize(pfes->GetFaceNbrVSize());
|
||||
Vector send_data(pfes->send_face_nbr_ldof.Size_of_connections());
|
||||
send_data.SetSize(pfes->send_face_nbr_ldof.Size_of_connections());
|
||||
|
||||
int *send_offset = pfes->send_face_nbr_ldof.GetI();
|
||||
const int *d_send_ldof = mfem::Read(pfes->send_face_nbr_ldof.GetJMemory(),
|
||||
|
||||
@@ -38,6 +38,11 @@ protected:
|
||||
initialized by ExchangeFaceNbrData(). */
|
||||
Vector face_nbr_data;
|
||||
|
||||
/** @brief Vector used as an MPI buffer to send face-neighbor data
|
||||
in ExchangeFaceNbrData() to neighboring processors. */
|
||||
//TODO: Use temporary memory to avoid CUDA malloc allocation cost.
|
||||
Vector send_data;
|
||||
|
||||
void ProjectBdrCoefficient(Coefficient *coeff[], VectorCoefficient *vcoeff,
|
||||
Array<int> &attr);
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ void QuadratureInterpolator::Eval3D(
|
||||
const int nq = maps.nqpt;
|
||||
const int ND = T_ND ? T_ND : nd;
|
||||
const int NQ = T_NQ ? T_NQ : nq;
|
||||
const int NMAX = NQ > ND ? NQ : ND;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
MFEM_VERIFY(ND <= MAX_ND3D, "");
|
||||
MFEM_VERIFY(NQ <= MAX_NQ3D, "");
|
||||
@@ -160,22 +161,24 @@ void QuadratureInterpolator::Eval3D(
|
||||
auto val = Reshape(q_val.Write(), NQ, VDIM, NE);
|
||||
auto der = Reshape(q_der.Write(), NQ, VDIM, 3, NE);
|
||||
auto det = Reshape(q_det.Write(), NQ, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
MFEM_FORALL_2D(e, NE, NMAX, 1, 1,
|
||||
{
|
||||
const int ND = T_ND ? T_ND : nd;
|
||||
const int NQ = T_NQ ? T_NQ : nq;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
constexpr int max_ND = T_ND ? T_ND : MAX_ND3D;
|
||||
constexpr int max_VDIM = T_VDIM ? T_VDIM : MAX_VDIM3D;
|
||||
double s_E[max_VDIM*max_ND];
|
||||
for (int d = 0; d < ND; d++)
|
||||
MFEM_SHARED double s_E[max_VDIM*max_ND];
|
||||
MFEM_FOREACH_THREAD(d, x, ND)
|
||||
{
|
||||
for (int c = 0; c < VDIM; c++)
|
||||
{
|
||||
s_E[c+d*VDIM] = E(d,c,e);
|
||||
}
|
||||
}
|
||||
for (int q = 0; q < NQ; ++q)
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(q, x, NQ)
|
||||
{
|
||||
if (eval_flags & VALUES)
|
||||
{
|
||||
|
||||
@@ -495,8 +495,9 @@ void FaceQuadratureInterpolator::Mult(
|
||||
}
|
||||
}
|
||||
|
||||
void FaceQuadratureInterpolator::Values(
|
||||
const Vector &e_vec, Vector &q_val) const
|
||||
|
||||
void FaceQuadratureInterpolator::Values(const Vector &e_vec,
|
||||
Vector &q_val) const
|
||||
{
|
||||
Vector q_der, q_det, q_nor;
|
||||
Mult(e_vec, VALUES, q_val, q_der, q_det, q_nor);
|
||||
|
||||
@@ -1058,6 +1058,7 @@ void DiscreteAdaptTC::SetDiscreteTargetBase(const GridFunction &tspec_)
|
||||
// make a copy of tspec->tspec_temp, increase its size, and
|
||||
// copy data from tspec_temp -> tspec, then add new entries
|
||||
Vector tspec_temp = tspec;
|
||||
tspec.UseDevice(true);
|
||||
tspec.SetSize(ncomp*dof_cnt);
|
||||
|
||||
for (int i = 0; i < tspec_temp.Size(); i++)
|
||||
@@ -1214,6 +1215,7 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
|
||||
Array<int> dofs;
|
||||
DenseMatrix D_rho(dim), Q_phi(dim), R_theta(dim);
|
||||
tspec_fesv->GetElementVDofs(e_id, dofs);
|
||||
tspec.UseDevice(true);
|
||||
tspec.GetSubVector(dofs, tspec_vals);
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
|
||||
+5
-2
@@ -33,10 +33,11 @@ void AdvectorCG::ComputeAtNewPosition(const Vector &new_nodes,
|
||||
const int pnt_cnt = new_field.Size()/ncomp;
|
||||
|
||||
new_field = field0;
|
||||
|
||||
new_field.HostReadWrite();
|
||||
Vector new_field_temp;
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
{
|
||||
Vector new_field_temp(new_field.GetData()+i*pnt_cnt, pnt_cnt);
|
||||
new_field_temp.MakeRef(new_field, i*pnt_cnt, pnt_cnt);
|
||||
ComputeAtNewPositionScalar(new_nodes, new_field_temp);
|
||||
}
|
||||
|
||||
@@ -94,6 +95,7 @@ void AdvectorCG::ComputeAtNewPositionScalar(const Vector &new_nodes,
|
||||
double v_max = 0.0;
|
||||
const int s = new_field.Size();
|
||||
|
||||
u.HostReadWrite();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
double vel = 0.;
|
||||
@@ -149,6 +151,7 @@ void AdvectorCG::ComputeAtNewPositionScalar(const Vector &new_nodes,
|
||||
#endif
|
||||
|
||||
// Trim the overshoots and undershoots.
|
||||
new_field.HostReadWrite();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
if (new_field(i) < glob_minv) { new_field(i) = glob_minv; }
|
||||
|
||||
+35
-15
@@ -61,15 +61,11 @@ Device Device::device_singleton;
|
||||
bool Device::device_env = false;
|
||||
bool Device::mem_host_env = false;
|
||||
bool Device::mem_device_env = false;
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
bool Device::use_umpire = true;
|
||||
#endif
|
||||
|
||||
Device::Device() : mode(Device::SEQUENTIAL),
|
||||
backends(Backend::CPU),
|
||||
destroy_mm(false),
|
||||
mpi_gpu_aware(false),
|
||||
host_mem_type(MemoryType::HOST),
|
||||
host_mem_class(MemoryClass::HOST),
|
||||
device_mem_type(MemoryType::HOST),
|
||||
device_mem_class(MemoryClass::HOST)
|
||||
Device::Device()
|
||||
{
|
||||
if (getenv("MFEM_MEMORY") && !mem_host_env && !mem_device_env)
|
||||
{
|
||||
@@ -135,7 +131,7 @@ Device::Device() : mode(Device::SEQUENTIAL),
|
||||
{
|
||||
MFEM_ABORT("Unknown memory backend!");
|
||||
}
|
||||
mm.Configure(host_mem_type, device_mem_type);
|
||||
mm.Configure(host_mem_type, device_mem_type, device_mem_type);
|
||||
}
|
||||
|
||||
if (getenv("MFEM_DEVICE"))
|
||||
@@ -165,6 +161,8 @@ Device::~Device()
|
||||
Get().host_mem_class = MemoryClass::HOST;
|
||||
Get().device_mem_type = MemoryType::HOST;
|
||||
Get().device_mem_class = MemoryClass::HOST;
|
||||
Get().device_temp_mem_type = MemoryType::HOST;
|
||||
Get().device_temp_mem_class = MemoryClass::HOST;
|
||||
}
|
||||
|
||||
void Device::Configure(const std::string &device, const int dev)
|
||||
@@ -260,6 +258,10 @@ void Device::Print(std::ostream &out)
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
out << ',' << MemoryTypeName[static_cast<int>(device_mem_type)];
|
||||
if (device_temp_mem_type != device_mem_type)
|
||||
{
|
||||
out << ',' << MemoryTypeName[static_cast<int>(device_temp_mem_type)];
|
||||
}
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
@@ -272,7 +274,8 @@ void Device::UpdateMemoryTypeAndClass()
|
||||
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
// If MFEM has been compiled with Umpire support, use it as the default
|
||||
if (!mem_host_env) { host_mem_type = MemoryType::HOST_UMPIRE; }
|
||||
// TODO TMS: temporary
|
||||
//if (!mem_host_env && use_umpire) { host_mem_type = MemoryType::HOST_UMPIRE; }
|
||||
#endif
|
||||
|
||||
// Enable the device memory type
|
||||
@@ -296,11 +299,16 @@ void Device::UpdateMemoryTypeAndClass()
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef MFEM_USE_UMPIRE
|
||||
device_mem_type = MemoryType::DEVICE;
|
||||
#else
|
||||
device_mem_type = MemoryType::DEVICE_UMPIRE;
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
if (use_umpire)
|
||||
{
|
||||
device_mem_type = MemoryType::DEVICE_UMPIRE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
device_mem_type = MemoryType::DEVICE;
|
||||
}
|
||||
}
|
||||
}
|
||||
device_mem_class = MemoryClass::DEVICE;
|
||||
@@ -320,8 +328,20 @@ void Device::UpdateMemoryTypeAndClass()
|
||||
device_mem_type = MemoryType::DEVICE_DEBUG;
|
||||
}
|
||||
|
||||
// Setup device_temp_mem_{type,class}
|
||||
switch (device_mem_type)
|
||||
{
|
||||
case MemoryType::DEVICE_UMPIRE:
|
||||
device_temp_mem_type = MemoryType::DEVICE_TEMP_UMPIRE;
|
||||
break;
|
||||
default:
|
||||
device_temp_mem_type = device_mem_type;
|
||||
break;
|
||||
}
|
||||
device_temp_mem_class = device_mem_class;
|
||||
|
||||
// Update the memory manager with the new settings
|
||||
mm.Configure(host_mem_type, device_mem_type);
|
||||
mm.Configure(host_mem_type, device_mem_type, device_temp_mem_type);
|
||||
}
|
||||
|
||||
void Device::Enable()
|
||||
|
||||
+28
-16
@@ -119,20 +119,26 @@ private:
|
||||
|
||||
static bool device_env, mem_host_env, mem_device_env;
|
||||
static Device device_singleton;
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
static bool use_umpire;
|
||||
#endif
|
||||
|
||||
MODES mode;
|
||||
MODES mode{Device::SEQUENTIAL};
|
||||
int dev = 0; ///< Device ID of the configured device.
|
||||
int ngpu = -1; ///< Number of detected devices; -1: not initialized.
|
||||
unsigned long backends; ///< Bitwise-OR of all configured backends.
|
||||
unsigned long backends{Backend::CPU}; ///< Bitwise-OR of all configured backends.
|
||||
/// Set to true during configuration, except in 'device_singleton'.
|
||||
bool destroy_mm;
|
||||
bool mpi_gpu_aware;
|
||||
bool destroy_mm{false};
|
||||
bool mpi_gpu_aware{false};
|
||||
|
||||
MemoryType host_mem_type; ///< Current Host MemoryType
|
||||
MemoryClass host_mem_class; ///< Current Host MemoryClass
|
||||
MemoryType host_mem_type{MemoryType::HOST}; ///< Current Host MemoryType
|
||||
MemoryClass host_mem_class{MemoryClass::HOST}; ///< Current Host MemoryClass
|
||||
|
||||
MemoryType device_mem_type; ///< Current Device MemoryType
|
||||
MemoryClass device_mem_class; ///< Current Device MemoryClass
|
||||
MemoryType device_mem_type{MemoryType::HOST}; ///< Current Device MemoryType
|
||||
MemoryClass device_mem_class{MemoryClass::HOST}; ///< Current Device MemoryClass
|
||||
|
||||
MemoryType device_temp_mem_type{MemoryType::HOST}; ///< Current Device MemoryType
|
||||
MemoryClass device_temp_mem_class{MemoryClass::HOST}; ///< Current Device MemoryClass
|
||||
|
||||
char *device_option = NULL;
|
||||
Device(Device const&);
|
||||
@@ -173,14 +179,6 @@ public:
|
||||
@note This object should be destroyed after all other MFEM objects that
|
||||
use the Device are destroyed. */
|
||||
Device(const std::string &device, const int dev = 0)
|
||||
: mode(Device::SEQUENTIAL),
|
||||
backends(Backend::CPU),
|
||||
destroy_mm(false),
|
||||
mpi_gpu_aware(false),
|
||||
host_mem_type(MemoryType::HOST),
|
||||
host_mem_class(MemoryClass::HOST),
|
||||
device_mem_type(MemoryType::HOST),
|
||||
device_mem_class(MemoryClass::HOST)
|
||||
{ Configure(device, dev); }
|
||||
|
||||
/// Destructor.
|
||||
@@ -260,10 +258,24 @@ public:
|
||||
/** @deprecated Use GetDeviceMemoryClass() instead. */
|
||||
static inline MemoryClass GetMemoryClass() { return Get().device_mem_class; }
|
||||
|
||||
/** @brief Get the current Device Temporary MemoryType. This is the MemoryType used by
|
||||
MFEM classes when allocating temporary memory to be used with device kernels.
|
||||
*/
|
||||
static inline MemoryType GetDeviceTempMemoryType() { return Get().device_temp_mem_type; }
|
||||
|
||||
/** @brief Get the current Device Temporary MemoryClass. This is the MemoryClass used
|
||||
by MFEM device kernels when they need to access temporary Memory objects. */
|
||||
static inline MemoryClass GetDeviceTempMemoryClass() { return Get().device_temp_mem_class; }
|
||||
|
||||
static void SetGPUAwareMPI(const bool force = true)
|
||||
{ Get().mpi_gpu_aware = force; }
|
||||
|
||||
static bool GetGPUAwareMPI() { return Get().mpi_gpu_aware; }
|
||||
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
static bool UseUmpire() { return Get().use_umpire; }
|
||||
static void UseUmpire(bool use) { Get().use_umpire = use; }
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
+118
-44
@@ -67,15 +67,19 @@ MemoryType MemoryManager::GetDualMemoryType_(MemoryType mt)
|
||||
{
|
||||
switch (mt)
|
||||
{
|
||||
case MemoryType::HOST: return MemoryType::DEVICE;
|
||||
// TODO TMS: temporary
|
||||
case MemoryType::HOST: return MemoryType::DEVICE_UMPIRE;
|
||||
case MemoryType::HOST_32: return MemoryType::DEVICE;
|
||||
case MemoryType::HOST_64: return MemoryType::DEVICE;
|
||||
case MemoryType::HOST_DEBUG: return MemoryType::DEVICE_DEBUG;
|
||||
case MemoryType::HOST_UMPIRE: return MemoryType::DEVICE_UMPIRE;
|
||||
//case MemoryType::HOST_UMPIRE: return MemoryType::DEVICE_UMPIRE;
|
||||
case MemoryType::MANAGED: return MemoryType::MANAGED;
|
||||
case MemoryType::DEVICE: return MemoryType::HOST;
|
||||
case MemoryType::DEVICE_DEBUG: return MemoryType::HOST_DEBUG;
|
||||
case MemoryType::DEVICE_UMPIRE: return MemoryType::HOST_UMPIRE;
|
||||
//case MemoryType::DEVICE_UMPIRE: return MemoryType::HOST_UMPIRE;
|
||||
case MemoryType::DEVICE_UMPIRE: return MemoryType::HOST;
|
||||
//case MemoryType::DEVICE_TEMP_UMPIRE: return MemoryType::HOST_UMPIRE;
|
||||
case MemoryType::DEVICE_TEMP_UMPIRE: return MemoryType::HOST;
|
||||
default: mfem_error("Unknown memory type!");
|
||||
}
|
||||
MFEM_VERIFY(false,"");
|
||||
@@ -88,6 +92,9 @@ static void MFEM_VERIFY_TYPES(const MemoryType h_mt, const MemoryType d_mt)
|
||||
MFEM_ASSERT(IsDeviceMemory(d_mt),"");
|
||||
const bool sync =
|
||||
(h_mt == MemoryType::HOST_UMPIRE && d_mt == MemoryType::DEVICE_UMPIRE) ||
|
||||
(h_mt == MemoryType::HOST_UMPIRE && d_mt == MemoryType::DEVICE_TEMP_UMPIRE) ||
|
||||
(h_mt == MemoryType::HOST && d_mt == MemoryType::DEVICE_UMPIRE) ||
|
||||
(h_mt == MemoryType::HOST && d_mt == MemoryType::DEVICE_TEMP_UMPIRE) ||
|
||||
(h_mt == MemoryType::HOST_DEBUG && d_mt == MemoryType::DEVICE_DEBUG) ||
|
||||
(h_mt == MemoryType::MANAGED && d_mt == MemoryType::MANAGED) ||
|
||||
(h_mt == MemoryType::HOST_64 && d_mt == MemoryType::DEVICE) ||
|
||||
@@ -461,48 +468,96 @@ public:
|
||||
#ifndef MFEM_USE_UMPIRE
|
||||
class UmpireHostMemorySpace : public NoHostMemorySpace { };
|
||||
class UmpireDeviceMemorySpace : public NoDeviceMemorySpace { };
|
||||
class UmpireDeviceTempMemorySpace : public NoDeviceMemorySpace { };
|
||||
#else
|
||||
|
||||
// TODO TMS: replace with um.hasAllocatorId(int) when it exists
|
||||
bool UmpireHasId(const umpire::ResourceManager & rm, int id)
|
||||
{
|
||||
const auto & ids = rm.getAllocatorIds();
|
||||
return std::find(ids.begin(), ids.end(), id) != ids.end();
|
||||
}
|
||||
|
||||
/// The Umpire host memory space
|
||||
class UmpireHostMemorySpace : public HostMemorySpace
|
||||
{
|
||||
private:
|
||||
const char *name;
|
||||
umpire::ResourceManager &rm;
|
||||
umpire::Allocator h_allocator;
|
||||
umpire::strategy::AllocationStrategy *strat;
|
||||
bool owns_allocator{false};
|
||||
public:
|
||||
~UmpireHostMemorySpace() { h_allocator.release(); }
|
||||
UmpireHostMemorySpace():
|
||||
HostMemorySpace(),
|
||||
name(mm.GetUmpireAllocatorHostName()),
|
||||
rm(umpire::ResourceManager::getInstance()),
|
||||
h_allocator(rm.isAllocator(name)? rm.getAllocator(name):
|
||||
rm.makeAllocator<umpire::strategy::DynamicPool>
|
||||
(name, rm.getAllocator("HOST"))),
|
||||
strat(h_allocator.getAllocationStrategy()) { }
|
||||
// TODO: this only releases unused memory
|
||||
~UmpireHostMemorySpace() { if (owns_allocator) { h_allocator.release(); } }
|
||||
UmpireHostMemorySpace(): HostMemorySpace(),
|
||||
rm(umpire::ResourceManager::getInstance())
|
||||
{
|
||||
const int id = MemoryManager::GetUmpireHostAllocatorId();
|
||||
if (!UmpireHasId(rm, id))
|
||||
{
|
||||
h_allocator = rm.makeAllocator<umpire::strategy::DynamicPool>("MFEM_HOST",
|
||||
rm.getAllocator("HOST"));
|
||||
owns_allocator = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
h_allocator = rm.getAllocator(id);
|
||||
}
|
||||
MemoryManager::SetUmpireHostAllocatorId(id);
|
||||
}
|
||||
void Alloc(void **ptr, size_t bytes) { *ptr = h_allocator.allocate(bytes); }
|
||||
void Dealloc(void *ptr) { h_allocator.deallocate(ptr); }
|
||||
void Insert(void *ptr, size_t bytes)
|
||||
{ rm.registerAllocation(ptr, {ptr, bytes, strat}); }
|
||||
{ mfem_error("UmpireHostMemorySpace::Insert is unsupported"); }
|
||||
};
|
||||
|
||||
/// The Umpire device memory space
|
||||
#ifdef MFEM_USE_CUDA
|
||||
class UmpireDeviceMemorySpace : public DeviceMemorySpace
|
||||
class UmpireDeviceMemorySpaceImpl : public DeviceMemorySpace
|
||||
{
|
||||
public:
|
||||
enum class AllocatorType { TEMPORARY, PERMANENT };
|
||||
private:
|
||||
const char *name;
|
||||
umpire::ResourceManager &rm;
|
||||
umpire::Allocator d_allocator;
|
||||
bool owns_allocator{false};
|
||||
|
||||
int SetupAllocator(int possible_id, const char * allocator_name)
|
||||
{
|
||||
if (!UmpireHasId(rm, possible_id))
|
||||
{
|
||||
d_allocator = rm.makeAllocator<umpire::strategy::DynamicPool>(allocator_name,
|
||||
rm.getAllocator("DEVICE"));
|
||||
owns_allocator = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
d_allocator = rm.getAllocator(possible_id);
|
||||
}
|
||||
|
||||
return d_allocator.getId();
|
||||
}
|
||||
public:
|
||||
~UmpireDeviceMemorySpace() { d_allocator.release(); }
|
||||
UmpireDeviceMemorySpace():
|
||||
DeviceMemorySpace(),
|
||||
name(mm.GetUmpireAllocatorDeviceName()),
|
||||
rm(umpire::ResourceManager::getInstance()),
|
||||
d_allocator(rm.isAllocator(name)? rm.getAllocator(name):
|
||||
rm.makeAllocator<umpire::strategy::DynamicPool>
|
||||
(name, rm.getAllocator("DEVICE"))) { }
|
||||
// TODO: this only releases unused memory
|
||||
~UmpireDeviceMemorySpaceImpl() { if (owns_allocator) { d_allocator.release(); } }
|
||||
UmpireDeviceMemorySpaceImpl(AllocatorType t): DeviceMemorySpace(),
|
||||
rm(umpire::ResourceManager::getInstance())
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case AllocatorType::PERMANENT:
|
||||
MemoryManager::SetUmpireDeviceAllocatorId(SetupAllocator(
|
||||
MemoryManager::GetUmpireDeviceAllocatorId(),
|
||||
"MFEM_DEVICE"));
|
||||
break;
|
||||
case AllocatorType::TEMPORARY:
|
||||
MemoryManager::SetUmpireDeviceTempAllocatorId(SetupAllocator(
|
||||
MemoryManager::GetUmpireDeviceTempAllocatorId(),
|
||||
"MFEM_DEVICE_TEMPORARY"));
|
||||
break;
|
||||
default:
|
||||
mfem_error("Unknown Umpire AllocatorType");
|
||||
}
|
||||
}
|
||||
void Alloc(Memory &base) { base.d_ptr = d_allocator.allocate(base.bytes); }
|
||||
void Dealloc(Memory &base) { d_allocator.deallocate(base.d_ptr); }
|
||||
void *HtoD(void *dst, const void *src, size_t bytes)
|
||||
@@ -536,8 +591,23 @@ public:
|
||||
//rm.copy(dst, const_cast<void*>(src), bytes); return dst;
|
||||
}
|
||||
};
|
||||
|
||||
class UmpireDeviceMemorySpace : public UmpireDeviceMemorySpaceImpl
|
||||
{
|
||||
public:
|
||||
UmpireDeviceMemorySpace() : UmpireDeviceMemorySpaceImpl(
|
||||
AllocatorType::PERMANENT) {}
|
||||
};
|
||||
|
||||
class UmpireDeviceTempMemorySpace : public UmpireDeviceMemorySpaceImpl
|
||||
{
|
||||
public:
|
||||
UmpireDeviceTempMemorySpace() : UmpireDeviceMemorySpaceImpl(
|
||||
AllocatorType::TEMPORARY) {}
|
||||
};
|
||||
#else
|
||||
class UmpireDeviceMemorySpace : public NoDeviceMemorySpace { };
|
||||
class UmpireDeviceTempMemorySpace : public NoDeviceMemorySpace { };
|
||||
#endif // MFEM_USE_CUDA
|
||||
#endif // MFEM_USE_UMPIRE
|
||||
|
||||
@@ -568,7 +638,7 @@ public:
|
||||
host[static_cast<int>(MT::HOST_64)] = new Aligned64HostMemorySpace();
|
||||
// HOST_DEBUG is delayed, as it reroutes signals
|
||||
host[static_cast<int>(MT::HOST_DEBUG)] = nullptr;
|
||||
host[static_cast<int>(MT::HOST_UMPIRE)] = new UmpireHostMemorySpace();
|
||||
host[static_cast<int>(MT::HOST_UMPIRE)] = nullptr;
|
||||
host[static_cast<int>(MT::MANAGED)] = new UvmHostMemorySpace();
|
||||
|
||||
// Filling the device memory backends, shifting with the device size
|
||||
@@ -610,8 +680,12 @@ public:
|
||||
private:
|
||||
HostMemorySpace* NewHostCtrl(const MemoryType mt)
|
||||
{
|
||||
if (mt == MT::HOST_DEBUG) { return new MmuHostMemorySpace(); }
|
||||
MFEM_ABORT("Unknown host memory controller!");
|
||||
switch (mt)
|
||||
{
|
||||
case MT::HOST_DEBUG: return new MmuHostMemorySpace();
|
||||
case MT::HOST_UMPIRE: return new UmpireHostMemorySpace();
|
||||
default: MFEM_ABORT("Unknown host memory controller!");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -620,6 +694,7 @@ private:
|
||||
switch (mt)
|
||||
{
|
||||
case MT::DEVICE_UMPIRE: return new UmpireDeviceMemorySpace();
|
||||
case MT::DEVICE_TEMP_UMPIRE: return new UmpireDeviceTempMemorySpace();
|
||||
case MT::DEVICE_DEBUG: return new MmuDeviceMemorySpace();
|
||||
case MT::DEVICE:
|
||||
{
|
||||
@@ -760,7 +835,7 @@ bool MemoryManager::MemoryClassCheck_(MemoryClass mc, void *h_ptr,
|
||||
const bool known = mm.IsKnown(h_ptr);
|
||||
const bool alias = mm.IsAlias(h_ptr);
|
||||
const bool check = known || ((flags & Mem::ALIAS) && alias);
|
||||
MFEM_VERIFY(check,"");
|
||||
MFEM_VERIFY(check,"Unknown host pointer: " << h_ptr);
|
||||
const internal::Memory &mem =
|
||||
(flags & Mem::ALIAS) ?
|
||||
*maps->aliases.at(h_ptr).mem : maps->memories.at(h_ptr);
|
||||
@@ -783,6 +858,7 @@ bool MemoryManager::MemoryClassCheck_(MemoryClass mc, void *h_ptr,
|
||||
MFEM_VERIFY(d_mt == MemoryType::DEVICE ||
|
||||
d_mt == MemoryType::DEVICE_DEBUG ||
|
||||
d_mt == MemoryType::DEVICE_UMPIRE ||
|
||||
d_mt == MemoryType::DEVICE_TEMP_UMPIRE ||
|
||||
d_mt == MemoryType::MANAGED,"");
|
||||
return true;
|
||||
}
|
||||
@@ -1262,22 +1338,15 @@ MemoryManager::MemoryManager() { Init(); }
|
||||
MemoryManager::~MemoryManager() { if (exists) { Destroy(); } }
|
||||
|
||||
void MemoryManager::Configure(const MemoryType host_mt,
|
||||
const MemoryType device_mt)
|
||||
const MemoryType device_mt,
|
||||
const MemoryType device_tmt)
|
||||
{
|
||||
Init();
|
||||
host_mem_type = host_mt;
|
||||
device_mem_type = device_mt;
|
||||
device_temp_mem_type = device_tmt;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
void MemoryManager::SetUmpireAllocatorNames(const char *h_name,
|
||||
const char *d_name)
|
||||
{
|
||||
h_umpire_name = h_name;
|
||||
d_umpire_name = d_name;
|
||||
}
|
||||
#endif
|
||||
|
||||
void MemoryManager::Destroy()
|
||||
{
|
||||
MFEM_VERIFY(exists, "MemoryManager has already been destroyed!");
|
||||
@@ -1381,12 +1450,14 @@ MemoryManager mm;
|
||||
bool MemoryManager::exists = false;
|
||||
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
const char* MemoryManager::h_umpire_name = "HOST";
|
||||
const char* MemoryManager::d_umpire_name = "DEVICE";
|
||||
int MemoryManager::h_umpire_id = -1;
|
||||
int MemoryManager::d_umpire_id = -1;
|
||||
int MemoryManager::d_umpire_temp_id = -1;
|
||||
#endif
|
||||
|
||||
MemoryType MemoryManager::host_mem_type = MemoryType::HOST;
|
||||
MemoryType MemoryManager::device_mem_type = MemoryType::HOST;
|
||||
MemoryType MemoryManager::device_temp_mem_type = MemoryType::HOST;
|
||||
|
||||
const char *MemoryTypeName[MemoryTypeSize] =
|
||||
{
|
||||
@@ -1403,11 +1474,14 @@ const char *MemoryTypeName[MemoryTypeSize] =
|
||||
#endif
|
||||
"device-debug",
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
"cuda-umpire"
|
||||
"cuda-umpire",
|
||||
"cuda-umpire-temp"
|
||||
#elif defined(MFEM_USE_HIP)
|
||||
"hip-umpire"
|
||||
"hip-umpire",
|
||||
"hip-umpire-temp"
|
||||
#else
|
||||
"device-umpire"
|
||||
"device-umpire",
|
||||
"device-umpire-temp"
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
+37
-25
@@ -27,18 +27,19 @@ namespace mfem
|
||||
/// Memory types supported by MFEM.
|
||||
enum class MemoryType
|
||||
{
|
||||
HOST, ///< Host memory; using new[] and delete[]
|
||||
HOST_32, ///< Host memory; aligned at 32 bytes
|
||||
HOST_64, ///< Host memory; aligned at 64 bytes
|
||||
HOST_DEBUG, ///< Host memory; allocated from a "host-debug" pool
|
||||
HOST_UMPIRE, ///< Host memory; using Umpire
|
||||
MANAGED, /**< Managed memory; using CUDA or HIP *MallocManaged
|
||||
and *Free */
|
||||
DEVICE, ///< Device memory; using CUDA or HIP *Malloc and *Free
|
||||
DEVICE_DEBUG, /**< Pseudo-device memory; allocated on host from a
|
||||
"device-debug" pool */
|
||||
DEVICE_UMPIRE, ///< Device memory; using Umpire
|
||||
SIZE ///< Number of host and device memory types
|
||||
HOST, ///< Host memory; using new[] and delete[]
|
||||
HOST_32, ///< Host memory; aligned at 32 bytes
|
||||
HOST_64, ///< Host memory; aligned at 64 bytes
|
||||
HOST_DEBUG, ///< Host memory; allocated from a "host-debug" pool
|
||||
HOST_UMPIRE, ///< Host memory; using Umpire
|
||||
MANAGED, /**< Managed memory; using CUDA or HIP *MallocManaged
|
||||
and *Free */
|
||||
DEVICE, ///< Device memory; using CUDA or HIP *Malloc and *Free
|
||||
DEVICE_DEBUG, /**< Pseudo-device memory; allocated on host from a
|
||||
"device-debug" pool */
|
||||
DEVICE_UMPIRE, ///< Device memory; using Umpire
|
||||
DEVICE_TEMP_UMPIRE, ///< Temporary Device memory; using Umpire
|
||||
SIZE ///< Number of host and device memory types
|
||||
};
|
||||
|
||||
/// Static casts to 'int' and sizes of some useful memory types.
|
||||
@@ -61,7 +62,7 @@ enum class MemoryClass
|
||||
HOST_UMPIRE, MANAGED } */
|
||||
HOST_32, ///< Memory types: { HOST_32, HOST_64, HOST_DEBUG }
|
||||
HOST_64, ///< Memory types: { HOST_64, HOST_DEBUG }
|
||||
DEVICE, ///< Memory types: { DEVICE, DEVICE_DEBUG, DEVICE_UMPIRE, MANAGED }
|
||||
DEVICE, ///< Memory types: { DEVICE, DEVICE_DEBUG, DEVICE_UMPIRE, DEVICE_TEMP_UMPIRE, MANAGED }
|
||||
MANAGED ///< Memory types: { MANAGED }
|
||||
};
|
||||
|
||||
@@ -450,16 +451,16 @@ private:
|
||||
|
||||
template <std::size_t align_bytes, bool dummy = true> struct Alloc
|
||||
{
|
||||
#if __cplusplus < 201703L
|
||||
static inline T *New(std::size_t)
|
||||
{
|
||||
#if __cplusplus < 201703L
|
||||
// Generate an error in debug mode
|
||||
MFEM_ASSERT(false, "overaligned type cannot use MemoryType::HOST");
|
||||
return nullptr;
|
||||
#else
|
||||
return new T[size];
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static inline T *New(std::size_t size) { return new T[size]; }
|
||||
#endif
|
||||
};
|
||||
|
||||
#if __cplusplus < 201703L
|
||||
@@ -489,6 +490,9 @@ private:
|
||||
/// Device memory type set during the Setup.
|
||||
static MemoryType device_mem_type;
|
||||
|
||||
/// Device temporary memory type set during the Setup.
|
||||
static MemoryType device_temp_mem_type;
|
||||
|
||||
/// Allow to detect if a global memory manager instance exists.
|
||||
static bool exists;
|
||||
|
||||
@@ -497,8 +501,9 @@ private:
|
||||
|
||||
/// Host and device allocator names for Umpire.
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
static const char *h_umpire_name;
|
||||
static const char *d_umpire_name;
|
||||
static int h_umpire_id;
|
||||
static int d_umpire_id;
|
||||
static int d_umpire_temp_id;
|
||||
#endif
|
||||
|
||||
private: // Static methods used by the Memory<T> class
|
||||
@@ -621,15 +626,21 @@ public:
|
||||
/// Initialize the memory manager.
|
||||
void Init();
|
||||
|
||||
/// Configure the Memory manager with given default host and device types
|
||||
/// Configure the Memory manager with given default host, device, and device temporary types
|
||||
/// This method will be called when configuring a device.
|
||||
void Configure(const MemoryType h_mt, const MemoryType d_mt);
|
||||
void Configure(const MemoryType h_mt, const MemoryType d_mt,
|
||||
const MemoryType d_tmt);
|
||||
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
/// Set the host and device UMpire allocator names
|
||||
void SetUmpireAllocatorNames(const char *h_name, const char *d_name);
|
||||
const char *GetUmpireAllocatorHostName() { return h_umpire_name; }
|
||||
const char *GetUmpireAllocatorDeviceName() { return d_umpire_name; }
|
||||
/// Set the host and device Umpire allocator ids
|
||||
static void SetUmpireHostAllocatorId(int h_id) { h_umpire_id = h_id; }
|
||||
static void SetUmpireDeviceAllocatorId(int d_id) { d_umpire_id = d_id; }
|
||||
static void SetUmpireDeviceTempAllocatorId(int d_id) { d_umpire_temp_id = d_id; }
|
||||
|
||||
/// Get the host and device Umpire allocator ids
|
||||
static int GetUmpireHostAllocatorId() { return h_umpire_id; }
|
||||
static int GetUmpireDeviceAllocatorId() { return d_umpire_id; }
|
||||
static int GetUmpireDeviceTempAllocatorId() { return d_umpire_temp_id; }
|
||||
#endif
|
||||
|
||||
/// Free all the device memories
|
||||
@@ -654,6 +665,7 @@ public:
|
||||
|
||||
static MemoryType GetHostMemoryType() { return host_mem_type; }
|
||||
static MemoryType GetDeviceMemoryType() { return device_mem_type; }
|
||||
static MemoryType GetDeviceTempMemoryType() { return device_temp_mem_type; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -3595,4 +3595,114 @@ void BatchLUSolve(const DenseTensor &Mlu, const Array<int> &P, Vector &X)
|
||||
|
||||
}
|
||||
|
||||
void BatchLUFactor(Vector &Minv,const int m,const int NE, Array<int> &P)
|
||||
{
|
||||
P.SetSize(m*NE);
|
||||
auto data_all = mfem::Reshape(Minv.ReadWrite(), m, m, NE);
|
||||
auto piv_all = mfem::Reshape(P.Write(), m, NE);
|
||||
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
|
||||
double *data = &data_all(0,0,e);
|
||||
int *ipiv = &piv_all(0,e);
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
|
||||
// pivoting
|
||||
{
|
||||
int piv = i;
|
||||
double a = fabs(data[piv+i*m]);
|
||||
for (int j = i+1; j < m; j++)
|
||||
{
|
||||
const double b = fabs(data[j+i*m]);
|
||||
if (b > a)
|
||||
{
|
||||
a = b;
|
||||
piv = j;
|
||||
}
|
||||
}
|
||||
ipiv[i] = piv;
|
||||
if (piv != i)
|
||||
{
|
||||
// swap rows i and piv in both L and U parts
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
mfem::kernels::internal::Swap<double>(data[i+j*m], data[piv+j*m]);
|
||||
}
|
||||
}
|
||||
}//pivot end
|
||||
|
||||
//Q: How to check for errors?
|
||||
//if (abs(data[i + i*m]) <= TOL)
|
||||
//{
|
||||
//return false; // failed
|
||||
//}
|
||||
|
||||
const double a_ii_inv = 1.0 / data[i+i*m];
|
||||
for (int j = i+1; j < m; j++)
|
||||
{
|
||||
data[j+i*m] *= a_ii_inv;
|
||||
}
|
||||
|
||||
for (int k = i+1; k < m; k++)
|
||||
{
|
||||
const double a_ik = data[i+k*m];
|
||||
for (int j = i+1; j < m; j++)
|
||||
{
|
||||
data[j+k*m] -= a_ik * data[j+i*m];
|
||||
}
|
||||
}
|
||||
|
||||
}//m loop
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void BatchLUSolve(Vector &Minv, int m, int NE,
|
||||
Array<int> &P, Vector &X)
|
||||
{
|
||||
|
||||
auto data_all = mfem::Reshape(Minv.Read(), m, m, NE);
|
||||
auto piv_all = mfem::Reshape(P.Read(), m, NE);
|
||||
auto x_all = mfem::Reshape(X.ReadWrite(), m, NE);
|
||||
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
|
||||
const double *data = &data_all(0,0,e);
|
||||
const int *ipiv = &piv_all(0,e);
|
||||
double *x = &x_all(0,e);
|
||||
|
||||
// X <- P X
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
mfem::kernels::internal::Swap<double>(x[i], x[ipiv[i]]);
|
||||
}
|
||||
|
||||
// X <- L^{-1} X
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
const double x_j = x[j];
|
||||
for (int i = j+1; i < m; i++)
|
||||
{
|
||||
x[i] -= data[i+j*m] * x_j;
|
||||
}
|
||||
}
|
||||
|
||||
// X <- U^{-1} X
|
||||
for (int j = m-1; j >= 0; j--)
|
||||
{
|
||||
const double x_j = ( x[j] /= data[j+j*m] );
|
||||
for (int i = 0; i < j; i++)
|
||||
{
|
||||
x[i] -= data[i+j*m] * x_j;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -884,6 +884,10 @@ void BatchLUFactor(DenseTensor &Mlu, Array<int> &P, const double TOL = 0.0);
|
||||
dimension m x n. */
|
||||
void BatchLUSolve(const DenseTensor &Mlu, const Array<int> &P, Vector &X);
|
||||
|
||||
void BatchLUFactor(Vector &Minv,int m,int NE, Array<int> &P);
|
||||
|
||||
void BatchLUSolve(Vector &Minv, int m, int NE,
|
||||
Array<int> &P, Vector &X);
|
||||
|
||||
// Inline methods
|
||||
|
||||
|
||||
+162
-31
@@ -28,6 +28,25 @@ namespace mfem
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
int SparseMatrix::SparseMatrixCount = 0;
|
||||
cusparseHandle_t SparseMatrix::handle;
|
||||
size_t SparseMatrix::bufferSize = 0;
|
||||
void * SparseMatrix::dBuffer = nullptr;
|
||||
#endif
|
||||
|
||||
void SparseMatrix::InitCuSparse()
|
||||
{
|
||||
/* Initialize CuSparse library */
|
||||
#ifdef MFEM_USE_CUDA
|
||||
SparseMatrixCount++;
|
||||
if (SparseMatrixCount == 1 && Device::Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
cusparseCreate(&handle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SparseMatrix::SparseMatrix(int nrows, int ncols)
|
||||
: AbstractSparseMatrix(nrows, (ncols >= 0) ? ncols : nrows),
|
||||
Rows(new RowNode *[nrows]),
|
||||
@@ -50,6 +69,8 @@ SparseMatrix::SparseMatrix(int nrows, int ncols)
|
||||
#ifdef MFEM_USE_MEMALLOC
|
||||
NodesMem = new RowNodeAlloc;
|
||||
#endif
|
||||
|
||||
InitCuSparse();
|
||||
}
|
||||
|
||||
SparseMatrix::SparseMatrix(int *i, int *j, double *data, int m, int n)
|
||||
@@ -67,6 +88,8 @@ SparseMatrix::SparseMatrix(int *i, int *j, double *data, int m, int n)
|
||||
#ifdef MFEM_USE_MEMALLOC
|
||||
NodesMem = NULL;
|
||||
#endif
|
||||
|
||||
InitCuSparse();
|
||||
}
|
||||
|
||||
SparseMatrix::SparseMatrix(int *i, int *j, double *data, int m, int n,
|
||||
@@ -98,6 +121,8 @@ SparseMatrix::SparseMatrix(int *i, int *j, double *data, int m, int n,
|
||||
A[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
InitCuSparse();
|
||||
}
|
||||
|
||||
SparseMatrix::SparseMatrix(int nrows, int ncols, int rowsize)
|
||||
@@ -119,9 +144,11 @@ SparseMatrix::SparseMatrix(int nrows, int ncols, int rowsize)
|
||||
{
|
||||
I[i] = i * rowsize;
|
||||
}
|
||||
|
||||
InitCuSparse();
|
||||
}
|
||||
|
||||
SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph)
|
||||
SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph, MemoryType mt)
|
||||
: AbstractSparseMatrix(mat.Height(), mat.Width())
|
||||
{
|
||||
if (mat.Finalized())
|
||||
@@ -129,8 +156,8 @@ SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph)
|
||||
const int nnz = mat.I[height];
|
||||
if (copy_graph)
|
||||
{
|
||||
I.New(height+1, mat.I.GetMemoryType());
|
||||
J.New(nnz, mat.J.GetMemoryType());
|
||||
I.New(height+1, mt == MemoryType::SIZE ? mat.I.GetMemoryType() : mt);
|
||||
J.New(nnz, mt == MemoryType::SIZE ? mat.J.GetMemoryType() : mt);
|
||||
I.CopyFrom(mat.I, height+1);
|
||||
J.CopyFrom(mat.J, nnz);
|
||||
}
|
||||
@@ -141,7 +168,7 @@ SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph)
|
||||
I.ClearOwnerFlags();
|
||||
J.ClearOwnerFlags();
|
||||
}
|
||||
A.New(nnz, mat.A.GetMemoryType());
|
||||
A.New(nnz, mt == MemoryType::SIZE ? mat.A.GetMemoryType() : mt);
|
||||
A.CopyFrom(mat.A, nnz);
|
||||
|
||||
Rows = NULL;
|
||||
@@ -184,6 +211,8 @@ SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph)
|
||||
ColPtrNode = NULL;
|
||||
At = NULL;
|
||||
isSorted = mat.isSorted;
|
||||
|
||||
InitCuSparse();
|
||||
}
|
||||
|
||||
SparseMatrix::SparseMatrix(const Vector &v)
|
||||
@@ -211,6 +240,8 @@ SparseMatrix::SparseMatrix(const Vector &v)
|
||||
J[r] = r;
|
||||
A[r] = v[r];
|
||||
}
|
||||
|
||||
InitCuSparse();
|
||||
}
|
||||
|
||||
SparseMatrix& SparseMatrix::operator=(const SparseMatrix &rhs)
|
||||
@@ -250,6 +281,16 @@ void SparseMatrix::SetEmpty()
|
||||
NodesMem = NULL;
|
||||
#endif
|
||||
isSorted = false;
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
if (initBuffers)
|
||||
{
|
||||
cusparseDestroySpMat(matA_descr);
|
||||
cusparseDestroyDnVec(vecX_descr);
|
||||
cusparseDestroyDnVec(vecY_descr);
|
||||
initBuffers = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int SparseMatrix::RowSize(const int i) const
|
||||
@@ -494,24 +535,29 @@ void SparseMatrix::GetDiag(Vector & d) const
|
||||
|
||||
d.SetSize(height);
|
||||
|
||||
int j, end;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
auto I = this->ReadI();
|
||||
auto J = this->ReadJ();
|
||||
auto A = this->ReadData();
|
||||
auto dd = d.Write();
|
||||
|
||||
end = I[i+1];
|
||||
for (j = I[i]; j < end; j++)
|
||||
MFEM_FORALL(i, height,
|
||||
{
|
||||
const int begin = I[i];
|
||||
const int end = I[i+1];
|
||||
int j;
|
||||
for (j = begin; j < end; j++)
|
||||
{
|
||||
if (J[j] == i)
|
||||
{
|
||||
d[i] = A[j];
|
||||
dd[i] = A[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == end)
|
||||
{
|
||||
d[i] = 0.;
|
||||
dd[i] = 0.;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Produces a DenseMatrix from a SparseMatrix
|
||||
@@ -587,16 +633,72 @@ void SparseMatrix::AddMult(const Vector &x, Vector &y, const double a) const
|
||||
auto d_A = Read(A, nnz);
|
||||
auto d_x = x.Read();
|
||||
auto d_y = y.ReadWrite();
|
||||
MFEM_FORALL(i, height,
|
||||
|
||||
//Skip if matrix has no non-zeros
|
||||
if (nnz == 0) {return;}
|
||||
if (Device::Allows(Backend::CUDA_MASK) && useCuSparse)
|
||||
{
|
||||
double d = 0.0;
|
||||
const int end = d_I[i+1];
|
||||
for (int j = d_I[i]; j < end; j++)
|
||||
#ifdef MFEM_USE_CUDA
|
||||
const double alpha = a;
|
||||
const double beta = 1.0;
|
||||
|
||||
//Setup descriptors
|
||||
if (!initBuffers)
|
||||
{
|
||||
d += d_A[j] * d_x[d_J[j]];
|
||||
/* Setup matrix descriptor */
|
||||
cusparseCreateCsr(&matA_descr,Height(), Width(), J.Capacity(),
|
||||
const_cast<int *>(d_I),
|
||||
const_cast<int *>(d_J), const_cast<double *>(d_A), CUSPARSE_INDEX_32I,
|
||||
CUSPARSE_INDEX_32I, CUSPARSE_INDEX_BASE_ZERO, CUDA_R_64F);
|
||||
|
||||
/*Create handles for input/output vectors */
|
||||
cusparseCreateDnVec(&vecX_descr, x.Size(), const_cast<double *>(d_x),
|
||||
CUDA_R_64F);
|
||||
cusparseCreateDnVec(&vecY_descr, y.Size(), d_y, CUDA_R_64F);
|
||||
|
||||
initBuffers = true;
|
||||
}
|
||||
d_y[i] += a * d;
|
||||
});
|
||||
|
||||
/*Allocate space for kernel. Buffer is shared between different sparsemats */
|
||||
size_t newBufferSize = 0;
|
||||
cusparseSpMV_bufferSize(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha,
|
||||
matA_descr,
|
||||
vecX_descr, &beta, vecY_descr, CUDA_R_64F,
|
||||
CUSPARSE_CSRMV_ALG1, &newBufferSize);
|
||||
|
||||
//Check if need to resize
|
||||
if (newBufferSize > bufferSize)
|
||||
{
|
||||
bufferSize = newBufferSize;
|
||||
if (dBuffer != NULL) { CuMemFree(dBuffer); }
|
||||
CuMemAlloc(&dBuffer, bufferSize);
|
||||
}
|
||||
|
||||
//Update input/output vectors
|
||||
cusparseDnVecSetValues(vecX_descr, const_cast<double *>(d_x));
|
||||
cusparseDnVecSetValues(vecY_descr, d_y);
|
||||
|
||||
// Y = alpha A * X + beta * Y
|
||||
cusparseSpMV(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA_descr,
|
||||
vecX_descr, &beta, vecY_descr, CUDA_R_64F, CUSPARSE_CSRMV_ALG1, dBuffer);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
//Native version
|
||||
MFEM_FORALL(i, height,
|
||||
{
|
||||
double d = 0.0;
|
||||
const int end = d_I[i+1];
|
||||
for (int j = d_I[i]; j < end; j++)
|
||||
{
|
||||
d += d_A[j] * d_x[d_J[j]];
|
||||
}
|
||||
d_y[i] += a * d;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
const double *Ap = A, *xp = x.GetData();
|
||||
double *yp = y.GetData();
|
||||
@@ -2145,31 +2247,46 @@ void SparseMatrix::DiagScale(const Vector &b, Vector &x, double sc) const
|
||||
{
|
||||
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
|
||||
|
||||
const int nnz = J.Capacity();
|
||||
|
||||
const bool use_dev = b.UseDevice() || x.UseDevice();
|
||||
|
||||
auto bp = b.Read(use_dev);
|
||||
auto xp = x.Write(use_dev);
|
||||
|
||||
auto Ap = Read(A, nnz);
|
||||
auto Ip = Read(I, height+1);
|
||||
auto Jp = Read(J, nnz);
|
||||
|
||||
bool scale = (sc != 1.0);
|
||||
for (int i = 0, j = 0; i < height; i++)
|
||||
MFEM_FORALL(i, height,
|
||||
{
|
||||
int end = I[i+1];
|
||||
for ( ; true; j++)
|
||||
int end = Ip[i+1];
|
||||
for (int j = Ip[i]; true; j++)
|
||||
{
|
||||
MFEM_VERIFY(j != end, "Couldn't find diagonal in row. i = " << i
|
||||
<< ", j = " << j
|
||||
<< ", I[i+1] = " << end );
|
||||
if (J[j] == i)
|
||||
if (j == end)
|
||||
{
|
||||
MFEM_VERIFY(std::abs(A[j]) > 0.0, "Diagonal " << j << " must be nonzero");
|
||||
//MFEM_ABORT_KERNEL("Diagonal not found in SparseMatrix::DiagScale");
|
||||
}
|
||||
if (Jp[j] == i)
|
||||
{
|
||||
if (!(std::abs(Ap[j]) > 0.0))
|
||||
{
|
||||
//MFEM_ABORT_KERNEL("Zero diagonal in SparseMatrix::DiagScale");
|
||||
}
|
||||
|
||||
if (scale)
|
||||
{
|
||||
x(i) = sc * b(i) / A[j];
|
||||
xp[i] = sc * bp[i] / Ap[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
x(i) = b(i) / A[j];
|
||||
xp[i] = bp[i] / Ap[j];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
j = end;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2749,6 +2866,10 @@ void SparseMatrix::Print(std::ostream & out, int _width) const
|
||||
return;
|
||||
}
|
||||
|
||||
// HostRead forces synchronization
|
||||
HostReadI();
|
||||
HostReadJ();
|
||||
HostReadData();
|
||||
for (i = 0; i < height; i++)
|
||||
{
|
||||
out << "[row " << i << "]\n";
|
||||
@@ -2938,6 +3059,16 @@ void SparseMatrix::Destroy()
|
||||
delete NodesMem;
|
||||
#endif
|
||||
delete At;
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
if (initBuffers)
|
||||
{
|
||||
cusparseDestroySpMat(matA_descr);
|
||||
cusparseDestroyDnVec(vecX_descr);
|
||||
cusparseDestroyDnVec(vecY_descr);
|
||||
initBuffers = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int SparseMatrix::ActualWidth() const
|
||||
|
||||
+53
-3
@@ -21,6 +21,12 @@
|
||||
#include "../general/globals.hpp"
|
||||
#include "densemat.hpp"
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#include <cusparse.h>
|
||||
#include <library_types.h>
|
||||
#include "../general/cuda.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -80,9 +86,33 @@ protected:
|
||||
void Destroy(); // Delete all owned data
|
||||
void SetEmpty(); // Init all entries with empty values
|
||||
|
||||
bool useCuSparse{true}; //Use CuSparse if available
|
||||
|
||||
// Initialize CuSparse
|
||||
void InitCuSparse();
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
cusparseStatus_t status;
|
||||
static cusparseHandle_t handle;
|
||||
cusparseMatDescr_t descr=0;
|
||||
static size_t bufferSize;
|
||||
static void *dBuffer;
|
||||
mutable bool initBuffers{false};
|
||||
|
||||
static int SparseMatrixCount;
|
||||
mutable cusparseSpMatDescr_t matA_descr;
|
||||
mutable cusparseDnVecDescr_t vecX_descr;
|
||||
mutable cusparseDnVecDescr_t vecY_descr;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// Create an empty SparseMatrix.
|
||||
SparseMatrix() { SetEmpty(); }
|
||||
SparseMatrix()
|
||||
{
|
||||
SetEmpty();
|
||||
|
||||
InitCuSparse();
|
||||
}
|
||||
|
||||
/** @brief Create a sparse matrix with flexible sparsity structure using a
|
||||
row-wise linked list (LIL) format. */
|
||||
@@ -113,11 +143,14 @@ public:
|
||||
/** If @a mat is finalized and @a copy_graph is false, the #I and #J arrays
|
||||
will use a shallow copy (copy the pointers only) without transferring
|
||||
ownership. */
|
||||
SparseMatrix(const SparseMatrix &mat, bool copy_graph = true);
|
||||
SparseMatrix(const SparseMatrix &mat, bool copy_graph = true, MemoryType mt = MemoryType::SIZE);
|
||||
|
||||
/// Create a SparseMatrix with diagonal @a v, i.e. A = Diag(v)
|
||||
SparseMatrix(const Vector & v);
|
||||
|
||||
// Runtime option to use CuSparse
|
||||
// Only valid when using a CUDA backend
|
||||
void UseCuSparse(bool _useCuSparse = true) { useCuSparse = _useCuSparse;}
|
||||
|
||||
/// Assignment operator: deep copy
|
||||
SparseMatrix& operator=(const SparseMatrix &rhs);
|
||||
@@ -573,11 +606,28 @@ public:
|
||||
void Swap(SparseMatrix &other);
|
||||
|
||||
/// Destroys sparse matrix.
|
||||
virtual ~SparseMatrix() { Destroy(); }
|
||||
virtual ~SparseMatrix()
|
||||
{
|
||||
Destroy();
|
||||
#ifdef MFEM_USE_CUDA
|
||||
if (handle && SparseMatrixCount==1 && Device::Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
cusparseDestroy(handle);
|
||||
CuMemFree(dBuffer);
|
||||
}
|
||||
SparseMatrixCount--;
|
||||
#endif
|
||||
}
|
||||
|
||||
Type GetType() const { return MFEM_SPARSEMAT; }
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, SparseMatrix const& mat)
|
||||
{
|
||||
mat.Print(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
/// Applies f() to each element of the matrix (after it is finalized).
|
||||
void SparseMatrixFunction(SparseMatrix &S, double (*f)(double));
|
||||
|
||||
|
||||
+69
-19
@@ -911,20 +911,27 @@ FaceElementTransformations *Mesh::GetFaceElementTransformations(int FaceNo,
|
||||
// NC meshes: prepend slave edge/face transformation to Loc2
|
||||
if (Nonconforming() && IsSlaveFace(face_info))
|
||||
{
|
||||
ApplyLocalSlaveTransformation(FaceElemTr.Loc2.Transf, face_info);
|
||||
|
||||
if (face_type == Element::SEGMENT)
|
||||
{
|
||||
// flip Loc2 to match Loc1 and Face
|
||||
DenseMatrix &pm = FaceElemTr.Loc2.Transf.GetPointMat();
|
||||
std::swap(pm(0,0), pm(0,1));
|
||||
std::swap(pm(1,0), pm(1,1));
|
||||
}
|
||||
ApplyLocalSlaveTransformation(FaceElemTr, face_info, false);
|
||||
}
|
||||
}
|
||||
|
||||
FaceElemTr.SetConfigurationMask(mask);
|
||||
|
||||
// This check can be useful for internal debugging, however it will fail on
|
||||
// periodic boundary faces, so we keep it disabled in general.
|
||||
#if 0
|
||||
#ifdef MFEM_DEBUG
|
||||
double dist = FaceElemTr.CheckConsistency();
|
||||
if (dist >= 1e-12)
|
||||
{
|
||||
mfem::out << "\nInternal error: face id = " << FaceNo
|
||||
<< ", dist = " << dist << '\n';
|
||||
FaceElemTr.CheckConsistency(1); // print coordinates
|
||||
MFEM_ABORT("internal error");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return &FaceElemTr;
|
||||
}
|
||||
|
||||
@@ -933,8 +940,8 @@ bool Mesh::IsSlaveFace(const FaceInfo &fi) const
|
||||
return fi.NCFace >= 0 && nc_faces_info[fi.NCFace].Slave;
|
||||
}
|
||||
|
||||
void Mesh::ApplyLocalSlaveTransformation(IsoparametricTransformation &transf,
|
||||
const FaceInfo &fi)
|
||||
void Mesh::ApplyLocalSlaveTransformation(FaceElementTransformations &FT,
|
||||
const FaceInfo &fi, bool is_ghost)
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix composition;
|
||||
@@ -942,8 +949,34 @@ void Mesh::ApplyLocalSlaveTransformation(IsoparametricTransformation &transf,
|
||||
static DenseMatrix composition;
|
||||
#endif
|
||||
MFEM_ASSERT(fi.NCFace >= 0, "");
|
||||
transf.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
|
||||
transf.SetPointMat(composition);
|
||||
MFEM_ASSERT(nc_faces_info[fi.NCFace].Slave, "internal error");
|
||||
if (!is_ghost)
|
||||
{
|
||||
// side 1 -> child side, side 2 -> parent side
|
||||
IsoparametricTransformation < = FT.Loc2.Transf;
|
||||
LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
|
||||
// In 2D, we need to flip the point matrix since it is aligned with the
|
||||
// parent side.
|
||||
if (Dim == 2)
|
||||
{
|
||||
// swap points (columns) 0 and 1
|
||||
std::swap(composition(0,0), composition(0,1));
|
||||
std::swap(composition(1,0), composition(1,1));
|
||||
}
|
||||
LT.SetPointMat(composition);
|
||||
}
|
||||
else // is_ghost == true
|
||||
{
|
||||
// side 1 -> parent side, side 2 -> child side
|
||||
IsoparametricTransformation < = FT.Loc1.Transf;
|
||||
LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
|
||||
// In 2D, there is no need to flip the point matrix since it is already
|
||||
// aligned with the parent side, see also ParNCMesh::GetFaceNeighbors.
|
||||
// In 3D the point matrix was flipped during construction in
|
||||
// ParNCMesh::GetFaceNeighbors and due to that it is already aligned with
|
||||
// the parent side.
|
||||
LT.SetPointMat(composition);
|
||||
}
|
||||
}
|
||||
|
||||
FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
|
||||
@@ -988,7 +1021,21 @@ void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
|
||||
|
||||
Geometry::Type Mesh::GetFaceGeometryType(int Face) const
|
||||
{
|
||||
return (Dim == 1) ? Geometry::POINT : faces[Face]->GetGeometryType();
|
||||
switch (Dim)
|
||||
{
|
||||
case 1: return Geometry::POINT;
|
||||
case 2: return Geometry::SEGMENT;
|
||||
case 3:
|
||||
if (Face < NumOfFaces) // local (non-ghost) face
|
||||
{
|
||||
return faces[Face]->GetGeometryType();
|
||||
}
|
||||
// ghost face
|
||||
const int nc_face_id = faces_info[Face].NCFace;
|
||||
MFEM_ASSERT(nc_face_id >= 0, "parent ghost faces are not supported");
|
||||
return faces[nc_faces_info[nc_face_id].MasterFace]->GetGeometryType();
|
||||
}
|
||||
return Geometry::INVALID;
|
||||
}
|
||||
|
||||
Element::Type Mesh::GetFaceElementType(int Face) const
|
||||
@@ -5261,7 +5308,10 @@ void Mesh::GenerateNCFaceInfo()
|
||||
|
||||
slave_fi.Elem2No = master_fi.Elem1No;
|
||||
slave_fi.Elem2Inf = 64 * master_nc.MasterFace; // get lf no. stored above
|
||||
// NOTE: orientation part of Elem2Inf is encoded in the point matrix
|
||||
// NOTE: In 3D, the orientation part of Elem2Inf is encoded in the point
|
||||
// matrix. In 2D, the point matrix has the orientation of the parent
|
||||
// edge, so its columns need to be flipped when applying it, see
|
||||
// ApplyLocalSlaveTransformation.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10474,17 +10524,17 @@ GeometricFactors::GeometricFactors(const Mesh *mesh, const IntegrationRule &ir,
|
||||
unsigned eval_flags = 0;
|
||||
if (flags & GeometricFactors::COORDINATES)
|
||||
{
|
||||
X.SetSize(vdim*NQ*NE);
|
||||
X.SetSize(vdim*NQ*NE, Device::GetDeviceTempMemoryType());
|
||||
eval_flags |= QuadratureInterpolator::VALUES;
|
||||
}
|
||||
if (flags & GeometricFactors::JACOBIANS)
|
||||
{
|
||||
J.SetSize(dim*vdim*NQ*NE);
|
||||
J.SetSize(dim*vdim*NQ*NE, Device::GetDeviceTempMemoryType());
|
||||
eval_flags |= QuadratureInterpolator::DERIVATIVES;
|
||||
}
|
||||
if (flags & GeometricFactors::DETERMINANTS)
|
||||
{
|
||||
detJ.SetSize(NQ*NE);
|
||||
detJ.SetSize(NQ*NE, Device::GetDeviceTempMemoryType());
|
||||
eval_flags |= QuadratureInterpolator::DETERMINANTS;
|
||||
}
|
||||
|
||||
@@ -10494,7 +10544,7 @@ GeometricFactors::GeometricFactors(const Mesh *mesh, const IntegrationRule &ir,
|
||||
qi->SetOutputLayout(QVectorLayout::byNODES);
|
||||
if (elem_restr)
|
||||
{
|
||||
Vector Enodes(vdim*ND*NE);
|
||||
Vector Enodes(vdim*ND*NE, Device::GetDeviceTempMemoryType());
|
||||
elem_restr->Mult(*nodes, Enodes);
|
||||
qi->Mult(Enodes, eval_flags, X, J, detJ);
|
||||
}
|
||||
|
||||
+8
-2
@@ -131,11 +131,16 @@ protected:
|
||||
// face. Elem2No is < 0 and -1-Elem2No is the index of the ghost
|
||||
// face-neighbor element that generated this slave ghost face. In this
|
||||
// case, Elem2Inf >= 0.
|
||||
// Relevant methods: GenerateFaces(), GenerateNCFaceInfo(),
|
||||
// ParNCMesh::GetFaceNeighbors(),
|
||||
// ParMesh::ExchangeFaceNbrData()
|
||||
|
||||
struct NCFaceInfo
|
||||
{
|
||||
bool Slave; // true if this is a slave face, false if master face
|
||||
int MasterFace; // if Slave, this is the index of the master face
|
||||
// If not Slave, 'MasterFace' is the local face index of this master face
|
||||
// as a face in the unique adjacent element.
|
||||
const DenseMatrix* PointMatrix; // if Slave, position within master face
|
||||
// (NOTE: PointMatrix points to a matrix owned by NCMesh.)
|
||||
|
||||
@@ -363,8 +368,9 @@ protected:
|
||||
|
||||
/** Used in GetFaceElementTransformations to account for the fact that a
|
||||
slave face occupies only a portion of its master face. */
|
||||
void ApplyLocalSlaveTransformation(IsoparametricTransformation &transf,
|
||||
const FaceInfo &fi);
|
||||
void ApplyLocalSlaveTransformation(FaceElementTransformations &FT,
|
||||
const FaceInfo &fi, bool is_ghost);
|
||||
|
||||
bool IsSlaveFace(const FaceInfo &fi) const;
|
||||
|
||||
/// Returns the orientation of "test" relative to "base"
|
||||
|
||||
+34
-21
@@ -2363,16 +2363,16 @@ Table *ParMesh::GetFaceToAllElementTable() const
|
||||
return face_elem;
|
||||
}
|
||||
|
||||
ElementTransformation* ParMesh::GetGhostFaceTransformation(
|
||||
void ParMesh::GetGhostFaceTransformation(
|
||||
FaceElementTransformations* FETr, Element::Type face_type,
|
||||
Geometry::Type face_geom)
|
||||
{
|
||||
// calculate composition of FETr->Loc1 and FETr->Elem1
|
||||
DenseMatrix &face_pm = FaceTransformation.GetPointMat();
|
||||
DenseMatrix &face_pm = FETr->GetPointMat();
|
||||
if (Nodes == NULL)
|
||||
{
|
||||
FETr->Elem1->Transform(FETr->Loc1.Transf.GetPointMat(), face_pm);
|
||||
FaceTransformation.SetFE(GetTransformationFEforElementType(face_type));
|
||||
FETr->SetFE(GetTransformationFEforElementType(face_type));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2388,15 +2388,23 @@ ElementTransformation* ParMesh::GetGhostFaceTransformation(
|
||||
FETr->Loc1.Transform(face_el->GetNodes(), eir);
|
||||
Nodes->GetVectorValues(*FETr->Elem1, eir, face_pm);
|
||||
#endif
|
||||
FaceTransformation.SetFE(face_el);
|
||||
FETr->SetFE(face_el);
|
||||
}
|
||||
return &FaceTransformation;
|
||||
}
|
||||
|
||||
FaceElementTransformations *ParMesh::
|
||||
GetSharedFaceTransformations(int sf, bool fill2)
|
||||
GetSharedFaceTransformations(int sf, bool fill2, bool direct)
|
||||
{
|
||||
int FaceNo = GetSharedFace(sf);
|
||||
//int FaceNo = GetSharedFace(sf);
|
||||
int FaceNo;
|
||||
if (direct)
|
||||
{
|
||||
FaceNo = sf;
|
||||
}
|
||||
else
|
||||
{
|
||||
FaceNo = GetSharedFace(sf);
|
||||
}
|
||||
|
||||
FaceInfo &face_info = faces_info[FaceNo];
|
||||
|
||||
@@ -2453,22 +2461,10 @@ GetSharedFaceTransformations(int sf, bool fill2)
|
||||
// adjust Loc1 or Loc2 of the master face if this is a slave face
|
||||
if (is_slave)
|
||||
{
|
||||
// is a ghost slave? -> master not a ghost -> choose Elem1 local transf
|
||||
// not a ghost slave? -> master is a ghost -> choose Elem2 local transf
|
||||
IsoparametricTransformation &loctr =
|
||||
is_ghost ? FaceElemTr.Loc1.Transf : FaceElemTr.Loc2.Transf;
|
||||
|
||||
if (is_ghost || fill2)
|
||||
{
|
||||
ApplyLocalSlaveTransformation(loctr, face_info);
|
||||
}
|
||||
|
||||
if (face_type == Element::SEGMENT && fill2)
|
||||
{
|
||||
// fix slave orientation in 2D: flip Loc2 to match Loc1 and Face
|
||||
DenseMatrix &pm = FaceElemTr.Loc2.Transf.GetPointMat();
|
||||
std::swap(pm(0,0), pm(0,1));
|
||||
std::swap(pm(1,0), pm(1,1));
|
||||
// is_ghost -> modify side 1, otherwise -> modify side 2:
|
||||
ApplyLocalSlaveTransformation(FaceElemTr, face_info, is_ghost);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2478,6 +2474,23 @@ GetSharedFaceTransformations(int sf, bool fill2)
|
||||
GetGhostFaceTransformation(&FaceElemTr, face_type, face_geom);
|
||||
}
|
||||
|
||||
FaceElemTr.SetConfigurationMask(fill2 ? 31 : 21);
|
||||
|
||||
// This check can be useful for internal debugging, however it will fail on
|
||||
// periodic boundary faces, so we keep it disabled in general.
|
||||
#if 0
|
||||
#ifdef MFEM_DEBUG
|
||||
double dist = FaceElemTr.CheckConsistency();
|
||||
if (dist >= 1e-12)
|
||||
{
|
||||
mfem::out << "\nInternal error: face id = " << FaceNo
|
||||
<< ", dist = " << dist << ", rank = " << MyRank << '\n';
|
||||
FaceElemTr.CheckConsistency(1); // print coordinates
|
||||
MFEM_ABORT("internal error");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return &FaceElemTr;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -107,7 +107,7 @@ protected:
|
||||
void GetFaceNbrElementTransformation(
|
||||
int i, IsoparametricTransformation *ElTr);
|
||||
|
||||
ElementTransformation* GetGhostFaceTransformation(
|
||||
void GetGhostFaceTransformation(
|
||||
FaceElementTransformations* FETr, Element::Type face_type,
|
||||
Geometry::Type face_geom);
|
||||
|
||||
@@ -291,9 +291,9 @@ public:
|
||||
|
||||
/** Get the FaceElementTransformations for the given shared face (edge 2D).
|
||||
In the returned object, 1 and 2 refer to the local and the neighbor
|
||||
elements, respectively. */
|
||||
elements, respectively. Use direct if sf is the face number */
|
||||
FaceElementTransformations *
|
||||
GetSharedFaceTransformations(int sf, bool fill2 = true);
|
||||
GetSharedFaceTransformations(int sf, bool fill2 = true, bool direct = false);
|
||||
|
||||
/// Return the number of shared faces (3D), edges (2D), vertices (1D)
|
||||
int GetNSharedFaces() const;
|
||||
|
||||
@@ -1263,6 +1263,8 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
const DenseMatrix* pm = &sf.point_matrix;
|
||||
if (!sloc && Dim == 3)
|
||||
{
|
||||
// TODO: does this handle triangle faces correctly?
|
||||
|
||||
// ghost slave in 3D needs flipping orientation
|
||||
DenseMatrix* pm2 = new DenseMatrix(*pm);
|
||||
std::swap((*pm2)(0,1), (*pm2)(0,3));
|
||||
@@ -1282,6 +1284,14 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
// processor, but on the other it is the element containing the
|
||||
// master face. In the latter case we need to flip the pm.
|
||||
}
|
||||
else if (!sloc && Dim == 2)
|
||||
{
|
||||
fi.Elem2Inf ^= 1; // set orientation to 1
|
||||
// The point matrix (used to define "side 1" which is the same as
|
||||
// "parent side" in this case) does not require a flip since it
|
||||
// is aligned with the parent side, so NO flip is performed in
|
||||
// Mesh::ApplyLocalSlaveTransformation.
|
||||
}
|
||||
|
||||
MFEM_ASSERT(fi.NCFace < 0, "");
|
||||
fi.NCFace = pmesh.nc_faces_info.Size();
|
||||
|
||||
@@ -32,6 +32,16 @@ public:
|
||||
: attr(attr), coeff(coeff)
|
||||
{}
|
||||
|
||||
VelDirichletBC_T(VelDirichletBC_T &&obj)
|
||||
{
|
||||
// Deep copy the attribute array
|
||||
this->attr = obj.attr;
|
||||
|
||||
// Move the coefficient pointer
|
||||
this->coeff = obj.coeff;
|
||||
obj.coeff = nullptr;
|
||||
}
|
||||
|
||||
~VelDirichletBC_T() { delete coeff; }
|
||||
|
||||
Array<int> attr;
|
||||
@@ -46,6 +56,16 @@ public:
|
||||
: attr(attr), coeff(coeff)
|
||||
{}
|
||||
|
||||
PresDirichletBC_T(PresDirichletBC_T &&obj)
|
||||
{
|
||||
// Deep copy the attribute array
|
||||
this->attr = obj.attr;
|
||||
|
||||
// Move the coefficient pointer
|
||||
this->coeff = obj.coeff;
|
||||
obj.coeff = nullptr;
|
||||
}
|
||||
|
||||
~PresDirichletBC_T() { delete coeff; }
|
||||
|
||||
Array<int> attr;
|
||||
@@ -60,6 +80,16 @@ public:
|
||||
: attr(attr), coeff(coeff)
|
||||
{}
|
||||
|
||||
AccelTerm_T(AccelTerm_T &&obj)
|
||||
{
|
||||
// Deep copy the attribute array
|
||||
this->attr = obj.attr;
|
||||
|
||||
// Move the coefficient pointer
|
||||
this->coeff = obj.coeff;
|
||||
obj.coeff = nullptr;
|
||||
}
|
||||
|
||||
~AccelTerm_T() { delete coeff; }
|
||||
|
||||
Array<int> attr;
|
||||
|
||||
Reference in New Issue
Block a user