MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
ex40.cpp
Go to the documentation of this file.
1// MFEM Example 40
2//
3// Compile with: make ex40
4//
5// Sample runs: ex40 -step 10.0 -gr 2.0
6// ex40 -step 10.0 -gr 2.0 -o 3 -r 1
7// ex40 -step 10.0 -gr 2.0 -r 4 -m ../data/l-shape.mesh
8// ex40 -step 10.0 -gr 2.0 -r 2 -m ../data/fichera.mesh
9//
10// Description: This example code demonstrates how to use MFEM to solve the
11// eikonal equation,
12//
13// |โˆ‡๐‘ข| = 1 in ฮฉ, ๐‘ข = 0 on โˆ‚ฮฉ.
14//
15// The viscosity solution of this problem coincides with the unique optimum
16// of the nonlinear program
17//
18// maximize โˆซ_ฮฉ ๐‘ข d๐‘ฅ subject to |โˆ‡๐‘ข| โ‰ค 1 in ฮฉ, ๐‘ข = 0 on โˆ‚ฮฉ, (โ‹†)
19//
20// which is the foundation for method implemented below.
21//
22// Following the proximal Galerkin methodology [1,2] (see also Example
23// 36), we construct a Legendre function for the closed unit ball
24// ๐ตโ‚ := {๐‘ฅ โˆˆ Rโฟ | |๐‘ฅ| โ‰ค 1}. Our choice is the Hellinger entropy,
25//
26// R(๐‘ฅ) = โˆ’( 1 โˆ’ |๐‘ฅ|ยฒ )^{1/2},
27//
28// although other choices are possible, each leading to a slightly
29// different algorithm. We then adaptively regularize the optimization
30// problem (โ‹†) with the Bregman divergence of the Hellinger entropy,
31//
32// maximize โˆซ_ฮฉ ๐‘ข d๐‘ฅ - ฮฑโ‚–โปยน D(โˆ‡๐‘ข,โˆ‡๐‘ขโ‚–โ‚‹โ‚) subject to ๐‘ข = 0 on ฮฉ.
33//
34// This results in a sequence of functions ( ๐œ“โ‚– , ๐‘ขโ‚– ),
35//
36// ๐‘ขโ‚– โ†’ ๐‘ข, ๐œ“โ‚–/|๐œ“โ‚–| โ†’ โˆ‡๐‘ข as k โ†’ โˆž,
37//
38// defined by the nonlinear saddle-point problems
39//
40// Find ๐œ“โ‚– โˆˆ H(div,ฮฉ) and ๐‘ขโ‚– โˆˆ Lยฒ(ฮฉ) such that
41// ( (โˆ‡R)โปยน(๐œ“โ‚–) , ฯ„ ) + ( ๐‘ขโ‚– , โˆ‡โ‹…ฯ„ ) = 0 โˆ€ ฯ„ โˆˆ H(div,ฮฉ)
42// ( โˆ‡โ‹…๐œ“โ‚– , v ) = ( โˆ‡โ‹…๐œ“โ‚–โ‚‹โ‚ - ฮฑโ‚– , v ) โˆ€ v โˆˆ Lยฒ(ฮฉ)
43//
44// where (โˆ‡R)โปยน(๐œ“) = ๐œ“ / ( 1 + |๐œ“|ยฒ )^{1/2} and ฮฑโ‚– = ฮฑโ‚€rแต, where r โ‰ฅ 1
45// is a prescribed growth rate. (r = 1 is the most stable.) The
46// saddle-point problems are solved using a damped quasi-Newton method
47// with a tunable regularization parameter 0 โ‰ค ฯต << 1.
48//
49// [1] Keith, B. and Surowiec, T. (2024) Proximal Galerkin: A structure-
50// preserving finite element method for pointwise bound constraints.
51// Foundations of Computational Mathematics, 1โ€“97.
52// [2] Dokken, J., Farrell, P., Keith, B., Papadopoulos, I., and
53// Surowiec, T. (2025) The latent variable proximal point algorithm
54// for variational problems with inequality constraints. (To appear.)
55
56#include "mfem.hpp"
57#include <fstream>
58#include <iostream>
59
60using namespace std;
61using namespace mfem;
62
63class IsomorphismCoefficient : public VectorCoefficient
64{
65protected:
66 GridFunction *psi;
67
68public:
69 IsomorphismCoefficient(int vdim, GridFunction &psi_)
70 : VectorCoefficient(vdim), psi(&psi_) { }
71
73
74 void Eval(Vector &V, ElementTransformation &T,
75 const IntegrationPoint &ip) override;
76};
77
78class DIsomorphismCoefficient : public MatrixCoefficient
79{
80protected:
81 GridFunction *psi;
82 real_t eps;
83
84public:
85 DIsomorphismCoefficient(int height, GridFunction &psi_, real_t eps_ = 0.0)
86 : MatrixCoefficient(height), psi(&psi_), eps(eps_) { }
87
88 void Eval(DenseMatrix &K, ElementTransformation &T,
89 const IntegrationPoint &ip) override;
90};
91
92int main(int argc, char *argv[])
93{
94 // 1. Parse command-line options.
95 const char *mesh_file = "../data/star.mesh";
96 int order = 1;
97 int max_it = 5;
98 int ref_levels = 3;
99 real_t alpha = 1.0;
100 real_t growth_rate = 1.0;
101 real_t newton_scaling = 0.8;
102 real_t eps = 1e-6;
103 real_t tol = 1e-4;
104 bool visualization = true;
105
106 OptionsParser args(argc, argv);
107 args.AddOption(&mesh_file, "-m", "--mesh",
108 "Mesh file to use.");
109 args.AddOption(&order, "-o", "--order",
110 "Finite element order (polynomial degree).");
111 args.AddOption(&ref_levels, "-r", "--refs",
112 "Number of h-refinements.");
113 args.AddOption(&max_it, "-mi", "--max-it",
114 "Maximum number of iterations");
115 args.AddOption(&tol, "-tol", "--tol",
116 "Stopping criteria based on the difference between"
117 "successive solution updates");
118 args.AddOption(&alpha, "-step", "--step",
119 "Initial size alpha");
120 args.AddOption(&growth_rate, "-gr", "--growth-rate",
121 "Growth rate of the step size alpha");
122 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
123 "--no-visualization",
124 "Enable or disable GLVis visualization.");
125 args.Parse();
126 if (!args.Good())
127 {
128 args.PrintUsage(cout);
129 return 1;
130 }
131 args.PrintOptions(cout);
132
133 // 2. Read the mesh from the mesh file.
134 Mesh mesh(mesh_file, 1, 1);
135 int dim = mesh.Dimension();
136 int sdim = mesh.SpaceDimension();
137
138 MFEM_ASSERT(mesh.bdr_attributes.Size(),
139 "This example does not support meshes"
140 " without boundary attributes."
141 )
142
143 // 3. Postprocess the mesh.
144 // 3A. Refine the mesh to increase the resolution.
145 for (int l = 0; l < ref_levels; l++)
146 {
147 mesh.UniformRefinement();
148 }
149
150 // 3B. Interpolate the geometry after refinement to control geometry error.
151 // NOTE: Minimum second-order interpolation is used to improve the accuracy.
152 int curvature_order = max(order,2);
153 mesh.SetCurvature(curvature_order);
154
155 // 4. Define the necessary finite element spaces on the mesh.
156 RT_FECollection RTfec(order, dim);
157 FiniteElementSpace RTfes(&mesh, &RTfec);
158
159 L2_FECollection L2fec(order, dim);
160 FiniteElementSpace L2fes(&mesh, &L2fec);
161
162 cout << "Number of H(div) dofs: "
163 << RTfes.GetTrueVSize() << endl;
164 cout << "Number of Lยฒ dofs: "
165 << L2fes.GetTrueVSize() << endl;
166
167 // 5. Define the offsets for the block matrices
168 Array<int> offsets(3);
169 offsets[0] = 0;
170 offsets[1] = RTfes.GetVSize();
171 offsets[2] = L2fes.GetVSize();
172 offsets.PartialSum();
173
174 BlockVector x(offsets), rhs(offsets);
175 x = 0.0; rhs = 0.0;
176
177 // 6. Define the solution vectors as a finite element grid functions
178 // corresponding to the fespaces.
179 GridFunction u_gf, delta_psi_gf;
180 delta_psi_gf.MakeRef(&RTfes,x,offsets[0]);
181 u_gf.MakeRef(&L2fes,x,offsets[1]);
182
183 GridFunction psi_old_gf(&RTfes);
184 GridFunction psi_gf(&RTfes);
185 GridFunction u_old_gf(&L2fes);
186
187 // 7. Define initial guesses for the solution variables.
188 delta_psi_gf = 0.0;
189 psi_gf = 0.0;
190 u_gf = 0.0;
191 psi_old_gf = psi_gf;
192 u_old_gf = u_gf;
193
194 // 8. Prepare for glvis output.
195 char vishost[] = "localhost";
196 int visport = 19916;
197 socketstream sol_sock;
198 if (visualization)
199 {
200 sol_sock.open(vishost,visport);
201 sol_sock.precision(8);
202 }
203
204 // 9. Coefficients to be used later.
205 ConstantCoefficient neg_alpha_cf((real_t) -1.0*alpha);
206 ConstantCoefficient zero_cf(0.0);
207 IsomorphismCoefficient Z(sdim, psi_gf);
208 DIsomorphismCoefficient DZ(sdim, psi_gf, eps);
209 ScalarVectorProductCoefficient neg_Z(-1.0, Z);
210 DivergenceGridFunctionCoefficient div_psi_cf(&psi_gf);
211 DivergenceGridFunctionCoefficient div_psi_old_cf(&psi_old_gf);
212 SumCoefficient psi_old_minus_psi(div_psi_old_cf, div_psi_cf, 1.0, -1.0);
213
214 // 10. Assemble constant matrices/vectors to avoid reassembly in the loop.
215 LinearForm b0, b1;
216 b0.MakeRef(&RTfes,rhs.GetBlock(0),0);
217 b1.MakeRef(&L2fes,rhs.GetBlock(1),0);
218
220 b1.AddDomainIntegrator(new DomainLFIntegrator(neg_alpha_cf));
221 b1.AddDomainIntegrator(new DomainLFIntegrator(psi_old_minus_psi));
222
223 BilinearForm a00(&RTfes);
225
226 MixedBilinearForm a10(&RTfes,&L2fes);
228 a10.Assemble();
229 a10.Finalize();
230 SparseMatrix &A10 = a10.SpMat();
231 SparseMatrix *A01 = Transpose(A10);
232
233 // 11. Iterate.
234 int k;
235 int total_iterations = 0;
236 real_t increment_u = 0.1;
237 GridFunction u_tmp(&L2fes);
238 for (k = 0; k < max_it; k++)
239 {
240 u_tmp = u_old_gf;
241
242 mfem::out << "\nOUTER ITERATION " << k+1 << endl;
243
244 int j;
245 for ( j = 0; j < 5; j++)
246 {
247 total_iterations++;
248
249 b0.Assemble();
250 b1.Assemble();
251
252 a00.Assemble(false);
253 a00.Finalize(false);
254 SparseMatrix &A00 = a00.SpMat();
255
256 // Construct Schur-complement preconditioner
257 Vector A00_diag(a00.Height());
258 A00.GetDiag(A00_diag);
259 A00_diag.Reciprocal();
260 SparseMatrix *S = Mult_AtDA(*A01, A00_diag);
261
262 BlockDiagonalPreconditioner prec(offsets);
263 prec.SetDiagonalBlock(0,new DSmoother(A00));
264#ifndef MFEM_USE_SUITESPARSE
265 prec.SetDiagonalBlock(1,new GSSmoother(*S));
266#else
267 prec.SetDiagonalBlock(1,new UMFPackSolver(*S));
268#endif
269 prec.owns_blocks = 1;
270
271 BlockOperator A(offsets);
272 A.SetBlock(0,0,&A00);
273 A.SetBlock(1,0,&A10);
274 A.SetBlock(0,1,A01);
275
276 MINRES(A,prec,rhs,x,0,2000,1e-12);
277 delete S;
278
279 u_tmp -= u_gf;
280 real_t Newton_update_size = u_tmp.ComputeL2Error(zero_cf);
281 u_tmp = u_gf;
282
283 // Damped Newton update
284 psi_gf.Add(newton_scaling, delta_psi_gf);
285 a00.Update();
286
287 if (visualization)
288 {
289 sol_sock << "solution\n" << mesh << u_gf << "window_title 'Discrete solution'"
290 << flush;
291 }
292
293 mfem::out << "Newton_update_size = " << Newton_update_size << endl;
294
295 if (Newton_update_size < increment_u)
296 {
297 break;
298 }
299 }
300
301 u_tmp = u_gf;
302 u_tmp -= u_old_gf;
303 increment_u = u_tmp.ComputeL2Error(zero_cf);
304
305 mfem::out << "Number of Newton iterations = " << j+1 << endl;
306 mfem::out << "Increment (|| uโ‚• - uโ‚•_prvs||) = " << increment_u << endl;
307
308 u_old_gf = u_gf;
309 psi_old_gf = psi_gf;
310
311 if (increment_u < tol || k == max_it-1)
312 {
313 break;
314 }
315
316 alpha *= max(growth_rate, 1_r);
317 neg_alpha_cf.constant = -alpha;
318
319 }
320
321 mfem::out << "\n Outer iterations: " << k+1
322 << "\n Total iterations: " << total_iterations
323 << "\n Total dofs: " << RTfes.GetTrueVSize() + L2fes.GetTrueVSize()
324 << endl;
325
326 delete A01;
327 return 0;
328}
329
330void IsomorphismCoefficient::Eval(Vector &V, ElementTransformation &T,
331 const IntegrationPoint &ip)
332{
333 MFEM_ASSERT(psi != NULL, "grid function is not set");
334
335 Vector psi_vals(vdim);
336 psi->GetVectorValue(T, ip, psi_vals);
337 real_t norm = psi_vals.Norml2();
338 real_t phi = 1.0 / sqrt(1.0 + norm*norm);
339
340 V = psi_vals;
341 V *= phi;
342}
343
344void DIsomorphismCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
345 const IntegrationPoint &ip)
346{
347 MFEM_ASSERT(psi != NULL, "grid function is not set");
348 MFEM_ASSERT(eps >= 0, "eps is negative");
349
350 Vector psi_vals(height);
351 psi->GetVectorValue(T, ip, psi_vals);
352 real_t norm = psi_vals.Norml2();
353 real_t phi = 1.0 / sqrt(1.0 + norm*norm);
354
355 K = 0.0;
356 for (int i = 0; i < height; i++)
357 {
358 K(i,i) = phi + eps;
359 for (int j = 0; j < height; j++)
360 {
361 K(i,j) -= psi_vals(i) * psi_vals(j) * pow(phi, 3);
362 }
363 }
364}
int Size() const
Return the logical size of the array.
Definition array.hpp:147
void PartialSum()
Fill the entries of the array with the cumulative sum of the entries.
Definition array.cpp:103
A "square matrix" operator for the associated FE space and BLFIntegrators The sum of all the BLFInteg...
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
virtual void Update(FiniteElementSpace *nfes=NULL)
Update the FiniteElementSpace and delete all data associated with the old one.
void Finalize(int skip_zeros=1) override
Finalizes the matrix initialization if the AssemblyLevel is AssemblyLevel::LEGACY....
void Assemble(int skip_zeros=1)
Assembles the form i.e. sums over all domain/bdr integrators.
const SparseMatrix & SpMat() const
Returns a const reference to the sparse matrix: .
A class to handle Block diagonal preconditioners in a matrix-free implementation.
void SetDiagonalBlock(int iblock, Operator *op)
Add a square block op in the block-entry (iblock, iblock).
A class to handle Block systems in a matrix-free implementation.
void SetBlock(int iRow, int iCol, Operator *op, real_t c=1.0)
Add a block op in the block-entry (iblock, jblock).
A class to handle Vectors in a block fashion.
Vector & GetBlock(int i)
Get the i-th vector in the block.
A coefficient that is constant across space and time.
Data type for scaled Jacobi-type smoother of sparse matrix.
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
Scalar coefficient defined as the Divergence of a vector GridFunction.
Class for domain integration .
Definition lininteg.hpp:106
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
virtual int GetTrueVSize() const
Return the number of vector true (conforming) dofs.
Definition fespace.hpp:851
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:848
Data type for Gauss-Seidel smoother of sparse matrix.
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:31
virtual void MakeRef(FiniteElementSpace *f, real_t *v)
Make the GridFunction reference external data on a new FiniteElementSpace.
Definition gridfunc.cpp:233
virtual real_t ComputeL2Error(Coefficient *exsol[], const IntegrationRule *irs[]=NULL, const Array< int > *elems=NULL) const
Returns ||exsol - u_h||_L2 for scalar or vector H1 or L2 elements.
virtual void GetVectorValue(int i, const IntegrationPoint &ip, Vector &val) const
Definition gridfunc.cpp:473
Class for integration point with weight.
Definition intrules.hpp:35
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
Vector with associated FE space and LinearFormIntegrators.
void AddDomainIntegrator(LinearFormIntegrator *lfi)
Adds new Domain Integrator. Assumes ownership of lfi.
virtual void MakeRef(FiniteElementSpace *f, Vector &v, int v_offset)
Make the LinearForm reference external data on a new FiniteElementSpace.
void Assemble()
Assembles the linear form i.e. sums over all domain/bdr integrators.
Base class for Matrix Coefficients that optionally depend on time and space.
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
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1219
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
void Assemble(int skip_zeros=1)
void Finalize(int skip_zeros=1) override
Finalizes the matrix initialization if the AssemblyLevel is AssemblyLevel::LEGACY.
const SparseMatrix & SpMat() const
Returns a const reference to the sparse matrix: .
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds a domain integrator. Assumes ownership of bfi.
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:66
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.
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:403
Vector coefficient defined as a product of scalar and vector coefficients.
Data type sparse matrix.
Definition sparsemat.hpp:51
void GetDiag(Vector &d) const
Returns the Diagonal of A.
Scalar coefficient defined as the linear combination of two scalar coefficients or a scalar and a sca...
Direct sparse solver using UMFPACK.
Definition solvers.hpp:1121
Base class for vector Coefficients that optionally depend on time and space.
virtual void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip)=0
Evaluate the vector coefficient in the element described by T at the point ip, storing the result in ...
for VectorFiniteElements (Nedelec, Raviart-Thomas)
Definition lininteg.hpp:344
Vector data type.
Definition vector.hpp:82
void Reciprocal()
(*this)(i) = 1.0 / (*this)(i)
Definition vector.cpp:383
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:322
int open(const char hostname[], int port)
Open the socket stream on 'port' at 'hostname'.
const real_t alpha
Definition ex15.cpp:369
int dim
Definition ex24.cpp:53
int main()
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition table.cpp:486
void MINRES(const Operator &A, const Vector &b, Vector &x, int print_it, int max_it, real_t rtol, real_t atol)
MINRES method without preconditioner. (tolerances are squared)
Definition solvers.cpp:1870
SparseMatrix * Mult_AtDA(const SparseMatrix &A, const Vector &D, SparseMatrix *OAtDA)
Matrix multiplication A^t D A. All matrices must be finalized.
float real_t
Definition config.hpp:43
const char vishost[]