594 lines
17 KiB
C++
594 lines
17 KiB
C++
#include "mfem.hpp"
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
class ZCoefficient : public VectorCoefficient
|
|
{
|
|
protected:
|
|
GridFunction *psi;
|
|
|
|
public:
|
|
ZCoefficient(int vdim, GridFunction &psi_)
|
|
: VectorCoefficient(vdim), psi(&psi_) { }
|
|
|
|
virtual void Eval(Vector &V, ElementTransformation &T,
|
|
const IntegrationPoint &ip);
|
|
};
|
|
|
|
class DZCoefficient : public MatrixCoefficient
|
|
{
|
|
protected:
|
|
GridFunction *psi;
|
|
|
|
public:
|
|
DZCoefficient(int height, GridFunction &psi_)
|
|
: MatrixCoefficient(height), psi(&psi_) { }
|
|
|
|
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
|
const IntegrationPoint &ip);
|
|
};
|
|
|
|
|
|
bool CheckVectorComponents(const mfem::GridFunction &gf, double limit)
|
|
{
|
|
const double* data = gf.GetData();
|
|
const int size = gf.Size();
|
|
|
|
for (int i = 0; i < size; ++i)
|
|
{
|
|
if (std::abs(data[i]) > limit)
|
|
{
|
|
const int vdim = gf.FESpace()->GetVDim();
|
|
int dof_index = i / vdim;
|
|
int component_index = i % vdim;
|
|
|
|
std::cout << "--> Condition VIOLATED at DOF #" << dof_index
|
|
<< ", component " << component_index
|
|
<< ". Value: " << data[i]
|
|
<< ", Limit: " << limit << std::endl;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
class RotationCoefficient : public MatrixCoefficient {
|
|
public:
|
|
RotationCoefficient() : MatrixCoefficient(2) {}
|
|
|
|
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
|
|
const IntegrationPoint &ip) {
|
|
M(0,0) = 0; M(0,1) = -1; // [0, -1]
|
|
M(1,0) = 1; M(1,1) = 0; // [1, 0]
|
|
}
|
|
};
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
// const char *mesh_file = "../data/star.mesh";
|
|
int order = 2;
|
|
int order_l2 = 1;
|
|
int max_it = 10;
|
|
int ref_levels = 3;
|
|
real_t alpha = 1.0;
|
|
real_t growth_rate = 1.0;
|
|
real_t newton_scaling = 0.9;
|
|
real_t tichonov = 1e-1;
|
|
real_t tol = 1e-6;
|
|
|
|
int ex = 1;
|
|
|
|
bool visualization = true;
|
|
|
|
OptionsParser args(argc, argv);
|
|
// args.AddOption(&mesh_file, "-m", "--mesh",
|
|
// "Mesh file to use.");
|
|
args.AddOption(&order, "-o", "--order",
|
|
"Finite element order for RT space");
|
|
args.AddOption(&order_l2, "-o2", "--order", "FEM order for L2 vec space");
|
|
args.AddOption(&ex, "-ex", "--example", "example number");
|
|
args.AddOption(&ref_levels, "-r", "--refs",
|
|
"Number of h-refinements.");
|
|
args.AddOption(&max_it, "-mi", "--max-it",
|
|
"Maximum number of iterations");
|
|
args.AddOption(&tol, "-tol", "--tol",
|
|
"Stopping criteria based on the difference between"
|
|
"successive solution updates");
|
|
args.AddOption(&alpha, "-step", "--step",
|
|
"Initial size alpha");
|
|
args.AddOption(&growth_rate, "-gr", "--growth-rate",
|
|
"Growth rate of the step size alpha");
|
|
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
|
"--no-visualization",
|
|
"Enable or disable GLVis visualization.");
|
|
args.Parse();
|
|
if (!args.Good())
|
|
{
|
|
args.PrintUsage(cout);
|
|
return 1;
|
|
}
|
|
args.PrintOptions(cout);
|
|
|
|
Mesh mesh = Mesh::MakeCartesian2D(1, 1, Element::Type::TRIANGLE, false);
|
|
const int dim = mesh.Dimension();
|
|
const int sdim = mesh.SpaceDimension();
|
|
|
|
for (int l = 0; l < ref_levels; l++)
|
|
{
|
|
mesh.UniformRefinement();
|
|
}
|
|
|
|
int curvature_order = max(order,2);
|
|
mesh.SetCurvature(curvature_order);
|
|
|
|
RT_FECollection RTfec(order, dim);
|
|
FiniteElementSpace RTfes(&mesh, &RTfec);
|
|
|
|
H1_FECollection h1fec(order_l2, sdim);
|
|
FiniteElementSpace h1fes(&mesh, &h1fec);
|
|
|
|
L2_FECollection L2fec(order_l2, dim);
|
|
FiniteElementSpace L2fes(&mesh, &L2fec, 2);
|
|
|
|
Array<int> ess_tdof_list_rt;
|
|
RTfes.GetBoundaryTrueDofs(ess_tdof_list_rt);
|
|
|
|
Array<int> ess_tdof_list_h1;
|
|
h1fes.GetBoundaryTrueDofs(ess_tdof_list_h1);
|
|
|
|
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
|
ess_bdr = 1;
|
|
|
|
|
|
// ess_tdof_list = 1;
|
|
// if (mesh.bdr_attributes.Size())
|
|
// {
|
|
// Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
|
// ess_bdr = 1;
|
|
|
|
// RTfes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
|
// }
|
|
|
|
cout << "Number of H(div) dofs: "
|
|
<< RTfes.GetTrueVSize() << endl;
|
|
cout << "Number of L² dofs: "
|
|
<< L2fes.GetTrueVSize() << endl;
|
|
cout << "Number of H1 dofs: "
|
|
<< h1fes.GetTrueVSize() << endl;
|
|
|
|
Array<int> offsets({0, RTfes.GetVSize(), h1fes.GetVSize(), L2fes.GetVSize(), 1});
|
|
offsets.PartialSum();
|
|
|
|
BlockVector x(offsets), rhs(offsets);
|
|
x = 0.0; rhs = 0.0;
|
|
|
|
GridFunction p_gf(&RTfes, x.GetBlock(0)), vphi_gf(&h1fes, x.GetBlock(1)), delta_psi_gf(&L2fes, x.GetBlock(2));
|
|
|
|
GridFunction psi_old_gf(&L2fes);
|
|
GridFunction psi_gf(&L2fes);
|
|
GridFunction p_old_gf(&RTfes);
|
|
|
|
delta_psi_gf = 0.0;
|
|
psi_gf = 0.0;
|
|
p_gf = 0.0;
|
|
psi_old_gf = psi_gf;
|
|
p_old_gf = p_gf;
|
|
|
|
VectorGridFunctionCoefficient psi_old_cf(&psi_old_gf), psi_cf(&psi_gf), p_vc(&p_gf);
|
|
|
|
char vishost[] = "localhost";
|
|
int visport = 19916;
|
|
socketstream sol_sock, varphi_sol_sock, true_sock, varphi_true_sock;
|
|
if (visualization)
|
|
{
|
|
sol_sock.open(vishost,visport);
|
|
sol_sock.precision(8);
|
|
|
|
// sol_sock << "keys jlA\n";
|
|
// turn off perspective & light
|
|
// sol_sock << "keys cmA\n"; // colorbar + mesh + anti-alias
|
|
|
|
true_sock.open(vishost,visport);
|
|
true_sock.precision(8);
|
|
|
|
varphi_sol_sock.open(vishost, visport);
|
|
varphi_sol_sock.precision(8);
|
|
|
|
varphi_true_sock.open(vishost, visport);
|
|
varphi_true_sock.precision(8);
|
|
}
|
|
|
|
ConstantCoefficient one_cf(1.0);
|
|
ConstantCoefficient neg_one(-1.0);
|
|
VectorConstantCoefficient zero_vec_cf(Vector({0., 0.}));
|
|
ConstantCoefficient zero_cf(0.0);
|
|
VectorConstantCoefficient one_vec_cf(Vector({1., 1.}));
|
|
VectorConstantCoefficient neg_one_vec_cf(Vector({-1., -1.}));
|
|
|
|
ConstantCoefficient tichonov_cf(tichonov);
|
|
ConstantCoefficient neg_tichonov_cf(-1.0*tichonov);
|
|
|
|
ConstantCoefficient alpha_cf((real_t) alpha);
|
|
ProductCoefficient neg_alpha_cf(neg_one, alpha_cf);
|
|
|
|
ZCoefficient Z(sdim, psi_gf);
|
|
DZCoefficient DZ(sdim, psi_gf);
|
|
ScalarMatrixProductCoefficient neg_DZ(-1.0, DZ);
|
|
|
|
VectorSumCoefficient psi_newton_res(psi_old_cf, psi_cf, 1., -1.);
|
|
|
|
LinearForm b0(&RTfes, rhs.GetBlock(0).GetData()), b2(&L2fes, rhs.GetBlock(2).GetData());
|
|
|
|
b0.AddDomainIntegrator(new VectorFEDomainLFIntegrator(psi_newton_res));
|
|
|
|
VectorFunctionCoefficient f_coeff(2, [ex](const Vector &x, Vector &u) {
|
|
// NOTE: constant example
|
|
// u(0) = 0.5;
|
|
// u(1) = 0.0;
|
|
|
|
// NOTE: linear example
|
|
// u(0) = x(0);
|
|
// u(1) = -1.0 * x(1);
|
|
|
|
if (ex == 1) {
|
|
// NOTE: trig example
|
|
// u(0) = cosh(M_PI*x(0)) * sin(M_PI*x(1));
|
|
// u(1) = sinh(M_PI*x(0)) * cos(M_PI*x(1));
|
|
|
|
// u /= cosh(M_PI);
|
|
// u(0) = pow(x(1), 2) * (1. - 2./3 * x(1));
|
|
// u(1) = pow(x(0), 2) * (-1. + 2./3 * x(0));
|
|
// u*= -4.;
|
|
|
|
u(0) = sin(2 * M_PI * x(0)) * cos(2*M_PI*x(1));
|
|
u(1) = cos(2*M_PI*x(0))*sin(2*M_PI*x(1));
|
|
u *= -.9;
|
|
|
|
}
|
|
else if (ex == 2) {
|
|
// NOTE: trig example 2
|
|
u(0) = cos(M_PI * x(0)) * sin (M_PI * x(1));
|
|
u(1) = cos(M_PI * x(1)) * sin (M_PI * x(0));
|
|
|
|
u *= (1. + 2. * pow(M_PI, 2));
|
|
}
|
|
});
|
|
|
|
// mfem::Coefficient *f_rhs = nullptr;
|
|
//
|
|
// f_rhs = new mfem::FunctionCoefficient([](const mfem::Vector &x)
|
|
// {
|
|
// return 1./4. * sin(M_PI*x(0)) * sin (M_PI*x(1));
|
|
// });
|
|
|
|
// FunctionCoefficient f_rhs([](const mfem::Vector &x)
|
|
// {
|
|
// return 1./4. * cos(M_PI*x(0)) * cos(M_PI*x(1));
|
|
// });
|
|
|
|
ScalarVectorProductCoefficient alpha_f_cf(alpha_cf, f_coeff);
|
|
b0.AddDomainIntegrator(new VectorFEDomainLFIntegrator(alpha_f_cf));
|
|
|
|
|
|
// ProductCoefficient alpha_f_cf(alpha_cf, f_rhs);
|
|
// b0.AddDomainIntegrator(new VectorFEDomainLFDivIntegrator(alpha_f_cf));
|
|
|
|
b2.AddDomainIntegrator(new VectorDomainLFIntegrator(Z));
|
|
|
|
|
|
RotationCoefficient R;
|
|
ScalarMatrixProductCoefficient neg_R(neg_one, R);
|
|
ScalarMatrixProductCoefficient alpha_R(alpha_cf, R);
|
|
|
|
// BlockMatrix A(offsets);
|
|
|
|
BilinearForm a11(&h1fes);
|
|
a11.AddDomainIntegrator(new DiffusionIntegrator(neg_one));
|
|
|
|
// ConstantCoefficient eps_cf(-1e-6);
|
|
|
|
|
|
a11.Assemble(false);
|
|
a11.Finalize(false);
|
|
|
|
SparseMatrix &A11 = a11.SpMat();
|
|
|
|
BilinearForm a22(&L2fes);
|
|
a22.AddDomainIntegrator(new VectorMassIntegrator(neg_DZ));
|
|
// a22.AddDomainIntegrator(new VectorMassIntegrator(eps_cf));
|
|
|
|
|
|
// Avg 0 condition
|
|
int dof_h1(h1fes.GetTrueVSize());
|
|
LinearForm avg0_data(&h1fes);
|
|
avg0_data.AddDomainIntegrator(new DomainLFIntegrator(one_cf));
|
|
avg0_data.Assemble();
|
|
Array<int> avg0_i({0, dof_h1}), avg0_j(dof_h1);
|
|
std::iota(avg0_j.begin(), avg0_j.end(), 0);
|
|
SparseMatrix avg0(avg0_i.GetData(), avg0_j.GetData(), avg0_data.GetData(), 1,
|
|
dof_h1, false, false, true);
|
|
auto avg0T = *Transpose(avg0);
|
|
|
|
|
|
int k;
|
|
int total_iterations = 0;
|
|
real_t increment_p = 0.1;
|
|
GridFunction p_tmp(&RTfes);
|
|
|
|
for (k = 0; k < max_it; k++)
|
|
{
|
|
p_tmp = p_old_gf;
|
|
|
|
mfem::out << "\nOUTER ITERATION " << k+1 << endl;
|
|
|
|
int j;
|
|
for ( j = 0; j < 5; j++)
|
|
{
|
|
total_iterations++;
|
|
|
|
b0.Assemble();
|
|
b2.Assemble();
|
|
|
|
BlockMatrix A(offsets);
|
|
|
|
BilinearForm a00(&RTfes);
|
|
a00.AddDomainIntegrator(new DivDivIntegrator(alpha_cf));
|
|
a00.SetDiagonalPolicy(mfem::Operator::DIAG_ONE);
|
|
|
|
a00.Assemble();
|
|
a00.EliminateEssentialBC(ess_tdof_list_rt, x.GetBlock(0), rhs.GetBlock(0), mfem::Operator::DIAG_ONE);
|
|
a00.Finalize();
|
|
|
|
SparseMatrix &A00 = a00.SpMat();
|
|
|
|
a22.Assemble(false);
|
|
a22.Finalize(false);
|
|
|
|
SparseMatrix &A22 = a22.SpMat();
|
|
|
|
MixedBilinearForm a01(&h1fes, &RTfes);
|
|
a01.AddDomainIntegrator(new MixedVectorGradientIntegrator(alpha_R));
|
|
|
|
a01.Assemble(false);
|
|
a01.EliminateTestDofs(ess_tdof_list_rt);
|
|
a01.Finalize(false);
|
|
|
|
SparseMatrix &A01 = a01.SpMat();
|
|
SparseMatrix *A10 = Transpose(A01);
|
|
*A10 *= 1.0/alpha;
|
|
|
|
// A.SetBlock(0, 1, &A01);
|
|
// A.SetBlock(1, 0, A10);
|
|
|
|
MixedBilinearForm a20(&RTfes, &L2fes);
|
|
a20.AddDomainIntegrator(new VectorFEMassIntegrator());
|
|
|
|
a20.Assemble();
|
|
a20.EliminateTrialDofs(ess_tdof_list_rt, x.GetBlock(0), rhs.GetBlock(0));
|
|
|
|
a20.Finalize();
|
|
SparseMatrix &A20 = a20.SpMat();
|
|
SparseMatrix *A02 = Transpose(A20);
|
|
|
|
// NOTE: this does not work because VectorFEMassIntegrator expects the test & trial spaces to be in a specific order :/
|
|
// MixedBilinearForm a02(&L2fes, &RTfes);
|
|
// a02.AddDomainIntegrator(new VectorFEMassIntegrator());
|
|
// a02.Assemble(false);
|
|
// a02.EliminateTestDofs(ess_bdr);
|
|
// a02.Finalize();
|
|
// SparseMatrix &A02 = a02.SpMat();
|
|
// SparseMatrix *A20 = Transpose(A02);
|
|
|
|
A.SetBlock(0,0,&A00);
|
|
A.SetBlock(1,0,A10);
|
|
A.SetBlock(0,1,&A01);
|
|
A.SetBlock(1,1,&A11);
|
|
A.SetBlock(2,0,&A20);
|
|
A.SetBlock(0,2,A02);
|
|
A.SetBlock(2,2,&A22);
|
|
A.SetBlock(3, 1, &avg0);
|
|
A.SetBlock(1, 3, &avg0T);
|
|
|
|
// TODO: correct the schur preconditioner
|
|
// Vector A22_diag(a22.Height());
|
|
// A22.GetDiag(A22_diag);
|
|
// A22_diag.Reciprocal();
|
|
// SparseMatrix *S = Mult_AtDA(A20, A22_diag);
|
|
|
|
// prec.SetDiagonalBlock(2, new DSmoother(A22));
|
|
// #ifndef MFEM_USE_SUITESPARSE
|
|
BlockDiagonalPreconditioner prec(offsets);
|
|
|
|
prec.SetDiagonalBlock(0,new GSSmoother(A00));
|
|
prec.SetDiagonalBlock(1,new GSSmoother(A11));
|
|
prec.SetDiagonalBlock(2,new DSmoother(A22));
|
|
|
|
prec.owns_blocks = 3;
|
|
|
|
GMRES(A,prec,rhs,x,0,10000,500,1e-12,0.0);
|
|
|
|
// prec.SetDiagonalBlock(0, new GSSmoother(*S));
|
|
// #else
|
|
// prec.SetDiagonalBlock(0, new UMFPackSolver(*S));
|
|
// SparseMatrix *A_mono = A.CreateMonolithic();
|
|
// UMFPackSolver umf(*A_mono);
|
|
// umf.Mult(rhs, x);
|
|
// #endif
|
|
|
|
// delete S;
|
|
|
|
p_tmp -= p_gf;
|
|
real_t Newton_update_size = p_tmp.ComputeL2Error(zero_vec_cf);
|
|
p_tmp = p_gf;
|
|
|
|
// Damped Newton update
|
|
psi_gf.Add(newton_scaling, delta_psi_gf);
|
|
// a11.Update();
|
|
// a22.Update();
|
|
b0.Update();
|
|
b2.Update();
|
|
|
|
if (visualization)
|
|
{
|
|
GridFunction p_vec(&L2fes);
|
|
p_vec.ProjectCoefficient(p_vc);
|
|
|
|
sol_sock << "solution\n" << mesh << p_vec << "window_title 'Discrete solution '" << flush;
|
|
|
|
varphi_sol_sock << "solution\n" << mesh << vphi_gf << "window_title 'Discrete varphi '" << flush;
|
|
}
|
|
|
|
mfem::out << "Newton_update_size = " << Newton_update_size << endl;
|
|
|
|
if (Newton_update_size < increment_p)
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
p_tmp = p_gf;
|
|
p_tmp -= p_old_gf;
|
|
increment_p = p_tmp.ComputeL2Error(zero_vec_cf);
|
|
|
|
mfem::out << "Number of Newton iterations = " << j+1 << endl;
|
|
mfem::out << "Increment (|| uₕ - uₕ_prvs||) = " << increment_p << endl;
|
|
|
|
p_old_gf = p_gf;
|
|
psi_old_gf = psi_gf;
|
|
|
|
if (increment_p < tol || k == max_it-1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
alpha *= max(growth_rate, 1_r);
|
|
alpha_cf.constant = alpha;
|
|
}
|
|
// delete A01;
|
|
// delete A10;
|
|
|
|
mfem::out << "\n Outer iterations: " << k+1
|
|
<< "\n Total iterations: " << total_iterations
|
|
<< "\n Total dofs: " << RTfes.GetTrueVSize() + L2fes.GetTrueVSize()
|
|
<< endl;
|
|
|
|
VectorFunctionCoefficient exact_coeff(2, [ex](const Vector &x, Vector &u) {
|
|
// NOTE: constant example
|
|
// u(0) = 0.5;
|
|
// u(1) = 0.0;
|
|
|
|
// NOTE: linear example
|
|
// u(0) = x(0);
|
|
// u(1) = -1.0 * x(1);
|
|
|
|
if (ex == 1) {
|
|
// NOTE: trig example
|
|
// u(0) = cosh(M_PI*x(0)) * sin(M_PI*x(1));
|
|
// u(1) = sinh(M_PI*x(0)) * cos(M_PI*x(1));
|
|
//
|
|
// u /= cosh(M_PI);
|
|
// u(0) = - sin(M_PI*x(0))*cos(M_PI*x(1));
|
|
// u(1) = cos(M_PI*x(0)) * sin(M_PI*x(1));
|
|
// u *= M_PI;
|
|
|
|
// u(0) = x(0) * (1. - x(0))*(1 - 2.*x(1));
|
|
// u(1) = x(1) * (1. - x(1))*(2.*x(0) - 1.);
|
|
// u*= 4.;
|
|
|
|
u(0) = sin(2 * M_PI * x(0)) * cos(2*M_PI*x(1));
|
|
u(1) = -cos(2*M_PI*x(0))*sin(2*M_PI*x(1));
|
|
u *= 0.9;
|
|
}
|
|
else if (ex == 2) {
|
|
// NOTE trig example 2
|
|
u(0) = cos(M_PI * x(0)) * sin (M_PI * x(1));
|
|
u(1) = cos(M_PI * x(1)) * sin (M_PI * x(0));
|
|
}
|
|
});
|
|
|
|
FunctionCoefficient varphi_exact_coeff([](const Vector &x) {
|
|
return 1 / (2. * M_PI) * sin(2*M_PI*x(0)) * sin(2*M_PI*x(1));
|
|
});
|
|
// GridFunctionCoefficient vphi_exact_gf(&varphi_exact_coeff);
|
|
if (visualization) {
|
|
GridFunction vphi_exact_gf(&h1fes);
|
|
vphi_exact_gf.ProjectCoefficient(varphi_exact_coeff);
|
|
|
|
GridFunction exact_vec(&L2fes);
|
|
exact_vec.ProjectCoefficient(exact_coeff);
|
|
|
|
true_sock << "solution\n" << mesh << exact_vec << "window_title 'True solution '" << flush;
|
|
varphi_true_sock << "solution\n" << mesh << vphi_exact_gf << "window_title 'varphi True Solution '" << flush;
|
|
}
|
|
|
|
if (CheckVectorComponents(p_gf, 1.0))
|
|
{
|
|
std::cout << "Result: SUCCESS. All components are within the limit." << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Result: FAILURE. At least one component is outside the limit." << std::endl;
|
|
}
|
|
|
|
real_t l2_error = p_gf.ComputeL2Error(exact_coeff);
|
|
|
|
cout << "L2 error: " << l2_error << endl;
|
|
|
|
mfem::Coefficient *div_u_exact = nullptr;
|
|
|
|
// NOTE: for constant, linear, trig examples, div p = 0
|
|
if (ex == 1)
|
|
{
|
|
// For ex=1, the divergence is zero.
|
|
div_u_exact = new mfem::ConstantCoefficient(0.0);
|
|
}
|
|
else if (ex == 2) {
|
|
// NOTE: for trig example2, div p != 0
|
|
div_u_exact = new mfem::FunctionCoefficient([](const mfem::Vector &x)
|
|
{
|
|
return -2. * M_PI * sin(M_PI * x(0)) * sin(M_PI * x(1));
|
|
});
|
|
}
|
|
|
|
real_t hdiv_error = p_gf.ComputeDivError(div_u_exact);
|
|
|
|
cout << "div error: " << hdiv_error << endl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// NOTE: 2D ONLY
|
|
void ZCoefficient::Eval(Vector &V, ElementTransformation &T,
|
|
const IntegrationPoint &ip)
|
|
{
|
|
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
|
|
|
Vector psi_vals(2);
|
|
psi->GetVectorValue(T, ip, psi_vals);
|
|
|
|
V.SetSize(2);
|
|
|
|
for (int i = 0; i < psi_vals.Size(); ++i) { V(i) = tanh(psi_vals(i) / 2.); }
|
|
}
|
|
|
|
// NOTE: 2D ONLY
|
|
void DZCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
|
const IntegrationPoint &ip)
|
|
{
|
|
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
|
|
|
Vector psi_vals(2);
|
|
psi->GetVectorValue(T, ip, psi_vals);
|
|
|
|
K.SetSize(2);
|
|
K = 0.0;
|
|
for (int i = 0; i < 2; ++i) { K(i, i) = (1. - pow(tanh(psi_vals(i) / 2.), 2)) / 2.; }
|
|
}
|