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)