def __init__(self, fluids): num_fl = len(fluids) if num_fl > 0: fl = tuple(fluids) memorise.T_ph[fl] = np.empty((0, num_fl + 3), float) memorise.T_ph_f[fl] = [] memorise.T_ps[fl] = np.empty((0, num_fl + 4), float) memorise.T_ps_f[fl] = [] memorise.v_ph[fl] = np.empty((0, num_fl + 3), float) memorise.v_ph_f[fl] = [] memorise.visc_ph[fl] = np.empty((0, num_fl + 3), float) memorise.visc_ph_f[fl] = [] memorise.s_ph[fl] = np.empty((0, num_fl + 3), float) memorise.s_ph_f[fl] = [] memorise.count = 0 for f in fluids: try: pmin, pmax = CPPSI('PMIN', f), CPPSI('PMAX', f) except ValueError: pmin, pmax = 2000, 2000e5 for f in fluids: try: Tmin, Tmax = CPPSI('TMIN', f), CPPSI('TMAX', f) except ValueError: Tmin, Tmax = 2000, 2000e5 memorise.vrange[f] = [pmin, pmax, Tmin, Tmax]
def ds_mix_pdT(flow, T): r""" calculates partial derivate of entropy to temperature at constant pressure :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :param T: temperature in K :type T: numeric :returns: ds / dT (float) - derivative in J / (kg * K :sup:`2`) .. math:: \frac{\partial s_{mix}}{\partial T} = \frac{s_{mix}(p,T+d)-s_{mix}(p,T-d)}{2 \cdot d} """ n = molar_massflow(flow[3]) d = 2 s_u = 0 s_l = 0 for fluid, x in flow[3].items(): if x > err: pp = flow[1] * x / (molar_masses[fluid] * n) s_u += CPPSI('S', 'P', pp, 'T', T + d, fluid) * x s_l += CPPSI('S', 'P', pp, 'T', T - d, fluid) * x return (s_u - s_l) / (2 * d)
def h_mix_pQ(flow, Q): """ calculates enthalpy from pressure and quality .. note:: This function works for pure fluids only! :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :param Q: fraction of vapour mass to total mass in 1 :type Q: numeric :returns: h (float) - enthalpy in J / kg """ n = molar_massflow(flow[3]) h = 0 for fluid, x in flow[3].items(): if x > err: pp = flow[1] * x / (molar_masses[fluid] * n) pcrit = CPPSI('Pcrit', fluid) while pp > pcrit: flow[1] = flow[1] * 0.95 h += CPPSI('H', 'P', pp, 'Q', Q, fluid) * x return h
def h_ps(p, s, fluid): r""" Calculates the enthalpy from pressure and entropy for a pure fluid. Parameters ---------- p : float Pressure p / Pa. s : float Specific entropy h / (J/(kgK)). fluid : str Fluid name. Returns ------- h : float Specific enthalpy h / (J/kg). """ # if 'IDGAS::' in fluid: # msg = 'Ideal gas calculation not available by now.' # logging.warning(msg) if 'TESPy::' in fluid: db = tespy_fluid.fluids[fluid].funcs['s_pT'] T = newton(reverse_2d, reverse_2d_deriv, [db, p, s], 0) return tespy_fluid.fluids[fluid].funcs['h_pT'].ev(p, T) elif 'INCOMP::' in fluid: return CPPSI('H', 'P', p, 'S', s, fluid) else: memorise.heos[fluid].update(CP.PSmass_INPUTS, p, s) return memorise.heos[fluid].hmass()
def visc_pT(p, T, fluid): r""" Calculates the dynamic viscosity from pressure and temperature for a pure fluid. Parameters ---------- p : float Pressure p / Pa. T : float Temperature T / K. fluid : str Fluid name. Returns ------- visc : float Viscosity visc / Pa s. """ # if 'IDGAS::' in fluid: # msg = 'Ideal gas calculation not available by now.' # logging.warning(msg) if 'TESPy::' in fluid: return tespy_fluid.fluids[fluid].funcs['visc_pT'].ev(p, T) elif 'INCOMP::' in fluid: return CPPSI('V', 'P', p, 'T', T, fluid) else: memorise.heos[fluid].update(CP.PT_INPUTS, p, T) return memorise.heos[fluid].viscosity()
def visc_mix_pT(flow, T): r""" calculates dynamic viscosity from pressure and temperature :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :param T: temperature in K :type T: numeric :returns: v (float) - specific volume in kg / m :sup:`3` .. math:: \eta_{mix}(p,T)=\frac{\sum_{i} \left( \eta(p,T,fluid_{i}) \cdot y_{i} \cdot M_{i} \right)} {\sum_{i} \left(y_{i} \cdot M_{i} \right)}\; \forall i \in \text{fluid components}\\ y: \text{volume fraction}\\ M: \text{molar mass} """ n = molar_massflow(flow[3]) a = 0 b = 0 for fluid, x in flow[3].items(): if x > err: bi = x * math.sqrt(molar_masses[fluid]) / (molar_masses[fluid] * n) b += bi a += bi * CPPSI('V', 'P', flow[1], 'T', T, fluid) return a / b
def h_mix_pQ(flow, Q): r""" Calculates the enthalpy from pressure and vapour mass fraction. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. Q : float Vapour mass fraction Q / 1. Returns ------- h : float Specific enthalpy h / (J/kg). Note ---- This function works for pure fluids only! """ fluid = single_fluid(flow[3]) if not isinstance(fluid, str): msg = 'The function h_mix_pQ can only be used for pure fluids.' logging.error(msg) raise ValueError(msg) pcrit = CPPSI('Pcrit', fluid) if flow[1] > pcrit: memorise.heos[fluid].update(CP.PQ_INPUTS, pcrit * 0.95, Q) else: memorise.heos[fluid].update(CP.PQ_INPUTS, flow[1], Q) return memorise.heos[fluid].hmass()
def T_bp_p(flow): r""" Calculates temperature from boiling point pressure. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. Returns ------- T : float Temperature at boiling point. Note ---- This function works for pure fluids only! """ for fluid, x in flow[3].items(): if x > err: pcrit = CPPSI('Pcrit', fluid) if flow[1] > pcrit: memorise.heos[fluid].update(CP.PQ_INPUTS, pcrit * 0.95, 1) else: memorise.heos[fluid].update(CP.PQ_INPUTS, flow[1], 1) return memorise.heos[fluid].T()
def d_pT(p, T, fluid): r""" Calculates the density from pressure and temperature for a pure fluid. Parameters ---------- p : float Pressure p / Pa. T : float Temperature T / K. fluid : str Fluid name. Returns ------- d : float Density d / (kg/:math:`\mathrm{m}^3`). """ # if 'IDGAS::' in fluid: # msg = 'Ideal gas calculation not available by now.' # logging.warning(msg) if 'TESPy::' in fluid: return tespy_fluid.fluids[fluid].funcs['d_pT'].ev(p, T) elif 'INCOMP::' in fluid: return CPPSI('D', 'P', p, 'T', T, fluid) else: memorise.heos[fluid].update(CP.PT_INPUTS, p, T) return memorise.heos[fluid].rhomass()
def s_mix_pT(flow, T): r""" calculates entropy from pressure and temperature uses CoolProp reverse functions for pure fluids, newton for mixtures :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :param T: temperature in K :type T: numeric :returns: s (float) - entropy in J / (kg * K) .. math:: s_{mix}(p,T)=\sum_{i} x_{i} \cdot s(pp_{i},T,fluid_{i})- \sum_{i} x_{i} \cdot R_{i} \cdot \ln \frac{pp_{i}}{p}\; \forall i \in \text{fluid components}\\ pp: \text{partial pressure}\\ R: \text{gas constant} """ n = molar_massflow(flow[3]) s = 0 for fluid, x in flow[3].items(): if x > err: pp = flow[1] * x / (molar_masses[fluid] * n) s += CPPSI('S', 'P', pp, 'T', T, fluid) * x s -= (x * gas_constants[fluid] / molar_masses[fluid] * math.log(pp / flow[1])) return s
def T_ph(p, h, fluid): r""" Calculates the temperature from pressure and enthalpy for a pure fluid. Parameters ---------- p : float Pressure p / Pa. h : float Specific enthalpy h / (J/kg). fluid : str Fluid name. Returns ------- T : float Temperature T / K. """ # if 'IDGAS::' in fluid: # msg = 'Ideal gas calculation not available by now.' # logging.warning(msg) if 'TESPy::' in fluid: db = tespy_fluid.fluids[fluid].funcs['h_pT'] return newton(reverse_2d, reverse_2d_deriv, [db, p, h], 0) elif 'INCOMP::' in fluid: return CPPSI('T', 'P', p, 'H', h, fluid) else: memorise.heos[fluid].update(CP.HmassP_INPUTS, h, p) return memorise.heos[fluid].T()
def h_pT(p, T, fluid): r""" Calculates the enthalpy from pressure and temperature for a pure fluid. Parameters ---------- p : float Pressure p / Pa. T : float Temperature T / K. fluid : str Fluid name. Returns ------- h : float Specific enthalpy h / (J/kg). """ # if 'IDGAS::' in fluid: # msg = 'Ideal gas calculation not available by now.' # logging.warning(msg) if 'TESPy::' in fluid: return tespy_fluid.fluids[fluid].funcs['h_pT'].ev(p, T) elif 'INCOMP::' in fluid: return CPPSI('H', 'P', p, 'T', T, fluid) else: memorise.heos[fluid].update(CP.PT_INPUTS, p, T) return memorise.heos[fluid].hmass()
def visc_ph(p, h, fluid): r""" Calculates the dynamic viscosity from pressure and enthalpy for a pure fluid. Parameters ---------- p : float Pressure p / Pa. h : float Specific enthalpy h / (J/kg). fluid : str Fluid name. Returns ------- visc : float Viscosity visc / Pa s. """ # if 'IDGAS::' in fluid: # msg = 'Ideal gas calculation not available by now.' # logging.warning(msg) if 'TESPy::' in fluid: db = tespy_fluid.fluids[fluid].funcs['h_pT'] T = newton(reverse_2d, reverse_2d_deriv, [db, p, h], 0) return tespy_fluid.fluids[fluid].funcs['visc_pT'].ev(p, T) elif 'INCOMP::' in fluid: return CPPSI('V', 'P', p, 'H', h, fluid) else: memorise.heos[fluid].update(CP.HmassP_INPUTS, h, p) return memorise.heos[fluid].viscosity()
def h_mix_pT(flow, T): r""" calculates enthalpy from pressure and temperature :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :param T: temperature in K :type T: numeric :returns: h (float) - enthalpy in J / kg .. math:: h_{mix}(p,T)=\sum_{i} h(pp_{i},T,fluid_{i})\; \forall i \in \text{fluid components}\\ pp: \text{partial pressure} """ n = molar_massflow(flow[3]) h = 0 for fluid, x in flow[3].items(): if x > err: ni = x / molar_masses[fluid] h += CPPSI('H', 'P', flow[1] * ni / n, 'T', T, fluid) * x return h
def T_mix_ph(flow): r""" calculates the temperature from pressure and enthalpy, uses CoolProp reverse functions for pure fluids, newton for mixtures - check if property has already been memorised - calculate property otherwise :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :returns: T (float) - temperature in K **fluid mixtures** .. math:: T_{mix}\left(p,h\right) = T_{i}\left(pp_{i},h_{i}\right)\; \forall i \in \text{fluid components}\\ h_{i} = h \left(pp_{i}, T_{mix} \right)\\ pp: \text{partial pressure} """ fl = tuple(sorted(list(flow[3].keys()))) a = memorise.T_ph[fl][:, 0:-1] b = np.array([flow[1], flow[2]] + list(flow[3].values())) ix = np.where(np.all(abs(a - b) <= err**2, axis=1))[0] if ix.size == 1: T = memorise.T_ph[fl][ix, -1][0] memorise.T_ph_f[fl] += [T] return T else: if num_fluids(flow[3]) > 1: val = newton(h_mix_pT, dh_mix_pdT, flow, flow[2], val0=300, valmin=70, valmax=3000, imax=10) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [val]]) memorise.T_ph[fl] = np.append(memorise.T_ph[fl], new, axis=0) return val else: for fluid, x in flow[3].items(): if x > err: val = CPPSI('T', 'H', flow[2], 'P', flow[1], fluid) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [val]]) memorise.T_ph[fl] = np.append(memorise.T_ph[fl], new, axis=0) return val
def T_mix_ps(flow, s): r""" calculates the temperature from pressure and entropy, uses CoolProp reverse functions for pure fluids, newton for mixtures :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :param s: entropy in J / (kg * K) :type s: numeric :returns: T (float) - temperature in K **fluid mixtures** .. math:: T_{mix}\left(p,s\right) = T_{i}\left(pp_{i},s_{i}\right)\; \forall i \in \text{fluid components}\\ s_{i} = s \left(pp_{i}, T_{mix} \right)\\ pp: \text{partial pressure} """ fl = tuple(sorted(list(flow[3].keys()))) a = memorise.T_ps[fl][:, 0:-1] b = np.array([flow[1], flow[2]] + list(flow[3].values()) + [s]) ix = np.where(np.all(abs(a - b) <= err**2, axis=1))[0] if ix.size == 1: T = memorise.T_ps[fl][ix, -1][0] memorise.T_ps_f[fl] += [T] return T else: if num_fluids(flow[3]) > 1: val = newton(s_mix_pT, ds_mix_pdT, flow, s, val0=300, valmin=70, valmax=3000, imax=10) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [s, val]]) memorise.T_ps[fl] = np.append(memorise.T_ps[fl], new, axis=0) return val else: for fluid, x in flow[3].items(): if x > err: val = CPPSI('T', 'S', s, 'P', flow[1], fluid) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [s, val]]) memorise.T_ps[fl] = np.append(memorise.T_ps[fl], new, axis=0) return val
def h_pT(p, T, fluid): r""" returns the enthalpy of a pure fluid given pressure and temperature :param p: pressure :type p: float :param T: temperature :type T: float :param fluid: fluid alias :type fluid: str :returns: h (float) - enthalpy in J / kg """ if 'IDGAS::' in fluid: print('Ideal gas calculation not available by now.') elif 'TESPy::' in fluid: return tespy_fluid.fluids[fluid].funcs['h_pT'].ev(p, T) else: return CPPSI('H', 'P', p, 'T', T, fluid)
def T_ps(p, s, fluid): r""" returns the temperature of a pure fluid given pressure and entropy :param p: pressure :type p: float :param s: entropy :type s: float :param fluid: fluid alias :type fluid: str :returns: T (float) - temperature in K """ if 'IDGAS::' in fluid: print('Ideal gas calculation not available by now.') elif 'TESPy::' in fluid: db = tespy_fluid.fluids[fluid].funcs['s_pT'] return newton(reverse_2d, reverse_2d_deriv, [db, p, s], 0) else: return CPPSI('T', 'P', p, 'S', s, fluid)
def visc_pT(p, T, fluid): r""" returns the dynamic viscosity of a pure fluid given pressure and temperature :param p: pressure :type p: float :param T: temperature :type T: float :param fluid: fluid alias :type fluid: str :returns: visc (float) - dynamic viscosity in Pa s """ if 'IDGAS::' in fluid: print('Ideal gas calculation not available by now.') elif 'TESPy::' in fluid: return tespy_fluid.fluids[fluid].funcs['visc_pT'].ev(p, T) else: return CPPSI('V', 'P', p, 'T', T, fluid)
def molar_massflow(flow): r""" calculates molar massflow :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :returns: mm (float) - molar massflow in mol / s .. math:: mm = \sum_{i} \left( \frac{x_{i}}{M_{i}} \right) """ mm = 0 for fluid, x in flow.items(): if x > err: try: mm += x / molar_masses[fluid] except: mm += x / CPPSI('molar_mass', fluid) return mm
def visc_ph(p, h, fluid): r""" returns the dynamic viscosity of a pure fluid given pressure and enthalpy :param p: pressure :type p: float :param h: enthalpy :type h: float :param fluid: fluid alias :type fluid: str :returns: visc (float) - dynamic viscosity in Pa s """ if 'IDGAS::' in fluid: print('Ideal gas calculation not available by now.') elif 'TESPy::' in fluid: db = tespy_fluid.fluids[fluid].funcs['h_pT'] T = newton(reverse_2d, reverse_2d_deriv, [db, p, h], 0) return tespy_fluid.fluids[fluid].funcs['visc_pT'].ev(p, T) else: return CPPSI('V', 'P', p, 'H', h, fluid)
def h_ps(p, s, fluid): r""" returns the enthalpy of a pure fluid given pressure and entropy :param p: pressure :type p: float :param s: entropy :type s: float :param fluid: fluid alias :type fluid: str :returns: h (float) - enthalpy in J / kg """ if 'IDGAS::' in fluid: print('Ideal gas calculation not available by now.') elif 'TESPy::' in fluid: db = tespy_fluid.fluids[fluid].funcs['s_pT'] T = newton(reverse_2d, reverse_2d_deriv, [db, p, s], 0) return tespy_fluid.fluids[fluid].funcs['h_pT'].ev(p, T) else: return CPPSI('H', 'P', p, 'S', s, fluid)
def visc_mix_ph(flow): r""" calculates dynamic viscosity from pressure and enthalpy, uses CoolProp reverse functions for pure fluids, newton for mixtures :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :returns: v (float) - specific volume in Pa s **fluid mixtures** .. math:: \eta_{mix}\left(p,h\right) = \eta\left(p,T_{mix}(p,h)\right) """ fl = tuple(sorted(list(flow[3].keys()))) a = memorise.visc_ph[fl][:, 0:-1] b = np.array([flow[1], flow[2]] + list(flow[3].values())) ix = np.where(np.all(abs(a - b) <= err**2, axis=1))[0] if ix.size == 1: visc = memorise.visc_ph[fl][ix, -1][0] memorise.visc_ph_f[fl] += [visc] return visc else: if num_fluids(flow[3]) > 1: val = visc_mix_pT(flow, T_mix_ph(flow)) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [val]]) memorise.visc_ph[fl] = np.append(memorise.visc_ph[fl], new, axis=0) return val else: for fluid, x in flow[3].items(): if x > err: val = CPPSI('V', 'P', flow[1], 'H', flow[2], fluid) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [val]]) memorise.visc_ph[fl] = np.append(memorise.visc_ph[fl], new, axis=0) return val
def s_mix_ph(flow): r""" calculates entropy from pressure and enthalpy uses CoolProp reverse functions for pure fluids, newton for mixtures :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :returns: s (float) - entropy in J / (kg * K) **fluid mixtures** .. math:: s_{mix}\left(p,h\right) = s\left(p,T_{mix}(p,h)\right) """ fl = tuple(sorted(list(flow[3].keys()))) a = memorise.s_ph[fl][:, 0:-1] b = np.array([flow[1], flow[2]] + list(flow[3].values())) ix = np.where(np.all(abs(a - b) <= err**2, axis=1))[0] if ix.size == 1: s = memorise.s_ph[fl][ix, -1][0] memorise.s_ph_f[fl] += [s] return s else: if num_fluids(flow[3]) > 1: val = s_mix_pT(flow, T_mix_ph(flow)) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [val]]) memorise.s_ph[fl] = np.append(memorise.s_ph[fl], new, axis=0) return val else: for fluid, x in flow[3].items(): if x > err: val = CPPSI('S', 'P', flow[1], 'H', flow[2], fluid) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [val]]) memorise.s_ph[fl] = np.append(memorise.s_ph[fl], new, axis=0) return val
def v_mix_pT(flow, T): r""" calculates specific volume from pressure and temperature :param flow: vector containing [mass flow, pressure, enthalpy, fluid] :type flow: list :param T: temperature in K :type T: numeric :returns: v (float) - specific volume in kg / m :sup:`3` .. math:: v_{mix}(p,T)=\sum_{i} v(pp_{i},T,fluid_{i})\; \forall i \in \text{fluid components}\\ pp: \text{partial pressure} """ n = molar_massflow(flow[3]) d = 0 for fluid, x in flow[3].items(): if x > err: pp = flow[1] * x / (molar_masses[fluid] * n) d += CPPSI('D', 'P', pp, 'T', T, fluid) * x return 1 / d
def add_fluids(fluids, memorise_fluid_properties=True): r""" Add list of fluids to fluid memorisation class. - Generate arrays for fluid property lookup if memorisation is activated. - Calculate/set fluid property value ranges for convergence checks. Parameters ---------- fluids : dict Dict of fluid and corresponding CoolProp back end for fluid property memorization. memorise_fluid_properties : boolean Activate or deactivate fluid property value memorisation. Default state is activated (:code:`True`). Note ---- The Memorise class creates globally accessible variables for different fluid property calls as dictionaries: - T(p,h) - T(p,s) - v(p,h) - visc(p,h) - s(p,h) Each dictionary uses the list of fluids passed to the Memorise class as identifier for the fluid property memorisation. The fluid properties are stored as numpy array, where each column represents the mass fraction of the respective fluid and the additional columns are the values for the fluid properties. The fluid property function will then look for identical fluid property inputs (p, h, (s), fluid mass fraction). If the inputs are in the array, the first column of that row is returned, see example. Example ------- T(p,h) for set of fluids ('water', 'air'): - row 1: [282.64527752319697, 10000, 40000, 1, 0] - row 2: [284.3140698256616, 10000, 47000, 1, 0] """ # number of fluids num_fl = len(fluids) if memorise_fluid_properties and num_fl > 0: fl = tuple(fluids.keys()) # fluid property tables Memorise.T_ph[fl] = np.empty((0, num_fl + 4), float) Memorise.T_ps[fl] = np.empty((0, num_fl + 5), float) Memorise.v_ph[fl] = np.empty((0, num_fl + 4), float) Memorise.visc_ph[fl] = np.empty((0, num_fl + 4), float) Memorise.s_ph[fl] = np.empty((0, num_fl + 4), float) msg = ( 'Added fluids ' + ', '.join(fl) + ' to memorise lookup tables.') logging.debug(msg) Memorise.water = None for f, back_end in fluids.items(): # save name for water in memorise if f in get_aliases("H2O"): Memorise.water = f if f in Memorise.state: del Memorise.state[f] # create CoolProp.AbstractState object try: Memorise.state[f] = CP.AbstractState(back_end, f) Memorise.back_end[f] = back_end except ValueError: msg = ( 'Could not find the fluid "' + f + '" in the fluid ' 'property database.' ) logging.warning(msg) continue msg = ( 'Created CoolProp.AbstractState object for fluid ' + f + ' with back end ' + back_end + '.') logging.debug(msg) # pressure range try: pmin = Memorise.state[f].trivial_keyed_output(CP.iP_min) pmax = Memorise.state[f].trivial_keyed_output(CP.iP_max) except ValueError: pmin = 1e4 pmax = 1e8 msg = ( 'Could not find values for maximum and minimum ' 'pressure.') logging.warning(msg) # temperature range Tmin = Memorise.state[f].trivial_keyed_output(CP.iT_min) Tmax = Memorise.state[f].trivial_keyed_output(CP.iT_max) # value range for fluid properties Memorise.value_range[f] = [pmin, pmax, Tmin, Tmax] try: molar_masses[f] = Memorise.state[f].molar_mass() gas_constants[f] = Memorise.state[f].gas_constant() except ValueError: try: molar_masses[f] = CPPSI('M', f) gas_constants[f] = CPPSI('GAS_CONSTANT', f) except ValueError: molar_masses[f] = 1 gas_constants[f] = 1 msg = ( 'Could not find values for molar mass and gas ' 'constant.') logging.warning(msg) msg = ( 'Specifying fluid property ranges for pressure and ' 'temperature for convergence check of fluid ' + f + '.') logging.debug(msg)
def __init__(self, alias, fluid, p_range, T_range, **kwargs): if not hasattr(tespy_fluid, 'fluids'): tespy_fluid.fluids = {} if not isinstance(alias, str): msg = 'Alias must be of type String.' raise TypeError(msg) if 'IDGAS::' in alias: msg = 'You are not allowed to use "IDGAS::" within your alias.' raise ValueError(msg) # process parameters if 'TESPy::' in alias: self.alias = alias else: self.alias = 'TESPy::' + alias self.fluid = fluid # load LUT from this path self.path = kwargs.get('path', dc_cp()) # adjust value ranges according to specified unit system self.p_range = np.array(p_range) self.T_range = np.array(T_range) # set up grid self.p = np.linspace(self.p_range[0], self.p_range[1]) self.T = np.linspace(self.T_range[0], self.T_range[1]) # plotting self.plot = kwargs.get('plot', False) # calculate molar mass and gas constant for f in self.fluid: molar_masses[f] = CPPSI('M', f) gas_constants[f] = CPPSI('GAS_CONSTANT', f) molar_masses[self.alias] = 1 / molar_massflow(self.fluid) gas_constants[self.alias] = (gas_constants['uni'] / molar_masses[self.alias]) # create look up tables tespy_fluid.fluids[self.alias] = {} params = {} params['h_pT'] = h_mix_pT params['s_pT'] = s_mix_pT params['d_pT'] = d_mix_pT params['visc_pT'] = visc_mix_pT self.funcs = {} if not self.path.is_set: for key in params.keys(): self.funcs[key] = self.generate_lookup(key, params[key]) else: for key in params.keys(): self.funcs[key] = self.load_lookup(key) tespy_fluid.fluids[self.alias] = self print('Successfully created LUTs for custom fluid ' + self.alias)
def add_fluids(fluids): r""" Add list of fluids to fluid memorisation class. - Generate arrays for fluid property lookup. - Calculate/set fluid property value ranges for convergence checks. Parameters ---------- fluids : dict Dict of fluid and corresponding CoolProp back end for fluid property memorization. Note ---- The memorise class creates globally accessible variables for different fluid property calls as dictionaries: - T(p,h) - T(p,s) - v(p,h) - visc(p,h) - s(p,h) Each dictionary uses the list of fluids passed to the memorise class as identifier for the fluid property memorisation. The fluid properties are stored as numpy array, where each column represents the mass fraction of the respective fluid and the additional columns are the values for the fluid properties. The fluid property function will then look for identical fluid property inputs (p, h, (s), fluid mass fraction). If the inputs are in the array, the first column of that row is returned, see example. Example ------- T(p,h) for set of fluids ('water', 'air'): - row 1: [282.64527752319697, 10000, 40000, 1, 0] - row 2: [284.3140698256616, 10000, 47000, 1, 0] """ # number of fluids num_fl = len(fluids) if num_fl > 0: fl = tuple(fluids.keys()) # fluid property tables memorise.T_ph[fl] = np.empty((0, num_fl + 3), float) memorise.T_ps[fl] = np.empty((0, num_fl + 4), float) memorise.v_ph[fl] = np.empty((0, num_fl + 3), float) memorise.visc_ph[fl] = np.empty((0, num_fl + 3), float) memorise.s_ph[fl] = np.empty((0, num_fl + 3), float) # lists for memory cache, values not in these lists will be deleted # from the table after every tespy.networks.network.solve call. memorise.T_ph_f[fl] = [] memorise.T_ps_f[fl] = [] memorise.v_ph_f[fl] = [] memorise.visc_ph_f[fl] = [] memorise.s_ph_f[fl] = [] memorise.count = 0 msg = 'Added fluids ' + str(fl) + ' to memorise lookup tables.' logging.debug(msg) for f, back_end in fluids.items(): if back_end == 'IF97': msg = ( 'Due to a bug in the IF97 CoolProp back end, it is not ' 'possible to use this back end at the moment. For more ' 'information see ' 'https://github.com/CoolProp/CoolProp/issues/1918.') logging.error(msg) raise ValueError(msg) elif f not in memorise.state.keys() and back_end != 'TESPy': # create CoolProp.AbstractState object try: memorise.state[f] = CP.AbstractState(back_end, f) except ValueError: msg = ( 'Could not find the fluid "' + f + '" in the fluid ' 'property database. If you are using a stoichimetric ' 'combustion chamber, this warning can be ignored in ' 'case the fluid is your fuel, your flue gas or your ' 'air.') logging.warning(msg) continue msg = ( 'Created CoolProp.AbstractState object for fluid ' + f + ' with back end ' + back_end + '.') logging.debug(msg) # pressure range try: pmin = memorise.state[f].trivial_keyed_output(CP.iP_min) pmax = memorise.state[f].trivial_keyed_output(CP.iP_max) except ValueError: pmin = 1e4 pmax = 1e8 msg = ( 'Could not find values for maximum and minimum ' 'pressure.') logging.warning(msg) # temperature range Tmin = memorise.state[f].trivial_keyed_output(CP.iT_min) Tmax = memorise.state[f].trivial_keyed_output(CP.iT_max) # value range for fluid properties memorise.value_range[f] = [pmin, pmax, Tmin, Tmax] try: molar_masses[f] = memorise.state[f].molar_mass() gas_constants[f] = memorise.state[f].gas_constant() except ValueError: try: molar_masses[f] = CPPSI('M', f) gas_constants[f] = CPPSI('GAS_CONSTANT', f) except ValueError: molar_masses[f] = 1 gas_constants[f] = 1 msg = ( 'Could not find values for molar mass and gas ' 'constant.') logging.warning(msg) msg = ( 'Specifying fluid property ranges for pressure and ' 'temperature for convergence check of fluid ' + f + '.') logging.debug(msg) elif back_end == 'TESPy': pmin = tespy_fluid.fluids[f].p[0] pmax = tespy_fluid.fluids[f].p[-1] Tmin = tespy_fluid.fluids[f].T[0] Tmax = tespy_fluid.fluids[f].T[-1] msg = ( 'Loading fluid property ranges for TESPy-fluid ' + f + '.') logging.debug(msg) # value range for fluid properties memorise.value_range[f] = [pmin, pmax, Tmin, Tmax] molar_masses[f] = 1 / molar_mass_flow( tespy_fluid.fluids[f].fluid) gas_constants[f] = ( gas_constants['uni'] / molar_masses[f]) msg = ('Specifying fluid property ranges for pressure and ' 'temperature for convergence check.') logging.debug(msg)
def add_fluids(fluids): r""" Add list of fluids to fluid memorisation class. - Generate arrays for fluid property lookup. - Calculate/set fluid property value ranges for convergence checks. Parameters ---------- fluids : list List of fluid for fluid property memorization. Note ---- The memorise class creates globally accessible variables for different fluid property calls as dictionaries: - T(p,h) - T(p,s) - v(p,h) - visc(p,h) - s(p,h) Each dictionary uses the list of fluids passed to the memorise class as identifier for the fluid property memorisation. The fluid properties are stored as numpy array, where each column represents the mass fraction of the respective fluid and the additional columns are the values for the fluid properties. The fluid property function will then look for identical fluid property inputs (p, h, (s), fluid mass fraction). If the inputs are in the array, the first column of that row is returned, see example. Example ------- T(p,h) for set of fluids ('water', 'air'): - row 1: [282.64527752319697, 10000, 40000, 1, 0] - row 2: [284.3140698256616, 10000, 47000, 1, 0] """ # number of fluids num_fl = len(fluids) if num_fl > 0: fl = tuple(fluids) # fluid property tables memorise.T_ph[fl] = np.empty((0, num_fl + 3), float) memorise.T_ps[fl] = np.empty((0, num_fl + 4), float) memorise.v_ph[fl] = np.empty((0, num_fl + 3), float) memorise.visc_ph[fl] = np.empty((0, num_fl + 3), float) memorise.s_ph[fl] = np.empty((0, num_fl + 3), float) # lists for memory cache, values not in these lists will be deleted # from the table after every tespy.networks.network.solve call. memorise.T_ph_f[fl] = [] memorise.T_ps_f[fl] = [] memorise.v_ph_f[fl] = [] memorise.visc_ph_f[fl] = [] memorise.s_ph_f[fl] = [] memorise.count = 0 msg = 'Added fluids ' + str(fl) + ' to memorise lookup tables.' logging.debug(msg) for f in fluids: if 'TESPy::' in f: if f in tespy_fluid.fluids.keys(): pmin = tespy_fluid.fluids[f].p_range[0] pmax = tespy_fluid.fluids[f].p_range[1] Tmin = tespy_fluid.fluids[f].T_range[0] Tmax = tespy_fluid.fluids[f].T_range[1] msg = ('Loading fluid property ranges for TESPy-fluid ' + f + '.') logging.debug(msg) # value range for fluid properties memorise.vrange[f] = [pmin, pmax, Tmin, Tmax] msg = ('Specifying fluid property ranges for pressure and ' 'temperature for convergence check.') logging.debug(msg) else: memorise.vrange[f] = [2000, 2000000, 300, 2000] elif 'INCOMP::' in f: # temperature range available only for incompressibles Tmin, Tmax = CPPSI('TMIN', f), CPPSI('TMAX', f) memorise.vrange[f] = [2000, 2000000, Tmin, Tmax] else: if f not in memorise.heos.keys(): # abstractstate object memorise.heos[f] = CP.AbstractState('HEOS', f) msg = ('Created CoolProp.AbstractState object for fluid ' + f + ' in memorise class.') logging.debug(msg) # pressure range pmin, pmax = CPPSI('PMIN', f), CPPSI('PMAX', f) # temperature range Tmin, Tmax = CPPSI('TMIN', f), CPPSI('TMAX', f) # value range for fluid properties memorise.vrange[f] = [pmin, pmax, Tmin, Tmax] msg = ('Specifying fluid property ranges for pressure and ' 'temperature for convergence check of fluid ' + f + '.') logging.debug(msg)
def __init__(self, alias, fluid, p_range, T_range, path=None, plot=False): if not isinstance(alias, str): msg = 'Alias must be of type String.' logging.error(msg) raise TypeError(msg) if 'IDGAS::' in alias: msg = 'You are not allowed to use "IDGAS::" within your alias.' logging.error(msg) raise ValueError(msg) # process parameters if 'TESPy::' in alias: self.alias = alias else: self.alias = 'TESPy::' + alias self.fluid = fluid # adjust value ranges according to specified unit system self.p_range = np.array(p_range) self.T_range = np.array(T_range) # set up grid self.p = np.linspace(self.p_range[0], self.p_range[1]) self.T = np.linspace(self.T_range[0], self.T_range[1]) # plotting self.plot = plot # path for loading self.path = path # calculate molar mass and gas constant for f in self.fluid: molar_masses[f] = CPPSI('M', f) gas_constants[f] = CPPSI('GAS_CONSTANT', f) molar_masses[self.alias] = 1 / molar_mass_flow(self.fluid) gas_constants[self.alias] = (gas_constants['uni'] / molar_masses[self.alias]) # create look up tables tespy_fluid.fluids[self.alias] = {} memorise.add_fluids(self.fluid.keys()) params = {} params['h_pT'] = h_mix_pT params['s_pT'] = s_mix_pT params['d_pT'] = d_mix_pT params['visc_pT'] = visc_mix_pT self.funcs = {} if self.path is None: # generate fluid properties msg = 'Generating lookup-tables from CoolProp fluid properties.' logging.debug(msg) for key in params.keys(): self.funcs[key] = self.generate_lookup(key, params[key]) msg = 'Loading function values for function ' + key + '.' logging.debug(msg) else: # load fluid properties from specified path msg = ('Generating lookup-tables from base path ' + self.path + '/' + self.alias + '/.') logging.debug(msg) for key in params.keys(): self.funcs[key] = self.load_lookup(key) msg = 'Loading function values for function ' + key + '.' logging.debug(msg) tespy_fluid.fluids[self.alias] = self msg = ('Successfully created look-up-tables for custom fluid ' + self.alias + '.') logging.debug(msg)