def T_ph(p, h, fluid): r""" Calculate 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 fluid in tespy_fluid.fluids.keys(): db = tespy_fluid.fluids[fluid].funcs['h_pT'] return newton(reverse_2d, reverse_2d_deriv, [db, p, h], 0) else: memorise.state[fluid].update(CP.HmassP_INPUTS, h, p) return memorise.state[fluid].T()
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 h_ps(p, s, fluid): r""" Calculate 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 fluid in tespy_fluid.fluids.keys(): 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: memorise.state[fluid].update(CP.PSmass_INPUTS, p, s) return memorise.state[fluid].hmass()
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 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 calc_bus_expr(self, bus): r""" Return the busses' characteristic line input expression. Parameters ---------- bus : tespy.connections.bus.Bus Bus to calculate the characteristic function expression for. Returns ------- expr : float Ratio of power to power design depending on the bus base specification. """ b = bus.comps.loc[self] if np.isnan(b['P_ref']) or b['P_ref'] == 0: return 1 else: comp_val = self.bus_func(b) if b['base'] == 'component': return abs(comp_val / b['P_ref']) else: bus_value = newton( bus_char_evaluation, bus_char_derivative, [comp_val, b['P_ref'], b['char']], 0, val0=b['P_ref'], valmin=-1e15, valmax=1e15) return bus_value / b['P_ref']
def visc_ph(p, h, fluid): r""" Calculate 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 fluid in tespy_fluid.fluids.keys(): 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: memorise.state[fluid].update(CP.HmassP_INPUTS, h, p) return memorise.state[fluid].viscosity()
def d_ph(p, h, fluid): r""" Calculate the density 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 ------- d : float Density d / (kg/:math:`\mathrm{m}^3`). """ if fluid in tespy_fluid.fluids.keys(): 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['d_pT'].ev(p, T) else: memorise.state[fluid].update(CP.HmassP_INPUTS, h, p) return memorise.state[fluid].rhomass()
def T_ps(p, s, fluid): r""" Calculate the temperature 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 ------- T : float Temperature T / K. """ if fluid in tespy_fluid.fluids.keys(): db = tespy_fluid.fluids[fluid].funcs['s_pT'] return newton(reverse_2d, reverse_2d_deriv, [db, p, s], 0) else: memorise.state[fluid].update(CP.PSmass_INPUTS, p, s) return memorise.state[fluid].T()
def calc_bus_value(self, bus): r""" Return the busses' value of the component's energy transfer. Parameters ---------- bus : tespy.connections.bus Bus to calculate energy transfer on. Returns ------- bus_value : float Value of the energy transfer on the specified bus. .. math:: \dot{E}_\mathrm{bus} = \begin{cases} \frac{\dot{E}_\mathrm{component}}{f\left( \frac{\dot{E}_\mathrm{bus}}{\dot{E}_\mathrm{bus,ref}}\right)} & \text{bus base = 'bus'}\\ \dot{E}_\mathrm{component} \cdot f\left( \frac{\dot{E}_\mathrm{component}} {\dot{E}_\mathrm{component,ref}}\right) & \text{bus base = 'component'} \end{cases} Note ---- If the base value of the bus is the bus value itself, a newton iteration is used to find the bus value satisfying the corresponding equation (case 1). """ b = bus.comps.loc[self] comp_val = self.bus_func(b) if np.isnan(b['P_ref']): expr = 1 else: if b['base'] == 'component': expr = abs(comp_val / b['P_ref']) else: bus_value = newton(bus_char_evaluation, bus_char_derivative, [comp_val, b['P_ref'], b['char']], 0, val0=b['P_ref'], valmin=-1e15, valmax=1e15) return bus_value if b['base'] == 'component': return comp_val * b['char'].evaluate(expr) else: return comp_val / b['char'].evaluate(expr)
def calc_bus_efficiency(self, bus): r""" Return the busses' efficiency. Parameters ---------- bus : tespy.connections.bus Bus to calculate the efficiency value on. Returns ------- efficiency : float Efficiency value of the bus. .. math:: \eta_\mathrm{bus} = \begin{cases} \eta\left( \frac{\dot{E}_\mathrm{bus}}{\dot{E}_\mathrm{bus,ref}}\right) & \text{bus base = 'bus'}\\ \eta\left( \frac{\dot{E}_\mathrm{component}} {\dot{E}_\mathrm{component,ref}}\right) & \text{bus base = 'component'} \end{cases} Note ---- If the base value of the bus is the bus value itself, a newton iteration is used to find the bus value satisfying the corresponding equation (case 1). """ b = bus.comps.loc[self] comp_val = self.bus_func(b) if np.isnan(b['P_ref']): expr = 1 else: if b['base'] == 'component': expr = abs(comp_val / b['P_ref']) else: bus_value = newton(bus_char_evaluation, bus_char_derivative, [comp_val, b['P_ref'], b['char']], 0, val0=b['P_ref'], valmin=-1e15, valmax=1e15) expr = bus_value / b['P_ref'] return b['char'].evaluate(expr)
def test_newton_bounds(): """ Test newton algorithm value limit handling. Try to calculate a zero crossing of a quadratic function in three tries. - zero crossing within limits, starting value near 4 - zero crossing within limits, starting value near -5 - zero crossing below minimum - zero crossing above maximum The function is x^2 + x - 20, there crossings are -5 and 4. """ result = newton(func, deriv, [], 0, valmin=-10, valmax=10, val0=0) msg = ('The newton algorithm should find the zero crossing at 4.0. ' + str(round(result, 1)) + ' was found instead.') eq_(4.0, result, msg) result = newton(func, deriv, [], 0, valmin=-10, valmax=10, val0=-10) msg = ('The newton algorithm should find the zero crossing at -5.0. ' + str(round(result, 1)) + ' was found instead.') eq_(-5.0, result, msg) result = newton(func, deriv, [], 0, valmin=-4, valmax=-2, val0=-3) msg = ('The newton algorithm should not be able to find a zero crossing. ' 'The value ' + str(round(result, 1)) + ' was found, but the ' 'algorithm should have found the lower boundary of -4.0.') eq_(-4.0, result, msg) result = newton(func, deriv, [], 0, valmin=-20, valmax=-10, val0=-10) msg = ('The newton algorithm should not be able to find a zero crossing. ' 'The value ' + str(round(result, 1)) + ' was found, but the ' 'algorithm should have found the upper boundary of -10.0.') eq_(-10.0, result, msg)
def entropy_iteration_IF97(p, h, fluid, output): r""" Calculate state in IF97 back-end via entropy iteration. Parameters ---------- p : float Pressure p / Pa. h : float Specific enthalpy h / (J/kg). fluid : str Fluid name. Returns ------- T : float Temperature T / K. """ # region 1 exclusive issue! Memorise.state[fluid].update(CP.HmassP_INPUTS, h, p) if p <= 16.529164252605 * 1e6: h_at_ph = Memorise.state[fluid].hmass() deviation = abs(h_at_ph - h) if deviation / h > 0.001: # region 1, where isenthalpic lines are tangent to saturation dome if p > 1e6 and p < 1e7 and h > 2700000 and h < 2850000: smin = 5750 smax = 6500 # bottom left corner in Ts diagram elif h < 10000: smin = 0 smax = 50 else: # proximity to saturated liquid Memorise.state[fluid].update(CP.PQ_INPUTS, p, 0) h_sat_l = Memorise.state[fluid].hmass() if abs(h - h_sat_l) / h_sat_l < 1e-1: if p < 1000: smin = 0 elif p < 60000: smin = Memorise.state[fluid].smass() * 0.9 else: smin = Memorise.state[fluid].smass() * 0.95 Memorise.state[fluid].update(CP.PQ_INPUTS, p, 0.3) smax = Memorise.state[fluid].smass() # all others else: Memorise.state[fluid].update(CP.HmassP_INPUTS, h, p) s0 = Memorise.state[fluid].smass() smin = 0.8 * s0 smax = 1.2 * s0 s0 = (smax + smin) / 2 s = newton(func=h_ps_IF97, deriv=dh_pds_IF97, params=[fluid, p], y=h, val0=s0, valmin=smin, valmax=smax, max_iter=5, tol_rel=1e-3, tol_mode='rel') Memorise.state[fluid].update(CP.PSmass_INPUTS, p, s) if output == 'T': return Memorise.state[fluid].T() elif output == 's': return Memorise.state[fluid].smass() elif output == 'rho': return Memorise.state[fluid].rhomass() else: return Memorise.state[fluid].viscosity()
def T_mix_ps(flow, s, T0=300): r""" Calculates 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()) 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, 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 else: # unknown fluid properties if num_fluids(flow[3]) > 1: # calculate the fluid properties for fluid mixtures if T0 < 70: T0 = 300 val = newton(s_mix_pT, ds_mix_pdT, flow, s, val0=T0, valmin=70, valmax=3000, imax=10) new = np.array([[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""" Calculates 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()) 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, 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 else: # unknown fluid properties if num_fluids(flow[3]) > 1: # calculate the fluid properties for fluid mixtures if T0 < 70: T0 = 300 val = newton(h_mix_pT, dh_mix_pdT, flow, flow[2], val0=T0, valmin=70, valmax=3000, imax=10) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [val]]) # memorise the newly calculated value memorise.T_ph[fl] = np.append(memorise.T_ph[fl], new, axis=0) return val else: # calculate fluid property for pure fluids for fluid, x in flow[3].items(): if x > err: val = T_ph(flow[1], flow[2], fluid) new = np.array([[flow[1], flow[2]] + list(flow[3].values()) + [val]]) # memorise the newly calculated value memorise.T_ph[fl] = np.append(memorise.T_ph[fl], new, axis=0) return val
def T_mix_ph(flow, T0=675): 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 if memorisation: a = Memorise.T_ph[fl][:, :-2] 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 Memorise.T_ph[fl][ix, -1] += 1 return Memorise.T_ph[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(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: # memorise the newly calculated value new = np.asarray( [[flow[1], flow[2]] + list(flow[3].values()) + [val, 0]]) Memorise.T_ph[fl] = np.append(Memorise.T_ph[fl], new, axis=0) return val
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) else: # calculate fluid property for pure fluids val = T_ps(flow[1], s, fluid) 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