248 lines
6.9 KiB
C++
248 lines
6.9 KiB
C++
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
//
|
|
// Navier MMS example
|
|
//
|
|
// A manufactured solution is defined as
|
|
//
|
|
// u = [pi * sin(t) * sin(pi * x)^2 * sin(2 * pi * y),
|
|
// -(pi * sin(t) * sin(2 * pi * x)) * sin(pi * y)^2].
|
|
//
|
|
// p = cos(pi * x) * sin(t) * sin(pi * y)
|
|
//
|
|
// The solution is used to compute the symbolic forcing term (right hand side),
|
|
// of the equation. Then the numerical solution is computed and compared to the
|
|
// exact manufactured solution to determine the error.
|
|
|
|
#include "navier_solver.hpp"
|
|
#include <fstream>
|
|
|
|
using namespace mfem;
|
|
using namespace navier;
|
|
|
|
struct s_NavierContext
|
|
{
|
|
int ser_ref_levels = 1;
|
|
int order = 5;
|
|
double kinvis = 1.0;
|
|
double t_final = 10 * 0.25e-4;
|
|
double dt = 0.25e-4;
|
|
bool pa = true;
|
|
bool ni = false;
|
|
bool visualization = false;
|
|
bool checkres = false;
|
|
} ctx;
|
|
|
|
void vel(const Vector &x, double t, Vector &u)
|
|
{
|
|
double xi = x(0);
|
|
double yi = x(1);
|
|
|
|
u(0) = M_PI * sin(t) * pow(sin(M_PI * xi), 2.0) * sin(2.0 * M_PI * yi);
|
|
u(1) = -(M_PI * sin(t) * sin(2.0 * M_PI * xi) * pow(sin(M_PI * yi), 2.0));
|
|
}
|
|
|
|
double p(const Vector &x, double t)
|
|
{
|
|
double xi = x(0);
|
|
double yi = x(1);
|
|
|
|
return cos(M_PI * xi) * sin(t) * sin(M_PI * yi);
|
|
}
|
|
|
|
void accel(const Vector &x, double t, Vector &u)
|
|
{
|
|
double xi = x(0);
|
|
double yi = x(1);
|
|
|
|
u(0) = M_PI * sin(t) * sin(M_PI * xi) * sin(M_PI * yi)
|
|
* (-1.0
|
|
+ 2.0 * pow(M_PI, 2.0) * sin(t) * sin(M_PI * xi)
|
|
* sin(2.0 * M_PI * xi) * sin(M_PI * yi))
|
|
+ M_PI
|
|
* (2.0 * ctx.kinvis * pow(M_PI, 2.0)
|
|
* (1.0 - 2.0 * cos(2.0 * M_PI * xi)) * sin(t)
|
|
+ cos(t) * pow(sin(M_PI * xi), 2.0))
|
|
* sin(2.0 * M_PI * yi);
|
|
|
|
u(1) = M_PI * cos(M_PI * yi) * sin(t)
|
|
* (cos(M_PI * xi)
|
|
+ 2.0 * ctx.kinvis * pow(M_PI, 2.0) * cos(M_PI * yi)
|
|
* sin(2.0 * M_PI * xi))
|
|
- M_PI * (cos(t) + 6.0 * ctx.kinvis * pow(M_PI, 2.0) * sin(t))
|
|
* sin(2.0 * M_PI * xi) * pow(sin(M_PI * yi), 2.0)
|
|
+ 4.0 * pow(M_PI, 3.0) * cos(M_PI * yi) * pow(sin(t), 2.0)
|
|
* pow(sin(M_PI * xi), 2.0) * pow(sin(M_PI * yi), 3.0);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
MPI_Session mpi(argc, argv);
|
|
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&ctx.ser_ref_levels,
|
|
"-rs",
|
|
"--refine-serial",
|
|
"Number of times to refine the mesh uniformly in serial.");
|
|
args.AddOption(&ctx.order,
|
|
"-o",
|
|
"--order",
|
|
"Order (degree) of the finite elements.");
|
|
args.AddOption(&ctx.dt, "-dt", "--time-step", "Time step.");
|
|
args.AddOption(&ctx.t_final, "-tf", "--final-time", "Final time.");
|
|
args.AddOption(&ctx.pa,
|
|
"-pa",
|
|
"--enable-pa",
|
|
"-no-pa",
|
|
"--disable-pa",
|
|
"Enable partial assembly.");
|
|
args.AddOption(&ctx.ni,
|
|
"-ni",
|
|
"--enable-ni",
|
|
"-no-ni",
|
|
"--disable-ni",
|
|
"Enable numerical integration rules.");
|
|
args.AddOption(&ctx.visualization,
|
|
"-vis",
|
|
"--visualization",
|
|
"-no-vis",
|
|
"--no-visualization",
|
|
"Enable or disable GLVis visualization.");
|
|
args.AddOption(
|
|
&ctx.checkres,
|
|
"-cr",
|
|
"--checkresult",
|
|
"-no-cr",
|
|
"--no-checkresult",
|
|
"Enable or disable checking of the result. Returns -1 on failure.");
|
|
args.Parse();
|
|
if (!args.Good())
|
|
{
|
|
if (mpi.Root())
|
|
{
|
|
args.PrintUsage(mfem::out);
|
|
}
|
|
return 1;
|
|
}
|
|
if (mpi.Root())
|
|
{
|
|
args.PrintOptions(mfem::out);
|
|
}
|
|
|
|
Mesh *mesh = new Mesh("../../data/inline-quad.mesh");
|
|
mesh->EnsureNodes();
|
|
GridFunction *nodes = mesh->GetNodes();
|
|
*nodes *= 2.0;
|
|
*nodes -= 1.0;
|
|
|
|
for (int i = 0; i < ctx.ser_ref_levels; ++i)
|
|
{
|
|
mesh->UniformRefinement();
|
|
}
|
|
|
|
if (mpi.Root())
|
|
{
|
|
std::cout << "Number of elements: " << mesh->GetNE() << std::endl;
|
|
}
|
|
|
|
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
|
delete mesh;
|
|
|
|
// Create the flow solver.
|
|
NavierSolver naviersolver(pmesh, ctx.order, ctx.kinvis);
|
|
naviersolver.EnablePA(ctx.pa);
|
|
naviersolver.EnableNI(ctx.ni);
|
|
|
|
// Set the initial condition.
|
|
ParGridFunction *u_ic = naviersolver.GetCurrentVelocity();
|
|
VectorFunctionCoefficient u_excoeff(pmesh->Dimension(), vel);
|
|
u_ic->ProjectCoefficient(u_excoeff);
|
|
|
|
FunctionCoefficient p_excoeff(p);
|
|
|
|
// Add Dirichlet boundary conditions to velocity space restricted to
|
|
// selected attributes on the mesh.
|
|
Array<int> attr(pmesh->bdr_attributes.Max());
|
|
attr = 1;
|
|
naviersolver.AddVelDirichletBC(vel, attr);
|
|
|
|
Array<int> domain_attr(pmesh->attributes.Max());
|
|
domain_attr = 1;
|
|
naviersolver.AddAccelTerm(accel, domain_attr);
|
|
|
|
double t = 0.0;
|
|
double dt = ctx.dt;
|
|
double t_final = ctx.t_final;
|
|
bool last_step = false;
|
|
|
|
naviersolver.Setup(dt);
|
|
|
|
double err_u = 0.0;
|
|
double err_p = 0.0;
|
|
ParGridFunction *u_gf = nullptr;
|
|
ParGridFunction *p_gf = nullptr;
|
|
u_gf = naviersolver.GetCurrentVelocity();
|
|
p_gf = naviersolver.GetCurrentPressure();
|
|
|
|
for (int step = 0; !last_step; ++step)
|
|
{
|
|
if (t + dt >= t_final - dt / 2)
|
|
{
|
|
last_step = true;
|
|
}
|
|
|
|
naviersolver.Step(t, dt, step);
|
|
|
|
// Compare against exact solution of velocity and pressure.
|
|
u_excoeff.SetTime(t);
|
|
p_excoeff.SetTime(t);
|
|
err_u = u_gf->ComputeL2Error(u_excoeff);
|
|
err_p = p_gf->ComputeL2Error(p_excoeff);
|
|
|
|
if (mpi.Root())
|
|
{
|
|
printf("%11s %11s %11s %11s\n", "Time", "dt", "err_u", "err_p");
|
|
printf("%.5E %.5E %.5E %.5E err\n", t, dt, err_u, err_p);
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
if (ctx.visualization)
|
|
{
|
|
char vishost[] = "localhost";
|
|
int visport = 19916;
|
|
socketstream sol_sock(vishost, visport);
|
|
sol_sock << "parallel " << mpi.WorldSize() << " " << mpi.WorldRank()
|
|
<< "\n";
|
|
sol_sock << "solution\n" << *pmesh << *u_ic << std::flush;
|
|
}
|
|
|
|
naviersolver.PrintTimingData();
|
|
|
|
// Test if the result for the test run is as expected.
|
|
if (ctx.checkres)
|
|
{
|
|
double tol = 1e-3;
|
|
if (err_u > tol || err_p > tol)
|
|
{
|
|
if (mpi.Root())
|
|
{
|
|
mfem::out << "Result has a larger error than expected."
|
|
<< std::endl;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
delete pmesh;
|
|
|
|
return 0;
|
|
} |