MFEM  v4.3.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
ex4p.cpp
Go to the documentation of this file.
1 // MFEM Example 4 - Parallel Version
2 //
3 // Compile with: make ex4p
4 //
5 // Sample runs: mpirun -np 4 ex4p -m ../data/square-disc.mesh
6 // mpirun -np 4 ex4p -m ../data/star.mesh
7 // mpirun -np 4 ex4p -m ../data/beam-tet.mesh
8 // mpirun -np 4 ex4p -m ../data/beam-hex.mesh
9 // mpirun -np 4 ex4p -m ../data/beam-hex.mesh -o 2 -pa
10 // mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
11 // mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
12 // mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
13 // mpirun -np 4 ex4p -m ../data/fichera-q3.mesh -o 2 -sc
14 // mpirun -np 4 ex4p -m ../data/square-disc-nurbs.mesh -o 3
15 // mpirun -np 4 ex4p -m ../data/beam-hex-nurbs.mesh -o 3
16 // mpirun -np 4 ex4p -m ../data/periodic-square.mesh -no-bc
17 // mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
18 // mpirun -np 4 ex4p -m ../data/amr-quad.mesh
19 // mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
20 // mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
21 // mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
22 // mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
23 //
24 // Device sample runs:
25 // mpirun -np 4 ex4p -m ../data/star.mesh -pa -d cuda
26 // mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-cuda
27 // mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-omp
28 // mpirun -np 4 ex4p -m ../data/beam-hex.mesh -pa -d cuda
29 //
30 // Description: This example code solves a simple 2D/3D H(div) diffusion
31 // problem corresponding to the second order definite equation
32 // -grad(alpha div F) + beta F = f with boundary condition F dot n
33 // = <given normal field>. Here, we use a given exact solution F
34 // and compute the corresponding r.h.s. f. We discretize with
35 // Raviart-Thomas finite elements.
36 //
37 // The example demonstrates the use of H(div) finite element
38 // spaces with the grad-div and H(div) vector finite element mass
39 // bilinear form, as well as the computation of discretization
40 // error when the exact solution is known. Bilinear form
41 // hybridization and static condensation are also illustrated.
42 //
43 // We recommend viewing examples 1-3 before viewing this example.
44 
45 #include "mfem.hpp"
46 #include <fstream>
47 #include <iostream>
48 
49 using namespace std;
50 using namespace mfem;
51 
52 // Exact solution, F, and r.h.s., f. See below for implementation.
53 void F_exact(const Vector &, Vector &);
54 void f_exact(const Vector &, Vector &);
55 double freq = 1.0, kappa;
56 
57 int main(int argc, char *argv[])
58 {
59  // 1. Initialize MPI.
60  int num_procs, myid;
61  MPI_Init(&argc, &argv);
62  MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
63  MPI_Comm_rank(MPI_COMM_WORLD, &myid);
64 
65  // 2. Parse command-line options.
66  const char *mesh_file = "../data/star.mesh";
67  int order = 1;
68  bool set_bc = true;
69  bool static_cond = false;
70  bool hybridization = false;
71  bool pa = false;
72  const char *device_config = "cpu";
73  bool visualization = 1;
74 
75  OptionsParser args(argc, argv);
76  args.AddOption(&mesh_file, "-m", "--mesh",
77  "Mesh file to use.");
78  args.AddOption(&order, "-o", "--order",
79  "Finite element order (polynomial degree).");
80  args.AddOption(&set_bc, "-bc", "--impose-bc", "-no-bc", "--dont-impose-bc",
81  "Impose or not essential boundary conditions.");
82  args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
83  " solution.");
84  args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
85  "--no-static-condensation", "Enable static condensation.");
86  args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
87  "--no-hybridization", "Enable hybridization.");
88  args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
89  "--no-partial-assembly", "Enable Partial Assembly.");
90  args.AddOption(&device_config, "-d", "--device",
91  "Device configuration string, see Device::Configure().");
92  args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
93  "--no-visualization",
94  "Enable or disable GLVis visualization.");
95  args.Parse();
96  if (!args.Good())
97  {
98  if (myid == 0)
99  {
100  args.PrintUsage(cout);
101  }
102  MPI_Finalize();
103  return 1;
104  }
105  if (myid == 0)
106  {
107  args.PrintOptions(cout);
108  }
109  kappa = freq * M_PI;
110 
111  // 3. Enable hardware devices such as GPUs, and programming models such as
112  // CUDA, OCCA, RAJA and OpenMP based on command line options.
113  Device device(device_config);
114  if (myid == 0) { device.Print(); }
115 
116  // 4. Read the (serial) mesh from the given mesh file on all processors. We
117  // can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
118  // and volume, as well as periodic meshes with the same code.
119  Mesh *mesh = new Mesh(mesh_file, 1, 1);
120  int dim = mesh->Dimension();
121  int sdim = mesh->SpaceDimension();
122 
123  // 5. Refine the serial mesh on all processors to increase the resolution. In
124  // this example we do 'ref_levels' of uniform refinement. We choose
125  // 'ref_levels' to be the largest number that gives a final mesh with no
126  // more than 1,000 elements.
127  {
128  int ref_levels =
129  (int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
130  for (int l = 0; l < ref_levels; l++)
131  {
132  mesh->UniformRefinement();
133  }
134  }
135 
136  // 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
137  // this mesh further in parallel to increase the resolution. Once the
138  // parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
139  // meshes need to be reoriented before we can define high-order Nedelec
140  // spaces on them (this is needed in the ADS solver below).
141  ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
142  delete mesh;
143  {
144  int par_ref_levels = 2;
145  for (int l = 0; l < par_ref_levels; l++)
146  {
147  pmesh->UniformRefinement();
148  }
149  }
150  pmesh->ReorientTetMesh();
151 
152  // 7. Define a parallel finite element space on the parallel mesh. Here we
153  // use the Raviart-Thomas finite elements of the specified order.
154  FiniteElementCollection *fec = new RT_FECollection(order-1, dim);
155  ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
156  HYPRE_BigInt size = fespace->GlobalTrueVSize();
157  if (myid == 0)
158  {
159  cout << "Number of finite element unknowns: " << size << endl;
160  }
161 
162  // 8. Determine the list of true (i.e. parallel conforming) essential
163  // boundary dofs. In this example, the boundary conditions are defined
164  // by marking all the boundary attributes from the mesh as essential
165  // (Dirichlet) and converting them to a list of true dofs.
166  Array<int> ess_tdof_list;
167  if (pmesh->bdr_attributes.Size())
168  {
169  Array<int> ess_bdr(pmesh->bdr_attributes.Max());
170  ess_bdr = set_bc ? 1 : 0;
171  fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
172  }
173 
174  // 9. Set up the parallel linear form b(.) which corresponds to the
175  // right-hand side of the FEM linear system, which in this case is
176  // (f,phi_i) where f is given by the function f_exact and phi_i are the
177  // basis functions in the finite element fespace.
179  ParLinearForm *b = new ParLinearForm(fespace);
181  b->Assemble();
182 
183  // 10. Define the solution vector x as a parallel finite element grid function
184  // corresponding to fespace. Initialize x by projecting the exact
185  // solution. Note that only values from the boundary faces will be used
186  // when eliminating the non-homogeneous boundary condition to modify the
187  // r.h.s. vector b.
188  ParGridFunction x(fespace);
190  x.ProjectCoefficient(F);
191 
192  // 11. Set up the parallel bilinear form corresponding to the H(div)
193  // diffusion operator grad alpha div + beta I, by adding the div-div and
194  // the mass domain integrators.
196  Coefficient *beta = new ConstantCoefficient(1.0);
197  ParBilinearForm *a = new ParBilinearForm(fespace);
198  if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
199  a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
201 
202  // 12. Assemble the parallel bilinear form and the corresponding linear
203  // system, applying any necessary transformations such as: parallel
204  // assembly, eliminating boundary conditions, applying conforming
205  // constraints for non-conforming AMR, static condensation,
206  // hybridization, etc.
207  FiniteElementCollection *hfec = NULL;
208  ParFiniteElementSpace *hfes = NULL;
209  if (static_cond)
210  {
212  }
213  else if (hybridization)
214  {
215  hfec = new DG_Interface_FECollection(order-1, dim);
216  hfes = new ParFiniteElementSpace(pmesh, hfec);
218  ess_tdof_list);
219  }
220  a->Assemble();
221 
222  OperatorPtr A;
223  Vector B, X;
224  a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
225 
226  if (myid == 0 && !pa)
227  {
228  cout << "Size of linear system: "
229  << A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
230  }
231 
232  // 13. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
233  // the 3D ADS preconditioners from hypre. If using hybridization, the
234  // system is preconditioned with hypre's BoomerAMG. In the partial
235  // assembly case, use Jacobi preconditioning.
236  Solver *prec = NULL;
237  CGSolver *pcg = new CGSolver(MPI_COMM_WORLD);
238  pcg->SetOperator(*A);
239  pcg->SetRelTol(1e-12);
240  pcg->SetMaxIter(2000);
241  pcg->SetPrintLevel(1);
242  if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
243  else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
244  else
245  {
246  ParFiniteElementSpace *prec_fespace =
247  (a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
248  if (dim == 2) { prec = new HypreAMS(*A.As<HypreParMatrix>(), prec_fespace); }
249  else { prec = new HypreADS(*A.As<HypreParMatrix>(), prec_fespace); }
250  }
251  pcg->SetPreconditioner(*prec);
252  pcg->Mult(B, X);
253 
254  // 14. Recover the parallel grid function corresponding to X. This is the
255  // local finite element solution on each processor.
256  a->RecoverFEMSolution(X, *b, x);
257 
258  // 15. Compute and print the L^2 norm of the error.
259  {
260  double err = x.ComputeL2Error(F);
261  if (myid == 0)
262  {
263  cout << "\n|| F_h - F ||_{L^2} = " << err << '\n' << endl;
264  }
265  }
266 
267  // 16. Save the refined mesh and the solution in parallel. This output can
268  // be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
269  {
270  ostringstream mesh_name, sol_name;
271  mesh_name << "mesh." << setfill('0') << setw(6) << myid;
272  sol_name << "sol." << setfill('0') << setw(6) << myid;
273 
274  ofstream mesh_ofs(mesh_name.str().c_str());
275  mesh_ofs.precision(8);
276  pmesh->Print(mesh_ofs);
277 
278  ofstream sol_ofs(sol_name.str().c_str());
279  sol_ofs.precision(8);
280  x.Save(sol_ofs);
281  }
282 
283  // 17. Send the solution by socket to a GLVis server.
284  if (visualization)
285  {
286  char vishost[] = "localhost";
287  int visport = 19916;
288  socketstream sol_sock(vishost, visport);
289  sol_sock << "parallel " << num_procs << " " << myid << "\n";
290  sol_sock.precision(8);
291  sol_sock << "solution\n" << *pmesh << x << flush;
292  }
293 
294  // 18. Free the used memory.
295  delete pcg;
296  delete prec;
297  delete hfes;
298  delete hfec;
299  delete a;
300  delete alpha;
301  delete beta;
302  delete b;
303  delete fespace;
304  delete fec;
305  delete pmesh;
306 
307  MPI_Finalize();
308 
309  return 0;
310 }
311 
312 
313 // The exact solution (for non-surface meshes)
314 void F_exact(const Vector &p, Vector &F)
315 {
316  int dim = p.Size();
317 
318  double x = p(0);
319  double y = p(1);
320  // double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
321 
322  F(0) = cos(kappa*x)*sin(kappa*y);
323  F(1) = cos(kappa*y)*sin(kappa*x);
324  if (dim == 3)
325  {
326  F(2) = 0.0;
327  }
328 }
329 
330 // The right hand side
331 void f_exact(const Vector &p, Vector &f)
332 {
333  int dim = p.Size();
334 
335  double x = p(0);
336  double y = p(1);
337  // double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
338 
339  double temp = 1 + 2*kappa*kappa;
340 
341  f(0) = temp*cos(kappa*x)*sin(kappa*y);
342  f(1) = temp*cos(kappa*y)*sin(kappa*x);
343  if (dim == 3)
344  {
345  f(2) = 0;
346  }
347 }
int Size() const
Return the logical size of the array.
Definition: array.hpp:134
virtual void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1)
Definition: pfespace.cpp:790
Conjugate gradient method.
Definition: solvers.hpp:316
ParFiniteElementSpace * SCParFESpace() const
Return the parallel trace FE space associated with static condensation.
OpType * As() const
Return the Operator pointer statically cast to a specified OpType. Similar to the method Get()...
Definition: handle.hpp:99
The Auxiliary-space Maxwell Solver in hypre.
Definition: hypre.hpp:1540
The Auxiliary-space Divergence Solver in hypre.
Definition: hypre.hpp:1579
A coefficient that is constant across space and time.
Definition: coefficient.hpp:78
virtual void FormLinearSystem(const Array< int > &ess_tdof_list, Vector &x, Vector &b, OperatorHandle &A, Vector &X, Vector &B, int copy_interior=0)
Form the linear system A X = B, corresponding to this bilinear form and the linear form b(...
virtual void Mult(const Vector &b, Vector &x) const
Operator application: y=A(x).
Definition: solvers.cpp:616
virtual void ReorientTetMesh()
See the remarks for the serial version in mesh.hpp.
Definition: pmesh.cpp:2798
Pointer to an Operator of a specified type.
Definition: handle.hpp:33
HYPRE_BigInt GlobalTrueVSize() const
Definition: pfespace.hpp:275
int Size() const
Returns the size of the vector.
Definition: vector.hpp:190
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:846
virtual void Save(std::ostream &out) const
Definition: pgridfunc.cpp:841
void Print(std::ostream &out=mfem::out)
Print the configuration of the MFEM virtual device object.
Definition: device.cpp:279
void SetAssemblyLevel(AssemblyLevel assembly_level)
Set the desired assembly level.
double kappa
Definition: ex24.cpp:54
Abstract parallel finite element space.
Definition: pfespace.hpp:28
virtual void ProjectCoefficient(Coefficient &coeff)
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
Definition: pgridfunc.cpp:493
(Q div u, div v) for RT elements
void EnableHybridization(FiniteElementSpace *constr_space, BilinearFormIntegrator *constr_integ, const Array< int > &ess_tdof_list)
Enable hybridization.
The BoomerAMG solver in hypre.
Definition: hypre.hpp:1387
Class for parallel linear form.
Definition: plinearform.hpp:26
void SetPrintLevel(int print_lvl)
Definition: solvers.cpp:71
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
Definition: optparser.cpp:150
constexpr char vishost[]
Jacobi smoothing for a given bilinear form (no matrix necessary).
Definition: solvers.hpp:130
double b
Definition: lissajous.cpp:42
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition: mesh.cpp:9143
constexpr int visport
void SetMaxIter(int max_it)
Definition: solvers.hpp:100
T Max() const
Find the maximal element in the array, using the comparison operator &lt; for class T.
Definition: array.cpp:68
void F_exact(const Vector &, Vector &)
Definition: ex4.cpp:266
void Assemble(int skip_zeros=1)
Assemble the local matrix.
virtual void Print(std::ostream &out=mfem::out) const
Definition: pmesh.cpp:4382
int Dimension() const
Definition: mesh.hpp:911
void PrintUsage(std::ostream &out) const
Print the usage message.
Definition: optparser.cpp:457
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition: fe_coll.hpp:341
A general vector function coefficient.
int SpaceDimension() const
Definition: mesh.hpp:912
void AddDomainIntegrator(LinearFormIntegrator *lfi)
Adds new Domain Integrator. Assumes ownership of lfi.
Definition: linearform.cpp:39
bool StaticCondensationIsEnabled() const
Check if static condensation was actually enabled by a previous call to EnableStaticCondensation().
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition: mesh.hpp:204
void SetRelTol(double rtol)
Definition: solvers.hpp:98
OutStream err(std::cerr)
Global stream used by the library for standard error output. Initially it uses the same std::streambu...
Definition: globals.hpp:71
Base class Coefficients that optionally depend on space and time. These are used by the BilinearFormI...
Definition: coefficient.hpp:39
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition: fe_coll.hpp:26
void AddOption(bool *var, const char *enable_short_name, const char *enable_long_name, const char *disable_short_name, const char *disable_long_name, const char *description, bool required=false)
Add a boolean option and set &#39;var&#39; to receive the value. Enable/disable tags are used to set the bool...
Definition: optparser.hpp:82
void f_exact(const Vector &, Vector &)
Definition: ex3.cpp:257
HYPRE_Int HYPRE_BigInt
virtual void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x)
double a
Definition: lissajous.cpp:41
virtual double ComputeL2Error(Coefficient *exsol[], const IntegrationRule *irs[]=NULL) const
Definition: pgridfunc.hpp:273
int dim
Definition: ex24.cpp:53
double freq
Definition: ex24.cpp:54
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
void PrintOptions(std::ostream &out) const
Print the options.
Definition: optparser.cpp:327
Class for parallel bilinear form.
for VectorFiniteElements (Nedelec, Raviart-Thomas)
Definition: lininteg.hpp:266
virtual void SetOperator(const Operator &op)
Also calls SetOperator for the preconditioner if there is one.
Definition: solvers.hpp:330
const double alpha
Definition: ex15.cpp:369
Vector data type.
Definition: vector.hpp:60
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition: solvers.cpp:92
Base class for solvers.
Definition: operator.hpp:648
Class for parallel grid function.
Definition: pgridfunc.hpp:32
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition: device.hpp:121
Wrapper for hypre&#39;s ParCSR matrix class.
Definition: hypre.hpp:277
Class for parallel meshes.
Definition: pmesh.hpp:32
int main()
void EnableStaticCondensation()
Enable the use of static condensation. For details see the description for class StaticCondensation i...
double f(const Vector &p)
bool Good() const
Return true if the command line options were parsed successfully.
Definition: optparser.hpp:150