MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
pref321.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, 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// 3:1 Refinement Miniapp: Parallel 3:1 anisotropic mesh refinements
14// -----------------------------------------------------------------
15//
16// This miniapp performs random 3:1 refinements of a quadrilateral or hexahedral
17// mesh. A diffusion equation is solved in an H1 finite element space defined on
18// the refined mesh, and its continuity is verified across local and shared
19// faces.
20//
21// Compile with: make pref321
22//
23// Sample runs: mpirun -np 4 pref321 -mm -dim 2 -o 2 -r 100
24// mpirun -np 4 pref321 -mm -dim 3 -o 2 -r 100
25// mpirun -np 4 pref321 -m ../../data/star.mesh -o 2 -r 100
26
27#include "mfem.hpp"
28#include <fstream>
29#include <iostream>
30
31using namespace std;
32using namespace mfem;
33
35
36// Find the two children of parent element `elem` after its refinement in one
37// direction.
38void FindChildren(const Mesh &mesh, int elem, Array<int> &children)
39{
41 MFEM_ASSERT(mesh.GetNE() == cf.embeddings.Size(), "");
42
43 // Note that row `elem` of the table constructed by cf.MakeCoarseToFineTable
44 // is an alternative to this global loop, but constructing the table is also
45 // a global operation with global storage.
46 for (int i = 0; i < mesh.GetNE(); i++)
47 {
48 const int p = cf.embeddings[i].parent;
49 if (p == elem)
50 {
51 children.Append(i);
52 }
53 }
54}
55
56// Refine 3:1 via 2 refinements with scalings 2/3 and 1/2.
57void Refine31(Mesh &mesh, int elem, char type)
58{
59 Array<Refinement> refs; // Refinement is defined in ncmesh.hpp
60 refs.Append(Refinement(elem, type, 2.0 / 3.0));
61 mesh.GeneralRefinement(refs);
62
63 // Find the elements with parent `elem`
64 Array<int> children;
65 FindChildren(mesh, elem, children);
66 MFEM_ASSERT(children.Size() == 2, "");
67
68 const int elem1 = children[0];
69
70 refs.SetSize(0);
71 refs.Append(Refinement(elem1, type)); // Default scaling of 0.5
72 mesh.GeneralRefinement(refs);
73}
74
75// Randomly select elements for 3:1 refinements in random directions.
76void TestAnisoRefRandom(int num_refs, int dim, ParMesh &mesh, int myid,
77 int seed = 0)
78{
79 std::mt19937 gen(seed);
80 for (int i = 0; i < num_refs; i++)
81 {
82 const int elem = gen() % mesh.GetNE();
83 const int t = gen() % dim;
84 auto type = t == 0 ? Refinement::X :
85 (t == 1 ? Refinement::Y : Refinement::Z);
86
87 // In 3D, check for conflicts in the parallel refinements.
88 if (dim == 3)
89 {
90 std::set<int> conflicts; // Indices in refs of conflicting elements
92 refs.Append(Refinement(elem, type));
93 const bool conflict = mesh.AnisotropicConflict(refs, conflicts);
94 if (conflict)
95 {
96 if (myid == 0)
97 cout << "Anisotropic conflict on iteration " << i
98 << ", retrying\n";
99 i--;
100 continue;
101 }
102 }
103
104 Refine31(mesh, elem, type);
105 }
106
107 mesh.EnsureNodes();
108 mesh.SetScaledNCMesh();
109}
110
111int main(int argc, char *argv[])
112{
113 Mpi::Init(argc, argv);
114 Hypre::Init();
115
116 const int num_procs = Mpi::WorldSize();
117 const int myid = Mpi::WorldRank();
118
119 // 1. Parse command-line options.
120 const char *mesh_file = "../../data/star.mesh";
121 int order = 1;
122 bool visualization = true;
123 bool makeMesh = false;
124 int num_refs = 1;
125 int tdim = 2; // Mesh dimension for Cartesian meshes.
126
127 OptionsParser args(argc, argv);
128 args.AddOption(&mesh_file, "-m", "--mesh",
129 "Mesh file to use.");
130 args.AddOption(&order, "-o", "--order",
131 "Finite element order (polynomial degree).");
132 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
133 "--no-visualization",
134 "Enable or disable GLVis visualization.");
135 args.AddOption(&makeMesh, "-mm", "--make-mesh", "-no-mm",
136 "--no-make-mesh", "Create Cartesian mesh");
137 args.AddOption(&tdim, "-dim", "--dimension", "Dimension for Cartesian mesh");
138 args.AddOption(&num_refs, "-r", "--refs", "Number of 3:1 refinements");
139 args.Parse();
140 if (!args.Good())
141 {
142 if (myid == 0)
143 {
144 args.PrintUsage(cout);
145 }
146 return 1;
147 }
148 if (myid == 0)
149 {
150 args.PrintOptions(cout);
151 }
152
153 // 2. Create or read the serial mesh on all ranks, then apply the same
154 // deterministic 3:1 refinement sequence before partitioning it.
155 Mesh mesh;
156 if (makeMesh)
157 {
158 mesh = tdim == 3 ? Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON) :
160 }
161 else
162 {
163 mesh = Mesh::LoadFromFile(mesh_file, 1, 1);
164 }
165
166 const int dim = mesh.Dimension();
167
168 mesh.EnsureNCMesh();
169 mesh.SetScaledNCMesh();
170
171 // 3. Partition the refined serial mesh.
172 ParMesh pmesh(MPI_COMM_WORLD, mesh);
173 mesh.Clear();
174
175 TestAnisoRefRandom(num_refs, dim, pmesh, myid, myid);
176
177 // 4. Define a parallel H1 finite element space and report its global size.
178 H1_FECollection fec(order, dim);
179 ParFiniteElementSpace fespace(&pmesh, &fec);
180 if (myid == 0)
181 {
182 cout << "Number of finite element unknowns: "
183 << fespace.GlobalTrueVSize() << endl;
184 }
185
186 // 5. Assemble and solve the Poisson problem, following ex1p.
187 ParGridFunction x(&fespace);
188 x = 0.0;
189
190 ParLinearForm b(&fespace);
191 ConstantCoefficient one(1.0);
192 b.AddDomainIntegrator(new DomainLFIntegrator(one));
193 b.Assemble();
194
195 ParBilinearForm a(&fespace);
196 a.AddDomainIntegrator(new DiffusionIntegrator());
197 a.Assemble();
198
199 OperatorPtr A;
200 Vector B, X;
202 if (pmesh.bdr_attributes.Size())
203 {
204 Array<int> ess_bdr(pmesh.bdr_attributes.Max());
205 ess_bdr = 0;
206 pmesh.MarkExternalBoundaries(ess_bdr);
207 fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
208 }
209
210 a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
211
213 CGSolver cg(MPI_COMM_WORLD);
214 cg.SetPreconditioner(M);
215 cg.SetOperator(*A);
216 cg.SetRelTol(1e-12);
217 cg.SetMaxIter(2000);
218 cg.SetPrintLevel(1);
219 cg.Mult(B, X);
220
221 a.RecoverFEMSolution(X, b, x);
222
223 // 6. Verify the continuity of the solution in H1 over local and shared
224 // faces and compute the global maximum jump.
225 const real_t h1err = CheckH1Continuity(x);
226 if (myid == 0)
227 {
228 cout << "Error of H1 continuity: " << h1err << endl;
229 }
230 MFEM_VERIFY(h1err < 1.0e-7, "H1 discontinuity found");
231
232 // 7. Save the refined mesh and the solution in parallel. This output can
233 // be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
234 {
235 ostringstream mesh_name, sol_name;
236 mesh_name << "mesh." << setfill('0') << setw(6) << myid;
237 sol_name << "sol." << setfill('0') << setw(6) << myid;
238
239 ofstream mesh_ofs(mesh_name.str().c_str());
240 mesh_ofs.precision(8);
241 pmesh.Print(mesh_ofs);
242
243 ofstream sol_ofs(sol_name.str().c_str());
244 sol_ofs.precision(8);
245 x.Save(sol_ofs);
246 }
247
248 // 8. Send the parallel solution to GLVis.
249 if (visualization)
250 {
251 char vishost[] = "localhost";
252 int visport = 19916;
253 socketstream sol_sock(vishost, visport);
254 sol_sock << "parallel " << num_procs << " " << myid << "\n";
255 sol_sock.precision(8);
256 sol_sock << "solution\n" << pmesh << x << flush;
257 }
258
259 return 0;
260}
261
263{
264 const ParFiniteElementSpace *pfes = x.ParFESpace();
265 ParMesh *pmesh = pfes->GetParMesh();
266 const int dim = pmesh->Dimension();
267
268 real_t errorMax = 0.0;
269
270 // Shared-face values require face-neighbor data.
272
273 // First handle faces for which both elements are local to this rank.
274 for (int f = 0; f < pmesh->GetNumFaces(); f++)
275 {
276 const auto info = pmesh->GetFaceInformation(f);
277 if (!info.IsLocal())
278 {
279 continue;
280 }
281
283 const int faceOrder = dim == 3 ? pfes->GetFaceOrder(f) :
284 pfes->GetEdgeOrder(f);
285 const IntegrationRule &ir = IntRules.Get(FT->FaceGeom, 2 * faceOrder);
286
287 for (int i = 0; i < ir.GetNPoints(); i++)
288 {
289 const IntegrationPoint &fip = ir.IntPoint(i);
290 IntegrationPoint ip1, ip2;
291
292 FT->Loc1.Transform(fip, ip1);
293 FT->Loc2.Transform(fip, ip2);
294
295 const real_t v1 = x.GetValue(*FT->Elem1, ip1);
296 const real_t v2 = x.GetValue(*FT->Elem2, ip2);
297 errorMax = std::max(errorMax, std::abs(v1 - v2));
298 }
299 }
300
301 // Then check partition interfaces. Conforming shared faces are handled on
302 // the lower-rank side, while shared slave nonconforming faces are handled
303 // only on the slave side and therefore do not need additional filtering.
304 for (int sf = 0; sf < pmesh->GetNSharedFaces(); sf++)
305 {
306 const int f = pmesh->GetSharedFace(sf);
307 const auto info = pmesh->GetFaceInformation(f);
308 if (!info.IsShared())
309 {
310 continue;
311 }
312
314 const int faceOrder = dim == 3 ? pfes->GetFaceOrder(f) :
315 pfes->GetEdgeOrder(f);
316 const IntegrationRule &ir = IntRules.Get(FT->FaceGeom, 2 * faceOrder);
317
318 for (int i = 0; i < ir.GetNPoints(); i++)
319 {
320 const IntegrationPoint &fip = ir.IntPoint(i);
321 IntegrationPoint ip1, ip2;
322
323 FT->Loc1.Transform(fip, ip1);
324 FT->Loc2.Transform(fip, ip2);
325
326 const real_t v1 = x.GetValue(*FT->Elem1, ip1);
327 const real_t v2 = x.GetValue(*FT->Elem2, ip2);
328 errorMax = std::max(errorMax, std::abs(v1 - v2));
329 }
330 }
331
332 MPI_Allreduce(MPI_IN_PLACE, &errorMax, 1, MFEM_MPI_REAL_T, MPI_MAX,
333 pmesh->GetComm());
334
335 return errorMax;
336}
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:69
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
int Size() const
Return the logical size of the array.
Definition array.hpp:192
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:941
Conjugate gradient method.
Definition solvers.hpp:627
A coefficient that is constant across space and time.
Class for domain integration .
Definition lininteg.hpp:108
A specialized ElementTransformation class representing a face and its two neighboring elements.
Definition eltrans.hpp:750
ElementTransformation * Elem2
Definition eltrans.hpp:791
ElementTransformation * Elem1
Definition eltrans.hpp:791
IntegrationPointTransformation Loc1
Definition eltrans.hpp:793
IntegrationPointTransformation Loc2
Definition eltrans.hpp:793
int GetEdgeOrder(int edge, int variant=0) const
Definition fespace.cpp:3379
int GetFaceOrder(int face, int variant=0) const
Returns the polynomial degree of the i'th face finite element.
Definition fespace.cpp:3395
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
The BoomerAMG solver in hypre.
Definition hypre.hpp:1829
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
void Transform(const IntegrationPoint &, IntegrationPoint &)
Definition eltrans.cpp:587
Class for integration point with weight.
Definition intrules.hpp:35
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:96
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:255
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition intrules.hpp:258
const IntegrationRule & Get(int GeomType, int Order)
Returns an integration rule for given GeomType and Order.
Mesh data type.
Definition mesh.hpp:67
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:309
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:7302
void GeneralRefinement(const Array< Refinement > &refinements, int nonconforming=-1, int nc_limit=0)
Definition mesh.cpp:11713
void EnsureNodes()
Make sure that the mesh has valid nodes, i.e. its geometry is described by a vector finite element gr...
Definition mesh.cpp:7159
void SetScaledNCMesh()
Definition mesh.hpp:2543
void Clear()
Clear the contents of the Mesh.
Definition mesh.hpp:835
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1368
static Mesh MakeCartesian3D(int nx, int ny, int nz, Element::Type type, real_t sx=1.0, real_t sy=1.0, real_t sz=1.0, bool sfc_ordering=true)
Creates a mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into nx*ny*nz hexahedra if type =...
Definition mesh.cpp:4786
static Mesh LoadFromFile(const std::string &filename, int generate_edges=0, int refine=1, bool fix_orientation=true)
Definition mesh.cpp:4758
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:318
void EnsureNCMesh(bool simplices_nonconforming=false)
Definition mesh.cpp:11781
static Mesh MakeCartesian2D(int nx, int ny, Element::Type type, bool generate_edges=false, real_t sx=1.0, real_t sy=1.0, bool sfc_ordering=true)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny quadrilaterals if type = QUADRILATER...
Definition mesh.cpp:4776
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).
const CoarseFineTransformations & GetRefinementTransforms() const
Definition ncmesh.cpp:5204
Pointer to an Operator of a specified type.
Definition handle.hpp:34
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:31
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:361
ParMesh * GetParMesh() const
Definition pfespace.hpp:341
Class for parallel grid function.
Definition pgridfunc.hpp:50
void Save(std::ostream &out) const override
real_t GetValue(int i, const IntegrationPoint &ip, int vdim=1) const override
ParFiniteElementSpace * ParFESpace() const
Class for parallel linear form.
Class for parallel meshes.
Definition pmesh.hpp:35
MPI_Comm GetComm() const
Definition pmesh.hpp:403
int GetNSharedFaces() const
Return the number of shared faces (3D), edges (2D), vertices (1D)
Definition pmesh.cpp:3193
FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31) override
Definition pmesh.cpp:2926
bool AnisotropicConflict(const Array< Refinement > &refinements, std::set< int > &conflicts) const
Return true if the input array of refinements to be performed would result in conflicting anisotropic...
Definition pmesh.cpp:3929
void MarkExternalBoundaries(Array< int > &bdr_marker, bool excl=true) const override
Mark boundary attributes of external boundaries.
Definition pmesh.cpp:7011
int GetSharedFace(int sface) const
Return the local face index for the given shared face.
Definition pmesh.cpp:3212
FaceElementTransformations * GetSharedFaceTransformations(int sf, bool fill2=true)
Get the FaceElementTransformations for the given shared face (edge 2D) using the shared face index sf...
Definition pmesh.cpp:2952
void Print(std::ostream &out=mfem::out, const std::string &comments="") const override
Definition pmesh.cpp:4856
Vector data type.
Definition vector.hpp:82
const int * ess_tdof_list
int dim
Definition ex24.cpp:53
int main()
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
float real_t
Definition config.hpp:46
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
IntegrationRules IntRules(0, Quadrature1D::GaussLegendre)
A global object with all integration rules (defined in intrules.cpp)
Definition intrules.hpp:549
const char vishost[]
STL namespace.
real_t p(const Vector &x, real_t t)
void FindChildren(const Mesh &mesh, int elem, Array< int > &children)
Definition pref321.cpp:38
void Refine31(Mesh &mesh, int elem, char type)
Definition pref321.cpp:57
void TestAnisoRefRandom(int num_refs, int dim, ParMesh &mesh, int myid, int seed=0)
Definition pref321.cpp:76
real_t CheckH1Continuity(ParGridFunction &x)
Definition pref321.cpp:262
Defines the coarse-fine transformations of all fine elements.
Definition ncmesh.hpp:90
Array< Embedding > embeddings
Fine element positions in their parents.
Definition ncmesh.hpp:92