Sucking Interface Problem

    Planar boiling configuration. A thin gas layer is initialized on the left side of the 2D domain. The gas is at saturation temperature and it is in contact with a superheated liquid phase. The temperature gradient between the interface, at saturation temperature, and the surrounding superheated environment leads to the phase change. This boiling problem is similar to the Scriven test case, but with a planar geometry. This test case setup was inspired by Welch and Wilson, 2000, and Zhao et al., 2022. The analytical solution describing the gas layer thickness in time reads:

    \displaystyle \delta(t) = 2\beta\sqrt{\alpha_g t}

    where \alpha_g is the thermal diffusivity, defined as \alpha_g = \lambda_g/\rho_g/cp_g, while \beta is the growth constant, which is obtained as a function of the physical properties of the simulation, as reported in Boyd et al., 2023: \displaystyle \exp (\beta^2) \text{erf} (\beta) \left( \beta - \dfrac{\left(T_{bulk} - T_{sat}\right)c_{p,g}\lambda_l \sqrt{\alpha_g} \exp\left(-\beta^2 \dfrac{\rho_g^2\alpha_g}{\rho_l^2\alpha_l}\right)}{\Delta h_{ev} \lambda_g \sqrt{\pi \alpha_l}\text{erfc}\left( \beta\dfrac{\rho_g\sqrt{\alpha_g}}{\rho_l\sqrt{\alpha_l}} \right)} \right) = 0

    The animation shows the map of the temperature field, which is initialized with the analytic solution at the beginning of the simulation. The velocity field due to the expansion term in the continuity equation points toward the liquid phase. Therefore, this test case considers the additional transport of temperature from the Stefan flow in liquid phase.

    Evolution of the gas layer thickness

    Phase Change Setup

    We move the interface using the velocity uf, with the expansion term shifted toward the gas-phase. In this way uf is divergence-free at the interface. The double pressure velocity couping is used to obtain an extended velocity, used to transport the gas phase tracers.

    #define INT_USE_UF
    #define SHIFT_TO_GAS
    #define INIT_TEMP

    Simulation Setup

    We use the centered solver with the divergence source term, and the extended velocity is obtained using the centered-doubled procedure. The evaporation model is used in combination with the temperature-gradient mechanism, which manages the solution of the temperature field.

    We declare the variables required by the temperature-gradient model.

    double lambda1, lambda2, cp1, cp2, dhev;
    double TL0, TG0, TIntVal, Tsat, Tbulk;

    Boundary conditions

    Outflow boundary conditions for velocity and pressure are imposed on the right wall, while the temperature is set to the bulk value on the wall in contact with the liquid phase, and to the saturation value on the wall adjacent to the gaseous layer.

    u.n[right] = neumann (0.);
    u.t[right] = neumann (0.);
    p[right] = dirichlet (0.);
    uext.n[right] = neumann (0.);
    uext.t[right] = neumann (0.);
    pext[right] = dirichlet (0.);
    TL[right] = dirichlet (Tbulk);
    TG[left] = dirichlet (Tsat);

    Problem Data

    We declare the maximum and minimum levels of refinement, the \lambda parameter, the initial thickness of the vapor layer.

    int maxlevel, minlevel = 1;
    double tshift, betaGrowth, effective_height0;

    Growth Constant and Analytical Temperature

    We use gsl, and the fsolve.h module, to find the value of the growth constant, which is then used to compute the analytical temperature profile:

    \displaystyle T(x,t) = T_{bulk} - \left(\dfrac{T_{bulk}-T_{sat}}{\text{erfc}\left( \beta \dfrac{\rho_g\sqrt{\alpha_g}}{\rho_l\sqrt{\alpha_l}} \right)} \right) \text{erfc}\left( \dfrac{x}{2\sqrt{\alpha_l t}} + \beta\dfrac{\rho_g - \rho_l}{\rho_l}\sqrt{\dfrac{\alpha_g}{\alpha_k}} \right)

    #define USE_GSL
    #include "fsolve-gsl.h"

    We define the function to zero for the growth constant.

    int betafun (const gsl_vector * x, void * params, gsl_vector * f) {
      double * xdata = x->data;
      double * fdata = f->data;
      double beta = xdata[0];
      double alpha1 = lambda1/rho1/cp1;
      double alpha2 = lambda2/rho2/cp2;
      fdata[0] = exp(sq(beta))*erf(beta)*(beta -
              ( (Tbulk - Tsat)*cp2*lambda1*sqrt (alpha2)*exp
              (-sq(beta)*sq(rho2)*alpha2/sq(rho1)/alpha1) ) /
      return GSL_SUCCESS;

    We define the function for the exact temperature field.

    double tempexact (double x, double beta, double t) {
      double alpha1 = lambda1/rho1/cp1;
      double alpha2 = lambda2/rho2/cp2;
      return Tbulk - ((Tbulk - Tsat)/erfc(beta*rho2*sqrt(alpha2)/rho1/sqrt(alpha1)))
          * erfc (x/2./sqrt(alpha1*t) + beta*(rho2 - rho1)/rho1*sqrt(alpha2/alpha1));
    int main (void) {

    We set the material properties of the fluids.

      rho1 = 958.4, rho2 = 0.597;
      mu1 = 2.80e-4, mu2 = 1.26e-5;
      lambda1 = 0.679, lambda2 = 0.025;
      cp1 = 4216., cp2 = 2030.;
      dhev = 2.26e+6;

    We set the bulk and the saturation temperature values. The interface must remain at saturation temperature, while the values TL0 an TG0 are used to set the initial and boundary conditions.

      Tbulk = 378.15, Tsat = 373.15, TIntVal = Tsat;
      TL0 = Tbulk, TG0 = TIntVal;

    We change the dimension of the domain, and the surface tension coefficient.

      L0 = 10.e-3;
      f.sigma = 0.0059;

    We run the simulation at four different levels of refinement.

      for (maxlevel = 6; maxlevel <= 8; maxlevel++) {
        init_grid (1 << maxlevel);
    event init (i = 0) {

    The gas phase layer thickness is initially set to 0.476 mm.

      fraction (f, (x - 0.476e-3));
      effective_height0 = L0 - 1./L0*statsf(f).sum;

    We use the root finding procedure implemented in gsl_multiroots for solving the transcendental equation to find the growth constant.

      Array * arrUnk = array_new();
        double betafg = 0.9;
        array_append (arrUnk, &betafg, sizeof(double));
        double * unks = (double *)arrUnk->p;
        fsolve (betafun, arrUnk, NULL);
        betaGrowth = unks[0];
      array_free (arrUnk);

    We find the time of the analytical solution corresponding to the value of the vapor layer thickness that we just initialized.

      tshift = rho2*cp2/lambda2*sq (effective_height0/2./betaGrowth);

    We initialize the temperature field, setting the liquid phase temperature to the analytical value. We also set an initial value for the velocity field to speed up convergence of the Poisson equation at the first iteration.

      foreach() {
        TL[] = f[]*tempexact (x, betaGrowth, t+tshift);
        TG[] = (1. - f[])*Tsat;
        T[]  = TL[] + TG[];
        u.x[] = 11.e-3*f[];
        //u.x[] = 0.;
        u.y[] = 0.;

    We refine the domain according to the interface, velocity, and the temperature field.

    #if TREE
    event adapt (i++) {
      double uemax = 1e-2;
      adapt_wavelet_leave_interface ({T,u.x,u.y}, {f},
          (double[]){1e-3,uemax,uemax}, maxlevel, minlevel, 1);


    The following lines of code are for post-processing purposes.

    Exact Solution

    We write a function that computes the exact solution for the thickness of the gas phase layer in time.

    double exact (double time) {
      return 2.*betaGrowth*sqrt(lambda2/rho2/cp2*time);

    Output Files

    We write the thickness of the gas layer from this simulation, the exact solution, and the relative error.

    event output (t += 0.01) {
      double effective_height = L0 - 1./L0*statsf(f).sum;
      double relerr = fabs (exact(t+tshift) - effective_height) / exact(t+tshift);
      char name[80];
      sprintf (name, "OutputData-%d", maxlevel);
      static FILE * fp = fopen (name, "w");
      // Average velocity
      double uavg = 0., favg = 0.;
      foreach(reduction(+:uavg) reduction(+:favg)) {
        double ux = 0.5*(uf.x[] + uf.x[1]);
        uavg += f[]*ux;
        favg += f[];
      uavg /= favg;
      fprintf (fp, "%g %g %g %g %g\n", t + tshift, effective_height, exact (t+tshift), relerr, uavg);
      fflush (fp);

    Temperature Profile

    We write on a file the temperature profile at the final time step.

    event profiles (t = end) {
      char name[80];
      sprintf (name, "Temperature-%d", maxlevel);

    We create an array with the temperature profile for each processor.

      Array * arrtemp = array_new();
      for (double x = 0.; x < L0; x += 0.5*L0/(1 << maxlevel)) {
        double val = interpolate (T, x, 0.5*L0);
        val = (val == nodata) ? 0. : val;
        array_append (arrtemp, &val, sizeof(double));
      double * temps = (double *)arrtemp->p;

    We sum each element of the arrays in every processor.

      @if _MPI
      int size = arrtemp->len/sizeof(double);
      MPI_Allreduce (MPI_IN_PLACE, temps, size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);

    The master node writes the temperature profile.

      if (pid() == 0) {
        FILE * fpp = fopen (name, "w");
        int count = 0;
        for (double x = 0; x < L0; x += 0.5*L0/(1 << maxlevel)) {
          double tempanal = tempexact (x, betaGrowth, t+tshift);
          double radius = exact (t+tshift);
          tempanal = (x <= radius) ? Tsat : tempanal;
          fprintf (fpp, "%g %g %g\n", x, temps[count], tempanal);
        fflush (fpp);
        fclose (fpp);
      array_free (arrtemp);


    We write the animation with the evolution of the temperature field, and the gas-liquid interface. We reduce the total simulation time in order to speed up the simulation on the basilisk server.

    //event movie (t += 0.01; t <= 1) {
    event movie (t += 0.01; t <= 0.2) {
      view (tx = -0.5, ty = -0.5);
      draw_vof ("f", lw = 1.5);
      squares ("T", min = Tsat, max = Tbulk, linear = true);
      save ("movie.mp4");


    set xlabel "t [s]"
    set ylabel "Vapor Layer Thickness [m]"
    set key top left
    set size square
    set grid
    plot "OutputData-8" every 1 u 1:3 w p ps 2 t "Analytic", \
         "OutputData-6" u 1:2 w l lw 2 t "LEVEL 6", \
         "OutputData-7" u 1:2 w l lw 2 t "LEVEL 7", \
         "OutputData-8" u 1:2 w l lw 2 t "LEVEL 8"
    Evolution of the vapor layer thickness (script)

    Evolution of the vapor layer thickness (script)

    stats "OutputData-6" using (last6=$4) nooutput
    stats "OutputData-7" using (last7=$4) nooutput
    stats "OutputData-8" using (last8=$4) nooutput
    set print "Errors.csv"
    print sprintf ("%d %.12f", 2**6, last6)
    print sprintf ("%d %.12f", 2**7, last7)
    print sprintf ("%d %.12f", 2**8, last8)
    unset print
    set xlabel "N"
    set ylabel "Relative Error"
    set logscale x 2
    set logscale y
    set xr[2**5:2**9]
    set yr[1e-3:1]
    set size square
    set grid
    plot "Errors.csv" w p pt 8 ps 2 title "Results", \
      30*x**(-1) lw 2 title "1^{st} order", \
      600*x**(-2) lw 2 title "2^{nd} order"
    Relative Errors (script)

    Relative Errors (script)

    set xlabel "Radius [m]"
    set ylabel "Temperature [K]"
    set key bottom right
    set size square
    set grid
    set yr[373:379]
    plot "Temperature-8" every 5 u 1:3 w p ps 2 t "Analytic", \
         "Temperature-6" u 1:2 w l lw 2 t "LEVEL 6", \
         "Temperature-7" u 1:2 w l lw 2 t "LEVEL 7", \
         "Temperature-8" u 1:2 w l lw 2 t "LEVEL 8"
    Temperature Profile (script)

    Temperature Profile (script)



    Bradley Boyd and Yue Ling. A consistent volume-of-fluid approach for direct numerical simulation of the aerodynamic breakup of a vaporizing drop. Computers & Fluids, 254:105807, 2023.


    Shuo Zhao, Jie Zhang, and Ming-Jiu Ni. Boiling and evaporation model for liquid-gas flows: A sharp and conservative method based on the geometrical vof approach. Journal of Computational Physics, 452:110908, 2022.


    Samuel WJ Welch and John Wilson. A volume of fluid based method for fluid flows with phase change. Journal of computational physics, 160(2):662–682, 2000.