sandbox/ryang/multilayer_varydensity/dispersion.c

    Dispersion relations for various models

    This test case measures the dependence of the oscillation frequency of a gravity wave of wavenumber k on the water depth h. The corresponding phase velocity c=\omega/k is then compared to the exact linear dispersion relation c_e = \sqrt{gk\tanh(kh)}

    More details can be found in Popinet (2020).

    We use a 1D grid and either the Green-Naghdi solver or the multi-layer solver.

    #define ML 1
    #define HALF 1 //Rui: for remapping
    #include "grid/multigrid1D.h"
    #define a_baro(eta, i) 0.
    #if !ML
      #include "green-naghdi.h"
    #else
      #include "layered/hydro.h"
      #include "nh1.h"
      // necessary for stability at high h0 of the box scheme
      #include "layered/remap.h"
    #endif
    
    double emax = 1.;
    double h0 = 0.1;  //Rui: height of bottom liquid
    double * rho = (double [10]){ 1. [-3,0,1] }; //Rui: define a big enough rho array

    We initialise a wave of small relative amplitude (10^{-3}) to be in the linear regime. For the multilayer model, we also run a case with three “optimised” non-uniform layer thicknesses.

    event init (i = 0)
    {
    #if ML
      if (nl == 3 && emax == 1.5)
        beta[0] = 0.68, beta[1] = 0.265, beta[2] = 0.055;
      foreach()
        foreach_layer()
          h[] = beta[point.l]*h0*(1. + 0.001*cos(x))*2.; //Rui: factor of 2 since total height is 2*h0 
    #else
      foreach()
        h[] = h0*(1. + 0.001*cos(x));
    #endif
    }

    The code below locates the time of zero “up-crossing” of the amplitude in the middle of the wave and computes the corresponding averaged wave period.

    double Tm = 0., Es = 1., Ee = 1.;
    int nm = 0;
    
    event logfile (i++)
    {
      double pe = 0., ke = 0.;
    #if ML  
      foreach(reduction(+:ke) reduction(+:pe)) {//Rui: not working locally without the reduction compared to the original version
        double H = 0.;
        foreach_layer() {
          if (point.l < nl/2){ //Rui: only calculate energy for the bottom liquid
            ke += Delta*h[]*(sq(u.x[]) + sq(w[]))/2.;
            H += h[];
          }
        }
        pe += Delta*G*sq(H - h0)/2.;
      }
    #else
      foreach(reduction(+:ke) reduction(+:pe)) { //Rui: add reduction
        int l = 0;
        for (vector u in ul)
          ke += Delta*layer[l++]*h[]*sq(u.x[])/2.;
        pe += Delta*G*sq(h[] - h0)/2.;
      }
    #endif
    
    #if ML
      double H = 0.;
      foreach_point (L0/2., reduction(+:H)) //Rui: add reduction
        foreach_layer()
          if (point.l < nl/2) //Rui: H is the height for the local interface
             H += h[];
    #else
      double H = interpolate (h, L0/2., linear = false);
    #endif
      double dh = (H - h0)/h0;
    #if 0
      static FILE * fp = fopen ("timeseries", "w");
      char name[80];
      sprintf (name, "w%d", nl - 1);
      scalar w = lookup_field (name);
      fprintf (fp, "%g %g %g %g %g\n", t/(2.*pi/sqrt(tanh(h0))),
    	   H - h0, dh, ke, pe);
    #endif
      
      static double told = 0., hold = 0., eold = 0., tsold = -1;
      if (i == 0) {
        Tm = 0., nm = 0;
        Es = 0.;
        told = 0., hold = 0., tsold = -1;
      }
      else {
        if (i > 1 && hold < 0. && dh > 0.) {
          // this is an (upward) zero-crossing at time ts
          double ts = told - hold*(t - told)/(dh - hold);
          Ee = eold - hold*(ke + pe - eold)/(dh - hold);
          if (Es == 0.)
    	Es = Ee;
          //      fprintf (fp, "ts %g 0 %g\n", ts, Ee);
          if (tsold > 0. && nm < 4) {
    	// we average the periods
    	Tm += ts - tsold;
    	nm++;
          }
          tsold = ts;
        }
        told = t;
        hold = dh;
        eold = ke + pe;
      }
    }

    After 9.25 (exact) wave periods, we dump the vertical velocity profiles.

    #if ML
    event profile (t = 9.25*2.*pi/sqrt(tanh(h0)))
    {
      if (nl == 10) { //Rui: plot profile for nl = 10 (original nl =5)
        char name[80];
        sprintf (name, "profile-%g", h0);
        FILE * fp = fopen (name, "w");
        foreach_point (L0/2.) {
          double zl = zb[];
          foreach_layer() {
    	if (point.l < nl/2){ //Rui: only for bottom liquid
    	  fprintf (fp, "%g %g %g\n", zl + h[]/2., w[], phi[]);
    	  zl += h[];
    	}
          }
        }
        fclose (fp);
      }
    }
    #endif // ML

    After ten (exact) wave periods, we stop and dump the solution.

    event dumps (t = 10.*2.*pi/sqrt(tanh(h0))) // ten wave periods
    {
      FILE * fp = fopen ("prof", "w");
      fprintf (fp, "x");
      for (scalar s in all)
        fprintf (fp, " %s", s.name);
      fprintf (fp, "\n");
      foreach() {
        fprintf (fp, "%g", x);
        for (scalar s in all)
          fprintf (fp, " %g", s[]);
        fprintf (fp, "\n");
      }
      fprintf (fp, "\n");
      fclose (fp);
    }
    
    #if 0
    event movie (i += 1) {
      static FILE * fp = popen ("gnuplot 2> /dev/null", "w");
      if (i == 0)
        fputs ("set term x11\n", fp);
      fprintf (fp,
    	   "set title 'nl = %d, h0 = %g, t = %.2f'\n"
    	   "unset key\n"
    	   "p []'-' u 1:4 w l, '' u 1:6 w l, '' u 1:8 w l, '' u 1:10 w l\n",
    	   // "p []'-' u 1:2 w l\n",
    	   nl, h0, t);
      foreach() {
        double etap = zb[];
        foreach_layer()
          etap += h[];
        fprintf (fp, "%g %g", x, eta[] - etap);
        //    eta[] = etap;
        foreach_layer()
          fprintf (fp, " %g %g", h[], u.x[]);
        fprintf (fp, "\n");
      }
      fprintf (fp, "e\n\n");
      fflush (fp);
    
      fprintf (stderr, "%g %g\n", t, statsf(eta).max); 
    }
    #endif

    We output the wave period and relative energy variation and compute the relative error emax.

    event end (t = end)
    {
      fprintf (stderr, "%g %g %g %g %d %d %d\n",
    	   h0, sqrt(tanh(h0)), 2.*pi/(Tm/nm), (Ee - Es)/Es,
    #if !ML
    	   mgD.i, mgD.nrelax
    #else	   
    	   mgp.i, mgp.nrelax
    #endif
    	   , i);
      fflush (stderr);
      emax = 2.*pi/(Tm/nm)/sqrt(tanh(h0));
    }

    We run for several numbers of layers and many water depths, stopping when the error becomes too large.

    int main()
    {
      periodic (right);
      size (2.*pi);
      DT = HUGE [0]; // dimensionless time
      N = 128;
    #if ML
      CFL_H = 0.5;
      TOLERANCE = 1e-6;
      linearised = true;
    #if 1
      for (nl = 2; nl <= 10; nl += 2) //Rui: doubled compared to single phase
    #else
      nl = 4;
    #endif
      {
        char name[80];
        sprintf (name, "log-%d", nl);
        freopen (name, "w", stderr);
        emax = 1.;
        DT = 2.*pi/sqrt(tanh(h0))/200./nl/2.; //Rui: changed for DT test
        for (int l = 0; l < nl; l++)   //Rui: update the density profile
          rho[l] = l < nl/2 ? 1. : 0.001;    
    #if 1
        for (h0 = 0.1; h0 <= 100. && emax > 0.96; h0 *= 1.3)
    #else
        h0 = 0.37;
    #endif
        {
          run();
        }
      }
    #if 0
      freopen ("log-opt", "w", stderr);
      nl = 3;
      emax = 1.;
      for (h0 = 0.1; h0 <= 100. && emax > 0.96; h0 *= 1.3) {
        emax = 1.5;
        run();
      }
    #endif
    #else
      alpha_d = 1.159;
      for (h0 = 0.1; h0 <= 100. && emax > 0.96 && emax < 1.04; h0 *= 1.2)
        run();
    #endif
    }

    Note that the standard Green-Naghdi model has a [2,2] Padé approximant dispersion relation (see e.g. Clamond et al. (2017)). The same as Nwogu (1993). Here we used the optimized version of Chazel et al. (2011).

    The discrete dispersion relations can be computed using this Maxima script.

    g = 1.
    k = 1.
    
    omega1_keller(h_0)=sqrt((4*h_0*g*k**2)/(h_0**2*k**2+4))
    
    omega2_keller(h_0,h_1)=sqrt(((4*h_0*h_1**2+4*h_0**2*h_1)*g*k**4+(16*h_1+16*h_0)*g*k**2)/(h_0**2*h_1**2*k**4+(4*h_1**2+16*h_0*h_1+4*h_0**2)*k**2+16))
    
    omega3_keller(h_0,h_1,h_2)=sqrt((((4*h_0*h_1**2+4*h_0**2*h_1)*h_2**2+4*h_0**2*h_1**2*h_2)*g*k**6+((16*h_1+16*h_0)*h_2**2+(16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2+16*h_0*h_1**2+16*h_0**2*h_1)*g*k**4+(64*h_2+64*h_1+64*h_0)*g*k**2)/(h_0**2*h_1**2*h_2**2*k**6+((4*h_1**2+16*h_0*h_1+4*h_0**2)*h_2**2+(16*h_0*h_1**2+16*h_0**2*h_1)*h_2+4*h_0**2*h_1**2)*k**4+(16*h_2**2+(64*h_1+64*h_0)*h_2+16*h_1**2+64*h_0*h_1+16*h_0**2)*k**2+64))
    
    omega4_keller(h_0,h_1,h_2,h_3)=sqrt(((((4*h_0*h_1**2+4*h_0**2*h_1)*h_2**2+4*h_0**2*h_1**2*h_2)*h_3**2+4*h_0**2*h_1**2*h_2**2*h_3)*g*k**8+(((16*h_1+16*h_0)*h_2**2+(16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2+16*h_0*h_1**2+16*h_0**2*h_1)*h_3**2+((16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2**2+(64*h_0*h_1**2+64*h_0**2*h_1)*h_2+16*h_0**2*h_1**2)*h_3+(16*h_0*h_1**2+16*h_0**2*h_1)*h_2**2+16*h_0**2*h_1**2*h_2)*g*k**6+((64*h_2+64*h_1+64*h_0)*h_3**2+(64*h_2**2+(256*h_1+256*h_0)*h_2+64*h_1**2+256*h_0*h_1+64*h_0**2)*h_3+(64*h_1+64*h_0)*h_2**2+(64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2+64*h_0*h_1**2+64*h_0**2*h_1)*g*k**4+(256*h_3+256*h_2+256*h_1+256*h_0)*g*k**2)/(h_0**2*h_1**2*h_2**2*h_3**2*k**8+(((4*h_1**2+16*h_0*h_1+4*h_0**2)*h_2**2+(16*h_0*h_1**2+16*h_0**2*h_1)*h_2+4*h_0**2*h_1**2)*h_3**2+((16*h_0*h_1**2+16*h_0**2*h_1)*h_2**2+16*h_0**2*h_1**2*h_2)*h_3+4*h_0**2*h_1**2*h_2**2)*k**6+((16*h_2**2+(64*h_1+64*h_0)*h_2+16*h_1**2+64*h_0*h_1+16*h_0**2)*h_3**2+((64*h_1+64*h_0)*h_2**2+(64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2+64*h_0*h_1**2+64*h_0**2*h_1)*h_3+(16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2**2+(64*h_0*h_1**2+64*h_0**2*h_1)*h_2+16*h_0**2*h_1**2)*k**4+(64*h_3**2+(256*h_2+256*h_1+256*h_0)*h_3+64*h_2**2+(256*h_1+256*h_0)*h_2+64*h_1**2+256*h_0*h_1+64*h_0**2)*k**2+256))
    
    omega5_keller(h_0,h_1,h_2,h_3,h_4)=sqrt((((((4*h_0*h_1**2+4*h_0**2*h_1)*h_2**2+4*h_0**2*h_1**2*h_2)*h_3**2+4*h_0**2*h_1**2*h_2**2*h_3)*h_4**2+4*h_0**2*h_1**2*h_2**2*h_3**2*h_4)*g*k**10+((((16*h_1+16*h_0)*h_2**2+(16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2+16*h_0*h_1**2+16*h_0**2*h_1)*h_3**2+((16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2**2+(64*h_0*h_1**2+64*h_0**2*h_1)*h_2+16*h_0**2*h_1**2)*h_3+(16*h_0*h_1**2+16*h_0**2*h_1)*h_2**2+16*h_0**2*h_1**2*h_2)*h_4**2+(((16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2**2+(64*h_0*h_1**2+64*h_0**2*h_1)*h_2+16*h_0**2*h_1**2)*h_3**2+((64*h_0*h_1**2+64*h_0**2*h_1)*h_2**2+64*h_0**2*h_1**2*h_2)*h_3+16*h_0**2*h_1**2*h_2**2)*h_4+((16*h_0*h_1**2+16*h_0**2*h_1)*h_2**2+16*h_0**2*h_1**2*h_2)*h_3**2+16*h_0**2*h_1**2*h_2**2*h_3)*g*k**8+(((64*h_2+64*h_1+64*h_0)*h_3**2+(64*h_2**2+(256*h_1+256*h_0)*h_2+64*h_1**2+256*h_0*h_1+64*h_0**2)*h_3+(64*h_1+64*h_0)*h_2**2+(64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2+64*h_0*h_1**2+64*h_0**2*h_1)*h_4**2+((64*h_2**2+(256*h_1+256*h_0)*h_2+64*h_1**2+256*h_0*h_1+64*h_0**2)*h_3**2+((256*h_1+256*h_0)*h_2**2+(256*h_1**2+1024*h_0*h_1+256*h_0**2)*h_2+256*h_0*h_1**2+256*h_0**2*h_1)*h_3+(64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2**2+(256*h_0*h_1**2+256*h_0**2*h_1)*h_2+64*h_0**2*h_1**2)*h_4+((64*h_1+64*h_0)*h_2**2+(64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2+64*h_0*h_1**2+64*h_0**2*h_1)*h_3**2+((64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2**2+(256*h_0*h_1**2+256*h_0**2*h_1)*h_2+64*h_0**2*h_1**2)*h_3+(64*h_0*h_1**2+64*h_0**2*h_1)*h_2**2+64*h_0**2*h_1**2*h_2)*g*k**6+((256*h_3+256*h_2+256*h_1+256*h_0)*h_4**2+(256*h_3**2+(1024*h_2+1024*h_1+1024*h_0)*h_3+256*h_2**2+(1024*h_1+1024*h_0)*h_2+256*h_1**2+1024*h_0*h_1+256*h_0**2)*h_4+(256*h_2+256*h_1+256*h_0)*h_3**2+(256*h_2**2+(1024*h_1+1024*h_0)*h_2+256*h_1**2+1024*h_0*h_1+256*h_0**2)*h_3+(256*h_1+256*h_0)*h_2**2+(256*h_1**2+1024*h_0*h_1+256*h_0**2)*h_2+256*h_0*h_1**2+256*h_0**2*h_1)*g*k**4+(1024*h_4+1024*h_3+1024*h_2+1024*h_1+1024*h_0)*g*k**2)/(h_0**2*h_1**2*h_2**2*h_3**2*h_4**2*k**10+((((4*h_1**2+16*h_0*h_1+4*h_0**2)*h_2**2+(16*h_0*h_1**2+16*h_0**2*h_1)*h_2+4*h_0**2*h_1**2)*h_3**2+((16*h_0*h_1**2+16*h_0**2*h_1)*h_2**2+16*h_0**2*h_1**2*h_2)*h_3+4*h_0**2*h_1**2*h_2**2)*h_4**2+(((16*h_0*h_1**2+16*h_0**2*h_1)*h_2**2+16*h_0**2*h_1**2*h_2)*h_3**2+16*h_0**2*h_1**2*h_2**2*h_3)*h_4+4*h_0**2*h_1**2*h_2**2*h_3**2)*k**8+(((16*h_2**2+(64*h_1+64*h_0)*h_2+16*h_1**2+64*h_0*h_1+16*h_0**2)*h_3**2+((64*h_1+64*h_0)*h_2**2+(64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2+64*h_0*h_1**2+64*h_0**2*h_1)*h_3+(16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2**2+(64*h_0*h_1**2+64*h_0**2*h_1)*h_2+16*h_0**2*h_1**2)*h_4**2+(((64*h_1+64*h_0)*h_2**2+(64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2+64*h_0*h_1**2+64*h_0**2*h_1)*h_3**2+((64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2**2+(256*h_0*h_1**2+256*h_0**2*h_1)*h_2+64*h_0**2*h_1**2)*h_3+(64*h_0*h_1**2+64*h_0**2*h_1)*h_2**2+64*h_0**2*h_1**2*h_2)*h_4+((16*h_1**2+64*h_0*h_1+16*h_0**2)*h_2**2+(64*h_0*h_1**2+64*h_0**2*h_1)*h_2+16*h_0**2*h_1**2)*h_3**2+((64*h_0*h_1**2+64*h_0**2*h_1)*h_2**2+64*h_0**2*h_1**2*h_2)*h_3+16*h_0**2*h_1**2*h_2**2)*k**6+((64*h_3**2+(256*h_2+256*h_1+256*h_0)*h_3+64*h_2**2+(256*h_1+256*h_0)*h_2+64*h_1**2+256*h_0*h_1+64*h_0**2)*h_4**2+((256*h_2+256*h_1+256*h_0)*h_3**2+(256*h_2**2+(1024*h_1+1024*h_0)*h_2+256*h_1**2+1024*h_0*h_1+256*h_0**2)*h_3+(256*h_1+256*h_0)*h_2**2+(256*h_1**2+1024*h_0*h_1+256*h_0**2)*h_2+256*h_0*h_1**2+256*h_0**2*h_1)*h_4+(64*h_2**2+(256*h_1+256*h_0)*h_2+64*h_1**2+256*h_0*h_1+64*h_0**2)*h_3**2+((256*h_1+256*h_0)*h_2**2+(256*h_1**2+1024*h_0*h_1+256*h_0**2)*h_2+256*h_0*h_1**2+256*h_0**2*h_1)*h_3+(64*h_1**2+256*h_0*h_1+64*h_0**2)*h_2**2+(256*h_0*h_1**2+256*h_0**2*h_1)*h_2+64*h_0**2*h_1**2)*k**4+(256*h_4**2+(1024*h_3+1024*h_2+1024*h_1+1024*h_0)*h_4+256*h_3**2+(1024*h_2+1024*h_1+1024*h_0)*h_3+256*h_2**2+(1024*h_1+1024*h_0)*h_2+256*h_1**2+1024*h_0*h_1+256*h_0**2)*k**2+1024))
    
    alpha=1.159
    omega_gn(k,h)=sqrt(g*h*k**2*(1.+(alpha-1.)*(k*h)**2/3.)/(1.+alpha*(k*h)**2/3.))
    
    set xlabel 'kH'
    set ylabel 'c/c_e'
    set key bottom left
    set xr [0.1:100]
    set yr [0.96:1.04]
    set logscale x
    set grid
    plot  omega1_keller(x)/sqrt(tanh(x)) t '1 layer', \
          'log-1' u ($1):($3/sqrt(tanh($1))) t '' pt 5, \
          omega2_keller(x/2.,x/2.)/sqrt(tanh(x)) t '2 layers', \
          'log-2' u ($1):($3/sqrt(tanh($1))) t '' pt 6, \
          omega3_keller(x/3.,x/3.,x/3.)/sqrt(tanh(x)) t '3 layers', \
          'log-3' u ($1):($3/sqrt(tanh($1))) t '' pt 9, \
          omega4_keller(x/4.,x/4.,x/4.,x/4.)/sqrt(tanh(x)) t '4 layers', \
          'log-4' u ($1):($3/sqrt(tanh($1))) t '' pt 10, \
          omega5_keller(x/5.,x/5.,x/5.,x/5.,x/5.)/sqrt(tanh(x)) t '5 layers', \
          'log-5' u ($1):($3/sqrt(tanh($1))) t '' pt 13, \
          omega3_keller(0.68*x,0.265*x,0.055*x)/sqrt(tanh(x)) \
              t 'Optimised 3 layers' lc 0, \
          'log-opt' u ($1):($3/sqrt(tanh($1))) t '' pt 14 lc 0, \
          omega_gn(1,x)/sqrt(tanh(x)) t 'Green-Naghdi (1 layer)' lc 4, \
          '../dispersion-gn/log' u ($1):($3/sqrt(tanh($1))) pt 15 t ''
    Dispersion relation (script)
    set ylabel 'Energy variation per period (%)'
    set yr [*:*]
    set key top left
    plot  'log-1' u ($1):($4*10.) t '1 layer' pt 5 lt 2, \
          'log-2' u ($1):($4*10.) t '2 layers' pt 6 lt 4, \
          'log-3' u ($1):($4*10.) t '3 layers' pt 9 lt 6, \
          'log-4' u ($1):($4*10.) t '4 layers' pt 10 lt 8, \
          'log-5' u ($1):($4*10.) t '5 layers' pt 13 lt 10, \
          'log-opt' u ($1):($4*10.) t 'Optimised 3 layers' pt 14 lc 0, \
          '../dispersion-gn/log' u ($1):($4*10.) pt 15 lt 14 t 'Optimised Green-Naghdi'
    Relative energy evolution (script)
    reset
    g = 1
    k = 1
    A = 1e-3
    omega(H) = sqrt(g*H*k**2*tanh(k*H)/(k*H))
    wmax(H) = A*H*g*k/omega(H)*sinh(k*H)/cosh(k*H)
    ws(x,z,t,H) = - A*H*g*k/omega(H)*sinh(k*z)/cosh(k*H)*sin(omega(H)*t - k*x)
    set xlabel 'z/H'
    set ylabel 'w/w_{max}'
    set key top left
    range = "1.06045 2.32981 5.11859 24.7065"
    plot [0:1][:1]							   \
      for [H in range]						   \
        ws(pi,x*H,9.25*2.*pi/omega(H),H)/wmax(H) w l t 'kH = '.H,	   \
      for [H in range]						   \
        'profile-'.H u ($1/H):($2/wmax(H)) w p t ''
    Vertical profiles of vertical velocity at t=37/4T (script)

    References

    [clamond2017]

    Didier Clamond, Denys Dutykh, and Dimitrios Mitsotakis. Conservative modified serre–green–naghdi equations with improved dispersion characteristics. Communications in Nonlinear Science and Numerical Simulation, 45:245–257, 2017.

    [chazel2011]

    Florent Chazel, David Lannes, and Fabien Marche. Numerical simulation of strongly nonlinear and dispersive waves using a green–naghdi model. Journal of Scientific Computing, 48(1-3):105–116, 2011.

    [nwogu1993]

    Okey Nwogu. Alternative form of boussinesq equations for nearshore wave propagation. Journal of waterway, port, coastal, and ocean engineering, 119(6):618–638, 1993.