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

from tespy.tools.global_vars import FLUID_ALIASES
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, _, p_sat, pp_water = cond_check(p, T, fluid_data, water_alias) # at saturation liquid mass may be zero, but we cannot calculate water properties with pT if not _is_larger_than_precision(mass_liquid) and abs(pp_water - p_sat) / p_sat > 1e-6: 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: # at saturation liquid mass may be zero, but we cannot calculate water properties with pT if mass_liquid > 0: 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, _, p_sat, pp_water = cond_check(p, T, fluid_data, water_alias) # at saturation liquid mass may be zero, but we cannot calculate water properties with pT if not _is_larger_than_precision(mass_liquid) and abs(pp_water - p_sat) / p_sat > 1e-6: 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: if mass_liquid > 0: 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)) _, molar_fraction_gas, mass_liquid, _, p_sat, pp_water = cond_check(p, T, fluid_data, water_alias) # at saturation liquid mass may be zero, but we cannot calculate water properties with pT if not _is_larger_than_precision(mass_liquid) and abs(pp_water - p_sat) / p_sat > 1e-6: 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: if mass_liquid > 0: 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_ALIASES.get_fluid(fluid) 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): return ( FLUID_ALIASES.get_fluid("H2O") & 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 ---------- p : float pressure of mixture T : float temperature of mixture fluid_data : dict Dictionary of fluid data: :code:`{fluid_name: {"mass_fraction": float, "wrapper": FluidPropertyWrapper}}` water_alias : str label of the water in the fluid_data dictionary Returns ------- tuple Tuple containing gas phase mass specific and molar specific compositions and overall liquid water mass fraction, as well as saturation pressure of water and partial pressure of water in gas phase """ 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 # make something up here for now p_sat = p / 2 pp_water = p / 3 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, p_sat, pp_water
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, }