sandbox/bugs/porous3D_mu.c
I read in file “examples/porous3D.c” that the viscosity is equal to 1, so I tried changing “mu”, what caused change in results for which I do not know an explanation. In the attached file you can see in which places I put “mu” and how I changed its value.
At the top of the file, I added “const face vector muc[] = {1.,1.,1.};”.
Option 1: Because it is written that the viscosity is equal to 1, I changed it directly (inside the “event init”) to be equal to “muc” (i.e. “mu = muc;”).
Option 2: Because I saw that “mu” was defined inside “int main” in other examples (e.g. “karman.c”, “sphere.c”, “isotropic.c”), I tried moving “mu = fm;” from “event init” to “int main”.
Option 3: I also tried using “mu = muc;” inside “int main”.
For every option tried out, I saw strange values in file “log”, while pictures were not generated.
Do I not understand something about Basilisk or did I encounter a bug? I am learning Basilisk (going through examples), and, before I clarify this, I think I cannot be certain in any result I get with it.
const face vector muc[] = {1.,1.,1.}; //////////////////////////////////////////////////////////////////////////////////////////
Stokes flow through a complex 3D porous medium
This is the 3D equivalent of the 2D porous medium test case.
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 "grid/octree.h"
#include "embed.h"
#include "navier-stokes/centered.h"
#include "view.h"
#include "navier-stokes/perfs.h"
We will vary the maximum level of refinement, starting from 5.
int maxlevel = 5;
The porous medium is defined by the union of a random collection of
spheres. The number of spheres ns
can be varied to vary the
porosity.
void porous (scalar cs, face vector fs)
{
int ns = 700;
[ns];
coord pcdouble R[ns];
(0);
srand for (int i = 0; i < ns; i++) {
foreach_dimension()
[i].x = 0.5*noise();
pc[i] = 0.04 + 0.08*fabs(noise());
R}
Once we have defined the random centers and radii, we can compute the levelset function \phi representing the embedded boundary.
vertex scalar phi[];
foreach_vertex() {
[] = HUGE; phi
Since the medium is periodic, we need to take into account all the
disk images using periodic symmetries. Note that this means that each
function evaluation requires 27 times ns
evaluations of the
function for a sphere. This is expensive but could be improved a lot
using a more clever algorithm.
for (double xp = -L0; xp <= L0; xp += L0)
for (double yp = -L0; yp <= L0; yp += L0)
for (double zp = -L0; zp <= L0; zp += L0)
for (int i = 0; i < ns; i++)
[] = intersection (phi[], (sq(x + xp - pc[i].x) +
phi(y + yp - pc[i].y) +
sq(z + zp - pc[i].z) -
sq(R[i])));
sq}
boundary ({phi});
fractions (phi, cs, fs);
This is necessary to remove degenerate fractions which could cause convergence problems.
fractions_cleanup (cs, fs);
}
The domain is the periodic unit cube centered on the origin.
int main()
{
(-0.5, -0.5, -0.5);
origin foreach_dimension()
periodic (right);
= muc; // OPTION 3 ///////////////////////////////////////////////////////////////////////////////////////////////
mu // mu = fm; // OPTION 2 ///////////////////////////////////////////////////////////////////////////////////////////////
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.
= true;
stokes = 2e-5;
DT = HUGE;
TOLERANCE = 2;
NITERMIN = 1 << maxlevel;
N
run();
}
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.,0.};
= g;
a // mu = muc; // OPTION 1 ///////////////////////////////////////////////////////////////////////////////////////////////
// mu = fm; // DEFAULT ///////////////////////////////////////////////////////////////////////////////////////////////
The boundary condition is zero velocity on the embedded boundary.
We initialize the reference velocity.
foreach()
[] = u.x[];
un}
#if 0 // used for debugging only
maxpos (scalar s)
coord {
= {0,0,0};
coord pmax double numax = 0.;
foreach (serial)
if (fabs(s[]) > numax) {
= fabs(s[]);
numax = (coord){x,y,z};
pmax }
return pmax;
}
event dumps (i += 10) {
scalar nu[], du[], crappy[];
foreach() {
[] = norm(u);
nu[] = u.x[] - un[];
dudouble val;
[] = (embed_flux (point, u.x, mu, &val) != 0.);
crappy}
#if !_MPI
= maxpos (nu);
coord numax fprintf (stderr, "numax: %g %g %g\n", numax.x, numax.y, numax.z);
= maxpos (p);
numax #if 0
= locate (numax.x, numax.y, numax.z);
Point point fprintf (stderr, "pmax: %g %g %g %g %g\n", numax.x, numax.y, numax.z,
[], cs[]);
p#endif
= maxpos (du);
numax {
= locate (numax.x, numax.y, numax.z);
Point point fprintf (stderr, "dumax: %g %g %g\n", numax.x, numax.y, numax.z);
FILE * fp = fopen ("dumax", "w");
foreach_neighbor(1) {
fprintf (fp, "fs %g %g %g %g\n", x - Delta/2., y, z, fs.x[]);
fprintf (fp, "fs %g %g %g %g\n", x, y - Delta/2., z, fs.y[]);
fprintf (fp, "fs %g %g %g %g\n", x, y, z - Delta/2., fs.z[]);
fprintf (fp, "fs %g %g %g %g\n", x + Delta/2., y, z, fs.x[1]);
fprintf (fp, "fs %g %g %g %g\n", x, y + Delta/2., z, fs.y[0,1]);
fprintf (fp, "fs %g %g %g %g\n", x, y, z + Delta/2., fs.z[0,0,1]);
fprintf (fp, "%g %g %g %g\n", x, y, z, cs[]);
}
fclose (fp);
}
#endif
.nodump = false;
pdump();
}
#endif
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 (stderr, "%d %d %d %d %d %d %d %d %.3g %.3g %.3g %.3g %.3g\n",
, i,
maxlevel.i, mgp.nrelax, mgp.minlevel,
mgp.i, mgu.nrelax, mgu.minlevel,
mgu, mgp.resa*dt, mgu.resa, statsf(u.x).sum, normf(p).max); du
If the relative change of the velocity is small enough we stop this simulation.
if (i > 1 && (avg < 1e-9 || du < 0.01)) {
We are interested in the permeability k of the medium, which is defined by U = \frac{k}{\mu}\nabla p = \frac{k}{\mu}\rho g with U the average fluid velocity.
We output fields and dump the simulation.
scalar nu[];
foreach()
[] = norm(u);
nuboundary ({nu});
view (fov = 32.2073, quat = {-0.309062,0.243301,0.0992085,0.914026},
= 0.0122768, ty = 0.0604286, bg = {1,1,1},
tx = 600, height = 600);
width box();
draw_vof("cs", "fs", fc = {0.5,0.5,0.5});
char name[80];
sprintf (name, "cs-%d.png", maxlevel);
save (name);
box();
isosurface ("u.x", 1e-5, fc = {0.,0.7,0.7});
sprintf (name, "nu-%d.png", maxlevel);
save (name);
view (fov = 19.1765, quat = {0,0,0,1},
= 0.0017678, ty = 0.00157844, bg = {1,1,1},
tx = 600, height = 600);
width squares ("nu", linear = true);
cells();
sprintf (name, "cross-%d.png", maxlevel);
save (name);
sprintf (name, "dump-%d", maxlevel);
dump (name);
We stop at level 8.
if (maxlevel >= 8)
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.
++;
maxlevel#if TREE
({cs,u}, (double[]){1e-2,4e-6,4e-6,4e-6}, maxlevel);
adapt_wavelet #endif
porous (cs, fs);
boundary (all); // this is necessary since BCs depend on embedded fractions
event ("adapt");
}
}



set xlabel 'Level'
set grid
set ytics format '%.1e'
plot 'out' w lp t ''
set xlabel 'Iterations'
set logscale y
set ytics format '%.0e'
set yrange [1e-8:]
set key center left
plot '../porous3D.ref' u 2:9 w l t '', '' u 2:10 w l t '', \
'' u 2:11 w l t '', '' u 2:12 w l t '', '' u 2:13 w l t '', \
'log' u 2:9 w p t 'du', '' u 2:10 w p t 'resp', \
'' u 2:11 w p t 'resu', '' u 2:12 w p t 'u.x.sum', '' u 2:13 w p t 'p.max'