def isentropic(inflow, outflow, T0=675): r""" Calculate the enthalpy at the outlet after isentropic process. Parameters ---------- inflow : list Inflow fluid property vector containing mass flow, pressure, enthalpy and fluid composition. outflow : list Outflow fluid property vector containing mass flow, pressure, enthalpy and fluid composition. Returns ------- h_s : float Enthalpy after isentropic state change. .. math:: h_\mathrm{s} = \begin{cases} h\left(p_{out}, s\left(p_{in}, h_{in}\right) \right) & \text{pure fluids}\\ h\left(p_{out}, s\left(p_{in}, T_{in}\right) \right) & \text{mixtures}\\ \end{cases} """ fluid = single_fluid(inflow[3]) if fluid is not None: return h_ps(outflow[1], s_ph(inflow[1], inflow[2], fluid), fluid) else: s_mix = s_mix_ph(inflow) return h_mix_ps(outflow, s_mix, T0=T0)
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 h_mix_pQ(flow, Q): r""" Calculate 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 fluid is None: msg = 'The function h_mix_pQ can only be used for pure fluids.' logging.error(msg) raise ValueError(msg) try: memorise.state[fluid].update(CP.PQ_INPUTS, flow[1], Q) except ValueError: pcrit = memorise.state[fluid].trivial_keyed_output(CP.iP_critical) memorise.state[fluid].update(CP.PQ_INPUTS, pcrit * 0.99, Q) return memorise.state[fluid].hmass()
def T_bp_p(flow): r""" Calculate 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! """ fluid = single_fluid(flow[3]) pcrit = memorise.state[fluid].trivial_keyed_output(CP.iP_critical) if flow[1] > pcrit: memorise.state[fluid].update(CP.PQ_INPUTS, pcrit * 0.99, 1) else: memorise.state[fluid].update(CP.PQ_INPUTS, flow[1], 1) return memorise.state[fluid].T()
def s_mix_ph(flow, T0=300): r""" Calculate the entropy from pressure and enthalpy. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. Returns ------- s : float Specific entropy s / (J/(kgK)). Note ---- First, check if fluid property has been memorised already. If this is the case, return stored value, otherwise calculate value and store it in the memorisation class. Uses CoolProp interface for pure fluids, newton algorithm for mixtures: .. math:: s_{mix}\left(p,h\right) = s\left(p,T_{mix}(p,h)\right) """ # check if fluid properties have been calculated before fl = tuple(flow[3].keys()) memorisation = fl in memorise.s_ph.keys() if memorisation is True: a = memorise.s_ph[fl][:, :-1] b = np.asarray([flow[1], flow[2]] + list(flow[3].values())) ix = np.where(np.all(abs(a - b) <= err, axis=1))[0] if ix.size == 1: # known fluid properties s = memorise.s_ph[fl][ix, -1][0] memorise.s_ph_f[fl] += [s] return s # unknown fluid properties fluid = single_fluid(flow[3]) if fluid is None: # calculate the fluid properties for fluid mixtures val = s_mix_pT(flow, T_mix_ph(flow, T0=T0)) else: # calculate fluid property for pure fluids val = s_ph(flow[1], flow[2], fluid) if memorisation is True: # memorise the newly calculated value new = np.asarray([[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_ph(flow, T0=675): r""" Calculate the specific volume from pressure and enthalpy. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. Returns ------- v : float Specific volume v / (:math:`\mathrm{m}^3`/kg). Note ---- First, check if fluid property has been memorised already. If this is the case, return stored value, otherwise calculate value and store it in the memorisation class. Uses CoolProp interface for pure fluids, newton algorithm for mixtures: .. math:: v_{mix}\left(p,h\right) = v\left(p,T_{mix}(p,h)\right) """ # check if fluid properties have been calculated before fl = tuple(flow[3].keys()) memorisation = fl in Memorise.v_ph if memorisation: a = Memorise.v_ph[fl][:, :-2] b = np.asarray([flow[1], flow[2]] + list(flow[3].values())) ix = np.where(np.all(abs(a - b) <= err, axis=1))[0] if ix.size == 1: # known fluid properties Memorise.v_ph[fl][ix, -1] += 1 return Memorise.v_ph[fl][ix, -2][0] # unknown fluid properties fluid = single_fluid(flow[3]) if fluid is None: # calculate the fluid properties for fluid mixtures val = v_mix_pT(flow, T_mix_ph(flow, T0=T0)) else: # calculate fluid property for pure fluids val = 1 / d_ph(flow[1], flow[2], fluid) if memorisation: # memorise the newly calculated value new = np.asarray( [[flow[1], flow[2]] + list(flow[3].values()) + [val, 0]]) Memorise.v_ph[fl] = np.append(Memorise.v_ph[fl], new, axis=0) return val
def h_mix_pQ(flow, Q): r""" Calculate 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 fluid is None: if sum(flow[3].values()) == 0: msg = 'The function h_mix_pQ is called without fluid information.' logging.error(msg) raise ValueError(msg) else: msg = 'The function h_mix_pQ can only be used for pure fluids.' logging.error(msg) raise ValueError(msg) try: Memorise.state[fluid].update(CP.PQ_INPUTS, flow[1], Q) except ValueError: p_crit = get_p_crit(fluid) Memorise.state[fluid].update(CP.PQ_INPUTS, p_crit * 0.99, Q) return Memorise.state[fluid].hmass()
def create_group_data(self): """Collect the component group exergy data.""" for group in self.sankey_data.keys(): E_D = 0 for df in [self.component_data, self.bus_data]: E_D += df[df['group'] == group]['E_D'].sum() self.sankey_data[group].loc['E_D'] = [E_D, 'E_D'] # establish connections for fuel exergy via bus balance for b in self.E_F: input_value = self.calculate_group_input_value(b.label) self.sankey_data['E_F'].loc[b.label] = [ self.sankey_data[b.label]['value'].sum() - input_value, 'E_F' ] # establish connections for product exergy via bus balance for b in self.E_P: input_value = self.calculate_group_input_value(b.label) self.sankey_data[b.label].loc['E_P'] = [ input_value - self.sankey_data[b.label]['value'].sum(), 'E_P' ] # establish connections for exergy loss via bus balance for b in self.E_L: input_value = self.calculate_group_input_value(b.label) self.sankey_data[b.label].loc['E_L'] = [ input_value - self.sankey_data[b.label]['value'].sum(), 'E_L' ] for fkt_group, data in self.sankey_data.items(): comps = self.component_data[self.component_data['group'] == fkt_group].index for comp in comps: comp_obj = self.nw.get_comp(comp) sources = self.nw.conns[self.nw.conns['source'] == comp_obj] for conn in sources['object']: if conn.target.label not in comps: target_group = self.component_data.loc[ conn.target.label, 'group'] target_value = conn.Ex_physical cat = hlp.single_fluid(conn.fluid.val) if cat is None: cat = 'mix' if target_group in data.index: self.sankey_data[fkt_group].loc[ target_group, 'value'] += target_value else: self.sankey_data[fkt_group].loc[target_group] = [ target_value, cat ] # create overview of component groups self.group_data = pd.DataFrame(columns=['E_F', 'E_P', 'E_D'], dtype='float64') for fkt_group in self.component_data['group'].unique(): self.group_data.loc[fkt_group, 'E_F'] = ( self.calculate_group_input_value(fkt_group)) self.group_data.loc[fkt_group, 'E_D'] = ( self.sankey_data[fkt_group].loc['E_D', 'value']) # calculate missing values self.group_data['E_P'] = (self.group_data['E_F'] - self.group_data['E_D']) self.group_data['epsilon'] = (self.group_data['E_P'] / self.group_data['E_F']) self.group_data['y_Dk'] = (self.group_data['E_D'] / self.network_data.loc['E_F']) self.group_data['y*_Dk'] = (self.group_data['E_D'] / self.network_data.loc['E_D'])
def T_mix_ps(flow, s, T0=300): r""" Calculate the temperature from pressure and entropy. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. s : float Entropy of flow in J / (kgK). Returns ------- T : float Temperature T / K. Note ---- First, check if fluid property has been memorised already. If this is the case, return stored value, otherwise calculate value and store it in the memorisation class. Uses CoolProp interface for pure fluids, newton algorithm for mixtures: .. math:: T_{mix}\left(p,s\right) = T_{i}\left(p,s_{i}\right)\; \forall i \in \text{fluid components}\\ s_{i} = s \left(pp_{i}, T_{mix} \right)\\ pp: \text{partial pressure} """ # check if fluid properties have been calculated before fl = tuple(flow[3].keys()) memorisation = fl in memorise.T_ps.keys() if memorisation is True: a = memorise.T_ps[fl][:, :-1] b = np.asarray([flow[1], flow[2]] + list(flow[3].values()) + [s]) ix = np.where(np.all(abs(a - b) <= err, axis=1))[0] if ix.size == 1: # known fluid properties T = memorise.T_ps[fl][ix, -1][0] memorise.T_ps_f[fl] += [T] return T # unknown fluid properties fluid = single_fluid(flow[3]) if fluid is None: # calculate the fluid properties for fluid mixtures if memorisation is True: valmin = max( [memorise.value_range[f][2] for f in fl if flow[3][f] > err] ) + 0.1 if T0 < valmin or np.isnan(T0): T0 = valmin * 1.1 else: valmin = 70 val = newton(s_mix_pT, ds_mix_pdT, flow, s, val0=T0, valmin=valmin, valmax=3000, imax=10) if memorisation is True: new = np.asarray( [[flow[1], flow[2]] + list(flow[3].values()) + [s, val]]) # memorise the newly calculated value memorise.T_ps[fl] = np.append(memorise.T_ps[fl], new, axis=0) return val else: # calculate fluid property for pure fluids msg = ('The calculation of temperature from pressure and entropy ' 'for pure fluids should not be required, as the ' 'calculation is always possible from pressure and ' 'enthalpy. If there is a case, where you need to calculate ' 'temperature from these properties, please inform us: ' 'https://github.com/oemof/tespy.') logging.error(msg) raise ValueError(msg)
def T_mix_ph(flow, T0=300): r""" Calculate the temperature from pressure and enthalpy. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. Returns ------- T : float Temperature T / K. Note ---- First, check if fluid property has been memorised already. If this is the case, return stored value, otherwise calculate value and store it in the memorisation class. Uses CoolProp interface for pure fluids, newton algorithm for mixtures: .. math:: T_{mix}\left(p,h\right) = T_{i}\left(p,h_{i}\right)\; \forall i \in \text{fluid components}\\ h_{i} = h \left(pp_{i}, T_{mix} \right)\\ pp: \text{partial pressure} """ # check if fluid properties have been calculated before fl = tuple(flow[3].keys()) memorisation = fl in memorise.T_ph.keys() if memorisation is True: a = memorise.T_ph[fl][:, :-1] b = np.array([flow[1], flow[2]] + list(flow[3].values())) ix = np.where(np.all(abs(a - b) <= err, axis=1))[0] if ix.size == 1: # known fluid properties T = memorise.T_ph[fl][ix, -1][0] memorise.T_ph_f[fl] += [T] return T # unknown fluid properties fluid = single_fluid(flow[3]) if fluid is None: # calculate the fluid properties for fluid mixtures if memorisation is True: valmin = max( [memorise.value_range[f][2] for f in fl if flow[3][f] > err] ) + 0.1 if T0 < valmin or np.isnan(T0): T0 = valmin * 1.1 else: valmin = 70 val = newton(h_mix_pT, dh_mix_pdT, flow, flow[2], val0=T0, valmin=valmin, valmax=3000, imax=10) else: # calculate fluid property for pure fluids val = T_ph(flow[1], flow[2], fluid) if memorisation is True: # memorise the newly calculated value new = np.asarray([[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 h_mix_pT(flow, T, force_gas=False): r""" Calculate the enthalpy from pressure and Temperature. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. T : float Temperature of flow T / K. Returns ------- h : float Enthalpy h / (J/kg). Note ---- Calculation for fluid mixtures. .. 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_mass_flow(flow[3]) h = 0 fluid_name = single_fluid(flow[3]) if fluid_name is None: x_i = { fluid: y / (molar_masses[fluid] * n) for fluid, y in flow[3].items() } water = Memorise.water if (water is not None and not force_gas and flow[3][water] > err): y_i_gas, x_i_gas, y_water_liq, x_water_liq = ( cond_check(flow[3], x_i, flow[1], n, T) ) else: y_i_gas = flow[3] y_water_liq = 0 x_i_gas = x_i for fluid, y in y_i_gas.items(): if y > err: if fluid == water and y_water_liq > 0: Memorise.state[fluid].update(CP.QT_INPUTS, 0, T) h += Memorise.state[fluid].hmass() * y_water_liq Memorise.state[fluid].update(CP.QT_INPUTS, 1, T) h += Memorise.state[fluid].hmass() * y * (1 - y_water_liq) else: h += h_pT( flow[1] * x_i_gas[fluid], T, fluid, force_gas ) * y * (1 - y_water_liq) else: h = h_pT(flow[1], T, fluid_name, force_gas) return h
def T_mix_ps(flow, s, T0=675): r""" Calculate the temperature from pressure and entropy. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. s : float Entropy of flow in J / (kgK). Returns ------- T : float Temperature T / K. Note ---- First, check if fluid property has been memorised already. If this is the case, return stored value, otherwise calculate value and store it in the memorisation class. Uses CoolProp interface for pure fluids, newton algorithm for mixtures: .. math:: T_{mix}\left(p,s\right) = T_{i}\left(p,s_{i}\right)\; \forall i \in \text{fluid components}\\ s_{i} = s \left(pp_{i}, T_{mix} \right)\\ pp: \text{partial pressure} """ # check if fluid properties have been calculated before fl = tuple(flow[3].keys()) memorisation = fl in Memorise.T_ps if memorisation: a = Memorise.T_ps[fl][:, :-2] b = np.asarray([flow[1], flow[2]] + list(flow[3].values()) + [s]) ix = np.where(np.all(abs(a - b) <= err, axis=1))[0] if ix.size == 1: # known fluid properties Memorise.T_ps[fl][ix, -1] += 1 return Memorise.T_ps[fl][ix, -2][0] # unknown fluid properties fluid = single_fluid(flow[3]) if fluid is None: # calculate the fluid properties for fluid mixtures valmin = max( [Memorise.value_range[f][2] for f in fl if flow[3][f] > err] ) + 0.1 if T0 < valmin or np.isnan(T0): T0 = valmin * 1.1 val = newton(s_mix_pT, ds_mix_pdT, flow, s, val0=T0, valmin=valmin, valmax=3000, imax=10) else: # calculate fluid property for pure fluids val = T_ps(flow[1], s, fluid) if memorisation: new = np.asarray( [[flow[1], flow[2]] + list(flow[3].values()) + [s, val, 0]]) # memorise the newly calculated value Memorise.T_ps[fl] = np.append(Memorise.T_ps[fl], new, axis=0) return val
def s_mix_pT(flow, T, force_gas=False): r""" Calculate the entropy from pressure and temperature. Parameters ---------- flow : list Fluid property vector containing mass flow, pressure, enthalpy and fluid composition. T : float Temperature T / K. Returns ------- s : float Specific entropy s / (J/(kgK)). Note ---- Calculation for fluid mixtures. .. 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_mass_flow(flow[3]) s = 0 fluid_name = single_fluid(flow[3]) if fluid_name is None: x_i = { fluid: y / (molar_masses[fluid] * n) for fluid, y in flow[3].items() } water = Memorise.water if (water is not None and not force_gas and flow[3][water] > err): y_i_gas, x_i_gas, y_water_liq, x_water_liq = ( cond_check(flow[3], x_i, flow[1], n, T) ) else: y_i_gas = flow[3] y_water_liq = 0 x_i_gas = x_i for fluid, y in y_i_gas.items(): if y > err: if fluid == water and y_water_liq > 0: Memorise.state[water].update(CP.QT_INPUTS, 1, T) s += Memorise.state[water].smass() * y * ( 1 - y_water_liq ) Memorise.state[water].update(CP.QT_INPUTS, 0, T) s += Memorise.state[water].smass() * y_water_liq else: pp = flow[1] * x_i_gas[fluid] s += y * (1 - y_water_liq) * s_pT(pp, T, fluid, force_gas) else: s = s_pT(flow[1], T, fluid_name, force_gas) return s