Compare commits

...
3 changed files with 971 additions and 1 deletions
+515
View File
@@ -0,0 +1,515 @@
// MFEM Example ?? - Serial Version
//
// Compile with: make amr-battery
//
// Sample runs: amr-battery -rs 7
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the thermal battery
// problem described in Mitchell 2013.
//
// The boundary conditions are defined as (where u is the solution
// field):
//
// Dirichlet: u = d
// Neumann: n.Grad(u) = g
// Robin: n.Grad(u) + a u = b
//
// We recommend viewing Examples 1 and 27 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
static double cx[5] = {0.0, 6.1, 6.5, 8.0, 8.4};
static double cy[8] = {0.0, 0.8, 1.6, 3.6, 18.8, 21.2, 23.2, 24.0};
static double cp[5] = {25.0, 7.0, 5.0, 0.2, 0.05};
static double cq[5] = {25.0, 0.8, 0.0001, 0.2, 0.05};
static double cf[5] = {0.0, 1.0, 1.0, 0.0, 0.0};
enum BoundarySegment
{
top = 1, // 1-based for MFEM attributes
right = 2,
bottom = 3,
left = 4
};
Mesh * GenerateSerialMesh(const double sx, const double sy);
void diffCoeff(const Vector &x, Vector &d);
double fcoeff(const Vector & x);
// Compute the average value of alpha*n.Grad(sol) + beta*sol over the boundary
// attributes marked in bdr_marker. Also computes the L2 norm of
// alpha*n.Grad(sol) + beta*sol - gamma over the same boundary.
double IntegrateBC(const GridFunction &sol, const Array<int> &bdr_marker,
double alpha, double beta, double gamma,
double &err);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
int ser_ref_levels = 2;
int order = 1;
bool visualization = true;
double nbc_val = 0.0;
int max_dofs = 50000;
OptionsParser args(argc, argv);
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&max_dofs, "-md", "--max-dofs",
"Stop after reaching this many degrees of freedom.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(mfem::out);
// 2. Construct the (serial) mesh and refine it if requested.
Mesh *mesh = GenerateSerialMesh(cx[4], cy[7]);
for (int l = 0; l < ser_ref_levels; l++)
{
mesh->UniformRefinement();
}
int dim = mesh->Dimension();
// 3. Define a finite element space on the serial mesh. Here we use either
// continuous Lagrange finite elements or discontinuous Galerkin finite
// elements of the specified order.
H1_FECollection fec(order, dim);
FiniteElementSpace fespace(mesh, &fec);
int size = fespace.GetTrueVSize();
mfem::out << "Number of finite element unknowns: " << size << endl;
// 4. Create "marker arrays" to define the portions of boundary associated
// with each type of boundary condition. These arrays have an entry
// corresponding to each boundary attribute. Placing a '1' in entry i
// marks attribute i+1 as being active, '0' is inactive.
Array<int> nbc_bdr(mesh->bdr_attributes.Max());
Array<int> rbc_bdr_top(mesh->bdr_attributes.Max());
Array<int> rbc_bdr_right(mesh->bdr_attributes.Max());
Array<int> rbc_bdr_bottom(mesh->bdr_attributes.Max());
//Array<int> dbc_bdr(mesh->bdr_attributes.Max());
nbc_bdr = 0; nbc_bdr[BoundarySegment::left - 1] = 1;
rbc_bdr_top = 0; rbc_bdr_top[BoundarySegment::top - 1] = 1;
rbc_bdr_right = 0; rbc_bdr_right[BoundarySegment::right - 1] = 1;
rbc_bdr_bottom = 0; rbc_bdr_bottom[BoundarySegment::bottom - 1] = 1;
Array<int> ess_tdof_list(0);
// 5. Setup the various coefficients needed for the Laplace operator and the
// various boundary conditions.
const double c_top = 1.0;
const double c_right = 2.0;
const double c_bottom = 3.0;
const double gn_top = 3.0;
const double gn_right = 2.0;
const double gn_bottom = 1.0;
VectorFunctionCoefficient matCoef(2, diffCoeff);
ConstantCoefficient nbcCoef(nbc_val);
ConstantCoefficient rbcACoef_top(c_top);
ConstantCoefficient rbcACoef_right(c_right);
ConstantCoefficient rbcACoef_bottom(c_bottom);
ConstantCoefficient rbcBCoef_top(gn_top);
ConstantCoefficient rbcBCoef_right(gn_right);
ConstantCoefficient rbcBCoef_bottom(gn_bottom);
// 6. Define the solution vector u as a finite element grid function
// corresponding to fespace. Initialize u with initial guess of zero.
GridFunction u(&fespace);
u = 0.0;
// 7. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
BilinearForm a(&fespace);
BilinearFormIntegrator *integ = new DiffusionIntegrator(matCoef);
a.AddDomainIntegrator(integ);
// Add a mass integrator on the Robin boundary
a.AddBoundaryIntegrator(new MassIntegrator(rbcACoef_top), rbc_bdr_top);
a.AddBoundaryIntegrator(new MassIntegrator(rbcACoef_right), rbc_bdr_right);
a.AddBoundaryIntegrator(new MassIntegrator(rbcACoef_bottom), rbc_bdr_bottom);
// 8. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// that uses the ComputeElementFlux method of the DiffusionIntegrator to
// recover a smoothed flux (gradient) that is subtracted from the element
// flux to get an error indicator. We need to supply the space for the
// smoothed flux: an (H1)^sdim (i.e., vector-valued) space is used here.
FiniteElementSpace flux_fespace(mesh, &fec, dim);
ZienkiewiczZhuEstimator estimator(*integ, u, flux_fespace);
estimator.SetAnisotropic();
// 9. A refiner selects and refines elements based on a refinement strategy.
// The strategy here is to refine elements with errors larger than a
// fraction of the maximum element error. Other strategies are possible.
// The refiner will call the given error estimator.
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.7);
// 10. Assemble the linear form for the right hand side vector.
LinearForm b(&fespace);
FunctionCoefficient rhs(fcoeff);
b.AddDomainIntegrator(new DomainLFIntegrator(rhs));
// Add the desired value for n.Grad(u) on the Neumann boundary
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(nbcCoef), nbc_bdr);
// Add the desired value for n.Grad(u) + a*u on the Robin boundary
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(rbcBCoef_top), rbc_bdr_top);
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(rbcBCoef_right), rbc_bdr_right);
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(rbcBCoef_bottom), rbc_bdr_bottom);
// 11. The main AMR loop. In each iteration we solve the problem on the
// current mesh, visualize the solution, and refine the mesh.
for (int it = 0; ; it++)
{
int cdofs = fespace.GetTrueVSize();
cout << "\nAMR iteration " << it << endl;
cout << "Number of unknowns: " << cdofs << endl;
a.Assemble();
b.Assemble();
// 12. Construct the linear system.
OperatorPtr A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, u, b, A, X, B);
#ifndef MFEM_USE_SUITESPARSE
// 13. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system AX=B with PCG in the symmetric case, and GMRES in the
// non-symmetric one.
{
GSSmoother M((SparseMatrix&)(*A));
//DSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 1, 5000, 1e-12, 0.0);
}
#else
// 13. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the
// system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
#endif
// 14. Recover the grid function corresponding to U. This is the local finite
// element solution.
a.RecoverFEMSolution(X, b, u);
// 15. Send the solution by socket to a GLVis server.
if (visualization)
{
string title_str = "H1";
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *mesh << u
<< "window_title '" << title_str << " Solution'"
<< " keys 'mmc'" << flush;
}
// 16. Compute the various boundary integrals.
mfem::out << endl
<< "Verifying boundary conditions" << endl
<< "=============================" << endl;
{
// Integrate n.Grad(u) on the homogeneous Neumann boundary and compare to
// the expected value of zero.
double err, avg = IntegrateBC(u, nbc_bdr, 1.0, 0.0, nbc_val, err);
bool hom_nbc = true;
mfem::out << "Average of n.Grad(u) on Gamma_nbc:\t"
<< avg << ", \t"
<< (hom_nbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) + a * u on the Robin boundary and compare to the
// expected value.
double err, avg = IntegrateBC(u, rbc_bdr_top, cq[0], c_top, gn_top, err);
bool hom_rbc = (gn_top == 0.0);
err /= hom_rbc ? 1.0 : fabs(gn_top);
mfem::out << "Average of q n.Grad(u)+c*u on Gamma_rbc_top:\t"
<< avg << ", \t"
<< (hom_rbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) + a * u on the Robin boundary and compare to the
// expected value.
double err, avg = IntegrateBC(u, rbc_bdr_right, cq[0], c_right, gn_right, err);
bool hom_rbc = (gn_right == 0.0);
err /= hom_rbc ? 1.0 : fabs(gn_right);
mfem::out << "Average of q n.Grad(u)+c*u on Gamma_rbc_right:\t"
<< avg << ", \t"
<< (hom_rbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) + a * u on the Robin boundary and compare to the
// expected value.
double err, avg = IntegrateBC(u, rbc_bdr_bottom, cq[0], c_bottom, gn_bottom, err);
bool hom_rbc = (gn_bottom == 0.0);
err /= hom_rbc ? 1.0 : fabs(gn_bottom);
mfem::out << "Average of q n.Grad(u)+c*u on Gamma_rbc_bottom:\t"
<< avg << ", \t"
<< (hom_rbc ? "absolute" : "relative")
<< " error " << err << endl;
}
if (cdofs > max_dofs)
{
cout << "Reached the maximum number of dofs. Stop." << endl;
break;
}
// 17. Call the refiner to modify the mesh. The refiner calls the error
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
refiner.Apply(*mesh);
if (refiner.Stop())
{
cout << "Stopping criterion satisfied. Stop." << endl;
break;
}
// 18. Update the space to reflect the new state of the mesh. Also,
// interpolate the solution x so that it lies in the new space but
// represents the same function. This saves solver iterations later
// since we'll have a good initial guess of x in the next step.
// Internally, FiniteElementSpace::Update() calculates an
// interpolation matrix which is then used by GridFunction::Update().
fespace.Update();
u.Update();
// 19. Inform also the bilinear and linear forms that the space has
// changed.
a.Update();
b.Update();
} // end of AMR loop
// 20. Save the refined mesh and the solution. This output can be viewed
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
{
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
u.Save(sol_ofs);
}
// 21. Free the used memory.
delete mesh;
return 0;
}
Mesh * GenerateSerialMesh(const double sx, const double sy)
{
Mesh mesh = Mesh::MakeCartesian2D(1, 1, Element::Type::QUADRILATERAL, true, sx, sy);
for (int i=0; i<mesh.GetNBE(); ++i)
{
Element *bdryElem = mesh.GetBdrElement(i);
Array<int> v;
bdryElem->GetVertices(v);
MFEM_VERIFY(v.Size() == 2, "");
const double mx = 0.5 * (mesh.GetVertex(v[0])[0] + mesh.GetVertex(v[1])[0]);
const double my = 0.5 * (mesh.GetVertex(v[0])[1] + mesh.GetVertex(v[1])[1]);
if (mx == 0.0)
mesh.SetBdrAttribute(i, BoundarySegment::left);
else if (my == 0.0)
mesh.SetBdrAttribute(i, BoundarySegment::bottom);
else if (mx == sx)
mesh.SetBdrAttribute(i, BoundarySegment::right);
else if (my == sy)
mesh.SetBdrAttribute(i, BoundarySegment::top);
else
{
MFEM_ABORT("attribute not set");
}
}
MFEM_VERIFY(mesh.bdr_attributes.Max() == 4, "");
return new Mesh(mesh);
}
double IntegrateBC(const GridFunction &x, const Array<int> &bdr,
double alpha, double beta, double gamma,
double &err)
{
double nrm = 0.0;
double avg = 0.0;
err = 0.0;
const bool a_is_zero = alpha == 0.0;
const bool b_is_zero = beta == 0.0;
const FiniteElementSpace &fes = *x.FESpace();
MFEM_ASSERT(fes.GetVDim() == 1, "");
Mesh &mesh = *fes.GetMesh();
Vector shape, loc_dofs, w_nor;
DenseMatrix dshape;
Array<int> dof_ids;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (bdr[mesh.GetBdrAttribute(i)-1] == 0) { continue; }
FaceElementTransformations *FTr = mesh.GetBdrFaceTransformations(i);
if (FTr == nullptr) { continue; }
const FiniteElement &fe = *fes.GetFE(FTr->Elem1No);
MFEM_ASSERT(fe.GetMapType() == FiniteElement::VALUE, "");
const int int_order = 2*fe.GetOrder() + 3;
const IntegrationRule &ir = IntRules.Get(FTr->FaceGeom, int_order);
fes.GetElementDofs(FTr->Elem1No, dof_ids);
x.GetSubVector(dof_ids, loc_dofs);
if (!a_is_zero)
{
const int sdim = FTr->Face->GetSpaceDim();
w_nor.SetSize(sdim);
dshape.SetSize(fe.GetDof(), sdim);
}
if (!b_is_zero)
{
shape.SetSize(fe.GetDof());
}
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
IntegrationPoint eip;
FTr->Loc1.Transform(ip, eip);
FTr->Face->SetIntPoint(&ip);
double face_weight = FTr->Face->Weight();
double val = 0.0;
if (!a_is_zero)
{
FTr->Elem1->SetIntPoint(&eip);
fe.CalcPhysDShape(*FTr->Elem1, dshape);
CalcOrtho(FTr->Face->Jacobian(), w_nor);
val += alpha * dshape.InnerProduct(w_nor, loc_dofs) / face_weight;
}
if (!b_is_zero)
{
fe.CalcShape(eip, shape);
val += beta * (shape * loc_dofs);
}
// Measure the length of the boundary
nrm += ip.weight * face_weight;
// Integrate alpha * n.Grad(x) + beta * x
avg += val * ip.weight * face_weight;
// Integrate |alpha * n.Grad(x) + beta * x - gamma|^2
val -= gamma;
err += (val*val) * ip.weight * face_weight;
}
}
// Normalize by the length of the boundary
if (std::abs(nrm) > 0.0)
{
err /= nrm;
avg /= nrm;
}
// Compute l2 norm of the error in the boundary condition (negative
// quadrature weights may produce negative 'err')
err = (err >= 0.0) ? sqrt(err) : -sqrt(-err);
// Return the average value of alpha * n.Grad(x) + beta * x
return avg;
}
int GetSubdomain(const Vector & x)
{
int ix{0};
int iy{0};
for (int i=1; i<4; ++i)
{
if (x[0] >= cx[i])
ix = i;
else
break;
}
for (int i=1; i<7; ++i)
{
if (x[1] >= cy[i])
iy = i;
else
break;
}
int k = 5;
if (iy == 0 || iy == 6 || ix == 3)
k = 1;
else if (ix == 1 && iy > 0 && iy < 5)
k = 4;
else if (ix == 0 && iy == 3)
k = 3;
else if (ix == 0 && (iy == 2 || iy == 4))
k = 2;
return k;
}
double pcoeff(const Vector & x)
{
return cp[GetSubdomain(x) - 1];
}
double qcoeff(const Vector & x)
{
return cq[GetSubdomain(x) - 1];
}
void diffCoeff(const Vector &x, Vector &d)
{
d = 0.0;
d(0) = pcoeff(x);
d(1) = qcoeff(x);
}
double fcoeff(const Vector & x)
{
return cf[GetSubdomain(x) - 1];
}
+455
View File
@@ -0,0 +1,455 @@
// MFEM Example ?? - Serial Version
//
// Compile with: make battery
//
// Sample runs: battery -rs 7
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the thermal battery
// problem described in Mitchell 2013.
//
// The boundary conditions are defined as (where u is the solution
// field):
//
// Dirichlet: u = d
// Neumann: n.Grad(u) = g
// Robin: n.Grad(u) + a u = b
//
// We recommend viewing Examples 1 and 27 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
static double cx[5] = {0.0, 6.1, 6.5, 8.0, 8.4};
static double cy[8] = {0.0, 0.8, 1.6, 3.6, 18.8, 21.2, 23.2, 24.0};
static double cp[5] = {25.0, 7.0, 5.0, 0.2, 0.05};
static double cq[5] = {25.0, 0.8, 0.0001, 0.2, 0.05};
static double cf[5] = {0.0, 1.0, 1.0, 0.0, 0.0};
enum BoundarySegment
{
top = 1, // 1-based for MFEM attributes
right = 2,
bottom = 3,
left = 4
};
Mesh * GenerateSerialMesh(const double sx, const double sy);
void diffCoeff(const Vector &x, Vector &d);
double fcoeff(const Vector & x);
// Compute the average value of alpha*n.Grad(sol) + beta*sol over the boundary
// attributes marked in bdr_marker. Also computes the L2 norm of
// alpha*n.Grad(sol) + beta*sol - gamma over the same boundary.
double IntegrateBC(const GridFunction &sol, const Array<int> &bdr_marker,
double alpha, double beta, double gamma,
double &err);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
int ser_ref_levels = 2;
int order = 1;
bool visualization = true;
double nbc_val = 0.0;
OptionsParser args(argc, argv);
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(mfem::out);
// 2. Construct the (serial) mesh and refine it if requested.
Mesh *mesh = GenerateSerialMesh(cx[4], cy[7]);
for (int l = 0; l < ser_ref_levels; l++)
{
mesh->UniformRefinement();
}
int dim = mesh->Dimension();
// 3. Define a finite element space on the serial mesh. Here we use either
// continuous Lagrange finite elements or discontinuous Galerkin finite
// elements of the specified order.
H1_FECollection fec(order, dim);
FiniteElementSpace fespace(mesh, &fec);
int size = fespace.GetTrueVSize();
mfem::out << "Number of finite element unknowns: " << size << endl;
// 4. Create "marker arrays" to define the portions of boundary associated
// with each type of boundary condition. These arrays have an entry
// corresponding to each boundary attribute. Placing a '1' in entry i
// marks attribute i+1 as being active, '0' is inactive.
Array<int> nbc_bdr(mesh->bdr_attributes.Max());
Array<int> rbc_bdr_top(mesh->bdr_attributes.Max());
Array<int> rbc_bdr_right(mesh->bdr_attributes.Max());
Array<int> rbc_bdr_bottom(mesh->bdr_attributes.Max());
//Array<int> dbc_bdr(mesh->bdr_attributes.Max());
nbc_bdr = 0; nbc_bdr[BoundarySegment::left - 1] = 1;
rbc_bdr_top = 0; rbc_bdr_top[BoundarySegment::top - 1] = 1;
rbc_bdr_right = 0; rbc_bdr_right[BoundarySegment::right - 1] = 1;
rbc_bdr_bottom = 0; rbc_bdr_bottom[BoundarySegment::bottom - 1] = 1;
Array<int> ess_tdof_list(0);
// 5. Setup the various coefficients needed for the Laplace operator and the
// various boundary conditions.
const double c_top = 1.0;
const double c_right = 2.0;
const double c_bottom = 3.0;
const double gn_top = 3.0;
const double gn_right = 2.0;
const double gn_bottom = 1.0;
VectorFunctionCoefficient matCoef(2, diffCoeff);
ConstantCoefficient nbcCoef(nbc_val);
ConstantCoefficient rbcACoef_top(c_top);
ConstantCoefficient rbcACoef_right(c_right);
ConstantCoefficient rbcACoef_bottom(c_bottom);
ConstantCoefficient rbcBCoef_top(gn_top);
ConstantCoefficient rbcBCoef_right(gn_right);
ConstantCoefficient rbcBCoef_bottom(gn_bottom);
// 6. Define the solution vector u as a finite element grid function
// corresponding to fespace. Initialize u with initial guess of zero.
GridFunction u(&fespace);
u = 0.0;
// 7. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
BilinearForm a(&fespace);
a.AddDomainIntegrator(new DiffusionIntegrator(matCoef));
// Add a mass integrator on the Robin boundary
a.AddBoundaryIntegrator(new MassIntegrator(rbcACoef_top), rbc_bdr_top);
a.AddBoundaryIntegrator(new MassIntegrator(rbcACoef_right), rbc_bdr_right);
a.AddBoundaryIntegrator(new MassIntegrator(rbcACoef_bottom), rbc_bdr_bottom);
a.Assemble();
// 8. Assemble the linear form for the right hand side vector.
LinearForm b(&fespace);
FunctionCoefficient rhs(fcoeff);
b.AddDomainIntegrator(new DomainLFIntegrator(rhs));
// Add the desired value for n.Grad(u) on the Neumann boundary
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(nbcCoef), nbc_bdr);
// Add the desired value for n.Grad(u) + a*u on the Robin boundary
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(rbcBCoef_top), rbc_bdr_top);
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(rbcBCoef_right), rbc_bdr_right);
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(rbcBCoef_bottom), rbc_bdr_bottom);
b.Assemble();
// 9. Construct the linear system.
OperatorPtr A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, u, b, A, X, B);
#ifndef MFEM_USE_SUITESPARSE
// 10. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system AX=B with PCG in the symmetric case, and GMRES in the
// non-symmetric one.
{
GSSmoother M((SparseMatrix&)(*A));
//DSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 1, 5000, 1e-12, 0.0);
}
#else
// 11. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the
// system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
#endif
// 12. Recover the grid function corresponding to U. This is the local finite
// element solution.
a.RecoverFEMSolution(X, b, u);
// 13. Compute the various boundary integrals.
mfem::out << endl
<< "Verifying boundary conditions" << endl
<< "=============================" << endl;
{
// Integrate n.Grad(u) on the homogeneous Neumann boundary and compare to
// the expected value of zero.
double err, avg = IntegrateBC(u, nbc_bdr, 1.0, 0.0, nbc_val, err);
bool hom_nbc = true;
mfem::out << "Average of n.Grad(u) on Gamma_nbc:\t"
<< avg << ", \t"
<< (hom_nbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) + a * u on the Robin boundary and compare to the
// expected value.
double err, avg = IntegrateBC(u, rbc_bdr_top, cq[0], c_top, gn_top, err);
bool hom_rbc = (gn_top == 0.0);
err /= hom_rbc ? 1.0 : fabs(gn_top);
mfem::out << "Average of q n.Grad(u)+c*u on Gamma_rbc_top:\t"
<< avg << ", \t"
<< (hom_rbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) + a * u on the Robin boundary and compare to the
// expected value.
double err, avg = IntegrateBC(u, rbc_bdr_right, cq[0], c_right, gn_right, err);
bool hom_rbc = (gn_right == 0.0);
err /= hom_rbc ? 1.0 : fabs(gn_right);
mfem::out << "Average of q n.Grad(u)+c*u on Gamma_rbc_right:\t"
<< avg << ", \t"
<< (hom_rbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) + a * u on the Robin boundary and compare to the
// expected value.
double err, avg = IntegrateBC(u, rbc_bdr_bottom, cq[0], c_bottom, gn_bottom, err);
bool hom_rbc = (gn_bottom == 0.0);
err /= hom_rbc ? 1.0 : fabs(gn_bottom);
mfem::out << "Average of q n.Grad(u)+c*u on Gamma_rbc_bottom:\t"
<< avg << ", \t"
<< (hom_rbc ? "absolute" : "relative")
<< " error " << err << endl;
}
// 14. Save the refined mesh and the solution. This output can be viewed
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
{
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
u.Save(sol_ofs);
}
// 15. Send the solution by socket to a GLVis server.
if (visualization)
{
string title_str = "H1";
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *mesh << u
<< "window_title '" << title_str << " Solution'"
<< " keys 'mmc'" << flush;
}
// 16. Free the used memory.
delete mesh;
return 0;
}
Mesh * GenerateSerialMesh(const double sx, const double sy)
{
Mesh mesh = Mesh::MakeCartesian2D(1, 1, Element::Type::QUADRILATERAL, false, sx, sy);
for (int i=0; i<mesh.GetNBE(); ++i)
{
Element *bdryElem = mesh.GetBdrElement(i);
Array<int> v;
bdryElem->GetVertices(v);
MFEM_VERIFY(v.Size() == 2, "");
const double mx = 0.5 * (mesh.GetVertex(v[0])[0] + mesh.GetVertex(v[1])[0]);
const double my = 0.5 * (mesh.GetVertex(v[0])[1] + mesh.GetVertex(v[1])[1]);
if (mx == 0.0)
mesh.SetBdrAttribute(i, BoundarySegment::left);
else if (my == 0.0)
mesh.SetBdrAttribute(i, BoundarySegment::bottom);
else if (mx == sx)
mesh.SetBdrAttribute(i, BoundarySegment::right);
else if (my == sy)
mesh.SetBdrAttribute(i, BoundarySegment::top);
else
{
MFEM_ABORT("attribute not set");
}
}
MFEM_VERIFY(mesh.bdr_attributes.Max() == 4, "");
return new Mesh(mesh);
}
double IntegrateBC(const GridFunction &x, const Array<int> &bdr,
double alpha, double beta, double gamma,
double &err)
{
double nrm = 0.0;
double avg = 0.0;
err = 0.0;
const bool a_is_zero = alpha == 0.0;
const bool b_is_zero = beta == 0.0;
const FiniteElementSpace &fes = *x.FESpace();
MFEM_ASSERT(fes.GetVDim() == 1, "");
Mesh &mesh = *fes.GetMesh();
Vector shape, loc_dofs, w_nor;
DenseMatrix dshape;
Array<int> dof_ids;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (bdr[mesh.GetBdrAttribute(i)-1] == 0) { continue; }
FaceElementTransformations *FTr = mesh.GetBdrFaceTransformations(i);
if (FTr == nullptr) { continue; }
const FiniteElement &fe = *fes.GetFE(FTr->Elem1No);
MFEM_ASSERT(fe.GetMapType() == FiniteElement::VALUE, "");
const int int_order = 2*fe.GetOrder() + 3;
const IntegrationRule &ir = IntRules.Get(FTr->FaceGeom, int_order);
fes.GetElementDofs(FTr->Elem1No, dof_ids);
x.GetSubVector(dof_ids, loc_dofs);
if (!a_is_zero)
{
const int sdim = FTr->Face->GetSpaceDim();
w_nor.SetSize(sdim);
dshape.SetSize(fe.GetDof(), sdim);
}
if (!b_is_zero)
{
shape.SetSize(fe.GetDof());
}
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
IntegrationPoint eip;
FTr->Loc1.Transform(ip, eip);
FTr->Face->SetIntPoint(&ip);
double face_weight = FTr->Face->Weight();
double val = 0.0;
if (!a_is_zero)
{
FTr->Elem1->SetIntPoint(&eip);
fe.CalcPhysDShape(*FTr->Elem1, dshape);
CalcOrtho(FTr->Face->Jacobian(), w_nor);
val += alpha * dshape.InnerProduct(w_nor, loc_dofs) / face_weight;
}
if (!b_is_zero)
{
fe.CalcShape(eip, shape);
val += beta * (shape * loc_dofs);
}
// Measure the length of the boundary
nrm += ip.weight * face_weight;
// Integrate alpha * n.Grad(x) + beta * x
avg += val * ip.weight * face_weight;
// Integrate |alpha * n.Grad(x) + beta * x - gamma|^2
val -= gamma;
err += (val*val) * ip.weight * face_weight;
}
}
// Normalize by the length of the boundary
if (std::abs(nrm) > 0.0)
{
err /= nrm;
avg /= nrm;
}
// Compute l2 norm of the error in the boundary condition (negative
// quadrature weights may produce negative 'err')
err = (err >= 0.0) ? sqrt(err) : -sqrt(-err);
// Return the average value of alpha * n.Grad(x) + beta * x
return avg;
}
int GetSubdomain(const Vector & x)
{
int ix{0};
int iy{0};
for (int i=1; i<4; ++i)
{
if (x[0] >= cx[i])
ix = i;
else
break;
}
for (int i=1; i<7; ++i)
{
if (x[1] >= cy[i])
iy = i;
else
break;
}
int k = 5;
if (iy == 0 || iy == 6 || ix == 3)
k = 1;
else if (ix == 1 && iy > 0 && iy < 5)
k = 4;
else if (ix == 0 && iy == 3)
k = 3;
else if (ix == 0 && (iy == 2 || iy == 4))
k = 2;
return k;
}
double pcoeff(const Vector & x)
{
return cp[GetSubdomain(x) - 1];
}
double qcoeff(const Vector & x)
{
return cq[GetSubdomain(x) - 1];
}
void diffCoeff(const Vector &x, Vector &d)
{
d = 0.0;
d(0) = pcoeff(x);
d(1) = qcoeff(x);
}
double fcoeff(const Vector & x)
{
return cf[GetSubdomain(x) - 1];
}
+1 -1
View File
@@ -996,7 +996,7 @@ void DiffusionIntegrator::ComputeElementFlux
#endif
vec.SetSize(dim);
vecdxt.SetSize(spaceDim);
pointflux.SetSize(MQ ? spaceDim : 0);
pointflux.SetSize(MQ || VQ ? spaceDim : 0);
const IntegrationRule &ir = fluxelem.GetNodes();
fnd = ir.GetNPoints();