sandbox/ghigo/src/test-navier-stokes/hydrostatic2.c
Hydrostatic balance with refined embedded boundaries
This test case is related to the test case flow in a complex porous medium. We check that for a “closed pore” medium, hydrostatic balance can be recovered. This is not trivial in particular when the spatial resolution is variable, since pressure values need to be interpolated (at least) to second-order close to embedded boundaries. This behaviour depends on the restriction and prolongation operators used close to embedded boundaries.
#include "grid/quadtree.h"
#include "../myembed.h"
#include "../mycentered.h"
#include "view.h"
Geometry
We use a similar porous medium as in porous.c but with enough disks so that the pores are entirely closed.
void p_shape (scalar c, face vector f)
{
int ns = 200;
double xc[ns], yc[ns], R[ns];
(0);
srand for (int i = 0; i < ns; i++)
[i] = 0.5*noise(), yc[i] = 0.5*noise(), R[i] = 0.02 + 0.04*fabs(noise());
xc
vertex scalar phi[];
foreach_vertex() {
[] = HUGE;
phifor (double xp = -L0; xp <= L0; xp += L0)
for (double yp = -L0; yp <= L0; yp += L0)
for (int i = 0; i < ns; i++)
[] = intersection (phi[], (sq (x + xp - xc[i]) +
phi(y + yp - yc[i]) -
sq (R[i])));
sq }
boundary ({phi});
fractions (phi, c, f);
fractions_cleanup (c, f,
= 1.e-14, cmin = 1.e-14);
smin }
Setup
We define the mesh adaptation parameters.
#define lmin (6) // Min mesh refinement level
#define lmax (8) // Max mesh refinement level
int main()
{
The domain is 1\times 1.
= 1.;
L0 (L0);
size (-L0/2., -L0/2.); origin
We set the maximum timestep.
= 1.e-2; DT
We set the tolerance of the Poisson solver.
= 1.e-6; TOLERANCE
We initialize the grid.
= 1 << (lmin);
N (N);
init_grid
run();
}
Boundary conditions
Properties
Initial conditions
event init (i = 0)
{
We define the gravity acceleration vector.
const face vector g[] = {1., 2.};
= g; a
We use “third-order” face flux interpolation.
#if ORDER2
for (scalar s in {u, p, pf})
.third = false;
s#else
for (scalar s in {u, p, pf})
.third = true;
s#endif // ORDER2
#if TREE
When using TREE and in the presence of embedded boundaries, we should also define the gradient of u at the cell center of cut-cells.
#endif // TREE
We initialize the embedded boundary.
#if TREE
When using TREE, we refine the mesh around the embedded boundary.
astats ss;
int ic = 0;
do {
++;
icp_shape (cs, fs);
= adapt_wavelet ({cs}, (double[]) {1.e-2},
ss = (lmax), minlevel = (1));
maxlevel } while ((ss.nf || ss.nc) && ic < 100);
#endif // TREE
p_shape (cs, fs);
We also define the volume fraction at the previous timestep csm1=cs.
= cs; csm1
We define the no-slip boundary conditions for the velocity.
.n[embed] = dirichlet (0);
u.t[embed] = dirichlet (0);
u[embed] = neumann (0);
p
.n[embed] = dirichlet (0);
uf.t[embed] = dirichlet (0);
uf[embed] = neumann (0); pf
We finally give an initial guess for the pressure.
foreach() {
[] = cs[] ? (g.x[]*x + g.y[]*y) : nodata; // exact pressure
p[] = p[];
pf}
boundary ((scalar *) {p, pf});
}
Embedded boundaries
Adaptive mesh refinement
Outputs
We only solve the Euler equations for a few time iterations.
event logfile (i++; i <= 100)
{
We check the convergence rate and the norms of the velocity field (which should be negligible).
assert (normf(u.x).max < 1.e-14 &&
normf(u.y).max < 1.e-14);
fprintf (stderr, "%d %g %g %d %d %d %d %d %d %g %g %g %g %g %g\n",
, t, dt,
i.i, mgpf.nrelax, mgpf.minlevel,
mgpf.i, mgp.nrelax, mgp.minlevel,
mgp.resb, mgpf.resa,
mgpf.resb, mgp.resa,
mgpnormf(u.x).max, normf(u.y).max
);
}
Snapshots
event snapshot (t = end)
{
view (fov = 20,
= 0., ty = 0.,
tx = {1,1,1},
bg = 800, height = 800);
width
draw_vof ("cs", filled = -1, lw = 5, fc = {1,1,1});
squares ("p", spread = -1);
save ("p.png");
}
Results
The pressure is hydrostatic, in each of the pores.
