Source code for tespy.tools.fluid_properties.mixtures

# -*- coding: utf-8

"""Module for fluid property mixture routines.


This file is part of project TESPy (github.com/oemof/tespy). It's copyrighted
by the contributors recorded in the version control history of the file,
available from its original location
tespy/tools/fluid_properties/mixtures.py

SPDX-License-Identifier: MIT
"""

import math

import CoolProp as CP

from tespy.tools.global_vars import gas_constants

from .helpers import _is_larger_than_precision
from .helpers import calc_molar_mass_mixture
from .helpers import get_molar_fractions


[docs] def h_mix_pT_ideal(p=None, T=None, fluid_data=None, **kwargs): molar_fractions = get_molar_fractions(fluid_data) h = 0 for fluid, data in fluid_data.items(): if _is_larger_than_precision(data["mass_fraction"]): pp = p * molar_fractions[fluid] h += data["wrapper"].h_pT(pp, T) * data["mass_fraction"] return h
[docs] def h_mix_pT_ideal_cond(p=None, T=None, fluid_data=None, **kwargs): water_alias = _water_in_mixture(fluid_data) if water_alias: water_alias = next(iter(water_alias)) mass_fractions_gas, molar_fraction_gas, mass_liquid, molar_liquid = cond_check(p, T, fluid_data, water_alias) if not _is_larger_than_precision(mass_liquid): return h_mix_pT_ideal(p, T, fluid_data, **kwargs) h = 0 for fluid, data in fluid_data.items(): if _is_larger_than_precision(data["mass_fraction"]): if fluid == water_alias: h += fluid_data[water_alias]["wrapper"].h_QT(0, T) * mass_liquid h += fluid_data[water_alias]["wrapper"].h_QT(1, T) * mass_fractions_gas[fluid] * (1 - mass_liquid) else: pp = p * molar_fraction_gas[fluid] h += data["wrapper"].h_pT(pp, T) * mass_fractions_gas[fluid] * (1 - mass_liquid) return h else: return h_mix_pT_ideal(p, T, fluid_data, **kwargs)
[docs] def h_mix_pT_forced_gas(p, T, fluid_data, **kwargs): molar_fractions = get_molar_fractions(fluid_data) h = 0 for fluid, data in fluid_data.items(): if _is_larger_than_precision(data["mass_fraction"]): pp = p * molar_fractions[fluid] if fluid == "H2O" and pp >= data["wrapper"]._p_min: if T <= data["wrapper"].T_sat(pp): h += data["wrapper"].h_QT(1, T) * data["mass_fraction"] else: h += data["wrapper"].h_pT(pp, T) * data["mass_fraction"] else: h += data["wrapper"].h_pT(pp, T) * data["mass_fraction"] return h
[docs] def h_mix_pT_incompressible(p, T, fluid_data, **kwargs): h = 0 for data in fluid_data.values(): if _is_larger_than_precision(data["mass_fraction"]): h += data["wrapper"].h_pT(p, T) * data["mass_fraction"] return h
[docs] def s_mix_pT_ideal(p=None, T=None, fluid_data=None, **kwargs): molar_fractions = get_molar_fractions(fluid_data) s = 0 for fluid, data in fluid_data.items(): if _is_larger_than_precision(data["mass_fraction"]): pp = p * molar_fractions[fluid] s += data["wrapper"].s_pT(pp, T) * data["mass_fraction"] return s
[docs] def s_mix_pT_ideal_cond(p=None, T=None, fluid_data=None, **kwargs): water_alias = _water_in_mixture(fluid_data) if water_alias: water_alias = next(iter(water_alias)) mass_fractions_gas, molar_fraction_gas, mass_liquid, molar_liquid = cond_check(p, T, fluid_data, water_alias) if mass_liquid == 0: return s_mix_pT_ideal(p, T, fluid_data, **kwargs) s = 0 for fluid, data in fluid_data.items(): if _is_larger_than_precision(data["mass_fraction"]): if fluid == water_alias: s += fluid_data[water_alias]["wrapper"].s_QT(0, T) * mass_liquid s += fluid_data[water_alias]["wrapper"].s_QT(1, T) * mass_fractions_gas[fluid] * (1 - mass_liquid) else: pp = p * molar_fraction_gas[fluid] s += data["wrapper"].s_pT(pp, T) * mass_fractions_gas[fluid] * (1 - mass_liquid) return s else: return s_mix_pT_ideal(p, T, fluid_data, **kwargs)
[docs] def s_mix_pT_incompressible(p=None, T=None, fluid_data=None, **kwargs): s = 0 for data in fluid_data.values(): if _is_larger_than_precision(data["mass_fraction"]): s += data["wrapper"].s_pT(p, T) * data["mass_fraction"] return s
[docs] def v_mix_pT_ideal(p=None, T=None, fluid_data=None, **kwargs): molar_fractions = get_molar_fractions(fluid_data) d = 0 for fluid, data in fluid_data.items(): if _is_larger_than_precision(data["mass_fraction"]): pp = p * molar_fractions[fluid] d += data["wrapper"].d_pT(pp, T) return 1 / d
[docs] def v_mix_pT_ideal_cond(p=None, T=None, fluid_data=None, **kwargs): water_alias = _water_in_mixture(fluid_data) if water_alias: water_alias = next(iter(water_alias)) mass_fractions_gas, molar_fraction_gas, mass_liquid, molar_liquid = cond_check(p, T, fluid_data, water_alias) if mass_liquid == 0: return v_mix_pT_ideal(p, T, fluid_data, **kwargs) d = 0 for fluid, data in fluid_data.items(): if _is_larger_than_precision(data["mass_fraction"]): if fluid == water_alias: d += fluid_data[water_alias]["wrapper"].d_QT(0, T) * mass_liquid d += fluid_data[water_alias]["wrapper"].d_QT(1, T) * (1 - mass_liquid) else: pp = p * molar_fraction_gas[fluid] d += data["wrapper"].d_pT(pp, T) * (1 - mass_liquid) return 1 / d else: return v_mix_pT_ideal(p, T, fluid_data, **kwargs)
[docs] def v_mix_pT_incompressible(p=None, T=None, fluid_data=None, **kwargs): v = 0 for data in fluid_data.values(): if _is_larger_than_precision(data["mass_fraction"]): v += 1 / data["wrapper"].d_pT(p, T) * data["mass_fraction"] return v
[docs] def viscosity_mix_pT_ideal(p=None, T=None, fluid_data=None, **kwargs): r""" Calculate dynamic viscosity from pressure and temperature. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. T : float Temperature T / K. Returns ------- visc : float Dynamic viscosity visc / Pa s. Note ---- Calculation for fluid mixtures. .. math:: \eta_{mix}(p,T)=\frac{\sum_{i} \left( \eta(p,T,fluid_{i}) \cdot y_{i} \cdot \sqrt{M_{i}} \right)} {\sum_{i} \left(y_{i} \cdot \sqrt{M_{i}} \right)}\; \forall i \in \text{fluid components}\\ y: \text{volume fraction}\\ M: \text{molar mass} Reference: :cite:`Herning1936`. """ molar_fractions = get_molar_fractions(fluid_data) a = 0 b = 0 for fluid, data in fluid_data.items(): if _is_larger_than_precision(data["mass_fraction"]): bi = molar_fractions[fluid] * data["wrapper"]._molar_mass ** 0.5 b += bi a += bi * data["wrapper"].viscosity_pT(p, T) return a / b
[docs] def viscosity_mix_pT_incompressible(p=None, T=None, fluid_data=None, **kwargs): viscosity = 0 for data in fluid_data.values(): if _is_larger_than_precision(data["mass_fraction"]): viscosity += data["wrapper"].viscosity_pT(p, T) * data["mass_fraction"] return viscosity
[docs] def exergy_chemical_ideal_cond(pamb, Tamb, fluid_data, Chem_Ex): molar_fractions = get_molar_fractions(fluid_data) water_alias = _water_in_mixture(fluid_data) if water_alias: water_alias = next(iter(water_alias)) _, molar_fractions_gas, _, molar_liquid = cond_check( pamb, Tamb, fluid_data, water_alias ) else: molar_fractions_gas = molar_fractions molar_liquid = 0 ex_cond = 0 ex_dry = 0 for fluid, x in molar_fractions_gas.items(): if x == 0: continue fluid_aliases = fluid_data[fluid]["wrapper"]._aliases if molar_liquid > 0 and "water" in fluid_aliases: y = [ Chem_Ex[k][2] for k in fluid_aliases if k in Chem_Ex ] ex_cond += molar_liquid * y[0] y = [Chem_Ex[k][3] for k in fluid_aliases if k in Chem_Ex] ex_dry += x * y[0] + Tamb * gas_constants['uni'] * 1e-3 * x * math.log(x) ex_chemical = ex_cond + ex_dry * (1 - molar_liquid) ex_chemical *= 1 / calc_molar_mass_mixture( fluid_data, molar_fractions ) return ex_chemical * 1e3 # Data from Chem_Ex are in kJ / mol
def _water_in_mixture(fluid_data): water_aliases = set(CP.CoolProp.get_aliases("H2O")) return water_aliases & set([f for f in fluid_data if _is_larger_than_precision(fluid_data[f]["mass_fraction"])])
[docs] def cond_check(p, T, fluid_data, water_alias): """Check if water is partially condensing in gaseous mixture. Parameters ---------- y_i : dict Mass specific fluid composition. x_i : dict Mole specific fluid composition. p : float Pressure of mass flow. n : float Molar mass flow. T : float Temperature of mass flow. Returns ------- tuple Tuple containing gas phase mass specific and molar specific compositions and overall liquid water mass fraction. """ molar_fractions = get_molar_fractions(fluid_data) molar_fractions_gas = molar_fractions mass_fractions_gas = {f: v["mass_fraction"] for f, v in fluid_data.items()} water_mass_liquid = 0 water_molar_liquid = 0 if fluid_data[water_alias]["wrapper"]._is_below_T_critical(T): p_sat = fluid_data[water_alias]["wrapper"].p_sat(T) pp_water = p * molar_fractions[water_alias] if p_sat < pp_water: water_molar_gas = (1 - molar_fractions[water_alias]) / (p / p_sat - 1) water_molar_liquid = molar_fractions[water_alias] - water_molar_gas x_gas_sum = 1 - water_molar_liquid molar_fractions_gas = {f: x / x_gas_sum for f, x in molar_fractions.items()} molar_fractions_gas[water_alias] = water_molar_gas / x_gas_sum water_mass_liquid = ( water_molar_liquid * fluid_data[water_alias]["wrapper"]._molar_mass / calc_molar_mass_mixture(fluid_data, molar_fractions) ) molar_mass_mixture = calc_molar_mass_mixture(fluid_data, molar_fractions_gas) mass_fractions_gas = { fluid: ( x / molar_mass_mixture * fluid_data[fluid]["wrapper"]._molar_mass ) for fluid, x in molar_fractions_gas.items() } return mass_fractions_gas, molar_fractions_gas, water_mass_liquid, water_molar_liquid
T_MIX_PH_REVERSE = { "ideal": h_mix_pT_ideal, "ideal-cond": h_mix_pT_ideal_cond, "incompressible": h_mix_pT_incompressible } T_MIX_PS_REVERSE = { "ideal": s_mix_pT_ideal, "ideal-cond": s_mix_pT_ideal_cond, "incompressible": s_mix_pT_incompressible } H_MIX_PT_DIRECT = { "ideal": h_mix_pT_ideal, "ideal-cond": h_mix_pT_ideal_cond, "incompressible": h_mix_pT_incompressible, "forced-gas": h_mix_pT_forced_gas } S_MIX_PT_DIRECT = { "ideal": s_mix_pT_ideal, "ideal-cond": s_mix_pT_ideal_cond, "incompressible": s_mix_pT_incompressible } V_MIX_PT_DIRECT = { "ideal": v_mix_pT_ideal, "ideal-cond": v_mix_pT_ideal_cond, "incompressible": v_mix_pT_incompressible } VISCOSITY_MIX_PT_DIRECT = { "ideal": viscosity_mix_pT_ideal, "ideal-cond": viscosity_mix_pT_ideal, "incompressible": viscosity_mix_pT_incompressible } EXERGY_CHEMICAL = { "ideal-cond": exergy_chemical_ideal_cond, }