sandbox/lbattershill/pdc_final/fluidised_flow.c
Wave generation by fluidised flow. Three-phase Navier-Stokes with option for diffusion between two phases.
This is the Navier-Stokes VOF case of the Bougouin, 2020 fluidized granular flow tsunami generation experiment.
//#include "grid/octree.h"
#include "navier-stokes/centered.h"
Initialise density tracer f3 (representative of third phase)
scalar f3[];
double rho3 = 1400; //density of dense salt water
double mu3 = 0.1; //density of salt water at 20degrees
We “overload” the default by defining the rho() and mu() macros before including the code for two phases.
#ifndef rho
# define rho(f) (clamp(f,0.,1.)*(clamp(f3[],0.,1.)*(rho3 - rho1) + rho1 -rho2) + rho2)
#endif
#ifndef mu
# define mu(f) (clamp(f,0.,1.)*(clamp(f3[],0.,1.)*(mu3 - mu1) + mu1 - mu2) + mu2) //viscoisty is a function of the volumne fraction
#endif
Definition of the robin boundary, see Antoon’s robin.c for details.
#define robin(a,b,c) ((dirichlet ((c)*Delta/(2*(b) + (a)*Delta))) + ((neumann (0))* ((2*(b) - (a)*Delta)/(2*(b) + (a)*Delta) + 1.)))
Optional to include diffusion
#def IMPLICIT_DIFFUSION 0
#if IMPLICIT_DIFFUSION
#include "sandbox/qmagdelaine/phase_change/advection_Q.h"
#endif
#include "two-phase.h"
#include "myconserving.h" //updated conserving for new three-phase formulation
#if IMPLICIT_DIFFUSION
#include "sandbox/qmagdelaine/phase_change/mixtures.h"
//#include "sandbox/qmagdelaine/phase_change/elementary_body.h" //Optional
#endif
//#include "tension.h" //Optional: if including surface tension
#include "utils.h"
double ue = 1e-3;
#define io (0.338)
#define MUR 54
#define MAXLEVEL 12
Initialise domain, 6 units long.
int main() {
L0 = 6.;
origin (0, 0);
rho1 = 998; //water
rho2 = 1.27; //air
mu1 = 1.002e-3; //water
mu2 = 1.002e-3/MUR; //air
//f.sigma = 73e-3;
init_grid (1 << MAXLEVEL);
//fp=fopen("tracers_cons_final.dat", "w");
const face vector av[] = {9.81*sin(0.268), -9.81*cos(0.268), 0};
a = av;
CFL = 0.4;
run();
}
Implementation of robin boundary condition, robin(a,b,c) where b is the slip length.
u.t[bottom] = robin(1.,0.04,0.);
u.n[left] = dirichlet(0.);
u.t[left] = dirichlet(0.);
Optional if including diffusion
#if IMPLICIT_DIFFUSION
attribute {
double D;
}
#endif
Initialise the geometry.
event init (t = 0)
{
foreach() {
if (y < tan(0.268)*(x-(1+io))) { //with water
f[] = 1.;
f3[] = 0.;
}
else if (x < ((y - tan(1.309)*(io))/(-tan(1.309))) && y < 0.225 && z <= 0.2 ) { //accounts for the extra volume
f[] = 1.;
f3[] = 1.;
}
else {
f[] = 0.;
f3[] = 0.;
}
foreach_dimension()
u.x[] = 0.;
}
boundary ({f,f3,u});
}
//* Output statistics to a logfile. */
#include "view.h"
event logfile (i++){
fprintf (stderr, "%d %g %d %d\n", i, t, mgp.i, mgu.i);
}
If accounting for diffusion… fixme: diffusion into bottom boundary!
#if IMPLICIT_DIFFUSION
#define D_L 0.0001
mgstats mgd1;
event tracer_diffusion (i++) {
foreach()
f[] = clamp(f[], 0., 1.);
boundary ({f});
foreach()
f3[] = (f[] > F_ERR ? f3[]/f[] : 0.);
boundary({f3});
f3.D = D_L;
f3.inverse = false;
no_flux_diffusion (f3, f, dt);
foreach()
f3[] *= f[];
boundary({f3});
}
#endif
Crude method for boundary implementation but works well for no diffusion case:
scalar tri[];
#define TRIANGLE (tan(0.268)*(x-(1+io+1.02)))
event makeslope (i++) {
fraction (tri, y <= TRIANGLE);
foreach()
foreach_dimension()
u.x[] -= u.x[]*tri[];
face vector tri_face[];
face_fraction (tri, tri_face);
boundary ((scalar *){tri_face});
foreach_face()
uf.x[] -= tri_face.x[]*uf.x[];
boundary ((scalar *){u, uf});
}
//3D boundary
scalar edge[];
event makeedge (i++) {
fraction(edge, z > 0.2);
foreach()
foreach_dimension()
u.x[] -= u.x[]*edge[];
boundary((scalar *){u, uf});
}
Output images
int time_step = 0.; //don't need this
event output_files (t += 0.01; t < 4.5) {
char one[80], two[80], three[80], four[80];
foreach() {
if (tri[] > 0.5)
tri[] = -1;
}
//First two outputs show JPG snapshots for FOV
sprintf (one, "tracer-%d.png",time_step );
output_ppm(f3, file = one, map = cool_warm, n = 2048, linear = true, box = {{0,0},{4.5,1}}, mask = tri);
sprintf (two, "density-%d.png",time_step );
output_ppm(rho, file = two, min = 1, max = 1400, n = 2048, linear = true, box = {{0,0},{4.5,1}}, mask = tri);
//Bview outputs
view(fov = 20, psi = 0.268, tx = -0.55, ty = -0.05);
box();
char str[80];
sprintf(str, "t = %g", t);
draw_string(str, pos = 4, lw =3);
draw_vof("f", filled = -1, fc = {1,1,1});
squares("f3");
save("movie.mp4");
//We also output a vertical cross section of the velocity compoentns
sprintf (three, "vprof-%d",time_step);
FILE * fp1 = fopen (three, "w");
for (double y = 0.; y <= 0.05; y += 8/(pow(2,MAXLEVEL))){
fprintf (fp1, "%g %g %g %g\n", y,interpolate (u.x, 1.238, y, 0.1),interpolate (u.y, 1.238, y, 0.1),interpolate(f, 1.238,y, 0.1));} //Outputs y, u.x, u.y, f
fclose (fp1);
time_step += 1.;
}
Outputs
We are interested in the viscous dissipation rate in both water, granular fluid and air.
int dissipation_rate (double* rates)
{
double rateWater = 0.0;
double rateAir = 0.0;
double rateFlow = 0.0;
foreach (reduction (+:rateWater) reduction (+:rateAir) reduction (+:rateFlow)) {
double dudx = (u.x[1] - u.x[-1] )/(2.*Delta);
double dudy = (u.x[0,1] - u.x[0,-1] )/(2.*Delta);
double dudz = (u.x[0,0,1] - u.x[0,0,-1])/(2.*Delta);
double dvdx = (u.y[1] - u.y[-1] )/(2.*Delta);
double dvdy = (u.y[0,1] - u.y[0,-1] )/(2.*Delta);
double dvdz = (u.y[0,0,1] - u.y[0,0,-1])/(2.*Delta);
double dwdx = (u.z[1] - u.z[-1] )/(2.*Delta);
double dwdy = (u.z[0,1] - u.z[0,-1] )/(2.*Delta);
double dwdz = (u.z[0,0,1] - u.z[0,0,-1])/(2.*Delta);
double SDeformxx = dudx;
double SDeformxy = 0.5*(dudy + dvdx);
double SDeformxz = 0.5*(dudz + dwdx);
double SDeformyx = SDeformxy;
double SDeformyy = dvdy;
double SDeformyz = 0.5*(dvdz + dwdy);
double SDeformzx = SDeformxz;
double SDeformzy = SDeformyz;
double SDeformzz = dwdz;
double sqterm = 2.*dv()*(sq(SDeformxx) + sq(SDeformxy) + sq(SDeformxz) +
sq(SDeformyx) + sq(SDeformyy) + sq(SDeformyz) +
sq(SDeformzx) + sq(SDeformzy) + sq(SDeformzz)) ;
rateWater += mu1/rho1*(1.-f3[])*f[]*sqterm; //water (when f3 is one, this will be zero)
rateFlow += mu3/rho3*(f3[])*f[]*sqterm; //flow
rateAir += mu2/rho2*(1. - f[])*sqterm; //air
}
rates[0] = rateWater;
rates[1] = rateAir;
rates[2] = rateFlow;
return 0; }
We log the evolution of the kinetic and potential energies and dissipation rate as functions of the non-dimensional time.
event graphs (t += 0.01) {
static FILE * fpwater = fopen("budgetWater.dat", "w");
static FILE * fpair = fopen("budgetAir.dat", "w");
static FILE * fflow = fopen("budgetFlow.dat", "w");
double keWater = 0., gpeWater = 0.;
double keAir = 0., gpeAir = 0.;
double keFlow = 0., gpeFlow = 0.;
foreach(reduction(+:keWater) reduction(+:gpeWater)
reduction(+:keAir) reduction(+:gpeAir)
reduction(+:keFlow) reduction(+:gpeFlow)) {
double norm2 = 0.;
foreach_dimension()
norm2 += sq(u.x[]);
keWater += rho1*(1.0-f3[])*norm2*f[]*dv();
keAir += rho2*norm2*(1.0-f[])*dv();
keFlow += rho3*f3[]*norm2*f[]*dv();
gpeWater += rho1*f[]*(1.0-f3[])*dv()*9.81*(-sin(0.268)*x+cos(0.268)*y);
gpeAir += rho2*(1.0-f[])*dv()*9.81*(-sin(0.268)*x+cos(0.268)*y);
gpeFlow += rho3*f[]*(f3[])*dv()*9.81*(-sin(0.268)*x+cos(0.268)*y);
}
double rates[3];
dissipation_rate(rates);
double dissWater = rates[0];
double dissAir = rates[1];
double dissFlow = rates[2];
if (i == 0) {
fprintf (fpwater, "t ke gpe dissipation\n");
fprintf (fpair, "t ke gpe dissipation\n");
fprintf (fflow, "t ke gpe dissipation\n");
}
fprintf (fpwater, "%g %g %g %g\n",
t, keWater/2., gpeWater, dissWater);
fprintf (fpair, "%g %g %g %g\n",
t, keAir/2., gpeAir, dissAir);
fprintf (fflow, "%g %g %g %g\n",
t, keFlow/2., gpeFlow, dissFlow);
}
We output the water elevation profile, which is used to extract wave gauges.
int add = 0;
event interface (t+= 0.01) {
char fname[99];
sprintf(fname, "water_elevation%d.txt", add);
FILE * fp = fopen(fname, "w");
output_facets(f,fp);
fclose(fp);
add += 1.;
}
Option not to include - vtu files not necessary.
int counter = 0;
#include "output_vtu_foreach.h"
event vtk_file (t += 0.05) {
FILE * fp;
char name[80];
sprintf(name, "test_0.01_%d.vtu", counter);
fp = fopen(name,"w");
output_vtu_bin_foreach((scalar *) {rho,f}, (vector *) {u},N,fp,false);
fclose(fp);
counter += 1.;
}
Adapt with respect to vorticity.
event adapt (i++)
{
scalar omega[];
vorticity(u,omega);
adapt_wavelet((scalar *){omega,f},(double[]){ue,ue,0.01},MAXLEVEL);
}
References
A. Bougouin, R. Paris and O. Roche. Impact of Fluidized Granular Flows into Water: Implications for Tsunamis Generated by Pyroclastic Flows.