Menu

simulationCases/dropAtomisation.c

Drop Atomisation

Simulates viscoelastic drop atomisation using the Basilisk two-phase solver and log-conformation rheology.

Author

Ayush Dixit, Vatsal Sanjay Date: Oct 20, 2024

// #include "axi.h"
#include "grid/octree.h"
// #include "grid/quadtree.h"
#include "navier-stokes/centered.h"

#define VANILLA// vanilla cannot do 3D
#if VANILLA
#include "../src-local/log-conform-viscoelastic.h"
#define logFile "logAxi-vanilla.dat"
#else
#if AXI
#include "../src-local/log-conform-viscoelastic-scalar-2D.h"
#define logFile "logAxi-scalar.dat"
#else
#include "../src-local/log-conform-viscoelastic-scalar-3D.h"
#define logFile "log3D-scalar.dat"
#endif
#endif

#define FILTERED // Smear density and viscosity jumps

#include "../src-local/two-phaseVE.h"

#include "navier-stokes/conserving.h"
#include "tension.h"

Output Cadence

#define tsnap (0.1) // 0.001 only for some cases.

Adaptivity Tolerances

  • fErr: VOF error for f
  • KErr: Curvature error (height-function)
  • VelErr: Velocity error
  • AErr: Conformation error in liquid
#define fErr (1e-2)
#define KErr (1e-4)
#define VelErr (1e-2)
#define AErr (1e-3)

#define R2(x,y,z)  (sq(x-3.) + sq(y) + sq(z))

Boundary Conditions

  • Inflow: left
  • Outflow: right
u.n[left]  = dirichlet(1.);
u.n[right] = neumann(0.);
p[right] = dirichlet(0);

int MAXlevel;

Dimensionless Groups

  • We: Weber number
  • Oh: Solvent Ohnesorge number
  • Oha: Air Ohnesorge number
  • De: Deborah number
  • Ec: Elasto-capillary number
double Oh, Oha, De, We, RhoInOut, Ec, tmax;
char nameOut[80], dumpFile[80];

main()

Sets domain parameters, material properties, and launches the run.

int  main(int argc, char const *argv[]) {
  // dtmax = 1e-5; //  BEWARE of this for stability issues.

  L0 = 20;
  init_grid (1 << 6);

  origin (0, -L0/
  #if dimension ==
  , -L0/
  #endif
  );

  // Values taken from the terminal
  MAXlevel = 7;
  RhoInOut = 830.;

  // elastic parts
  De = 0.0;
  Ec = 0.0;

  // Newtonian parts
  We = 15000; // based on the density of the gas
  Oh = 3e-3; // based on the density of the liquid
  Oha = 0.018*Oh; // based on the density of the liquid
  tmax = 200;

  // Create a folder named intermediate where all the simulation snapshots are stored.
  char comm[80];
  sprintf (comm, "mkdir -p intermediate");
  system(comm);
  // Name of the restart file. See writingFiles event.
  sprintf (dumpFile, "restart");


  rho1 = RhoInOut, rho2 = 1e0;

  // as both densities are based on the density of the liquid, we must multiply the Ohnesorge number by the square root of the density ratio
  mu1 = sqrt(RhoInOut)*Oh/sqrt(We), mu2 = sqrt(RhoInOut)*Oha/sqrt(We);

  // elastic parts
  // in G1, we need to mutiply by the density ratio (again, because Ec is based on the density of the liquid but in the code it is based on the density of the gas)
  G1 = Ec/We, G2 = 0.0;
  // Here, lambda is essentially the Weissenberg number, so there is no density in the expression.
  lambda1 = De*sqrt(We), lambda2 = 0.0;

  // surface tension -- the Weber number is based on the density of the gas! So, all good.
  f.sigma = 1.0/We;

  run();
}

event init (t = 0) {
  if (!restore (file = dumpFile)){
    refine(R2(x,y,z) < 1.1 && R2(x,y,z) > 0.9 && level < MAXlevel);
    fraction (f, 1. - R2(x,y,z));
  }
}

Adaptive Mesh Refinement

Refines based on interface, curvature, and velocity errors.

event adapt(i++){
  scalar KAPPA[];
  curvature(f, KAPPA);

  adapt_wavelet ((scalar *){f, KAPPA, u.x, u.y
  #if dimension ==
  ,u.z
  #endif
  },(double[]){fErr, KErr, VelErr, VelErr,
  #if dimension ==
  VelErr
  #endif
  },MAXlevel, 4);
}

Dumping Snapshots

Writes restart and time-stamped snapshot dumps.

event writingFiles (t = 0; t += tsnap; t <= tmax) {
  dump (file = dumpFile);
  sprintf (nameOut, "intermediate/snapshot-%5.4f", t);
  dump(file=nameOut);
}

Ending Simulation

Prints summary parameters at completion.

event end (t = end) {
  if (pid() == 0)
    fprintf(ferr, "Level %d, Oh %2.1e, We %2.1e, Oha %2.1e, De %2.1e, Ec %2.1e\n", MAXlevel, Oh, We, Oha, De, Ec);
}

Log Writing

Tracks kinetic energy and aborts on blow-up or decay.

event logWriting (i++) {

  double ke = 0.;
  foreach (reduction(+:ke)){
    ke += (0.5*rho(f[])*(sq(u.x[]) + sq(u.y[])
   #if dimension ==
    + sq(u.z[])
   #endif
   ))*pow(Delta, dimension);
  }

  static FILE * fp;
  if (pid() == 0) {
    const char* mode = (i == 0) ? "w" : "a";
    fp = fopen(logFile, mode);
    if (fp == NULL) {
      fprintf(ferr, "Error opening log file\n");
      return 1;
    }

    if (i == 0) {
      fprintf(ferr, "Level %d, Oh %2.1e, We %2.1e, Oha %2.1e, De %2.1e, Ec %2.1e\n", MAXlevel, Oh, We, Oha, De, Ec);
      fprintf(ferr, "i dt t ke\n");
      fprintf(fp, "Level %d, Oh %2.1e, We %2.1e, Oha %2.1e, De %2.1e, Ec %2.1e\n", MAXlevel, Oh, We, Oha, De, Ec);
      fprintf(fp, "i dt t ke rM\n");
    }

    fprintf(fp, "%d %g %g %g\n", i, dt, t, ke);
    fprintf(ferr, "%d %g %g %g\n", i, dt, t, ke);

    fflush(fp);
    fclose(fp);
  }

  assert(ke > -1e-10);

  if (i > 1e1 && pid() == 0) {
    if (ke > 1e6 || ke < 1e-6) {
      const char* message = (ke > 1e6) ?
        "The kinetic energy blew up. Stopping simulation\n" :
        "kinetic energy too small now! Stopping!\n";

      fprintf(ferr, "%s", message);

      fp = fopen("log", "a");
      fprintf(fp, "%s", message);
      fflush(fp);
      fclose(fp);

      dump(file=dumpFile);
      return 1;
    }
  }

}