MFEM  v4.3.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 //
3 // Compile with: make ex6p
4 //
5 // Sample runs: mpirun -np 4 ex6p -m ../data/star-hilbert.mesh -o 2
6 // mpirun -np 4 ex6p -m ../data/square-disc.mesh -rm 1 -o 1
7 // mpirun -np 4 ex6p -m ../data/square-disc.mesh -rm 1 -o 2 -h1
8 // mpirun -np 4 ex6p -m ../data/square-disc.mesh -o 2 -cs
9 // mpirun -np 4 ex6p -m ../data/square-disc-nurbs.mesh -o 2
10 // mpirun -np 4 ex6p -m ../data/fichera.mesh -o 2
11 // mpirun -np 4 ex6p -m ../data/escher.mesh -rm 2 -o 2
12 // mpirun -np 4 ex6p -m ../data/escher.mesh -o 2 -cs
13 // mpirun -np 4 ex6p -m ../data/disc-nurbs.mesh -o 2
14 // mpirun -np 4 ex6p -m ../data/ball-nurbs.mesh
15 // mpirun -np 4 ex6p -m ../data/pipe-nurbs.mesh
16 // mpirun -np 4 ex6p -m ../data/star-surf.mesh -o 2
17 // mpirun -np 4 ex6p -m ../data/square-disc-surf.mesh -rm 2 -o 2
18 // mpirun -np 4 ex6p -m ../data/inline-segment.mesh -o 1 -md 200
19 // mpirun -np 4 ex6p -m ../data/amr-quad.mesh
20 // mpirun -np 4 ex6p --restart
21 //
22 // Device sample runs:
23 // mpirun -np 4 ex6p -pa -d cuda
24 // mpirun -np 4 ex6p -pa -d occa-cuda
25 // mpirun -np 4 ex6p -pa -d raja-omp
26 // mpirun -np 4 ex6p -pa -d ceed-cpu
27 // * mpirun -np 4 ex6p -pa -d ceed-cuda
28 // mpirun -np 4 ex6p -pa -d ceed-cuda:/gpu/cuda/shared
29 //
30 // Description: This is a version of Example 1 with a simple adaptive mesh
31 // refinement loop. The problem being solved is again the Laplace
32 // equation -Delta u = 1 with homogeneous Dirichlet boundary
33 // conditions. The problem is solved on a sequence of meshes which
34 // are locally refined in a conforming (triangles, tetrahedrons)
35 // or non-conforming (quadrilaterals, hexahedra) manner according
36 // to a simple ZZ error estimator.
37 //
38 // The example demonstrates MFEM's capability to work with both
39 // conforming and nonconforming refinements, in 2D and 3D, on
40 // linear, curved and surface meshes. Interpolation of functions
41 // from coarse to fine meshes, restarting from a checkpoint, as
42 // well as persistent GLVis visualization are also illustrated.
43 //
44 // We recommend viewing Example 1 before viewing this example.
45 
46 #include "mfem.hpp"
47 #include <fstream>
48 #include <iostream>
49 
50 using namespace std;
51 using namespace mfem;
52 
53 int main(int argc, char *argv[])
54 {
55  // 1. Initialize MPI.
56  int num_procs, myid;
57  MPI_Init(&argc, &argv);
58  MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
59  MPI_Comm_rank(MPI_COMM_WORLD, &myid);
60 
61  // 2. Parse command-line options.
62  const char *mesh_file = "../data/star.mesh";
63  int order = 1;
64  bool pa = false;
65  const char *device_config = "cpu";
66  bool nc_simplices = true;
67  int reorder_mesh = 0;
68  int max_dofs = 100000;
69  bool smooth_rt = true;
70  bool restart = false;
71  bool visualization = true;
72 
73  OptionsParser args(argc, argv);
74  args.AddOption(&mesh_file, "-m", "--mesh",
75  "Mesh file to use.");
76  args.AddOption(&order, "-o", "--order",
77  "Finite element order (polynomial degree).");
78  args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
79  "--no-partial-assembly", "Enable Partial Assembly.");
80  args.AddOption(&device_config, "-d", "--device",
81  "Device configuration string, see Device::Configure().");
82  args.AddOption(&reorder_mesh, "-rm", "--reorder-mesh",
83  "Reorder elements of the coarse mesh to improve "
84  "dynamic partitioning: 0=none, 1=hilbert, 2=gecko.");
85  args.AddOption(&nc_simplices, "-ns", "--nonconforming-simplices",
86  "-cs", "--conforming-simplices",
87  "For simplicial meshes, enable/disable nonconforming"
88  " refinement");
89  args.AddOption(&max_dofs, "-md", "--max-dofs",
90  "Stop after reaching this many degrees of freedom.");
91  args.AddOption(&smooth_rt, "-rt", "--smooth-rt", "-h1", "--smooth-h1",
92  "Represent the smooth flux in RT or vector H1 space.");
93  args.AddOption(&restart, "-res", "--restart", "-no-res", "--no-restart",
94  "Restart computation from the last checkpoint.");
95  args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
96  "--no-visualization",
97  "Enable or disable GLVis visualization.");
98  args.Parse();
99  if (!args.Good())
100  {
101  if (myid == 0)
102  {
103  args.PrintUsage(cout);
104  }
105  MPI_Finalize();
106  return 1;
107  }
108  if (myid == 0)
109  {
110  args.PrintOptions(cout);
111  }
112 
113  // 3. Enable hardware devices such as GPUs, and programming models such as
114  // CUDA, OCCA, RAJA and OpenMP based on command line options.
115  Device device(device_config);
116  if (myid == 0) { device.Print(); }
117 
118  ParMesh *pmesh;
119  if (!restart)
120  {
121  // 4. Read the (serial) mesh from the given mesh file on all processors.
122  // We can handle triangular, quadrilateral, tetrahedral, hexahedral,
123  // surface and volume meshes with the same code.
124  Mesh mesh(mesh_file, 1, 1);
125 
126  // 5. A NURBS mesh cannot be refined locally so we refine it uniformly
127  // and project it to a standard curvilinear mesh of order 2.
128  if (mesh.NURBSext)
129  {
130  mesh.UniformRefinement();
131  mesh.SetCurvature(2);
132  }
133 
134  // 6. MFEM supports dynamic partitioning (load balancing) of parallel non-
135  // conforming meshes based on space-filling curve (SFC) partitioning.
136  // SFC partitioning is extremely fast and scales to hundreds of
137  // thousands of processors, but requires the coarse mesh to be ordered,
138  // ideally as a sequence of face-neighbors. The mesh may already be
139  // ordered (like star-hilbert.mesh) or we can order it here. Ordering
140  // type 1 is a fast spatial sort of the mesh, type 2 is a high quality
141  // optimization algorithm suitable for ordering general unstructured
142  // meshes.
143  if (reorder_mesh)
144  {
145  Array<int> ordering;
146  switch (reorder_mesh)
147  {
148  case 1: mesh.GetHilbertElementOrdering(ordering); break;
149  case 2: mesh.GetGeckoElementOrdering(ordering); break;
150  default: MFEM_ABORT("Unknown mesh reodering type " << reorder_mesh);
151  }
152  mesh.ReorderElements(ordering);
153  }
154 
155  // 7. Make sure the mesh is in the non-conforming mode to enable local
156  // refinement of quadrilaterals/hexahedra, and the above partitioning
157  // algorithm. Simplices can be refined either in conforming or in non-
158  // conforming mode. The conforming mode however does not support
159  // dynamic partitioning.
160  mesh.EnsureNCMesh(nc_simplices);
161 
162  // 8. Define a parallel mesh by partitioning the serial mesh.
163  // Once the parallel mesh is defined, the serial mesh can be deleted.
164  pmesh = new ParMesh(MPI_COMM_WORLD, mesh);
165  }
166  else
167  {
168  // 9. We can also restart the computation by loading the mesh from a
169  // previously saved check-point.
170  string fname(MakeParFilename("ex6p-checkpoint.", myid));
171  ifstream ifs(fname);
172  MFEM_VERIFY(ifs.good(), "Checkpoint file " << fname << " not found.");
173  pmesh = new ParMesh(MPI_COMM_WORLD, ifs);
174  }
175 
176  int dim = pmesh->Dimension();
177  int sdim = pmesh->SpaceDimension();
178 
179  MFEM_VERIFY(pmesh->bdr_attributes.Size() > 0,
180  "Boundary attributes required in the mesh.");
181  Array<int> ess_bdr(pmesh->bdr_attributes.Max());
182  ess_bdr = 1;
183 
184  // 10. Define a finite element space on the mesh. The polynomial order is
185  // one (linear) by default, but this can be changed on the command line.
186  H1_FECollection fec(order, dim);
187  ParFiniteElementSpace fespace(pmesh, &fec);
188 
189  // 11. As in Example 1p, we set up bilinear and linear forms corresponding to
190  // the Laplace problem -\Delta u = 1. We don't assemble the discrete
191  // problem yet, this will be done in the main loop.
192  ParBilinearForm a(&fespace);
193  if (pa)
194  {
195  a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
196  a.SetDiagonalPolicy(Operator::DIAG_ONE);
197  }
198  ParLinearForm b(&fespace);
199 
200  ConstantCoefficient one(1.0);
201 
203  a.AddDomainIntegrator(integ);
205 
206  // 12. The solution vector x and the associated finite element grid function
207  // will be maintained over the AMR iterations. We initialize it to zero.
208  ParGridFunction x(&fespace);
209  x = 0;
210 
211  // 13. Connect to GLVis.
212  char vishost[] = "localhost";
213  int visport = 19916;
214 
215  socketstream sout;
216  if (visualization)
217  {
218  sout.open(vishost, visport);
219  if (!sout)
220  {
221  if (myid == 0)
222  {
223  cout << "Unable to connect to GLVis server at "
224  << vishost << ':' << visport << endl;
225  cout << "GLVis visualization disabled.\n";
226  }
227  visualization = false;
228  }
229 
230  sout.precision(8);
231  }
232 
233  // 14. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
234  // with L2 projection in the smoothing step to better handle hanging
235  // nodes and parallel partitioning. We need to supply a space for the
236  // discontinuous flux (L2) and a space for the smoothed flux.
237  L2_FECollection flux_fec(order, dim);
238  ParFiniteElementSpace flux_fes(pmesh, &flux_fec, sdim);
239  FiniteElementCollection *smooth_flux_fec = NULL;
240  ParFiniteElementSpace *smooth_flux_fes = NULL;
241  if (smooth_rt && dim > 1)
242  {
243  // Use an H(div) space for the smoothed flux (this is the default).
244  smooth_flux_fec = new RT_FECollection(order-1, dim);
245  smooth_flux_fes = new ParFiniteElementSpace(pmesh, smooth_flux_fec, 1);
246  }
247  else
248  {
249  // Another possible option for the smoothed flux space: H1^dim space
250  smooth_flux_fec = new H1_FECollection(order, dim);
251  smooth_flux_fes = new ParFiniteElementSpace(pmesh, smooth_flux_fec, dim);
252  }
253  L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fes, *smooth_flux_fes);
254 
255  // 15. A refiner selects and refines elements based on a refinement strategy.
256  // The strategy here is to refine elements with errors larger than a
257  // fraction of the maximum element error. Other strategies are possible.
258  // The refiner will call the given error estimator.
259  ThresholdRefiner refiner(estimator);
260  refiner.SetTotalErrorFraction(0.7);
261 
262  // 16. The main AMR loop. In each iteration we solve the problem on the
263  // current mesh, visualize the solution, and refine the mesh.
264  for (int it = 0; ; it++)
265  {
266  HYPRE_BigInt global_dofs = fespace.GlobalTrueVSize();
267  if (myid == 0)
268  {
269  cout << "\nAMR iteration " << it << endl;
270  cout << "Number of unknowns: " << global_dofs << endl;
271  }
272 
273  // 17. Assemble the right-hand side and determine the list of true
274  // (i.e. parallel conforming) essential boundary dofs.
275  Array<int> ess_tdof_list;
276  fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
277  b.Assemble();
278 
279  // 18. Assemble the stiffness matrix. Note that MFEM doesn't care at this
280  // point that the mesh is nonconforming and parallel. The FE space is
281  // considered 'cut' along hanging edges/faces, and also across
282  // processor boundaries.
283  a.Assemble();
284 
285  // 19. Create the parallel linear system: eliminate boundary conditions.
286  // The system will be solved for true (unconstrained/unique) DOFs only.
287  OperatorPtr A;
288  Vector B, X;
289 
290  const int copy_interior = 1;
291  a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
292 
293  // 20. Solve the linear system A X = B.
294  // * With full assembly, use the BoomerAMG preconditioner from hypre.
295  // * With partial assembly, use a diagonal preconditioner.
296  Solver *M = NULL;
297  if (pa)
298  {
299  M = new OperatorJacobiSmoother(a, ess_tdof_list);
300  }
301  else
302  {
303  HypreBoomerAMG *amg = new HypreBoomerAMG;
304  amg->SetPrintLevel(0);
305  M = amg;
306  }
307  CGSolver cg(MPI_COMM_WORLD);
308  cg.SetRelTol(1e-6);
309  cg.SetMaxIter(2000);
310  cg.SetPrintLevel(3); // print the first and the last iterations only
311  cg.SetPreconditioner(*M);
312  cg.SetOperator(*A);
313  cg.Mult(B, X);
314  delete M;
315 
316  // 21. Switch back to the host and extract the parallel grid function
317  // corresponding to the finite element approximation X. This is the
318  // local solution on each processor.
319  a.RecoverFEMSolution(X, b, x);
320 
321  // 22. Send the solution by socket to a GLVis server.
322  if (visualization)
323  {
324  sout << "parallel " << num_procs << " " << myid << "\n";
325  sout << "solution\n" << *pmesh << x << flush;
326  }
327 
328  if (global_dofs >= max_dofs)
329  {
330  if (myid == 0)
331  {
332  cout << "Reached the maximum number of dofs. Stop." << endl;
333  }
334  break;
335  }
336 
337  // 23. Call the refiner to modify the mesh. The refiner calls the error
338  // estimator to obtain element errors, then it selects elements to be
339  // refined and finally it modifies the mesh. The Stop() method can be
340  // used to determine if a stopping criterion was met.
341  refiner.Apply(*pmesh);
342  if (refiner.Stop())
343  {
344  if (myid == 0)
345  {
346  cout << "Stopping criterion satisfied. Stop." << endl;
347  }
348  break;
349  }
350 
351  // 24. Update the finite element space (recalculate the number of DOFs,
352  // etc.) and create a grid function update matrix. Apply the matrix
353  // to any GridFunctions over the space. In this case, the update
354  // matrix is an interpolation matrix so the updated GridFunction will
355  // still represent the same function as before refinement.
356  fespace.Update();
357  x.Update();
358 
359  // 25. Load balance the mesh, and update the space and solution. Currently
360  // available only for nonconforming meshes.
361  if (pmesh->Nonconforming())
362  {
363  pmesh->Rebalance();
364 
365  // Update the space and the GridFunction. This time the update matrix
366  // redistributes the GridFunction among the processors.
367  fespace.Update();
368  x.Update();
369  }
370 
371  // 26. Inform also the bilinear and linear forms that the space has
372  // changed.
373  a.Update();
374  b.Update();
375 
376  // 27. Save the current state of the mesh every 5 iterations. The
377  // computation can be restarted from this point. Note that unlike in
378  // visualization, we need to use the 'ParPrint' method to save all
379  // internal parallel data structures.
380  if ((it + 1) % 5 == 0)
381  {
382  ofstream ofs(MakeParFilename("ex6p-checkpoint.", myid));
383  ofs.precision(8);
384  pmesh->ParPrint(ofs);
385 
386  if (myid == 0)
387  {
388  cout << "\nCheckpoint saved." << endl;
389  }
390  }
391  }
392 
393  delete smooth_flux_fes;
394  delete smooth_flux_fec;
395  delete pmesh;
396 
397  MPI_Finalize();
398  return 0;
399 }
double GetGeckoElementOrdering(Array< int > &ordering, int iterations=4, int window=4, int period=2, int seed=0, bool verbose=false, double time_limit=0)
Definition: mesh.cpp:1647
int Size() const
Return the logical size of the array.
Definition: array.hpp:134
Class for domain integration L(v) := (f, v)
Definition: lininteg.hpp:97
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
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
std::string MakeParFilename(const std::string &prefix, const int myid, const std::string suffix, const int width)
Construct a string of the form &quot;&lt;prefix&gt;&lt;myid&gt;&lt;suffix&gt;&quot; where the integer myid is padded with leading...
Definition: globals.cpp:43
virtual void Update(bool want_transform=true)
Definition: pfespace.cpp:2988
Pointer to an Operator of a specified type.
Definition: handle.hpp:33
HYPRE_BigInt GlobalTrueVSize() const
Definition: pfespace.hpp:275
bool Stop() const
Check if STOP action is requested, e.g. stopping criterion is satisfied.
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.
Abstract parallel finite element space.
Definition: pfespace.hpp:28
The L2ZienkiewiczZhuEstimator class implements the Zienkiewicz-Zhu error estimation procedure where t...
Definition: estimators.hpp:217
void Update(ParFiniteElementSpace *pf=NULL)
Update the object according to the given new FE space *pf.
Definition: plinearform.cpp:21
void SetDiagonalPolicy(DiagonalPolicy policy)
Sets diagonal policy used upon construction of the linear system.
virtual void Update(FiniteElementSpace *nfes=NULL)
Update the FiniteElementSpace and delete all data associated with the old one.
The BoomerAMG solver in hypre.
Definition: hypre.hpp:1387
bool Apply(Mesh &mesh)
Perform the mesh operation.
void Rebalance()
Definition: pmesh.cpp:3602
bool Nonconforming() const
Definition: mesh.hpp:1367
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[]
void EnsureNCMesh(bool simplices_nonconforming=false)
Definition: mesh.cpp:8800
virtual void Update()
Transform by the Space UpdateMatrix (e.g., on Mesh change).
Definition: pgridfunc.cpp:81
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
void SetPrintLevel(int print_level)
Definition: hypre.hpp:1467
constexpr int visport
Mesh refinement operator using an error threshold.
void SetMaxIter(int max_it)
Definition: solvers.hpp:100
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1)
Definition: mesh.cpp:4882
T Max() const
Find the maximal element in the array, using the comparison operator &lt; for class T.
Definition: array.cpp:68
void Assemble(int skip_zeros=1)
Assemble the local matrix.
void GetHilbertElementOrdering(Array< int > &ordering)
Definition: mesh.cpp:1814
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
int SpaceDimension() const
Definition: mesh.hpp:912
void AddDomainIntegrator(LinearFormIntegrator *lfi)
Adds new Domain Integrator. Assumes ownership of lfi.
Definition: linearform.cpp:39
Abstract base class BilinearFormIntegrator.
Definition: bilininteg.hpp:34
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
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
HYPRE_Int HYPRE_BigInt
virtual void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x)
double a
Definition: lissajous.cpp:41
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition: mesh.hpp:206
void ReorderElements(const Array< int > &ordering, bool reorder_vertices=true)
Definition: mesh.cpp:1866
int dim
Definition: ex24.cpp:53
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.
int open(const char hostname[], int port)
Open the socket stream on &#39;port&#39; at &#39;hostname&#39;.
virtual void SetOperator(const Operator &op)
Also calls SetOperator for the preconditioner if there is one.
Definition: solvers.hpp:330
Vector data type.
Definition: vector.hpp:60
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition: solvers.cpp:92
Arbitrary order H1-conforming (continuous) finite elements.
Definition: fe_coll.hpp:216
Base class for solvers.
Definition: operator.hpp:648
Class for parallel grid function.
Definition: pgridfunc.hpp:32
void SetTotalErrorFraction(double fraction)
Set the total fraction used in the computation of the threshold. The default value is 1/2...
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition: device.hpp:121
void ParPrint(std::ostream &out) const
Save the mesh in a parallel mesh format.
Definition: pmesh.cpp:5525
Class for parallel meshes.
Definition: pmesh.hpp:32
int main()
Arbitrary order &quot;L2-conforming&quot; discontinuous finite elements.
Definition: fe_coll.hpp:285
bool Good() const
Return true if the command line options were parsed successfully.
Definition: optparser.hpp:150