Ejemplo n.º 1
0
def raoult_bubble_temperature(model, mixture, pressure, psat=None):
    """ Calculates the bubble temperature at a given pressure using Raoult's law
    and a saturation pressure routine. Defaults to an approximate saturation
    pressure calculated using the Clausius-Clapeyron equation.
    Requires that the mixture have an overall composition, this is the assumed
    composition of the liquid at the bubble point.
    Returns a mixture with the vapor and liquid compositions at the bubble point
    and all state values and departure functions completed.
    This algorithm is unstable in the region close to the mixture critial point.
    model: model that implements the Model interface
    mixture: instance of Mixture
    pressure: float in Pascals
    psat: function, mixture -> saturation pressure in Pascals
    """
    mixture.pressure = pressure
    mixture.quality = 1.0
    mixture.composition['liquid'] = mixture.composition['overall']
    mixture.normalize()

    if psat is None:
        mixture.temperature = _rt_bt(mixture)
        Ps = psatguess(mixture.critical['acentric'], mixture.reduced['temperature']) * mixture.critical['pressure']
    else:
        T0 = np.dot(tsatguess(mixture.critical['acentric'], mixture.reduced['pressure']) * mixture.critical['temperature'], mixture.composition['liquid'])

        def temperature_loop(temperature):
            mixture.temperature = temperature
            Ps = psat(mixture)
            return pressure - np.dot(Ps, mixture.composition['liquid'])

        mixture.temperature = opt.newton(temperature_loop, T0)
        Ps = psat(mixture)

    mixture.composition['vapor'] = mixture.composition['liquid'] * Ps / mixture.pressure
    return model(mixture)
Ejemplo n.º 2
0
def _rt_bt(mixture):
    """ Calculate the bubble temperature using Raoult's Law"""
    T0 = np.dot(tsatguess(mixture.critical['acentric'], mixture.reduced['pressure']) * mixture.critical['temperature'], mixture.composition['liquid'])

    # Calculate bubble temperature
    for count in range(MAX_ITER):
        f0 = mixture.composition['liquid'] * mixture.critical['pressure'] * np.power(10, (7 / 3) * (mixture.critical['acentric'] + 1) * (1 - mixture.critical['temperature'] / T0))
        T1 = T0 + (mixture.state['pressure'] - f0.sum()) / ((7 / 3) * np.log(10) * T0**-2 * np.sum(f0 * mixture.critical['temperature'] * (mixture.critical['acentric'] + 1)))

        if np.allclose(T0, T1):
            return T1

        T0 = T1

    raise SearchError("Bubble temperature failed to converge after {} iterations".format(MAX_ITER))
Ejemplo n.º 3
0
def two_loop_bubble_temperature(model, mixture, pressure, bounds=None):
    """ Calculates the bubble temperature at a given pressure, uses Raoult's law
    for an initial guess of the bubble temperature, unless otherwise specified.
    Requires that the mixture have an overall composition, this is the assumed
    composition of the liquid at the bubble point.
    Returns a mixture with the vapor and liquid compositions at the bubble point
    and all state values and departure functions completed.
    This algorithm is unstable in the region close to the mixture critial point.
    model: model that implements the Model interface
    mixture: instance of Mixture
    pressure: float in Pascals
    bounds: tuple, boundary of temperature region to search, in Kelvin
    """
    mixture.pressure = pressure
    mixture.quality = 1.0
    mixture.composition['liquid'] = mixture.composition['overall']
    mixture.normalize()

    def temperature_loop(temperature):
        """ Uses fixed point iteration with Aitkens sequence acceleration to
        find the vapor phase composition """
        assert temperature > 0, "Temperature must be greater than zero"
        mixture.temperature = temperature
        lnphiL = model.logfug(mixture, 'liquid')
        y0 = mixture.composition['liquid'] * psatguess(mixture.critical['acentric'], mixture.reduced['temperature']) / mixture.reduced['pressure']

        for count in range(MAX_ITER):
            mixture.composition['vapor'] = y0 / y0.sum()
            lnphiV = model.logfug(mixture, 'vapor')
            y1 = mixture.composition['liquid'] * np.exp(lnphiL - lnphiV)

            mixture.composition['vapor'] = y1 / y1.sum()
            lnphiV = model.logfug(mixture, 'vapor')
            y2 = mixture.composition['liquid'] * np.exp(lnphiL - lnphiV)

            d = y2 - 2.0 * y1 + y0
            y = np.where(np.absolute(d) < 1e-16, y2, y2 - (y2 - y1)**2 / d)

            if np.allclose(y, y2, atol=ERROR_TOL):
                mixture.composition['vapor'] = y2 / y2.sum()
                return 1 - y.sum()
            y0 = y

        raise SearchError("Temperature loop ({} K) failed to converge after {} iterations".format(temperature, MAX_ITER))

    # Find bracket for temperature loop zero and solve for bubble temperature
    if bounds is None:
        # Use Raoult's Law to get an initial guess of the bubble temperature
        try:
            # This uses Newton's method and may fail spectacularly
            t0 = _rt_bt(mixture)
        except SearchError:
            t0 = np.dot(tsatguess(mixture.critical['acentric'], mixture.reduced['pressure']) * mixture.critical['temperature'], mixture.composition['liquid'])
        finally:
            y0 = temperature_loop(t0)
            dt = +0.2 * t0 if y0 > 0 else -0.2 * t0
            bounds = bounds_search(temperature_loop, t0, dt)

    mixture.temperature = bounds[0]
    if np.all(mixture.reduced['temperature'] > Trcrit):
        msg = "Temperature {} is too high, bubble point routine is unstable in this region".format(t0)
        warnings.warn(msg, RuntimeWarning)

    mixture.pressure = opt.brentq(temperature_loop, *bounds)
    return model(mixture)