sandbox/ecipriano/src/multicomponent.h

    #include "intgrad.h"
    #include "fsolve.h"
    
    Phase * liq_int, * gas_int;
    
    scalar * mEvapList = NULL;
    
    int * LSI = NULL, * GOSI = NULL, NGOS, inertIndex;
    
    double * YIntVals;
    
    typedef struct {
      Phase * liq, * gas;
      scalar fl, fg;
      face vector fsl, fsg;
      coord c;
    } EnergyBalanceData;
    
    double divq_rad_int (double TInti, double Tbulk = 300., double alphacorr = 0) {
      return alphacorr*5.670373e-8*(pow (Tbulk, 4.) - pow (TInti, 4.));
    }
    
    void energy_balance (const double * xdata, double * fdata, void * params) {
      EnergyBalanceData * data = params;
    
      Phase * liq = data->liq, * gas = data->gas;
      scalar fl = data->fl, fg = data->fg;
      face vector fsl = data->fsl, fsg = data->fsg;
    
      scalar TL = liq->T, TG = gas->T;
    
      foreach_point (data->c.x, data->c.y, data->c.z, serial) {
        double TInti = xdata[0];
        bool success = false;
    
        double gtrgrad = ebmgrad (point, TG, fl, fg, fsl, fsg, true,  TInti, &success);
        double ltrgrad = ebmgrad (point, TL, fl, fg, fsl, fsg, false, TInti, &success);
    
        scalar lambda1 = liq->lambda, lambda2 = gas->lambda;
    
        double vapheat = 0.;
        for (size_t i = 0; i < liq->n; i++) {
          scalar mEvap = mEvapList[LSI[i]];
          scalar dhev = liq->dhevList[i];
          vapheat -= mEvap[]*dhev[];
        }
    
        fdata[0] = vapheat
          - divq_rad_int (TInti, TG0, pcm.emissivity)
          + lambda1[]*ltrgrad
          + lambda2[]*gtrgrad
          ;
      }
    }
    
    #if USE_GSL
    int energy_balance_gsl (const gsl_vector * x, void * params, gsl_vector * f) {
      double * xdata = x->data, * fdata = f->data;
      energy_balance (xdata, fdata, params);
      return GSL_SUCCESS;
    }
    #endif
    
    // Antoine functions and default condition
    
    double antoine_default (double T, double P, int i) {
      if (YIntVal)
        return YIntVal;
      else {
        scalar YL = liq->YList[i];
        if (YL.antoine)
          return YL.antoine (T, P);
        else
          return YIntVals[i];
      }
    }
    
    double (* antoine) (double, double, int) = &antoine_default;
    
    event defaults (i = 0) {
      liq_int = new_phase_minimal ("LInt", NLS, false, liq_species);
      gas_int = new_phase_minimal ("GInt", NGS, true, gas_species);
    
      foreach_species_in (gas_int) {
        scalar mEvap = new scalar;
        char name[80];
        sprintf (name, "mEvap%s", gas_int->species[i]);
        free (mEvap.name);
        mEvap.name = strdup (name);
        mEvap.nodump = true;
        mEvapList = list_add (mEvapList, mEvap);
      }
    
      // Liquid species indices *LSI* array
      Array * arrLSI = array_new();
      for (int i = 0; i < NGS; i++)
        for (int j = 0; j < NLS; j++)
          if (strcmp (gas_int->species[i], liq_int->species[j]) == 0)
            array_append (arrLSI, &i, sizeof (int));
      LSI = (int *)array_shrink (arrLSI);
    
      // Gas-only species indices *GOSI* array
      Array * arrGOSI = array_new();
      for (int i = 0; i < NGS; i++) {
        bool species_is_also_liquid = false;
        for (int j = 0; j < NLS; j++) {
          if (strcmp (gas_int->species[i], liq_int->species[j]) == 0)
            species_is_also_liquid = true;
        }
        if (!species_is_also_liquid)
          array_append (arrGOSI, &i, sizeof (int));
      }
      NGOS = arrGOSI->len / sizeof (int);
      GOSI = (int *)array_shrink (arrGOSI);
      assert (NGOS == (NGS - NLS));
    
      // fixme: needs the inert_species name
      inertIndex = NGS - 1;
      //for (int i = 0; i < NGS; i++)
      //  if (strcmp (gas_int->species[i], inert_species) == 0)
      //    inertIndex = i;
    
      // Default antoine functions: NLS and the species
      YIntVals = (double *)calloc (NLS, sizeof (double));
    
      // We dump only non-null vaporization rates
      foreach_species_in (liq_int) {
        scalar mEvap = mEvapList[LSI[i]];
        mEvap.nodump = false;
      }
    }
    
    event cleanup (t = end) {
      delete_phase (liq_int), liq_int = NULL;
      delete_phase (gas_int), gas_int = NULL;
      delete (mEvapList), free (mEvapList), mEvapList = NULL;
      free (LSI), LSI = NULL;
      free (GOSI), GOSI = NULL;
      free (YIntVals);
    }
    
    event reset_sources (i++) {
      foreach() {
        foreach_scalar_in (liq_int) {
          T[] = 0.;
          foreach_species_in (liq_int) {
            Y[] = 0.;
            X[] = 0.;
          }
        }
        foreach_scalar_in (gas_int) {
          T[] = 0.;
          foreach_species_in (gas_int) {
            Y[] = 0.;
            X[] = 0.;
          }
        }
      }
    }
    
    event phasechange (i++) {
      phase_tracers_to_scalars (liq, f, tol = F_ERR);
      phase_tracers_to_scalars (gas, f, tol = F_ERR);
    
      scalar fl[], fg[];
      foreach() {
        fl[] = f[];
        fg[] = 1. - f[];
      }
    
      face vector fsl[], fsg[];
      face_fraction (fl, fsl);
      face_fraction (fg, fsg);
    
      // Calculate interfacial temperature
      scalar TLInt = liq_int->T, TGInt = gas_int->T;
      foreach_interfacial (f, F_ERR) {
        TLInt[] = TIntVal;
        TGInt[] = TIntVal;
        if (!pcm.isothermal_interface) {
          TLInt[] = avg_neighbor (point, liq->T, f);
          TGInt[] = TLInt[];
        }
      }
    
      // Calculate interfacial liquid mass fraction
      foreach_interfacial (f, F_ERR) {
        foreach_species_in (liq_int)
          Y[] = avg_neighbor (point, liq->YList[i], f);
        foreach_species_in (gas_int) {
          //Y[] = avg_neighbor (point, gas->YList[i], f);
          scalar YG = gas->YList[i];
          Y[] = YG[];
        }
      }
    
      // Update mole fractions and moles
      memcpy (liq_int->MWs, liq->MWs, liq->n*sizeof (double));
      memcpy (gas_int->MWs, gas->MWs, gas->n*sizeof (double));
      phase_update_mw_moles (liq_int);
      phase_update_mw_moles (gas_int);
    
      // Variables for interfacial mole fractions
      double * ygcorr = malloc ((NLS + 1)*sizeof (double));
      double * xgcorr = malloc ((NLS + 1)*sizeof (double));
      double * mwcorr = malloc ((NLS + 1)*sizeof (double));
    
      // Variables for gas-only mass fraction
      double * ygolist  = malloc (NGOS*sizeof (double));
      double * mwgolist = malloc (NGOS*sizeof (double));
    
      foreach_interfacial (f, F_ERR, serial) {
    
        // Store the sum of the gas-only species
        double sum_ngos_yg_old = 0., sum_ngos_xg_old = 0.;
        for (int i = 0; i < NGOS; i++) {
          scalar YGInt = gas_int->YList[GOSI[i]];
          scalar XGInt = gas_int->XList[GOSI[i]];
          sum_ngos_yg_old += YGInt[];
          sum_ngos_xg_old += XGInt[];
        }
    
        for (int i = 0; i < NLS; i++) {
          scalar XLInt = liq_int->XList[i];
          scalar XGInt = gas_int->XList[LSI[i]];
    
          XGInt[] = min (antoine (TLInt[], Pref, i), 0.98)*XLInt[];
        }
    
        // Calculate the molecular weight of the gas-only species mixture
        for (int i = 0; i < NGOS; i++) {
          scalar YGInt = gas_int->YList[GOSI[i]];
          ygolist[i] = YGInt[];
          mwgolist[i] = gas_int->MWs[GOSI[i]];
        }
        correctfrac (ygolist, NGOS);
        double mwgasonly = mass2mw (ygolist, mwgolist, NGOS);
    
        // Convert interfacial mole fractions to mass fractions
        double sum_xgl = 0.;
        for (int i = 0; i < NLS; i++) {
          scalar XGInt = gas_int->XList[LSI[i]];
          sum_xgl += XGInt[];
          xgcorr[i] = XGInt[];
          mwcorr[i] = liq_int->MWs[i];
        }
        xgcorr[NLS] = 1. - sum_xgl;
        mwcorr[NLS] = mwgasonly;
        mole2massfrac (ygcorr, xgcorr, mwcorr, NLS + 1);
    
        // Recover the interface gas-phase side mass fractions
        for (int i = 0; i < NLS; i++) {
          scalar YGInt = gas_int->YList[LSI[i]];
          YGInt[] = ygcorr[i];
        }
    
        // Adjust the interface gas-phase (gas only) mass fractions
        for (int i = 0; i < NGOS; i++) {
          scalar YGInt = gas_int->YList[GOSI[i]];
          scalar XGInt = gas_int->XList[GOSI[i]];
          YGInt[] *= ygcorr[NLS] / (sum_ngos_yg_old + 1e-10);
          XGInt[] *= xgcorr[NLS] / (sum_ngos_xg_old + 1e-10);
        }
      }
      free (ygcorr);
      free (xgcorr);
      free (mwcorr);
      free (ygolist);
      free (mwgolist);
    
      // Compute the total and species vaporization rates
      foreach_interfacial (f, F_ERR) {
        scalar rhoG = gas->rho;
    
        double jGtot = 0.;
        if (pcm.fick_corrected) {
          for (int i = 0; i < NGS; i++) {
            scalar DG = gas->DList[i];
            double gtrgrad = 0.;
            if (pcm.molar_diffusion) {
              scalar XG = gas->XList[i];
              scalar XGInt = gas_int->XList[i];
              scalar MWGInt = gas_int->MW;
              gtrgrad = ebmgrad (point, XG, fl, fg, fsl, fsg, true, XGInt[], false);
              gtrgrad *= (MWGInt[] > 0) ? gas_int->MWs[i]/MWGInt[] : 0.;
            }
            else {
              scalar YGInt = gas_int->YList[i];
              scalar YG = gas->YList[i];
              gtrgrad = ebmgrad (point, YG, fl, fg, fsl, fsg, true, YGInt[], false);
            }
            jGtot += -rhoG[]*DG[]*gtrgrad;
          }
        }
    
        double sum_jG = 0., sum_YGInt = 0.;
        for (int i = 0; i < NLS; i++) {
          scalar YGInt = gas_int->YList[LSI[i]];
          scalar DG = gas->DList[LSI[i]];
          double gtrgrad = 0.;
          if (pcm.molar_diffusion) {
            scalar XGInt = gas_int->XList[LSI[i]];
            scalar XG = gas->XList[LSI[i]];
            scalar MWGInt = gas_int->MW;
    
            gtrgrad = ebmgrad (point, XG, fl, fg, fsl, fsg, true, XGInt[], false);
            gtrgrad *= (MWGInt[] > 0) ? gas_int->MWs[LSI[i]]/MWGInt[] : 0.;
          }
          else {
            scalar YG = gas->YList[LSI[i]];
            gtrgrad = ebmgrad (point, YG, fl, fg, fsl, fsg, true, YGInt[], false);
          }
          sum_jG += -rhoG[]*DG[]*gtrgrad - YGInt[]*jGtot;
          sum_YGInt += YGInt[];
        }
        mEvapTot[] = sum_jG / min (1. - sum_YGInt, 0.99);
    
        for (int i = 0; i < NLS; i++) {
          scalar mEvap = mEvapList[LSI[i]];
          scalar YGInt = gas_int->YList[LSI[i]];
          scalar DG = gas->DList[LSI[i]];
          double gtrgrad = 0.;
          if (pcm.molar_diffusion) {
            scalar XGInt = gas_int->XList[LSI[i]];
            scalar XG = gas->XList[LSI[i]];
            scalar MWGInt = gas_int->MW;
    
            gtrgrad = ebmgrad (point, XG, fl, fg, fsl, fsg, true, XGInt[], false);
            gtrgrad *= (MWGInt[] > 0) ? gas_int->MWs[LSI[i]]/MWGInt[] : 0.;
          }
          else {
            scalar YG = gas->YList[LSI[i]];
            gtrgrad = ebmgrad (point, YG, fl, fg, fsl, fsg, true, YGInt[], false);
          }
          mEvap[] = mEvapTot[]*YGInt[] - rhoG[]*DG[]*gtrgrad - YGInt[]*jGtot;
        }
      }
    
      // Find the interfacial temperature which respects the vaporization rate
      if (!pcm.isothermal && !pcm.isothermal_interface) {
        EnergyBalanceData data;
        data.liq = liq, data.gas = gas;
        data.fl = fl, data.fg = fg;
        data.fsl = fsl, data.fsg = fsg;
    
        foreach_interfacial (f, F_ERR, serial) {
          Array * arrUnk = array_new();
          double vali = TLInt[];
          array_append (arrUnk, &vali, sizeof (double));
    
          coord o = {x, y, z};
          foreach_dimension()
            data.c.x = o.x;
    
    #if USE_GSL
          fsolve (energy_balance_gsl, arrUnk, &data);
    #else
          fprintf (ferr,
              "src/multicomponent.h:%d: error: missing root-finding method\n",
              LINENO), fflush (ferr);
          exit(1);
    #endif
    
          double * unk = (double *)arrUnk->p;
          TLInt[] = unk[0];
          TGInt[] = TLInt[];
          array_free (arrUnk);
        }
      }
    
      // Calculate species and temperature source terms
      foreach_interfacial_plic (f, F_ERR) {
    
        foreach_species_in (gas) {
          scalar mEvap = mEvapList[i];
          scalar YInt = gas_int->YList[i];
    
          if (pcm.diffusion == EXPLICIT_ONLY)
            SYexp[] += -(mEvap[] - mEvapTot[]*YInt[])*dirac;
          else {
            SYexp[] += -mEvap[]*dirac;
            SYimp[] += +mEvapTot[]*dirac;
          }
        }
    
        foreach_species_in (liq) {
          scalar mEvap = mEvapList[LSI[i]];
          scalar YInt = liq_int->YList[i];
    
          if (pcm.diffusion == EXPLICIT_ONLY)
            SYexp[] += +(mEvap[] - mEvapTot[]*YInt[])*dirac;
          else {
            SYexp[] += +mEvap[]*dirac;
            SYimp[] += -mEvapTot[]*dirac;
          }
        }
    
        scalar TL = liq->T, TG = gas->T;
        double ltrgrad = ebmgrad (point, TL, fl, fg, fsl, fsg, false, TLInt[], false);
        double gtrgrad = ebmgrad (point, TG, fl, fg, fsl, fsg, true, TGInt[], false);
    
        scalar slT = liq->STexp, sgT = gas->STexp;
        scalar lambdal = liq->lambda, lambdag = gas->lambda;
    
        slT[] += lambdal[]*ltrgrad*dirac;
        sgT[] += lambdag[]*gtrgrad*dirac;
      }
    
      // We add the heat from the mass diffusion process
      if (pcm.mass_diffusion_enthalpy) {
        phase_add_heat_species_diffusion (liq, f, pcm.molar_diffusion, F_ERR);
        phase_add_heat_species_diffusion (gas, f, pcm.molar_diffusion, F_ERR);
      }
    
      phase_scalars_to_tracers (liq, f);
      phase_scalars_to_tracers (gas, f);
    }