void
GeneralVaporMixtureFluidProperties::e_from_p_T(Real p,
                                               Real T,
                                               std::vector<Real> x,
                                               Real & e,
                                               Real & de_dp,
                                               Real & de_dT,
                                               std::vector<Real> & de_dx) const
{
  const Real x_primary = primaryMassFraction(x);

  Real ep, dep_dp, dep_dT;
  _fp_primary->e_from_p_T(p, T, ep, dep_dp, dep_dT);
  e = x_primary * ep;
  de_dp = x_primary * dep_dp;
  de_dT = x_primary * dep_dT;
  de_dx.resize(_n_secondary_vapors);
  for (unsigned int i = 0; i < _n_secondary_vapors; ++i)
  {
    Real ei, dei_dp, dei_dT;
    _fp_secondary[i]->e_from_p_T(p, T, ei, dei_dp, dei_dT);
    e += x[i] * ei;
    de_dp += x[i] * dei_dp;
    de_dT += x[i] * dei_dT;
    de_dx[i] = ei - ep;
  }
}
Real
GeneralVaporMixtureFluidProperties::k_from_p_T(Real p, Real T, std::vector<Real> x) const
{
  const Real x_primary = primaryMassFraction(x);
  Real M_primary = _fp_primary->molarMass();

  Real sum = x_primary / M_primary;
  for (unsigned int i = 0; i < _n_secondary_vapors; ++i)
    sum += x[i] / _fp_secondary[i]->molarMass();
  Real M_star = 1. / sum;

  Real vp = _fp_primary->v_from_p_T(p, T);
  Real ep = _fp_primary->e_from_p_T(p, T);
  Real k = x_primary * M_star / M_primary * _fp_primary->k_from_v_e(vp, ep);

  for (unsigned int i = 0; i < _n_secondary_vapors; ++i)
  {
    Real vi = _fp_secondary[i]->v_from_p_T(p, T);
    Real ei = _fp_secondary[i]->e_from_p_T(p, T);
    Real Mi = _fp_secondary[i]->molarMass();
    k += x[i] * M_star / Mi * _fp_secondary[i]->k_from_v_e(vi, ei);
  }

  return k;
}
void
GeneralVaporMixtureFluidProperties::v_from_p_T(Real p,
                                               Real T,
                                               std::vector<Real> x,
                                               Real & v,
                                               Real & dv_dp,
                                               Real & dv_dT,
                                               std::vector<Real> & dv_dx) const
{
  const Real x_primary = primaryMassFraction(x);

  Real vp, dvp_dp, dvp_dT;
  _fp_primary->v_from_p_T(p, T, vp, dvp_dp, dvp_dT);
  v = x_primary * vp;
  dv_dp = x_primary * dvp_dp;
  dv_dT = x_primary * dvp_dT;
  dv_dx.resize(_n_secondary_vapors);
  for (unsigned int i = 0; i < _n_secondary_vapors; ++i)
  {
    Real vi, dvi_dp, dvi_dT;
    _fp_secondary[i]->v_from_p_T(p, T, vi, dvi_dp, dvi_dT);
    v += x[i] * vi;
    dv_dp += x[i] * dvi_dp;
    dv_dT += x[i] * dvi_dT;
    dv_dx[i] = vi - vp;
  }
}
Real
IdealRealGasMixtureFluidProperties::k_from_T_v(Real T, Real v, const std::vector<Real> & x) const
{
  const Real x_primary = primaryMassFraction(x);
  Real M_primary = _fp_primary->molarMass();

  Real sum = x_primary / M_primary;
  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
    sum += x[i] / _fp_secondary[i]->molarMass();
  Real M_star = 1. / sum;

  Real vp = v / x_primary;
  Real ep = _fp_primary->e_from_T_v(T, vp);
  Real k = x_primary * M_star / M_primary * _fp_primary->k_from_v_e(vp, ep);

  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
  {
    Real vi = v / x[i];
    Real ei = _fp_secondary[i]->e_from_T_v(T, vp);
    Real Mi = _fp_secondary[i]->molarMass();
    k += x[i] * M_star / Mi * _fp_secondary[i]->k_from_v_e(vi, ei);
  }

  return k;
}
void
IdealRealGasMixtureFluidProperties::p_T_from_v_e(
    Real v, Real e, const std::vector<Real> & x, Real & p, Real & T) const
{
  Real v_primary = v / primaryMassFraction(x);
  static const Real vc = 1. / _fp_primary->criticalDensity();
  static const Real ec = _fp_primary->criticalInternalEnergy();

  // Initial estimate of a bracketing interval for the temperature
  Real lower_temperature, upper_temperature;
  if (v_primary > vc)
  {
    Real e_sat_primary = _fp_primary->e_spndl_from_v(v_primary);
    lower_temperature = _fp_primary->T_from_v_e(v_primary, e_sat_primary);
  }
  else
    lower_temperature = _fp_primary->T_from_v_e(v_primary, ec);

  upper_temperature = _T_mix_max;

  // Use BrentsMethod to find temperature
  auto energy_diff = [&v, &e, &x, this](Real T) { return this->e_from_T_v(T, v, x) - e; };

  BrentsMethod::bracket(energy_diff, lower_temperature, upper_temperature);
  T = BrentsMethod::root(energy_diff, lower_temperature, upper_temperature);

  p = p_from_T_v(T, v, x);
}
Real
GeneralVaporMixtureFluidProperties::e_from_p_T(Real p, Real T, std::vector<Real> x) const
{
  const Real x_primary = primaryMassFraction(x);

  Real e = x_primary * _fp_primary->e_from_p_T(p, T);
  for (unsigned int i = 0; i < _n_secondary_vapors; ++i)
    e += x[i] * _fp_secondary[i]->e_from_p_T(p, T);

  return e;
}
Real
IdealRealGasMixtureFluidProperties::cv_from_T_v(Real T, Real v, const std::vector<Real> & x) const
{
  const Real x_primary = primaryMassFraction(x);
  Real cv = x_primary * _fp_primary->cv_from_T_v(T, v / x_primary);

  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
    cv += x[i] * _fp_secondary[i]->cv_from_T_v(T, v / x[i]);

  return cv;
}
void
IdealRealGasMixtureFluidProperties::e_from_T_v(Real T,
                                               Real v,
                                               const std::vector<Real> & x,
                                               Real & e,
                                               Real & de_dT,
                                               Real & de_dv,
                                               std::vector<Real> & de_dx) const
{
  Real e_primary, de_dT_primary, de_dv_primary, de_dx_primary, dxi_dx_primary;
  Real de_dT_sec, dxj_dxi, dx_primary_dxi;
  std::vector<Real> e_sec, de_dv_sec;

  const Real x_primary = primaryMassFraction(x);
  de_dx.resize(_n_secondary_vapors);
  e_sec.resize(_n_secondary_vapors);
  de_dv_sec.resize(_n_secondary_vapors);

  _fp_primary->e_from_T_v(T, v / x_primary, e_primary, de_dT_primary, de_dv_primary);
  e = x_primary * e_primary;
  de_dT = x_primary * de_dT_primary;
  de_dv = de_dv_primary;
  de_dx_primary = e_primary - x_primary * de_dv_primary * v / (x_primary * x_primary);

  // get the partial pressures and their derivatives first
  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
  {
    _fp_secondary[i]->e_from_T_v(T, v / x[i], e_sec[i], de_dT_sec, de_dv_sec[i]);

    e += x[i] * e_sec[i];
    de_dT += x[i] * de_dT_sec;
    de_dv += de_dv_sec[i];
    dxi_dx_primary = -x[i] / (1. - x_primary);
    de_dx_primary +=
        dxi_dx_primary * e_sec[i] - x[i] * de_dv_sec[i] * v / (x[i] * x[i]) * dxi_dx_primary;
  }

  // get the composition dependent derivatives of the secondary vapors
  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
  {
    de_dx[i] = e_sec[i] - x[i] * de_dv_sec[i] * v / (x[i] * x[i]);
    for (unsigned int j = 0; j < _n_secondary_vapors; j++)
    {
      if (j == i)
        continue;
      dxj_dxi = -x[j] / (1. - x[i]);
      de_dx[i] += dxj_dxi * e_sec[j] - x[j] * de_dv_sec[j] * v / (x[j] * x[j]) * dxj_dxi;
    }
    dx_primary_dxi = -x_primary / (1. - x[i]);
    de_dx[i] += dx_primary_dxi * e_primary -
                x_primary * de_dv_primary * v / (x_primary * x_primary) * dx_primary_dxi;
  }
}
Real
GeneralVaporMixtureFluidProperties::cv_from_p_T(Real p, Real T, std::vector<Real> x) const
{
  const Real x_primary = primaryMassFraction(x);
  Real vp = _fp_primary->v_from_p_T(p, T);
  Real ep = _fp_primary->e_from_p_T(p, T);
  Real cv = x_primary * _fp_primary->cv_from_v_e(vp, ep);

  for (unsigned int i = 0; i < _n_secondary_vapors; ++i)
  {
    Real vi = _fp_secondary[i]->v_from_p_T(p, T);
    Real ei = _fp_secondary[i]->e_from_p_T(p, T);
    cv += x[i] * _fp_secondary[i]->cv_from_v_e(vi, ei);
  }

  return cv;
}
Real
IdealRealGasMixtureFluidProperties::cp_from_T_v(Real T, Real v, const std::vector<Real> & x) const
{
  Real p, dp_dT, dp_dv;
  Real h, dh_dT, dh_dv;

  p_from_T_v(T, v, x, p, dp_dT, dp_dv);

  const Real x_primary = primaryMassFraction(x);

  _fp_primary->h_from_T_v(T, v / x_primary, h, dh_dT, dh_dv);
  Real cp = x_primary * (dh_dT - dh_dv * dp_dT / dp_dv);

  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
  {
    _fp_secondary[i]->h_from_T_v(T, v / x[i], h, dh_dT, dh_dv);
    cp += x[i] * (dh_dT - dh_dv * dp_dT / dp_dv);
  }

  return cp;
}
void
IdealRealGasMixtureFluidProperties::s_from_T_v(
    Real T, Real v, const std::vector<Real> & x, Real & s, Real & ds_dT, Real & ds_dv) const
{
  Real s_primary, ds_dT_primary, ds_dv_primary;
  Real s_sec, ds_dT_sec, ds_dv_sec;

  const Real x_primary = primaryMassFraction(x);

  _fp_primary->s_from_T_v(T, v / x_primary, s_primary, ds_dT_primary, ds_dv_primary);
  s = x_primary * s_primary;
  ds_dT = x_primary * ds_dT_primary;
  ds_dv = ds_dv_primary;

  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
  {
    _fp_secondary[i]->s_from_T_v(T, v / x[i], s_sec, ds_dT_sec, ds_dv_sec);

    s += x[i] * s_sec;
    ds_dT += x[i] * ds_dT_sec;
    ds_dv += ds_dv_sec;
  }
}
void
IdealRealGasMixtureFluidProperties::p_from_T_v(
    Real T, Real v, const std::vector<Real> & x, Real & p, Real & dp_dT, Real & dp_dv) const
{
  Real p_primary, dp_dT_primary, dp_dv_primary;
  Real p_sec, dp_dT_sec, dp_dv_sec;

  const Real x_primary = primaryMassFraction(x);

  _fp_primary->p_from_T_v(T, v / x_primary, p_primary, dp_dT_primary, dp_dv_primary);
  p = p_primary;
  dp_dT = dp_dT_primary;
  dp_dv = dp_dv_primary / x_primary;

  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
  {
    _fp_secondary[i]->p_from_T_v(T, v / x[i], p_sec, dp_dT_sec, dp_dv_sec);

    p += p_sec;
    dp_dT += dp_dT_sec;
    dp_dv += dp_dv_sec / x[i];
  }
}
Real
IdealRealGasMixtureFluidProperties::v_from_p_T(Real p, Real T, const std::vector<Real> & x) const
{
  const Real x_primary = primaryMassFraction(x);
  Real M_primary = _fp_primary->molarMass();

  Real sum = x_primary / M_primary;
  for (unsigned int i = 0; i < _n_secondary_vapors; i++)
    sum += x[i] / _fp_secondary[i]->molarMass();
  Real M_star = 1. / sum;
  Real v_ideal = R_molar * T / (M_star * p);

  // check range of validity for primary (condensable) component
  static const Real Tc = _fp_primary->criticalTemperature();
  static const Real vc = 1. / _fp_primary->criticalDensity();
  Real v_spndl, e_spndl;
  if (T < Tc)
    _fp_primary->v_e_spndl_from_T(T, v_spndl, e_spndl);
  else
    v_spndl = vc;

  Real lower_spec_volume = v_spndl * x_primary;
  Real upper_spec_volume = v_ideal; // p*v/(RT) <= 1

  // Initial estimate of a bracketing interval for the temperature
  Real p_max = p_from_T_v(T, lower_spec_volume, x);
  if (p > p_max || upper_spec_volume < lower_spec_volume)
    return getNaN();

  // Use BrentsMethod to find temperature
  auto pressure_diff = [&T, &p, &x, this](Real v) { return this->p_from_T_v(T, v, x) - p; };

  BrentsMethod::bracket(pressure_diff, lower_spec_volume, upper_spec_volume);
  Real v = BrentsMethod::root(pressure_diff, lower_spec_volume, upper_spec_volume);

  return v;
}
Real
GeneralVaporMixtureFluidProperties::c_from_p_T(Real p, Real T, std::vector<Real> x) const
{
  const Real x_primary = primaryMassFraction(x);

  Real vp, dvp_dp_T, dvp_dT_p;
  _fp_primary->v_from_p_T(p, T, vp, dvp_dp_T, dvp_dT_p);
  Real v = x_primary * vp;
  Real dv_dp_T = x_primary * dvp_dp_T;
  Real dv_dT_p = x_primary * dvp_dT_p;

  Real sp, dsp_dp_T, dsp_dT_p;
  _fp_primary->s_from_p_T(p, T, sp, dsp_dp_T, dsp_dT_p);
  Real ds_dp_T = x_primary * dsp_dp_T;
  Real ds_dT_p = x_primary * dsp_dT_p;

  for (unsigned int i = 0; i < _n_secondary_vapors; ++i)
  {
    Real vi, dvi_dp_T, dvi_dT_p;
    _fp_secondary[i]->v_from_p_T(p, T, vi, dvi_dp_T, dvi_dT_p);
    v += x[i] * vi;
    dv_dp_T += x[i] * dvi_dp_T;
    dv_dT_p += x[i] * dvi_dT_p;

    Real si, dsi_dp_T, dsi_dT_p;
    _fp_secondary[i]->s_from_p_T(p, T, si, dsi_dp_T, dsi_dT_p);
    ds_dp_T += x[i] * dsi_dp_T;
    ds_dT_p += x[i] * dsi_dT_p;
  }

  Real dv_dp_s = dv_dp_T - dv_dT_p * ds_dp_T / ds_dT_p;

  if (dv_dp_s >= 0)
    mooseWarning(name(), ":c_from_p_T(), dv_dp_s = ", dv_dp_s, ". Should be negative.");
  return v * std::sqrt(-1. / dv_dp_s);
}
Real
IdealRealGasMixtureFluidProperties::xs_prim_from_p_T(Real p,
                                                     Real T,
                                                     const std::vector<Real> & x) const
{
  Real T_c = _fp_primary->criticalTemperature();
  Real xs;
  if (T > T_c)
    // return 1. to indicate that no water would condense for
    // given (p,T)
    xs = 1.;
  else
  {
    Real pp_sat = _fp_primary->pp_sat_from_p_T(p, T);
    if (pp_sat < 0.)
    {
      // return 1. to indicate that no water would condense for
      // given (p,T)
      xs = 1.;
      return xs;
    }
    Real v_primary = _fp_primary->v_from_p_T(pp_sat, T);
    Real pp_sat_secondary = p - pp_sat;

    Real v_secondary;
    if (_n_secondary_vapors == 1)
      v_secondary = _fp_secondary[0]->v_from_p_T(pp_sat_secondary, T);
    else
    {
      std::vector<Real> x_sec(_n_secondary_vapors);
      const Real x_primary = primaryMassFraction(x);
      Real sum = 0.;
      for (unsigned int i = 0; i < _n_secondary_vapors; i++)
      {
        x_sec[i] = x[i] / (1. - x_primary);
        sum += x_sec[i] / _fp_secondary[i]->molarMass();
      }
      Real M_star = 1. / sum;
      v_secondary = R_molar * T / (M_star * pp_sat_secondary);
      int it = 0;
      double f = 1., df_dvs, pp_sec, p_sec, dp_dT_sec, dp_dv_sec, dp_dT, dp_dv;
      double tol_p = 1.e-8;
      while (std::fabs(f / pp_sat_secondary) > tol_p)
      {
        pp_sec = 0.;
        dp_dT = 0.;
        dp_dv = 0.;
        for (unsigned int i = 0; i < _n_secondary_vapors; i++)
        {
          _fp_secondary[i]->p_from_T_v(T, v_secondary / x_sec[i], p_sec, dp_dT_sec, dp_dv_sec);
          pp_sec += p_sec;
          dp_dT += dp_dT_sec;
          dp_dv += dp_dv_sec / x_sec[i];
        }
        f = pp_sec - pp_sat_secondary;
        df_dvs = dp_dv;
        v_secondary -= f / df_dvs;
        if (it++ > 15)
          return getNaN();
      }
    }

    xs = v_secondary / (v_primary + v_secondary);
  }

  return xs;
}