sandbox/kaitaotang/rmst.c

    Compressible Richtmyer-Meshkov instability with surface tension

    We use the compressible solver with VOF interface tracking and surface tension developed in Fuster and Popinet (2018).

    #include "grid/multigrid.h"
    #include "two-phase-compressible.h"
    #include "compressible-tension.h"

    Set length scale and resolution for the problem.

    #define D 1.0
    #define LEVEL 9

    Define Mach number and Weber numbers.

    #define M 1.2
    #define We 6

    Some useful reference quantities.

    double Ms = M;
    double rhoR = 0.1;
    double rhoW = 1;
    double pR = 1.;
    double tend = 4;
    double gam = 1.4;

    Calculate the postshock conditions based on the Mach number.

    double prat(double Ms, double gam){
      return 2.*gam*sq(Ms)/(gam+1.) - (gam-1.)/(gam+1.);
    }
    
    double rhorat(double Ms, double gam){
      return (gam + 1.)*sq(Ms)/((gam - 1.)*sq(Ms) + 2.);
    }
    
    double urat(double Ms, double gam){
      return 1./rhorat(Ms, gam);
    }
    
    double us(double Ms, double gam, double rhoR, double pR){
      return Ms*sqrt(gam*pR/rhoR);
    }

    Some useful fields.

    FILE * fp = NULL;
    
    double rho1, rho2, A, dv;
    double p0R = 1.;
    
    double rhoL, uL, pL, ushock, qL, EL, eta0, l, x0;
    scalar denslap[];
    vector densgrad[];
    scalar adensgrad[];
    scalar schl[];
    scalar totdens[];
    scalar vort[];

    CFL number needs to be reduced to 0.25 for obtaining physical results at low Mach numbers.

    double CFLac = 0.25;

    Make sure we meet the CFL condition by correctly estimating maximum signal speeds (|u|+|c|).

    event stability (i++) {
      stats pstats = statsf(p);
      stats fr1stats = statsf(frho1);
      stats qstats = statsf(q.x);
      
      double cson = fmax(sqrt(gamma1*(pstats.max)/rhoL), sqrt(gamma2*(pstats.max + PI2)/rhoW));
      double u_c = fmax(fabs(qstats.max/rhoL), fabs(qstats.max/rhoW));
    
      foreach () {
          DT    = L0*CFLac/(cson + u_c)/pow(2,LEVEL);
          dtmax = L0*CFLac/(cson + u_c)/pow(2,LEVEL);
      }
    }

    Boundary conditions.

    p[left] = dirichlet(pL);
    frho1[left] = dirichlet(rhoL);
    q.n[left] = dirichlet(qL);
    q.t[left] = dirichlet(0.);
    fE1[left] = dirichlet(EL);
    q.n[bottom] = neumann(0.);
    q.n[top] = neumann(0.);
    p[top] = neumann(0.);
    p[bottom] = neumann(0.);
    frho1[top] = neumann(0.);
    frho1[bottom] = neumann(0.);

    Main routine, set all the relevant parameters and domain size, etc.

    int main() {
      gamma1 = gam;
      gamma2 = gam;
      PI2 = 0.;
      l=D;
    
      eta0= 0.01*l;
      rhoL = rhorat(Ms, gam)*rhoR;
      pL = pR*prat(Ms, gam);
      ushock = us(Ms, gam, rhoR, pR);
      uL = ushock*(1.-urat(Ms, gam));
      qL = uL*rhoL;
      EL = pL/(gam-1.) + 0.5*sq(qL)/rhoL;
      fprintf(ferr, "%g %g %g %g\n", pL, rhoL, qL, EL);
      periodic(top);

    Size of the domain:

      size (11.*D);
      x0=-eta0-L0/324; origin (x0, -L0/11.);

    The density is variable.

      rho1 = rhoR;
      rho2 = rhoW;
      A=(rho2-rho1)/(rho2+rho1);

    Set viscosity (mu1, mu2) and surface tension (f.sigma) via pre-shock Weber numbers.

      dv = Ms*sqrt(gam*pR/rhoR)-sqrt(gam*pR*prat(Ms,gam)/(rhoR*rhorat(Ms,gam)))*sqrt(((gam-1)*sq(Ms)+2)/(2*gam*sq(Ms)-(gam-1))); 
      fprintf(ferr, "dv=%g\n", dv);
      f.sigma = (rho1+rho2)*sq(A*dv)*eta0/We;
    
      mu1=0; mu2=0;
      f.gradient = frho1.gradient = frho2.gradient = p.gradient = q.x.gradient = q.y.gradient = minmod;
    
      N = (1 << LEVEL)*11;
      fprintf(ferr, "uL=%g %g\n", uL, ushock);

    We open a file indexed by the level to store the time evolution of the kinetic energy.

      char name[80];
      sprintf (name, "k-%d", LEVEL);
      fp = fopen (name, "w");
      run();
      fclose (fp);

    We use grep to filter the lines generated by gnuplot containing the results of the fits (see below).

      system ("grep ^fit out >> log");
    }
    
    event init (i = 0) {
      fprintf(ferr, "start\n");   
      fprintf(ferr, "l=%g\n", l); fprintf(ferr, "L0=%g\n", L0); fprintf(ferr, "sigma=%g\n", f.sigma);
      fprintf(ferr, "mu=%g\n", mu1);

    We initialise the shape of the interface.

      fraction (f, -x+eta0*cos(2*pi*y/l) );
      boundary({f});
    
      foreach() {
        double pLap = p0R;
        double m = (x < -7.);
        p[] = f[]*(m*pL + (1. - m)*pR) + (1.-f[])*pLap;
        frho1[] = f[]*(m*rhoL + (1. - m)*rhoR);
        frho2[] = (1. - f[])*rhoW;
        q.x[] = m*frho1[]*uL;
        q.y[] = 0.;
        fE1[] = f[]*(p[]/(gam - 1.) + 0.5*pow(q.x[],2)/rhoL);
        fE2[] = (1.-f[])*(pLap + PI2*gamma2)/(gamma2-1.);
      }
      boundary ((scalar *){q.x, q.y, frho1, frho2, p, fE1, fE2});
    }

    At each timestep we output the kinetic energy.

    //event logfile (i++; t <= 3.0) {
    event logfile (i++; t <= 12.0) {
      stats pstats = statsf(p);
      double ke = 0.;
      foreach (reduction(+:ke))
        ke += sq(Delta)*(sq(q.x[]) + sq(q.y[]))/(frho1[]+frho2[]);
      fprintf (fp, "%g %g %g %d\n", t, ke, mgp.i, pstats.max);
      fflush (fp);
    }
    
    event outputInterface(t += 0.02) {
      char resultname[100];
      sprintf( resultname, "results%4.2f_%d.dat", t, pid() );
      FILE * fp = fopen(resultname, "w");
      scalar xpos[];
      scalar ypos[];
      position (f, xpos, {1, 0});
      position (f, ypos, {0, 1});
      foreach()
          if (xpos[] != nodata)
            fprintf (fp, "%g %g\n", xpos[], ypos[]);
      fclose (fp);
    }
    
    event output (t = 0.00; t += 0.01){
      srand(time(NULL));
      scalar vort[];
      vector uu[];
      dump("dump");
      fprintf(ferr, "t=%g\n", t);
    
      
      foreach()
        {
          totdens[] = frho1[] + frho2[];
          vort[] = (q.y[1] - q.y[-1])/(2.*Delta) - (q.x[0,1]-q.x[0,-1])/(2.*Delta);
          foreach_dimension() {
    	    adensgrad[] += sq((totdens[1] - totdens[-1])/(2.*Delta));
          }
          adensgrad[] = sqrt(adensgrad[]);
          denslap[] = -fabs(totdens[1] + totdens[0,1] + totdens[-1] + totdens[0,-1] - 4.*totdens[])/sq(Delta);
        }
      
      stats sadensg = statsf(adensgrad);
      foreach()
        {
          double kscale = 40.;
          schl[] = exp( -kscale * adensgrad[] / sadensg.max);
        }
      
      static FILE * fp = fopen("p.ppm", "w");
      static FILE * fuf = fopen("f.ppm", "w");
      static FILE * fr1 = fopen("frho1.ppm", "w");
      static FILE * fr2 = fopen("frho2.ppm", "w");
      static FILE * fqx = fopen("fqx.ppm", "w");
      static FILE * fe1 = fopen("fe1.ppm", "w");
      static FILE * fe2 = fopen("fe2.ppm", "w"); 
      static FILE * fdl = fopen("fdl.ppm", "w");
      static FILE * fsc = fopen("fsc.ppm", "w");
      static FILE * fom = fopen("fom.ppm", "w");
      output_ppm (p, fp, n=512*11, box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (f, fuf, n=512*11, min=0., max=1., box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (frho1, fr1, n=512*11, box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (frho2, fr2, n=512*11, box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (q.x, fqx, n=512*11, box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (fE1, fe1, n=512*11, box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (fE2, fe2, n=512*11, box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (denslap, fdl, n=512*11, map=gray, box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (schl, fsc, n=512*11, map=gray, box = {{x0,-L0/11},{x0+L0,0}});
      output_ppm (vort, fom, n=512*11, box = {{x0,-L0/11},{x0+L0,0}});
    }
    
    event end (t==tend) {
    }
    
    #if TREE
    event adapt (i++) {
      scalar ffp[];
      foreach()
        ffp[] = 2.*f[] - 1.;
      adapt_wavelet ({ffp, frho1, p}, (double[]){0.01, 0.01, 0.01}, LEVEL, 5);
    }
    
    #endif //TREE