/** # Direct numerical simulation of convective turulence In this test case adaptive grids are tested for convective turbulence with a fixed bottom temperature (i.e. Buoyancy) and (inital) liniarly stratified "atmosphere". The direct numerical simulation (DNS) test case is inspired by [1], Here reference results are provided, the used code here is [MicroHH](http://microhh.org/). ## Loading modules and declaring parameters - We specify that we would like to perform our computation on an oct-tree grid (i.e. 3D adaptive) - We specify that we want to solve the equations of fluid motion, specify a diffusive tracer (T) that will be our buoyancy field - Set the maximum level of refinment (minimum is " MAXLEVEL - 4" ) - We initizalize some global variables that will prove to be usefull. Note: We have a normalized linear stratification, bottom buoyancy and thereby lengthscale L. Therefore, the Reynoldsnumber is directly controlled by the viscosity of the fluid. */ #include "grid/octree.h" #include "navier-stokes/centered.h" #include "tracer.h" #include "diffusion.h" #include "profil/profile2.h" #include "lambda2.h" #define sqN 1 #define MAXLEVEL 9 #define TT pow(molvis,-0.333333333) #define temp 45*TT #define sq(x) x * x char names[80]; double ue = 1e-2; scalar T[]; scalar * tracers = {T}; mgstats mgT; face vector av[]; double molvis = 1.225289152e-4; /** ## Main Set boundary conditions, define domain, initialize viscosity, intialize grid, link boussinesq accelertation term, and call "run()" loop for time integration. */ int main() { T[bottom]=dirichlet(1); T[top] = dirichlet(3); u.t[bottom]=dirichlet(0); L0 = 3.; X0 = Y0 = Z0 = 0; init_grid(1 << (MAXLEVEL-1)); const face vector muc[] = {molvis,molvis,molvis}; mu = muc; a=av; run(); } /** ## Initialization - Set a small timestep and tolerance on the poisson solver. This is for the first few timesteps for a correct initisalization - Initizalize the stratification - Refine the grid and add a perturbation to buoyancy and velocity components to kickstart the growth of the instability. */ event init(t=0) { DT=0.001; TOLERANCE=1e-5; foreach() { T[]=(sqN*y); } refine (y < 0.05 && level < (MAXLEVEL), all); foreach() { T[]+=(0.04*noise()*(exp(-y/0.02))); foreach_dimension() { u.x[]=0.01*noise()*(exp(-y/0.02)); } } boundary(all); } /** ## Buoyancy acceleration & Spongelayer and diffusion of T[] field Define the acceleration term and do diffusion of diffusive buoyancy scalar. */ event acceleration (i++) { coord Del = { 0 , 1, 0}; foreach_face() { av.x[] = Del.x* (((T[]+T[-1])/2)-(((u.x[]+u.x[-1])/2)*((exp((y-3)/0.1)*(y>3)*(y<3))+(y>3)))); } foreach() { T[]-=(T[]-y) * ((exp((y-3)/0.05)*(y>2)*(y<3))+(y>3)); } } /** ## Diffusion Diffusion of buoyancy scalar. */ event Diffusion (i++) { mgT = diffusion (T, dt, mu); boundary(all); } /** ## Log file Boring log file */ event logfile(t+=1; t<=temp) { fprintf(ferr,"%dD\tt=%g\ti=%d\tDT=%g\n" ,dimension,t,i,DT); } /** ## Adaptation Refine and coarsen the grid and set the timestep and tolerance to a more sensible value after some initial timesteps. Note however that, from this point on, the timestep is typically limited by the CFL condition, not by DT. */ event adapt (i++) { adapt_wavelet((scalar *){u,T},(double[]){ue,ue,ue,ue},MAXLEVEL,(MAXLEVEL-4)); if (i==10) { fprintf(ferr,"Adapt timestep before DT = %g.",DT); DT=0.1; TOLERANCE = 1e-3; fprintf(ferr,"now DT = %g\n",DT); } } /** # Diagnostics The code below is used to diagnose the evolution of the simulation and numerically obtained results with vertical profiles, time series and slices. ## Profiles First we use the 'profile()' function included from profile2.h to write files containing vertical (y-direction) profiles of the vertical variance, the horizontal variance, the kinetic energy, the resolved dissipation and the ratio between an estimated viscous length scale and the grid box size. The profiels are evaluated for [0= 0 ? interpolate(T,xp,yp,zp) : nodata; fiel[i][k] = point.level >=0 ? interpolate(u.y,xp,yp,zp) : nodata; //fprintf(ferr,"%g\t%d\t%d\n",field[i][k],i,k); k++; } i++; } if (pid() == 0) { // master @ if _MPI { MPI_Reduce (MPI_IN_PLACE, field[0], pow(2,(MAXLEVEL*2)), MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); MPI_Reduce (MPI_IN_PLACE, fiel[0], pow(2,(MAXLEVEL*2)), MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); } @endif for(int i = 0; i= 0 ? s[] : nodata; //field[i][len*j + k++] = interpolate (s, xp, yp,zp); } } if (pid() == 0) { // master @if _MPI MPI_Reduce (MPI_IN_PLACE, field[0], len*nn*nn, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); @endif int k = 0; for (scalar s in list) { for (int i = 0; i < nn; i++) { for (int j = 0; j < nn; j++) { fprintf (fpver, "%g\t", field[i][len*j + k]); } fputc ('\n', fpver); } fflush (fpver); k++; } } @if _MPI else // slave MPI_Reduce (field[0], NULL, len*nn*nn, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); @endif matrix_free (field); scalar * lists = {T,u.y}; sprintf(names,"./data/horslicet=%g.dat",t); FILE *fphor =fopen (names,"w"); nn = (pow(2,MAXLEVEL)); len = list_len(lists); field = matrix_new (nn, nn, len*sizeof(double)); double yp = Y0+(1/3); for (int i = 0; i < nn; i++) { double xp = stp*i + X0 + stp/2.; for (int j = 0; j < nn; j++) { double zp = stp*j + Y0 + stp/2.; Point point = locate (xp, yp,zp); int k = 0; for (scalar s in lists) // field[i][len*j + k++] = interpolate (s, xp, yp,zp); field[i][len*j + k++] = point.level >= 0 ? s[] : nodata; } } if (pid() == 0) { // master @if _MPI MPI_Reduce (MPI_IN_PLACE, field[0], len*nn*nn, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); @endif int k = 0; for (scalar s in lists) { for (int i = 0; i < nn; i++) { for (int j = 0; j < nn; j++) { fprintf (fphor, "%g\t", field[i][len*j + k]); } fputc ('\n', fphor); } fflush (fphor); k++; } } @if _MPI else // slave MPI_Reduce (field[0], NULL, len*nn*nn, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); @endif matrix_free (field); } /** # Results A version of the code above was run on the SURF-sara HPC-cloud computer. A visualization of the results looks something like this: [Youtube link](https://www.youtube.com/watch?v=K1zTd5eoFQ4) A more quantitative validation with reference results is the comparison of the energy in the flow field as a function of time: ![Validation results: adaptive solver (Basilisk) and MicroHH by reference[1]](/sandbox/Antoonvh/chielzor.jpg) # Reference [1] van Heerwaarden, Chiel C., and Juan Pedro Mellado. "Growth and decay of a convective boundary layer over a surface with a constant temperature." Journal of the Atmospheric Sciences 2016 (2016). */