sandbox/hugoj/multilayer_stratified/ml_convection.c

    Convection with the multilayer

    This code aims to represent a oceanic convection case. The water is stratified with N^2=\sqrt{g\frac{\partial \rho}{\partial z}}. The equation of state is linear, following \rho = \rho_0(1-\alpha(T-T_0)) or in Basilisk’s dr.h notation \Delta \rho = -\alpha(T-T_0).

    The experiment depends on the Richardson number Ri=N^2.H^2/w_*^2 and Prandtl number which is the ratio of viscosity to temperature diffusion Pr=\nu/\kappa. We use the convective velocity w_*=(-B0.h)^(1/3), B_0 the buoyancy flux at the surface $B_0=Q with Q the heat flux in W.m^{-2}, H the depth of the mixed layer. A convective time can be built t_*=H/w_* and [Van Roeckel, Souza] get an analytical expression for the mixed layer height evolution:

    \begin{equation} h(t) = \sqrt{a \frac{-B_0}{N^2}t } \end{equation}

    where a is a prefactor. [Souza] found that their prefactor was a=3.0 when they take into account entrainment of cold water into the mixed layer, while [Roeckel] has a=2.8 without entrainment.

    //#include "grid/gpu/multigrid.h"
    #include "grid/multigrid.h"
    //#include "grid/multigrid1D.h"
    #include "hydro.h"
    #include "layered/nh.h"

    We use remapping, but this might not be necessary

    #include "layered/remap.h"
    #include "layered/perfs.h"
    #include "bderembl/libs/extra.h"      // parameters from namlist
    #include "bderembl/libs/netcdf_bas.h" // read/write netcdf files
    
    //#define g_ 9.81
    
    /*
    
    TO DO : 
    - add dimensions so that they can be saved in the netcdf ?
    
    */
    
    /*
    DEFAULT PARAMETERS
    
    These parameters are changed by the values in the namelist
    
    Dimensions : [Length, Time, Temperature, Energy, mass]
    */
    char namlist[80] = "namelist.toml";    // file name of namlist
    char file_out[20] = "out.nc";          // file name of output
    // -> Initial conditions
    double strat = 0.000002 [0,-2];       // [s-2] N^2 stratification
    double Ts = 20. [0,0,1];              // [K] Surface temperature (arbitrary)
    // -> Forcing
    double qt = 100. [-2,-1,0,1];         // [W.m-2] Heat flux
    // -> Domain definition
    int N_grid = 5;                       // 2^N_grid : number of x and y gridpoints
    double L = 200.0 [1];                 // domain size
    int N_layer = 5;                      // number of layers
    double h0 = 1.0 [1];                  // depth of water
    // -> Runtime parameters
    int restart = 0 [0];                  // 1: restart, 0: no restart
    double tend = 2.0 [0,1];              // end time of simulation
    // -> saving outputs
    double dtout = 2.0 [0,1];             // dt for output in netcdf
    double smalltime = 1e-10 [0,1];       // (s) small time increment
    int pad = 4 [0];                      // number of 0-padding for ouput files
    int nout = 1 [0];                     // number of the outfile
    char fileout[100];                    // name of outfile
    // -> physical properties
    double nu0 = 0.00025 [2,-1];          // Viscosity for vertical diffusion
    double thetaH = 0.5 [0];              // theta_h for dumping fast barotropic modes
    // -> stratification related
    double rho0 = 1025. [-3,0,0,0,1];     // [kg.m-3] reference density
    double cp = 4.2e3 [0,0,-1,1,-1];      // [J.kg-1.K-1] heat capacity water
    double alphaT = 2e-4 [0,0,-1];        // [K-1] Thermal expansion coeff for water
    double Pr = 1.;                       // Prandtl number = nu/kappa
    double kappa = 1.5e-5 [2,-1];         // [m2.s-1] Scalar vertical diffusion coeff
    double T0 = 20. [0,0,1];              // [°C] Reference temperature
    double Trand = 0.1 [0,0,1];           // [°C] Random temperature perturbution
    const double g_ = 9.81 [1,-2];        // [m.s-2] Gravity
    #define drho(T) (alphaT*(T0-T))       // Linear equation of state (Vallis 2.4)
    #define Tini(z) strat/(g_*alphaT)*z + Ts
    
    #include "layered/dr.h"
    
    // diag
    double *T_profile;
    double *W_profile;
    double *Tflx_profile;
    double dt_mean = 1.;
    
    static FILE * fp1;
    static FILE * fp2;
    
    double umax,vmax;
    
    int main(int argc, char *argv[])  
    {
     
      // Building a 'params' array with all parameters from the namlist
      params = array_new();
      add_param("N_grid", &N_grid, "int");
      add_param("L", &L, "double");
      add_param("N_layer", &N_layer, "int");
      add_param("h0", &h0, "double");
      add_param("tend", &tend, "double");
      add_param("nu0", &nu0, "double");
      add_param("thetaH", &thetaH, "double");
      add_param("restart", &restart, "int");
      add_param("dtout", &dtout, "double");
      add_param("strat", &strat, "double");
      add_param("Ts", &Ts, "double");
      add_param("rho0", &rho0, "double");
      add_param("cp", &cp, "double");
      add_param("alphaT", &alphaT, "double");
      add_param("qt", &qt, "double");
      add_param("dt_mean", &dt_mean, "double");
      add_param("Pr", &Pr, "double");
      // show_dimension (u);
      //dimensional (T_profile[0], T0);
      //dimensional (W_profile[0], L/dt);
    
      // Search for the configuration file with a given path or read params.in
      if (argc == 2)
        strcpy(file_param, argv[1]);
      else
        strcpy(file_param, namlist);
      read_params(file_param);
      
      // Settings solver values from namlist values
      L0 = L;
      nu = nu0;
      kappa = nu0/Pr;
      N = 1 << N_grid; // 1*2^N_grid
      nl = N_layer;
      G = g_;
      theta_H = thetaH;
      CFL_H = 8.;
      CFL = 0.8;
      //TOLERANCE = 1e-8;
      //CFL_H = 40; 
      //CFL=0.5;
    
      // Boundary condition
      origin (-L0/2., -L0/2.);
      
      //periodic (right);
      #if dimension==2
        periodic (top);
      #endif
      periodic (left);
    
      // T profile
      T_profile = (double*)calloc(nl, sizeof(double));
      W_profile = (double*)calloc(nl, sizeof(double));
      Tflx_profile = (double*)calloc(nl, sizeof(double));
      fp1  = fopen("T_profile.dat","w"); // reset file
      fclose(fp1);
      // fp2  = fopen("Tflx_profile.dat","w"); // reset file
      // fclose(fp2);
    
      fprintf (stderr, "Read in parameters!\n");
      fprintf (stderr, "CFL_H=%g CFL=%g\n", CFL_H,CFL);
    
      run();
      
    }
    
    event init(i =  0) {
      // reset({T},0.);
      geometric_beta (0., true); // if !=0, varying layer thickness 1./3.
      // step 1: set eta and h
      foreach() {
        zb[] = -h0;
        eta[] = 0.;
        double H = - zb[];
        foreach_layer() 
          h[] = H*beta[point.l];
      }
    
      reset({T},0.);
      // step 2: set currents
      foreach(cpu) {
        double z = zb[];
        //dimensional (T[]==Trand);
        foreach_layer() {
          z += h[]/2.;
          foreach_dimension()
            u.x[] = 0.;
          w[] = 0.;
          T[] = Tini(z) + Trand * noise() ;
          z += h[]/2.;
        }
      }
      
      // initializing diag arrays
      // T_profile[0] = Trand; // <- this passes the dimensional analysis
      // dimensional (T_profile[0] == Trand); // <- this doesnt
       for (int i=0; i<nl; ++i) {
         T_profile[i] = Trand*0.;
         Tflx_profile[i] = Trand*L0/DT*0.;
         W_profile[i] = L0/DT*0.;
       }
      
      show_dimension (T);
      
      // original version, bug for now (currents writes = 0)
      // create_nc({zb, h, u, w, eta, T}, file_out);
    
      // temporary fix
      #if dimension==1
        create_nc((scalar *){zb, h, u.x, w, eta, T}, file_out);
      #else
        create_nc((scalar *){zb, h, u.x, u.y, w, eta, T}, file_out);
      #endif
      fprintf (stderr,"Done initialization!\n");
    }
    
    // vertical diffusion on T
    // This implements a heat flux at the surface
    event viscous_term (i++)
    {
      foreach()
        vertical_diffusion (point, h, T, dt, kappa, qt/(kappa*rho0*cp), 0., 0.); // T[0,0,nl-1]
        //TODO: explain more how I do the bottom flux = 0. This is in a custom
        // diffusion.h in hugoj/lib/diffusion.h
    }
    
    //Writing a 4D netcdf file
    event output(t = 0.; t<= tend+smalltime; t+=dtout){
      write_nc();
    }
    
    event end(t=tend+smalltime){
      dump();
    }
    
    // event print_max_u (i++)
    // {
    //   umax = 0.;
    //   vmax = 0.;
    //   foreach_layer(){
    //     foreach(reduction(max:umax) reduction(max:vmax)){
    //       if (fabs(u.x[])>umax){
    //         umax=u.x[];
    //       }
    //       #if dimension>1
    //       if (fabs(u.y[])>vmax){
    //           vmax=u.y[];
    //         }
    //       #endif
    //     }
    //   }
    //   if (vmax>umax){
    //     umax = vmax;
    //   }
    //   fprintf(stderr, "max current is %g with dt=%g and DT=%g and dtmax=%g\n", umax, dt, DT,dtmax);
    // }
    
    
    
    
    #if DIAG==1
    double* h_avg(scalar var, double* profile){
      /*
      This function computes the horizontal average of var.
      
      INPUTS:
        var: scalar (C Basilisk), the variable to average.
        profile: array of double of length nl
      OUTPUTS:
        an array of double with the average values inside
       */
    
      int NB;
      #if dimension==1
        NB=N;
      #else
        NB=N*N;
      #endif
    
      foreach_layer() {
        double sum = 0.;
        foreach (reduction(+:sum))
          sum += var[];
        profile[_layer] += sum / NB;
      }
    
      return profile;
    }
    
    int write_profile(char* name, double* profile, FILE* fp){
      /*
      docstring
    
      */
      // main worker is writing the file
      if (pid()==0) {
        fp  = fopen(name,"a");
        if (fp == NULL){
          fprintf(stderr, "Error opening file %s", name);
          return 2;
        }
        for (int i=0; i<nl; ++i) {
          fprintf (fp, "%f %d %g\n", t, i, profile[i]);
        }
        fprintf(fp,"\n");
        fclose(fp);
      }
      return 0;
    }
    
    
    // This event compute layer average of T, w
    event compute_horizontal_avg (t+=dt_mean; t<=tend+smalltime){
      T_profile = h_avg(T, T_profile);
      // show_dimension (T_profile[0]);
      // show_dimension (W_profile[0]);
      //W_profile = h_avg(w, W_profile);
    }
    
    // event compute_flx_wT(t+=dt_mean, t<=tend+1e-10){
    //   foreach(reduction(+:Tflx_profile[:nl]))
    //     foreach_layer(){
    //     #if dimension==1
    //       Tflx_profile[point.l] += ((w[]-W_profile[point.l])*(T[]-T_profile[point.l])) / N;
    //     #else
    //       Tflx_profile[point.l] += ((w[]-W_profile[point.l])*(T[]-T_profile[point.l])) / (N*N);
    //     #endif
    //   }
    // }
    
    
    // This even writes to a file the layer average
    event write_diag(t=0., t+=dt_mean){
        write_profile("T_profile.dat", T_profile, fp1);
        //write_profile("Tflx_profile.dat", Tflx_profile, fp2);
    
        // Reset the profile for all workers
        for (int i=0; i<nl; ++i) {
          T_profile[i] = 0.0;
          //Tflx_profile[i] = 0.0;
        }
    }
    
    
    event cleanup(t=end){
      free(T_profile);
      //free(Tflx_profile);
    }
    #endif
    Results: plots
    # Plot the heatmap
    set pm3d map
    set view map
    set xlabel "Time (s)"
    set ylabel "Layer"
    set cblabel "T (°C)"
    set yrange [0:14]
    set xrange [0:200]
    set terminal pngcairo size 800,600 enhanced font 'Verdana_r,12'
    set output 'T_profile.png'
    set size 0.9, 0.9
    splot "T_profile.dat" using 1:2:3 with pm3d
    unset output
    (script)