MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
schwarz_ex1p.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11//
12// -------------------------------------------------
13// Overlapping Grids Miniapp: Poisson problem (ex1p)
14// -------------------------------------------------
15//
16// This example code demonstrates use of MFEM to solve the Poisson problem:
17//
18// -Delta u = 1 \in [0, 1]^2, u_b = 0 \in \dO
19//
20// on two overlapping grids. Using simultaneous Schwarz iterations, the Poisson
21// equation is solved iteratively, with boundary data interpolated between the
22// overlapping boundaries for each grid. The overlapping Schwarz method was
23// introduced by H. A. Schwarz in 1870, see also Section 2.2 of "Stability
24// analysis of a singlerate and multirate predictor-corrector scheme for
25// overlapping grids" by Mittal, Dutta and Fischer, arXiv:2010.00118.
26//
27// Compile with: make schwarz_ex1p
28//
29// Sample runs: mpirun -np 4 schwarz_ex1p -nm 3 -np1 2 -np2 1 -np3 1
30// mpirun -np 4 schwarz_ex1p -nm 2 -np1 2 -np2 2
31// mpirun -np 4 schwarz_ex1p -nm 2 -np1 2 -np2 2 -m1 ../../data/star.mesh -m2 ../../data/beam-quad.mesh
32
33#include "mfem.hpp"
34#include <fstream>
35#include <iostream>
36
37using namespace std;
38using namespace mfem;
39
40// Method to use FindPointsGSLIB to determine the boundary points of a mesh that
41// are interior to another mesh.
43 Vector &vxyz, int color,
44 Array<int> ess_tdof_list,
45 Array<int> &ess_tdof_list_int, int dim)
46{
47 int number_boundary = ess_tdof_list.Size(),
48 number_true = vxyz.Size()/dim;
49
50 Vector bnd(number_boundary*dim);
51 Array<unsigned int> colorv(number_boundary);
52 for (int i = 0; i < number_boundary; i++)
53 {
54 int idx = ess_tdof_list[i];
55 for (int d = 0; d < dim; d++)
56 {
57 bnd(i+d*number_boundary) = vxyz(idx + d*number_true);
58 }
59 colorv[i] = (unsigned int)color;
60 }
61
62 finder.FindPoints(bnd, colorv);
63
64 const Array<unsigned int> &code_out = finder.GetCode();
65
66 // Setup ess_tdof_list_int
67 for (int i = 0; i < number_boundary; i++)
68 {
69 int idx = ess_tdof_list[i];
70 if (code_out[i] != 2) { ess_tdof_list_int.Append(idx); }
71 }
72}
73
74int main(int argc, char *argv[])
75{
76 // Initialize MPI and HYPRE.
77 Mpi::Init(argc, argv);
78 int num_procs = Mpi::WorldSize();
79 int myid = Mpi::WorldRank();
81
82 // Parse command-line options.
83 int lim_meshes = 3; // should be greater than nmeshes
84 Array <const char *> mesh_file_list(lim_meshes);
85 Array <int> np_list(lim_meshes), rs_levels(lim_meshes),
86 rp_levels(lim_meshes);
87 mesh_file_list[0] = "../../data/square-disc.mesh";
88 mesh_file_list[1] = "../../data/inline-quad.mesh";
89 mesh_file_list[2] = "../../data/inline-quad.mesh";
90 int order = 2;
91 bool visualization = true;
92 rs_levels = 0;
93 rp_levels = 0;
94 np_list = 0;
95 double rel_tol = 1.e-8;
96 int visport = 19916;
97 int nmeshes = 3;
98
99 OptionsParser args(argc, argv);
100 args.AddOption(&mesh_file_list[0], "-m1", "--mesh",
101 "Mesh file to use.");
102 args.AddOption(&mesh_file_list[1], "-m2", "--mesh",
103 "Mesh file to use.");
104 args.AddOption(&mesh_file_list[2], "-m3", "--mesh",
105 "Mesh file to use.");
106 args.AddOption(&order, "-o", "--order",
107 "Finite element order (polynomial degree) or -1 for"
108 " isoparametric space.");
109 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
110 "--no-visualization",
111 "Enable or disable GLVis visualization.");
112 args.AddOption(&rs_levels[0], "-r1", "--refine-serial",
113 "Number of times to refine the mesh 1 uniformly in serial.");
114 args.AddOption(&rs_levels[1], "-r2", "--refine-serial",
115 "Number of times to refine the mesh 2 uniformly in serial.");
116 args.AddOption(&rs_levels[2], "-r3", "--refine-serial",
117 "Number of times to refine the mesh 3 uniformly in serial.");
118 args.AddOption(&rp_levels[0], "-rp1", "--refine-parallel",
119 "Number of times to refine the mesh 1 uniformly in parallel.");
120 args.AddOption(&rp_levels[1], "-rp2", "--refine-parallel",
121 "Number of times to refine the mesh 2 uniformly in parallel.");
122 args.AddOption(&rp_levels[2], "-rp3", "--refine-parallel",
123 "Number of times to refine the mesh 3 uniformly in parallel.");
124 args.AddOption(&np_list[0], "-np1", "--np1",
125 "number of MPI ranks for mesh 1");
126 args.AddOption(&np_list[1], "-np2", "--np2",
127 "number of MPI ranks for mesh 2");
128 args.AddOption(&np_list[2], "-np3", "--np3",
129 "number of MPI ranks for mesh 3");
130 args.AddOption(&nmeshes, "-nm", "--nm",
131 "number of meshes");
132 args.AddOption(&rel_tol, "-rt", "--relative tolerance",
133 "Tolerance for Schwarz iteration convergence criterion.");
134 args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
135 args.Parse();
136 if (!args.Good())
137 {
138 args.PrintUsage(cout);
139 return 1;
140 }
141 if (myid == 0)
142 {
143 args.PrintOptions(cout);
144 }
145
146 // Check number of mpi ranks specified for each mesh. If individual mpi ranks
147 // are not specified for all the meshes, set some default values.
148 MFEM_VERIFY(num_procs >= nmeshes, "Not enough MPI ranks.");
149 if (np_list.Sum() == 0)
150 {
151 int np_per_mesh = num_procs/nmeshes;
152 for (int i = 0; i < nmeshes; i++)
153 {
154 np_list[i] = np_per_mesh;
155 }
156 np_list[nmeshes-1] += num_procs - nmeshes*np_per_mesh;
157 }
158 MFEM_VERIFY(np_list.Sum() == num_procs, " The individual mpi ranks for each"
159 " of the meshes do not add up to the total ranks specified.");
160
161 // Setup MPI communicator for each mesh by splitting MPI_COMM_WORLD.
162 MPI_Comm *comml = new MPI_Comm;
163 int color = 0;
164 int npsum = 0;
165 for (int i = 0; i < nmeshes; i++)
166 {
167 npsum += np_list[i];
168 if (myid < npsum) { color = i; break; }
169 }
170
171 MPI_Comm_split(MPI_COMM_WORLD, color, myid, comml);
172 int myidlocal, numproclocal;
173 MPI_Comm_rank(*comml, &myidlocal);
174 MPI_Comm_size(*comml, &numproclocal);
175
176 // Read the mesh from the given mesh file. We can handle triangular,
177 // quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
178 // the same code.
179 Mesh *mesh = new Mesh(mesh_file_list[color], 1, 1);
180 int dim = mesh->Dimension();
181
182 // Refine the mesh to increase the resolution. In this example we do
183 // 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
184 // largest number that gives a final mesh with no more than 50,000 elements.
185 for (int lev = 0; lev < rs_levels[color]; lev++) { mesh->UniformRefinement(); }
186
187 // Define a parallel mesh by a partitioning of the serial mesh. Refine this
188 // mesh further in parallel to increase the resolution. Once the parallel
189 // mesh is defined, the serial mesh can be deleted.
190 ParMesh *pmesh;
191 pmesh = new ParMesh(*comml, *mesh);
192 for (int l = 0; l < rp_levels[color]; l++)
193 {
194 pmesh->UniformRefinement();
195 }
196 delete mesh;
197
198 // Define a finite element space on the mesh. Here we use continuous
199 // Lagrange finite elements of the specified order. If order < 1, we
200 // instead use an isoparametric/isogeometric space.
202 if (order > 0)
203 {
204 fec = new H1_FECollection(order, dim);
205 }
206 else if (pmesh->GetNodes())
207 {
208 fec = pmesh->GetNodes()->OwnFEC();
209 if (myid == 0)
210 {
211 cout << "Using isoparametric FEs: " << fec->Name() << endl;
212 }
213 }
214 else
215 {
216 fec = new H1_FECollection(order = 1, dim);
217 }
218 ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
219 HYPRE_BigInt size = fespace->GlobalTrueVSize();
220 if (myid == 0)
221 {
222 cout << "Number of finite element unknowns: " << size << endl;
223 }
224
225 // Determine the list of true (i.e. conforming) essential boundary dofs.
226 // In this example, the boundary conditions are defined by marking all
227 // the boundary attributes from the mesh as essential (Dirichlet) and
228 // converting them to a list of true dofs.
229 Array<int> ess_tdof_list;
230 if (pmesh->bdr_attributes.Size())
231 {
232 Array<int> ess_bdr(pmesh->bdr_attributes.Max());
233 ess_bdr = 1;
234 fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
235 }
236
237 // Set up the linear form b(.) which corresponds to the right-hand side of
238 // the FEM linear system, which in this case is (1,phi_i) where phi_i are
239 // the basis functions in the finite element fespace1.
240 ParLinearForm *b = new ParLinearForm(fespace);
241 ConstantCoefficient one(1.0);
242 b->AddDomainIntegrator(new DomainLFIntegrator(one));
243 b->Assemble();
244
245 // Define the solution vector x as a finite element grid function
246 // corresponding to fespace1. Initialize x with initial guess of zero,
247 // which satisfies the boundary conditions.
248 ParGridFunction x(fespace);
249 x = 0.0;
250 x.SetTrueVector();
251
252 // Setup FindPointsGSLIB and determine points on each mesh's boundary that
253 // are interior to another mesh.
254 pmesh->SetCurvature(order, false, dim, Ordering::byNODES);
255 {
256 Vector vxyz = *pmesh->GetNodes();
257
258 // For the default mesh inputs, we need to rescale inline-quad.mesh such
259 // that it does not cover the entire domain [0, 1]^2 and still has a
260 // non-trivial overlap with the other mesh.
261 if (strcmp(mesh_file_list[0], "../../data/square-disc.mesh") == 0 &&
262 strcmp(mesh_file_list[1], "../../data/inline-quad.mesh") == 0 &&
263 strcmp(mesh_file_list[2], "../../data/inline-quad.mesh") == 0 )
264 {
265 if (nmeshes == 2)
266 {
267 if (color == 1) // rescale from [0, 1]^2 to [0.25, 0.75]^2
268 {
269 for (int i = 0; i < vxyz.Size(); i++)
270 {
271 vxyz(i) = 0.5 + 0.5*(vxyz(i)-0.5);
272 }
273 }
274 }
275 else if (nmeshes == 3)
276 {
277 if (color == 1)
278 {
279 // rescale from [0, 1]^2 to [0.21, 0.61] in x and [0.25, 0.75] in y
280 const int pts_cnt = vxyz.Size()/dim;
281 for (int i = 0; i < pts_cnt; i++)
282 {
283 vxyz(i) = 0.41 + 0.4*(vxyz(i)-0.5);
284 }
285 for (int i = 0; i < pts_cnt; i++)
286 {
287 vxyz(i+pts_cnt) = 0.5 + 0.5*(vxyz(i+pts_cnt)-0.5);
288 }
289 }
290 else if (color == 2)
291 {
292 // rescale from [0, 1]^2 to [0.4, 0.8] in x and [0.2, 0.8] in y
293 const int pts_cnt = vxyz.Size()/dim;
294 for (int i = 0; i < pts_cnt; i++)
295 {
296 vxyz(i) = 0.6 + 0.4*(vxyz(i)-0.5);
297 }
298 for (int i = 0; i < pts_cnt; i++)
299 {
300 vxyz(i+pts_cnt) = 0.5 + 0.6*(vxyz(i+pts_cnt)-0.5);
301 }
302 }
303 }
304 }
305 pmesh->SetNodes(vxyz);
306 }
307
308 pmesh->GetNodes()->SetTrueVector();
309 Vector vxyz = pmesh->GetNodes()->GetTrueVector();
310
311
312 OversetFindPointsGSLIB finder(MPI_COMM_WORLD);
313 finder.Setup(*pmesh, color);
314
315 Array<int> ess_tdof_list_int;
316 GetInterdomainBoundaryPoints(finder, vxyz, color,
317 ess_tdof_list, ess_tdof_list_int, dim);
318
319 // Use FindPointsGSLIB to interpolate the solution at interdomain boundary
320 // points.
321 const int number_boundary = ess_tdof_list_int.Size(),
322 number_true = vxyz.Size()/dim;
323
324 int number_boundary_g = number_boundary;
325 MPI_Allreduce(&number_boundary, &number_boundary_g, 1, MPI_INT, MPI_SUM,
326 *comml);
327 MFEM_VERIFY(number_boundary_g != 0, " Please use overlapping grids.");
328
329 Array<unsigned int> colorv;
330 colorv.SetSize(number_boundary);
331
332 MPI_Barrier(MPI_COMM_WORLD);
333 Vector bnd(number_boundary*dim);
334 for (int i = 0; i < number_boundary; i++)
335 {
336 int idx = ess_tdof_list_int[i];
337 for (int d = 0; d < dim; d++)
338 {
339 bnd(i+d*number_boundary) = vxyz(idx + d*number_true);
340 }
341 colorv[i] = (unsigned int)color;
342 }
343 Vector interp_vals1(number_boundary);
344 finder.Interpolate(bnd, colorv, x, interp_vals1);
345
346 // Set up the bilinear form a(.,.) on the finite element space corresponding
347 // to the Laplacian operator -Delta, by adding a Diffusion integrator.
348 ParBilinearForm *a = new ParBilinearForm(fespace);
349 a->AddDomainIntegrator(new DiffusionIntegrator(one));
350
351 // Assemble the bilinear form and the corresponding linear system,
352 // applying any necessary transformations such as: eliminating boundary
353 // conditions, applying conforming constraints for non-conforming AMR,
354 // static condensation, etc.
355 a->Assemble();
356
357 delete b;
358
359 // Use simultaneous Schwarz iterations to iteratively solve the PDE and
360 // interpolate interdomain boundary data to impose Dirichlet boundary
361 // conditions.
362
363 int NiterSchwarz = 100;
364 for (int schwarz = 0; schwarz < NiterSchwarz; schwarz++)
365 {
366 b = new ParLinearForm(fespace);
367 b->AddDomainIntegrator(new DomainLFIntegrator(one));
368 b->Assemble();
369
370 OperatorPtr A;
371 Vector B, X;
372 a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
373
374 // Solve the linear system A X = B.
375 // Use a simple symmetric Gauss-Seidel preconditioner with PCG.
376 Solver *prec = NULL;
377 prec = new HypreBoomerAMG;
378 dynamic_cast<HypreBoomerAMG *>(prec)->SetPrintLevel(-1);
379 CGSolver cg(*comml);
380 cg.SetRelTol(1e-12);
381 cg.SetMaxIter(2000);
382 cg.SetPrintLevel(0);
383 cg.SetPreconditioner(*prec);
384 cg.SetOperator(*A);
385 cg.Mult(B, X);
386 delete prec;
387
388 // Recover the solution as a finite element grid function.
389 a->RecoverFEMSolution(X, *b, x);
390
391 // Interpolate boundary condition
392 finder.Interpolate(x, interp_vals1);
393
394 double dxmax = std::numeric_limits<float>::min();
395 double xinf = x.Normlinf();
396 double xinfg = xinf;
397 MPI_Allreduce(&xinf, &xinfg, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
398 x.SetTrueVector();
399 Vector xt = x.GetTrueVector();
400 for (int i = 0; i < number_boundary; i++)
401 {
402 int idx = ess_tdof_list_int[i];
403 double dx = std::abs(xt(idx)-interp_vals1(i))/xinfg;
404 if (dx > dxmax) { dxmax = dx; }
405 xt(idx) = interp_vals1(i);
406 }
407 x.SetFromTrueDofs(xt);
408 double dxmaxg = dxmax;
409 MPI_Allreduce(&dxmax, &dxmaxg, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
410
411 delete b;
412
413 if (myid == 0)
414 {
415 std::cout << std::setprecision(8) <<
416 "Iteration: " << schwarz <<
417 ", Relative residual: " << dxmaxg << endl;
418 }
419
420 if (dxmaxg < rel_tol) { break; }
421 }
422
423 // Send the solution by socket to a GLVis server.
424 if (visualization)
425 {
426 char vishost[] = "localhost";
427 socketstream sol_sock(vishost, visport);
428 sol_sock << "parallel " << num_procs << " " << myid << "\n";
429 sol_sock.precision(8);
430 sol_sock << "solution\n" << *pmesh << x << flush;
431 }
432
433 // 15. Free the used memory.
434 finder.FreeData();
435 delete a;
436 delete fespace;
437 if (order > 0) { delete fec; }
438 delete pmesh;
439 delete comml;
440
441
442 return 0;
443}
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:68
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:758
int Size() const
Return the logical size of the array.
Definition array.hpp:147
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:830
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.
Class for domain integration .
Definition lininteg.hpp:106
virtual const Array< unsigned int > & GetCode() const
Definition gslib.hpp:295
virtual void FreeData()
Definition gslib.cpp:1128
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
virtual const char * Name() const
Definition fe_coll.hpp:79
void SetTrueVector()
Shortcut for calling GetTrueDofs() with GetTrueVector() as argument.
Definition gridfunc.hpp:147
const Vector & GetTrueVector() const
Read only access to the (optional) internal true-dof Vector.
Definition gridfunc.hpp:133
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:275
The BoomerAMG solver in hypre.
Definition hypre.hpp:1717
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
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 Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:9294
void SetNodes(const Vector &node_coord)
Updates the vertex/node locations. Invokes NodesUpdated().
Definition mesh.cpp:9306
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).
Pointer to an Operator of a specified type.
Definition handle.hpp:34
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.
OversetFindPointsGSLIB enables use of findpts for arbitrary number of overlapping grids.
Definition gslib.hpp:371
void Setup(Mesh &m, const int meshid, GridFunction *gfmax=NULL, const double bb_t=0.1, const double newt_tol=1.0e-12, const int npt_max=256)
Definition gslib.cpp:2382
void Interpolate(const Vector &point_pos, Array< unsigned int > &point_id, const GridFunction &field_in, Vector &field_out, int point_pos_ordering=Ordering::byNODES)
Definition gslib.cpp:2557
void FindPoints(const Vector &point_pos, Array< unsigned int > &point_id, int point_pos_ordering=Ordering::byNODES)
Definition gslib.cpp:2477
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 SetFromTrueDofs(const Vector &tv) override
Set the GridFunction from the given true-dof vector.
Class for parallel linear form.
Class for parallel meshes.
Definition pmesh.hpp:34
void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1) override
Set the curvature of the mesh nodes using the given polynomial degree.
Definition pmesh.cpp:2006
Base class for solvers.
Definition operator.hpp:780
Vector data type.
Definition vector.hpp:82
real_t Normlinf() const
Returns the l_infinity norm of the vector.
Definition vector.cpp:972
int Size() const
Returns the size of the vector.
Definition vector.hpp:226
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
const char vishost[]
struct schwarz_common schwarz
void GetInterdomainBoundaryPoints(OversetFindPointsGSLIB &finder, Vector &vxyz, int color, Array< int > ess_tdof_list, Array< int > &ess_tdof_list_int, int dim)