sandbox/hugoj/breaking_strat/ml_breaking_strat.c

    # Wave breaking with stratification (multilayer solver)

    USAGE

    • Compilation and run (with mpirun) make

    • Compilation and run (gpu) __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia make name.gpu.tst

    • HPC source file generation make _name.c

    • Running on HPC

    -> first compile on hpc with mpicc -std=c99 -O2 _*.c

    -> then run (in a slurm file) srun ./name

    const double g_ = 9.81 [1,-2];        // [m.s-2] Gravity
    
    #include "grid/multigrid.h"
    //#include "grid/multigrid1D.h"
    #include "layered/hydro.h"
    #include "layered/nh.h"
    #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
    #include "hugoj/lib/spectrum.h"       // Initial conditions generation
    
    /*
    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)
    double P = 0.2 [1, -1];               // energy level (estimated so that kpHs is reasonable)
    int coeff_kpL0 = 10 [];               // kpL0 = coeff_kpL0 * pi
    int N_mode = 32 [];                   // Number of modes in wavenumber space
    int N_power = 5 [];                   // directional spreading coeff
    int F_shape = 0 [];                   // shape of the initial spectrum
    double kp = PI*10/200.0 [-1];         // peak wave number
    // -> 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
    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 betaT = 2e-4 [0,0,-1];         // [K-1] Thermal expansion coeff for water
    double Dtemp = 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
    
    #define drho(T) (betaT*(T0-T))        // Linear equation of state: drho = betaT*(T0-T) (Vallis 2.4)
    #define Tini(z) strat/(g_*betaT)*z + Ts
    #include "layered/dr.h"
    
    // diag
    double *T_profile;
    double *u_profile;
    double dt_mean = 1.;
    
    static FILE * fp1;
    static FILE * fp2;
    
    
    
    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("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("betaT", &betaT, "double");
      add_param("qt", &qt, "double");
      add_param("dt_mean", &dt_mean, "double");
    
      kp = PI * coeff_kpL0 / L; // kpL=coeff x pi peak wavelength
      
      // 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;
      N = 1 << N_grid; // 1*2^N_grid
      nl = N_layer;
      G = g_;
      theta_H = thetaH;
      CFL_H = 1; 
      CFL=0.8;
      
      // Boundary condition
      origin (-L0/2., -L0/2.);
      periodic (top);
      periodic (left);
    
      // diags
      T_profile = (double*)calloc(nl, sizeof(double));
      u_profile = (double*)calloc(nl, sizeof(double));
      fp1  = fopen("T_profile.dat","w"); // reset file
      fclose(fp1);
      fp2  = fopen("u_profile.dat","w"); // reset file
      fclose(fp2);
    
      fprintf (stderr, "Read in parameters!\n");
      
    
      run();
      
    }
    
    event init(i =  0) {
    
      geometric_beta (1/3., true); // if !=0, varying layer thickness
      
      //We generate a spectrum using spectrum.h
      T_Spectrum spectrum;
      spectrum = read_spectrum(N_mode);

    set T. Doing this now instead of later in the code allows for a initial stratitifcation that follows layers.

      foreach() {
        zb[] = -h0;
        double H =  - zb[];
        double z = zb[];
        foreach_layer() {
          h[] = H*beta[point.l];
          z+=h[]/2.;
          T[] = Tini(z); // + noise();
          z+=h[]/2.;
        } 
      }
    
      // set eta and h
      foreach() {
        eta[] = wave(x, y, N_grid, spectrum);
        double H = wave(x, y, N_grid, spectrum) - zb[];
        foreach_layer() {
          h[] = H*beta[point.l];
        } 
      }

    we now remap the T field onto the new layer coordinates

      vertical_remapping (h, tracers);

    set currents

      foreach() {
        double z = zb[];
        foreach_layer() {
          z += h[]/2.;
          u.x[] = u_x(x, y, z, N_grid, spectrum);
          u.y[] = u_y(x, y, z, N_grid, spectrum);
          w[] = u_z(x, y, z, N_grid, spectrum);
          // T[] = Tini(z);
          // fprintf(stderr, "l=%d, z=%f, Tini(z)=%f", point.l, z, Tini(z));
          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.;
        u_profile[i] = L0/DT*0.;
      }
      // show_dimension (T_profile[0]);
      // show_dimension (W_profile[0]);
    
    
      // temporary fix, should use u instead of u.x, u.y
      create_nc({zb, h, u.x, u.y, w, eta, T}, file_out);
      fprintf (stderr,"Done initialization!\n");
    }
    
    
    // dump outputs
    event output(t = 0.; t<= tend+smalltime; t+=dtout){
      write_nc();
      // regular dump
      char dname[100];
      sprintf (dname, "dump_t%g", t);
      dump(dname);
    }
    
    
    
    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
       */
      foreach(reduction(+:profile[:nl]))
        foreach_layer(){
        #if dimension==1
          profile[point.l] += var[] / N;// (N*N); // * dt / dt_mean;
        #else
          profile[point.l] += var[] / (N*N);
        #endif
        }
      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);
      u_profile = h_avg(u.x, u_profile);
    }
    
    
    
    // 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("u_profile.dat", u_profile, fp2);
    
        // Reset the profile for all workers
        for (int i=0; i<nl; ++i) {
          T_profile[i] = 0.0;
          u_profile[i] = 0.0;
        }
    }
    
    
    event cleanup(t=end){
      free(T_profile);
      free(u_profile);
    }
    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)