MFEM v4.8.0
Finite element discretization library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
navier_kovasznay_vs.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// Navier Kovasznay example with variable time step
13//
14// Solve for the steady Kovasznay flow at Re = 40 defined by
15//
16// u = [1 - exp(L * x) * cos(2 * pi * y),
17// L / (2 * pi) * exp(L * x) * sin(2 * pi * y)],
18//
19// p = 1/2 * (1 - exp(2 * L * x)),
20//
21// with L = Re/2 - sqrt(Re^2/4 + 4 * pi^2).
22//
23// The problem domain is set up like this
24//
25// +-------------+
26// | |
27// | |
28// | |
29// | |
30// Inflow -> | | -> Outflow
31// | |
32// | |
33// | |
34// | |
35// | |
36// +-------------+
37//
38// and Dirichlet boundary conditions are applied for the velocity on every
39// boundary. The problem, although steady state, is time integrated up to the
40// final time and the solution is compared with the known exact solution.
41//
42// Additionally, this example shows the usage of variable time steps with
43// Navier. A basic sample algorithm for determining the next time step is
44// provided based on a CFL restriction on the velocity.
45
46#include "navier_solver.hpp"
47#include <fstream>
48
49using namespace mfem;
50using namespace navier;
51
52struct s_NavierContext
53{
54 int ser_ref_levels = 1;
55 int order = 6;
56 real_t kinvis = 1.0 / 40.0;
57 real_t t_final = 10 * 0.001;
58 real_t dt = 0.001;
59 real_t reference_pressure = 0.0;
60 real_t reynolds = 1.0 / kinvis;
61 real_t lam = 0.5 * reynolds
62 - sqrt(0.25 * reynolds * reynolds + 4.0 * M_PI * M_PI);
63 bool pa = true;
64 bool ni = false;
65 bool visualization = false;
66 bool checkres = false;
68
69void vel_kovasznay(const Vector &x, real_t t, Vector &u)
70{
71 real_t xi = x(0);
72 real_t yi = x(1);
73
74 u(0) = 1.0 - exp(ctx.lam * xi) * cos(2.0 * M_PI * yi);
75 u(1) = ctx.lam / (2.0 * M_PI) * exp(ctx.lam * xi) * sin(2.0 * M_PI * yi);
76}
77
79{
80 real_t xi = x(0);
81
82 return 0.5 * (1.0 - exp(2.0 * ctx.lam * xi)) + ctx.reference_pressure;
83}
84
85int main(int argc, char *argv[])
86{
87 Mpi::Init(argc, argv);
89 int visport = 19916;
90
91 OptionsParser args(argc, argv);
92 args.AddOption(&ctx.ser_ref_levels,
93 "-rs",
94 "--refine-serial",
95 "Number of times to refine the mesh uniformly in serial.");
96 args.AddOption(&ctx.order,
97 "-o",
98 "--order",
99 "Order (degree) of the finite elements.");
100 args.AddOption(&ctx.dt, "-dt", "--time-step", "Time step.");
101 args.AddOption(&ctx.t_final, "-tf", "--final-time", "Final time.");
102 args.AddOption(&ctx.pa,
103 "-pa",
104 "--enable-pa",
105 "-no-pa",
106 "--disable-pa",
107 "Enable partial assembly.");
108 args.AddOption(&ctx.ni,
109 "-ni",
110 "--enable-ni",
111 "-no-ni",
112 "--disable-ni",
113 "Enable numerical integration rules.");
114 args.AddOption(&ctx.visualization,
115 "-vis",
116 "--visualization",
117 "-no-vis",
118 "--no-visualization",
119 "Enable or disable GLVis visualization.");
120 args.AddOption(
121 &ctx.checkres,
122 "-cr",
123 "--checkresult",
124 "-no-cr",
125 "--no-checkresult",
126 "Enable or disable checking of the result. Returns -1 on failure.");
127 args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
128 args.Parse();
129 if (!args.Good())
130 {
131 if (Mpi::Root())
132 {
133 args.PrintUsage(mfem::out);
134 }
135 return 1;
136 }
137 if (Mpi::Root())
138 {
140 }
141
142 Mesh mesh = Mesh::MakeCartesian2D(2, 4, Element::QUADRILATERAL, false, 1.5,
143 2.0);
144
145 mesh.EnsureNodes();
146 GridFunction *nodes = mesh.GetNodes();
147 *nodes -= 0.5;
148
149 for (int i = 0; i < ctx.ser_ref_levels; ++i)
150 {
151 mesh.UniformRefinement();
152 }
153
154 if (Mpi::Root())
155 {
156 std::cout << "Number of elements: " << mesh.GetNE() << std::endl;
157 }
158
159 auto *pmesh = new ParMesh(MPI_COMM_WORLD, mesh);
160 mesh.Clear();
161
162 // Create the flow solver.
163 NavierSolver flowsolver(pmesh, ctx.order, ctx.kinvis);
164 flowsolver.EnablePA(ctx.pa);
165 flowsolver.EnableNI(ctx.ni);
166
167 // Set the initial condition.
168 ParGridFunction *u_ic = flowsolver.GetCurrentVelocity();
169 VectorFunctionCoefficient u_excoeff(pmesh->Dimension(), vel_kovasznay);
170 u_ic->ProjectCoefficient(u_excoeff);
171
173
174 // Add Dirichlet boundary conditions to velocity space restricted to
175 // selected attributes on the mesh.
176 Array<int> attr(pmesh->bdr_attributes.Max());
177 attr = 1;
178 flowsolver.AddVelDirichletBC(vel_kovasznay, attr);
179
180 real_t t = 0.0;
181 real_t dt = ctx.dt;
182 real_t t_final = ctx.t_final;
183 bool last_step = false;
184
185 flowsolver.Setup(dt);
186
187 real_t err_u = 0.0;
188 real_t err_p = 0.0;
189 ParGridFunction *u_next_gf = nullptr;
190 ParGridFunction *u_gf = nullptr;
191 ParGridFunction *p_gf = nullptr;
192
193 ParGridFunction p_ex_gf(flowsolver.GetCurrentPressure()->ParFESpace());
194 GridFunctionCoefficient p_ex_gf_coeff(&p_ex_gf);
195
196 real_t cfl_max = 0.8;
197 real_t cfl_tol = 1e-4;
198
199 for (int step = 0; !last_step; ++step)
200 {
201 if (t + dt >= t_final - dt / 2)
202 {
203 last_step = true;
204 }
205
206 // Take a provisional step
207 flowsolver.Step(t, dt, step, true);
208
209 // Retrieve the computed provisional velocity
210 u_next_gf = flowsolver.GetProvisionalVelocity();
211
212 // Compute the CFL based on the provisional velocity
213 real_t cfl = flowsolver.ComputeCFL(*u_next_gf, dt);
214
215 real_t error_est = cfl / (cfl_max + cfl_tol);
216 if (error_est >= 1.0)
217 {
218 // Reject the time step
219 if (Mpi::Root())
220 {
221 std::cout
222 << "Step reached maximum CFL, retrying with smaller step size..."
223 << std::endl;
224 }
225 dt *= 0.5;
226 step -= 1;
227 }
228 else
229 {
230 // Accept the time step
231 t += dt;
232
233 // Predict new step size
234 real_t fac_safety = 2.0;
235 real_t eta = pow(1.0 / (fac_safety * error_est), 1.0 / (1.0 + 3.0));
236 real_t fac_min = 0.1;
237 real_t fac_max = 1.4;
238 dt = dt * std::min(fac_max, std::max(fac_min, eta));
239
240 // Queue new time step in the history array
241 flowsolver.UpdateTimestepHistory(dt);
242 }
243
244 u_gf = flowsolver.GetCurrentVelocity();
245 p_gf = flowsolver.GetCurrentPressure();
246
247 u_excoeff.SetTime(t);
248 p_excoeff.SetTime(t);
249
250 // Remove mean value from exact pressure solution.
251 p_ex_gf.ProjectCoefficient(p_excoeff);
252 flowsolver.MeanZero(p_ex_gf);
253
254 err_u = u_gf->ComputeL2Error(u_excoeff);
255 err_p = p_gf->ComputeL2Error(p_ex_gf_coeff);
256
257 if (Mpi::Root())
258 {
259 printf("%5s %8s %8s %8s %11s %11s\n",
260 "Order",
261 "CFL",
262 "Time",
263 "dt",
264 "err_u",
265 "err_p");
266 printf("%5.2d %8.2E %.2E %.2E %.5E %.5E err\n",
267 ctx.order,
268 cfl,
269 t,
270 dt,
271 err_u,
272 err_p);
273 fflush(stdout);
274 }
275 }
276
277 if (ctx.visualization)
278 {
279 char vishost[] = "localhost";
280 socketstream sol_sock(vishost, visport);
281 sol_sock.precision(8);
282 sol_sock << "parallel " << Mpi::WorldSize() << " "
283 << Mpi::WorldRank() << "\n";
284 sol_sock << "solution\n" << *pmesh << *u_ic << std::flush;
285 }
286
287 flowsolver.PrintTimingData();
288
289 // Test if the result for the test run is as expected.
290 if (ctx.checkres)
291 {
292 real_t tol_u = 1e-6;
293 real_t tol_p = 1e-5;
294 if (err_u > tol_u || err_p > tol_p)
295 {
296 if (Mpi::Root())
297 {
298 mfem::out << "Result has a larger error than expected."
299 << std::endl;
300 }
301 return -1;
302 }
303 }
304
305 delete pmesh;
306
307 return 0;
308}
virtual void SetTime(real_t t)
Set the time for time dependent coefficients.
A general function coefficient.
Coefficient defined by a GridFunction. This coefficient is mesh dependent.
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:31
static void Init()
Initialize hypre by calling HYPRE_Init() and set default options. After calling Hypre::Init(),...
Definition hypre.cpp:33
Mesh data type.
Definition mesh.hpp:64
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:6432
void Clear()
Clear the contents of the Mesh.
Definition mesh.hpp:761
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1282
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:9294
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:4471
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11295
static bool Root()
Return true if the rank in MPI_COMM_WORLD is zero.
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).
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 grid function.
Definition pgridfunc.hpp:50
real_t ComputeL2Error(Coefficient *exsol[], const IntegrationRule *irs[]=NULL, const Array< int > *elems=NULL) const override
Returns ||u_ex - u_h||_L2 in parallel for H1 or L2 elements.
ParFiniteElementSpace * ParFESpace() const
void ProjectCoefficient(Coefficient &coeff) override
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
Class for parallel meshes.
Definition pmesh.hpp:34
virtual void SetTime(real_t t)
Set the time for time dependent coefficients.
A general vector function coefficient.
Vector data type.
Definition vector.hpp:82
Transient incompressible Navier Stokes solver in a split scheme formulation.
ParGridFunction * GetProvisionalVelocity()
Return a pointer to the provisional velocity ParGridFunction.
void Setup(real_t dt)
Initialize forms, solvers and preconditioners.
real_t ComputeCFL(ParGridFunction &u, real_t dt)
Compute CFL.
void AddVelDirichletBC(VectorCoefficient *coeff, Array< int > &attr)
Add a Dirichlet boundary condition to the velocity field.
ParGridFunction * GetCurrentPressure()
Return a pointer to the current pressure ParGridFunction.
void PrintTimingData()
Print timing summary of the solving routine.
void EnablePA(bool pa)
Enable partial assembly for every operator.
void MeanZero(ParGridFunction &v)
Remove the mean from a ParGridFunction.
ParGridFunction * GetCurrentVelocity()
Return a pointer to the current velocity ParGridFunction.
void UpdateTimestepHistory(real_t dt)
Rotate entries in the time step and solution history arrays.
void Step(real_t &time, real_t dt, int cur_step, bool provisional=false)
Compute solution at the next time step t+dt.
int main()
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
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
float real_t
Definition config.hpp:43
const char vishost[]
real_t pres_kovasznay(const Vector &x, real_t t)
struct s_NavierContext ctx
void vel_kovasznay(const Vector &x, real_t t, Vector &u)
std::array< int, NCMesh::MaxFaceNodes > nodes