MFEM v4.8.0
Finite element discretization library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
ex6p.cpp
Go to the documentation of this file.
1// MFEM Example 6 - Parallel Version
2// PETSc Modification
3//
4// Compile with: make ex6p
5//
6// Sample runs:
7// mpirun -np 4 ex6p -m ../../data/amr-quad.mesh
8// mpirun -np 4 ex6p -m ../../data/amr-quad.mesh -nonoverlapping
9//
10// Description: This is a version of Example 1 with a simple adaptive mesh
11// refinement loop. The problem being solved is again the Poisson
12// equation -Delta u = 1 with homogeneous Dirichlet boundary
13// conditions. The problem is solved on a sequence of meshes which
14// are locally refined in a conforming (triangles, tetrahedrons)
15// or non-conforming (quadrilaterals, hexahedra) manner according
16// to a simple ZZ error estimator.
17//
18// The example demonstrates MFEM's capability to work with both
19// conforming and nonconforming refinements, in 2D and 3D, on
20// linear, curved and surface meshes. Interpolation of functions
21// from coarse to fine meshes, as well as persistent GLVis
22// visualization are also illustrated.
23//
24// PETSc assembly timings can be benchmarked if requested by
25// command line.
26//
27// We recommend viewing Example 1 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
40int main(int argc, char *argv[])
41{
42 // 1. Initialize MPI and HYPRE.
43 Mpi::Init(argc, argv);
44 int num_procs = Mpi::WorldSize();
45 int myid = Mpi::WorldRank();
47
48 // 2. Parse command-line options.
49 const char *mesh_file = "../../data/star.mesh";
50 int order = 1;
51 bool visualization = true;
52 int max_dofs = 100000;
53 bool use_petsc = true;
54 const char *petscrc_file = "";
55 bool use_nonoverlapping = false;
56 const char *device_config = "cpu";
57
58 OptionsParser args(argc, argv);
59 args.AddOption(&mesh_file, "-m", "--mesh",
60 "Mesh file to use.");
61 args.AddOption(&order, "-o", "--order",
62 "Finite element order (polynomial degree).");
63 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
64 "--no-visualization",
65 "Enable or disable GLVis visualization.");
66 args.AddOption(&max_dofs, "-md", "--max_dofs",
67 "Maximum number of dofs.");
68 args.AddOption(&use_petsc, "-usepetsc", "--usepetsc", "-no-petsc",
69 "--no-petsc",
70 "Use or not PETSc to solve the linear system.");
71 args.AddOption(&petscrc_file, "-petscopts", "--petscopts",
72 "PetscOptions file to use.");
73 args.AddOption(&use_nonoverlapping, "-nonoverlapping", "--nonoverlapping",
74 "-no-nonoverlapping", "--no-nonoverlapping",
75 "Use or not the block diagonal PETSc's matrix format "
76 "for non-overlapping domain decomposition.");
77 args.AddOption(&device_config, "-d", "--device",
78 "Device configuration string, see Device::Configure().");
79 args.Parse();
80 if (!args.Good())
81 {
82 if (myid == 0)
83 {
84 args.PrintUsage(cout);
85 }
86 return 1;
87 }
88 if (myid == 0)
89 {
90 args.PrintOptions(cout);
91 }
92
93 // 2b. Enable hardware devices such as GPUs, and programming models such as
94 // CUDA, OCCA, RAJA and OpenMP based on command line options.
95 Device device(device_config);
96 if (myid == 0) { device.Print(); }
97
98 // 2c. We initialize PETSc
99 if (use_petsc) { MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL); }
100
101 // 3. Read the (serial) mesh from the given mesh file on all processors. We
102 // can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
103 // and volume meshes with the same code.
104 Mesh *mesh = new Mesh(mesh_file, 1, 1);
105 int dim = mesh->Dimension();
106 int sdim = mesh->SpaceDimension();
107
108 // 4. Refine the serial mesh on all processors to increase the resolution.
109 // Also project a NURBS mesh to a piecewise-quadratic curved mesh. Make
110 // sure that the mesh is non-conforming.
111 if (mesh->NURBSext)
112 {
113 mesh->UniformRefinement();
114 mesh->SetCurvature(2);
115 }
116 mesh->EnsureNCMesh();
117
118 // 5. Define a parallel mesh by partitioning the serial mesh.
119 // Once the parallel mesh is defined, the serial mesh can be deleted.
120 ParMesh pmesh(MPI_COMM_WORLD, *mesh);
121 delete mesh;
122
123 MFEM_VERIFY(pmesh.bdr_attributes.Size() > 0,
124 "Boundary attributes required in the mesh.");
125 Array<int> ess_bdr(pmesh.bdr_attributes.Max());
126 ess_bdr = 1;
127
128 // 6. Define a finite element space on the mesh. The polynomial order is
129 // one (linear) by default, but this can be changed on the command line.
130 H1_FECollection fec(order, dim);
131 ParFiniteElementSpace fespace(&pmesh, &fec);
132
133 // 7. As in Example 1p, we set up bilinear and linear forms corresponding to
134 // the Poisson problem -\Delta u = 1. We don't assemble the discrete
135 // problem yet, this will be done in the main loop.
136 ParBilinearForm a(&fespace);
137 ParLinearForm b(&fespace);
138
139 ConstantCoefficient one(1.0);
140
142 a.AddDomainIntegrator(integ);
143 b.AddDomainIntegrator(new DomainLFIntegrator(one));
144
145 // 8. The solution vector x and the associated finite element grid function
146 // will be maintained over the AMR iterations. We initialize it to zero.
147 ParGridFunction x(&fespace);
148 x = 0;
149
150 // 9. Connect to GLVis.
151 char vishost[] = "localhost";
152 int visport = 19916;
153
154 socketstream sout;
155 if (visualization)
156 {
157 sout.open(vishost, visport);
158 if (!sout)
159 {
160 if (myid == 0)
161 {
162 cout << "Unable to connect to GLVis server at "
163 << vishost << ':' << visport << endl;
164 cout << "GLVis visualization disabled.\n";
165 }
166 visualization = false;
167 }
168
169 sout.precision(8);
170 }
171
172 // 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
173 // with L2 projection in the smoothing step to better handle hanging
174 // nodes and parallel partitioning. We need to supply a space for the
175 // discontinuous flux (L2) and a space for the smoothed flux (H(div) is
176 // used here).
177 L2_FECollection flux_fec(order, dim);
178 ParFiniteElementSpace flux_fes(&pmesh, &flux_fec, sdim);
179 RT_FECollection smooth_flux_fec(order-1, dim);
180 ParFiniteElementSpace smooth_flux_fes(&pmesh, &smooth_flux_fec);
181 // Another possible option for the smoothed flux space:
182 // H1_FECollection smooth_flux_fec(order, dim);
183 // ParFiniteElementSpace smooth_flux_fes(&pmesh, &smooth_flux_fec, dim);
184 L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fes, smooth_flux_fes);
185
186 // 11. A refiner selects and refines elements based on a refinement strategy.
187 // The strategy here is to refine elements with errors larger than a
188 // fraction of the maximum element error. Other strategies are possible.
189 // The refiner will call the given error estimator.
190 ThresholdRefiner refiner(estimator);
191 refiner.SetTotalErrorFraction(0.7);
192
193 // 12. The main AMR loop. In each iteration we solve the problem on the
194 // current mesh, visualize the solution, and refine the mesh.
195 for (int it = 0; ; it++)
196 {
197 HYPRE_BigInt global_dofs = fespace.GlobalTrueVSize();
198 if (myid == 0)
199 {
200 cout << "\nAMR iteration " << it << endl;
201 cout << "Number of unknowns: " << global_dofs << endl;
202 }
203
204 // 13. Assemble the stiffness matrix and the right-hand side. Note that
205 // MFEM doesn't care at this point that the mesh is nonconforming
206 // and parallel. The FE space is considered 'cut' along hanging
207 // edges/faces, and also across processor boundaries.
208 a.Assemble();
209 b.Assemble();
210
211 // 14. Create the parallel linear system: eliminate boundary conditions,
212 // constrain hanging nodes and nodes across processor boundaries.
213 // The system will be solved for true (unconstrained/unique) DOFs only.
214 Array<int> ess_tdof_list;
215 fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
216 real_t time;
217 const int copy_interior = 1;
218
219 if (use_petsc)
220 {
221 a.SetOperatorType(use_nonoverlapping ?
224 Vector pX,pB;
225 MPI_Barrier(MPI_COMM_WORLD);
226 time = -MPI_Wtime();
227 a.FormLinearSystem(ess_tdof_list, x, b, pA, pX, pB, copy_interior);
228 MPI_Barrier(MPI_COMM_WORLD);
229 time += MPI_Wtime();
230 if (myid == 0) { cout << "PETSc assembly timing : " << time << endl; }
231 }
232
233 a.Assemble();
234 b.Assemble();
235 a.SetOperatorType(Operator::Hypre_ParCSR);
237 Vector B, X;
238 MPI_Barrier(MPI_COMM_WORLD);
239 time = -MPI_Wtime();
240 a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
241 MPI_Barrier(MPI_COMM_WORLD);
242 time += MPI_Wtime();
243 if (myid == 0) { cout << "HYPRE assembly timing : " << time << endl; }
244
245 // 15. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
246 // preconditioner from hypre.
247 HypreBoomerAMG amg;
248 amg.SetPrintLevel(0);
249 CGSolver pcg(A.GetComm());
250 pcg.SetPreconditioner(amg);
251 pcg.SetOperator(A);
252 pcg.SetRelTol(1e-6);
253 pcg.SetMaxIter(200);
254 pcg.SetPrintLevel(3); // print the first and the last iterations only
255 pcg.Mult(B, X);
256
257 // 16. Extract the parallel grid function corresponding to the finite element
258 // approximation X. This is the local solution on each processor.
259 a.RecoverFEMSolution(X, b, x);
260
261 // 17. Send the solution by socket to a GLVis server.
262 if (visualization)
263 {
264 sout << "parallel " << num_procs << " " << myid << "\n";
265 sout << "solution\n" << pmesh << x << flush;
266 }
267
268 if (global_dofs > max_dofs)
269 {
270 if (myid == 0)
271 {
272 cout << "Reached the maximum number of dofs. Stop." << endl;
273 }
274 // we need to call Update here to delete any internal PETSc object that
275 // have been created by the ParBilinearForm; otherwise, these objects
276 // will be destroyed at the end of the main scope, when PETSc has been
277 // already finalized.
278 a.Update();
279 b.Update();
280 break;
281 }
282
283 // 18. Call the refiner to modify the mesh. The refiner calls the error
284 // estimator to obtain element errors, then it selects elements to be
285 // refined and finally it modifies the mesh. The Stop() method can be
286 // used to determine if a stopping criterion was met.
287 refiner.Apply(pmesh);
288 if (refiner.Stop())
289 {
290 if (myid == 0)
291 {
292 cout << "Stopping criterion satisfied. Stop." << endl;
293 }
294 a.Update();
295 b.Update();
296 break;
297 }
298
299 // 19. Update the finite element space (recalculate the number of DOFs,
300 // etc.) and create a grid function update matrix. Apply the matrix
301 // to any GridFunctions over the space. In this case, the update
302 // matrix is an interpolation matrix so the updated GridFunction will
303 // still represent the same function as before refinement.
304 fespace.Update();
305 x.Update();
306
307 // 20. Load balance the mesh, and update the space and solution. Currently
308 // available only for nonconforming meshes.
309 if (pmesh.Nonconforming())
310 {
311 pmesh.Rebalance();
312
313 // Update the space and the GridFunction. This time the update matrix
314 // redistributes the GridFunction among the processors.
315 fespace.Update();
316 x.Update();
317 }
318
319 // 21. Inform also the bilinear and linear forms that the space has
320 // changed.
321 a.Update();
322 b.Update();
323 }
324
325 // We finalize PETSc
326 if (use_petsc) { MFEMFinalizePetsc(); }
327
328 return 0;
329}
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
Abstract base class BilinearFormIntegrator.
Conjugate gradient method.
Definition solvers.hpp:538
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the Conjugate Gradient method.
Definition solvers.cpp:751
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition solvers.hpp:551
A coefficient that is constant across space and time.
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
Class for domain integration .
Definition lininteg.hpp:106
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:275
The BoomerAMG solver in hypre.
Definition hypre.hpp:1717
void SetPrintLevel(int print_level)
Definition hypre.hpp:1797
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:408
MPI_Comm GetComm() const
MPI communicator.
Definition hypre.hpp:598
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
void SetRelTol(real_t rtol)
Definition solvers.hpp:229
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition solvers.cpp:174
virtual void SetPrintLevel(int print_lvl)
Legacy method to set the level of verbosity of the solver output.
Definition solvers.cpp:72
void SetMaxIter(int max_it)
Definition solvers.hpp:231
The L2ZienkiewiczZhuEstimator class implements the Zienkiewicz-Zhu error estimation procedure where t...
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
bool Apply(Mesh &mesh)
Perform the mesh operation.
bool Stop() const
Check if STOP action is requested, e.g. stopping criterion is satisfied.
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
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:298
bool Nonconforming() const
Return a bool indicating whether this mesh is nonconforming.
Definition mesh.hpp:2367
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 EnsureNCMesh(bool simplices_nonconforming=false)
Definition mesh.cpp:10951
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1)
Set the curvature of the mesh nodes using the given polynomial degree.
Definition mesh.cpp:6484
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11295
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).
@ PETSC_MATIS
ID for class PetscParMatrix, MATIS format.
Definition operator.hpp:289
@ Hypre_ParCSR
ID for class HypreParMatrix.
Definition operator.hpp:287
@ PETSC_MATAIJ
ID for class PetscParMatrix, MATAIJ format.
Definition operator.hpp:288
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
void Update(bool want_transform=true) override
Class for parallel grid function.
Definition pgridfunc.hpp:50
void Update() override
Transform by the Space UpdateMatrix (e.g., on Mesh change).
Definition pgridfunc.cpp:91
Class for parallel linear form.
Class for parallel meshes.
Definition pmesh.hpp:34
void Rebalance()
Definition pmesh.cpp:4008
Wrapper for PETSc's matrix class.
Definition petsc.hpp:319
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:403
Mesh refinement operator using an error threshold.
void SetTotalErrorFraction(real_t fraction)
Set the total fraction used in the computation of the threshold. The default value is 1/2.
Vector data type.
Definition vector.hpp:82
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
int dim
Definition ex24.cpp:53
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
float real_t
Definition config.hpp:43
const char vishost[]