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;
}
StackSegment::StackSegment()
    :min(getNaN())
    ,max(getNaN())
{

}
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;
}