sandbox/ecipriano/src/phasechange.h

    #include "common-evaporation.h"
    #include "phase.h"
    
    scalar mEvapTot[];
    extern scalar * mEvapList;
    
    int NLS = 0, NGS = 0;
    double lambda1 = 1., lambda2 = 1., dhev = 1., cp1 = 1., cp2 = 1.;
    double TG0 = 300., TL0 = 300., TIntVal = 300., Pref = 101325.;
    double Dmix1 = 0., Dmix2 = 1., YIntVal = 0., YG0 = 0., YL0 = 1.;
    double MW1 = 1., MW2 = 1.;
    
    Phase * liq, * gas;
    char ** liq_species = NULL, ** gas_species = NULL;
    
    scalar fu[], fu1[], * fulist = {fu,fu1};  // fixme: fu1 should be dynamic
    scalar T[], Y[];
    
    #include "regression.h"
    
    enum advection_policy {
      NO_ADVECTION, ADVECTION_VELOCITY, SOURCE_TERM, PLANE_SHIFTING
    };
    
    enum shifting_policy {
      NO_SHIFTING, SHIFT_TO_LIQUID, SHIFT_TO_GAS
    };
    
    enum diffusion_policy {
      EXPLICIT_ONLY, EXPLICIT_IMPLICIT
    };
    
    struct PhaseChangeModel {
      enum advection_policy advection;
      enum shifting_policy shifting;
      enum diffusion_policy diffusion;
      bool boiling;
      bool byrhogas;
      bool expansion;
      bool consistent;
      bool isothermal;
      bool isomassfrac;
      bool isothermal_interface;
      bool fick_corrected;
      bool molar_diffusion;
      bool mass_diffusion_enthalpy;
      bool divergence;
      bool chemistry;
      bool normalize;
      double emissivity;
    } pcm = {
      ADVECTION_VELOCITY,         // advection
      SHIFT_TO_LIQUID,            // shifting
      EXPLICIT_IMPLICIT,          // diffusion
      false,                      // boiling
      false,                      // byrhogas
      true,                       // expansion
      false,                      // consistent
      false,                      // isothermal
      false,                      // isomassfrac
      false,                      // isothermal_interface
    #if TWO_PHASE_VARPROP
      true,                       // fick_corrected
      true,                       // molar_diffusion
      true,                       // mass_diffusion_enthalpy
      true,                       // divergence
    #else
      false,                      // fick_corrected
      false,                      // molar_diffusion
      false,                      // mass_diffusion_enthalpy
      false,                      // divergence
    #endif
      false,                      // chemistry
      true,                       // normalize
      0.,                         // emissivity
    };
    
    void intexp_explicit (scalar intexp, scalar f, scalar mEvapTot) {
      scalar rhol = liq->rho, rhog = gas->rho;
      foreach_interfacial_plic (f, F_ERR)
        intexp[] = (rhol[] > 0. && rhog[] > 0.) ?
          mEvapTot[]*(1./rhog[] - 1./rhol[])*dirac : 0.;
    }
    
    static scalar * f_tracers = NULL;
    
    event defaults (i = 0) {
      liq = new_phase ("L", NLS, false, liq_species);
      gas = new_phase ("G", NGS, true, gas_species);
    
      liq->isothermal = pcm.isothermal;
      gas->isothermal = pcm.isothermal;
      liq->isomassfrac = pcm.isomassfrac;
      gas->isomassfrac = pcm.isomassfrac;
    
      double * xl = malloc (liq->n*sizeof (double));
      double * xg = malloc (gas->n*sizeof (double));
      double * Dl = malloc (liq->n*sizeof (double));
      double * Dg = malloc (gas->n*sizeof (double));
      double * dhevsl = malloc (liq->n*sizeof (double));
      double * dhevsg = malloc (gas->n*sizeof (double));
      double * cpsl = malloc (liq->n*sizeof (double));
      double * cpsg = malloc (gas->n*sizeof (double));
      double * MWl = malloc (liq->n*sizeof (double));
      double * MWg = malloc (gas->n*sizeof (double));
    
      foreach_species_in (liq)
        xl[i] = 1.;
      correctfrac (xl, liq->n);
    
      foreach_species_in (gas)
        xg[i] = 1.;
      correctfrac (xg, gas->n);
    
      if (NLS == 1)
        xl[0] = 1.;
    
      if (NGS == 1)
        xg[0] = 1.;
      else if (NGS == 2)
        xg[0] = YG0, xg[1] = 1. - YG0;
    
      ThermoState tsl, tsg;
      tsl.T = TL0, tsl.P = Pref, tsl.x = xl;
      tsg.T = TG0, tsg.P = Pref, tsg.x = xg;
    
      phase_set_thermo_state (liq, &tsl);
      phase_set_thermo_state (gas, &tsg);
    
      foreach_species_in (liq) {
        Dl[i] = Dmix1;
        dhevsl[i] = dhev;
        cpsl[i] = cp1;
        MWl[i] = MW1;
      }
    
      foreach_species_in (gas) {
        Dg[i] = Dmix2;
        dhevsg[i] = dhev;
        cpsg[i] = cp2;
        MWg[i] = MW2;
      }
    
      phase_set_properties (liq,
          rho = rho1, mu = mu1,
          lambda = lambda1, cp = cp1,
          dhev = dhev, dhevs = dhevsl,
          D = Dl, cps = cpsl, MWs = MWl);
    
      phase_set_properties (gas,
          rho = rho2, mu = mu2,
          lambda = lambda2, cp = cp2,
          dhev = dhev, dhevs = dhevsg,
          D = Dg, cps = cpsg, MWs = MWg);
    
      free (xl);
      free (xg);
      free (Dl);
      free (Dg);
      free (dhevsl);
      free (dhevsg);
      free (cpsl);
      free (cpsg);
      free (MWl);
      free (MWg);
    
      phase_set_tracers (liq);
      phase_set_tracers (gas);
    
      if (pcm.consistent) {
        f_tracers = f.tracers;
        // These passages are necessary to avoid memory leaks
        scalar * f_tracers2 = list_concat (f.tracers, liq->tracers);
        f.tracers = list_concat (f_tracers2, gas->tracers);
        free (f_tracers2), f_tracers2 = NULL;
      }
      else {
        if (nv == 1) {
          scalar fug = fulist[0];
          // These passages are necessary to avoid memory leaks
          scalar * fug_tracers = list_concat (fug.tracers, liq->tracers);
          fug.tracers = list_concat (fug_tracers, gas->tracers);
          free (fug_tracers), fug_tracers = NULL;
        }
        else if (nv == 2) {
          scalar ful = fulist[1], fug = fulist[0];
          ful.tracers = list_concat (ful.tracers, liq->tracers);
          fug.tracers = list_concat (fug.tracers, gas->tracers);
        }
      }
    
    #if TREE
      // Only f.tracers are set to the correct refine functions in
      // [vof.h](src/vof.h). Other helper fractions with their tracers must be
      // set here. Do not remove.
      for (scalar c in fulist) {
        c.refine = c.prolongation = fraction_refine;
        c.dirty = true;
        scalar * tracers = c.tracers;
        for (scalar t in tracers) {
          t.restriction = restriction_volume_average;
          t.refine = t.prolongation = vof_concentration_refine;
          t.dirty = true;
          t.c = c;
    
          t.depends = list_add (t.depends, c);
        }
      }
    #endif
    }
    
    event init (i = 0) {
    #if VELOCITY_JUMP
      _boiling = pcm.boiling;
    #endif
    
      if (pcm.divergence)
        no_advection_div = true;
    
      if (nv == 1)
        pcm.consistent = true;
    
      if (phase_is_uniform (liq))
        phase_scalars_to_tracers (liq, f);
      if (phase_is_uniform (gas))
        phase_scalars_to_tracers (gas, f);
    
    #if TWO_PHASE_VARPROP
      event ("phase_properties");
    #endif
    }
    
    event cleanup (t = end) {
      for (scalar fu in fulist)
        free (fu.tracers), fu.tracers = NULL;
    
      if (pcm.consistent)
        free (f.tracers), f.tracers = f_tracers;
    
      delete_phase (liq);
      delete_phase (gas);
    }
    
    event reset_sources (i++) {
      phase_reset_sources (liq);
      phase_reset_sources (gas);
    
      foreach() {
        for (scalar mEvap in mEvapList)
          mEvap[] = 0.;
        for (scalar fu in fulist)
          fu[] = f[];
        for (scalar intexp in intexplist)
          intexp[] = 0.;
        for (scalar drhodt in drhodtlist)
          drhodt[] = 0.;
      }
    }
    
    event end_init (i = 0);
    
    event reset_sources (i++);
    
    // fixme: I should be able to compute MW and Moles for the molar diffusion even
    // if the model is used with constant properties
    #if TWO_PHASE_VARPROP
    event phase_properties (i++) {
      phase_tracers_to_scalars (liq, f, tol = F_ERR);
      phase_tracers_to_scalars (gas, f, tol = F_ERR);
    
      phase_update_mw_moles (liq, f, tol = P_ERR, extend = true);
      phase_update_mw_moles (gas, f, tol = P_ERR, extend = true);
    
      phase_update_properties (liq, &tp1, f, P_ERR);
      phase_update_properties (gas, &tp2, f, P_ERR);
    
      phase_extend_properties (liq, f, P_ERR);
      phase_extend_properties (gas, f, P_ERR);
    
      phase_scalars_to_tracers (liq, f);
      phase_scalars_to_tracers (gas, f);
    }
    #endif
    
    #if CHEMISTRY
    event chemistry (i++) {
      if (pcm.chemistry) {
        ode_function batch = batch_isothermal_constantpressure;
        unsigned int NEQ = gas->n;
        if (!gas->isothermal) {
          batch = batch_nonisothermal_constantpressure;
          NEQ++;
        }
    #if BINNING
        scalar YH2 = lookup_field ("YGCH3OH");
        scalar YO2 = lookup_field ("YO2");
        scalar YCO = lookup_field ("YCO");
        scalar T = gas->T;
    
        double eps = 1e-2;
        phase_chemistry_binning (gas, dt, batch, NEQ, {T,YH2,YO2,YCO},
            (double[]){eps,eps,eps,eps,eps}, verbose = true, f = f, tol = 1-F_ERR);
    
        //phase_chemistry_binning (gas, dt, batch, NEQ, {T,Y}, (double[]){1e-2,1e-2},
        //    verbose = true, f = f, tol = 1-F_ERR);
    #else
        phase_chemistry_direct (gas, dt, batch, NEQ, f, tol = 1-F_ERR);
    #endif
      }
    }
    #endif
    
    event phasechange (i++) {
      foreach() {
        mEvapTot[] = 0.;
        for (scalar mEvap in mEvapList)
          mEvapTot[] += mEvap[];
      }
    #if VELOCITY_JUMP
      scalar rhol = liq->rho, rhog = gas->rho;
      foreach()
        jump[] = (rhol[] > 0. && rhog[] > 0.) ?
          -mEvapTot[]*(1./rhol[] - 1./rhog[]) : 0.;
    
      scalar fext[];
      foreach()
        fext[] = f[];
      vof_to_ls (fext, ls, imax = 5);
    
      constant_extrapolation (jump, ls, 0.5, 10, c=fext, nl=0,
                nointerface=true, inverse=false, tol=1e-4);
      constant_extrapolation (jump, ls, 0.5, 10, c=fext, nl=0,
                nointerface=true, inverse=true, tol=1e-4);
    
      foreach_face()
        jumpf.x[] = face_value (jump, 0);
    #else
      scalar intexp = (pcm.boiling && nv > 1) ? intexplist[1] : intexplist[0];
      if (pcm.expansion) {
        intexp_explicit (intexp, f, mEvapTot);
    
        switch (pcm.shifting) {
          case NO_SHIFTING: break;
          case SHIFT_TO_LIQUID: shift_field (intexp, f, 1); break;
          case SHIFT_TO_GAS: shift_field (intexp, f, 0); break;
        }
      }
    #endif
    }
    
    #if TWO_PHASE_VARPROP
    event divergence (i++) {
      if (pcm.divergence) {
        phase_tracers_to_scalars (liq, f, tol = F_ERR);
        phase_tracers_to_scalars (gas, f, tol = F_ERR);
    
        //// [DIFF] Let's perform the velocity correction step
        //phase_diffusion_velocity (liq, f, pcm.fick_corrected, pcm.molar_diffusion);
        //phase_diffusion_velocity (gas, f, pcm.fick_corrected, pcm.molar_diffusion);
    
        phase_update_divergence (liq, f, pcm.fick_corrected, pcm.molar_diffusion);
        phase_update_divergence (gas, f, pcm.fick_corrected, pcm.molar_diffusion);
    
    #if 0
        vector ul = (nv > 1) ? ulist[1] : ulist[0];
        vector ug = ulist[0];
    
        face vector ufl = (nv > 1) ? uflist[1] : uflist[0];
        face vector ufg = uflist[0];
    
        phase_update_divergence_density (liq, ul, ufl, f);
        phase_update_divergence_density (gas, ug, ufg, f);
    #endif
    
        scalar divu1 = liq->divu, divu2 = gas->divu;
        foreach()
          drhodt[] = divu1[] + divu2[];
    
        if (nv > 1) {
          scalar drhodt1 = drhodtlist[1];
          foreach()
            drhodt1[] = divu1[];
        }
    
        phase_scalars_to_tracers (liq, f);
        phase_scalars_to_tracers (gas, f);
      }
    }
    #endif
    
    face vector ufsave[];
    
    event vof (i++) {
      // Transport due to the phase change
      // fixme: add consistency also for source and plane shifting
      scalar rhoh = pcm.byrhogas ? gas->rho : liq->rho;
      switch (pcm.advection) {
        case NO_ADVECTION:
          break;
        case ADVECTION_VELOCITY:
          vof_advection_phasechange (f, mEvapTot, rhoh, i);
          break;
        case SOURCE_TERM:
          vof_expl_sources (f, mEvapTot, rhoh, dt);
          break;
        case PLANE_SHIFTING:
          vof_plane_shifting (f, mEvapTot, rhoh, dt);
      }
    
      // Transport tracers with uf by default (i.e. nv == 1)
      scalar fug = fulist[0];
      vof_advection ({fug}, i);
    
      // Transport tracers with liquid velocity
      if (nv == 2) {
        scalar ful = fulist[1];
        face vector ufl = uflist[1];
        foreach_face() {
          ufsave.x[] = uf.x[];
          uf.x[] = ufl.x[];
        }
        vof_advection ({ful}, i);
      }
    }
    
    event vof_sources (i++) {
      if (nv == 2)
        foreach_face()
          uf.x[] = ufsave.x[];
    
      foreach() {
        f[] = clamp (f[], 0., 1.);
        f[] = (f[] < F_ERR) ? 0. : (f[] > 1.-F_ERR) ? 1. : f[];
      }
    
    #if TREE
      for (int l = 1; l < depth(); l++)
        foreach_level (l)
          f[] = (f[] > 0.5) ? 1. : 0.;
    #endif
    }
    
    event tracer_advection (i++);
    
    event tracer_diffusion (i++) {
    
      if (pcm.consistent) {
        phase_tracers_to_scalars (liq, f, tol = F_ERR);
        phase_tracers_to_scalars (gas, f, tol = F_ERR);
      }
      else {
        if (nv == 1) {
          phase_tracers_to_scalars (liq, fu, tol = F_ERR);
          phase_tracers_to_scalars (gas, fu, tol = F_ERR);
        }
        else if (nv == 2) {
          scalar ful = fulist[1], fug = fulist[0];
          phase_tracers_to_scalars (liq, ful, tol = F_ERR);
          phase_tracers_to_scalars (gas, fug, tol = F_ERR);
        }
      }
    
      phase_update_mw_moles (liq, f, tol = P_ERR, extend = true);
      phase_update_mw_moles (gas, f, tol = P_ERR, extend = true);
    
      // [DIFF]
      // Let's perform the velocity correction step
      phase_diffusion_velocity (liq, f, pcm.fick_corrected, pcm.molar_diffusion);
      phase_diffusion_velocity (gas, f, pcm.fick_corrected, pcm.molar_diffusion);
    
    #if TWO_PHASE_VARPROP
      phase_diffusion (gas, f, varcoeff = true);
      phase_diffusion (liq, f, varcoeff = true);
    #else
      phase_diffusion (gas, f, varcoeff = false);
      phase_diffusion (liq, f, varcoeff = false);
    #endif
    
      if (pcm.normalize) {
        phase_normalize_fractions (liq);
        phase_normalize_fractions (gas);
      }
    
      phase_scalars_to_tracers (liq, f);
      phase_scalars_to_tracers (gas, f);
    
      scalar TL = liq->T, TG = gas->T;
      foreach() {
        T[] = 0.;
        T[] += liq->isothermal ? f[]*TL[] : TL[];
        T[] += gas->isothermal ? (1. - f[])*TG[] : TG[];
      }
    
      // fixme: make it general choosing the right gas species
      foreach() {
        double Ysum = 0.;
        foreach_species_in (liq) {
          scalar YL = liq->YList[i], YG = gas->YList[i];
          Ysum += liq->isomassfrac ? f[]*YL[] : YL[];
          Ysum += gas->isomassfrac ? (1. - f[])*YG[] : YG[];
        }
        Y[] = Ysum;
      }
    }
    
    #if TWO_PHASE_VARPROP
    event properties (i++) {
      scalar rhol = liq->rho, rhog = gas->rho;
      scalar mul = liq->mu, mug = gas->mu;
      foreach() {
        rho1v[] = rhol[];
        rho2v[] = rhog[];
        mu1v[] = mul[];
        mu2v[] = mug[];
      }
    }
    #endif