Files
mfem/examples/ex1p.cpp
T
2011-04-08 15:27:24 -07:00

195 lines
6.4 KiB
C++

// MFEM Example 1 - Parallel Version
//
// Compile with: make ex1p
//
// Sample runs: mpirun -np 4 ex1p ../data/square-disc.mesh
// mpirun -np 4 ex1p ../data/star.mesh
// mpirun -np 4 ex1p ../data/escher.mesh
// mpirun -np 4 ex1p ../data/fichera.mesh
// mpirun -np 4 ex1p ../data/square-disc-p2.vtk
// mpirun -np 4 ex1p ../data/square-disc-p3.mesh
//
// Description: This example code demonstrates the use of MFEM to define a
// simple linear finite element discretization of the Laplace
// problem -Delta u = 1 with homogeneous Dirichlet boundary
// conditions.
//
// The example highlights the use of mesh refinement, finite
// element grid functions, as well as linear and bilinear forms
// corresponding to the left-hand side and right-hand side of the
// discrete linear system. We also cover the explicit elimination
// of boundary conditions on all boundary edges, and the optional
// connection to the GLVis tool for visualization.
#include <fstream>
#include "mfem.hpp"
int main (int argc, char *argv[])
{
int num_procs, myid;
// 1. Initialize MPI
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
Mesh *mesh;
if (argc == 1)
{
if (myid == 0)
cout << "\nUsage: mpirun -np <np> ex1p <mesh_file>\n" << endl;
MPI_Finalize();
return 1;
}
// 2. Read the (serial) mesh from the given mesh file on all processors.
// We can handle triangular, quadrilateral, tetrahedral or hexahedral
// elements with the same code.
ifstream imesh(argv[1]);
if (!imesh)
{
if (myid == 0)
cerr << "\nCan not open mesh file: " << argv[1] << '\n' << endl;
MPI_Finalize();
return 2;
}
mesh = new Mesh(imesh, 1, 1);
imesh.close();
// 3. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 10,000 elements.
{
int ref_levels =
(int)floor(log(10000./mesh->GetNE())/log(2.)/mesh->Dimension());
for (int l = 0; l < ref_levels; l++)
mesh->UniformRefinement();
}
// 4. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
pmesh->UniformRefinement();
}
// 5. Define a parallel finite element space on the parallel mesh. Here we
// use linear finite elements.
FiniteElementCollection *fec = new LinearFECollection;
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
// 6. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (1,phi_i) where phi_i are the basis functions in fespace.
ParLinearForm *b = new ParLinearForm(fespace);
ConstantCoefficient one(1.0);
b->AddDomainIntegrator(new DomainLFIntegrator(one));
b->Assemble();
// 7. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(fespace);
(Vector &)x = 0.0;
// 8. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator and imposing homogeneous Dirichlet boundary
// conditions. The boundary conditions are implemented by marking all the
// boundary attributes from the mesh as essential. After serial and
// parallel assembly we extract the corresponding parallel matrix A.
ParBilinearForm *a = new ParBilinearForm(fespace);
a->AddDomainIntegrator(new DiffusionIntegrator(one));
a->Assemble();
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
Array<int> ess_dofs;
fespace->GetEssentialVDofs(ess_bdr, ess_dofs);
a->EliminateEssentialBCFromDofs(ess_dofs, x, *b);
}
a->Finalize();
// 9. Define the parallel (hypre) matrix and vectors representing a(.,.),
// b(.) and the finite element approximation.
HypreParMatrix *A = a->ParallelAssemble();
HypreParVector *B = b->ParallelAssemble();
HypreParVector *X = x.ParallelAverage();
delete a;
delete b;
// 10. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
// preconditioner from hypre.
HypreSolver *amg = new HypreBoomerAMG(*A);
HyprePCG *pcg = new HyprePCG(*A);
pcg->SetTol(1e-12);
pcg->SetMaxIter(200);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*amg);
pcg->Mult(*B, *X);
// 11. Extract the parallel grid function corresponding to the finite element
// approximation X. This is the local solution on each processor.
x = *X;
// 12. 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;
if (myid == 0)
mesh_ofs.open("refined.mesh");
pmesh->PrintAsOne(mesh_ofs);
if (myid == 0)
mesh_ofs.close();
ofstream sol_ofs;
if (myid == 0)
sol_ofs.open("sol.gf");
x.SaveAsOne(sol_ofs);
if (myid == 0)
sol_ofs.close();
}
// 13. (Optional) Send the solution by socket to a GLVis server.
char vishost[] = "localhost";
int visport = 19916;
osockstream *sol_sock;
if (myid == 0)
{
sol_sock = new osockstream(visport, vishost);
if (pmesh->Dimension() == 2)
*sol_sock << "fem2d_gf_data\n";
else
*sol_sock << "fem3d_gf_data\n";
}
pmesh->PrintAsOne(*sol_sock);
x.SaveAsOne(*sol_sock);
if (myid == 0)
{
sol_sock->send();
delete sol_sock;
}
// 14. Free the used memory.
delete pcg;
delete amg;
delete X;
delete B;
delete A;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}