Stokes flow through a complex porous medium

The medium is periodic and described using embedded boundaries.

This tests mainly the robustness of the representation of embedded boundaries and the convergence of the viscous and Poisson solvers.

#include "embed.h"
#include "navier-stokes/centered.h"
#include "view.h"

We will vary the maximum level of refinement, starting from 7.

int maxlevel = 7;

The porous medium is defined by the union of a random collection of disks. The number of disks can be varied to vary the porosity.

void porous (scalar cs, face vector fs)
  int ns = 160; // 80, 160, 165, 200
  double xc[ns], yc[ns], R[ns];
  srand (0);
  for (int i = 0; i < ns; i++)
    xc[i] = 0.5*noise(), yc[i] = 0.5*noise(), R[i] = 0.02 + 0.04*fabs(noise());

Once we have defined the random centers and radii, we can compute the levelset function ϕ representing the embedded boundary.

  vertex scalar φ[];
  foreach_vertex() {
    φ[] = HUGE;

Since the medium is periodic, we need to take into account all the disk images using periodic symmetries.

    for (double xp = -L0; xp <= L0; xp += L0)
      for (double yp = -L0; yp <= L0; yp += L0)
	for (int i = 0; i < ns; i++)
	  φ[] = intersection (φ[], (sq(x + xp - xc[i]) +
					sq(y + yp - yc[i]) - sq(R[i])));
  fractions (φ, cs, fs);

The domain is the periodic unit square centered on the origin.

int main()
  origin (-0.5, -0.5);
  periodic (right);
  periodic (top);

We turn off the advection term. The choice of the maximum timestep and of the tolerance on the Poisson and viscous solves is not trivial. This was adjusted by trial and error to minimize (possibly) splitting errors and optimize convergence speed.

  stokes = true;
  DT = 2e-5;
  NITERMIN = 10;
  N = 1 << maxlevel;


scalar un[];

event init (t = 0) {

We define the porous embedded geometry.

  porous (cs, fs);

The gravity vector is aligned with the channel and viscosity is unity.

  const face vector g[] = {1.,0.};
  a = g;
  μ = fm;

The boundary condition is zero velocity on the embedded boundary.

  u.n[embed] = dirichlet_embed(0);
  u.t[embed] = dirichlet_embed(0);

We initialize the reference velocity.

    un[] = u.y[];

We check for a stationary solution.

event logfile (i++; i <= 500)
  double avg = normf(u.x).avg, du = change (u.x, un)/(avg + SEPS);
  fprintf (ferr, "%d %d %d %d %d %d %d %d %.3g %.3g %.3g %.3g %.3g\n",
	   maxlevel, i,
	   mgp.i, mgp.nrelax, mgp.minlevel,
	   mgu.i, mgu.nrelax, mgu.minlevel,
	   du, mgp.resa*dt, mgu.resa, statsf(u.x).sum, normf(p).max);

If the relative change of the velocity is small enough we stop this simulation.

  if (i > 1 && (avg < 1e-9 || du < 1e-2)) {

We are interested in the permeability k of the medium, which is defined by U=kμp=kμρg with U the average fluid velocity.

    stats s = statsf (u.x);
    printf ("%d %g\n", maxlevel, s.sum/s.volume);

We output fields and dump the simulation.

    scalar ν[];
      ν[] = sqrt (sq(u.x[]) + sq(u.y[]));
    boundary ({ν});

    view (fov = 19.3677);
    draw_vof ("cs", "fs", filled = -1, fc = {1,1,1});
    squares ("nu", linear = true, spread = 8);
    char name[80];
    sprintf (name, "nu-%d.png", maxlevel);
    save (name);

    draw_vof ("cs", "fs", filled = -1, fc = {1,1,1});
    squares ("p", linear = false, spread = -1);
    sprintf (name, "p-%d.png", maxlevel);
    save (name);

    draw_vof ("cs", "fs", filled = -1, fc = {1,1,1});
    squares ("level");
    sprintf (name, "level-%d.png", maxlevel);
    save (name);

    sprintf (name, "dump-%d", maxlevel);
    dump (name);

We stop at level 10.

    if (maxlevel == 10)
      return 1; /* stop */

We refine the converged solution to get the initial guess for the finer level. We also reset the embedded fractions to avoid interpolation errors on the geometry.

#if 0
    refine (level < maxlevel && cs[] > 0. && cs[] < 1.);
    adapt_wavelet ({cs,u}, (double[]){1e-2,2e-6,2e-6}, maxlevel);
    porous (cs, fs);
Norm of the velocity field.

Norm of the velocity field.

Pressure field.

Pressure field.

Adapted mesh, 10 levels of refinement.

Adapted mesh, 10 levels of refinement.

Permeability as a function of resolution

Permeability as a function of resolution

Convergence history

Convergence history

See also