Source code for tespy.connections.humidairconnection

# -*- coding: utf-8
"""Module of class Connection and class Ref.


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/connections/humidairconnection.py

SPDX-License-Identifier: MIT
"""
from CoolProp.CoolProp import HAPropsSI

from tespy.tools import fluid_properties as fp
from tespy.tools.data_containers import FluidComposition as dc_flu
from tespy.tools.data_containers import FluidProperties as dc_prop
from tespy.tools.data_containers import SimpleDataContainer as dc_simple
from tespy.tools.fluid_properties.functions import h_mix_pT
from tespy.tools.fluid_properties.mixtures import _get_fluid_alias
from tespy.tools.fluid_properties.mixtures import w_mix_fluid_data
from tespy.tools.fluid_properties.mixtures import w_mix_pT_humidair
from tespy.tools.helpers import seeded_random

from .connection import Connection
from .connection import connection_registry


[docs] @connection_registry class HAConnection(Connection):
[docs] def get_parameters(self): return { "fluid": dc_flu( d=1e-5, description="mass fractions of the fluid composition" ), "m": dc_prop( quantity="mass_flow", description="mass flow of dry air (system variable)" ), "mHA": dc_prop( quantity="mass_flow", description="mass flow of humid air" ), "mH2O": dc_prop( quantity="mass_flow", description="mass flow of liquid or solid water not contained in humid air" ), "p": dc_prop( quantity="pressure", description="absolute pressure of the fluid (system variable)" ), "h": dc_prop( quantity="enthalpy", description="dry air mass specific enthalpy (system variable)" ), "w": dc_prop( quantity="ratio", description="mass of water per mass of dry air" ), "T": dc_prop( func=self.T_func, dependents=self.T_dependents, num_eq=1, quantity="temperature", description="temperature of the fluid" ), "v": dc_prop( func=self.v_func, dependents=self.v_dependents, num_eq=1, quantity="volumetric_flow", description="volumetric flow of the fluid" ), "vol": dc_prop( quantity="specific_volume", description="specific volume of the fluid (output only)" ), "s": dc_prop( quantity="entropy", description="specific entropy of the fluid (output only)" ), "r": dc_prop( func=self.r_func, dependents=self.r_dependents, num_eq=1, quantity="ratio", description="relative humidity" ), "fluid_balance": dc_simple( func=self.fluid_balance_func, deriv=self.fluid_balance_deriv, _val=False, num_eq_sets=1, dependents=self.fluid_balance_dependents, description="apply an equation which closes the fluid balance with at least two unknown fluid mass fractions" ), }
def _parameter_specification(self, key, value): if key == "w" or key == "w0": if value is None: self.fluid.is_set = {} else: # specification of w is equivalent to specification of fluid # composition for humid air air = 1 / (1 + value) if key == "w": self.set_attr(fluid={"air": air, "water": 1 - air}) else: self.set_attr(fluid0={"air": air, "water": 1 - air}) else: super()._parameter_specification(key, value) # for HAConnection mixing rule cannot be modified, is always humidair def _get_mixing_rule(self): return "humidair" def _set_mixing_rule(self, value): if value is not None and value != self.mixing_rule: msg = ( "You cannot change the mixing rule specification for a " f"Connection of type {self.__class__.__name__}" ) raise ValueError(msg) mixing_rule = property(_get_mixing_rule, _set_mixing_rule) def _guess_starting_values(self, units): if self.h.is_var and not self.good_starting_values: value = seeded_random(self.label) T_rand = 280 + value * (300 - 280) h = fp.h_mix_pT(1e5, T_rand, self.fluid_data, self.mixing_rule) self.h.set_reference_val_SI(h) self._precalc_guess_values() def _precalc_guess_values(self): if not self.h.is_var: return if not self.good_starting_values: if self.T.is_set: try: w = self.calc_w() self.h.set_reference_val_SI( HAPropsSI("H", "P", self.p.val_SI, "T", self.T.val_SI, "W", w) ) except ValueError: pass def _precalc_guess_values_for_references(self): """precalculate starting values for specified temperature references """ pass def _presolve(self): air_alias = _get_fluid_alias("air", self.fluid_data) water_alias = _get_fluid_alias("water", self.fluid_data) if not air_alias: msg = "air must be present in fluid composition" raise ValueError(msg) elif not water_alias: msg = "water must be present in fluid composition" raise ValueError(msg) if len(self.fluid.is_var) > 0: return [] presolved_equations = [] if self.h.is_var and not self.p.is_var: if self.T.is_set: self.h.set_reference_val_SI(h_mix_pT(self.p.val_SI, self.T.val_SI, self.fluid_data, self.mixing_rule)) self.h._potential_var = False if "T" in self._equation_set_lookup.values(): presolved_equations += ["T"] presolved_equations = [ key for parameter in presolved_equations for key, value in self._equation_set_lookup.items() if value == parameter ] return presolved_equations def _adjust_to_property_limits(self, nw): if self.r.is_set and self.it < 5: # with relative humidity specified and at beginning of iterations # large water fraction is unlikely expected air_alias = list(_get_fluid_alias("air", self.fluid_data))[0] if air_alias in self.fluid.is_var: if self.fluid.val[air_alias] < 0.8: self.fluid.set_reference_val(air_alias, 0.95) water_alias = list(_get_fluid_alias("water", self.fluid_data))[0] if water_alias in self.fluid.is_var: if self.fluid.val[water_alias] > 0.2: self.fluid.set_reference_val(water_alias, 0.05) if self.p.is_var: if self.p.val_SI < 100: self.p.val_SI = 101 elif self.p.val_SI > 100e5: self.p.val_SI = 99e5 if self.h.is_var: # TODO: check minimum temperature how it matches minimum humidity ratio d = self.h._reference_container._d hmin = HAPropsSI("H", "T", -50 + 273.15, "P", self.p.val_SI, "R", 1) if self.h.val_SI < hmin: delta = max(abs(self.h.val_SI * d), d) * 5 self.h.set_reference_val_SI(hmin + delta) else: # TODO: where to get reasonable hmax from?! hmax = HAPropsSI("H", "T", 300 + 273.15, "P", self.p.val_SI, "R", 0) if self.h.val_SI > hmax: delta = max(abs(self.h.val_SI * d), d) * 5 self.h.set_reference_val_SI(hmax - delta) @classmethod def _result_attributes(cls): return ["m", "mHA", "mH2O", "p", "h", "T", "w", "s", "vol", "v", "r"] @classmethod def _print_attributes(cls): return ["m", "mHA", "mH2O", "p", "h", "T", "w", "r"]
[docs] def calc_r(self): w = self.calc_w() try: return HAPropsSI("R", "P", self.p.val_SI, "T", self.T.val_SI, "W", w) except ValueError as e: value = str(e).split("value (")[1].split(")")[0] return float(value)
[docs] def r_func(self): return self.r.val_SI - self.calc_r()
[docs] def r_dependents(self): water_alias = _get_fluid_alias("H2O", self.fluid_data) # water alias is already a set return { "scalars": [self.p, self.h], "vectors": [{self.fluid: water_alias}] }
[docs] def calc_w(self): return w_mix_pT_humidair(self.p.val_SI, self.T.val_SI, self.fluid_data)
[docs] def calc_results(self, units, skip_postprocess): self.m.set_val0_from_SI(units) self.p.set_val0_from_SI(units) self.h.set_val0_from_SI(units) self.fluid.val0 = self.fluid.val.copy() if skip_postprocess: return True self.T.val_SI = self.calc_T() self.vol.val_SI = self.calc_vol() # Mixture volume per mass of dry air self.v.val_SI = self.vol.val_SI * self.m.val_SI # Mixture volume flow rate # handle the water fraction self.w.val_SI = self.calc_w() # # Convert from kg water/kg dry air to mass fraction of water in humid air # x_h2o = self.w.val_SI / (1 + self.w.val_SI) # x_air = 1 - x_h2o self.mHA.val_SI = self.m.val_SI * (1 + self.w.val_SI) w_mixture = w_mix_fluid_data(self.fluid_data) # Calculate kg water/kg dry air that is not in the humid air delta_w = w_mixture - self.w.val_SI self.mH2O.val_SI = self.m.val_SI * delta_w self.r.val_SI = self.calc_r() # if self.r.val_SI > 1: # self.r.val_SI = np.nan for prop in self._result_attributes(): param = self.get_attr(prop) if not param.is_set: param.set_val_from_SI(units) return True