MFEM v4.8.0
Finite element discretization library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
ex3p.cpp
Go to the documentation of this file.
1// MFEM Example 3 - Parallel Version
2// PETSc Modification
3//
4// Compile with: make ex3p
5//
6// Sample runs:
7// mpirun -np 4 ex3p -m ../../data/klein-bottle.mesh -o 2 -f 0.1 --petscopts rc_ex3p
8// mpirun -np 4 ex3p -m ../../data/klein-bottle.mesh -o 2 -f 0.1 --petscopts rc_ex3p_bddc --nonoverlapping
9//
10// Description: This example code solves a simple electromagnetic diffusion
11// problem corresponding to the second order definite Maxwell
12// equation curl curl E + E = f with boundary condition
13// E x n = <given tangential field>. Here, we use a given exact
14// solution E and compute the corresponding r.h.s. f.
15// We discretize with Nedelec finite elements in 2D or 3D.
16//
17// The example demonstrates the use of H(curl) finite element
18// spaces with the curl-curl and the (vector finite element) mass
19// bilinear form, as well as the computation of discretization
20// error when the exact solution is known. Static condensation is
21// also illustrated.
22//
23// The example also show how to use the non-overlapping feature of
24// the ParBilinearForm class to obtain the linear operator in
25// a format suitable for the BDDC preconditioner in PETSc.
26//
27// We recommend viewing examples 1-2 before viewing this example.
28
29#include "mfem.hpp"
30#include <fstream>
31#include <iostream>
32
33#ifndef MFEM_USE_PETSC
34#error This example requires that MFEM is built with MFEM_USE_PETSC=YES
35#endif
36
37using namespace std;
38using namespace mfem;
39
40// Exact solution, E, and r.h.s., f. See below for implementation.
41void E_exact(const Vector &, Vector &);
42void f_exact(const Vector &, Vector &);
44int dim;
45
46int main(int argc, char *argv[])
47{
48 // 1. Initialize MPI and HYPRE.
49 Mpi::Init(argc, argv);
50 int num_procs = Mpi::WorldSize();
51 int myid = Mpi::WorldRank();
53
54 // 2. Parse command-line options.
55 const char *mesh_file = "../../data/beam-tet.mesh";
56 int ser_ref_levels = -1;
57 int par_ref_levels = 2;
58 int order = 1;
59 bool static_cond = false;
60 bool visualization = 1;
61 bool use_petsc = true;
62 const char *petscrc_file = "";
63 bool use_nonoverlapping = false;
64 const char *device_config = "cpu";
65
66 OptionsParser args(argc, argv);
67 args.AddOption(&mesh_file, "-m", "--mesh",
68 "Mesh file to use.");
69 args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
70 "Number of times to refine the mesh uniformly in serial.");
71 args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
72 "Number of times to refine the mesh uniformly in parallel.");
73 args.AddOption(&order, "-o", "--order",
74 "Finite element order (polynomial degree).");
75 args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
76 " solution.");
77 args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
78 "--no-static-condensation", "Enable static condensation.");
79 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
80 "--no-visualization",
81 "Enable or disable GLVis visualization.");
82 args.AddOption(&use_petsc, "-usepetsc", "--usepetsc", "-no-petsc",
83 "--no-petsc",
84 "Use or not PETSc to solve the linear system.");
85 args.AddOption(&petscrc_file, "-petscopts", "--petscopts",
86 "PetscOptions file to use.");
87 args.AddOption(&use_nonoverlapping, "-nonoverlapping", "--nonoverlapping",
88 "-no-nonoverlapping", "--no-nonoverlapping",
89 "Use or not the block diagonal PETSc's matrix format "
90 "for non-overlapping domain decomposition.");
91 args.AddOption(&device_config, "-d", "--device",
92 "Device configuration string, see Device::Configure().");
93 args.Parse();
94 if (!args.Good())
95 {
96 if (myid == 0)
97 {
98 args.PrintUsage(cout);
99 }
100 return 1;
101 }
102 if (myid == 0)
103 {
104 args.PrintOptions(cout);
105 }
106 kappa = freq * M_PI;
107
108 // 2b. Enable hardware devices such as GPUs, and programming models such as
109 // CUDA, OCCA, RAJA and OpenMP based on command line options.
110 Device device(device_config);
111 if (myid == 0) { device.Print(); }
112
113 // 2c. We initialize PETSc
114 if (use_petsc) { MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL); }
115
116 // 3. Read the (serial) mesh from the given mesh file on all processors. We
117 // can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
118 // and volume meshes with the same code.
119 Mesh *mesh = new Mesh(mesh_file, 1, 1);
120 dim = mesh->Dimension();
121#if PETSC_VERSION_LT(3,21,0)
122 if (dim == 3 && use_petsc && use_nonoverlapping)
123 {
124 cout << "\nFor three-dimensional runs you need a version of PETSc greater or equal 3.21.\n\n";
125 delete mesh;
128 return MFEM_SKIP_RETURN_VALUE;
129 }
130#endif
131 int sdim = mesh->SpaceDimension();
132
133 // 4. Refine the serial mesh on all processors to increase the resolution. In
134 // this example we do 'ref_levels' of uniform refinement. We choose
135 // 'ref_levels' to be the largest number that gives a final mesh with no
136 // more than 1,000 elements.
137 {
138 if (ser_ref_levels < 0)
139 {
140 ser_ref_levels = (int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
141 }
142 for (int l = 0; l < ser_ref_levels; l++)
143 {
144 mesh->UniformRefinement();
145 }
146 }
147
148 // 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
149 // this mesh further in parallel to increase the resolution. Once the
150 // parallel mesh is defined, the serial mesh can be deleted.
151 ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
152 delete mesh;
153 {
154 for (int l = 0; l < par_ref_levels; l++)
155 {
156 pmesh->UniformRefinement();
157 }
158 }
159
160 // 6. Define a parallel finite element space on the parallel mesh. Here we
161 // use the Nedelec finite elements of the specified order.
163 ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
164 HYPRE_BigInt size = fespace->GlobalTrueVSize();
165 if (myid == 0)
166 {
167 cout << "Number of finite element unknowns: " << size << endl;
168 }
169
170 // 7. Determine the list of true (i.e. parallel conforming) essential
171 // boundary dofs. In this example, the boundary conditions are defined
172 // by marking all the boundary attributes from the mesh as essential
173 // (Dirichlet) and converting them to a list of true dofs.
174 Array<int> ess_tdof_list;
175 if (pmesh->bdr_attributes.Size())
176 {
177 Array<int> ess_bdr(pmesh->bdr_attributes.Max());
178 ess_bdr = 1;
179 fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
180 }
181
182 // 8. Set up the parallel linear form b(.) which corresponds to the
183 // right-hand side of the FEM linear system, which in this case is
184 // (f,phi_i) where f is given by the function f_exact and phi_i are the
185 // basis functions in the finite element fespace.
187 ParLinearForm *b = new ParLinearForm(fespace);
188 b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
189 b->Assemble();
190
191 // 9. Define the solution vector x as a parallel finite element grid function
192 // corresponding to fespace. Initialize x by projecting the exact
193 // solution. Note that only values from the boundary edges will be used
194 // when eliminating the non-homogeneous boundary condition to modify the
195 // r.h.s. vector b.
196 ParGridFunction x(fespace);
199
200 // 10. Set up the parallel bilinear form corresponding to the EM diffusion
201 // operator curl muinv curl + sigma I, by adding the curl-curl and the
202 // mass domain integrators.
203 Coefficient *muinv = new ConstantCoefficient(1.0);
205 ParBilinearForm *a = new ParBilinearForm(fespace);
206 a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
207 a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
208
209 // 11. Assemble the parallel bilinear form and the corresponding linear
210 // system, applying any necessary transformations such as: parallel
211 // assembly, eliminating boundary conditions, applying conforming
212 // constraints for non-conforming AMR, static condensation, etc.
213 if (static_cond) { a->EnableStaticCondensation(); }
214 a->Assemble();
215
216 Vector B, X;
217 if (!use_petsc)
218 {
220 a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
221
222 if (myid == 0)
223 {
224 cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
225 }
226
227 // 12. Define and apply a parallel PCG solver for AX=B with the AMS
228 // preconditioner from hypre.
229 ParFiniteElementSpace *prec_fespace =
230 (a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
231 HypreSolver *ams = new HypreAMS(A, prec_fespace);
232 HyprePCG *pcg = new HyprePCG(A);
233 pcg->SetTol(1e-10);
234 pcg->SetMaxIter(500);
235 pcg->SetPrintLevel(2);
236 pcg->SetPreconditioner(*ams);
237 pcg->Mult(B, X);
238 delete pcg;
239 delete ams;
240 }
241 else
242 {
244 a->SetOperatorType(use_nonoverlapping ?
246 a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
247
248 if (myid == 0)
249 {
250 cout << "Size of linear system: " << A.M() << endl;
251 }
252
253 // 12. Define and apply a parallel PCG solver.
254 ParFiniteElementSpace *prec_fespace =
255 (a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
256 PetscPCGSolver *pcg = new PetscPCGSolver(A);
257 PetscPreconditioner *prec = NULL;
258 pcg->SetTol(1e-10);
259 pcg->SetMaxIter(500);
260 pcg->SetPrintLevel(2);
261 if (use_nonoverlapping)
262 {
263 // Auxiliary class for BDDC customization
265 // Inform the solver about the finite element space
266 opts.SetSpace(prec_fespace);
267 // Inform the solver about essential dofs
268 opts.SetEssBdrDofs(&ess_tdof_list);
269 // Create a BDDC solver with parameters
270 prec = new PetscBDDCSolver(A,opts);
271 }
272 else
273 {
274 // Create an empty preconditioner object that can
275 // be customized at runtime
276 prec = new PetscPreconditioner(A,"solver_");
277 }
278 pcg->SetPreconditioner(*prec);
279 pcg->Mult(B, X);
280 delete pcg;
281 delete prec;
282 }
283
284 // 13. Recover the parallel grid function corresponding to X. This is the
285 // local finite element solution on each processor.
286 a->RecoverFEMSolution(X, *b, x);
287
288 // 14. Compute and print the L^2 norm of the error.
289 {
291 if (myid == 0)
292 {
293 cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
294 }
295 }
296
297 // 15. Save the refined mesh and the solution in parallel. This output can
298 // be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
299 {
300 ostringstream mesh_name, sol_name;
301 mesh_name << "mesh." << setfill('0') << setw(6) << myid;
302 sol_name << "sol." << setfill('0') << setw(6) << myid;
303
304 ofstream mesh_ofs(mesh_name.str().c_str());
305 mesh_ofs.precision(8);
306 pmesh->Print(mesh_ofs);
307
308 ofstream sol_ofs(sol_name.str().c_str());
309 sol_ofs.precision(8);
310 x.Save(sol_ofs);
311 }
312
313 // 16. Send the solution by socket to a GLVis server.
314 if (visualization)
315 {
316 char vishost[] = "localhost";
317 int visport = 19916;
318 socketstream sol_sock(vishost, visport);
319 sol_sock << "parallel " << num_procs << " " << myid << "\n";
320 sol_sock.precision(8);
321 sol_sock << "solution\n" << *pmesh << x << flush;
322 }
323
324 // 17. Free the used memory.
325 delete a;
326 delete sigma;
327 delete muinv;
328 delete b;
329 delete fespace;
330 delete fec;
331 delete pmesh;
332
333 // We finalize PETSc
334 if (use_petsc) { MFEMFinalizePetsc(); }
335
336 return 0;
337}
338
339
340void E_exact(const Vector &x, Vector &E)
341{
342 if (dim == 3)
343 {
344 E(0) = sin(kappa * x(1));
345 E(1) = sin(kappa * x(2));
346 E(2) = sin(kappa * x(0));
347 }
348 else
349 {
350 E(0) = sin(kappa * x(1));
351 E(1) = sin(kappa * x(0));
352 if (x.Size() == 3) { E(2) = 0.0; }
353 }
354}
355
356void f_exact(const Vector &x, Vector &f)
357{
358 if (dim == 3)
359 {
360 f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
361 f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
362 f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
363 }
364 else
365 {
366 f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
367 f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
368 if (x.Size() == 3) { f(2) = 0.0; }
369 }
370}
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:68
int Size() const
Return the logical size of the array.
Definition array.hpp:147
Base class Coefficients that optionally depend on space and time. These are used by the BilinearFormI...
A coefficient that is constant across space and time.
Integrator for for Nedelec elements.
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition device.hpp:123
void Print(std::ostream &out=mfem::out)
Print the configuration of the MFEM virtual device object.
Definition device.cpp:297
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
The Auxiliary-space Maxwell Solver in hypre.
Definition hypre.hpp:1871
PCG solver in hypre.
Definition hypre.hpp:1301
void Mult(const HypreParVector &b, HypreParVector &x) const override
Solve Ax=b with hypre's PCG.
Definition hypre.cpp:4242
void SetPrintLevel(int print_lvl)
Definition hypre.cpp:4214
void SetPreconditioner(HypreSolver &precond)
Set the hypre solver to be used as a preconditioner.
Definition hypre.cpp:4219
void SetMaxIter(int max_iter)
Definition hypre.cpp:4204
void SetTol(real_t tol)
Definition hypre.cpp:4194
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:408
HYPRE_BigInt GetGlobalNumRows() const
Return the global number of rows.
Definition hypre.hpp:699
Abstract class for hypre's solvers and preconditioners.
Definition hypre.hpp:1188
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
Mesh data type.
Definition mesh.hpp:64
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:290
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1282
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1219
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11295
static void Finalize()
Finalize MPI (if it has been initialized and not yet already finalized).
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
static int WorldSize()
Return the size of MPI_COMM_WORLD.
static void Init(int &argc, char **&argv, int required=default_thread_required, int *provided=nullptr)
Singleton creation with Mpi::Init(argc, argv).
Arbitrary order H(curl)-conforming Nedelec finite elements.
Definition fe_coll.hpp:482
void FormLinearSystem(const Array< int > &ess_tdof_list, Vector &x, Vector &b, Operator *&A, Vector &X, Vector &B, int copy_interior=0)
Form a constrained linear system using a matrix-free approach.
Definition operator.cpp:114
@ PETSC_MATIS
ID for class PetscParMatrix, MATIS format.
Definition operator.hpp:289
@ PETSC_MATAIJ
ID for class PetscParMatrix, MATAIJ format.
Definition operator.hpp:288
virtual void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x)
Reconstruct a solution vector x (e.g. a GridFunction) from the solution X of a constrained linear sys...
Definition operator.cpp:148
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
void PrintUsage(std::ostream &out) const
Print the usage message.
void PrintOptions(std::ostream &out) const
Print the options.
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 'var' to receive the value. Enable/disable tags are used to set the bool...
Definition optparser.hpp:82
bool Good() const
Return true if the command line options were parsed successfully.
Class for parallel bilinear form.
Abstract parallel finite element space.
Definition pfespace.hpp:29
void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1) const override
HYPRE_BigInt GlobalTrueVSize() const
Definition pfespace.hpp:346
Class for parallel grid function.
Definition pgridfunc.hpp:50
void Save(std::ostream &out) const override
real_t ComputeL2Error(Coefficient *exsol[], const IntegrationRule *irs[]=NULL, const Array< int > *elems=NULL) const override
Returns ||u_ex - u_h||_L2 in parallel for H1 or L2 elements.
void ProjectCoefficient(Coefficient &coeff) override
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
Class for parallel linear form.
Class for parallel meshes.
Definition pmesh.hpp:34
void Print(std::ostream &out=mfem::out, const std::string &comments="") const override
Definition pmesh.cpp:4800
Auxiliary class for BDDC customization.
Definition petsc.hpp:831
void SetEssBdrDofs(const Array< int > *essdofs, bool loc=false)
Specify dofs on the essential boundary.
Definition petsc.hpp:850
void SetSpace(ParFiniteElementSpace *fe)
Definition petsc.hpp:845
void SetPreconditioner(Solver &precond)
Definition petsc.cpp:3129
void Mult(const Vector &b, Vector &x) const override
Application of the solver.
Definition petsc.cpp:3209
Wrapper for PETSc's matrix class.
Definition petsc.hpp:319
PetscInt M() const
Returns the global number of rows.
Definition petsc.cpp:1010
Abstract class for PETSc's preconditioners.
Definition petsc.hpp:805
void SetTol(real_t tol)
Definition petsc.cpp:2390
void SetPrintLevel(int plev)
Definition petsc.cpp:2472
void SetMaxIter(int max_iter)
Definition petsc.cpp:2445
for VectorFiniteElements (Nedelec, Raviart-Thomas)
Definition lininteg.hpp:344
A general vector function coefficient.
Vector data type.
Definition vector.hpp:82
int Size() const
Returns the size of the vector.
Definition vector.hpp:226
real_t sigma(const Vector &x)
Definition maxwell.cpp:102
int main()
HYPRE_Int HYPRE_BigInt
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
void MFEMInitializePetsc()
Convenience functions to initialize/finalize PETSc.
Definition petsc.cpp:206
void MFEMFinalizePetsc()
Definition petsc.cpp:246
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
float real_t
Definition config.hpp:43
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
const char vishost[]
real_t kappa
Definition ex3p.cpp:43
int dim
Definition ex3p.cpp:44
void E_exact(const Vector &, Vector &)
Definition ex3p.cpp:340
void f_exact(const Vector &, Vector &)
Definition ex3p.cpp:356
real_t freq
Definition ex3p.cpp:43