From 57d255822a6ff03d5ce999027910b851f4c5d366 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 9 May 2018 16:00:40 +0200 Subject: [PATCH 01/63] added class solar panel to components --- tespy/components/components.py | 237 +++++++++++++++++++++++++++++++-- 1 file changed, 223 insertions(+), 14 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 505d51d20..698f92677 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -846,15 +846,10 @@ def equations(self, nw): - :func:`tespy.components.components.compressor.eta_s_func` - :func:`tespy.components.components.turbine.eta_s_func` - characteristics - - - :func:`tespy.components.components.pump.char_func` - - :func:`tespy.components.components.compressor.char_func` - - :func:`tespy.components.components.turbine.char_func` - **additional equations** - - :func:`tespy.components.components.turbomachine.additional_equations` + - :func:`tespy.components.components.pump.additional_equations` + - :func:`tespy.components.components.compressor.additional_equations` - :func:`tespy.components.components.turbine.additional_equations` """ @@ -1129,7 +1124,7 @@ def additional_equations(self, nw): **optional equations** - - :func:`tespy.components.components.turbine.char_func` + - :func:`tespy.components.components.pump.char_func` """ vec_res = [] inl, outl = (nw.comps.loc[self].i.tolist(), @@ -1831,7 +1826,7 @@ class turbine(turbomachine): **equations** - see tespy.components.components.turbomachine.equations + see :func:`tespy.components.components.turbomachine.equations` **default design parameters** @@ -3845,7 +3840,7 @@ class heat_exchanger_simple(component): **equations** - see tespy.components.components.heat_exchager_simple.equations + see :func:`tespy.components.components.heat_exchager_simple.equations` **default design parameters** @@ -3882,7 +3877,7 @@ def comp_init(self, nw): self.t_a.val_SI = ((self.t_a.val + nw.T[nw.T_unit][0]) * nw.T[nw.T_unit][1]) self.t_a_design.val_SI = ((self.t_a_design.val + nw.T[nw.T_unit][0]) * - nw.T[nw.T_unit][1]) + nw.T[nw.T_unit][1]) def attr(self): return ['Q', 'pr', 'zeta', 'D', 'L', 'ks', @@ -3933,8 +3928,12 @@ def equations(self, nw): 0 = p_{in} \cdot pr - p_{out} - :func:`tespy.components.components.component.zeta_func` - - :func:`tespy.components.components.component.lamb_func` - - :func:`tespy.components.components.component.kA_func` + - :func:`tespy.components.components.heat_exchanger_simple.lamb_func` + + **additional equations** + + - :func:`tespy.components.components.heat_exchanger_simple.additional_equations` + - :func:`tespy.components.components.solar_collector.additional_equations` """ @@ -3958,6 +3957,29 @@ def equations(self, nw): if self.ks.is_set and self.D.is_set and self.L.is_set: vec_res += [self.lamb_func(inl, outl)] + vec_res += self.additional_equations(nw) + + return vec_res + + def additional_equations(self, nw): + r""" + additional equations for simple heat exchangers and pipes + + - applies kA-value for heat transfer calculation from ambient + temperature + + :param nw: network using this component object + :type nw: tespy.networks.network + :returns: vec_res (*list*) - residual value vector + + **optional equations** + + - :func:`tespy.components.components.heat_exchanger_simple.kA_func` + """ + vec_res = [] + inl, outl = (nw.comps.loc[self].i.tolist(), + nw.comps.loc[self].o.tolist()) + if self.kA.is_set and self.t_a.is_set: vec_res += [self.kA_func(inl, outl)] @@ -4019,6 +4041,25 @@ def derivatives(self, nw): self.ddx_func(inl, outl, self.lamb_func, 'h', i)) mat_deriv += lamb_deriv.tolist() + mat_deriv += self.additional_derivatives(nw) + + return np.asarray(mat_deriv) + + def additional_derivatives(self, nw): + r""" + calculate matrix of partial derivatives towards mass flow, pressure, + enthalpy and fluid composition for the additional equations + + :param nw: network using this component object + :type nw: tespy.networks.network + :returns: mat_deriv (*list*) - matrix of partial derivatives + """ + inl, outl = (nw.comps.loc[self].i.tolist(), + nw.comps.loc[self].o.tolist()) + num_i, num_o = len(inl), len(outl) + num_fl = len(nw.fluids) + mat_deriv = [] + if self.kA.is_set and self.t_a.is_set: kA_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) kA_deriv[0, 0, 0] = self.ddx_func(inl, outl, self.kA_func, 'm', 0) @@ -4029,7 +4070,7 @@ def derivatives(self, nw): self.ddx_func(inl, outl, self.kA_func, 'h', i)) mat_deriv += kA_deriv.tolist() - return np.asarray(mat_deriv) + return mat_deriv def lamb_func(self, inl, outl): r""" @@ -4339,6 +4380,174 @@ def component(self): # %% +class solar_collector(heat_exchanger_simple): + r""" + + class solar collector + + **available parameters** + + - Q: heat flux + - pr: outlet to inlet pressure ratio + - zeta: geometry independent friction coefficient + :math:`[\zeta]=\frac{\text{Pa}}{\text{m}^4}`, also see + :func:`tespy.components.components.component.zeta_func` + - D: diameter of the pipes + - L: length of the pipes + - ks: pipes roughness + - E: global solar radiation + - lkf_lin: linear loss key figure, + :math:`[\alpha_1]=\frac{\text{W}}{\text{K}}` + - lkf_quad: quadratic loss key figure, + :math:`[\alpha_2]=\frac{\text{W}}{\text{K}^2}` + - t_a: ambient temperature + + **equations** + + see tespy.components.components.solar_collector.equations + + **default design parameters** + + - pr + + **default offdesign parameters** + + - E *be aware that you must provide t_a, lkf_lin and lkf_quad + if you want the heat flux calculated by this method* + + **inlets and outlets** + + - in1 + - out1 + + .. image:: _images/solar_collector.svg + :scale: 100 % + :alt: alternative text + :align: center + """ + + def comp_init(self, nw): + + if self.kA_char.func is None: + method = self.kA_char.method + x = self.kA_char.x + y = self.kA_char.y + self.kA_char.func = cmp_char.heat_ex(method=method, x=x, y=y) + + self.t_a.val_SI = ((self.t_a.val + nw.T[nw.T_unit][0]) * + nw.T[nw.T_unit][1]) + self.t_a_design.val_SI = ((self.t_a_design.val + nw.T[nw.T_unit][0]) * + nw.T[nw.T_unit][1]) + + def attr(self): + return ['Q', 'pr', 'zeta', 'D', 'L', 'ks', + 'E', 'lkf_lin', 'lkf_quad', 't_a'] + + def attr_prop(self): + return {'Q': dc_cp(), 'pr': dc_cp(), 'zeta': dc_cp(), + 'D': dc_cp(), 'L': dc_cp(), 'ks': dc_cp(), + 'E': dc_cp(), 'lkf_lin': dc_cp(), 'lkf_quad': dc_cp(), + 't_a': dc_cp()} + + def inlets(self): + return ['in1'] + + def outlets(self): + return ['out1'] + + def default_design(self): + return ['pr'] + + def default_offdesign(self): + return ['E'] + + def component(self): + return 'solar collector' + + def additional_equations(self, nw): + r""" + additional equations for solar collectors + + - calculates collector heat flux from global solar radiation + + :param nw: network using this component object + :type nw: tespy.networks.network + :returns: vec_res (*list*) - residual value vector + + **optional equations** + + - :func:`tespy.components.components.heat_exchanger_simple.kA_func` + """ + vec_res = [] + inl, outl = (nw.comps.loc[self].i.tolist(), + nw.comps.loc[self].o.tolist()) + + if (self.E.is_set and self.lkf_lin.is_set and + self.lkf_quad.is_set and self.t_a.is_set): + vec_res += [self.energy_func(inl, outl)] + + return vec_res + + def additional_derivatives(self, nw): + r""" + calculate matrix of partial derivatives towards mass flow, pressure, + enthalpy and fluid composition for the additional equations + + :param nw: network using this component object + :type nw: tespy.networks.network + :returns: mat_deriv (*list*) - matrix of partial derivatives + """ + inl, outl = (nw.comps.loc[self].i.tolist(), + nw.comps.loc[self].o.tolist()) + num_i, num_o = len(inl), len(outl) + num_fl = len(nw.fluids) + mat_deriv = [] + + if (self.E.is_set and self.lkf_lin.is_set and + self.lkf_quad.is_set and self.t_a.is_set): + # insert logic for derivatives here + energy_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + energy_deriv[0, 0, 0] = outl[0].h.val_SI - inl[0].h.val_SI + for i in range(2): + energy_deriv[0, i, 1] = ( + self.ddx_func(inl, outl, self.energy_func, 'p', i)) + energy_deriv[0, i, 2] = ( + self.ddx_func(inl, outl, self.energy_func, 'h', i)) + mat_deriv += energy_deriv.tolist() + + return mat_deriv + + def energy_func(self, inl, outl): + r""" + equation for solar panel energy balance + + :param inlets: the components connections at the inlets + :type inlets: list + :param outlets: the components connections at the outlets + :type outlets: list + :returns: val (*float*) - residual value of equation + + .. math:: + T_m = \frac{T_{out} + T_{in}}{2}\\ + + 0 = \dot{m} \cdot \left( h_{out} - h_{in} \right) - + \left\{E - \left(T_m - T_{amb} \right) \cdot + \left[ \alpha_1 + \alpha_2 \cdot \left(\ + T_m - T_{amb}\right) \right] \right\} + """ + + i = inl[0].to_flow() + o = outl[0].to_flow() + + T_m = (T_mix_ph(i) + T_mix_ph(o)) / 2 + + return (i[0] * (o[2] - i[2]) - (self.E.val - (T_m - self.t_a.val_SI) * + (self.lkf_lin.val + + self.lkf_quad.val * (T_m - self.t_a.val_SI)))) + +# %% + + class heat_exchanger(component): r""" - all components of class heat exchanger are counter flow heat exchangers From a4f66ac90f2ab99fe035f1f65097201877b41c3b Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 10 May 2018 13:17:20 +0200 Subject: [PATCH 02/63] added energy balance for solar collector --- tespy/components/components.py | 92 ++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 698f92677..1bd3fb4a1 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -4395,16 +4395,17 @@ class solar collector - D: diameter of the pipes - L: length of the pipes - ks: pipes roughness - - E: global solar radiation + - E: global solar radiation, :math:`[E] = \frac{\text{W}}{\text{m}^2}` - lkf_lin: linear loss key figure, - :math:`[\alpha_1]=\frac{\text{W}}{\text{K}}` + :math:`[\alpha_1]=\frac{\text{W}}{\text{K} \cdot \text{m}}` - lkf_quad: quadratic loss key figure, - :math:`[\alpha_2]=\frac{\text{W}}{\text{K}^2}` + :math:`[\alpha_2]=\frac{\text{W}}{\text{K}^2 \cdot \text{m}^2}` + - A: collector surface area :math:`[A]=\text{m}^2` - t_a: ambient temperature **equations** - see tespy.components.components.solar_collector.equations + see :func:`tespy.components.components.solar_collector.equations` **default design parameters** @@ -4412,8 +4413,7 @@ class solar collector **default offdesign parameters** - - E *be aware that you must provide t_a, lkf_lin and lkf_quad - if you want the heat flux calculated by this method* + - zeta **inlets and outlets** @@ -4428,26 +4428,18 @@ class solar collector def comp_init(self, nw): - if self.kA_char.func is None: - method = self.kA_char.method - x = self.kA_char.x - y = self.kA_char.y - self.kA_char.func = cmp_char.heat_ex(method=method, x=x, y=y) - self.t_a.val_SI = ((self.t_a.val + nw.T[nw.T_unit][0]) * nw.T[nw.T_unit][1]) - self.t_a_design.val_SI = ((self.t_a_design.val + nw.T[nw.T_unit][0]) * - nw.T[nw.T_unit][1]) def attr(self): return ['Q', 'pr', 'zeta', 'D', 'L', 'ks', - 'E', 'lkf_lin', 'lkf_quad', 't_a'] + 'E', 'lkf_lin', 'lkf_quad', 'A', 't_a'] def attr_prop(self): return {'Q': dc_cp(), 'pr': dc_cp(), 'zeta': dc_cp(), 'D': dc_cp(), 'L': dc_cp(), 'ks': dc_cp(), 'E': dc_cp(), 'lkf_lin': dc_cp(), 'lkf_quad': dc_cp(), - 't_a': dc_cp()} + 'A': dc_cp(), 't_a': dc_cp()} def inlets(self): return ['in1'] @@ -4459,7 +4451,7 @@ def default_design(self): return ['pr'] def default_offdesign(self): - return ['E'] + return ['zeta'] def component(self): return 'solar collector' @@ -4476,7 +4468,7 @@ def additional_equations(self, nw): **optional equations** - - :func:`tespy.components.components.heat_exchanger_simple.kA_func` + - :func:`tespy.components.components.solar_collector.energy_func` """ vec_res = [] inl, outl = (nw.comps.loc[self].i.tolist(), @@ -4503,8 +4495,8 @@ def additional_derivatives(self, nw): num_fl = len(nw.fluids) mat_deriv = [] - if (self.E.is_set and self.lkf_lin.is_set and - self.lkf_quad.is_set and self.t_a.is_set): + if (self.E.is_set and self.lkf_lin.is_set and self.lkf_quad.is_set and + self.A.is_set and self.t_a.is_set): # insert logic for derivatives here energy_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) energy_deriv[0, 0, 0] = outl[0].h.val_SI - inl[0].h.val_SI @@ -4519,7 +4511,7 @@ def additional_derivatives(self, nw): def energy_func(self, inl, outl): r""" - equation for solar panel energy balance + equation for solar collector energy balance :param inlets: the components connections at the inlets :type inlets: list @@ -4531,8 +4523,8 @@ def energy_func(self, inl, outl): T_m = \frac{T_{out} + T_{in}}{2}\\ 0 = \dot{m} \cdot \left( h_{out} - h_{in} \right) - - \left\{E - \left(T_m - T_{amb} \right) \cdot - \left[ \alpha_1 + \alpha_2 \cdot \left(\ + \left\{E \cdot A - \left(T_m - T_{amb} \right) \cdot A \cdot + \left[ \alpha_1 + \alpha_2 \cdot A \cdot \left(\ T_m - T_{amb}\right) \right] \right\} """ @@ -4541,9 +4533,57 @@ def energy_func(self, inl, outl): T_m = (T_mix_ph(i) + T_mix_ph(o)) / 2 - return (i[0] * (o[2] - i[2]) - (self.E.val - (T_m - self.t_a.val_SI) * - (self.lkf_lin.val + - self.lkf_quad.val * (T_m - self.t_a.val_SI)))) + return (i[0] * (o[2] - i[2]) - (self.E.val * self.A.val - + (T_m - self.t_a.val_SI) * + self.A.val * (self.lkf_lin.val + + self.lkf_quad.val * self.A.val * + (T_m - self.t_a.val_SI)))) + + def calc_parameters(self, nw, mode): + + inl, outl = (nw.comps.loc[self].i.tolist(), + nw.comps.loc[self].o.tolist()) + + if (mode == 'pre' and 'Q' in self.offdesign) or mode == 'post': + self.Q.val = inl[0].m.val_SI * (outl[0].h.val_SI - + inl[0].h.val_SI) + if (mode == 'pre' and 'pr' in self.offdesign) or mode == 'post': + self.pr.val = outl[0].p.val_SI / inl[0].p.val_SI + if (mode == 'pre' and 'zeta' in self.offdesign) or mode == 'post': + self.zeta.val = ((inl[0].p.val_SI - outl[0].p.val_SI) * + math.pi ** 2 / + (8 * inl[0].m.val_SI ** 2 * + (v_mix_ph(inl[0].to_flow()) + + v_mix_ph(outl[0].to_flow())) / 2)) + + if (mode == 'post' and self.lkf_lin.is_set and self.lkf_quad.is_set and + self.A.is_set and self.t_a.is_set): + + T_m = (T_mix_ph(inl[0].to_flow()) + + T_mix_ph(outl[0].to_flow())) / 2 + self.E.val = (((inl[0].m.val_SI * + (outl[0].h.val_SI - inl[0].h.val_SI)) + + ((T_m - self.t_a.val_SI) * self.A.val * + (self.lkf_lin.val + + self.lkf_quad.val * self.A.val * + (T_m - self.t_a.val_SI)))) / self.A.val) + + def print_parameters(self, nw): + + inl, outl = (nw.comps.loc[self].i.tolist(), + nw.comps.loc[self].o.tolist()) + + print('##### ', self.label, ' #####') + print('Q = ', self.Q.val, 'W; ' + 'pr = ', self.pr.val, '; ' + 'zeta = ', self.zeta.val, 'kg / m^4 * s; ' + 'm = ', inl[0].m.val_SI, 'kg / s; ' + 'Sq = ', inl[0].m.val_SI * (s_mix_ph(outl[0].to_flow()) - + s_mix_ph(inl[0].to_flow())), + 'W / K; ') + if (self.lkf_lin.is_set and self.lkf_quad.is_set and + self.A.is_set and self.t_a.is_set): + print('E = ', self.E.val, 'W / m^2') # %% From 367cc7af2bde9f4ac4b2460585176229fb9b0ad2 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 10 May 2018 13:17:34 +0200 Subject: [PATCH 03/63] added solar collector example code --- examples/solar_collector.py | 93 +++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 examples/solar_collector.py diff --git a/examples/solar_collector.py b/examples/solar_collector.py new file mode 100644 index 000000000..7514e2bc6 --- /dev/null +++ b/examples/solar_collector.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Aug 4 10:37:36 2017 + +@author: witte +""" + +from tespy import con, cmp, nwk +import numpy as np +from matplotlib import pyplot as plt +import pandas as pd + +# %% network + +fluid_list = ['H2O'] +nw = nwk.network(fluids=fluid_list, p_unit='bar', T_unit='C', + p_range=[4, 10], T_range=[10, 200]) + +# %% components + +# sinks & sources +back = cmp.source('to collector') +feed = cmp.sink('from collector') + +# collector +coll = cmp.solar_collector(label='solar thermal collector') + +# %% connections + +b_c = con.connection(back, 'out1', coll, 'in1') +c_f = con.connection(coll, 'out1', feed, 'in1') + +nw.add_conns(b_c, c_f) + +# %% component parameters + +# set combustion chamber fuel, air to stoichometric air ratio and thermal input +coll.set_attr(pr=0.99, Q=8e3, lkf_lin=1, lkf_quad=0.005, A=10, t_a=10) + +# %% connection parameters + +b_c.set_attr(p=5, T=20, fluid={'H2O': 1}) +c_f.set_attr(p0=2, T=120) + +# %% solving + +# going through several parametrisation possibilities +mode = 'design' +nw.solve(mode=mode) +nw.print_results() + +coll.set_attr(Q=7e3, E=9e3) +c_f.set_attr(T=np.nan) + +nw.solve(mode=mode) +nw.print_results() + +coll.set_attr(Q=np.nan, E=np.nan) +c_f.set_attr(T=100, m=1e-2) + +nw.solve(mode=mode) +nw.print_results() + +# looping over different temperature differences (assuming constant mass flow) +# and global radiation () + +c_f.set_attr(m=np.nan) +T_amb = np.linspace(0, 60, 14, dtype=float) +E_glob = np.linspace(100, 1000, 14, dtype=float) + +df = pd.DataFrame(columns=(60 - T_amb)) + +for E in E_glob: + eta = [] + coll.set_attr(E=E) + for T in T_amb: + coll.set_attr(t_a=T) + nw.solve(mode=mode) + eta += [coll.Q.val / (coll.E.val * coll.A.val)] + if eta[-1] < 0: + eta[-1] = np.nan + + df.loc[E] = eta + +E, T = np.meshgrid(60 - T_amb, E_glob) + +fig = plt.figure() +ax = fig.add_subplot(111, projection='3d') +ax.plot_wireframe(E, T, df.as_matrix()) +ax.set_xlabel('Temperaturdifferenz') +ax.set_ylabel('Globalstrahlung auf die schiefe Ebene') +ax.set_zlabel('Wirkungsgrad (nur thermische Verluste)') +plt.show() From 0f62c97691d55e73aea465ed82cfcfc68b2f3f9c Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 16 Jul 2018 12:36:56 +0200 Subject: [PATCH 04/63] adjusted version --- doc/conf.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index e85846912..bac1398eb 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -65,7 +65,7 @@ # The short X.Y version. version = '0.0.3-001' # The full version, including alpha/beta/rc tags. -release = '0.0.3-001 master' +release = '0.0.3-001 dev' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 8eadb760e..76842f1df 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def read(fname): setup(name='TESPy', - version='0.0.3-001', + version='0.0.3-001 dev', description='Thermal Engineering Systems in Python (TESPy)', url='http://github.com/oemof/tespy', author='Francesco Witte', From 31930c9130574002c87b840b33fd99c265f9e5a1 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 19 Jul 2018 15:48:06 +0200 Subject: [PATCH 05/63] removed convergence check in offdesign cases --- tespy/networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tespy/networks.py b/tespy/networks.py index 3dd80213e..48a68cb3d 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -1173,7 +1173,7 @@ def solve_control(self): i += 1 # check properties for consistency - if self.iter < 5: + if self.iter < 3 and self.init_file is None: for c in self.conns.index: self.solve_check_properties(c) From 699302648c97845382b6c1028637fe30e76d854b Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 19 Jul 2018 15:49:13 +0200 Subject: [PATCH 06/63] typo fixes, added x and y default data for compressor characteristic function (as dummy) --- tespy/components/components.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index b23372604..ad3136110 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -1571,7 +1571,8 @@ def attr(self): def attr_prop(self): return {'P': dc_cp(), 'eta_s': dc_cp(), 'pr': dc_cp(), 'vigv': dc_cp(), 'Sirr': dc_cp(), - 'char_map': dc_cc(func=cmp_char.compressor())} + 'char_map': dc_cc(func=cmp_char.compressor(), + x=[0, 1, 2], y=[0, 1, 2])} def default_offdesign(self): return ['char_map'] @@ -2329,7 +2330,7 @@ def calc_parameters(self, nw, mode): (self.h_os(inl, outl) - inl[0].h.val_SI)) if self.eta_s.val > 1 or self.eta_s.val <= 0: msg = ('Invalid value for isentropic efficiency.\n' - 'eta_s =', self.eta_s) + 'eta_s =', self.eta_s.val) print(msg) nw.errors += [self] From f5535275eb4b4d29c6ab2b187c5eda09657dda4e Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 23 Jul 2018 16:32:06 +0200 Subject: [PATCH 07/63] adjusted network export --- tespy/networks.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index 48a68cb3d..9584cf717 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -190,7 +190,8 @@ def __getstate__(self): required to pass Pool object within solving loop """ self_dict = self.__dict__.copy() - del self_dict['pool'] + if 'pool' in self_dict.keys(): + del self_dict['pool'] return self_dict def set_attr(self, **kwargs): @@ -418,6 +419,7 @@ def initialise(self): :returns: no return value """ + msg = ('Have you adjusted the value ranges for pressure, enthalpy' ' and temperature according to the specified unit system?') print(msg) @@ -2168,12 +2170,10 @@ def save_busses(self, fn): df = pd.DataFrame({'id': self.busses}, index=self.busses) df['id'] = df.apply(network.get_id, axis=1) - df['label'] = df.apply(network.get_props, axis=1, - args=('label',)) - df['P'] = df.apply(network.get_props, axis=1, - args=('P',)) - df['P_set'] = df.apply(network.get_props, axis=1, - args=('P_set',)) + cols = ['label', 'P', 'P_set'] + for col in cols: + df[col] = df.apply(network.get_props, axis=1, + args=(col,)) df.to_csv(fn, sep=';', decimal='.', index=False, na_rep='nan') @@ -2230,8 +2230,9 @@ def get_props(c, *args): not isinstance(c.name.get_attr(args[0]), list) and not isinstance(c.name.get_attr(args[0]), np.ndarray) and not isinstance(c.name.get_attr(args[0]), con.connection)): - - if args[0] == 'fluid' and args[1] != 'balance': + if len(args) == 1: + return c.name.get_attr(args[0]) + elif args[0] == 'fluid' and args[1] != 'balance': return c.name.fluid.get_attr(args[1])[args[2]] elif args[1] == 'ref': obj = c.name.get_attr(args[0]).get_attr(args[1]) From 1ba454f1dfc0f3cdd474320946e82c938d1577a7 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 23 Jul 2018 16:32:31 +0200 Subject: [PATCH 08/63] added dummy characteristics for pump --- tespy/components/components.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index ad3136110..6c9c7caac 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -1171,6 +1171,13 @@ def comp_init(self, nw): self.flow_char.func = cmp_char.characteristics(method=method, x=x, y=y) + if self.eta_s_char.func is None: + method = self.eta_s_char.method + x = self.eta_s_char.x + y = self.eta_s_char.y + self.eta_s_char.func = cmp_char.characteristics(method=method, + x=x, y=y) + def component(self): return 'pump' @@ -1179,7 +1186,7 @@ def attr(self): def attr_prop(self): return {'P': dc_cp(), 'eta_s': dc_cp(), 'pr': dc_cp(), 'Sirr': dc_cp(), - 'eta_s_char': dc_cc(), 'flow_char': dc_cc()} + 'eta_s_char': dc_cc(x=[0, 1, 2], y=[0, 1, 2]), 'flow_char': dc_cc(x=[0, 1, 2], y=[0, 1, 2])} def additional_equations(self, nw): r""" From 229426113ec00ee45ab3eecd9dcbe87f82776d04 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 23 Jul 2018 16:33:04 +0200 Subject: [PATCH 09/63] fixed bus.set_attr method --- tespy/connections.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tespy/connections.py b/tespy/connections.py index c0bffdcbd..4183f9d07 100644 --- a/tespy/connections.py +++ b/tespy/connections.py @@ -271,8 +271,8 @@ def __init__(self, label, **kwargs): def set_attr(self, **kwargs): - self.label = kwargs.get('label') - self.P = kwargs.get('P') + self.label = kwargs.get('label', self.label) + self.P = kwargs.get('P', self.P) if not np.isnan(self.P): self.P_set = True From 8f928216033192067ef061d560382fc423d57640 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 24 Jul 2018 14:00:22 +0200 Subject: [PATCH 10/63] fixed fluid0 specifcation for starting values --- tespy/connections.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tespy/connections.py b/tespy/connections.py index 4183f9d07..981f5c9a5 100644 --- a/tespy/connections.py +++ b/tespy/connections.py @@ -158,7 +158,8 @@ def set_attr(self, **kwargs): elif isinstance(kwargs[key], dict): # starting values if key in var0: - self.get_attr(key).set_attr(val0=kwargs[key]) + self.get_attr(key.replace('0', '')).set_attr( + val0=kwargs[key]) # specified parameters else: self.get_attr(key).set_attr(val=kwargs[key].copy()) From eac08cc8cc26c8e13f2de681db4462ff60c79dde Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 24 Jul 2018 14:00:39 +0200 Subject: [PATCH 11/63] fixed h_mix_pQ function --- tespy/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tespy/helpers.py b/tespy/helpers.py index 9eee96876..5bf045a07 100644 --- a/tespy/helpers.py +++ b/tespy/helpers.py @@ -962,8 +962,8 @@ def h_mix_pQ(flow, Q): if x > err: pp = flow[1] * x / (molar_masses[fluid] * n) pcrit = CPPSI('Pcrit', fluid) - while pp > pcrit: - flow[1] = flow[1] * 0.95 + if pp > pcrit: + pp = pcrit * 0.95 h += CPPSI('H', 'P', pp, 'Q', Q, fluid) * x From e3b2a779f90c3577671503f17da69be10c508eef Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 24 Jul 2018 14:01:04 +0200 Subject: [PATCH 12/63] fixed fluid initialisation from user specified starting values --- tespy/networks.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tespy/networks.py b/tespy/networks.py index 9584cf717..11598f1c1 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -539,7 +539,12 @@ def init_fluids(self): # if there is a starting value elif fluid in tmp0.keys(): - if not tmp_set[fluid]: + if fluid in tmp_set.keys(): + if not tmp_set[fluid]: + c.fluid.val[fluid] = tmp0[fluid] + c.fluid.val0[fluid] = tmp0[fluid] + c.fluid.val_set[fluid] = False + else: c.fluid.val[fluid] = tmp0[fluid] c.fluid.val0[fluid] = tmp0[fluid] c.fluid.val_set[fluid] = False From 0783e8a0268c65630da62929950ec410948f8b69 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 25 Jul 2018 11:50:53 +0200 Subject: [PATCH 13/63] adjusted merge fluid initialisation --- tespy/networks.py | 110 ++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index 11598f1c1..1a92ad73f 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -508,53 +508,51 @@ def init_fluids(self): c.fluid.val0 = collections.OrderedDict() c.fluid.val_set = collections.OrderedDict() - # set values for ordered dict - for fluid in self.fluids: + # if the number if fluids is one + if len(self.fluids) == 1: + c.fluid.val[self.fluids[0]] = 1 + c.fluid.val0[self.fluids[0]] = 1 - if len(self.fluids) == 1: - c.fluid.val[fluid] = 1 - c.fluid.val0[fluid] = 1 + if self.fluids[0] in tmp_set.keys(): + c.fluid.val_set[self.fluids[0]] = tmp_set[self.fluids[0]] + else: + c.fluid.val_set[self.fluids[0]] = False - try: - if tmp_set[fluid]: - c.fluid.val_set[fluid] = True - else: - c.fluid.val_set[fluid] = False - except: - c.fluid.val_set[fluid] = False + # jump to next connection + continue - else: + for fluid in self.fluids: - if fluid in tmp.keys(): - # if fluid in keys and is_set - if tmp_set[fluid]: - c.fluid.val[fluid] = tmp[fluid] - c.fluid.val0[fluid] = tmp[fluid] - c.fluid.val_set[fluid] = True - # if fluid in keys and not is_set - else: - c.fluid.val[fluid] = 0 - c.fluid.val0[fluid] = 0 - c.fluid.val_set[fluid] = False + if fluid in tmp.keys(): + # if fluid in keys and is_set + c.fluid.val[fluid] = tmp[fluid] + c.fluid.val0[fluid] = tmp[fluid] + + if fluid in tmp_set.keys(): + c.fluid.val_set[fluid] = tmp_set[fluid] + # if fluid in keys and not is_set + else: + c.fluid.val_set[fluid] = False - # if there is a starting value - elif fluid in tmp0.keys(): - if fluid in tmp_set.keys(): - if not tmp_set[fluid]: - c.fluid.val[fluid] = tmp0[fluid] - c.fluid.val0[fluid] = tmp0[fluid] - c.fluid.val_set[fluid] = False - else: + # if there is a starting value + if fluid in tmp0.keys(): + if fluid in tmp_set.keys(): + if not tmp_set[fluid]: c.fluid.val[fluid] = tmp0[fluid] c.fluid.val0[fluid] = tmp0[fluid] c.fluid.val_set[fluid] = False - - # if fluid not in keys else: - c.fluid.val[fluid] = 0 - c.fluid.val0[fluid] = 0 + c.fluid.val[fluid] = tmp0[fluid] + c.fluid.val0[fluid] = tmp0[fluid] c.fluid.val_set[fluid] = False + # if fluid not in keys + else: + c.fluid.val[fluid] = 0 + c.fluid.val0[fluid] = 0 + c.fluid.val_set[fluid] = False + + # fluid propagation complete for single fluid networks if len(self.fluids) == 1: return @@ -569,6 +567,15 @@ def init_fluids(self): self.init_target(c, c.t) self.init_source(c, c.s) + if isinstance(c.s, cmp.merge): + c.s.initialise_fluids(self) + if isinstance(c.t, cmp.merge): + c.t.initialise_fluids(self) + + for c in self.conns.index: + c.s.initialise_fluids(self) + c.t.initialise_fluids(self) + def init_target(self, c, start): """ propagates the fluids towards connections target, @@ -702,37 +709,6 @@ def init_properties(self): c.get_attr(key).ref.obj.get_attr(key).val_SI * c.get_attr(key).ref.f + c.get_attr(key).ref.d) - # propate fluids towards merges targets - # add merge cp to list redo, if number of fluids at merges outlet is - # still zero - redo = [] - if len(self.fluids) >= 1: - for cp in self.comps.index: - if isinstance(cp, cmp.merge): - cp.initialise_fluids(self) - for c in self.comps.loc[cp].o: - if hlp.num_fluids(c.fluid.val) != 0: - self.init_target(c, c.t) - else: - redo += [cp] - - # propagete fluids towards merges targets of the redo list - # do this, while the list is not empty - # if the iteration number is over 50, stop calculation - i = 0 - while len(redo) != 0: - for cp in redo: - cp.initialise_fluids(self) - for c in self.comps.loc[cp].o: - if hlp.num_fluids(c.fluid.val) != 0: - self.init_target(c, c.t) - redo.remove(cp) - - if i > 50: - msg = 'Media propagation failed.' - raise hlp.MyNetworkError(msg) - i += 1 - for c in self.conns.index: if c.x.val_set and not c.h.val_set: c.h.val_SI = hlp.h_mix_pQ(c.to_flow(), c.x.val_SI) From f1e93d37d4c6a79621c7ccd7d02562e78fe2f265 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 25 Jul 2018 11:51:29 +0200 Subject: [PATCH 14/63] adjusted merge fluid initialisation --- tespy/components/components.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 6c9c7caac..ef9ee347f 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -2772,17 +2772,27 @@ def initialise_fluids(self, nw): :type nw: tespy.networks.network :returns: no return value """ - for outconn in nw.comps.loc[self].o: - inl = nw.comps.loc[self].i.tolist() - for fluid in nw.fluids: - if not outconn.fluid.val_set[fluid]: - x = 0 - m = 0 - for i in inl: - m += i.m.val_SI - x += i.fluid.val[fluid] * i.m.val_SI + num_fl = {} + for o in nw.comps.loc[self].o: + num_fl[o] = num_fluids(o.fluid.val) - outconn.fluid.val[fluid] = x / m + for i in nw.comps.loc[self].i: + num_fl[i] = num_fluids(i.fluid.val) + + ls = [] + if any(num_fl.values()) and not all(num_fl.values()): + for conn, num in num_fl.items(): + if num == 1: + ls += [conn] + + for c in ls: + for fluid in nw.fluids: + for o in nw.comps.loc[self].o: + if not o.fluid.val_set[fluid]: + o.fluid.val[fluid] = c.fluid.val[fluid] + for i in nw.comps.loc[self].i: + if not i.fluid.val_set[fluid]: + i.fluid.val[fluid] = c.fluid.val[fluid] def initialise_source(self, c, key): r""" From 4b24651296a35b8332c3bfe054816c68e205ba64 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 25 Jul 2018 11:52:29 +0200 Subject: [PATCH 15/63] fixed derivatives for given heat flow of heat exchangers --- tespy/components/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 6c9c7caac..05c72389a 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -5486,7 +5486,7 @@ def derivatives(self, nw): Q_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) Q_deriv[0, 0, 0] = outl[0].h.val_SI - inl[0].h.val_SI Q_deriv[0, 0, 2] = -inl[0].m.val_SI - Q_deriv[0, 1, 2] = inl[0].m.val_SI + Q_deriv[0, 2, 2] = inl[0].m.val_SI mat_deriv += Q_deriv.tolist() if self.kA.is_set: From 3b14a4a254f631ea6fafc5a137b63fa78da15fc3 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 25 Jul 2018 18:46:28 +0200 Subject: [PATCH 16/63] added in-/outgoing connections to component's attributes --- tespy/components/components.py | 1790 +++++++++++++------------------- 1 file changed, 713 insertions(+), 1077 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 76e26e6bb..ea5440dad 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -7,6 +7,7 @@ import numpy as np import math +import time import CoolProp.CoolProp as CP @@ -263,16 +264,16 @@ def default_design(self): def default_offdesign(self): return [] - def equations(self, nw): + def equations(self): return [] def derivatives(self, nw): return [] - def bus_func(self, inl, outl): + def bus_func(self): return 0 - def bus_deriv(self, inl, outl): + def bus_deriv(self): return def initialise_source(self, c, key): @@ -315,14 +316,10 @@ def convergence_check(self, nw): # %% - def fluid_res(self, inl, outl): + def fluid_res(self): r""" returns residual values for fluid equations - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: vec_res (*list*) - a list containing the residual values **components with one inlet and one outlet** @@ -363,44 +360,44 @@ def fluid_res(self, inl, outl): """ vec_res = [] - if len(self.inlets()) == 1 and len(self.outlets()) == 1: - for fluid, x in inl[0].fluid.val.items(): - vec_res += [x - outl[0].fluid.val[fluid]] + if self.num_i == 1 and self.num_o == 1: + for fluid, x in self.inl[0].fluid.val.items(): + vec_res += [x - self.outl[0].fluid.val[fluid]] return vec_res if (isinstance(self, subsys_interface) or isinstance(self, heat_exchanger)): - for i in range(len(inl)): - for fluid, x in inl[i].fluid.val.items(): - vec_res += [x - outl[i].fluid.val[fluid]] + for i in range(self.num_i): + for fluid, x in self.inl[i].fluid.val.items(): + vec_res += [x - self.outl[i].fluid.val[fluid]] return vec_res if isinstance(self, splitter): - for o in outl: - for fluid, x in inl[0].fluid.val.items(): + for o in self.outl: + for fluid, x in self.inl[0].fluid.val.items(): vec_res += [x - o.fluid.val[fluid]] return vec_res if isinstance(self, merge): res = 0 - for fluid, x in outl[0].fluid.val.items(): - res = -x * outl[0].m.val_SI - for i in inl: + for fluid, x in self.outl[0].fluid.val.items(): + res = -x * self.outl[0].m.val_SI + for i in self.inl: res += i.fluid.val[fluid] * i.m.val_SI vec_res += [res] return vec_res if isinstance(self, drum): - for o in outl: - for fluid, x in inl[0].fluid.val.items(): + for o in self.outl: + for fluid, x in self.inl[0].fluid.val.items(): vec_res += [x - o.fluid.val[fluid]] return vec_res if isinstance(self, separator): - for fluid, x in inl[0].fluid.val.items(): - res = x * inl[0].m.val_SI - for o in outl: + for fluid, x in self.inl[0].fluid.val.items(): + res = x * self.inl[0].m.val_SI + for o in self.outl: res -= o.fluid.val[fluid] * o.m.val_SI vec_res += [res] return vec_res @@ -408,14 +405,10 @@ def fluid_res(self, inl, outl): if isinstance(self, source) or isinstance(self, sink): return None - def fluid_deriv(self, inl, outl): + def fluid_deriv(self): r""" returns derivatives for fluid equations - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - a list containing the derivatives **example:** @@ -444,40 +437,38 @@ def fluid_deriv(self, inl, outl): \end{array} \right) """ - num_i = len(inl) - num_o = len(outl) - num_fl = len(inl[0].fluid.val) + num_fl = len(self.inl[0].fluid.val) - if len(self.inlets()) == 1 and len(self.outlets()) == 1: - mat_deriv = np.zeros((num_fl, num_i + num_o, 3 + num_fl)) + if self.num_i == 1 and self.num_o == 1: + mat_deriv = np.zeros((num_fl, 2, 3 + num_fl)) i = 0 - for fluid, x in inl[0].fluid.val.items(): + for fluid, x in self.inl[0].fluid.val.items(): mat_deriv[i, 0, i + 3] = 1 mat_deriv[i, 1, i + 3] = -1 i += 1 return mat_deriv.tolist() if isinstance(self, heat_exchanger): - mat_deriv = np.zeros((num_fl * 2, num_i + num_o, 3 + num_fl)) + mat_deriv = np.zeros((num_fl * 2, 4, 3 + num_fl)) i = 0 - for fluid in inl[0].fluid.val.keys(): + for fluid in self.inl[0].fluid.val.keys(): mat_deriv[i, 0, i + 3] = 1 mat_deriv[i, 2, i + 3] = -1 i += 1 j = 0 - for fluid in inl[1].fluid.val.keys(): + for fluid in self.inl[1].fluid.val.keys(): mat_deriv[i + j, 1, j + 3] = 1 mat_deriv[i + j, 3, j + 3] = -1 j += 1 return mat_deriv.tolist() if isinstance(self, splitter): - mat_deriv = np.zeros((num_fl * num_i * num_o, - num_i + num_o, 3 + num_fl)) + mat_deriv = np.zeros((num_fl * self.num_o, + 1 + self.num_o, 3 + num_fl)) k = 0 - for o in outl: + for o in self.outl: i = 0 - for fluid, x in inl[0].fluid.val.items(): + for fluid, x in self.inl[0].fluid.val.items(): mat_deriv[i + k * num_fl, 0, i + 3] = 1 mat_deriv[i + k * num_fl, k + 1, i + 3] = -1 i += 1 @@ -485,25 +476,25 @@ def fluid_deriv(self, inl, outl): return mat_deriv.tolist() if isinstance(self, merge): - mat_deriv = np.zeros((num_fl, num_i + num_o, 3 + num_fl)) + mat_deriv = np.zeros((num_fl, self.num_i + 1, 3 + num_fl)) j = 0 - for fluid, x in outl[0].fluid.val.items(): + for fluid, x in self.outl[0].fluid.val.items(): k = 0 - for i in inl: + for i in self.inl: mat_deriv[j, k, 0] = i.fluid.val[fluid] mat_deriv[j, k, j + 3] = i.m.val_SI k += 1 mat_deriv[j, k, 0] = -x - mat_deriv[j, k, j + 3] = -outl[0].m.val_SI + mat_deriv[j, k, j + 3] = -self.outl[0].m.val_SI j += 1 return mat_deriv.tolist() if isinstance(self, drum): - mat_deriv = np.zeros((num_o * num_fl, num_i + num_o, 3 + num_fl)) + mat_deriv = np.zeros((2 * num_fl, 4, 3 + num_fl)) k = 0 - for o in outl: + for o in self.outl: i = 0 - for fluid, x in inl[0].fluid.val.items(): + for fluid, x in self.inl[0].fluid.val.items(): mat_deriv[i + k * num_fl, 0, i + 3] = 1 mat_deriv[i + k * num_fl, k + 2, i + 3] = -1 i += 1 @@ -511,16 +502,16 @@ def fluid_deriv(self, inl, outl): return mat_deriv.tolist() if isinstance(self, separator): - mat_deriv = np.zeros((num_fl, num_i + num_o, 3 + num_fl)) + mat_deriv = np.zeros((num_fl, 1 + self.num_o, 3 + num_fl)) j = 0 - for fluid, x in inl[0].fluid.val.items(): + for fluid, x in self.inl[0].fluid.val.items(): k = 0 - for o in outl: + for o in self.outl: mat_deriv[j, k, 0] = -o.fluid.val[fluid] mat_deriv[j, k, j + 3] = -o.m.val_SI k += 1 mat_deriv[j, 0, 0] = x - mat_deriv[j, 0, j + 3] = inl[0].m.val_SI + mat_deriv[j, 0, j + 3] = self.inl[0].m.val_SI j += 1 return mat_deriv.tolist() @@ -528,25 +519,22 @@ def fluid_deriv(self, inl, outl): return None if isinstance(self, subsys_interface): - mat_deriv = np.zeros((num_fl * num_i, num_i + num_o, 3 + num_fl)) - for i in range(num_i): + mat_deriv = np.zeros((num_fl * self.num_i, self.num_i + self.num_o, + 3 + num_fl)) + for i in range(self.num_i): j = 0 - for fluid in inl[i].fluid.val.keys(): + for fluid in self.inl[i].fluid.val.keys(): mat_deriv[i * num_fl + j, i, j + 3] = 1 - mat_deriv[i * num_fl + j, num_i + i, j + 3] = -1 + mat_deriv[i * num_fl + j, self.num_i + i, j + 3] = -1 j += 1 return mat_deriv.tolist() # %% - def mass_flow_res(self, inl, outl): + def mass_flow_res(self): r""" returns residual values for mass flow equations - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: vec_res (*list*) - a list containing the residual values **all components but heat exchanger and subsystem interface** @@ -566,32 +554,28 @@ def mass_flow_res(self, inl, outl): isinstance(self, combustion_chamber) or isinstance(self, combustion_chamber_stoich) or isinstance(self, drum) or - (len(self.inlets()) == 1 and len(self.outlets()) == 1)): + (self.num_i == 1 and self.num_o == 1)): res = 0 - for i in inl: + for i in self.inl: res += i.m.val_SI - for o in outl: + for o in self.outl: res -= o.m.val_SI return [res] if (isinstance(self, subsys_interface) or isinstance(self, heat_exchanger)): vec_res = [] - for i in range(len(inl)): - vec_res += [inl[i].m.val_SI - outl[i].m.val_SI] + for i in range(self.num_i): + vec_res += [self.inl[i].m.val_SI - self.outl[i].m.val_SI] return vec_res if isinstance(self, source) or isinstance(self, sink): return None - def mass_flow_deriv(self, inl, outl): + def mass_flow_deriv(self): r""" returns derivatives for mass flow equations - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - a list containing the derivatives **example** @@ -608,33 +592,32 @@ def mass_flow_deriv(self, inl, outl): \end{array} \right) """ - num_i = len(inl) - num_o = len(outl) - num_fl = len(inl[0].fluid.val) + num_fl = len(self.inl[0].fluid.val) if (isinstance(self, split) or isinstance(self, merge) or isinstance(self, combustion_chamber) or isinstance(self, combustion_chamber_stoich) or isinstance(self, drum) or - (len(self.inlets()) == 1 and len(self.outlets()) == 1)): - mat_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + (self.num_i == 1 and self.num_o == 1)): + mat_deriv = np.zeros((1, self.num_i + self.num_o, num_fl + 3)) j = 0 - for i in inl: + for i in self.inl: mat_deriv[0, j, 0] = 1 j += 1 k = 0 - for o in outl: + for o in self.outl: mat_deriv[0, k + j, 0] = -1 k += 1 return mat_deriv.tolist() if (isinstance(self, subsys_interface) or isinstance(self, heat_exchanger)): - mat_deriv = np.zeros((num_i, num_i + num_o, num_fl + 3)) - for i in range(num_i): + mat_deriv = np.zeros((self.num_i, self.num_i + self.num_o, + num_fl + 3)) + for i in range(self.num_i): mat_deriv[i, i, 0] = 1 - for j in range(num_o): + for j in range(self.num_o): mat_deriv[j, j + i + 1, 0] = -1 return mat_deriv.tolist() @@ -642,15 +625,11 @@ def mass_flow_deriv(self, inl, outl): return None # %% - def ddx_func(self, inl, outl, func, dx, pos): + def ddx_func(self, func, dx, pos): r""" calculates derivative of the function func to dx at components inlet or outlet in position pos - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :param func: function to calculate derivative :type func: function :param dx: dx @@ -679,52 +658,48 @@ def ddx_func(self, inl, outl, func, dx, pos): if dx == 'fluid': deriv = [] - for f in inl[0].fluid.val.keys(): - val = (inl + outl)[pos].fluid.val[f] + for f in self.inl[0].fluid.val.keys(): + val = (self.inl + self.outl)[pos].fluid.val[f] exp = 0 - if (inl + outl)[pos].fluid.val[f] + df <= 1: - (inl + outl)[pos].fluid.val[f] += df + if (self.inl + self.outl)[pos].fluid.val[f] + df <= 1: + (self.inl + self.outl)[pos].fluid.val[f] += df else: - (inl + outl)[pos].fluid.val[f] = 1 - exp += func(inl, outl) - if (inl + outl)[pos].fluid.val[f] - 2 * df >= 0: - (inl + outl)[pos].fluid.val[f] -= 2 * df + (self.inl + self.outl)[pos].fluid.val[f] = 1 + exp += func() + if (self.inl + self.outl)[pos].fluid.val[f] - 2 * df >= 0: + (self.inl + self.outl)[pos].fluid.val[f] -= 2 * df else: - (inl + outl)[pos].fluid.val[f] = 0 - exp -= func(inl, outl) - (inl + outl)[pos].fluid.val[f] = val + (self.inl + self.outl)[pos].fluid.val[f] = 0 + exp -= func() + (self.inl + self.outl)[pos].fluid.val[f] = val deriv += [exp / (2 * (dm + dp + dh + df))] else: exp = 0 - (inl + outl)[pos].m.val_SI += dm - (inl + outl)[pos].p.val_SI += dp - (inl + outl)[pos].h.val_SI += dh - exp += func(inl, outl) - - (inl + outl)[pos].m.val_SI -= 2 * dm - (inl + outl)[pos].p.val_SI -= 2 * dp - (inl + outl)[pos].h.val_SI -= 2 * dh - exp -= func(inl, outl) + (self.inl + self.outl)[pos].m.val_SI += dm + (self.inl + self.outl)[pos].p.val_SI += dp + (self.inl + self.outl)[pos].h.val_SI += dh + exp += func() + + (self.inl + self.outl)[pos].m.val_SI -= 2 * dm + (self.inl + self.outl)[pos].p.val_SI -= 2 * dp + (self.inl + self.outl)[pos].h.val_SI -= 2 * dh + exp -= func() deriv = exp / (2 * (dm + dp + dh + df)) - (inl + outl)[pos].m.val_SI += dm - (inl + outl)[pos].p.val_SI += dp - (inl + outl)[pos].h.val_SI += dh + (self.inl + self.outl)[pos].m.val_SI += dm + (self.inl + self.outl)[pos].p.val_SI += dp + (self.inl + self.outl)[pos].h.val_SI += dh return deriv # %% - def zeta_func(self, inl, outl): + def zeta_func(self): r""" calculates pressure drop from zeta (zeta1 for heat exchangers) - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: residual value for the pressure drop .. math:: @@ -739,8 +714,8 @@ def zeta_func(self, inl, outl): 0 = \zeta - \frac{(p_{in} - p_{out}) \cdot \pi^2}{8 \cdot \dot{m}_{in}^2 \cdot \frac{v_{in} + v_{out}}{2}} """ - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() if hasattr(self, 'zeta'): val = self.zeta.val else: @@ -835,7 +810,7 @@ def outlets(self): def component(self): return 'turbomachine' - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -874,29 +849,28 @@ def equations(self, nw): """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - vec_res += self.fluid_res(inl, outl) - vec_res += self.mass_flow_res(inl, outl) + vec_res += self.fluid_res() + vec_res += self.mass_flow_res() if self.P.is_set: - vec_res += [inl[0].m.val_SI * - (outl[0].h.val_SI - inl[0].h.val_SI) - + vec_res += [self.inl[0].m.val_SI * + (self.outl[0].h.val_SI - self.inl[0].h.val_SI) - self.P.val] if self.pr.is_set: - vec_res += [self.pr.val * inl[0].p.val_SI - outl[0].p.val_SI] + vec_res += [self.pr.val * self.inl[0].p.val_SI - + self.outl[0].p.val_SI] if self.eta_s.is_set: - self.eta_s_res = self.eta_s_func(inl, outl) + self.eta_s_res = self.eta_s_func() vec_res += [self.eta_s_res] - vec_res += self.additional_equations(nw) + vec_res += self.additional_equations() return vec_res - def additional_equations(self, nw): + def additional_equations(self): """ returns vector vec_res with result of additional equations for this component @@ -939,32 +913,27 @@ def derivatives(self, nw): be defined externally through other components or by connection parametrisation """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - mat_deriv += self.fluid_deriv(inl, outl) - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.fluid_deriv() + mat_deriv += self.mass_flow_deriv() if self.P.is_set: - P_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, num_fl + 3)) - for k in range(num_i + num_o - 1): - P_deriv[k, 0, 0] = outl[0].h.val_SI - inl[0].h.val_SI - P_deriv[k, 0, 2] = -inl[0].m.val_SI - P_deriv[k, k + 1, 2] = inl[0].m.val_SI + P_deriv = np.zeros((1, 2, num_fl + 3)) + P_deriv[0, 0, 0] = self.outl[0].h.val_SI - self.inl[0].h.val_SI + P_deriv[0, 0, 2] = -self.inl[0].m.val_SI + P_deriv[0, 1, 2] = self.inl[0].m.val_SI mat_deriv += P_deriv.tolist() if self.pr.is_set: - pr_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, num_fl + 3)) - for k in range(num_i + num_o - 1): - pr_deriv[k, 0, 1] = self.pr.val - pr_deriv[k, k + 1, 1] = -1 + pr_deriv = np.zeros((1, 2, num_fl + 3)) + pr_deriv[0, 0, 1] = self.pr.val + pr_deriv[0, 1, 1] = -1 mat_deriv += pr_deriv.tolist() if self.eta_s.is_set: - mat_deriv += self.eta_s_deriv(inl, outl) + mat_deriv += self.eta_s_deriv() mat_deriv += self.additional_derivatives(nw) @@ -981,7 +950,7 @@ def additional_derivatives(self, nw): """ return [] - def eta_s_func(self, inl, outl): + def eta_s_func(self): """ see subclasses """ @@ -989,7 +958,7 @@ def eta_s_func(self, inl, outl): 'please specify which type of turbomachine you are using.') raise MyComponentError(msg) - def eta_s_deriv(self, inl, outl): + def eta_s_deriv(self): """ see subclasses """ @@ -997,23 +966,21 @@ def eta_s_deriv(self, inl, outl): 'please specify which type of turbomachine you are using.') raise MyComponentError(msg) - def h_os(self, inl, outl): + def h_os(self, mode): """ calculates the enthalpy at the outlet if compression or expansion is isentropic - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list + :param mode: pre or postprocessing + :type inl: str :returns: h (*float*) - enthalpy after isentropic state change """ - if isinstance(inl[0], float) or isinstance(inl[0], int): - i = inl - o = outl + if mode == 'pre': + i = self.i0 + o = self.o0 else: - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() if num_fluids(i[3]) == 1: for fluid, x in i[3].items(): @@ -1024,20 +991,16 @@ def h_os(self, inl, outl): s_mix = s_mix_pT(i, T_mix) return h_mix_ps(o, s_mix) - def char_func(self, inl, outl): + def char_func(self): raise MyComponentError('Function not available for this component.') - def char_deriv(self, inl, outl): + def char_deriv(self): raise MyComponentError('Function not available.') - def bus_func(self, inl, outl): + def bus_func(self): r""" function for use on busses - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -1045,24 +1008,20 @@ def bus_func(self, inl, outl): val = \dot{m}_{in} \cdot \left( h_{out} - h_{in} \right) """ - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() return i[0] * (o[2] - i[2]) - def bus_deriv(self, inl, outl): + def bus_deriv(self): r""" calculate matrix of partial derivatives towards mass flow and enthalpy for bus function - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of partial derivatives """ - i = inl[0].to_flow() - o = outl[0].to_flow() - deriv = np.zeros((1, 2, len(inl[0].fluid.val) + 3)) + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() + deriv = np.zeros((1, 2, len(self.inl[0].fluid.val) + 3)) deriv[0, 0, 0] = o[2] - i[2] deriv[0, 0, 2] = - i[0] deriv[0, 1, 2] = i[0] @@ -1085,25 +1044,25 @@ def calc_parameters(self, nw, mode): :code:`self.dh_s0` at reference """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) if (mode == 'pre' and 'P' in self.offdesign) or mode == 'post': - self.P.val = inl[0].m.val_SI * (outl[0].h.val_SI - inl[0].h.val_SI) + self.P.val = self.inl[0].m.val_SI * ( + self.outl[0].h.val_SI - self.inl[0].h.val_SI) if (mode == 'pre' and 'pr' in self.offdesign) or mode == 'post': - self.pr.val = outl[0].p.val_SI / inl[0].p.val_SI + self.pr.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI if mode == 'pre': - self.i0 = inl[0].to_flow() - self.o0 = outl[0].to_flow() + self.i0 = self.inl[0].to_flow() + self.o0 = self.outl[0].to_flow() self.i0[3] = self.i0[3].copy() self.o0[3] = self.i0[3].copy() - self.dh_s0 = (self.h_os(self.i0, self.o0) - self.i0[2]) + self.dh_s0 = (self.h_os(mode) - self.i0[2]) if mode == 'post': - self.Sirr.val = inl[0].m.val_SI * (s_mix_ph(outl[0].to_flow()) - - s_mix_ph(inl[0].to_flow())) + self.Sirr.val = self.inl[0].m.val_SI * ( + s_mix_ph(self.outl[0].to_flow()) - + s_mix_ph(self.inl[0].to_flow())) def print_parameters(self, nw): @@ -1176,7 +1135,7 @@ def comp_init(self, nw): x = self.eta_s_char.x y = self.eta_s_char.y self.eta_s_char.func = cmp_char.characteristics(method=method, - x=x, y=y) + x=x, y=y) def component(self): return 'pump' @@ -1186,9 +1145,10 @@ def attr(self): def attr_prop(self): return {'P': dc_cp(), 'eta_s': dc_cp(), 'pr': dc_cp(), 'Sirr': dc_cp(), - 'eta_s_char': dc_cc(x=[0, 1, 2], y=[0, 1, 2]), 'flow_char': dc_cc(x=[0, 1, 2], y=[0, 1, 2])} + 'eta_s_char': dc_cc(x=[0, 1, 2], y=[1, 1, 1]), + 'flow_char': dc_cc(x=[0, 1, 2], y=[1, 1, 1])} - def additional_equations(self, nw): + def additional_equations(self): r""" additional equations for pumps @@ -1203,14 +1163,12 @@ def additional_equations(self, nw): - :func:`tespy.components.components.turbine.char_func` """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) if self.eta_s_char.is_set: - vec_res += self.char_func(inl, outl).tolist() + vec_res += self.char_func().tolist() if self.flow_char.is_set: - vec_res += self.flow_char_func(inl, outl).tolist() + vec_res += self.flow_char_func().tolist() return vec_res @@ -1223,36 +1181,30 @@ def additional_derivatives(self, nw): :type nw: tespy.networks.network :returns: mat_deriv (*list*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) mat_deriv = [] if self.eta_s_char.is_set: - mat_deriv += self.char_deriv(inl, outl) + mat_deriv += self.char_deriv() if self.flow_char.is_set: - mat_deriv += self.flow_char_deriv(inl, outl) + mat_deriv += self.flow_char_deriv() return mat_deriv - def eta_s_func(self, inl, outl): + def eta_s_func(self): r""" equation for isentropic efficiency of a pump - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: 0 = -\left( h_{out} - h_{in} \right) \cdot \eta_{s,c} + \left( h_{out,s} - h_{in} \right) """ - return (-(outl[0].h.val_SI - inl[0].h.val_SI) * self.eta_s.val + - (self.h_os(inl, outl) - inl[0].h.val_SI)) + return (-(self.outl[0].h.val_SI - self.inl[0].h.val_SI) * + self.eta_s.val + (self.h_os('post') - self.inl[0].h.val_SI)) - def eta_s_deriv(self, inl, outl): + def eta_s_deriv(self): """ calculates partial derivatives of the isentropic efficiency function @@ -1265,57 +1217,39 @@ def eta_s_deriv(self, inl, outl): for compression """ - num_i, num_o = len(inl), len(outl) - num_fl = len(inl[0].fluid.val) - mat_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - - if abs(self.eta_s_res) > err ** (2): + num_fl = len(self.inl[0].fluid.val) + mat_deriv = np.zeros((1, 2, num_fl + 3)) - for i in range(num_i + num_o): - mat_deriv[0, i, 1] = self.ddx_func(inl, outl, - self.eta_s_func, 'p', i) - if i == 0: - mat_deriv[0, i, 2] = self.ddx_func(inl, outl, - self.eta_s_func, 'h', i) - else: - mat_deriv[0, i, 2] = -self.eta_s.val - - else: - for i in range(num_i + num_o): - mat_deriv[0, i, 1] = -1 - mat_deriv[0, i, 2] = -1 + for i in range(2): + mat_deriv[0, i, 1] = self.ddx_func(self.eta_s_func, 'p', i) + if i == 0: + mat_deriv[0, i, 2] = self.ddx_func(self.eta_s_func, 'h', i) + else: + mat_deriv[0, i, 2] = -self.eta_s.val return mat_deriv.tolist() - def char_func(self, inl, outl): + def char_func(self): r""" isentropic efficiency characteristic of a pump - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*numpy array*) - residual value of equation .. math:: 0 = -\left( h_{out} - h_{in} \right) \cdot char\left( \dot{m}_{in} \cdot v_{in} \right) + \left( h_{out,s} - h_{in} \right) """ - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() return np.array([((o[2] - i[2]) * self.dh_s0 / (self.o0[2] - self.i0[2]) * self.eta_s_char.func.f_x(i[0] * v_mix_ph(i)) - - (self.h_os(i, o) - i[2]))]) + (self.h_os('post') - i[2]))]) - def char_deriv(self, inl, outl): + def char_deriv(self): r""" calculates the derivatives for the characteristics - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of derivatives **example** @@ -1335,35 +1269,28 @@ def char_deriv(self, inl, outl): \right) """ - num_i, num_o = len(inl), len(outl) - num_fl = len(inl[0].fluid.val) - mat_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + num_fl = len(self.inl[0].fluid.val) + mat_deriv = np.zeros((1, 2, num_fl + 3)) mat_deriv[0, 0, 0] = ( - self.ddx_func(inl, outl, self.char_func, 'm', 0)) + self.ddx_func(self.char_func, 'm', 0)) for i in range(2): - mat_deriv[0, i, 1] = ( - self.ddx_func(inl, outl, self.char_func, 'p', i)) - mat_deriv[0, i, 2] = ( - self.ddx_func(inl, outl, self.char_func, 'h', i)) + mat_deriv[0, i, 1] = self.ddx_func(self.char_func, 'p', i) + mat_deriv[0, i, 2] = self.ddx_func(self.char_func, 'h', i) return mat_deriv.tolist() - def flow_char_func(self, inl, outl): + def flow_char_func(self): r""" equation for characteristics of a pump - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*numpy array*) - residual value of equation .. math:: 0 = p_{out} - p_{in} - char\left( \dot{m}_{in} \cdot v_{in} \right) """ - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() expr = i[0] * v_mix_ph(i) @@ -1374,14 +1301,10 @@ def flow_char_func(self, inl, outl): return np.array([o[1] - i[1] - self.flow_char.func.f_x(expr)]) - def flow_char_deriv(self, inl, outl): + def flow_char_deriv(self): r""" calculates the derivatives for the characteristics - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of derivatives **example** @@ -1401,17 +1324,13 @@ def flow_char_deriv(self, inl, outl): \right) """ - num_i, num_o = len(inl), len(outl) - num_fl = len(inl[0].fluid.val) - mat_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + num_fl = len(self.inl[0].fluid.val) + mat_deriv = np.zeros((1, 2, num_fl + 3)) - mat_deriv[0, 0, 0] = ( - self.ddx_func(inl, outl, self.flow_char_func, 'm', 0)) - mat_deriv[0, 0, 2] = ( - self.ddx_func(inl, outl, self.flow_char_func, 'h', 0)) + mat_deriv[0, 0, 0] = self.ddx_func(self.flow_char_func, 'm', 0) + mat_deriv[0, 0, 2] = self.ddx_func(self.flow_char_func, 'h', 0) for i in range(2): - mat_deriv[0, i, 1] = ( - self.ddx_func(inl, outl, self.flow_char_func, 'p', i)) + mat_deriv[0, i, 1] = self.ddx_func(self.flow_char_func, 'p', i) return mat_deriv.tolist() @@ -1433,7 +1352,7 @@ def convergence_check(self, nw): successful performance """ - i, o = nw.comps.loc[self].i, nw.comps.loc[self].o + i, o = self.inl, self.outl if not o[0].p.val_set and o[0].p.val_SI < i[0].p.val_SI: o[0].p.val_SI = o[0].p.val_SI * 2 @@ -1512,12 +1431,9 @@ def calc_parameters(self, nw, mode): turbomachine.calc_parameters(self, nw, mode) - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - if (mode == 'pre' and 'eta_s' in self.offdesign) or mode == 'post': - self.eta_s.val = ((self.h_os(inl, outl) - inl[0].h.val_SI) / - (outl[0].h.val_SI - inl[0].h.val_SI)) + self.eta_s.val = ((self.h_os('post') - self.inl[0].h.val_SI) / + (self.outl[0].h.val_SI - self.inl[0].h.val_SI)) if self.eta_s.val > 1 or self.eta_s.val <= 0: msg = ('Invalid value for isentropic efficiency.\n' 'eta_s =', self.eta_s.val) @@ -1584,7 +1500,7 @@ def attr_prop(self): def default_offdesign(self): return ['char_map'] - def additional_equations(self, nw): + def additional_equations(self): r""" additional equations for compressor @@ -1599,11 +1515,9 @@ def additional_equations(self, nw): - :func:`tespy.components.components.compressor.char_func` """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) if self.char_map.is_set: - vec_res += self.char_func(inl, outl).tolist() + vec_res += self.char_func().tolist() return vec_res @@ -1616,33 +1530,27 @@ def additional_derivatives(self, nw): :type nw: tespy.networks.network :returns: mat_deriv (*list*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) mat_deriv = [] if self.char_map.is_set: - mat_deriv += self.char_deriv(inl, outl) + mat_deriv += self.char_deriv() return mat_deriv - def eta_s_func(self, inl, outl): + def eta_s_func(self): r""" equation for isentropic efficiency of a compressor - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: 0 = -\left( h_{out} - h_{in} \right) \cdot \eta_{s,c} + \left( h_{out,s} - h_{in} \right) """ - return (-(outl[0].h.val_SI - inl[0].h.val_SI) * self.eta_s.val + - (self.h_os(inl, outl) - inl[0].h.val_SI)) + return (-(self.outl[0].h.val_SI - self.inl[0].h.val_SI) * + self.eta_s.val + (self.h_os('post') - self.inl[0].h.val_SI)) - def eta_s_deriv(self, inl, outl): + def eta_s_deriv(self): """ calculates partial derivatives of the isentropic efficiency function @@ -1655,39 +1563,24 @@ def eta_s_deriv(self, inl, outl): for compression """ - num_i, num_o = len(inl), len(outl) - num_fl = len(inl[0].fluid.val) - mat_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - - if abs(self.eta_s_res) > err ** (2): - - for i in range(num_i + num_o): - mat_deriv[0, i, 1] = self.ddx_func(inl, outl, - self.eta_s_func, 'p', i) - if i == 0: - mat_deriv[0, i, 2] = self.ddx_func(inl, outl, - self.eta_s_func, 'h', i) - else: - mat_deriv[0, i, 2] = -self.eta_s.val + num_fl = len(self.inl[0].fluid.val) + mat_deriv = np.zeros((1, 2, num_fl + 3)) - else: - for i in range(num_i + num_o): - mat_deriv[0, i, 1] = -1 - mat_deriv[0, i, 2] = -1 + for i in range(2): + mat_deriv[0, i, 1] = self.ddx_func(self.eta_s_func, 'p', i) + if i == 0: + mat_deriv[0, i, 2] = self.ddx_func(self.eta_s_func, 'h', i) + else: + mat_deriv[0, i, 2] = -self.eta_s.val return mat_deriv.tolist() - def char_func(self, inl, outl): + def char_func(self): r""" equation(s) for characteristics of compressor - returns one value, if vigv is not set - returns two values, if vigv is set - - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*numpy array*) - residual value(s) of equation(s): - :code:`np.array([val1, val2])` if vigv_set @@ -1730,12 +1623,8 @@ def char_func(self, inl, outl): - set vigv (from compressor map with pressure ratio) - calculate relative factor for isentropic efficiency """ - if isinstance(inl[0], float): - i = inl - o = outl - else: - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() n = math.sqrt(T_mix_ph(self.i0)) / math.sqrt(T_mix_ph(i)) m = (i[0] * math.sqrt(T_mix_ph(i)) * self.i0[1] / (self.i0[0] * math.sqrt(T_mix_ph(self.i0)) * i[1])) @@ -1762,7 +1651,7 @@ def char_func(self, inl, outl): return np.array([ o[1] * self.i0[1] / (i[1] * self.o0[1]) - speedline[0](m), - ((self.h_os(i, o) - i[2]) / (o[2] - i[2])) / + ((self.h_os('post') - i[2]) / (o[2] - i[2])) / (self.dh_s0 / (self.o0[2] - self.i0[2])) - speedline[1](m) ]) @@ -1773,21 +1662,17 @@ def char_func(self, inl, outl): n + dn, m, o[1] / i[1] / (self.o0[1] / self.i0[1])) return np.array([ - ((self.h_os(i, o) - i[2]) / (o[2] - i[2])) / + ((self.h_os('post') - i[2]) / (o[2] - i[2])) / (self.dh_s0 / (self.o0[2] - self.i0[2])) - self.char_map.func.get_eta(n + dn, m, self.vigv.val) ]) - def char_deriv(self, inl, outl): + def char_deriv(self): r""" calculates the derivatives for the characteristics - if vigv is set two sets of equations are used - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of derivatives **example** @@ -1798,19 +1683,17 @@ def char_deriv(self, inl, outl): - improve asthetics, this part of code looks horrible """ - num_i = len(inl) - num_o = len(outl) - num_fl = len(inl[0].fluid.val) + num_fl = len(self.inl[0].fluid.val) - m11 = self.ddx_func(inl, outl, self.char_func, 'm', 0) - p11 = self.ddx_func(inl, outl, self.char_func, 'p', 0) - h11 = self.ddx_func(inl, outl, self.char_func, 'h', 0) + m11 = self.ddx_func(self.char_func, 'm', 0) + p11 = self.ddx_func(self.char_func, 'p', 0) + h11 = self.ddx_func(self.char_func, 'h', 0) - p21 = self.ddx_func(inl, outl, self.char_func, 'p', 1) - h21 = self.ddx_func(inl, outl, self.char_func, 'h', 1) + p21 = self.ddx_func(self.char_func, 'p', 1) + h21 = self.ddx_func(self.char_func, 'h', 1) if self.vigv.is_set: - deriv = np.zeros((2, num_i + num_o, num_fl + 3)) + deriv = np.zeros((2, 2, num_fl + 3)) deriv[0, 0, 0] = m11[0] deriv[0, 0, 1] = p11[0] deriv[0, 0, 2] = h11[0] @@ -1823,7 +1706,7 @@ def char_deriv(self, inl, outl): deriv[1, 1, 2] = h21[1] return deriv.tolist() else: - deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + deriv = np.zeros((1, 2, num_fl + 3)) deriv[0, 0, 0] = m11[0] deriv[0, 0, 1] = p11[0] deriv[0, 0, 2] = h11[0] @@ -1849,7 +1732,7 @@ def convergence_check(self, nw): successful performance """ - i, o = nw.comps.loc[self].i, nw.comps.loc[self].o + i, o = self.inl, self.outl if not o[0].p.val_set and o[0].p.val_SI < i[0].p.val_SI: o[0].p.val_SI = o[0].p.val_SI * 2 @@ -1918,13 +1801,9 @@ def calc_parameters(self, nw, mode): turbomachine.calc_parameters(self, nw, mode) - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - if (mode == 'pre' and 'eta_s' in self.offdesign) or mode == 'post': - self.eta_s.val = ((self.h_os(inl, outl) - - inl[0].h.val_SI) / - (outl[0].h.val_SI - inl[0].h.val_SI)) + self.eta_s.val = ((self.h_os('post') - self.inl[0].h.val_SI) / + (self.outl[0].h.val_SI - self.inl[0].h.val_SI)) if self.eta_s.val > 1 or self.eta_s.val <= 0: msg = ('Invalid value for isentropic efficiency.\n' 'eta_s =', self.eta_s.val) @@ -1939,11 +1818,8 @@ def print_parameters(self, nw): turbomachine.print_parameters(self, nw) - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - - i1 = inl[0].to_flow() - o1 = outl[0].to_flow() + i1 = self.inl[0].to_flow() + o1 = self.outl[0].to_flow() if self.char_map.is_set: n = math.sqrt(T_mix_ph(self.i0)) / math.sqrt(T_mix_ph(i1)) @@ -2032,7 +1908,7 @@ def attr_prop(self): def default_offdesign(self): return turbomachine.default_offdesign(self) + ['cone'] - def additional_equations(self, nw): + def additional_equations(self): r""" additional equations for turbines @@ -2049,14 +1925,12 @@ def additional_equations(self, nw): - :func:`tespy.components.components.turbine.cone_func` """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) if self.eta_s_char.is_set: - vec_res += self.char_func(inl, outl).tolist() + vec_res += self.char_func().tolist() if self.cone.is_set: - vec_res += [self.cone_func(inl, outl)] + vec_res += [self.cone_func()] return vec_res @@ -2069,47 +1943,37 @@ def additional_derivatives(self, nw): :type nw: tespy.networks.network :returns: mat_deriv (*list*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] if self.eta_s_char.is_set: - mat_deriv += self.char_deriv(inl, outl) + mat_deriv += self.char_deriv() if self.cone.is_set: - cone_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + cone_deriv = np.zeros((1, 2, num_fl + 3)) cone_deriv[0, 0, 0] = -1 - cone_deriv[0, 0, 1] = ( - self.ddx_func(inl, outl, self.cone_func, 'p', 0)) - cone_deriv[0, 0, 2] = ( - self.ddx_func(inl, outl, self.cone_func, 'h', 0)) - cone_deriv[0, 1, 2] = ( - self.ddx_func(inl, outl, self.cone_func, 'p', 1)) + cone_deriv[0, 0, 1] = self.ddx_func(self.cone_func, 'p', 0) + cone_deriv[0, 0, 2] = self.ddx_func(self.cone_func, 'h', 0) + cone_deriv[0, 1, 2] = self.ddx_func(self.cone_func, 'p', 1) mat_deriv += cone_deriv.tolist() return mat_deriv - def eta_s_func(self, inl, outl): + def eta_s_func(self): r""" equation for isentropic efficiency of a turbine - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: 0 = -\left( h_{out} - h_{in} \right) + \left( h_{out,s} - h_{in} \right) \cdot \eta_{s,e} """ - return (-(outl[0].h.val_SI - inl[0].h.val_SI) + - (self.h_os(inl, outl) - inl[0].h.val_SI) * + return (-(self.outl[0].h.val_SI - self.inl[0].h.val_SI) + + (self.h_os('post') - self.inl[0].h.val_SI) * self.eta_s.val) - def eta_s_deriv(self, inl, outl): + def eta_s_deriv(self): """ calculates partial derivatives of the isentropic efficiency function @@ -2122,36 +1986,29 @@ def eta_s_deriv(self, inl, outl): for compression """ - num_i, num_o = len(inl), len(outl) - num_fl = len(inl[0].fluid.val) - mat_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + num_fl = len(self.inl[0].fluid.val) + mat_deriv = np.zeros((1, 2, num_fl + 3)) if abs(self.eta_s_res) > err ** (2): - for i in range(num_i + num_o): - mat_deriv[0, i, 1] = self.ddx_func(inl, outl, - self.eta_s_func, 'p', i) + for i in range(2): + mat_deriv[0, i, 1] = self.ddx_func(self.eta_s_func, 'p', i) if i == 0: - mat_deriv[0, i, 2] = self.ddx_func(inl, outl, - self.eta_s_func, 'h', i) + mat_deriv[0, i, 2] = self.ddx_func(self.eta_s_func, 'h', i) else: mat_deriv[0, i, 2] = -1 else: - for i in range(num_i + num_o): + for i in range(2): mat_deriv[0, i, 1] = -1 mat_deriv[0, i, 2] = -1 return mat_deriv.tolist() - def cone_func(self, inl, outl): + def cone_func(self): r""" equation for stodolas cone law - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -2160,8 +2017,8 @@ def cone_func(self, inl, outl): \sqrt{\frac{1 - \left(\frac{p_{out}}{p_{in}} \right)^{2}} {1 - \left(\frac{p_{out,0}}{p_{in,0}} \right)^{2}}} - \dot{m}_{in} """ - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() n = 1 return (self.i0[0] * i[1] / self.i0[1] * math.sqrt( self.i0[1] * v_mix_ph(self.i0) / (i[1] * v_mix_ph(i))) * @@ -2177,7 +2034,7 @@ def cone_func(self, inl, outl): # math.sqrt((1 - (o1[1] / i1[1]) ** ((n + 1) / n)) / # (1 - (self.o1_0[1] / self.i1_0[1]) ** ((n + 1) / n))) - i1[0]) - def char_func(self, inl, outl): + def char_func(self): r""" equation for turbine characteristics @@ -2187,10 +2044,6 @@ def char_func(self, inl, outl): tespy.components.characteristics.turbine for more information on available methods - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*numpy array*) - residual value of equation .. math:: @@ -2198,11 +2051,11 @@ def char_func(self, inl, outl): expr \right) \cdot \Delta h_{s} """ - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() if self.eta_s_char.param == 'dh_s': - expr = math.sqrt(self.dh_s0 / (self.h_os(i, o) - i[2])) + expr = math.sqrt(self.dh_s0 / (self.h_os('post') - i[2])) elif self.eta_s_char.param == 'm': expr = i[0] / self.i0[0] elif self.eta_s_char.param == 'v': @@ -2221,30 +2074,22 @@ def char_func(self, inl, outl): return np.array([(-(o[2] - i[2]) + (self.o0[2] - self.i0[2]) / self.dh_s0 * self.eta_s_char.func.f_x(expr) * - (self.h_os(i, o) - i[2]))]) + (self.h_os('post') - i[2]))]) - def char_deriv(self, inl, outl): + def char_deriv(self): r""" partial derivatives for turbine characteristics - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of partial derivatives """ - num_i, num_o = len(inl), len(outl) - num_fl = len(inl[0].fluid.val) + num_fl = len(self.inl[0].fluid.val) - mat_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + mat_deriv = np.zeros((1, 2, num_fl + 3)) - mat_deriv[0, 0, 0] = ( - self.ddx_func(inl, outl, self.char_func, 'm', 0)) - for i in range(num_i + num_o): - mat_deriv[0, i, 1] = ( - self.ddx_func(inl, outl, self.char_func, 'p', i)) - mat_deriv[0, i, 2] = ( - self.ddx_func(inl, outl, self.char_func, 'h', i)) + mat_deriv[0, 0, 0] = self.ddx_func(self.char_func, 'm', 0) + for i in range(2): + mat_deriv[0, i, 1] = self.ddx_func(self.char_func, 'p', i) + mat_deriv[0, i, 2] = self.ddx_func(self.char_func, 'h', i) return mat_deriv.tolist() @@ -2255,7 +2100,7 @@ def convergence_check(self, nw): - set :math:`p_{out} = \frac{p_{in}}{2}` if :math:`p_{out}>p_{in}` - set :math:`h_{out} = 0,9 \cdot h_{in}` if :math:`h_{out}>h_{in}` """ - i, o = nw.comps.loc[self].i, nw.comps.loc[self].o + i, o = self.inl, self.outl if i[0].p.val_SI <= 1e5 and not i[0].p.val_set: i[0].p.val_SI = 1e5 @@ -2329,12 +2174,9 @@ def calc_parameters(self, nw, mode): turbomachine.calc_parameters(self, nw, mode) - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - if (mode == 'pre' and 'eta_s' in self.offdesign) or mode == 'post': - self.eta_s.val = ((outl[0].h.val_SI - inl[0].h.val_SI) / - (self.h_os(inl, outl) - inl[0].h.val_SI)) + self.eta_s.val = ((self.outl[0].h.val_SI - self.inl[0].h.val_SI) / + (self.h_os('post') - self.inl[0].h.val_SI)) if self.eta_s.val > 1 or self.eta_s.val <= 0: msg = ('Invalid value for isentropic efficiency.\n' 'eta_s =', self.eta_s.val) @@ -2399,7 +2241,7 @@ def outlets(self): def component(self): return 'split' - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -2436,29 +2278,27 @@ def equations(self, nw): been implemented! """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - vec_res += self.fluid_res(inl, outl) - vec_res += self.mass_flow_res(inl, outl) + vec_res += self.fluid_res() + vec_res += self.mass_flow_res() # pressure is the same at all connections - for o in outl: - vec_res += [inl[0].p.val_SI - o.p.val_SI] + for o in self.outl: + vec_res += [self.inl[0].p.val_SI - o.p.val_SI] # different equations for splitter and separator if isinstance(self, splitter): - for o in outl: - vec_res += [inl[0].h.val_SI - o.h.val_SI] + for o in self.outl: + vec_res += [self.inl[0].h.val_SI - o.h.val_SI] # different equations for splitter and separator if isinstance(self, separator): - if num_fluids(inl[0].fluid.val) <= 1: - for o in outl: - vec_res += [inl[0].h.val_SI - o.h.val_SI] + if num_fluids(self.inl[0].fluid.val) <= 1: + for o in self.outl: + vec_res += [self.inl[0].h.val_SI - o.h.val_SI] else: - for o in outl: - vec_res += [T_mix_ph(inl[0].to_flow()) - + for o in self.outl: + vec_res += [T_mix_ph(self.inl[0].to_flow()) - T_mix_ph(o.to_flow())] return vec_res @@ -2474,18 +2314,15 @@ def derivatives(self, nw): """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - mat_deriv += self.fluid_deriv(inl, outl) - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.fluid_deriv() + mat_deriv += self.mass_flow_deriv() - p_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, num_fl + 3)) + p_deriv = np.zeros((self.num_o, 1 + self.num_o, num_fl + 3)) k = 0 - for o in outl: + for o in self.outl: p_deriv[k, 0, 1] = 1 p_deriv[k, k + 1, 1] = -1 k += 1 @@ -2493,9 +2330,9 @@ def derivatives(self, nw): if isinstance(self, splitter): - h_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, num_fl + 3)) + h_deriv = np.zeros((self.num_o, 1 + self.num_o, num_fl + 3)) k = 0 - for o in outl: + for o in self.outl: h_deriv[k, 0, 2] = 1 h_deriv[k, k + 1, 2] = -1 k += 1 @@ -2503,12 +2340,11 @@ def derivatives(self, nw): mat_deriv += h_deriv.tolist() if isinstance(self, separator): - if num_fluids(inl[0].fluid.val) <= 1: + if num_fluids(self.inl[0].fluid.val) <= 1: - h_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, - num_fl + 3)) + h_deriv = np.zeros((self.num_o, 1 + self.num_o, num_fl + 3)) k = 0 - for o in outl: + for o in self.outl: h_deriv[k, 0, 2] = 1 h_deriv[k, k + 1, 2] = -1 k += 1 @@ -2516,11 +2352,10 @@ def derivatives(self, nw): mat_deriv += h_deriv.tolist() else: - T_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, - num_fl + 3)) - i = inl[0].to_flow() + T_deriv = np.zeros((self.num_o, 1 + self.num_o, num_fl + 3)) + i = self.inl[0].to_flow() k = 0 - for o in outl: + for o in self.outl: o = o.to_flow() T_deriv[k, 0, 1] = dT_mix_dph(i) T_deriv[k, 0, 2] = dT_mix_pdh(i) @@ -2578,20 +2413,17 @@ def initialise_target(self, c, key): def print_parameters(self, nw): - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - print('##### ', self.label, ' #####') - print('m_in = ', inl[0].m.val_SI, 'kg / s; ') + print('m_in = ', self.inl[0].m.val_SI, 'kg / s; ') i = 1 - for o in outl: + for o in self.outl: print('m_out' + str(i) + ' = ', o.m.val_SI, 'kg / s; ') i += 1 if isinstance(self, separator): - print('; fluid_in:', inl[0].fluid, '; ') + print('; fluid_in:', self.inl[0].fluid.val, '; ') i = 1 - for o in outl: - print('fluid_out' + str(i) + ' = ', o.fluid, 'kg / s; ') + for o in self.outl: + print('fluid_out' + str(i) + ' = ', o.fluid.val) i += 1 @@ -2686,7 +2518,7 @@ def outlets(self): def component(self): return 'merge' - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -2707,19 +2539,17 @@ def equations(self, nw): \forall i \in \mathrm{inlets} """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - vec_res += self.fluid_res(inl, outl) - vec_res += self.mass_flow_res(inl, outl) + vec_res += self.fluid_res() + vec_res += self.mass_flow_res() - h_res = -outl[0].m.val_SI * outl[0].h.val_SI - for i in inl: + h_res = -self.outl[0].m.val_SI * self.outl[0].h.val_SI + for i in self.inl: h_res += i.m.val_SI * i.h.val_SI vec_res += [h_res] - for i in inl: - vec_res += [outl[0].p.val_SI - i.p.val_SI] + for i in self.inl: + vec_res += [self.outl[0].p.val_SI - i.p.val_SI] return vec_res @@ -2733,30 +2563,27 @@ def derivatives(self, nw): :returns: mat_deriv (*numpy array*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - mat_deriv += self.fluid_deriv(inl, outl) - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.fluid_deriv() + mat_deriv += self.mass_flow_deriv() - h_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - h_deriv[0, num_i, 0] = -outl[0].h.val_SI - h_deriv[0, num_i, 2] = -outl[0].m.val_SI + h_deriv = np.zeros((1, self.num_i + 1, num_fl + 3)) + h_deriv[0, self.num_i, 0] = -self.outl[0].h.val_SI + h_deriv[0, self.num_i, 2] = -self.outl[0].m.val_SI k = 0 - for i in inl: + for i in self.inl: h_deriv[0, k, 0] = i.h.val_SI h_deriv[0, k, 2] = i.m.val_SI k += 1 mat_deriv += h_deriv.tolist() - p_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, num_fl + 3)) + p_deriv = np.zeros((self.num_i, self.num_i + 1, num_fl + 3)) k = 0 - for i in inl: + for i in self.inl: p_deriv[k, k, 1] = -1 - p_deriv[k, num_i, 1] = 1 + p_deriv[k, self.num_i, 1] = 1 k += 1 mat_deriv += p_deriv.tolist() @@ -2773,10 +2600,10 @@ def initialise_fluids(self, nw): :returns: no return value """ num_fl = {} - for o in nw.comps.loc[self].o: + for o in self.outl: num_fl[o] = num_fluids(o.fluid.val) - for i in nw.comps.loc[self].i: + for i in self.inl: num_fl[i] = num_fluids(i.fluid.val) ls = [] @@ -2787,10 +2614,10 @@ def initialise_fluids(self, nw): for c in ls: for fluid in nw.fluids: - for o in nw.comps.loc[self].o: + for o in self.outl: if not o.fluid.val_set[fluid]: o.fluid.val[fluid] = c.fluid.val[fluid] - for i in nw.comps.loc[self].i: + for i in self.inl: if not i.fluid.val_set[fluid]: i.fluid.val[fluid] = c.fluid.val[fluid] @@ -2838,15 +2665,12 @@ def initialise_target(self, c, key): def print_parameters(self, nw): - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - print('##### ', self.label, ' #####') j = 1 - for i in inl: + for i in self.inl: print('m_in' + str(j) + ' = ', i.m.val_SI, 'kg / s; ') j += 1 - print('m_out = ', outl[0].m.val_SI, 'kg / s; ') + print('m_out = ', self.outl[0].m.val_SI, 'kg / s; ') # %% @@ -3007,7 +2831,7 @@ def calc_lhv(self): return val - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -3049,24 +2873,22 @@ def equations(self, nw): """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - for fluid in nw.fluids: - vec_res += [self.reaction_balance(inl, outl, fluid)] + for fluid in self.inl[0].fluid.val.keys(): + vec_res += [self.reaction_balance(fluid)] - vec_res += self.mass_flow_res(inl, outl) + vec_res += self.mass_flow_res() - for i in inl: - vec_res += [outl[0].p.val_SI - i.p.val_SI] + for i in self.inl: + vec_res += [self.outl[0].p.val_SI - i.p.val_SI] - vec_res += [self.energy_balance(inl, outl)] + vec_res += [self.energy_balance()] if self.lamb.is_set: - vec_res += [self.lambda_func(inl, outl)] + vec_res += [self.lambda_func()] if self.ti.is_set: - vec_res += [self.ti_func(inl, outl)] + vec_res += [self.ti_func()] return vec_res @@ -3080,78 +2902,66 @@ def derivatives(self, nw): :returns: mat_deriv (*numpy array*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] # derivatives for reaction balance j = 0 - fl_deriv = np.zeros((num_fl, num_i + num_o, num_fl + 3)) + fl_deriv = np.zeros((num_fl, 3, num_fl + 3)) for fluid in nw.fluids: - for i in range(num_i + num_o): - fl_deriv[j, i, 0] = self.drb_dx(inl, outl, 'm', i, fluid) + for i in range(3): + fl_deriv[j, i, 0] = self.drb_dx('m', i, fluid) fl_deriv[j, i, 3:] = ( - self.drb_dx(inl, outl, 'fluid', i, fluid)) + self.drb_dx('fluid', i, fluid)) j += 1 mat_deriv += fl_deriv.tolist() # derivatives for mass balance - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.mass_flow_deriv() # derivatives for pressure equations - p_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, num_fl + 3)) - for k in range(num_i + num_o - 1): - p_deriv[k][num_i][1] = 1 + p_deriv = np.zeros((2, 3, num_fl + 3)) + for k in range(2): + p_deriv[k][2][1] = 1 p_deriv[k][k][1] = -1 mat_deriv += p_deriv.tolist() # derivatives for energy balance - eb_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - for i in range(num_i + num_o): + eb_deriv = np.zeros((1, 3, num_fl + 3)) + for i in range(3): eb_deriv[0, i, 0] = ( - self.ddx_func(inl, outl, self.energy_balance, 'm', i)) + self.ddx_func(self.energy_balance, 'm', i)) eb_deriv[0, i, 1] = ( - self.ddx_func(inl, outl, self.energy_balance, 'p', i)) - if i >= num_i: - eb_deriv[0, i, 2] = -(inl + outl)[i].m.val_SI + self.ddx_func(self.energy_balance, 'p', i)) + if i >= self.num_i: + eb_deriv[0, i, 2] = -(self.inl + self.outl)[i].m.val_SI else: - eb_deriv[0, i, 2] = (inl + outl)[i].m.val_SI + eb_deriv[0, i, 2] = (self.inl + self.outl)[i].m.val_SI mat_deriv += eb_deriv.tolist() if self.lamb.is_set: # derivatives for specified lambda - lamb_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - for i in range(num_i): - lamb_deriv[0, i, 0] = ( - self.ddx_func(inl, outl, self.lambda_func, 'm', i)) - lamb_deriv[0, i, 3:] = ( - self.ddx_func(inl, outl, self.lambda_func, - 'fluid', i)) + lamb_deriv = np.zeros((1, 3, num_fl + 3)) + for i in range(2): + lamb_deriv[0, i, 0] = self.ddx_func(self.lambda_func, 'm', i) + lamb_deriv[0, i, 3:] = self.ddx_func(self.lambda_func, + 'fluid', i) mat_deriv += lamb_deriv.tolist() if self.ti.is_set: # derivatives for specified lambda - ti_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - for i in range(num_i): - ti_deriv[0, i, 0] = ( - self.ddx_func(inl, outl, self.ti_func, 'm', i)) - ti_deriv[0, i, 3:] = ( - self.ddx_func(inl, outl, self.ti_func, - 'fluid', i)) - for o in range(num_o): - ti_deriv[0, o + i, 0] = ( - self.ddx_func(inl, outl, self.ti_func, 'm', o + i)) - ti_deriv[0, o + i, 3:] = ( - self.ddx_func(inl, outl, self.ti_func, - 'fluid', o + i)) + ti_deriv = np.zeros((1, 3, num_fl + 3)) + for i in range(2): + ti_deriv[0, i, 0] = self.ddx_func(self.ti_func, 'm', i) + ti_deriv[0, i, 3:] = self.ddx_func(self.ti_func, 'fluid', i) + ti_deriv[0, 2, 0] = self.ddx_func(self.ti_func, 'm', 2) + ti_deriv[0, 2, 3:] = self.ddx_func(self.ti_func, 'fluid', 2) mat_deriv += ti_deriv.tolist() return np.asarray(mat_deriv) - def reaction_balance(self, inl, outl, fluid): + def reaction_balance(self, fluid): r""" calculates the reactions mass balance for one fluid @@ -3159,10 +2969,6 @@ def reaction_balance(self, inl, outl, fluid): - calculate excess fuel - calculate residual value of the fluids balance - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :param fluid: fluid to calculate the reaction balance for :type fluid: str :returns: res (*float*) - residual value of mass balance @@ -3220,12 +3026,12 @@ def reaction_balance(self, inl, outl, fluid): """ n_fuel = 0 - for i in inl: + for i in self.inl: n_fuel += (i.m.val_SI * i.fluid.val[self.fuel.val] / molar_masses[self.fuel.val]) n_oxygen = 0 - for i in inl: + for i in self.inl: n_oxygen += (i.m.val_SI * i.fluid.val[self.o2] / molar_masses[self.o2]) @@ -3255,23 +3061,19 @@ def reaction_balance(self, inl, outl, fluid): res = dm - for i in inl: + for i in self.inl: res += i.fluid.val[fluid] * i.m.val_SI - for o in outl: + for o in self.outl: res -= o.fluid.val[fluid] * o.m.val_SI return res - def energy_balance(self, inl, outl): + def energy_balance(self): r""" calculates the energy balance of the adiabatic combustion chamber - reference temperature: 500 K - reference pressure: 1 bar - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: res (*float*) - residual value of energy balance .. math:: @@ -3285,12 +3087,12 @@ def energy_balance(self, inl, outl): p_ref = 1e5 res = 0 - for i in inl: + for i in self.inl: res += i.m.val_SI * (i.h.val_SI - h_mix_pT([i.m.val_SI, p_ref, i.h.val_SI, i.fluid.val], T_ref)) res += i.m.val_SI * i.fluid.val[self.fuel.val] * self.lhv - for o in outl: + for o in self.outl: res -= o.m.val_SI * (o.h.val_SI - h_mix_pT([o.m.val_SI, p_ref, o.h.val_SI, o.fluid.val], T_ref)) @@ -3298,14 +3100,10 @@ def energy_balance(self, inl, outl): return res - def lambda_func(self, inl, outl): + def lambda_func(self): r""" calculates the residual for specified lambda - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: res (*float*) - residual value of equation .. math:: @@ -3317,26 +3115,22 @@ def lambda_func(self, inl, outl): \left(n_{C,fuel} + 0.25 \cdot n_{H,fuel}\right)} - \lambda """ n_fuel = 0 - for i in inl: + for i in self.inl: n_fuel += (i.m.val_SI * i.fluid.val[self.fuel.val] / molar_masses[self.fuel.val]) n_oxygen = 0 - for i in inl: + for i in self.inl: n_oxygen += (i.m.val_SI * i.fluid.val[self.o2] / molar_masses[self.o2]) return (n_oxygen / (n_fuel * (self.n['C'] + self.n['H'] / 4)) - self.lamb.val) - def ti_func(self, inl, outl): + def ti_func(self): r""" calculates the residual for specified thermal input - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: res (*float*) - residual value of equation .. math:: @@ -3344,22 +3138,18 @@ def ti_func(self, inl, outl): 0 = ti - \dot{m}_f \cdot LHV """ m_fuel = 0 - for i in inl: + for i in self.inl: m_fuel += (i.m.val_SI * i.fluid.val[self.fuel.val]) - for o in outl: + for o in self.outl: m_fuel -= (o.m.val_SI * o.fluid.val[self.fuel.val]) return (self.ti.val - m_fuel * self.lhv) - def bus_func(self, inl, outl): + def bus_func(self): r""" function for use on busses - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -3368,45 +3158,36 @@ def bus_func(self, inl, outl): """ m_fuel = 0 - for i in inl: + for i in self.inl: m_fuel += (i.m.val_SI * i.fluid.val[self.fuel.val]) - for o in outl: + for o in self.outl: m_fuel -= (o.m.val_SI * o.fluid.val[self.fuel.val]) return m_fuel * self.lhv - def bus_deriv(self, inl, outl): + def bus_deriv(self): r""" calculate matrix of partial derivatives towards mass flow and fluid composition for bus function - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of partial derivatives """ - deriv = np.zeros((1, 3, len(inl[0].fluid.val) + 3)) + deriv = np.zeros((1, 3, len(self.inl[0].fluid.val) + 3)) for i in range(2): - deriv[0, i, 0] = self.ddx_func(inl, outl, self.bus_func, 'm', i) - deriv[0, i, 3:] = self.ddx_func(inl, outl, self.bus_func, - 'fluid', i) + deriv[0, i, 0] = self.ddx_func(self.bus_func, 'm', i) + deriv[0, i, 3:] = self.ddx_func(self.bus_func, 'fluid', i) - deriv[0, 2, 0] = self.ddx_func(inl, outl, self.bus_func, 'm', 2) - deriv[0, 2, 3:] = self.ddx_func(inl, outl, self.bus_func, 'fluid', 2) + deriv[0, 2, 0] = self.ddx_func(self.bus_func, 'm', 2) + deriv[0, 2, 3:] = self.ddx_func(self.bus_func, 'fluid', 2) return deriv - def drb_dx(self, inl, outl, dx, pos, fluid): + def drb_dx(self, dx, pos, fluid): r""" calculates derivative of the reaction balance to dx at components inlet or outlet in position pos for the fluid fluid - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :param dx: dx :type dx: str :param pos: position of inlet or outlet, logic: ['in1', 'in2', ..., @@ -3435,39 +3216,39 @@ def drb_dx(self, inl, outl, dx, pos, fluid): if dx == 'fluid': deriv = [] - for f in inl[0].fluid.val.keys(): - val = (inl + outl)[pos].fluid.val[f] + for f in self.inl[0].fluid.val.keys(): + val = (self.inl + self.outl)[pos].fluid.val[f] exp = 0 - if (inl + outl)[pos].fluid.val[f] + df <= 1: - (inl + outl)[pos].fluid.val[f] += df + if (self.inl + self.outl)[pos].fluid.val[f] + df <= 1: + (self.inl + self.outl)[pos].fluid.val[f] += df else: - (inl + outl)[pos].fluid.val[f] = 1 - exp += self.reaction_balance(inl, outl, fluid) - if (inl + outl)[pos].fluid.val[f] - 2 * df >= 0: - (inl + outl)[pos].fluid.val[f] -= 2 * df + (self.inl + self.outl)[pos].fluid.val[f] = 1 + exp += self.reaction_balance(fluid) + if (self.inl + self.outl)[pos].fluid.val[f] - 2 * df >= 0: + (self.inl + self.outl)[pos].fluid.val[f] -= 2 * df else: - (inl + outl)[pos].fluid.val[f] = 0 - exp -= self.reaction_balance(inl, outl, fluid) - (inl + outl)[pos].fluid.val[f] = val + (self.inl + self.outl)[pos].fluid.val[f] = 0 + exp -= self.reaction_balance(fluid) + (self.inl + self.outl)[pos].fluid.val[f] = val deriv += [exp / (2 * (dm + dp + dh + df))] else: exp = 0 - (inl + outl)[pos].m.val_SI += dm - (inl + outl)[pos].p.val_SI += dp - (inl + outl)[pos].h.val_SI += dh - exp += self.reaction_balance(inl, outl, fluid) - - (inl + outl)[pos].m.val_SI -= 2 * dm - (inl + outl)[pos].p.val_SI -= 2 * dp - (inl + outl)[pos].h.val_SI -= 2 * dh - exp -= self.reaction_balance(inl, outl, fluid) + (self.inl + self.outl)[pos].m.val_SI += dm + (self.inl + self.outl)[pos].p.val_SI += dp + (self.inl + self.outl)[pos].h.val_SI += dh + exp += self.reaction_balance(fluid) + + (self.inl + self.outl)[pos].m.val_SI -= 2 * dm + (self.inl + self.outl)[pos].p.val_SI -= 2 * dp + (self.inl + self.outl)[pos].h.val_SI -= 2 * dh + exp -= self.reaction_balance(fluid) deriv = exp / (2 * (dm + dp + dh + df)) - (inl + outl)[pos].m.val_SI += dm - (inl + outl)[pos].p.val_SI += dp - (inl + outl)[pos].h.val_SI += dh + (self.inl + self.outl)[pos].m.val_SI += dm + (self.inl + self.outl)[pos].p.val_SI += dp + (self.inl + self.outl)[pos].h.val_SI += dh return deriv @@ -3510,10 +3291,10 @@ def initialise_fluids(self, nw): self.h2o: m_h2o / m_fg } - for c in nw.comps.loc[self].o: - for fluid, x in c.fluid.val.items(): - if not c.fluid.val_set[fluid] and fluid in fg.keys(): - c.fluid.val[fluid] = fg[fluid] + for o in self.outl: + for fluid, x in o.fluid.val.items(): + if not o.fluid.val_set[fluid] and fluid in fg.keys(): + o.fluid.val[fluid] = fg[fluid] def convergence_check(self, nw): r""" @@ -3528,17 +3309,17 @@ def convergence_check(self, nw): :returns: no return value """ m = 0 - for i in nw.comps.loc[self].i: + for i in self.inl: if i.m.val_SI < 0 and not i.m.val_set: i.m.val_SI = 0.01 m += i.m.val_SI - for o in nw.comps.loc[self].o: + for o in self.outl: fluids = [f for f in o.fluid.val.keys() if not o.fluid.val_set[f]] for f in fluids: if f not in [self.o2, self.co2, self.h2o, self.fuel.val]: m_f = 0 - for i in nw.comps.loc[self].i: + for i in self.inl: m_f += i.fluid.val[f] * i.m.val_SI if abs(o.fluid.val[f] - m_f / m) > 0.03: @@ -3569,13 +3350,13 @@ def convergence_check(self, nw): else: continue - for c in nw.comps.loc[self].o: - if c.m.val_SI < 0 and not c.m.val_set: - c.m.val_SI = 10 - init_target(nw, c, c.t) + for o in self.outl: + if o.m.val_SI < 0 and not o.m.val_set: + o.m.val_SI = 10 + init_target(nw, o, o.t) - if c.h.val_SI < 7.5e5 and not c.h.val_set: - c.h.val_SI = 1e6 + if o.h.val_SI < 7.5e5 and not o.h.val_set: + o.h.val_SI = 1e6 if self.lamb.val < 1 and not self.lamb.is_set: self.lamb.val = 3 @@ -3624,20 +3405,17 @@ def initialise_target(self, c, key): def calc_parameters(self, nw, mode): - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - self.ti.val = 0 - for i in inl: + for i in self.inl: self.ti.val += i.m.val_SI * i.fluid.val[self.fuel.val] * self.lhv n_fuel = 0 - for i in inl: + for i in self.inl: n_fuel += (i.m.val_SI * i.fluid.val[self.fuel.val] / molar_masses[self.fuel.val]) n_oxygen = 0 - for i in inl: + for i in self.inl: n_oxygen += (i.m.val_SI * i.fluid.val[self.o2] / molar_masses[self.o2]) @@ -3650,11 +3428,11 @@ def calc_parameters(self, nw, mode): T_ref = 500 p_ref = 1e5 - for i in inl: + for i in self.inl: S -= i.m.val_SI * (s_mix_ph(i.to_flow()) - s_mix_pT([0, p_ref, 0, i.fluid.val], T_ref)) - for o in outl: + for o in self.outl: S += o.m.val_SI * (s_mix_ph(o.to_flow()) - s_mix_pT([0, p_ref, 0, o.fluid.val], T_ref)) @@ -3667,18 +3445,15 @@ def calc_parameters(self, nw, mode): def print_parameters(self, nw): - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - print('##### ', self.label, ' #####') print('Thermal Input = ', self.ti.val, 'lambda = ', self.lamb.val, 'S = ', self.S.val) j = 1 - for i in inl: + for i in self.inl: print('m_in' + str(j) + ' = ', i.m.val_SI, 'kg / s; ') j += 1 - print('m_out = ', outl[0].m.val_SI, 'kg / s; ') + print('m_out = ', self.outl[0].m.val_SI, 'kg / s; ') # %% @@ -4029,7 +3804,7 @@ def stoich_flue_gas(self, nw): [1000, nw.p_range_SI[1]], nw.T_range_SI, path=self.path) - def reaction_balance(self, inl, outl, fluid): + def reaction_balance(self, fluid): r""" calculates the reactions mass balance for one fluid @@ -4037,10 +3812,6 @@ def reaction_balance(self, inl, outl, fluid): - calculate excess fuel - calculate residual value of the fluids balance - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :param fluid: fluid to calculate the reaction balance for :type fluid: str :returns: res (*float*) - residual value of mass balance @@ -4104,11 +3875,11 @@ def reaction_balance(self, inl, outl, fluid): flue_gas = 'TESPy::' + self.fuel_alias.val + '_fg' m_fuel = 0 - for i in inl: + for i in self.inl: m_fuel += i.m.val_SI * i.fluid.val[fuel] m_air = 0 - for i in inl: + for i in self.inl: m_air += i.m.val_SI * i.fluid.val[air] if not self.lamb.is_set: @@ -4134,23 +3905,19 @@ def reaction_balance(self, inl, outl, fluid): res = dm - for i in inl: + for i in self.inl: res += i.fluid.val[fluid] * i.m.val_SI - for o in outl: + for o in self.outl: res -= o.fluid.val[fluid] * o.m.val_SI return res - def energy_balance(self, inl, outl): + def energy_balance(self): r""" calculates the energy balance of the adiabatic combustion chamber - reference temperature: 500 K - reference pressure: 1 bar - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: res (*float*) - residual value of energy balance .. math:: @@ -4166,12 +3933,12 @@ def energy_balance(self, inl, outl): p_ref = 1e5 res = 0 - for i in inl: + for i in self.inl: res += i.m.val_SI * (i.h.val_SI - h_mix_pT([i.m.val_SI, p_ref, i.h.val_SI, i.fluid.val], T_ref)) res += i.m.val_SI * i.fluid.val[fuel] * self.lhv - for o in outl: + for o in self.outl: res -= o.m.val_SI * (o.h.val_SI - h_mix_pT([o.m.val_SI, p_ref, o.h.val_SI, o.fluid.val], T_ref)) @@ -4179,14 +3946,10 @@ def energy_balance(self, inl, outl): return res - def lambda_func(self, inl, outl): + def lambda_func(self): r""" calculates the residual for specified thermal input - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: res (*float*) - residual value of equation .. math:: @@ -4202,20 +3965,16 @@ def lambda_func(self, inl, outl): m_air = 0 m_fuel = 0 - for i in inl: + for i in self.inl: m_air += (i.m.val_SI * i.fluid.val[air]) m_fuel += (i.m.val_SI * i.fluid.val[fuel]) return (self.lamb.val - (m_air / m_fuel) / self.air_min) - def ti_func(self, inl, outl): + def ti_func(self): r""" calculates the residual for specified thermal input - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: res (*float*) - residual value of equation .. math:: @@ -4225,22 +3984,18 @@ def ti_func(self, inl, outl): fuel = 'TESPy::' + self.fuel_alias.val m_fuel = 0 - for i in inl: + for i in self.inl: m_fuel += (i.m.val_SI * i.fluid.val[fuel]) - for o in outl: + for o in self.outl: m_fuel -= (o.m.val_SI * o.fluid.val[fuel]) return (self.ti.val - m_fuel * self.lhv) - def bus_func(self, inl, outl): + def bus_func(self): r""" function for use on busses - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -4251,10 +4006,10 @@ def bus_func(self, inl, outl): fuel = 'TESPy::' + self.fuel_alias.val m_fuel = 0 - for i in inl: + for i in self.inl: m_fuel += (i.m.val_SI * i.fluid.val[fuel]) - for o in outl: + for o in self.outl: m_fuel -= (o.m.val_SI * o.fluid.val[fuel]) return m_fuel * self.lhv @@ -4339,9 +4094,6 @@ def convergence_check(self, nw): def calc_parameters(self, nw, mode): - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - if self.air_alias.val in ['air', 'Air']: air = self.air_alias.val else: @@ -4349,11 +4101,11 @@ def calc_parameters(self, nw, mode): fuel = 'TESPy::' + self.fuel_alias.val m_fuel = 0 - for i in inl: + for i in self.inl: m_fuel += i.m.val_SI * i.fluid.val[fuel] m_air = 0 - for i in inl: + for i in self.inl: m_air += i.m.val_SI * i.fluid.val[air] if mode == 'post': @@ -4364,11 +4116,11 @@ def calc_parameters(self, nw, mode): T_ref = 500 p_ref = 1e5 - for i in inl: + for i in self.inl: S -= i.m.val_SI * (s_mix_ph(i.to_flow()) - s_mix_pT([0, p_ref, 0, i.fluid.val], T_ref)) - for o in outl: + for o in self.outl: S += o.m.val_SI * (s_mix_ph(o.to_flow()) - s_mix_pT([0, p_ref, 0, o.fluid.val], T_ref)) @@ -4379,7 +4131,7 @@ def calc_parameters(self, nw, mode): self.lamb.val = (m_air / m_fuel) / self.air_min self.ti.val = 0 - for i in inl: + for i in self.inl: self.ti.val += i.m.val_SI * i.fluid.val[fuel] * self.lhv # %% @@ -4438,7 +4190,7 @@ def outlets(self): def component(self): return 'vessel' - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -4465,19 +4217,18 @@ def equations(self, nw): """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - vec_res += self.fluid_res(inl, outl) - vec_res += self.mass_flow_res(inl, outl) + vec_res += self.fluid_res() + vec_res += self.mass_flow_res() - vec_res += [inl[0].h.val_SI - outl[0].h.val_SI] + vec_res += [self.inl[0].h.val_SI - self.outl[0].h.val_SI] if self.pr.is_set: - vec_res += [inl[0].p.val_SI * self.pr.val - outl[0].p.val_SI] + vec_res += [self.inl[0].p.val_SI * self.pr.val - + self.outl[0].p.val_SI] if self.zeta.is_set: - vec_res += [self.zeta_func(inl, outl)] + vec_res += [self.zeta_func()] return vec_res @@ -4491,37 +4242,33 @@ def derivatives(self, nw): :returns: mat_deriv (*numpy array*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - mat_deriv += self.fluid_deriv(inl, outl) - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.fluid_deriv() + mat_deriv += self.mass_flow_deriv() - h_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, num_fl + 3)) - for k in range(num_i + num_o - 1): - h_deriv[k, 0, 2] = 1 - h_deriv[k, k + 1, 2] = -1 + h_deriv = np.zeros((1, 2, num_fl + 3)) + h_deriv[0, 0, 2] = 1 + h_deriv[0, 1, 2] = -1 mat_deriv += h_deriv.tolist() if self.pr.is_set: - pr_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + pr_deriv = np.zeros((1, 2, num_fl + 3)) pr_deriv[0, 0, 1] = self.pr.val pr_deriv[0, 1, 1] = -1 mat_deriv += pr_deriv.tolist() if self.zeta.is_set: - zeta_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + zeta_deriv = np.zeros((1, 2, num_fl + 3)) for i in range(2): if i == 0: zeta_deriv[0, i, 0] = ( - self.ddx_func(inl, outl, self.zeta_func, 'm', i)) + self.ddx_func(self.zeta_func, 'm', i)) zeta_deriv[0, i, 1] = ( - self.ddx_func(inl, outl, self.zeta_func, 'p', i)) + self.ddx_func(self.zeta_func, 'p', i)) zeta_deriv[0, i, 2] = ( - self.ddx_func(inl, outl, self.zeta_func, 'h', i)) + self.ddx_func(self.zeta_func, 'h', i)) mat_deriv += zeta_deriv.tolist() return np.asarray(mat_deriv) @@ -4570,28 +4317,20 @@ def initialise_target(self, c, key): def calc_parameters(self, nw, mode): - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) + if mode == 'post' or (mode == 'pre' and 'pr' in self.offdesign): + self.pr.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI - if mode == 'post': - self.pr.val = outl[0].p.val_SI / inl[0].p.val_SI - self.zeta.val = ((inl[0].p.val_SI - outl[0].p.val_SI) * + if mode == 'post' or (mode == 'pre' and 'zeta' in self.offdesign): + self.zeta.val = ((self.inl[0].p.val_SI - self.outl[0].p.val_SI) * math.pi ** 2 / - (8 * inl[0].m.val_SI ** 2 * - (v_mix_ph(inl[0].to_flow()) + - v_mix_ph(outl[0].to_flow())) / 2)) - self.Sirr.val = inl[0].m.val_SI * (s_mix_ph(outl[0].to_flow()) - - s_mix_ph(inl[0].to_flow())) + (8 * self.inl[0].m.val_SI ** 2 * + (v_mix_ph(self.inl[0].to_flow()) + + v_mix_ph(self.outl[0].to_flow())) / 2)) - if mode == 'pre': - if 'pr' in self.offdesign: - self.pr.val = outl[0].p.val_SI / inl[0].p.val_SI - if 'zeta' in self.offdesign: - self.zeta.val = ((inl[0].p.val_SI - outl[0].p.val_SI) * - math.pi ** 2 / - (8 * inl[0].m.val_SI ** 2 * - (v_mix_ph(inl[0].to_flow()) + - v_mix_ph(outl[0].to_flow())) / 2)) + if mode == 'post': + self.Sirr.val = self.inl[0].m.val_SI * ( + s_mix_ph(self.outl[0].to_flow()) - + s_mix_ph(self.inl[0].to_flow())) def print_parameters(self, nw): @@ -4745,7 +4484,7 @@ def outlets(self): def component(self): return 'simplified heat exchanger' - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -4776,21 +4515,21 @@ def equations(self, nw): """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - vec_res += self.fluid_res(inl, outl) - vec_res += self.mass_flow_res(inl, outl) + vec_res += self.fluid_res() + vec_res += self.mass_flow_res() if self.Q.is_set: - vec_res += [inl[0].m.val_SI * - (outl[0].h.val_SI - inl[0].h.val_SI) - self.Q.val] + vec_res += [self.inl[0].m.val_SI * + (self.outl[0].h.val_SI - self.inl[0].h.val_SI) - + self.Q.val] if self.pr.is_set: - vec_res += [inl[0].p.val_SI * self.pr.val - outl[0].p.val_SI] + vec_res += [self.inl[0].p.val_SI * self.pr.val - + self.outl[0].p.val_SI] if self.zeta.is_set: - vec_res += [self.zeta_func(inl, outl)] + vec_res += [self.zeta_func()] if self.hydro_group.is_set: if self.hydro_group.method == 'HW': @@ -4798,10 +4537,10 @@ def equations(self, nw): else: func = self.lamb_func - vec_res += [func(inl, outl)] + vec_res += [func()] if self.kA_group.is_set: - vec_res += [self.kA_func(inl, outl)] + vec_res += [self.kA_func()] return vec_res @@ -4815,38 +4554,32 @@ def derivatives(self, nw): :returns: mat_deriv (*numpy array*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - mat_deriv += self.fluid_deriv(inl, outl) - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.fluid_deriv() + mat_deriv += self.mass_flow_deriv() if self.Q.is_set: - Q_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - Q_deriv[0, 0, 0] = outl[0].h.val_SI - inl[0].h.val_SI - Q_deriv[0, 0, 2] = -inl[0].m.val_SI - Q_deriv[0, 1, 2] = inl[0].m.val_SI + Q_deriv = np.zeros((1, 2, num_fl + 3)) + Q_deriv[0, 0, 0] = self.outl[0].h.val_SI - self.inl[0].h.val_SI + Q_deriv[0, 0, 2] = -self.inl[0].m.val_SI + Q_deriv[0, 1, 2] = self.inl[0].m.val_SI mat_deriv += Q_deriv.tolist() if self.pr.is_set: - pr_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + pr_deriv = np.zeros((1, 2, num_fl + 3)) pr_deriv[0, 0, 1] = self.pr.val pr_deriv[0, 1, 1] = -1 mat_deriv += pr_deriv.tolist() if self.zeta.is_set: - zeta_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + zeta_deriv = np.zeros((1, 2, num_fl + 3)) for i in range(2): if i == 0: - zeta_deriv[0, i, 0] = ( - self.ddx_func(inl, outl, self.zeta_func, 'm', i)) - zeta_deriv[0, i, 1] = ( - self.ddx_func(inl, outl, self.zeta_func, 'p', i)) - zeta_deriv[0, i, 2] = ( - self.ddx_func(inl, outl, self.zeta_func, 'h', i)) + zeta_deriv[0, i, 0] = self.ddx_func(self.zeta_func, 'm', i) + zeta_deriv[0, i, 1] = self.ddx_func(self.zeta_func, 'p', i) + zeta_deriv[0, i, 2] = self.ddx_func(self.zeta_func, 'h', i) mat_deriv += zeta_deriv.tolist() if self.hydro_group.is_set: @@ -4855,41 +4588,31 @@ def derivatives(self, nw): else: func = self.lamb_func - deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + deriv = np.zeros((1, 2, num_fl + 3)) for i in range(2): if i == 0: - deriv[0, i, 0] = ( - self.ddx_func(inl, outl, func, 'm', i)) - deriv[0, i, 1] = ( - self.ddx_func(inl, outl, func, 'p', i)) - deriv[0, i, 2] = ( - self.ddx_func(inl, outl, func, 'h', i)) + deriv[0, i, 0] = self.ddx_func(func, 'm', i) + deriv[0, i, 1] = self.ddx_func(func, 'p', i) + deriv[0, i, 2] = self.ddx_func(func, 'h', i) mat_deriv += deriv.tolist() if self.kA_group.is_set: - - kA_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - kA_deriv[0, 0, 0] = self.ddx_func(inl, outl, self.kA_func, 'm', 0) + kA_deriv = np.zeros((1, 2, num_fl + 3)) + kA_deriv[0, 0, 0] = self.ddx_func(self.kA_func, 'm', 0) for i in range(2): - kA_deriv[0, i, 1] = ( - self.ddx_func(inl, outl, self.kA_func, 'p', i)) - kA_deriv[0, i, 2] = ( - self.ddx_func(inl, outl, self.kA_func, 'h', i)) + kA_deriv[0, i, 1] = self.ddx_func(self.kA_func, 'p', i) + kA_deriv[0, i, 2] = self.ddx_func(self.kA_func, 'h', i) mat_deriv += kA_deriv.tolist() return np.asarray(mat_deriv) - def lamb_func(self, inl, outl): + def lamb_func(self): r""" equation for pressure drop from darcy friction factor - calculate reynolds and darcy friction factor - calculate pressure drop - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -4905,27 +4628,23 @@ def lamb_func(self, inl, outl): v: \text{specific volume}\\ \lambda: \text{darcy friction factor} """ - i, o = inl[0].to_flow(), outl[0].to_flow() + i, o = self.inl[0].to_flow(), self.outl[0].to_flow() visc_i, visc_o = visc_mix_ph(i), visc_mix_ph(o) v_i, v_o = v_mix_ph(i), v_mix_ph(o) - re = 4 * inl[0].m.val_SI / (math.pi * self.D.val * - (visc_i + visc_o) / 2) + re = 4 * self.inl[0].m.val_SI / (math.pi * self.D.val * + (visc_i + visc_o) / 2) - return ((inl[0].p.val_SI - outl[0].p.val_SI) - - 8 * inl[0].m.val_SI ** 2 * (v_i + v_o) / 2 * self.L.val * + return ((self.inl[0].p.val_SI - self.outl[0].p.val_SI) - + 8 * self.inl[0].m.val_SI ** 2 * (v_i + v_o) / 2 * self.L.val * lamb(re, self.ks.val, self.D.val) / (math.pi ** 2 * self.D.val ** 5)) - def hw_func(self, inl, outl): + def hw_func(self): r""" equation for pressure drop from Hazen–Williams equation - calculate pressure drop - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -4936,25 +4655,21 @@ def hw_func(self, inl, outl): \text{note: g is set to } 9.81 \frac{m}{s^2} """ - i, o = inl[0].to_flow(), outl[0].to_flow() + i, o = self.inl[0].to_flow(), self.outl[0].to_flow() v_i, v_o = v_mix_ph(i), v_mix_ph(o) - return ((inl[0].p.val_SI - outl[0].p.val_SI) - - (10.67 * inl[0].m.val_SI ** 1.852 * self.L.val / + return ((self.inl[0].p.val_SI - self.outl[0].p.val_SI) - + (10.67 * self.inl[0].m.val_SI ** 1.852 * self.L.val / (self.ks.val ** 1.852 * self.D.val ** 4.871)) * (9.81 * ((v_i + v_o) / 2) ** 0.852)) - def kA_func(self, inl, outl): + def kA_func(self): r""" equation for heat flux from ambient conditions - determine hot side and cold side of the heat exchanger - calculate heat flux - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -4981,9 +4696,9 @@ def kA_func(self, inl, outl): :func:`tespy.component.characteristics.heat_ex` """ - i, o = inl[0], outl[0] - T_i = T_mix_ph(i.to_flow()) - T_o = T_mix_ph(o.to_flow()) + i, o = self.inl, self.outl + T_i = T_mix_ph(i[0].to_flow()) + T_o = T_mix_ph(o[0].to_flow()) if self.t_a.val_SI > T_i: ttd_u = self.t_a.val_SI - T_o @@ -5007,14 +4722,10 @@ def kA_func(self, inl, outl): return (i.m.val_SI * (o.h.val_SI - i.h.val_SI) + self.kA.val * fkA * ( (ttd_u - ttd_l) / math.log(ttd_u / ttd_l))) - def bus_func(self, inl, outl): + def bus_func(self): r""" function for use on busses - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -5022,24 +4733,20 @@ def bus_func(self, inl, outl): val = \dot{m}_{in} \cdot \left( h_{out} - h_{in} \right) """ - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() return i[0] * (o[2] - i[2]) - def bus_deriv(self, inl, outl): + def bus_deriv(self): r""" calculate matrix of partial derivatives towards mass flow and enthalpy for bus function - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of partial derivatives """ - i = inl[0].to_flow() - o = outl[0].to_flow() - deriv = np.zeros((1, 2, len(inl[0].fluid.val) + 3)) + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() + deriv = np.zeros((1, 2, len(self.inl[0].fluid.val) + 3)) deriv[0, 0, 0] = o[2] - i[2] deriv[0, 0, 2] = - i[0] deriv[0, 1, 2] = i[0] @@ -5112,17 +4819,15 @@ def initialise_target(self, c, key): def calc_parameters(self, nw, mode): - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - if mode == 'post': - self.SQ1.val = inl[0].m.val_SI * (s_mix_ph(outl[0].to_flow()) - - s_mix_ph(inl[0].to_flow())) + self.SQ1.val = self.inl[0].m.val_SI * ( + s_mix_ph(self.outl[0].to_flow()) - + s_mix_ph(self.inl[0].to_flow())) if mode == 'pre': - self.i0 = inl[0].to_flow() - self.o0 = outl[0].to_flow() + self.i0 = self.inl[0].to_flow() + self.o0 = self.outl[0].to_flow() self.i0[3] = self.i0[3].copy() self.o0[3] = self.o0[3].copy() @@ -5146,8 +4851,8 @@ def calc_parameters(self, nw, mode): if t_a != np.nan: - T_i = T_mix_ph(inl[0].to_flow()) - T_o = T_mix_ph(outl[0].to_flow()) + T_i = T_mix_ph(self.inl[0].to_flow()) + T_o = T_mix_ph(self.outl[0].to_flow()) if t_a > T_i: ttd_u = t_a - T_o @@ -5165,29 +4870,29 @@ def calc_parameters(self, nw, mode): nw.errors += [self] if mode == 'post': - self.SQ2.val = - inl[0].m.val_SI * ( - outl[0].h.val_SI - inl[0].h.val_SI) / t_a + self.SQ2.val = - self.inl[0].m.val_SI * ( + self.outl[0].h.val_SI - self.inl[0].h.val_SI) / t_a self.Sirr.val = self.SQ1.val + self.SQ2.val - self.kA.val = inl[0].m.val_SI * ( - outl[0].h.val_SI - inl[0].h.val_SI) / ( + self.kA.val = self.inl[0].m.val_SI * ( + self.outl[0].h.val_SI - self.inl[0].h.val_SI) / ( (ttd_u - ttd_l) / math.log(ttd_l / ttd_u)) if (mode == 'pre' and 'Q' in self.offdesign) or mode == 'post': - self.Q.val = inl[0].m.val_SI * (outl[0].h.val_SI - - inl[0].h.val_SI) + self.Q.val = self.inl[0].m.val_SI * (self.outl[0].h.val_SI - + self.inl[0].h.val_SI) if (mode == 'pre' and 'pr' in self.offdesign) or mode == 'post': - self.pr.val = outl[0].p.val_SI / inl[0].p.val_SI + self.pr.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI if (mode == 'pre' and 'zeta' in self.offdesign) or mode == 'post': - self.zeta.val = ((inl[0].p.val_SI - outl[0].p.val_SI) * + self.zeta.val = ((self.inl[0].p.val_SI - self.outl[0].p.val_SI) * math.pi ** 2 / - (8 * inl[0].m.val_SI ** 2 * - (v_mix_ph(inl[0].to_flow()) + - v_mix_ph(outl[0].to_flow())) / 2)) + (8 * self.inl[0].m.val_SI ** 2 * + (v_mix_ph(self.inl[0].to_flow()) + + v_mix_ph(self.outl[0].to_flow())) / 2)) # improve this part (for heat exchangers only atm) if self.kA.is_set: - expr = inl[0].m.val_SI / self.i0[0] + expr = self.inl[0].m.val_SI / self.i0[0] if (expr > self.kA_char.func.x[-1] or expr < self.kA_char.func.x[0]): msg = ('Warning: Expression for characteristics out of bounds:' @@ -5365,7 +5070,7 @@ def default_offdesign(self): def component(self): return 'heat_exchanger' - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -5409,51 +5114,52 @@ def equations(self, nw): """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - vec_res += self.fluid_res(inl, outl) - vec_res += self.mass_flow_res(inl, outl) + vec_res += self.fluid_res() + vec_res += self.mass_flow_res() - vec_res += [inl[0].m.val_SI * (outl[0].h.val_SI - - inl[0].h.val_SI) + - inl[1].m.val_SI * (outl[1].h.val_SI - - inl[1].h.val_SI)] + vec_res += [self.inl[0].m.val_SI * (self.outl[0].h.val_SI - + self.inl[0].h.val_SI) + + self.inl[1].m.val_SI * (self.outl[1].h.val_SI - + self.inl[1].h.val_SI)] if self.Q.is_set: - vec_res += [inl[0].m.val_SI * (outl[0].h.val_SI - - inl[0].h.val_SI) - self.Q.val] + vec_res += [self.inl[0].m.val_SI * + (self.outl[0].h.val_SI - self.inl[0].h.val_SI) - + self.Q.val] if self.kA.is_set: - vec_res += [self.kA_func(inl, outl)] + vec_res += [self.kA_func()] # derivatives for logarithmic temperature difference not implemented # if self.td_log_set: -# vec_res += [self.td_log_func(inl, outl)] +# vec_res += [self.td_log_func()] if self.ttd_u.is_set: - vec_res += [self.ttd_u_func(inl, outl)] + vec_res += [self.ttd_u_func()] if self.ttd_l.is_set: - vec_res += [self.ttd_l_func(inl, outl)] + vec_res += [self.ttd_l_func()] if self.pr1.is_set: - vec_res += [self.pr1.val * inl[0].p.val_SI - outl[0].p.val_SI] + vec_res += [self.pr1.val * self.inl[0].p.val_SI - + self.outl[0].p.val_SI] if self.pr2.is_set: - vec_res += [self.pr2.val * inl[1].p.val_SI - outl[1].p.val_SI] + vec_res += [self.pr2.val * self.inl[1].p.val_SI - + self.outl[1].p.val_SI] if self.zeta1.is_set: - vec_res += [self.zeta_func(inl, outl)] + vec_res += [self.zeta_func()] if self.zeta2.is_set: - vec_res += [self.zeta2_func(inl, outl)] + vec_res += [self.zeta2_func()] - vec_res += self.additional_equations(nw) + vec_res += self.additional_equations() return vec_res - def additional_equations(self, nw): + def additional_equations(self): """ returns vector vec_res with result of additional equations for this component @@ -5474,40 +5180,37 @@ def derivatives(self, nw): :returns: mat_deriv (*numpy array*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - mat_deriv += self.fluid_deriv(inl, outl) - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.fluid_deriv() + mat_deriv += self.mass_flow_deriv() - q_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - for k in range(num_i): - q_deriv[0, k, 0] = outl[k].h.val_SI - inl[k].h.val_SI + q_deriv = np.zeros((1, 4, num_fl + 3)) + for k in range(2): + q_deriv[0, k, 0] = self.outl[k].h.val_SI - self.inl[k].h.val_SI - q_deriv[0, k, 2] = -inl[k].m.val_SI - q_deriv[0, 2, 2] = inl[0].m.val_SI - q_deriv[0, 3, 2] = inl[1].m.val_SI + q_deriv[0, k, 2] = -self.inl[k].m.val_SI + q_deriv[0, 2, 2] = self.inl[0].m.val_SI + q_deriv[0, 3, 2] = self.inl[1].m.val_SI mat_deriv += q_deriv.tolist() if self.Q.is_set: - Q_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - Q_deriv[0, 0, 0] = outl[0].h.val_SI - inl[0].h.val_SI - Q_deriv[0, 0, 2] = -inl[0].m.val_SI - Q_deriv[0, 2, 2] = inl[0].m.val_SI + Q_deriv = np.zeros((1, 4, num_fl + 3)) + Q_deriv[0, 0, 0] = self.outl[0].h.val_SI - self.inl[0].h.val_SI + Q_deriv[0, 0, 2] = -self.inl[0].m.val_SI + Q_deriv[0, 2, 2] = self.inl[0].m.val_SI mat_deriv += Q_deriv.tolist() if self.kA.is_set: - kA_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) - kA_deriv[0, 0, 0] = self.ddx_func(inl, outl, self.kA_func, 'm', 0) - kA_deriv[0, 1, 0] = self.ddx_func(inl, outl, self.kA_func, 'm', 1) - for i in range(num_i + num_o): + kA_deriv = np.zeros((1, 4, num_fl + 3)) + kA_deriv[0, 0, 0] = self.ddx_func(self.kA_func, 'm', 0) + kA_deriv[0, 1, 0] = self.ddx_func(self.kA_func, 'm', 1) + for i in range(4): kA_deriv[0, i, 1] = ( - self.ddx_func(inl, outl, self.kA_func, 'p', i)) + self.ddx_func(self.kA_func, 'p', i)) kA_deriv[0, i, 2] = ( - self.ddx_func(inl, outl, self.kA_func, 'h', i)) + self.ddx_func(self.kA_func, 'h', i)) mat_deriv += kA_deriv.tolist() # derivatives for logarithmic temperature difference not implemented @@ -5526,51 +5229,45 @@ def derivatives(self, nw): # self.ddx_func(i1, i2, o1, o2, self.td_log_func, 'h22')] + z]] if self.ttd_u.is_set: - mat_deriv += self.ttd_u_deriv(inl, outl) + mat_deriv += self.ttd_u_deriv() if self.ttd_l.is_set: - mat_deriv += self.ttd_l_deriv(inl, outl) + mat_deriv += self.ttd_l_deriv() if self.pr1.is_set: - pr1_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + pr1_deriv = np.zeros((1, 4, num_fl + 3)) pr1_deriv[0, 0, 1] = self.pr1.val pr1_deriv[0, 2, 1] = -1 mat_deriv += pr1_deriv.tolist() if self.pr2.is_set: - pr2_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + pr2_deriv = np.zeros((1, 4, num_fl + 3)) pr2_deriv[0, 1, 1] = self.pr2.val pr2_deriv[0, 3, 1] = -1 mat_deriv += pr2_deriv.tolist() if self.zeta1.is_set: - zeta1_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + zeta1_deriv = np.zeros((1, 4, num_fl + 3)) for i in range(2): if i == 0: zeta1_deriv[0, i * 2, 0] = ( - self.ddx_func(inl, outl, - self.zeta_func, 'm', i * 2)) + self.ddx_func(self.zeta_func, 'm', i * 2)) zeta1_deriv[0, i * 2, 1] = ( - self.ddx_func(inl, outl, - self.zeta_func, 'p', i * 2)) + self.ddx_func(self.zeta_func, 'p', i * 2)) zeta1_deriv[0, i * 2, 2] = ( - self.ddx_func(inl, outl, - self.zeta_func, 'h', i * 2)) + self.ddx_func(self.zeta_func, 'h', i * 2)) mat_deriv += zeta1_deriv.tolist() if self.zeta2.is_set: - zeta2_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + zeta2_deriv = np.zeros((1, 4, num_fl + 3)) for i in range(2): if i == 0: zeta2_deriv[0, i * 2 + 1, 0] = ( - self.ddx_func(inl, outl, - self.zeta2_func, 'm', i * 2 + 1)) + self.ddx_func(self.zeta2_func, 'm', i * 2 + 1)) zeta2_deriv[0, i * 2 + 1, 1] = ( - self.ddx_func(inl, outl, - self.zeta2_func, 'p', i * 2 + 1)) + self.ddx_func(self.zeta2_func, 'p', i * 2 + 1)) zeta2_deriv[0, i * 2 + 1, 2] = ( - self.ddx_func(inl, outl, - self.zeta2_func, 'h', i * 2 + 1)) + self.ddx_func(self.zeta2_func, 'h', i * 2 + 1)) mat_deriv += zeta2_deriv.tolist() mat_deriv += self.additional_derivatives(nw) @@ -5588,14 +5285,10 @@ def additional_derivatives(self, nw): """ return [] - def zeta2_func(self, inl, outl): + def zeta2_func(self): r""" calculates pressure drop from zeta2 - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: residual value for the pressure drop .. math:: @@ -5610,12 +5303,12 @@ def zeta2_func(self, inl, outl): 0 = \zeta_2 - \frac{(p_{2,in} - p_{2,out}) \cdot \pi^2}{8 \cdot \dot{m}_{2,in}^2 \cdot \frac{v_{2,in} + v_{2,out}}{2}} """ - i = inl[1].to_flow() - o = outl[1].to_flow() + i = self.inl[1].to_flow() + o = self.outl[1].to_flow() return (self.zeta2.val - (i[1] - o[1]) * math.pi ** 2 / (8 * i[0] ** 2 * (v_mix_ph(i) + v_mix_ph(o)) / 2)) - def kA_func(self, inl, outl): + def kA_func(self): r""" equation for heat flux from conditions on both sides of heat exchanger @@ -5626,10 +5319,6 @@ def kA_func(self, inl, outl): * :math:`T_{1,in} > T_{2,out}`? * :math:`T_{1,out} < T_{2,in}`? - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -5646,30 +5335,30 @@ def kA_func(self, inl, outl): :func:`tespy.component.characteristics.heat_ex` """ - i1 = inl[0].to_flow() - i2 = inl[1].to_flow() - o1 = outl[0].to_flow() - o2 = outl[1].to_flow() + i1 = self.inl[0].to_flow() + i2 = self.inl[1].to_flow() + o1 = self.outl[0].to_flow() + o2 = self.outl[1].to_flow() T_i1 = T_mix_ph(i1) T_i2 = T_mix_ph(i2) T_o1 = T_mix_ph(o1) T_o2 = T_mix_ph(o2) - if T_i1 <= T_o2 and not inl[0].T.val_set: + if T_i1 <= T_o2 and not self.inl[0].T.val_set: T_i1 = T_o2 + 2 - if T_i1 <= T_o2 and not outl[1].T.val_set: + if T_i1 <= T_o2 and not self.outl[1].T.val_set: T_o2 = T_i1 - 1 - if T_i1 <= T_o2 and inl[0].T.val_set and outl[1].T.val_set: + if T_i1 <= T_o2 and self.inl[0].T.val_set and self.outl[1].T.val_set: msg = ('Infeasibility at ' + str(self.label) + ': Upper ' 'temperature difference is negative!') raise MyComponentError(msg) - if T_o1 <= T_i2 and not outl[0].T.val_set: + if T_o1 <= T_i2 and not self.outl[0].T.val_set: T_o1 = T_i2 + 1 - if T_o1 <= T_i2 and not inl[1].T.val_set: + if T_o1 <= T_i2 and not self.inl[1].T.val_set: T_i2 = T_o1 - 1 - if T_o1 <= T_i2 and inl[1].T.val_set and outl[0].T.val_set: + if T_o1 <= T_i2 and self.inl[1].T.val_set and self.outl[0].T.val_set: msg = ('Infeasibility at ' + str(self.label) + ': Lower ' 'temperature difference is negative!') raise MyComponentError(msg) @@ -5702,7 +5391,7 @@ def kA_func(self, inl, outl): (T_o1 - T_i2 - T_i1 + T_o2) / math.log((T_o1 - T_i2) / (T_i1 - T_o2))) - def td_log_func(self, inl, outl): + def td_log_func(self): r""" equation for logarithmic temperature difference @@ -5713,10 +5402,6 @@ def td_log_func(self, inl, outl): * :math:`T_{1,in} > T_{2,out}`? * :math:`T_{1,out} < T_{2,in}`? - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -5726,30 +5411,30 @@ def td_log_func(self, inl, outl): {T_{1,out} - T_{2,in} - T_{1,in} + T_{2,out}} """ - i1 = inl[0].to_flow() - i2 = inl[1].to_flow() - o1 = outl[0].to_flow() - o2 = outl[1].to_flow() + i1 = self.inl[0].to_flow() + i2 = self.inl[1].to_flow() + o1 = self.outl[0].to_flow() + o2 = self.outl[1].to_flow() T_i1 = T_mix_ph(i1) T_i2 = T_mix_ph(i2) T_o1 = T_mix_ph(o1) T_o2 = T_mix_ph(o2) - if T_i1 <= T_o2 and not inl[0].T.val_set: + if T_i1 <= T_o2 and not self.inl[0].T.val_set: T_i1 = T_o2 + 1 - if T_i1 <= T_o2 and not outl[1].T.val_set: + if T_i1 <= T_o2 and not self.outl[1].T.val_set: T_o2 = T_i1 - 1 - if T_i1 <= T_o2 and inl[0].T.val_set and outl[1].T.val_set: + if T_i1 <= T_o2 and self.inl[0].T.val_set and self.outl[1].T.val_set: msg = ('Infeasibility at ' + str(self.label) + ': Upper ' 'temperature difference is negative!') raise MyComponentError(msg) - if T_o1 <= T_i2 and not outl[0].T.val_set: + if T_o1 <= T_i2 and not self.outl[0].T.val_set: T_o1 = T_i2 + 1 - if T_o1 <= T_i2 and not inl[1].T.val_set: + if T_o1 <= T_i2 and not self.inl[1].T.val_set: T_i2 = T_o1 - 1 - if T_o1 <= T_i2 and inl[1].T.val_set and outl[0].T.val_set: + if T_o1 <= T_i2 and self.inl[1].T.val_set and self.outl[0].T.val_set: msg = ('Infeasibility at ' + str(self.label) + ': Lower ' 'temperature difference is negative!') raise MyComponentError(msg) @@ -5758,88 +5443,68 @@ def td_log_func(self, inl, outl): math.log((T_o1 - T_i2) / (T_i1 - T_o2)) - T_o1 + T_i2 + T_i1 - T_o2) - def ttd_u_func(self, inl, outl): + def ttd_u_func(self): r""" equation for upper terminal temperature difference - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: 0 = ttd_{u} - T_{1,in} + T_{2,out} """ - i1 = inl[0].to_flow() - o2 = outl[1].to_flow() + i1 = self.inl[0].to_flow() + o2 = self.outl[1].to_flow() return self.ttd_u.val - T_mix_ph(i1) + T_mix_ph(o2) - def ttd_l_func(self, inl, outl): + def ttd_l_func(self): r""" equation for lower terminal temperature difference - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: 0 = ttd_{l} - T_{1,out} + T_{2,in} """ - i2 = inl[1].to_flow() - o1 = outl[0].to_flow() + i2 = self.inl[1].to_flow() + o1 = self.outl[0].to_flow() return self.ttd_l.val - T_mix_ph(o1) + T_mix_ph(i2) - def ttd_u_deriv(self, inl, outl): + def ttd_u_deriv(self): r""" calculate matrix of partial derivatives towards pressure and enthalpy for upper terminal temperature equation - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of partial derivatives """ - deriv = np.zeros((1, 4, len(inl[0].fluid.val) + 3)) + deriv = np.zeros((1, 4, len(self.inl[0].fluid.val) + 3)) for i in range(2): deriv[0, i * 3, 1] = ( - self.ddx_func(inl, outl, self.ttd_u_func, 'p', i * 3)) + self.ddx_func(self.ttd_u_func, 'p', i * 3)) deriv[0, i * 3, 2] = ( - self.ddx_func(inl, outl, self.ttd_u_func, 'h', i * 3)) + self.ddx_func(self.ttd_u_func, 'h', i * 3)) return deriv.tolist() - def ttd_l_deriv(self, inl, outl): + def ttd_l_deriv(self): r""" calculate matrix of partial derivatives towards pressure and enthalpy for lower terminal temperature equation - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of partial derivatives """ - deriv = np.zeros((1, 4, len(inl[0].fluid.val) + 3)) + deriv = np.zeros((1, 4, len(self.inl[0].fluid.val) + 3)) for i in range(2): deriv[0, i + 1, 1] = ( - self.ddx_func(inl, outl, self.ttd_l_func, 'p', i + 1)) + self.ddx_func(self.ttd_l_func, 'p', i + 1)) deriv[0, i + 1, 2] = ( - self.ddx_func(inl, outl, self.ttd_l_func, 'h', i + 1)) + self.ddx_func(self.ttd_l_func, 'h', i + 1)) return deriv.tolist() - def bus_func(self, inl, outl): + def bus_func(self): r""" function for use on busses - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -5847,24 +5512,20 @@ def bus_func(self, inl, outl): val = \dot{m}_{1,in} \cdot \left( h_{1,out} - h_{1,in} \right) """ - i = inl[0].to_flow() - o = outl[0].to_flow() + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() return i[0] * (o[2] - i[2]) - def bus_deriv(self, inl, outl): + def bus_deriv(self): r""" calculate matrix of partial derivatives towards mass flow and enthalpy for bus function - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: mat_deriv (*list*) - matrix of partial derivatives """ - i = inl[0].to_flow() - o = outl[0].to_flow() - deriv = np.zeros((1, 4, len(inl[0].fluid.val) + 3)) + i = self.inl[0].to_flow() + o = self.outl[0].to_flow() + deriv = np.zeros((1, 4, len(self.inl[0].fluid.val) + 3)) for i in range(2): deriv[0, 0, 0] = o[2] - i[2] deriv[0, 0, 2] = - i[0] @@ -5883,7 +5544,7 @@ def convergence_check(self, nw): :returns: no return value """ - i, o = nw.comps.loc[self].i.tolist(), nw.comps.loc[self].o.tolist() + i, o = self.inl, self.outl if i[0].h.val_SI < o[0].h.val_SI and not o[0].h.val_set: o[0].h.val_SI = i[0].h.val_SI / 2 @@ -5977,29 +5638,30 @@ def initialise_target(self, c, key): def calc_parameters(self, nw, mode): - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) + i1 = self.inl[0].to_flow() + i2 = self.inl[1].to_flow() + o1 = self.outl[0].to_flow() + o2 = self.outl[1].to_flow() if mode == 'pre': - self.i10 = inl[0].to_flow() - self.i20 = inl[1].to_flow() - self.o10 = outl[0].to_flow() - self.o20 = outl[1].to_flow() + self.i10 = i1 + self.i20 = i2 + self.o10 = o1 + self.o20 = o2 self.i10[3] = self.i10[3].copy() self.i20[3] = self.i20[3].copy() self.o10[3] = self.o10[3].copy() self.o20[3] = self.o20[3].copy() - T_i2 = T_mix_ph(inl[1].to_flow()) - T_o1 = T_mix_ph(outl[0].to_flow()) + T_i2 = T_mix_ph(i2) + T_o1 = T_mix_ph(o1) if isinstance(self, condenser): - i1 = inl[0].to_flow() T_i1 = T_mix_ph([i1[0], i1[1], h_mix_pQ(i1, 1), i1[3]]) else: - T_i1 = T_mix_ph(inl[0].to_flow()) - T_o2 = T_mix_ph(outl[1].to_flow()) + T_i1 = T_mix_ph(i1) + T_o2 = T_mix_ph(o2) if (mode == 'pre' and 'ttd_u' in self.offdesign) or mode == 'post': self.ttd_u.val = T_i1 - T_o2 if (mode == 'pre' and 'ttd_l' in self.offdesign) or mode == 'post': @@ -6014,8 +5676,8 @@ def calc_parameters(self, nw, mode): nw.errors += [self] if (mode == 'pre' and 'Q' in self.offdesign) or mode == 'post': - self.Q.val = inl[0].m.val_SI * (outl[0].h.val_SI - - inl[0].h.val_SI) + self.Q.val = self.inl[0].m.val_SI * (self.outl[0].h.val_SI - + self.inl[0].h.val_SI) if (mode == 'pre' and 'kA' in self.offdesign) or mode == 'post': if T_i1 <= T_o2 or T_o1 <= T_i2: @@ -6024,37 +5686,33 @@ def calc_parameters(self, nw, mode): else: self.td_log.val = ((T_o1 - T_i2 - T_i1 + T_o2) / math.log((T_o1 - T_i2) / (T_i1 - T_o2))) - self.kA.val = -(inl[0].m.val_SI * ( - outl[0].h.val_SI - inl[0].h.val_SI) / + self.kA.val = -(self.inl[0].m.val_SI * ( + self.outl[0].h.val_SI - self.inl[0].h.val_SI) / self.td_log.val) if (mode == 'pre' and 'pr1' in self.offdesign) or mode == 'post': - self.pr1.val = outl[0].p.val_SI / inl[0].p.val_SI + self.pr1.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI if (mode == 'pre' and 'pr2' in self.offdesign) or mode == 'post': - self.pr2.val = outl[1].p.val_SI / inl[1].p.val_SI + self.pr2.val = self.outl[1].p.val_SI / self.inl[1].p.val_SI if (mode == 'pre' and 'zeta1' in self.offdesign) or mode == 'post': - self.zeta1.val = ((inl[0].p.val_SI - outl[0].p.val_SI) * + self.zeta1.val = ((self.inl[0].p.val_SI - self.outl[0].p.val_SI) * math.pi ** 2 / - (8 * inl[0].m.val_SI ** 2 * - (v_mix_ph(inl[0].to_flow()) + - v_mix_ph(outl[0].to_flow())) / 2)) + (8 * self.inl[0].m.val_SI ** 2 * + (v_mix_ph(i1) + v_mix_ph(o1)) / 2)) if (mode == 'pre' and 'zeta2' in self.offdesign) or mode == 'post': - self.zeta2.val = ((inl[1].p.val_SI - outl[1].p.val_SI) * + self.zeta2.val = ((self.inl[1].p.val_SI - self.outl[1].p.val_SI) * math.pi ** 2 / - (8 * inl[1].m.val_SI ** 2 * - (v_mix_ph(inl[1].to_flow()) + - v_mix_ph(outl[1].to_flow())) / 2)) + (8 * self.inl[1].m.val_SI ** 2 * + (v_mix_ph(i2) + v_mix_ph(o2)) / 2)) if mode == 'post': - self.SQ1.val = inl[0].m.val_SI * (s_mix_ph(outl[0].to_flow()) - - s_mix_ph(inl[0].to_flow())) - self.SQ2.val = inl[1].m.val_SI * (s_mix_ph(outl[1].to_flow()) - - s_mix_ph(inl[1].to_flow())) + self.SQ1.val = self.inl[0].m.val_SI * (s_mix_ph(o1) - s_mix_ph(i1)) + self.SQ2.val = self.inl[1].m.val_SI * (s_mix_ph(o2) - s_mix_ph(i2)) self.Sirr.val = self.SQ1.val + self.SQ2.val # improve this part (for heat exchangers only atm) if self.kA.is_set: - expr = inl[0].m.val_SI / self.i10[0] + expr = self.inl[0].m.val_SI / self.i10[0] if (expr > self.kA_char1.func.x[-1] or expr < self.kA_char1.func.x[0]): msg = ('Warning: Expression for characteristics out of bounds:' @@ -6062,7 +5720,7 @@ def calc_parameters(self, nw, mode): print(msg) nw.errors += [self] - expr = inl[1].m.val_SI / self.i20[0] + expr = self.inl[1].m.val_SI / self.i20[0] if (expr > self.kA_char2.func.x[-1] or expr < self.kA_char2.func.x[0]): msg = ('Warning: Expression for characteristics out of bounds:' @@ -6164,7 +5822,7 @@ def default_offdesign(self): return [n for n in heat_exchanger.default_offdesign(self) if n != 'zeta1'] - def additional_equations(self, nw): + def additional_equations(self): r""" returns vector vec_res with result of additional equations for this component @@ -6181,7 +5839,7 @@ def additional_equations(self, nw): x: \text{vapour mass fraction} """ vec_res = [] - outl = nw.comps.loc[self].o.tolist() + outl = self.outl o1 = outl[0].to_flow() vec_res += [o1[2] - h_mix_pQ(o1, 0)] @@ -6198,21 +5856,18 @@ def additional_derivatives(self, nw): :returns: mat_deriv (*list*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - o1 = outl[0].to_flow() - x_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + o1 = self.outl[0].to_flow() + x_deriv = np.zeros((1, 4, num_fl + 3)) x_deriv[0, 2, 1] = -dh_mix_dpQ(o1, 0) x_deriv[0, 2, 2] = 1 mat_deriv += x_deriv.tolist() return mat_deriv - def kA_func(self, inl, outl): + def kA_func(self): r""" equation for heat flux from conditions on both sides of heat exchanger @@ -6225,10 +5880,6 @@ def kA_func(self, inl, outl): - kA refers to boiling temperature at hot side inlet - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: @@ -6251,24 +5902,24 @@ def kA_func(self, inl, outl): :func:`tespy.component.characteristics.heat_ex` """ - i1 = inl[0].to_flow() - i2 = inl[1].to_flow() - o1 = outl[0].to_flow() - o2 = outl[1].to_flow() + i1 = self.inl[0].to_flow() + i2 = self.inl[1].to_flow() + o1 = self.outl[0].to_flow() + o2 = self.outl[1].to_flow() T_i1 = T_mix_ph([i1[0], i1[1], h_mix_pQ(i1, 1), i1[3]]) T_i2 = T_mix_ph(i2) T_o1 = T_mix_ph(o1) T_o2 = T_mix_ph(o2) - if T_i1 <= T_o2 and not inl[0].T.val_set: + if T_i1 <= T_o2 and not self.inl[0].T.val_set: T_i1 = T_o2 + 0.5 - if T_i1 <= T_o2 and not outl[1].T.val_set: + if T_i1 <= T_o2 and not self.outl[1].T.val_set: T_o2 = T_i1 - 0.5 - if T_o1 <= T_i2 and not outl[0].T.val_set: + if T_o1 <= T_i2 and not self.outl[0].T.val_set: T_o1 = T_i2 + 1 - if T_o1 <= T_i2 and not inl[1].T.val_set: + if T_o1 <= T_i2 and not self.inl[1].T.val_set: T_i2 = T_o1 - 1 if self.kA_char1.param == 'm': @@ -6300,7 +5951,7 @@ def kA_func(self, inl, outl): math.log((T_o1 - T_i2) / (T_i1 - T_o2))) # function for logarithmic temperature difference not implemented -# def td_log_func(self, inl, outl): +# def td_log_func(self): # # T_i1 = T_mix_ph([i1[0], i1[1], h_mix_pQ(i1, 1), i1[3]]) # T_i2 = T_mix_ph(i2) @@ -6324,24 +5975,20 @@ def kA_func(self, inl, outl): # math.log((T_o1 - T_i2) / (T_i1 - T_o2)) - # T_o1 + T_i2 + T_i1 - T_o2) - def ttd_u_func(self, inl, outl): + def ttd_u_func(self): r""" equation for upper terminal temperature difference - ttd_u refers to boiling temperature at hot side inlet - :param inl: the components connections at the inlets - :type inl: list - :param outl: the components connections at the outlets - :type outl: list :returns: val (*float*) - residual value of equation .. math:: 0 = ttd_{u} - T_s \left(p_{1,in}\right) + T_{2,out} """ - i1 = inl[0].to_flow() - o2 = outl[1].to_flow() + i1 = self.inl[0].to_flow() + o2 = self.outl[1].to_flow() return (self.ttd_u.val - T_mix_ph([i1[0], i1[1], h_mix_pQ(i1, 1), i1[3]]) + T_mix_ph(o2)) @@ -6406,7 +6053,7 @@ def default_design(self): def default_offdesign(self): return heat_exchanger.default_offdesign(self) - def additional_equations(self, nw): + def additional_equations(self): r""" returns vector vec_res with result of additional equations for this component @@ -6424,9 +6071,8 @@ def additional_equations(self, nw): """ vec_res = [] - outl = nw.comps.loc[self].o.tolist() - o1 = outl[0].to_flow() + o1 = self.outl[0].to_flow() vec_res += [o1[2] - h_mix_pQ(o1, 1)] return vec_res @@ -6440,15 +6086,11 @@ def additional_derivatives(self, nw): :type nw: tespy.networks.network :returns: mat_deriv (*list*) - matrix of partial derivatives """ - - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - o1 = outl[0].to_flow() - x_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + o1 = self.outl[0].to_flow() + x_deriv = np.zeros((1, 4, num_fl + 3)) x_deriv[0, 2, 1] = -dh_mix_dpQ(o1, 1) x_deriv[0, 2, 2] = 1 mat_deriv += x_deriv.tolist() @@ -6497,7 +6139,7 @@ def outlets(self): def component(self): return 'drum' - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -6522,25 +6164,25 @@ def equations(self, nw): """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - vec_res += self.fluid_res(inl, outl) - vec_res += self.mass_flow_res(inl, outl) + vec_res += self.fluid_res() + vec_res += self.mass_flow_res() E_res = 0 - for i in inl: + for i in self.inl: E_res += i.m.val_SI * i.h.val_SI - for o in outl: + for o in self.outl: E_res -= o.m.val_SI * o.h.val_SI vec_res += [E_res] - p = inl[0].p.val_SI - for c in [inl[1]] + outl: + p = self.inl[0].p.val_SI + for c in [self.inl[1]] + self.outl: vec_res += [p - c.p.val_SI] - vec_res += [h_mix_pQ(outl[0].to_flow(), 0) - outl[0].h.val_SI] - vec_res += [h_mix_pQ(outl[1].to_flow(), 1) - outl[1].h.val_SI] + vec_res += [h_mix_pQ(self.outl[0].to_flow(), 0) - + self.outl[0].h.val_SI] + vec_res += [h_mix_pQ(self.outl[1].to_flow(), 1) - + self.outl[1].h.val_SI] return vec_res @@ -6554,38 +6196,35 @@ def derivatives(self, nw): :returns: mat_deriv (*numpy array*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) mat_deriv = [] - mat_deriv += self.fluid_deriv(inl, outl) - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.fluid_deriv() + mat_deriv += self.mass_flow_deriv() - E_deriv = np.zeros((1, num_i + num_o, num_fl + 3)) + E_deriv = np.zeros((1, 4, num_fl + 3)) k = 0 - for i in inl: + for i in self.inl: E_deriv[0, k, 0] = i.h.val_SI E_deriv[0, k, 2] = i.m.val_SI k += 1 j = 0 - for o in outl: + for o in self.outl: E_deriv[0, j + k, 0] = -o.h.val_SI E_deriv[0, j + k, 2] = -o.m.val_SI j += 1 mat_deriv += E_deriv.tolist() - p_deriv = np.zeros((num_i + num_o - 1, num_i + num_o, num_fl + 3)) - for k in range(num_i + num_o - 1): + p_deriv = np.zeros((3, 4, num_fl + 3)) + for k in range(3): p_deriv[k, 0, 1] = 1 p_deriv[k, k + 1, 1] = -1 mat_deriv += p_deriv.tolist() - o1 = outl[0].to_flow() - o2 = outl[1].to_flow() + o1 = self.outl[0].to_flow() + o2 = self.outl[1].to_flow() - x_deriv = np.zeros((num_o, num_i + num_o, num_fl + 3)) + x_deriv = np.zeros((2, 4, num_fl + 3)) x_deriv[0, 2, 1] = dh_mix_dpQ(o1, 0) x_deriv[0, 2, 2] = -1 x_deriv[1, 3, 1] = dh_mix_dpQ(o2, 1) @@ -6702,7 +6341,7 @@ def outlets(self): def component(self): return 'subsystem interface' - def equations(self, nw): + def equations(self): r""" returns vector vec_res with result of equations for this component @@ -6724,18 +6363,17 @@ def equations(self, nw): """ vec_res = [] - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - - vec_res += self.fluid_res(inl, outl) - vec_res += self.mass_flow_res(inl, outl) - for j in range(len(inl)): - i = inl[j] - o = outl[j] + num_inl = len(self.inl) + + vec_res += self.fluid_res() + vec_res += self.mass_flow_res() + for j in range(num_inl): + i = self.inl[j] + o = self.outl[j] vec_res += [i.p.val_SI - o.p.val_SI] - for j in range(len(inl)): - i = inl[j] - o = outl[j] + for j in range(num_inl): + i = self.inl[j] + o = self.outl[j] vec_res += [i.h.val_SI - o.h.val_SI] return vec_res @@ -6750,26 +6388,24 @@ def derivatives(self, nw): :returns: mat_deriv (*numpy array*) - matrix of partial derivatives """ - inl, outl = (nw.comps.loc[self].i.tolist(), - nw.comps.loc[self].o.tolist()) - num_i, num_o = len(inl), len(outl) num_fl = len(nw.fluids) + num_inl, num_outl = len(self.inl), len(self.outl) mat_deriv = [] - mat_deriv += self.fluid_deriv(inl, outl) - mat_deriv += self.mass_flow_deriv(inl, outl) + mat_deriv += self.fluid_deriv() + mat_deriv += self.mass_flow_deriv() - p_deriv = np.zeros((num_i, num_i + num_o, num_fl + 3)) - for i in range(num_i): + p_deriv = np.zeros((num_inl, num_inl + num_outl, num_fl + 3)) + for i in range(num_inl): p_deriv[i, i, 1] = 1 - for j in range(num_o): + for j in range(num_outl): p_deriv[j, j + i + 1, 1] = -1 mat_deriv += p_deriv.tolist() - h_deriv = np.zeros((num_i, num_i + num_o, num_fl + 3)) - for i in range(num_i): + h_deriv = np.zeros((num_inl, num_inl + num_outl, num_fl + 3)) + for i in range(num_inl): h_deriv[i, i, 2] = 1 - for j in range(num_o): + for j in range(num_outl): h_deriv[j, j + i + 1, 2] = -1 mat_deriv += h_deriv.tolist() From 477850b0990d9fce6994058ae1d4aa9a8598c70b Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 25 Jul 2018 18:46:57 +0200 Subject: [PATCH 17/63] changed network logic according to changes in components module --- tespy/networks.py | 66 +++++++++++------------------------------------ 1 file changed, 15 insertions(+), 51 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index 1a92ad73f..cea40d4af 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -473,6 +473,10 @@ def init_components(self): t = self.conns[self.conns.t == comp] self.comps.loc[comp] = [t.t_id.sort_values().index, s.s_id.sort_values().index] + comp.inl = t.t_id.sort_values().index.tolist() + comp.outl = s.s_id.sort_values().index.tolist() + comp.num_i = len(comp.inl) + comp.num_o = len(comp.outl) labels += [comp.label] if len(labels) != len(list(set(labels))): @@ -740,7 +744,6 @@ def init_val0(self, c, key): if math.isnan(c.get_attr(key).val0): val_s = c.s.initialise_source(c, key) val_t = c.t.initialise_target(c, key) -# print(key, c.s.label, val_s, c.t.label, val_t) if val_s == 0 and val_t == 0: if key == 'p': c.get_attr(key).val0 = 1e5 @@ -951,10 +954,6 @@ def solve(self, mode, init_file=None, design_file=None, dec='.', if init_only: return - # vectors for convergence history (massflow, pressure, enthalpy) - self.convergence[0] = np.zeros((len(self.conns), 0)) - self.convergence[1] = np.zeros((len(self.conns), 0)) - self.convergence[2] = np.zeros((len(self.conns), 0)) self.res = np.array([]) print('Solving network.') @@ -991,7 +990,6 @@ def solve(self, mode, init_file=None, design_file=None, dec='.', start_time = time.time() self.solve_loop() end_time = time.time() - self.processing('post') if self.parallel: @@ -1027,42 +1025,15 @@ def solve_loop(self): print('iter\t| residual') for self.iter in range(self.max_iter): - self.convergence[0] = np.column_stack(( - self.convergence[0], [0] * len(self.conns))) - self.convergence[1] = np.column_stack(( - self.convergence[1], [0] * len(self.conns))) - self.convergence[2] = np.column_stack(( - self.convergence[2], [0] * len(self.conns))) - self.solve_control() self.res = np.append(self.res, norm(self.vec_res)) print(self.iter + 1, '\t|', '{:.2e}'.format(norm(self.vec_res))) - k = 0 - for c in self.conns.index: - self.convergence[0][k][self.iter] = c.m.val_SI - self.convergence[1][k][self.iter] = c.p.val_SI - self.convergence[2][k][self.iter] = c.h.val_SI - k += 1 - - if self.iter > 3: - self.relax = 1 - self.iter += 1 # stop calculation after rediculous amount of iterations - if self.iter > 3: - if self.res[-1] < hlp.err ** (1 / 2): - if self.num_restart > 0: - self.convergence[0] = np.column_stack(( - self.convergence[0], [0] * len(self.conns))) - self.convergence[1] = np.column_stack(( - self.convergence[1], [0] * len(self.conns))) - self.convergence[2] = np.column_stack(( - self.convergence[2], [0] * len(self.conns))) - self.vec_z = np.zeros((self.num_vars * - len(self.conns),)) + if self.iter > 3 and self.res[-1] < hlp.err ** (1 / 2): break if self.iter > 15: @@ -1070,15 +1041,6 @@ def solve_loop(self): self.res[-1] >= self.res[-2]): print('ERROR: Convergence is making no progress, ' 'calculation stopped.') - if self.num_restart > 0: - self.convergence[0] = np.column_stack(( - self.convergence[0], [0] * len(self.conns))) - self.convergence[1] = np.column_stack(( - self.convergence[1], [0] * len(self.conns))) - self.convergence[2] = np.column_stack(( - self.convergence[2], [0] * len(self.conns))) - self.vec_z = np.zeros((self.num_vars * - len(self.conns),)) break if self.lin_dep: @@ -1102,6 +1064,7 @@ def solve_control(self): **Improvememts** """ self.vec_res = [] + self.solve_components() self.solve_connections() self.solve_busses() @@ -1309,15 +1272,16 @@ def solve_parallelize(self, func, data): def solve_comp(args): nw, data = args return [ - data[0].apply(network.solve_comp_eq, axis=1, args=(nw,)), + data[0].apply(network.solve_comp_eq, axis=1), data[0].apply(network.solve_comp_deriv, axis=1, args=(nw,)) ] - def solve_comp_eq(cp, nw): - return cp.name.equations(nw) + def solve_comp_eq(cp): + return cp.name.equations() def solve_comp_deriv(cp, nw): - return [cp.name.derivatives(nw)] + a = cp.name.derivatives(nw) + return [a] def solve_connections(self): """ @@ -1457,8 +1421,8 @@ def solve_busses(self): i = self.comps.loc[cp].i.tolist() o = self.comps.loc[cp].o.tolist() - P_res += cp.bus_func(i, o) * b.comps.loc[cp].factor - deriv = -cp.bus_deriv(i, o) + P_res += cp.bus_func() * b.comps.loc[cp].factor + deriv = -cp.bus_deriv() j = 0 for c in i + o: @@ -1767,7 +1731,7 @@ def solve_determination(self): """ n = 0 for cp in self.comps.index: - n += len(cp.equations(self)) + n += len(cp.equations()) for c in self.conns.index: n += [c.m.val_set, c.p.val_set, c.h.val_set, @@ -1834,7 +1798,7 @@ def process_busses(self): i = self.comps.loc[cp].i.tolist() o = self.comps.loc[cp].o.tolist() - b.P += cp.bus_func(i, o) * b.comps.loc[cp].factor + b.P += cp.bus_func() * b.comps.loc[cp].factor def process_components(cols, nw, mode): """ From 9665e509b430d5d416c6a9dc6dfedd7d35849de5 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 26 Jul 2018 07:50:14 +0200 Subject: [PATCH 18/63] minor adjustments to helpers, removed residuals --- tespy/helpers.py | 20 ++++++++++---------- tespy/networks.py | 3 +-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/tespy/helpers.py b/tespy/helpers.py index 5bf045a07..618542922 100644 --- a/tespy/helpers.py +++ b/tespy/helpers.py @@ -617,10 +617,10 @@ def T_mix_ph(flow): pp: \text{partial pressure} """ # check if fluid properties have been calculated before - fl = tuple(sorted(list(flow[3].keys()))) + 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**2, axis=1))[0] + ix = np.where(np.all(abs(a - b) <= err, axis=1))[0] if ix.size == 1: # known fluid properties @@ -772,10 +772,10 @@ def T_mix_ps(flow, s): pp: \text{partial pressure} """ # check if fluid properties have been calculated before - fl = tuple(sorted(list(flow[3].keys()))) + 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**2, axis=1))[0] + 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] @@ -1016,10 +1016,10 @@ def v_mix_ph(flow): v_{mix}\left(p,h\right) = v\left(p,T_{mix}(p,h)\right) """ # check if fluid properties have been calculated before - fl = tuple(sorted(list(flow[3].keys()))) + fl = tuple(flow[3].keys()) a = memorise.v_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] + ix = np.where(np.all(abs(a - b) <= err, axis=1))[0] if ix.size == 1: # known fluid properties v = memorise.v_ph[fl][ix, -1][0] @@ -1154,10 +1154,10 @@ def visc_mix_ph(flow): \eta_{mix}\left(p,h\right) = \eta\left(p,T_{mix}(p,h)\right) """ # check if fluid properties have been calculated before - fl = tuple(sorted(list(flow[3].keys()))) + fl = tuple(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] + ix = np.where(np.all(abs(a - b) <= err, axis=1))[0] if ix.size == 1: # known fluid properties visc = memorise.visc_ph[fl][ix, -1][0] @@ -1280,10 +1280,10 @@ def s_mix_ph(flow): s_{mix}\left(p,h\right) = s\left(p,T_{mix}(p,h)\right) """ # check if fluid properties have been calculated before - fl = tuple(sorted(list(flow[3].keys()))) + fl = tuple(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] + 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] diff --git a/tespy/networks.py b/tespy/networks.py index cea40d4af..1c131040b 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -1280,8 +1280,7 @@ def solve_comp_eq(cp): return cp.name.equations() def solve_comp_deriv(cp, nw): - a = cp.name.derivatives(nw) - return [a] + return [cp.name.derivatives(nw)] def solve_connections(self): """ From 685577748221f86fbf8cb30fad16f948801e6ca7 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sat, 28 Jul 2018 10:28:55 +0200 Subject: [PATCH 19/63] perform component initialisation only if network is not checked or for offdesign cases --- tespy/networks.py | 94 +++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index 1c131040b..41b57c929 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -387,15 +387,15 @@ def check_network(self): :raises: :code:`hlp.MyNetworkError`, if number of connections in the network does not match number of connections required """ - for comp in pd.unique(self.conns[['s', 't']].values.ravel()): + comps = pd.unique(self.conns[['s', 't']].values.ravel()) + self.init_components(comps) # build the dataframe for components + for comp in self.comps.index: freq = 0 freq += (self.conns[['s', 't']] == comp).sum().s freq += (self.conns[['s', 't']] == comp).sum().t - if comp.outlets() is not None: - freq -= len(comp.outlets()) - if comp.inlets() is not None: - freq -= len(comp.inlets()) + freq -= comp.num_i + freq -= comp.num_o if freq != 0: msg = (str(comp) + ' (' + str(comp.label) + ') is missing ' + str(-freq) + ' connections. Make sure all ' @@ -407,43 +407,7 @@ def check_network(self): self.checked = True print('Networkcheck successfull.') - def initialise(self): - """ - initilialises the network - - - component initlialisation - - fluid propagation on all connections - - initilialise fluid properties - - initialisiation from .csv-files - - switch components to offdesign mode for offedesign calculation - - :returns: no return value - """ - - msg = ('Have you adjusted the value ranges for pressure, enthalpy' - ' and temperature according to the specified unit system?') - print(msg) - - if len(self.fluids) == 0: - msg = ('Network has no fluids, please specify a list with fluids ' - 'on network creation.') - raise hlp.MyNetworkError(msg) - self.init_components() # build the dataframe for components - - if self.mode == 'offdesign': - self.init_offdesign() # characteristics for offdesign - - self.init_fluids() # start standard fluid initialisation - self.init_properties() # start standard property initialisation - - if self.mode == 'offdesign' and self.design_file is None: - msg = ('Please provide \'design_file\' for every offdesign ' - 'calculation.') - raise hlp.MyNetworkError(msg) # must provide design_file - else: - self.init_csv() # initialisation from csv - - def init_components(self): + def init_components(self, comps): """ writes the networks components into dataframe @@ -463,18 +427,18 @@ def init_components(self): :returns: no return value """ - comps = pd.unique(self.conns[['s', 't']].values.ravel()) self.comps = pd.DataFrame(index=comps, columns=['i', 'o']) labels = [] for comp in self.comps.index: comp.comp_init(self) s = self.conns[self.conns.s == comp] + s = s.s_id.sort_values().index t = self.conns[self.conns.t == comp] - self.comps.loc[comp] = [t.t_id.sort_values().index, - s.s_id.sort_values().index] - comp.inl = t.t_id.sort_values().index.tolist() - comp.outl = s.s_id.sort_values().index.tolist() + t = t.t_id.sort_values().index + self.comps.loc[comp] = [t, s] + comp.inl = t.tolist() + comp.outl = s.tolist() comp.num_i = len(comp.inl) comp.num_o = len(comp.outl) labels += [comp.label] @@ -486,6 +450,41 @@ def init_components(self): str(duplicates)) raise hlp.MyNetworkError(msg) + def initialise(self): + """ + initilialises the network + + - component initlialisation + - fluid propagation on all connections + - initilialise fluid properties + - initialisiation from .csv-files + - switch components to offdesign mode for offedesign calculation + + :returns: no return value + """ + + msg = ('Have you adjusted the value ranges for pressure, enthalpy' + ' and temperature according to the specified unit system?') + print(msg) + + if len(self.fluids) == 0: + msg = ('Network has no fluids, please specify a list with fluids ' + 'on network creation.') + raise hlp.MyNetworkError(msg) + + if self.mode == 'offdesign': + self.init_offdesign() # characteristics for offdesign + + self.init_fluids() # start standard fluid initialisation + self.init_properties() # start standard property initialisation + + if self.mode == 'offdesign' and self.design_file is None: + msg = ('Please provide \'design_file\' for every offdesign ' + 'calculation.') + raise hlp.MyNetworkError(msg) # must provide design_file + else: + self.init_csv() # initialisation from csv + def init_fluids(self): """ initialises the fluid vector on every connection of the network @@ -904,6 +903,7 @@ def init_offdesign(self): for var in cp.offdesign: if not cp.get_attr(var).is_set: cp.get_attr(var).set_attr(is_set=True) + cp.comp_init(self) for c in self.conns.index: for var in c.design: From 863c36591f33677da60bea578fb686fbfae0e64d Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sat, 28 Jul 2018 10:29:25 +0200 Subject: [PATCH 20/63] fixed kA_func with new inl, outl handling --- tespy/components/components.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index ea5440dad..9ce9a60ef 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -4708,7 +4708,7 @@ def kA_func(self): ttd_l = T_o - self.t_a.val_SI if self.kA_char.param == 'm': - expr = i.m.val_SI / self.i0[0] + expr = i[0].m.val_SI / self.i0[0] else: expr = 1 @@ -4719,8 +4719,8 @@ def kA_func(self): fkA = self.kA_char.func.f_x(expr) - return (i.m.val_SI * (o.h.val_SI - i.h.val_SI) + self.kA.val * fkA * ( - (ttd_u - ttd_l) / math.log(ttd_u / ttd_l))) + return (i[0].m.val_SI * (o[0].h.val_SI - i[0].h.val_SI) + self.kA.val * + fkA * ((ttd_u - ttd_l) / math.log(ttd_u / ttd_l))) def bus_func(self): r""" From 5be44992643ee3fa0f6bf6936303f58cbb9ae20f Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sat, 28 Jul 2018 10:35:40 +0200 Subject: [PATCH 21/63] updated unit system settings --- doc/heat_pump.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/heat_pump.rst b/doc/heat_pump.rst index 8e33ce00f..1ad157643 100644 --- a/doc/heat_pump.rst +++ b/doc/heat_pump.rst @@ -35,7 +35,7 @@ Further it is possible to choose a unit system and a value range for mass flow, from tespy import nwk, con, cmp nw = nwk.network(fluids=['water' , 'NH3'], - T='C', p='bar', h='kJ / kg', m='kg / s', + T_unit='C', p_unit='bar', h_unit='kJ / kg', m_unit='kg / s', p_range=[0.1, 100], T_range=[1, 500], h_range=[10, 5000]) We suggest using °C, bar and kJ/kg as units, and set the pressure range from 0.1 bar to 100 bar, temperature range from 1 °C to 500 °C, enthalpy range from 10 kJ/kg to 5000 kJ/kg . From 108f833f82dd9607e764f818ad20a414bc24ea45 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Sat, 28 Jul 2018 10:36:36 +0200 Subject: [PATCH 22/63] renamed lamb_func (heat_exchager_simple) to darcy_func, fixed broken links of heat_exchanger_simple equations --- tespy/components/components.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 76e26e6bb..92be4aafe 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -4769,9 +4769,9 @@ def equations(self, nw): 0 = p_{in} \cdot pr - p_{out} - :func:`tespy.components.components.component.zeta_func` - - :func:`tespy.components.components.component.lamb_func` - - :func:`tespy.components.components.component.hw_func` - - :func:`tespy.components.components.component.kA_func` + - :func:`tespy.components.components.heat_exchanger_simple.darcy_func` + - :func:`tespy.components.components.heat_exchanger_simple.hw_func` + - :func:`tespy.components.components.heat_exchanger_simple.kA_func` """ @@ -4796,7 +4796,7 @@ def equations(self, nw): if self.hydro_group.method == 'HW': func = self.hw_func else: - func = self.lamb_func + func = self.darcy_func vec_res += [func(inl, outl)] @@ -4853,7 +4853,7 @@ def derivatives(self, nw): if self.hydro_group.method == 'HW': func = self.hw_func else: - func = self.lamb_func + func = self.darcy_func deriv = np.zeros((1, num_i + num_o, num_fl + 3)) for i in range(2): @@ -4879,7 +4879,7 @@ def derivatives(self, nw): return np.asarray(mat_deriv) - def lamb_func(self, inl, outl): + def darcy_func(self, inl, outl): r""" equation for pressure drop from darcy friction factor From 98e79a42c19209c756fe7064e976b43f42e94b98 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 30 Jul 2018 10:05:48 +0200 Subject: [PATCH 23/63] improved subsystem __init__ and set_attr logic --- tespy/components/subsystems.py | 66 +++++++++++----------------------- 1 file changed, 20 insertions(+), 46 deletions(-) diff --git a/tespy/components/subsystems.py b/tespy/components/subsystems.py index 9c081a044..3b087c763 100644 --- a/tespy/components/subsystems.py +++ b/tespy/components/subsystems.py @@ -56,61 +56,38 @@ def __init__(self, label, **kwargs): else: self.label = label -# set default values + # set default values for key in self.attr(): self.__dict__.update({key: np.nan}) self.__dict__.update({key + '_set': False}) -# set provided values, check for invalid keys - invalid_keys = np.array([]) - for key in kwargs: - if key not in self.attr(): - invalid_keys = np.append(invalid_keys, key) - else: - if type(kwargs[key]) == float or type(kwargs[key]) == int: - self.__dict__.update({key: kwargs[key]}) - self.__dict__.update({key + '_set': True}) - else: - raise MyComponentError('Specified value does not match ' - 'requirements. Only numbers are ' - 'allowed as parameters.') - -# print invalid keywords - if len(invalid_keys) > 0: - print('\'', invalid_keys, '\' are invalid attributes.', - 'Available attributes for object \'', self, - '\' are:', self.attr()) - self.subsys_init() + self.set_attr(**kwargs) def set_attr(self, **kwargs): """ - set attribute of subsystem + set attributes of subsystem """ - invalid_keys = np.array([]) + # set provided values, check for invalid keys for key in kwargs: - if key not in self.attr(): - invalid_keys = np.append(invalid_keys, key) - else: - if np.isnan(kwargs[key]): - self.__dict__.update({key: np.nan}) - self.__dict__.update({key + '_set': False}) - elif (type(kwargs[key]) == float or - type(kwargs[key]) == np.float64 or - type(kwargs[key]) == int or - key == 'fuel'): + if key in self.attr(): + if (isinstance(kwargs[key], float) or + isinstance(kwargs[key], np.float64) or + isinstance(kwargs[key], int)): + if np.isnan(kwargs[key]): + self.__dict__.update({key: np.nan}) + self.__dict__.update({key + '_set': False}) + else: + self.__dict__.update({key: kwargs[key]}) + self.__dict__.update({key + '_set': True}) + + elif isinstance(kwargs[key], str): self.__dict__.update({key: kwargs[key]}) - self.__dict__.update({key + '_set': True}) - else: - msg = ('Specified value does not match requirements. ' - 'Only numeric parameters are allowed.') - raise TypeError(msg) - - if len(invalid_keys) > 0: - print('\'', invalid_keys, '\' are invalid attributes. ' - 'Available attributes for object \'', - self.__class__.__name__, '\' are:', self.attr()) + else: + msg = ('Component ' + self.label + ' has no attribute ' + + str(key)) + raise ValueError(msg) self.set_comps() self.set_conns() @@ -161,10 +138,7 @@ def subsys_init(self): - create network """ self.create_comps() - self.set_comps() - self.create_conns() - self.set_conns() def create_comps(self): return From d3a31031b2f23fa8d0d273fa1311f70e9b7e1fba Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 30 Jul 2018 13:48:12 +0200 Subject: [PATCH 24/63] added logging levels and redesigend console prints --- tespy/components/components.py | 120 ++++++++++-------- tespy/networks.py | 214 +++++++++++++++++++++++---------- 2 files changed, 220 insertions(+), 114 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 92be4aafe..a3f819412 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -222,9 +222,6 @@ def set_attr(self, **kwargs): str(key)) raise ValueError(msg) -# print('Updated ', self, '.') -# print(self.__dict__) - def get_attr(self, key): """ get the value of a components attribute @@ -238,8 +235,8 @@ def get_attr(self, key): if key in self.__dict__: return self.__dict__[key] else: - print(self.component(), ' \"', self.label, '\" ' - 'has no attribute \"', key, '\"') + print(self.component(), '\"' + self.label + '\" ' + 'has no attribute \"' + key + '\"') return None def comp_init(self, nw): @@ -1108,9 +1105,6 @@ def calc_parameters(self, nw, mode): def print_parameters(self, nw): print('##### ', self.label, ' #####') - if self.eta_s.val > 1: - print('!!!!! Error in parametrisation of the model, ' - 'eta_s higher than 1 !!!!!') print('P = ', self.P.val, 'W; ' 'eta_s = ', self.eta_s.val, '; ' 'pr = ', self.pr.val, '; ' @@ -1518,14 +1512,16 @@ def calc_parameters(self, nw, mode): if (mode == 'pre' and 'eta_s' in self.offdesign) or mode == 'post': self.eta_s.val = ((self.h_os(inl, outl) - inl[0].h.val_SI) / (outl[0].h.val_SI - inl[0].h.val_SI)) - if self.eta_s.val > 1 or self.eta_s.val <= 0: - msg = ('Invalid value for isentropic efficiency.\n' + if self.eta_s.val > 1 or self.eta_s.val <= 0 and nw.comperr: + msg = ('##### ERROR #####\n' + 'Invalid value for isentropic efficiency: ' 'eta_s =', self.eta_s.val) print(msg) nw.errors += [self] if (mode == 'pre' and 'eta_s_char' in self.offdesign): - print('Creating characteristics for component ', self) + if nw.compinfo: + print('Creating characteristics for component ', self) v_opt = (self.i0[0] * (v_mix_ph(self.i0) + v_mix_ph(self.o0)) / 2) H_opt = ((self.o0[1] - self.i0[1]) / @@ -1925,8 +1921,9 @@ def calc_parameters(self, nw, mode): self.eta_s.val = ((self.h_os(inl, outl) - inl[0].h.val_SI) / (outl[0].h.val_SI - inl[0].h.val_SI)) - if self.eta_s.val > 1 or self.eta_s.val <= 0: - msg = ('Invalid value for isentropic efficiency.\n' + if self.eta_s.val > 1 or self.eta_s.val <= 0 and nw.comperr: + msg = ('##### ERROR #####\n' + 'Invalid value for isentropic efficiency: ' 'eta_s =', self.eta_s.val) print(msg) nw.errors += [self] @@ -1953,17 +1950,19 @@ def print_parameters(self, nw): ) vigv = self.char_map.func.get_vigv(n, m, (o1[1] * self.i0[1]) / (i1[1] * self.o0[1])) - if abs(self.vigv.val - vigv) > err: - print('!!!!! Selected inlet guide vane angle is not feasible ' - '!!!!!') + if abs(self.vigv.val - vigv) > err and nw.compwarn: + msg = ('##### WARNING #####\n' + 'Selected inlet guide vane angle is not feasible.') if self.vigv.val > vigv: - print('calculated maximum angle:', vigv, - 'selected:', self.vigv.val) + msg += ('calculated maximum angle: ' + str(vigv) + + ' selected: ' + str(self.vigv.val)) else: - print('calculated minimum angle:', vigv, - 'selected:', self.vigv.val) + msg += ('calculated minimum angle: ' + str(vigv) + + ' selected: ' + str(self.vigv.val)) + print(msg) + else: - print('vigv = ', self.vigv.val) + print('vigv =', self.vigv.val) # %% @@ -2335,8 +2334,9 @@ def calc_parameters(self, nw, mode): if (mode == 'pre' and 'eta_s' in self.offdesign) or mode == 'post': self.eta_s.val = ((outl[0].h.val_SI - inl[0].h.val_SI) / (self.h_os(inl, outl) - inl[0].h.val_SI)) - if self.eta_s.val > 1 or self.eta_s.val <= 0: - msg = ('Invalid value for isentropic efficiency.\n' + if self.eta_s.val > 1 or self.eta_s.val <= 0 and nw.comperr: + msg = ('##### ERROR #####\n' + 'Invalid value for isentropic efficiency: ' 'eta_s =', self.eta_s.val) print(msg) nw.errors += [self] @@ -3671,7 +3671,7 @@ def print_parameters(self, nw): nw.comps.loc[self].o.tolist()) print('##### ', self.label, ' #####') - print('Thermal Input = ', self.ti.val, + print('thermal input = ', self.ti.val, 'lambda = ', self.lamb.val, 'S = ', self.S.val) j = 1 @@ -5157,11 +5157,12 @@ def calc_parameters(self, nw, mode): ttd_l = T_o - t_a if ttd_u < 0 or ttd_l < 0: - msg = ('Invalid value for terminal temperature ' - 'difference.' - 'ttd_u =', ttd_u, - 'ttd_l =', ttd_l) - print(msg) + if nw.compcerr: + msg = ('##### ERROR #####\n' + 'Invalid value for terminal temperature difference ' + 'at component ' + self.label + '.\n' + 'ttd_u = ' + str(ttd_u) + ' ttd_l = ' + str(ttd_l)) + print(msg) nw.errors += [self] if mode == 'post': @@ -5188,11 +5189,16 @@ def calc_parameters(self, nw, mode): # improve this part (for heat exchangers only atm) if self.kA.is_set: expr = inl[0].m.val_SI / self.i0[0] - if (expr > self.kA_char.func.x[-1] or - expr < self.kA_char.func.x[0]): - msg = ('Warning: Expression for characteristics out of bounds:' - ' value is ' + str(expr)) - print(msg) + minval = self.kA_char.func.x[0] + maxval = self.kA_char.func.x[-1] + if expr > maxval or expr < minval: + if nw.compwarn: + msg = ('##### WARNING #####\n' + 'Expression for characteristics out of bounds [' + + str(minval) + ', ' + str(maxval) + '], ' + ' value is ' + str(expr) + ' at ' + + self.label + '.') + print(msg) nw.errors += [self] def print_parameters(self, nw): @@ -6006,11 +6012,13 @@ def calc_parameters(self, nw, mode): self.ttd_l.val = T_o1 - T_i2 if self.ttd_u.val < 0 or self.ttd_l.val < 0: - msg = ('Invalid value for terminal temperature ' - 'difference.' - 'ttd_u =', self.ttd_u.val, - 'ttd_l =', self.ttd_l.val) - print(msg) + if nw.comperr: + msg = ('##### ERROR #####\n' + 'Invalid value for terminal temperature difference ' + 'at component ' + self.label + '.\n' + 'ttd_u = ' + str(self.ttd_u) + ' ' + 'ttd_l = ' + str(self.ttd_l)) + print(msg) nw.errors += [self] if (mode == 'pre' and 'Q' in self.offdesign) or mode == 'post': @@ -6055,28 +6063,34 @@ def calc_parameters(self, nw, mode): # improve this part (for heat exchangers only atm) if self.kA.is_set: expr = inl[0].m.val_SI / self.i10[0] - if (expr > self.kA_char1.func.x[-1] or - expr < self.kA_char1.func.x[0]): - msg = ('Warning: Expression for characteristics out of bounds:' - ' value is ' + str(expr)) - print(msg) + minval = self.kA_char1.func.x[0] + maxval = self.kA_char1.func.x[-1] + if expr > maxval or expr < minval: + if nw.compwarn: + msg = ('##### WARNING #####\n' + 'Expression for characteristics out of bounds [' + + str(minval) + ', ' + str(maxval) + '], ' + ' value is ' + str(expr) + ' at ' + + self.label + '.') + print(msg) nw.errors += [self] expr = inl[1].m.val_SI / self.i20[0] - if (expr > self.kA_char2.func.x[-1] or - expr < self.kA_char2.func.x[0]): - msg = ('Warning: Expression for characteristics out of bounds:' - ' value is ' + str(expr)) - print(msg) + minval = self.kA_char2.func.x[0] + maxval = self.kA_char2.func.x[-1] + if expr > maxval or expr < minval: + if nw.compwarn: + msg = ('##### WARNING #####\n' + 'Expression for characteristics out of bounds [' + + str(minval) + ', ' + str(maxval) + '], ' + ' value is ' + str(expr) + ' at ' + + self.label + '.') + print(msg) nw.errors += [self] def print_parameters(self, nw): print('##### ', self.label, ' #####') - if self.ttd_u.val < 0 and self.kA.is_set: - print('!!!!! ERROR calculating heat exchanger: !!!!!\n' - 'Negative value for TTD at given logarithmic temperature ' - 'difference or kA, result may be wrong.') print('Q = ', self.Q.val, 'W; ' 'ttd_u = ', self.ttd_u.val, 'K; ' 'ttd_l = ', self.ttd_l.val, 'K; ' diff --git a/tespy/networks.py b/tespy/networks.py index 1a92ad73f..946b0589d 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -123,6 +123,10 @@ def __init__(self, fluids, **kwargs): 'T': 'K' } + # printoptions + self.print_level = 'info' + self.set_printoptions() + # standard unit set self.m_unit = self.SI_units['m'] self.p_unit = self.SI_units['p'] @@ -250,13 +254,91 @@ def get_attr(self, key): if key in self.__dict__: return self.__dict__[key] else: - print('No attribute \"', key, '\" available!') + if self.nwkwarn: + print('No attribute \"' + str(key) + '\" available!') return None def attr(self): return ['m_unit', 'p_unit', 'h_unit', 'T_unit', 'p_range', 'h_range', 'T_range'] + def set_printoptions(self, **kwargs): + """r + + sets the printoptions for the calculation. + + :returns: no return value + + **allowed keywords** in kwargs: + + - print_level (*str*) - select the print level: + + - info + - warn + - err + - none + + - compinfo (*bool*) - print infos of components + - compwarn (*bool*) - print warnings of components + - comperr (*bool*) - print errors of components + + - nwkinfo (*bool*) - print info of network + - nwkwarn (*bool*) - print warnings of network + - nwkerr (*bool*) - print errors of network + + - iterinfo (*bool*) - print iterations + + """ + self.print_level = kwargs.get('print_level', self.print_level) + + if self.print_level == 'info': + self.compinfo = True + self.nwkinfo = True + self.iterinfo = True + self.compwarn = True + self.nwkwarn = True + self.comperr = True + self.nwkerr = True + + elif self.print_level == 'warn': + self.compinfo = False + self.nwkinfo = False + self.iterinfo = False + self.compwarn = True + self.nwkwarn = True + self.comperr = True + self.nwkerr = True + + elif self.print_level == 'err': + self.compinfo = False + self.nwkinfo = False + self.iterinfo = False + self.compwarn = False + self.nwkwarn = False + self.comperr = True + self.nwkerr = True + + elif self.print_level == 'none': + self.compinfo = False + self.nwkinfo = False + self.iterinfo = False + self.compwarn = False + self.nwkwarn = False + self.comperr = False + self.nwkerr = False + else: + msg = ('Available print leves are: \'info\', \'warn\', \'err\' and' + '\'none\'.') + raise ValueError(msg) + + self.compinfo = kwargs.get('compinfo', self.compinfo) + self.nwkinfo = kwargs.get('nwkinfo', self.nwkinfo) + self.iterinfo = kwargs.get('iterinfo', self.iterinfo) + self.compwarn = kwargs.get('compwarn', self.compwarn) + self.nwkwarn = kwargs.get('nwkwarn', self.nwkwarn) + self.comperr = kwargs.get('comperr', self.comperr) + self.nwkerr = kwargs.get('nwkerr', self.nwkerr) + def add_subsys(self, *args): """ adds connections to the network, calls check_conns method @@ -405,7 +487,8 @@ def check_network(self): raise hlp.MyNetworkError(msg) self.checked = True - print('Networkcheck successfull.') + if self.nwkinfo: + print('Networkcheck successfull.') def initialise(self): """ @@ -419,10 +502,10 @@ def initialise(self): :returns: no return value """ - - msg = ('Have you adjusted the value ranges for pressure, enthalpy' - ' and temperature according to the specified unit system?') - print(msg) + if self.nwkinfo: + msg = ('Have you adjusted the value ranges for pressure, enthalpy' + ' and temperature according to the specified unit system?') + print(msg) if len(self.fluids) == 0: msg = ('Network has no fluids, please specify a list with fluids ' @@ -740,7 +823,7 @@ def init_val0(self, c, key): if math.isnan(c.get_attr(key).val0): val_s = c.s.initialise_source(c, key) val_t = c.t.initialise_target(c, key) -# print(key, c.s.label, val_s, c.t.label, val_t) + if val_s == 0 and val_t == 0: if key == 'p': c.get_attr(key).val0 = 1e5 @@ -946,7 +1029,8 @@ def solve(self, mode, init_file=None, design_file=None, dec='.', self.initialise() - print('Network initialised.') + if self.nwkinfo: + print('Network initialised.') if init_only: return @@ -957,7 +1041,8 @@ def solve(self, mode, init_file=None, design_file=None, dec='.', self.convergence[2] = np.zeros((len(self.conns), 0)) self.res = np.array([]) - print('Solving network.') + if self.nwkinfo: + print('Solving network.') self.vec_res = [] self.iter = 0 @@ -992,6 +1077,16 @@ def solve(self, mode, init_file=None, design_file=None, dec='.', self.solve_loop() end_time = time.time() + if self.iterinfo: + print('--------+----------+----------+----------+----------+' + '---------') + msg = ('Total iterations: ' + str(self.iter) + ', ' + 'Calculation time: ' + + str(round(end_time - start_time, 1)) + ' s, ' + 'Iterations per second: ' + + str(round(self.iter / (end_time - start_time), 2))) + print(msg) + self.processing('post') if self.parallel: @@ -1011,12 +1106,8 @@ def solve(self, mode, init_file=None, design_file=None, dec='.', c.h.val0 = c.h.val c.fluid.val0 = c.fluid.val.copy() - print('Calculation complete.') - msg = ('Total iterations:' + str(self.iter) + ' - ' - 'Calculation time:' + str(round(end_time - start_time, 1)) + - 's - Iterations per second:' + - str(round(self.iter / (end_time - start_time), 2))) - print(msg) + if self.nwkinfo: + print('Calculation complete.') def solve_loop(self): """ @@ -1024,7 +1115,12 @@ def solve_loop(self): **Improvememts** """ - print('iter\t| residual') + if self.iterinfo: + msg = ('iter\t| residual | massflow | pressure | enthalpy | fluid') + print(msg) + print('--------+----------+----------+----------+----------+' + '---------') + for self.iter in range(self.max_iter): self.convergence[0] = np.column_stack(( @@ -1037,49 +1133,39 @@ def solve_loop(self): self.solve_control() self.res = np.append(self.res, norm(self.vec_res)) - print(self.iter + 1, '\t|', '{:.2e}'.format(norm(self.vec_res))) - - k = 0 - for c in self.conns.index: - self.convergence[0][k][self.iter] = c.m.val_SI - self.convergence[1][k][self.iter] = c.p.val_SI - self.convergence[2][k][self.iter] = c.h.val_SI - k += 1 - - if self.iter > 3: - self.relax = 1 + if self.iterinfo: + msg = (str(self.iter + 1)) + # should this be f(x_i) or the dx_i? + # -> accounts for self.res, too. + msg += '\t| ' + '{:.2e}'.format(norm(self.vec_res)) + msg += ' | ' + '{:.2e}'.format(norm( + self.vec_z[0::self.num_vars])) + msg += ' | ' + '{:.2e}'.format(norm( + self.vec_z[1::self.num_vars])) + msg += ' | ' + '{:.2e}'.format(norm( + self.vec_z[2::self.num_vars])) + ls = [] + for f in range(len(self.fluids)): + ls += self.vec_z[3 + f::self.num_vars].tolist() + + msg += ' | ' + '{:.2e}'.format(norm(ls)) + print(msg) self.iter += 1 # stop calculation after rediculous amount of iterations if self.iter > 3: if self.res[-1] < hlp.err ** (1 / 2): - if self.num_restart > 0: - self.convergence[0] = np.column_stack(( - self.convergence[0], [0] * len(self.conns))) - self.convergence[1] = np.column_stack(( - self.convergence[1], [0] * len(self.conns))) - self.convergence[2] = np.column_stack(( - self.convergence[2], [0] * len(self.conns))) - self.vec_z = np.zeros((self.num_vars * - len(self.conns),)) break if self.iter > 15: if (all(self.res[(self.iter - 3):] >= self.res[-2]) and self.res[-1] >= self.res[-2]): - print('ERROR: Convergence is making no progress, ' - 'calculation stopped.') - if self.num_restart > 0: - self.convergence[0] = np.column_stack(( - self.convergence[0], [0] * len(self.conns))) - self.convergence[1] = np.column_stack(( - self.convergence[1], [0] * len(self.conns))) - self.convergence[2] = np.column_stack(( - self.convergence[2], [0] * len(self.conns))) - self.vec_z = np.zeros((self.num_vars * - len(self.conns),)) - break + if self.nwkwarn: + print('##### WARNING #####\n' + 'Convergence is making no progress, calculation ' + 'stopped.') + break if self.lin_dep: break @@ -1115,8 +1201,9 @@ def solve_control(self): # check for linear dependency if self.lin_dep: - msg = ('error calculating the network:\n' - 'singularity in jacobian matrix, possible reasons are\n' + if self.nwkerr: + msg = ('##### ERROR #####\n' + 'singularity in jacobian matrix, frequent reasons are\n' '-> given Temperature with given pressure in two phase ' 'region, try setting enthalpy instead or ' 'provide accurate starting value for pressure.\n' @@ -1127,7 +1214,7 @@ def solve_control(self): 'combustion chamber, provide small (near to zero, ' 'but not zero) starting value.') print(msg) - return + return # add increment i = 0 @@ -1813,14 +1900,18 @@ def processing(self, mode): self.comps.apply(network.process_components, axis=1, args=(self, mode,)) - if mode == 'pre': - print('Preprocessing done.') - else: - print('Postprocessing.') + if self.nwkinfo: + if mode == 'pre': + print('Preprocessing done.') + else: + print('Postprocessing.') + + if mode == 'post': # clear fluid property memory hlp.memorise.del_memory(self.fluids) self.process_busses() - print('Done.') + if self.nwkinfo: + print('Done.') def process_busses(self): """ @@ -1863,11 +1954,12 @@ def print_results(self): Q_diss = [x.P for x in self.busses if x.label == 'Q_diss'] if len(P_res) != 0 and len(Q_diss) != 0: - print('process key figures') - print('eta_th = ' + str(1 - sum(Q_diss) / - (sum(P_res) + sum(Q_diss)))) - print('eps_hp = ' + str(abs(sum(Q_diss)) / sum(P_res))) - print('eps_cm = ' + str(abs(sum(Q_diss)) / sum(P_res) - 1)) + if self.nwkinfo: + print('process key figures') + print('eta_th = ' + str(1 - sum(Q_diss) / + (sum(P_res) + sum(Q_diss)))) + print('eps_hp = ' + str(abs(sum(Q_diss)) / sum(P_res))) + print('eps_cm = ' + str(abs(sum(Q_diss)) / sum(P_res) - 1)) msg = 'Do you want to print the components parammeters?' if hlp.query_yes_no(msg): From fa3caa0bab0c435e232461fe9fe2e681c2754a18 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 30 Jul 2018 13:50:41 +0200 Subject: [PATCH 25/63] fixed typo --- tespy/components/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index a3f819412..0cc4d06c0 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -5157,7 +5157,7 @@ def calc_parameters(self, nw, mode): ttd_l = T_o - t_a if ttd_u < 0 or ttd_l < 0: - if nw.compcerr: + if nw.comperr: msg = ('##### ERROR #####\n' 'Invalid value for terminal temperature difference ' 'at component ' + self.label + '.\n' From 9b710b431434afe65a025e6d6d0af29639fde12f Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 30 Jul 2018 14:13:07 +0200 Subject: [PATCH 26/63] fixed typo --- tespy/components/components.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 0cc4d06c0..424e34421 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -6016,8 +6016,8 @@ def calc_parameters(self, nw, mode): msg = ('##### ERROR #####\n' 'Invalid value for terminal temperature difference ' 'at component ' + self.label + '.\n' - 'ttd_u = ' + str(self.ttd_u) + ' ' - 'ttd_l = ' + str(self.ttd_l)) + 'ttd_u = ' + str(self.ttd_u.val) + ' ' + 'ttd_l = ' + str(self.ttd_l.val)) print(msg) nw.errors += [self] From 1294999d8b3278980d159315dfece2786c50dc5f Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 30 Jul 2018 14:23:02 +0200 Subject: [PATCH 27/63] removed residuals from previous merge --- tespy/networks.py | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index cd6f71058..a58003c4a 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -511,7 +511,6 @@ def initialise(self): msg = ('Network has no fluids, please specify a list with fluids ' 'on network creation.') raise hlp.MyNetworkError(msg) - self.init_components() # build the dataframe for components if self.mode == 'offdesign': self.init_offdesign() # characteristics for offdesign @@ -569,41 +568,6 @@ def init_components(self, comps): str(duplicates)) raise hlp.MyNetworkError(msg) - def initialise(self): - """ - initilialises the network - - - component initlialisation - - fluid propagation on all connections - - initilialise fluid properties - - initialisiation from .csv-files - - switch components to offdesign mode for offedesign calculation - - :returns: no return value - """ - - msg = ('Have you adjusted the value ranges for pressure, enthalpy' - ' and temperature according to the specified unit system?') - print(msg) - - if len(self.fluids) == 0: - msg = ('Network has no fluids, please specify a list with fluids ' - 'on network creation.') - raise hlp.MyNetworkError(msg) - - if self.mode == 'offdesign': - self.init_offdesign() # characteristics for offdesign - - self.init_fluids() # start standard fluid initialisation - self.init_properties() # start standard property initialisation - - if self.mode == 'offdesign' and self.design_file is None: - msg = ('Please provide \'design_file\' for every offdesign ' - 'calculation.') - raise hlp.MyNetworkError(msg) # must provide design_file - else: - self.init_csv() # initialisation from csv - def init_fluids(self): """ initialises the fluid vector on every connection of the network From 833b96d9cc7845069c3150ea99234611ee2440ff Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 30 Jul 2018 14:31:03 +0200 Subject: [PATCH 28/63] adjusted documentations of network printoptions --- tespy/networks.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index a58003c4a..4fce8e403 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -273,10 +273,10 @@ def set_printoptions(self, **kwargs): - print_level (*str*) - select the print level: - - info - - warn - - err - - none + - info: all printouts + - warn: errors and warnings + - err: errors only + - none: no printouts - compinfo (*bool*) - print infos of components - compwarn (*bool*) - print warnings of components From 052c9f8ddddec876902aed8c09137fefd6611990 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 31 Jul 2018 09:19:52 +0200 Subject: [PATCH 29/63] fixed typo in darcy_func --- tespy/components/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 2e2f2e855..ec790411f 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -4606,7 +4606,7 @@ def derivatives(self, nw): return np.asarray(mat_deriv) - def darcy_func(self, inl, outl): + def darcy_func(self): r""" equation for pressure drop from darcy friction factor From 082051c5c78e01823deb85c5a4d784fdfa513d81 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 31 Jul 2018 09:20:26 +0200 Subject: [PATCH 30/63] changed printout order for singularity in calculation --- tespy/networks.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index 4fce8e403..dd1d03a29 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -72,7 +72,6 @@ class network: def __init__(self, fluids, **kwargs): self.checked = False - self.errors = [] self.conns = pd.DataFrame(columns=['s', 's_id', 't', 't_id']) self.fluids = sorted(fluids) @@ -502,6 +501,7 @@ def initialise(self): :returns: no return value """ + self.errors = [] if self.nwkinfo: msg = ('Have you adjusted the value ranges for pressure, enthalpy' ' and temperature according to the specified unit system?') @@ -1087,6 +1087,23 @@ def solve(self, mode, init_file=None, design_file=None, dec='.', str(round(self.iter / (end_time - start_time), 2))) print(msg) + if self.lin_dep: + if self.nwkerr: + msg = ('##### ERROR #####\n' + 'singularity in jacobian matrix, frequent reasons are\n' + '-> given Temperature with given pressure in two phase ' + 'region, try setting enthalpy instead or ' + 'provide accurate starting value for pressure.\n' + '-> given logarithmic temperature differences ' + 'or kA-values for heat exchangers, \n' + '-> support better starting values.\n' + '-> bad starting value for fuel mass flow of ' + 'combustion chamber, provide small (near to zero, ' + 'but not zero) starting value.') + print(msg) + + return + self.processing('post') if self.parallel: @@ -1194,19 +1211,6 @@ def solve_control(self): # check for linear dependency if self.lin_dep: - if self.nwkerr: - msg = ('##### ERROR #####\n' - 'singularity in jacobian matrix, frequent reasons are\n' - '-> given Temperature with given pressure in two phase ' - 'region, try setting enthalpy instead or ' - 'provide accurate starting value for pressure.\n' - '-> given logarithmic temperature differences ' - 'or kA-values for heat exchangers, \n' - '-> support better starting values.\n' - '-> bad starting value for fuel mass flow of ' - 'combustion chamber, provide small (near to zero, ' - 'but not zero) starting value.') - print(msg) return # add increment From 773491dd60b28d80cfe2de054cd0f743c680e2d0 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 31 Jul 2018 13:33:56 +0200 Subject: [PATCH 31/63] fixed TESPy components section --- doc/using_tespy.rst | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/using_tespy.rst b/doc/using_tespy.rst index 7df679304..ea300a7de 100644 --- a/doc/using_tespy.rst +++ b/doc/using_tespy.rst @@ -445,10 +445,10 @@ There are two ways for specifying the customizable characteristic line of a comp # heat exchanger analogously he = cmp.heat_exchanger('evaporator') - turb.set_attr(kA_char1='EVA_HOT') - turb.set_attr(kA_char2='EVA_COLD') + he.set_attr(kA_char1='EVA_HOT') + he.set_attr(kA_char2='EVA_COLD') -All of these components come default characteristic lines, which can be found in the components documentation. +All of these components are supplied with default characteristic lines, which can be found in the :py:class:`documentation `. Custom components ----------------- @@ -511,17 +511,18 @@ The equations contain the information on the changes to the fluid properties wit .. math:: 0 = \dot{m}_{in} - \dot{m}_{out} + 0 = \dot{p}_{in} - \dot{p}_{out} - \Delta p -The equations method requires a tespy.networks.network object as parameter. You can aquire a list of the ingoing and outgoing equations by the following command: +The connections connected to your component are available as a list in :code:`self.inl` and :code:`self.outl` respectively. .. code:: python - def inlets(self): - if self.num_in_set: - return ['in' + str(i + 1) for i in range(self.num_in)] - else: - self.set_attr(num_in=2) - return self.inlets() + def equations(self): + + vec_res = [] + + vec_res += [self.inl[0].m.val_SI - self.outl[0].m.val_SI] + vec_res += [self.inl[0].p.val_SI - self.outl[0].p.val_SI - self.dp()] The equations are added to a list one after another, which will be returned at the end. @@ -530,7 +531,7 @@ Derivatives You need to calculate the partial derivatives of the equations to all variables of the network. This means, that you have to calculate the partial derivatives to mass flow, pressure, enthalpy and all fluids in the fluid vector on each incomming or outgoing connection of the component. -Add all derivatives to a list (in the same order as the equations) and return the list as numpy array (:code:`np.asarray(list)`). The derivatives can be calculated analytically or numerically by using the inbuilt function :code:`ddx_func(self, inlets, outlets, func, dx, pos)`. +Add all derivatives to a list (in the same order as the equations) and return the list as numpy array (:code:`np.asarray(list)`). The derivatives can be calculated analytically or numerically by using the inbuilt function :code:`ddx_func(self, func, dx, pos)`. - :code:`inlets` and :code:`outlets` are a list of the connections at the inlets and the outlets, - :code:`func` is the function you want to calculate the derivatives for, From 37cff065c749a2040e81debab33d33bf7bf612d7 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 1 Aug 2018 14:04:05 +0200 Subject: [PATCH 32/63] cubic interpolation for component characteristics --- tespy/components/characteristics.py | 6 +++--- tespy/components/components.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tespy/components/characteristics.py b/tespy/components/characteristics.py index 8e33e8685..1b72251df 100644 --- a/tespy/components/characteristics.py +++ b/tespy/components/characteristics.py @@ -347,15 +347,15 @@ def __init__(self, **kwargs): if self.y is None: self.y = self.default(method)[1] - self.char = interp1d(self.x, self.y, kind='linear', bounds_error=True) + self.char = interp1d(self.x, self.y, kind='cubic', bounds_error=True) def default(self, key): x = {} y = {} - x['default'] = np.array([0, 1, 2]) - y['default'] = np.array([1, 1, 1]) + x['default'] = np.array([0, 1, 2, 3]) + y['default'] = np.array([1, 1, 1, 1]) return x[key], y[key] diff --git a/tespy/components/components.py b/tespy/components/components.py index ec790411f..e9dfe375a 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -1139,8 +1139,8 @@ def attr(self): def attr_prop(self): return {'P': dc_cp(), 'eta_s': dc_cp(), 'pr': dc_cp(), 'Sirr': dc_cp(), - 'eta_s_char': dc_cc(x=[0, 1, 2], y=[1, 1, 1]), - 'flow_char': dc_cc(x=[0, 1, 2], y=[1, 1, 1])} + 'eta_s_char': dc_cc(x=[0, 1, 2, 3], y=[1, 1, 1, 1]), + 'flow_char': dc_cc(x=[0, 1, 2, 3], y=[1, 1, 1, 1])} def additional_equations(self): r""" @@ -1901,7 +1901,7 @@ def attr(self): def attr_prop(self): return {'P': dc_cp(), 'eta_s': dc_cp(), 'pr': dc_cp(), 'Sirr': dc_cp(), - 'eta_s_char': dc_cc(method='TRAUPEL', param='dh_s'), + 'eta_s_char': dc_cc(method='GENERIC', param='m'), 'cone': dc_cc(method='default')} def default_offdesign(self): From 7be806adb34ceae0bc274f0d9d7dba38a191d22b Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 1 Aug 2018 14:04:41 +0200 Subject: [PATCH 33/63] fixed iterinfo printout for linear dependency at first iteration --- tespy/networks.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index dd1d03a29..13d117f85 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -1148,17 +1148,23 @@ def solve_loop(self): # should this be f(x_i) or the dx_i? # -> accounts for self.res, too. msg += '\t| ' + '{:.2e}'.format(norm(self.vec_res)) - msg += ' | ' + '{:.2e}'.format(norm( - self.vec_z[0::self.num_vars])) - msg += ' | ' + '{:.2e}'.format(norm( - self.vec_z[1::self.num_vars])) - msg += ' | ' + '{:.2e}'.format(norm( - self.vec_z[2::self.num_vars])) - ls = [] - for f in range(len(self.fluids)): - ls += self.vec_z[3 + f::self.num_vars].tolist() - - msg += ' | ' + '{:.2e}'.format(norm(ls)) + if not self.lin_dep: + msg += ' | ' + '{:.2e}'.format(norm( + self.vec_z[0::self.num_vars])) + msg += ' | ' + '{:.2e}'.format(norm( + self.vec_z[1::self.num_vars])) + msg += ' | ' + '{:.2e}'.format(norm( + self.vec_z[2::self.num_vars])) + ls = [] + for f in range(len(self.fluids)): + ls += self.vec_z[3 + f::self.num_vars].tolist() + + msg += ' | ' + '{:.2e}'.format(norm(ls)) + else: + msg += ' | nan' + msg += ' | nan' + msg += ' | nan' + msg += ' | nan' print(msg) self.iter += 1 From ea0959cb7bcce4f8acfe46a9298cfd05718c3891 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Fri, 10 Aug 2018 14:22:19 +0200 Subject: [PATCH 34/63] moved the examples to the tespy_examples repository at fwitte/tespy_examples --- examples/chp.py | 188 ------------------ examples/combustion_chamber.py | 63 ------ examples/combustion_chamber_stoich.py | 62 ------ examples/simple_clausius_rankine.py | 97 --------- .../condenser_eva_results.csv | 0 {examples/tutorial => tutorial}/step_1.py | 0 {examples/tutorial => tutorial}/step_2.py | 0 {examples/tutorial => tutorial}/step_3.py | 0 8 files changed, 410 deletions(-) delete mode 100644 examples/chp.py delete mode 100644 examples/combustion_chamber.py delete mode 100644 examples/combustion_chamber_stoich.py delete mode 100644 examples/simple_clausius_rankine.py rename {examples/tutorial => tutorial}/condenser_eva_results.csv (100%) rename {examples/tutorial => tutorial}/step_1.py (100%) rename {examples/tutorial => tutorial}/step_2.py (100%) rename {examples/tutorial => tutorial}/step_3.py (100%) diff --git a/examples/chp.py b/examples/chp.py deleted file mode 100644 index a43ee8d72..000000000 --- a/examples/chp.py +++ /dev/null @@ -1,188 +0,0 @@ -# -*- coding: utf-8 -*- - -from tespy import con, cmp, nwk -import matplotlib.pyplot as plt -import pandas as pd - -# %% network - -fluids = ['water'] - -nw = nwk.network(fluids=fluids, p_unit='bar', T_unit='C', h_unit='kJ / kg', - p_range=[0.01, 110], T_range=[20, 800], h_range=[15, 5000]) - -# %% components - -# turbine part -vessel_turb = cmp.vessel(label='vessel_turb') -turbine_hp = cmp.turbine(label='turbine_hp') -split = cmp.splitter(label='splitter1') -turbine_lp = cmp.turbine(label='turbine_lp') - -# condenser and preheater -condenser = cmp.condenser(label='condenser') -preheater = cmp.condenser(label='preheater') -vessel = cmp.vessel(label='vessel1') -merge = cmp.merge(label='merge1') - -# feed water -pump = cmp.pump(label='pump') -steam_generator = cmp.heat_exchanger_simple(label='steam generator') - -# sources and sinks -source = cmp.source(label='source') -sink = cmp.sink(label='sink') - -# for cooling water -source_cw = cmp.source(label='source_cw') -sink_cw = cmp.sink(label='sink_cw') - -# %% connections - -# turbine part -fs_in = con.connection(source, 'out1', vessel_turb, 'in1') -fs = con.connection(vessel_turb, 'out1', turbine_hp, 'in1') -ext = con.connection(turbine_hp, 'out1', split, 'in1') -ext_pre = con.connection(split, 'out1', preheater, 'in1') -ext_turb = con.connection(split, 'out2', turbine_lp, 'in1') -nw.add_conns(fs_in, fs, ext, ext_pre, ext_turb) - -# preheater and condenser -ext_cond = con.connection(preheater, 'out1', vessel, 'in1') -cond_ws = con.connection(vessel, 'out1', merge, 'in2') -turb_ws = con.connection(turbine_lp, 'out1', merge, 'in1') -ws = con.connection(merge, 'out1', condenser, 'in1') -nw.add_conns(ext_cond, cond_ws, turb_ws, ws) - -# feed water -cond = con.connection(condenser, 'out1', pump, 'in1') -fw_c = con.connection(pump, 'out1', preheater, 'in2') -fw_w = con.connection(preheater, 'out2', steam_generator, 'in1') -fs_out = con.connection(steam_generator, 'out1', sink, 'in1') -nw.add_conns(cond, fw_c, fw_w, fs_out) - -# cooling water -cw_in = con.connection(source_cw, 'out1', condenser, 'in2') -cw_out = con.connection(condenser, 'out2', sink_cw, 'in1') -nw.add_conns(cw_in, cw_out) - -# %% busses - -# power bus -power_bus = con.bus('power') -power_bus.add_comps([turbine_hp, -1], [turbine_lp, -1], [pump, -1]) - -# heating bus -heat_bus = con.bus('heat') -heat_bus.add_comps([condenser, -1]) - -nw.add_busses(power_bus, heat_bus) - -# %% parametrization of components - -vessel_turb.set_attr(mode='man') -turbine_hp.set_attr(eta_s=0.9) -turbine_lp.set_attr(eta_s=0.9) - -condenser.set_attr(pr1=0.99, pr2=0.99, ttd_u=12) -preheater.set_attr(pr1=0.99, pr2=0.99, ttd_u=5) -vessel.set_attr(mode='man') - -pump.set_attr(eta_s=0.8) -steam_generator.set_attr(pr=0.95, mode='man') - -# %% parametrization of connections - -# fresh steam properties -fs_in.set_attr(p=100, T=550, fluid={'water': 1}) - -# pressure after turbine inlet vessel -fs.set_attr(p=100, design=['p']) - -# pressure extraction steam -ext.set_attr(p=10, design=['p']) - -# staring value for warm feed water -fw_w.set_attr(h0=310) - -# closing the cycle: fluid properties at sink must be identical to fluid -# properties at the source -fs_out.set_attr(p=con.ref(fs_in, 1, 0), h=con.ref(fs_in, 1, 0)) - -# cooling water inlet -cw_in.set_attr(T=60, p=10, fluid={'water': 1}) - -# setting key parameters: -# Power of the plant -power_bus.set_attr(P=5e6) -# -cw_out.set_attr(T=110) - - -# %% solving - -mode = 'design' - -nw.solve(init_file=None, mode=mode) -nw.print_results() -nw.save('chp') - -file = 'chp/results.csv' -mode = 'offdesign' - -# representation of part loads -P_range = [5.25e6, 5e6, 4.5e6, 4e6, 3.5e6, 3e6] - -# temperatures for the heating system -T_range = [120, 110, 100, 95, 90, 85, 80, 77.5, 75, 72.5, 70] - -df = pd.DataFrame(columns=P_range) - -# iterate over temperatures -for T in T_range: - cw_out.set_attr(T=T) - Q = [] - # iterate over power plant power output - for P in P_range: - print(T, P) - power_bus.set_attr(P=P) - - # use an initialisation file with parameters similar to next - # calculation - if T == T_range[0]: - nw.solve(init_file=file, design_file=file, mode=mode) - else: - nw.solve(init_file='chp_' + str(P/1e6) + '/results.csv', - design_file=file, mode=mode) - - nw.save('chp_' + str(P/1e6)) - Q += [heat_bus.P] - - df.loc[T] = Q - -df.to_csv('chp.csv') -# plotting -df = pd.read_csv('chp.csv', index_col=0) - -colors = ['#00395b', '#74adc1', '#b54036', '#ec6707', - '#bfbfbf', '#999999', '#010101'] - -fig, ax = plt.subplots() - -i = 0 -for T in T_range: - if T % 10 == 0: - plt.plot(df.loc[T], P_range, '-x', Color=colors[i], - label='$T_{VL}$ = ' + str(T) + ' °C', markersize=7, linewidth=2) - i += 1 - -ax.set_ylabel('$P$ in W') -ax.set_xlabel('$\dot{Q}$ in W') -plt.title('P-Q diagram for CHP with backpressure steam turbine') -plt.legend(loc='lower left') -ax.set_ylim([0, 6e6]) -ax.set_xlim([0, 14e6]) - -plt.show() - -fig.savefig('PQ_diagram.svg') diff --git a/examples/combustion_chamber.py b/examples/combustion_chamber.py deleted file mode 100644 index 68aab6048..000000000 --- a/examples/combustion_chamber.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Aug 4 10:37:36 2017 - -@author: witte -""" - -from tespy import con, cmp, nwk - -# %% network - -# define full fluid list for the network's variable space -fluid_list = ['Ar', 'N2', 'O2', 'CO2', 'CH4', 'H2O'] -# define unit systems and fluid property ranges -nw = nwk.network(fluids=fluid_list, p_unit='bar', T_unit='C', - p_range=[0.5, 10], T_range=[10, 1200]) - -# %% components - -# sinks & sources -amb = cmp.source('ambient') -sf = cmp.source('fuel') -fg = cmp.sink('flue gas outlet') - -# combustion chamber -comb = cmp.combustion_chamber(label='combustion chamber') - -# %% connections - -amb_comb = con.connection(amb, 'out1', comb, 'in1') -sf_comb = con.connection(sf, 'out1', comb, 'in2') -comb_fg = con.connection(comb, 'out1', fg, 'in1') - -nw.add_conns(sf_comb, amb_comb, comb_fg) - -# %% component parameters - -# set combustion chamber fuel, air to stoichometric air ratio and thermal input -comb.set_attr(fuel='CH4', lamb=3, ti=20000) - -# %% connection parameters - -# air from abient (ambient pressure and temperature), air composition must be -# stated component wise. -amb_comb.set_attr(p=1, T=20, - fluid={'Ar': 0.0129, 'N2': 0.7553, 'H2O': 0, - 'CH4': 0, 'CO2': 0.0004, 'O2': 0.2314}) - -# fuel, pressure must not be stated, as pressure is the same at all inlets and -# outlets of the combustion chamber -sf_comb.set_attr(T=25, - fluid={'CO2': 0.04, 'Ar': 0, 'N2': 0, - 'O2': 0, 'H2O': 0, 'CH4': 0.96}) - -# state desired flue gas temperature -comb_fg.set_attr() - -# %% solving - -mode = 'design' -nw.solve(mode=mode) -nw.print_results() -nw.save('combustion') diff --git a/examples/combustion_chamber_stoich.py b/examples/combustion_chamber_stoich.py deleted file mode 100644 index 819b04c69..000000000 --- a/examples/combustion_chamber_stoich.py +++ /dev/null @@ -1,62 +0,0 @@ - -from tespy import con, cmp, nwk, hlp - -# %% network - -# define full fluid list for the network's variable space -fluid_list = ['TESPy::myAir', 'TESPy::myFuel', 'TESPy::myFuel_fg'] -# define unit systems and fluid property ranges -nw = nwk.network(fluids=fluid_list, p_unit='bar', T_unit='C', - p_range=[0.001, 10], T_range=[10, 2000]) - -# %% components - -# sinks & sources -amb = cmp.source('ambient') -sf = cmp.source('fuel') -fg = cmp.sink('flue gas outlet') - -# combustion chamber - -comb = cmp.combustion_chamber_stoich('stoichiometric combustion chamber') - -# %% connections - -amb_comb = con.connection(amb, 'out1', comb, 'in1') -sf_comb = con.connection(sf, 'out1', comb, 'in2') -comb_fg = con.connection(comb, 'out1', fg, 'in1') - -nw.add_conns(sf_comb, amb_comb, comb_fg) - -# %% component parameters - -comb.set_attr(fuel={'CH4': 0.96, 'CO2': 0.04}, - air={'Ar': 0.0129, 'N2': 0.7553, 'H2O': 0, - 'CH4': 0, 'CO2': 0.0004, 'O2': 0.2314}, - fuel_alias='myFuel', air_alias='myAir', - lamb=3, ti=20000) -# %% connection parameters - -# air from abient (ambient pressure and temperature), air composition must be -# stated component wise. -amb_comb.set_attr(T=20, p=1, - fluid={'TESPy::myAir': 1, 'TESPy::myFuel': 0, - 'TESPy::myFuel_fg': 0}) - -# fuel, pressure must not be stated, as pressure is the same at all inlets and -# outlets of the combustion chamber -sf_comb.set_attr(T=25, - fluid={'TESPy::myAir': 0, 'TESPy::myFuel': 1, - 'TESPy::myFuel_fg': 0}) - -# %% solving - -mode = 'design' -nw.solve(mode=mode) -nw.print_results() - -comb.set_attr(path='./LUT') - -mode = 'design' -nw.solve(mode=mode) -nw.print_results() diff --git a/examples/simple_clausius_rankine.py b/examples/simple_clausius_rankine.py deleted file mode 100644 index da262e354..000000000 --- a/examples/simple_clausius_rankine.py +++ /dev/null @@ -1,97 +0,0 @@ -# -*- coding: utf-8 -*- -""" -@author: witte -""" -from tespy import con, nwk, cmp, hlp -import math - -# %% network - -fluids = ['water'] - -nw = nwk.network(fluids=fluids) -nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg', - p_range=[0.01, 150], T_range=[5, 800], h_range=[10, 5000]) - -# %% components - -# main components -turbine = cmp.turbine('turbine') -condenser = cmp.condenser('condenser') -pump = cmp.pump('pump') -steam_generator = cmp.heat_exchanger_simple('steam generator') -source = cmp.source('cycle opener') -sink = cmp.sink('cycle closer') - -# cooling water -source_cw = cmp.source('cooling water inlet') -sink_cw = cmp.sink('cooling water outlet') - -# %% connections - -# main cycle -fs_in = con.connection(source, 'out1', turbine, 'in1') -ws = con.connection(turbine, 'out1', condenser, 'in1') -cond = con.connection(condenser, 'out1', pump, 'in1') -fw = con.connection(pump, 'out1', steam_generator, 'in1') -fs_out = con.connection(steam_generator, 'out1', sink, 'in1') -nw.add_conns(fs_in, ws, cond, fw, fs_out) - -# cooling water -cw_in = con.connection(source_cw, 'out1', condenser, 'in2') -cw_out = con.connection(condenser, 'out2', sink_cw, 'in1') -nw.add_conns(cw_in, cw_out) - -# %% busses - -power = con.bus('total output power') -power.add_comps([turbine, 1], [pump, 1]) -nw.add_busses(power) - -# %% parametrization of components - -turbine.set_attr(eta_s=0.9) -condenser.set_attr(pr1=0.98, pr2=0.98, ttd_u=5) -pump.set_attr(eta_s=0.8) -steam_generator.set_attr(pr=0.95, mode='man') - -# %% parametrization of connections - -# offdesign calculation: use parameter design for auto deactivation -# turbine inlet pressure is deriven by stodolas law, outlet pressure by -# characteristic of condenser -fs_in.set_attr(p=100, T=500, fluid={'water': 1}, design=['p']) - -# closing the cycle: fluid properties at sink must be identical to fluid -# properties at the source -fs_out.set_attr(p=con.ref(fs_in, 1, 0), h=con.ref(fs_in, 1, 0)) - -cw_in.set_attr(T=20, p=5, fluid={'water': 1}) -cw_out.set_attr(T=30) - -# total output power as input parameter -power.set_attr(P=-10e6) - -# %% solving - -mode = 'design' - -file = 'cr' - -# solve the network, print the results to prompt and save -nw.solve(mode=mode) -nw.print_results() -nw.save(file) - -# change to offdesign mode -mode = 'offdesign' - -# reset power input -power.set_attr(P=-9e6) - -# the design file holds the information on the design case -# initialisation from previously design process -nw.solve(mode=mode, design_file=file + '/results.csv', - init_file=file + '/results.csv') -nw.print_results() -nw.save('cr_OD') diff --git a/examples/tutorial/condenser_eva_results.csv b/tutorial/condenser_eva_results.csv similarity index 100% rename from examples/tutorial/condenser_eva_results.csv rename to tutorial/condenser_eva_results.csv diff --git a/examples/tutorial/step_1.py b/tutorial/step_1.py similarity index 100% rename from examples/tutorial/step_1.py rename to tutorial/step_1.py diff --git a/examples/tutorial/step_2.py b/tutorial/step_2.py similarity index 100% rename from examples/tutorial/step_2.py rename to tutorial/step_2.py diff --git a/examples/tutorial/step_3.py b/tutorial/step_3.py similarity index 100% rename from examples/tutorial/step_3.py rename to tutorial/step_3.py From 5c8382458f0cef233afa15a0a5fef2491946d9a4 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Fri, 10 Aug 2018 14:27:59 +0200 Subject: [PATCH 35/63] fixed errors in README --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 4c93a610e..d2f7107e1 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ To get the latest news visit and follow our `website `_. Installing TESPy ================ -If you have a working Python3 environment, use pypi to install the latest tespy version: Clone the github repository and then run: +If you have a working Python3 environment, use pypi to install the latest tespy version: .. code:: bash @@ -31,7 +31,7 @@ If you want to use the latest features, you might want to install the **develope Examples ======== -For a short introduction on how TESPy works and how you can use it, we provid a short `Introduction `_.You can download the python scripts of the example plants from the tespy.examples folder. +For a short introduction on how TESPy works and how you can use it, we provide some `basic examples and tutorials `_, go and check them out. You can download the python scripts of all example plants from the `tespy_examples repository `_. License ======= From ff7b17de06a42698eba74c90b63dd981ca30471a Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 13 Aug 2018 13:49:23 +0200 Subject: [PATCH 36/63] reorganised the tutorials/examples section and added some explanation to the examples --- doc/getting_started.rst | 38 +++++++++++++++++++ doc/getting_started/basic.rst | 38 +++++++++++++++++++ doc/getting_started/chp.rst | 18 +++++++++ doc/getting_started/district_heating.rst | 35 +++++++++++++++++ doc/getting_started/heat_pump.rst | 23 +++++++++++ .../tutorial_combustion_chamber.rst} | 4 +- .../tutorial_heat_pump.rst} | 2 +- doc/tutorial.rst | 14 ------- 8 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 doc/getting_started.rst create mode 100644 doc/getting_started/basic.rst create mode 100644 doc/getting_started/chp.rst create mode 100644 doc/getting_started/district_heating.rst create mode 100644 doc/getting_started/heat_pump.rst rename doc/{combustion_chamber.rst => getting_started/tutorial_combustion_chamber.rst} (96%) rename doc/{heat_pump.rst => getting_started/tutorial_heat_pump.rst} (99%) delete mode 100644 doc/tutorial.rst diff --git a/doc/getting_started.rst b/doc/getting_started.rst new file mode 100644 index 000000000..c2510397c --- /dev/null +++ b/doc/getting_started.rst @@ -0,0 +1,38 @@ +~~~~~~~~~~~~~~~ +Getting started +~~~~~~~~~~~~~~~ + +Examples +======== + +In the example section we provide a variety of TESPy applications: + +* a very basic model of the clausius rankine process, +* the calculation of backpressure lines of a chp at different loads and feed flow temperature levels, +* modeling approach for a district heating system with various consumers and system infrastructure and +* the COP of a heat pump dependent on load and ambient temperature. You can find all examples in the `tespy examples github repository `_. + +.. toctree:: + :maxdepth: 1 + + getting_started/basic + getting_started/chp + getting_started/district_heating + getting_started/heat_pump + +.. _tespy_tutorial_label: + +Tutorials +========= + +We provide two different tutorials for you to better understand how to work with TESPy. +You will learn how to create basic models and get the idea of designing a plant and simulating the offdesign behaviour in the heat pump tutorial (this is the bases for the COP calculation of the heat pump in the examples). +On top of that, we created a tutorial for the usage of the combustion chamber: It is an important component for thermal power plants while beeing a source for many errors in the calculation. + +Contents: + +.. toctree:: + :maxdepth: 1 + + getting_started/tutorial_heat_pump + getting_started/tutorial_combustion_chamber diff --git a/doc/getting_started/basic.rst b/doc/getting_started/basic.rst new file mode 100644 index 000000000..09fb5e59f --- /dev/null +++ b/doc/getting_started/basic.rst @@ -0,0 +1,38 @@ +.. _basic_example_label: + +~~~~~~~~~~~~~~~~~~~~~~ +Clausius rankine cycle +~~~~~~~~~~~~~~~~~~~~~~ + +This example provides a model for a basic clausius rankine cycle. The process flow diagram is shown in the image below, the source code can be found at the :download:`tespy_examples repository `. + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/basic/flow_diagram.svg + :align: center + Figure: Topology of the basic clausius rankine cycle. + +The basic clausius rankine cycle is built up of a steam turbine, a condenser, the feed water pump and the steam generator. The ideal process' isentropic efficiencies of the steam turbine and the pump are at a value of 100 %, and pressure losses in the condenser and the steam generator are non-existent, which would result in the thermal efficiency beeing equal to the carnot efficiency. For this example realistic figures have been chosen. +After the plant design an offdesign calculation with 90 % rated power is performed. The inline-comments give you hints which and why offdesign parameters have been choosen. Additionally we added a calculation of thermal efficiency with the standard method as well as with the entropy method: + +.. math:: + + \eta_{th} = \frac{|\sum P|}{\sum \dot{Q}_{in}} + + \eta_{th} = \eta_c - \sum_i \Delta \eta_i + + T_{m,in} = \frac{\sum \dot{Q}_{in}}{\sum \dot{S}_{in}} + + T_{m,out} = \frac{\sum \dot{Q}_{out}}{\sum \dot{S}_{out}} + + \eta_c = 1 - \frac{T_{m,out}}{T_{m,in}} + + \Delta \eta_i = \frac{T_{m,out} \cdot \dot{S}_{irr,i}} + {\sum \dot{Q}_{in}} + + \text{indices: in = input, out = output} + + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/basic/efficiency.svg + :align: center + Figure: Efficiency of the basic clausius rankine cycle in design case. + +For a good start you can try to modify or exchange parameters. E. g. adjust the value of the upper terminal temperature difference at the condenser, or replace this parameter with a pressure at the turbine's outlet. In oder to get more familiar with how TESPy works you could try to insert more components, maybe add an extraction of the steam turbine for preheating the feed water. It is strongly recommended to add new components bit per bit, troubleshooting is much easier this way. diff --git a/doc/getting_started/chp.rst b/doc/getting_started/chp.rst new file mode 100644 index 000000000..7b2c25748 --- /dev/null +++ b/doc/getting_started/chp.rst @@ -0,0 +1,18 @@ +.. _chp_example_label: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +CHP with backpressure turbine +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We have set up a simple combined heat and power unit for this example. A backpressure steam turbine is operated with steam extraction for preheating purposes. You will find the source code :download:`here `. + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/chp/flow_diagram.svg + :align: center + Figure: Topology of the chp unit. + +At first, a plant design is chosen: The unit provides a total power of 5 MW and heating at a temperature of 110 °C for the feed flow. After that, the temperature at feed flow and live steam mass flow are altered (70 °C to 120 °C and 60 % to 105 % in respect to design mass flow) to cover the unit's range of operation. Thus, the calculation mode is switched to offdesign and the temperature and mass flow are altered in two embedded loops. The latest calculated case of the same mass flow is selected for initialisation in order to archieve better and faster convergence. The results are saved to .csv-files and the following plot of backpressure lines will be created. + + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/chp/PQ_diagram.svg + :align: center + Figure: Backpressure lines of a CHP unit. diff --git a/doc/getting_started/district_heating.rst b/doc/getting_started/district_heating.rst new file mode 100644 index 000000000..bcc1531b4 --- /dev/null +++ b/doc/getting_started/district_heating.rst @@ -0,0 +1,35 @@ +.. _dh_example_label: + +~~~~~~~~~~~~~~~~~~~~~~ +Distric heating system +~~~~~~~~~~~~~~~~~~~~~~ + +The district heating system is a great example for the usage of flexible user-defined subsystems. The example system and data are based on the district heating system Hamburg Wilhelmsburg [1]. +The source code for this example can be found `here `_. +Although the structure of the system (see the Figure below) does not seem very complex, it has more than 120 components. But we can easily determine repeating structures for the consumers and this is, where the subsystems come in place. + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/flow_diagram.svg + :align: center + Figure: Topology of the heating system. + +The single consumers are connected to the main grid with a controle valve at the outlet and each fork is connected with a pipe to the next fork. +Also, the main grid may have a dead end (e. g. in the housing areas, see subsystem closed) or is open to connect to another part of the grid (industrial area, see subsystem open). +Additionally, each branch of the main grid is connected to the upstream part with the fork subsystem (Ki, see subsystem fork). + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/subsys_closed.svg + :align: center + Figure: Generic topology of the dead end subsystem. + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/subsys_open.svg + :align: center + Figure: Generic topology of the open subsystem. + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/forks.svg + :align: center + Figure: Generic topology of the forks (variable number of branches). + +After the system has been set up, we designed the pipes' insulation in a way, that the feed flow the temperature gradient is at 1 K / 100 m and the back flow gradient is at 0.5 K / 100 m. +Having designed the system, heat losses at different ambient temperatures can be calculated, as the heat transfer coefficient for the pipes has been calculated in the design case. +By this way, it is for example possible to apply load profiles for the consumers as well as a profile for the ambient temperature to investigate the network heat losses over a specific period of time. + +[1]: Lorenzen, P.: "Das Wärmenetz als Speicher im Smart Grid: Betriebsführung eines Wärmenetzesin Kombination mit einem stromgeführten Heizkraftwerk". HAW Hamburg, 2014. \ No newline at end of file diff --git a/doc/getting_started/heat_pump.rst b/doc/getting_started/heat_pump.rst new file mode 100644 index 000000000..83c008bb8 --- /dev/null +++ b/doc/getting_started/heat_pump.rst @@ -0,0 +1,23 @@ +.. _heat_pump_cop_label: + +~~~~~~~~~~~~~~~~~~ +COP of a heat pump +~~~~~~~~~~~~~~~~~~ + +This example is based on the :ref:`heat pump tutorial ` and shows how to calculate the COP of a heat pump at different ambient temperatures and different loads of the plant. +The idea is very similar to the :ref:`CHP example `, thus you should have a look at the tutorial and the CHP example first. +You will find the source code `in this repository `_. + +.. figure:: https://github.com/fwitte/tespy_examples/tree/master/heat_pump/flow_diagram.svg + :align: center + Figure: Topology of the heat pump unit. + +After the plant has been designed, the consumer's heat demand and the ambient temperature are modified within defined ranges. +Generally, if you are performing offdesign calculation, keep in mind, that a good initial guess/solution is the key to good convergence progress. This is why, we initialise the calculation at a higher ambient temperature with the results +from the calculation of the same load and the nearest ambient temperature possible (in this case always the calculation one step before). This helps the algorithm to stabilize and find a solution. +If you skip out on a large range of temperature or power, you might run into convergence issues. The figure below shows the COP of the heat pump for the different temperature levels and at different loads. + +.. figure:: https://github.com/fwitte/tespy_examples/tree/master/heat_pump/COP.svg + :align: center + Figure: COP of the heat pump. + diff --git a/doc/combustion_chamber.rst b/doc/getting_started/tutorial_combustion_chamber.rst similarity index 96% rename from doc/combustion_chamber.rst rename to doc/getting_started/tutorial_combustion_chamber.rst index 42be62e19..9a5b4bdb8 100644 --- a/doc/combustion_chamber.rst +++ b/doc/getting_started/tutorial_combustion_chamber.rst @@ -13,7 +13,7 @@ combustion chamber ------------------ The combustion chamber is an important component within thermal power plants, but unfortunately is the reason for many issues, as the solving algorithm is very sensitive to small changes e. g. -the fluid composition. We will demonstrate how to handle the combustion chamber in a very small, simple example. You can download the full code from the `github repository `_. +the fluid composition. We will demonstrate how to handle the combustion chamber in a very small, simple example. You can download the full code from the :download:`tespy_examples repository `. First of all you need to define the network containing all fluid components used for the combustion chamber. **These are at least the fuel, oxygen, carbon-dioxide and water**. For this example we added Argon, and of course - as we are using Air for the combustion - Nitrogen. On top, it is recommended to specify reasonable ranges for the fluid properties. @@ -96,7 +96,7 @@ It is also possible to make modifications on the fluid composition, for example stoichiometric combustion chamber --------------------------------- -The example for the stoichiometric combustion chamber can be taken from the github repository's `examples folder `_. +The example for the stoichiometric combustion chamber can as well be taken from the :download:`tespy_examples repository `. Again, the network must have the information, which fluids will be part of the fluid vector. In contrast to the normal combustion chamber, you will need the following fluids: **Air, Fuel and Flue Gas**. For this tutorial we will call them: **"TESPy::myAir", "TESPy::myFuel" and "TESPy::myFuel_fg"**, we will see, why we chose these names for the fluids later. Do not forget to specify the ranges for pressure and temperature. This is a very important stept for this specific component, we will explain later, why it is. diff --git a/doc/heat_pump.rst b/doc/getting_started/tutorial_heat_pump.rst similarity index 99% rename from doc/heat_pump.rst rename to doc/getting_started/tutorial_heat_pump.rst index 1ad157643..c7aeac654 100644 --- a/doc/heat_pump.rst +++ b/doc/getting_started/tutorial_heat_pump.rst @@ -315,6 +315,6 @@ Here again, using the saved results from previous calculations is always favoura Further tasks ============= -After successfully modeling the heat pump in design and offdesign cases, you can now start using your model for further calculations. E. g., if you have a time series of required heat flux of your consumer, you can loop over the series and perform offdesign calculation adjusting the heat flux every time. Of course, this is possible with every offdesign parameter. We provide the scripts after each of the three steps of the tutorial: :download:`Step 1 <../examples/tutorial/step_1.py>`, :download:`Step 2 <../examples/tutorial/step_2.py>`, :download:`Step 3 <../examples/tutorial/step_3.py>`. +After successfully modeling the heat pump in design and offdesign cases, you can now start using your model for further calculations. E. g., if you have a time series of required heat flux of your consumer, you can loop over the series and perform offdesign calculation adjusting the heat flux every time. Of course, this is possible with every offdesign parameter. We provide the scripts after each of the three steps of the tutorial: :download:`Step 1 <../tutorial/step_1.py>`, :download:`Step 2 <../tutorial/step_2.py>`, :download:`Step 3 <../tutorial/step_3.py>`. Have fun working with TESPy! diff --git a/doc/tutorial.rst b/doc/tutorial.rst deleted file mode 100644 index 31e36ee44..000000000 --- a/doc/tutorial.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _tespy_tutorial_label: - -Tutorial/Examples -================= - -We provide two different tutorials for you to get started with TESPy. You will learn how to create basic models and get an idea of the design process of a thermal process plant. On top of that, we created a tutorial for the usage of the combustion chamber: It is an important component for thermal power plants while beeing a source for many errors in the calculation. Additional examples can be downloaded from the `github repository `_. - -Contents: - -.. toctree:: - :maxdepth: 1 - - heat_pump - combustion_chamber From 281637a899824773e8f952f9f94b9131930f18b8 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 13 Aug 2018 14:09:01 +0200 Subject: [PATCH 37/63] adjustments to new structure --- doc/getting_started.rst | 6 +++++- doc/introduction.rst | 25 +++++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/doc/getting_started.rst b/doc/getting_started.rst index c2510397c..bbae2bc69 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -2,6 +2,8 @@ Getting started ~~~~~~~~~~~~~~~ +.. _tespy_examples_label: + Examples ======== @@ -10,7 +12,9 @@ In the example section we provide a variety of TESPy applications: * a very basic model of the clausius rankine process, * the calculation of backpressure lines of a chp at different loads and feed flow temperature levels, * modeling approach for a district heating system with various consumers and system infrastructure and -* the COP of a heat pump dependent on load and ambient temperature. You can find all examples in the `tespy examples github repository `_. +* the COP of a heat pump dependent on load and ambient temperature. + +You can find all examples in the `tespy examples github repository `_. .. toctree:: :maxdepth: 1 diff --git a/doc/introduction.rst b/doc/introduction.rst index 964616152..203f0a1f1 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -4,11 +4,15 @@ TESPy - Thermal Engineering Systems in Python ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -TESPy stands for "Thermal Engineering Systems in Python" and provides a powerful simulation toolkit for thermal engineering plants such as power plants, district heating systems or heat pumps. It is an external extension module within the `Open Energy System Modeling Framework `_ and can be used as a standalone package. +TESPy stands for "Thermal Engineering Systems in Python" and provides a powerful simulation toolkit for thermal engineering plants such as power plants, district heating systems or heat pumps. +It is an external extension module within the `Open Energy System Modeling Framework `_ and can be used as a standalone package. -With the TESPy package you are able to calculate stationary operation in order to design the process of your plant. From that point it is possible to calculate the offdesign behaviour of your plant using underlying characteristics for each of the plants components. For now, the package inlcudes basic components, such as turbines, pumps, compressors, heat exchangers, pipes, mixers and splitters as well as some advanced components (derivatives of heat exchangers, drum). +With the TESPy package you are able to calculate stationary operation in order to design the process of your plant. +From that point it is possible to calculate the offdesign behaviour of your plant using underlying characteristics for each of the plants components. +For now, the package inlcudes basic components, such as turbines, pumps, compressors, heat exchangers, pipes, mixers and splitters as well as some advanced components (derivatives of heat exchangers, drum). -Everybody is welcome to use and/or develop TESPy. Contribution is already possible on a low level by simply fixing typos in TESPy's documentation or rephrasing sections which are unclear. If you want to support us that way please fork the TESPy repository to your own github account and make changes as described in the github guidelines: https://guides.github.com/activities/hello-world/ +Everybody is welcome to use and/or develop TESPy. Contribution is already possible on a low level by simply fixing typos in TESPy's documentation or rephrasing sections which are unclear. +If you want to support us that way please fork the TESPy repository to your own github account and make changes as described in the github guidelines: https://guides.github.com/activities/hello-world/ .. contents:: :depth: 1 @@ -19,7 +23,8 @@ Everybody is welcome to use and/or develop TESPy. Contribution is already possib Documentation ============= -You can find the full documentation at `readthedocs `_. Use the `project site `_ of readthedocs to choose the version of the documentation. Go to the `download page `_ to download different versions and formats (pdf, html, epub) of the documentation. Currently, only the dev branch is available. +You can find the full documentation at `readthedocs `_. Use the `project site `_ of readthedocs to choose the version of the documentation. +Go to the `download page `_ to download different versions and formats (pdf, html, epub) of the documentation. Currently, only the dev branch is available. To get the latest news visit and follow our `website `_. @@ -30,16 +35,20 @@ If you have a working Python3 environment, use pypi to install the latest tespy .. code:: bash - pip install tespy + pip install tespy -If you want to use the latest features, you might want to install the **developer version**. See section :ref:`'Developing TESPy' ` for more information. The developer version is not recommended for productive use. +We provide :ref:`more detailed installation instructions `, too. -.. _tespy_examples_label: +If you want to use the latest features, you might want to install the **developer version**. See section :ref:`Developing TESPy ` for more information. The developer version is not recommended for productive use. Examples ======== -For a short introduction on how TESPy works and how you can use it, we provide an :ref:`Introduction `. You can download the python scripts of the example plants from the `TESPy examples folder on github `_. On top of that, there is a :ref:`step-by-step tutorial ` on how to model a heat pump in TESPy. +For a short introduction on how TESPy works and how you can use it, we provide some examples at the :ref:`examples section `. +On top of that, there is a :ref:`step-by-step tutorial ` on how to model a heat pump in TESPy. + +The :ref:`using TESPy section ` provides you with information on the basics (TESPy network, components, connections) and +more advanced subjects (TESPy's solver, design/offdesign calculation, custom components and subsystems, network export and import). License ======= From b774fadb3c3e02a4211bba0bd20407893ccd510f Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 13 Aug 2018 14:09:26 +0200 Subject: [PATCH 38/63] adjusted subsystem set_conns --- tespy/components/subsystems.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tespy/components/subsystems.py b/tespy/components/subsystems.py index 3b087c763..57c783ee1 100644 --- a/tespy/components/subsystems.py +++ b/tespy/components/subsystems.py @@ -150,6 +150,8 @@ def set_comps(self): return def set_conns(self): + if not hasattr(self, 'nw'): + self.create_network() return def create_network(self): From 725111b7ce8908510c56ff42aa9c27adf6531620 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 13 Aug 2018 17:13:48 +0200 Subject: [PATCH 39/63] fixed table of contents --- doc/getting_started.rst | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/doc/getting_started.rst b/doc/getting_started.rst index bbae2bc69..ac6425ecd 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -16,13 +16,15 @@ In the example section we provide a variety of TESPy applications: You can find all examples in the `tespy examples github repository `_. -.. toctree:: - :maxdepth: 1 +.. contents:: `Examples` + :depth: 1 + :local: Examples + :backlinks: top - getting_started/basic - getting_started/chp - getting_started/district_heating - getting_started/heat_pump +.. include:: getting_started/basic +.. include:: getting_started/chp +.. include:: getting_started/district_heating +.. include:: getting_started/heat_pump .. _tespy_tutorial_label: @@ -33,10 +35,10 @@ We provide two different tutorials for you to better understand how to work with You will learn how to create basic models and get the idea of designing a plant and simulating the offdesign behaviour in the heat pump tutorial (this is the bases for the COP calculation of the heat pump in the examples). On top of that, we created a tutorial for the usage of the combustion chamber: It is an important component for thermal power plants while beeing a source for many errors in the calculation. -Contents: +.. contents:: `Tutorials` + :depth: 1 + :local: Tutorials + :backlinks: top -.. toctree:: - :maxdepth: 1 - - getting_started/tutorial_heat_pump - getting_started/tutorial_combustion_chamber +.. include:: getting_started/tutorial_heat_pump +.. include:: getting_started/tutorial_combustion_chamber From 6ec016023fb045a136f0e77821c8db81e4283a2a Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 14 Aug 2018 13:29:37 +0200 Subject: [PATCH 40/63] reorganised using tespy section, improved readability --- doc/using_tespy.rst | 839 +++++++++++++++++++++++++++++++------------- 1 file changed, 593 insertions(+), 246 deletions(-) diff --git a/doc/using_tespy.rst b/doc/using_tespy.rst index ea300a7de..ba4213b5c 100644 --- a/doc/using_tespy.rst +++ b/doc/using_tespy.rst @@ -1,20 +1,19 @@ .. _using_tespy_label: -########### +~~~~~~~~~~~ Using TESPy -########### +~~~~~~~~~~~ TESPy provides a simulation package for component based thermal engineering containing the most important -basic components of such plants. In the introduction you will learn the basics of modelling component based -plants in TESPy. +components of such plants. In the introduction you will learn the basics of modelling component based +plants in TESPy. We then give an overview on the main TESPy modules: -We give an overview on the available components, introduce you to creating you own components and component -groups and give a short introduction on how TESPys solver works and how to handle different calculations modes. -Information on handling of fluid properties can be found at the end of this page. + * tespy.networks, + * tespy.components, + * tespy.connections and + * tespy.network_reader. -We highly recommend to check our :ref:`step by step tutorial ` on how to -set up a heat pump in TESPy. You will learn, how to set up and design a plant as well as calculate offdesign/partload performance. -Additionally we provide basic examples in the :ref:`examples section `. +At the end of this page we give a brief overview how TESPy handles fluid properties. .. figure:: api/_images/tutorial_heat_pump.svg :align: center @@ -23,13 +22,23 @@ Additionally we provide basic examples in the :ref:`examples section ` on how to +set up a heat pump in TESPy. You will learn, how to set up and design a plant as well as calculate offdesign/partload performance. +Additionally we provide basic examples in the :ref:`examples section `. + +.. contents:: `Contents` + :depth: 1 + :local: + :backlinks: top + + Introduction ============ Set up a plant -------------- -In order to simulate a plant you will have to create a tespy.network. The network is the main container for the model. +In order to simulate a plant you will have to create a tespy.network first. The network is the main container for the model. You need to specify a list of the fluids you need for the calculation in your plant. For more information on the fluid properties jump to the :ref:`bottom of this page `. @@ -41,48 +50,57 @@ You need to specify a list of the fluids you need for the calculation in your pl my_plant = nwk.network(fluids=fluid_list) On top of that, it is possible to specify a unit system and value ranges for the networks variables. If you do not specify these, TESPy will use SI-units. +The specification of the value range is used to improve convergence stability. .. code-block:: python from tespy import nwk - # set the unitsystem for temperatures to °C and for pressure to bar + # set the unitsystem for temperatures to °C, for pressure to bar and enthalpy to kJ / kg my_plant.set_attr(T_unit='C', p_unit='bar', h_unit='kJ / kg') - my_plant.set_attr(T_range=[10, 700], p_unit=[0.05, 150], h_unit=[15, 4000]) + my_plant.set_attr(T_range=[10, 700], p_range=[0.05, 150], h_range=[15, 4000]) Now you can start to create the components of the network. - Set up components ----------------- -Available components can be found :ref:`here `. If you set up a component you have to specify a (within one network) unique label. Moreover, it is possible to specify parameters for the component, for example power P for a turbine or upper terminal temperature difference ttd_u of a heat exchanger. The full list of parameters for a specific component (e. g. a vessel) is stated in the classes documentation. +Available components can be found :ref:`here `. If you set up a component you have to specify a (within one network) unique label. +Moreover, it is possible to specify parameters for the component, for example power P for a turbine or upper terminal temperature difference ttd_u of a heat exchanger. +The full list of parameters for a specific component (e. g. a vessel) is stated in the classes documentation. -Parameters for components are generally optional. Only the components label and in case you want to use a combustion chamber, the combustion chambers fuel, are mandatory parameters to provide. If an optional parameter is not specified by the user, it will be a result of the plants simulation. In this way, the set of equations a component returns is determined by which parameters you specify. You can find all equations in the :ref:`components documentation ` as well. The example below shows how to create a component with specific parameters, set or reset and how to unset a parameter: +.. note:: + Parameters for components are generally optional. Only the components label and in case you want to use a combustion chamber, the combustion chambers fuel, are mandatory parameters to provide. + If an optional parameter is not specified by the user, it will be a result of the plants simulation. In this way, the set of equations a component returns is determined by which parameters you specify. + You can find all equations in the :ref:`components documentation ` as well. The example below shows how to create a component with specific parameters, set or reset and how to unset a parameter: .. code-block:: python from tespy import cmp - my_pump = cmp.pump(label='hp pump', P=1e3) # create pump labeled 'hp pump' - my_pump.set_attr(P=2e3, eta_s=0.9) # set the power to 2000 W, set isentropic efficiency to 90 % - my_pump.set_attr(P=math.nan) # unset power + feed_water_pump = cmp.pump(label='hp pump', P=1e3) # create pump labeled 'hp pump' + feed_water_pump.set_attr(P=2e3, eta_s=0.9) # set the power to 2000 W, set isentropic efficiency to 90 % + feed_water_pump.set_attr(P=math.nan) # unset power After setting up the components the next step is to connect the components in your network. Establish connections --------------------- -Connections are used to link two components (outlet of component 1 to inlet of component 2, source to target). If two components are connected to each other the fluid properties at the source will be equal to the properties at the target. It is possible to set the properties on each connection in a similar way as parameters are set for components. You may specify: +Connections are used to link two components (outlet of component 1 to inlet of component 2, source to target). +If two components are connected to each other the fluid properties at the source will be equal to the properties at the target. +It is possible to set the properties on each connection in a similar way as parameters are set for components. You may specify: - * mass flow*, - * pressure*, - * enthalpy*, - * temperature*, - * vapour mass fraction for pure fluids, - * a fluid vector and - * a balance closer for the fluid vector. + * mass flow* (m), + * pressure* (p), + * enthalpy* (h), + * temperature* (T), + * vapour mass fraction for pure fluids (x), + * a fluid vector (fluid) and + * a balance closer for the fluid vector (fluid_balance). -All parameters but the fluid vector have to be numeric values. The fluid vector has to be specified as dictonary, see the example below. The parameter :code:`fluid_balance` can only be :code:`True` or :code:`False`. For the properties marked with * it is possible to use references instead of numeric values. This can be used for example if you want to have the pressure in two parts of your network related in a specific way but you do not know the values prior to the plant simulation. +All parameters but the fluid vector have to be numeric values. The fluid vector has to be specified as dictonary, see the example below. +The parameter :code:`fluid_balance` can only be :code:`True` or :code:`False`. For the properties marked with * it is possible to use references instead of numeric values. +This can be used for example if you want to have the pressure in two parts of your network related in a specific way but you do not know the values prior to the plant simulation. .. code-block:: python @@ -95,6 +113,9 @@ All parameters but the fluid vector have to be numeric values. The fluid vector eco_drum = con.connection(economiser, 'out2', drum, 'in1', T=320, p=con.ref(d, 0.98, 0)) # setting temperature and pressure via reference object eva_eco = con.connection(evaporator, 'out1', economiser, 'in1', T=350, m=100) # setting temperature and mass flow eco_fgs = con.connection(economiser, 'out1', flue_gas_sink, 'in1', fluid_balance=True, fluid={'air': 1}, p=1) # setting fluid vector partially as well as the fluid balance parameter and pressure + + # this line is crutial, you have to add all connections to your network! + my_plant.add_conns(ws_cond, cond_cp, cp_fwt, fwt_fwp, fwp_eco, eco_drum, eva_eco, eco_fgs) .. figure:: api/_images/intro_connections.svg :align: center @@ -106,164 +127,238 @@ If you want to set, reset or unset a connection parameter the same logic as for .. code-block:: python ws_cond.set_attr(x=0.95, p=0.05) # reset vapour mass fraction, set pressure - fwp_eco.set_attr(p=math.nan) # unset pressure + fwp_eco.set_attr(p=math.nan) # unset pressure + +Start your calculation +---------------------- + +After building your network, the components and the connections, add the following line at the end of your script and off you go: + +.. code-block:: python + + my_plant.solve(mode='design') -On top of these specifications it is possible to specify the parameters data container manually. You need to import the :code:`hlp` module. The data container class for fluid properties (mass flow, pressure, enthalpy, temperature and vapour mass fraction) is called dc_prop. +Please be aware, that the execution of the lines of code above will not create a solvable TESPy network. For good first examples jump to the :ref:`TESPy examples `. + +In order to get a good overview of the TESPy functionalities, the following sections will walk you through the different TESPy modules in detail. + + +TESPy networks +============== + +The tespy.networks.network class handles preprocessing, solving and postprocessing. We will walk you through all the important steps. + +Setup +----- + +Network container +^^^^^^^^^^^^^^^^^ + +The TESPy network contains all data of your plant, which in terms of the calculation is represented by a nonlinear system of equations. The system variables of your TESPy network are: + + * mass flow, + * pressure, + * enthalpy and + * the mass fractions of the network's fluids. + +The solver will solve for these variables. As stated in the introduction the list of fluids is passed to your network on creation. +You should **always make use of the value ranges** of the system variables, like in the code block below. This improves the stability of the algorithm. Try to fit the boundaries as tight as possible, +for instance, if you kwow that the maximum pressure in the system will be at 10 bar, use it as upper boundary. You should **always state ranges for pressure and enthalpy**, temperature is optional. +Value ranges for mass flow and fluid composition are not necessary, as these are handeled automatically. .. code-block:: python - from tespy import hlp + from tespy import nwk - # set the pressure to 0.7 MPa, and set this parameter for the calculation - ws_cond.set_attr(p=dc_prop(val=0.07, val_set=True, unit='MPa')) + fluid_list = ['air', 'water'] + my_plant = nwk.network(fluids=fluid_list) + my_plant.set_attr(p_unit='bar', h_unit='kJ / kg') + my_plant.set_attr(p_range=[0.05, 10], h_range=[15, 2000]) - # set the pressure to 0.7 MPa, set this parameter for the calculation and add a reference - ws_cond.set_attr(p=dc_prop(val=0.07, val_set=True, unit='MPa', ref=con.ref(fwp_eco, 0.01, 0), ref_set=True)) +Prior to solving the network there are options regarding the console printouts using the :py:class:`set_printoptions method `. +You can choose the print_level (info, warn, err, none), or specify the printouts individually. Check out the :py:class:`documentation ` for all options. + +.. code-block:: python + + myplant.set_printoptions(print_level='info') -If you want to specify the fluid vector you can do it in the following way: +As seen in the introduction, you will have to create your networks from the components and the connections between them. +Add all connections, subsystems and busses you want to use in your network with the following methods: .. code-block:: python - ws_cond.set_attr(fluid=dc_flu(val={'water': 1, 'air': 0}, val_set:{'water': False, 'air': True})) + myplant.add_conns() + myplant.add_busses() + myplant.add_subsys() -Busses: power connections -------------------------- +You do not need to add the components to the network, as they are inherited via the added connections. +After having set up your network and added all required elements, you can start the calculation. -Busses can be used to add up the power of different turbomachinery or to add up heat flux of different heat exchangers within your network. The handling is very similar to connections and components. You need to add components to your busses as a list containing the component object and a factor, the power of the component will be multiplied with. Do not forget to add the busses to you network. +Start calculation +^^^^^^^^^^^^^^^^^ -This can be used for easy post processing, e. g. to calculate thermal efficiency or you can build up relations between components in your network. If you want to use the busses for postprocessing only, you do not specify the sum of the power or heat flux on your bus. If you set a value for P (equal parameter for heat flux or power), an additional equation will be added to your network. This could be useful, e. g. for establishing relations between different components, for instance when using a steam turbine powered feed water pump. In the code example the power of the turbine and the feed water pump is added up and set to zero, as the turbines and feed water pumps power have to be equal in absolute value but have different sign. The sign can be manipulated, e. g. in order to design two turbines with equal power output. +You can start the solution process with the following line: .. code-block:: python + + myplant.solve(mode='design') - from tespy import nwk, con - - ... - - fwp_bus = con.bus('feed water pump', P=0) # set a value for the total power on this bus. - fwp_bus.add_comps([turbine_fwp, 1], [fwp, 1]) - - turbine_bus = con.bus('turbines', P=0) # set a value for the total power on this bus - turbine_bus.add_comps([turbine_hp, 1], [turbine_lp, -1]) - # the values for the busses power can be altered by using .set_attr() - - power = con.bus('power output') # bus for postprocessing, no power (or heat flux) specified - power.add_comps([turbine_hp, 1], [turbine_lp, 1]) - - my_network.add_busses(fwp_bus, turbine_bus, power) - -Two labels for busses have a predefined function in the postprocessing analysis: 'P_res' and 'Q_diss'. If you specify these labels for your busses, 'P_res' will be interpreted as the total power of your process and 'Q_diss' as total amount of dissipated heat flux (from the process, not internally). Given these key figures, thermal efficiency or COP will be calculated and an entropy analysis for your systems components will be performed.* +This starts the initialisation of your network and proceeds to its calculation. The specification of the calculation mode is mandatory, see the list of available keywords: -*Planned feature, not implemented yet! + * :code:`init_file` is the .csv-file you want to use for initialisation, + * :code:`design_file` is the .csv-file which holds the information of your plants design point, + * :code:`mode` is the calculation mode (design-calculation or offdesign-calculation), + * :code:`max_iter` is the maximum amount of iterations performed by the solver, + * :code:`parallel` parallel computation (True/False) and + * :code:`init_only` stop after initialisation (True/False). -Subsystems/Component groups ---------------------------- +There are two calculation modes available (:code:`'design'` and :code:`'offdesign'`), which are explained in the subsections below. +If you choose :code:`offdesign` as calculation mode the specification of a design_file is mandatory. + +The usage of an initialisation file is always optional but highly recommended, as the convergence of the solution process will be improved. +If do not specify an :code:`init_file`, the initialisation from .csv-file will be skipped. +Parallel computation can improve the calculation velocity of very large networks or networks with a large number of fluids (if used for mixtures). **Parallel code execution does not work on windows at the moment!** +:code:`init_only=True` usually is used for debugging. You could use this feature to export a not solved network, if you want to do the parametrisation in .csv-files rather than your python script. + +Design mode ++++++++++++ + +The design mode is used to design your system and is always the first calculation of your plant. **The offdesign calculation is always based on a design calculation!**. +Obviously as you are designing the plant the way you want, you are flexible to choose the parameters to specify. +However, you can't specify parameters that are based on a design case, as for example the isentropic efficiency characteristic function of a turbine or a pump. Specifying a value for the efficiency is of course possible. -Subsystems are an easy way to add frequently used component groups such as a drum with evaporator or a preheater with desuperheater to your system. You can use the predefined subsystems or :ref:`create a subsytem yourself `. Every subsystem must have two interfaces, an inlet interface and an outlet interface. These interfaces have a variable number of connections, which can be connected with the rest of your network. The example below uses the predefined subsystem preheater with desuperheater (:code:`ph_desup_cond()`). The subsystems interfaces are subsystem.inlet and subsystem.outlet, both with two connections. All connections (and components) of the subsystem have to be added to the network in order to start a simulation. This can easily be done by adding the whole subsystem object to your network. +Offdesign mode +++++++++++++++ + +The offdesign mode is used to **calulate the performance of your plant, if parameters deviate from the plant's design point**. This can be partload operation, operation at different temperature or pressure levels etc.. +Thus, before starting an offdesing calculation you have to design your plant first. By stating :code:`'offdesign'` as calculation mode, **components and connections will auto-switch to the offdesign mode.** +For components, this means that all parameters provided in :code:`component.design` will be unset and instead all parameters provided in :code:`component.offdesign` will be set. +This applies to connections analogously. **The value of the newly set parameter is always equal to the value from the design case (or based on it for characteristics).** .. code-block:: python - from tespy import subsys, cmp - ext = cmp.source(label='extraction steam') - cond = cmp.sink(label='condensate') - fwc = cmp.source(label='feed water cold') - fww = cmp.sink(label='feed water warm') + myplant.solve(mode='design', design_file='design_results.csv', init_file='design_results.csv') - # a preheater with desuperheater part - preheater = subsys.ph_desup(label='sub1') - # connections into the subsystem are attached to subsystem.inlet, connections out of the subsystem to subsystem.outlet - ext_pre = connection(ext, 'out1', subsystem.inlet, 'in1') - pre_cond = connection(subsystem.outlet, 'out1', cond, 'in1') - fwc_pre = connection(fwc, 'out1',subsystem.inlet,'in2') - pre_fwc = connection(subsystem.outlet, 'out2', fww, 'in1') +The default design and offdesign parameters for components can be found in the components documentation. For connections, there are no default design and offdesign parameters. +You can specify custom design and offdesign parameters for components and connections. For example, for a condenser you would usually design it to a maximum terminal temperature difference, in offdesign the heat transfer coefficient +is selected. The heat transfer coefficient is calculated in the preprocessing of the offdesign case based on the results of the design-case. Of course, this applies to all other parameters in the same way. +Also, the pressure drop is a result of the geometry for the offdesign case, thus we swap the pressure ratios with zeta values. + +.. code-block:: python + + heat_ex.set_attr(design=['ttd_u', 'pr1', 'pr2'], offdesign=['kA', 'zeta1', 'zeta2']) - # parametrisation - preheater.set_attr(ttd=8, pr1_desup=1, pr2_desup=1, pr1_cond=1, pr2_cond=1) +If you want to **prevent the autoswitch from design to offdesign mode** for specific components, use :code:`heat_ex.set_attr(mode='man')`. - ext_pre.set_attr(m=5, p=4, h=29e5, fluid={'water': 1}) - fwc_pre.set_attr(p=50, h=3e5, fluid={'water': 1}) - pre_fwc.set_attr(p0=50) +For connections it works in the same way, e. g. write - # create the network and connections and subsystems - my_plant.add_conns(ext_pre, pre_cond, fwc_pre, pre_fwc) - my_plant.add_subsys(subsys) - +.. code-block:: python -.. figure:: api/_images/intro_subsys.svg - :align: center + connection.set_attr(design=['h'], offdesign=['T']) - Figure 3: Topology of the subsystem. +if you want to replace the enthalpy with the temperature for your offdesign. **The temperature is a result of the design calculation and that value is then used for the offdesign calculation in this example.** +The table below contains frequently used offdesign parameters of the available components. -Start your calculation ----------------------- +======================= ====================== =================================================== + component parameter affects +======================= ====================== =================================================== + vessel zeta pressure drop +----------------------- ---------------------- --------------------------------------------------- + pipe | zeta | pressure drop + | k_s, D, L | pressure drop (via dimensions and roughness) + | kA, t_a | heat flux (using constant ambient temperature) +----------------------- ---------------------- --------------------------------------------------- + simple heat exchanger see pipe +----------------------- ---------------------- --------------------------------------------------- + heat exchanger | zeta1 | pressure drop hot side + | zeta2 | pressure drop cold side + | kA | heat flux +----------------------- ---------------------- --------------------------------------------------- + pump char isentropic efficiency +----------------------- ---------------------- --------------------------------------------------- + turbine | cone | pressure drop, volumetric flow + | char | isentropic efficiency +----------------------- ---------------------- --------------------------------------------------- + compressor | char | mass flow, pressure rise, isentropic efficiency + | vigv :sup:`1` | see above, one arbitrary parameter less +======================= ====================== =================================================== -At the bottom of your script add the following line and off you go! Additional/advanced information on the solving process and which options are available are found :ref:`here `. +1: When setting the vigv angle the characteristic map will be used for a specific vigv angle. The vigv angle is a result of the calculation, if you use the characteristic map only. -.. code-block:: python +Solving +------- - my_plant.solve(mode='design') - -How can TESPy contribute to your energy system calculations? ------------------------------------------------------------- +A TESPy network can be represented as a linear system of nonlinear equations, consequently the solution is obtained with numerical methods. +TESPy uses the n-dimensional Newton–Raphson method to find the systems solution, which may only be found, if the network is parameterized correctly. +**The number of variables n** is :math:`n = num_{conn} \cdot (3 + num_{fluids})`. -In this part you learn how you can use TESPy for your energy system calculations: In energy system calculations, for instance in oemof-solph, plants are usually modelled as abstract components on a much lower level of detail. In order to represent a plant within an abstract component it is possible to supply characteristics establishing a connection between your energy system model and a specific plant model. Thus the characteristics are a representation of a specific plant layout in terms of topology and process parameters. +The algorithm requires starting values for all variables of the system, thus an initialisation of the system is runned prior to calculating the solution. +**High quality initial values are crutial for convergence speed and stability**, bad starting values might lead to instabilty and diverging calculation can be the result. +Thus there are different levels for the initialisation. -The following part will show how to generate characteristics for a CHP unit. There are various technologies and concepts, for this example we will generate characteristics for a simple CHP with a backpressure steam turbine and a regenerative reheating unit as shown in the figure below. We want the characteristics to provide a correlation between output power and output heat flux at different temperatures of flow into a district heating system. +Initialisation +^^^^^^^^^^^^^^ -.. figure:: api/_images/CHP.svg - :align: center - - Figure 4: Topology of the power plant. - -Important design information can be obtained from the table below, the locations are indicated in the figure. After designing the plant, the total power output defined by the power_bus has been changed stepwise from a slight overload of 5.25 MW to lower part loads 3.00 MW with a stepwidth of 0.50 MW (0.25 MW for first step). Further the required temperature for the heating system was changed from 80 °C to 120 °C in steps of 10 K. - -=========== =============== ======= ======== - location parameter value unit -=========== =============== ======= ======== - fs | pressure | 100 | bar - | temperature | 550 | °C ------------ --------------- ------- -------- - extr pressure 10 bar ------------ --------------- ------- -------- - power_bus 5e6 W ------------ --------------- ------- -------- - condenser ttd_u :sup:`2` 12 K ------------ --------------- ------- -------- - reheater ttd_u :sup:`2` 7 K ------------ --------------- ------- -------- - from_hs | pressure | 10 | bar - | temperature | 60 | °C ------------ --------------- ------- -------- - to_hs temperature 110 °C -=========== =============== ======= ======== - -2: ttd_u is the upper terminal temperature difference, defined as temperature difference between hot side inlet and cold side outlet. - -As a result we get the PQ-diagram of this power plant containing the characteristics at different temperatures in the heating system. Within your oemof-solph energy system it is now possible to implement the characteristic lines as a function of the temperature level in the heating system. - -.. figure:: api/_images/PQ_diagram.svg - :align: center - - Figure 5: PQ-diagram for a CHP unit. - -Download the :download:`source file <../examples/chp.py>` of this example. - -.. _tespy_solving_network_label: - -Solving a TESPy Network -======================= +The initialisation is performed in the following steps. + +**General preprocessing:** + + * check network consistency and initialise components (if network topology is changed to a prior calculation only), + * perform design/offdesign switch (for offdesign calculations only) + +**Finding starting values:** + + * fluid propagation, + * fluid property initialisation, + * initialisation from .csv (preprocessing with design_file for offdesign case and setting starting values with init_file). + +The network check is used to find errors in the network topology, the calulation can not start without a successful check. The component initialisation is important for components using charactersitcs and the combustion chamber, +a preprocessing of some parameters is required. The preprocessing for the components is performed in the :code:`comp_init` method of the components. +You will find the methods in the :py:class:`components module `. The design/offdesign switch is described in the network setup section. + +**The fluid propagation is a very important step in the initialisation:** Often, you will specify the fluid at one point of the network only, thus all other connections are missing an initial information on the fluid vector, +if you are not using an init_file. Also, you do not want to state a starting value for the fluid vector at every point of the network. The fluid propagation will push/pull the specified fluid through the network. +If you are using combustion chambers these will be starting points and a generic flue gas composition will be calculated prior to the propagation. + +.. note:: + If the fluid propagation fails, you often experience an error, where the fluid property database can not find a value, because the fluid is 'nan'. Providing starting values manually can fix this problem. + +The fluid property initialisation takes the user specified starting values if available and otherwise uses generic starting values on the bases of to which components the connection is linked to. + +Last step is the initialisation from an init_file: For offdesign cases a preprocessing based on the design_file in order to recreate the design case and set parameters based on the design case is performed. +If you specified an init_file TESPy searches through that file for the network topology and if the corresponding connection is found, the starting values for the system variables are extracted from that file. +**The file does not need to contain all connections of your network, thus you can build up your network bit by bit and initialise the existing parts of your network from the .csv-file.** +**Be aware that a change within the fluid vector does not allow this practice.** Thus, if you plan to use additional fluids in parts of the network you have not touched until now, you will need to state all fluids from the beginning. + +.. note:: + + Initialisation from a converged calculation usually yields the best performance and is highly receommended. + In order to initialise your calculation from a .csv-file, you need to provide its filename. If you saved your calculation restults you will find the file 'savename/results.csv'. -Before learning how solve your TESPy network a short introduction on how the solution process works is provdided below. Algorithm ---------- +^^^^^^^^^ + +In this section we will give you an introduction to the implemented solution algorithm. + +Newton–Raphson method ++++++++++++++++++++++ -A TESPy Network can be represented as a linear system of non-linear equations, consequently the solution is obtained with numerical methods. TESPy uses the n-dimensional newton algorithm to find the systems solution, which may only be found, if the network is parameterized correctly. The variables of the system are mass flow, pressure, enthalpy and the fluid components on each connection of the network. Thus, the number of fluids you specify in the fluid list for the network and the number of connections determine the number of variables in the system: +The Newton–Raphson method requires the calculation of residual values for the equations and of the partial derivatives to all system variables (jacobian matrix). +In the next step the matrix is inverted and multiplied with the residual vector to calculate the increment for the system variables. +This process is repeated until every equation's result in the system is "correct", thus the residual values are smaller than a specified error tolerance. All equations are of the same structure: -.. math:: num_{var} = num_{conn} \cdot (3 + num_{fluids}). +.. math:: + + 0 = \text{expression} + +calculate the residuals -The newton algorithm requires the calculation of residual values for the equations and partial derivatives of all variables (jacobian matrix). In the next step the matrix has to be inverted and multiplied with the residual vector to calculate the increment for the systems variables. This process is repeated until every equations result in the system is correct, thus the residual values are smaller than a specified error tolerance. +.. math:: + + f(\vec{x}_i) jacobian matrix J @@ -275,123 +370,125 @@ jacobian matrix J \frac{\partial f_n}{\partial x_1} & \frac{\partial f_n}{\partial x_2} & \cdots & \frac{\partial f_n}{\partial x_n} \end{array}\right) -calculate increment +derive the increment .. math:: \vec{x}_{i+1}=\vec{x}_i-J(\vec{x}_i)^{-1}\cdot f(\vec{x}_i) -stop when +while .. math:: - ||f(\vec{x}_i)|| \leq \epsilon + ||f(\vec{x}_i)|| > \epsilon + +.. note:: -This means that you have to provide the exact amount of required parameters (neither less nor more) and the parametrisation must not lead to linear dependencies. Each parameter you set for a connection or each power respectively heat flux you set for a bus will add one equation. On top, each component provides a different amount of basic equations plus the equations provided by your component specification. For example, setting the power of a pump results in an additional equation compared to a pump without specified power: + You have to provide the exact amount of required parameters (neither less nor more) and the parametrisation must not lead to linear dependencies. + Each parameter you set for a connection and each energy flow you specify for a bus will add one equation to your system. + On top, each component provides a different amount of basic equations plus the equations provided by your component specification. + For example, setting the power of a pump results in an additional equation compared to a pump without specified power: .. math:: \forall i \in \mathrm{network.fluids} \, &0 = fluid_{i,in} - fluid_{i,out}\\ &0 = \dot{m}_{in} - \dot{m}_{out}\\ \mathrm{additional:} \, &0 = 1000 - \dot{m}_{in} (\cdot {h_{out} - h_{in}}) -.. _using_tespy_solver_handling_label: +.. _using_tespy_convergence_check_label: -Handling --------- +Convergence stability ++++++++++++++++++++++ -After you added all of your connections, subsystems and busses to your network, you can start the calculation with the following command. +One of the main downsides of the Newton–Raphson method is that the initial stepwidth is very large and that it does not know physical boundaries, +for example mass fractions smaller than 0 and larger than 1 or negative pressure. Also, the large stepwidth can adjust enthalpy or pressure to quantities that are not covered by the fluid property databases. +This would cause an inability e. g. to calculate a temperature from pressure and enthalpy in the next iteration of the algorithm. In order to improve convergence stability, we have added a convergence check. -.. code-block:: python +**The convergence check manipulates the system variables after the increment has been added** (if the system variable's value is not user specified). This manipulation has four steps, the first is always applied: - nw.solve(init_file=None, design_file=None, mode='design', - dec='.', max_iter=50, parallel=False) - -This starts the initialisation of your network and proceeds to its calculation. + * cutting off mass fractions smaller than 0 and larger than 1: This way a mass fraction of a single fluid components never exceeds these boundaries. -* :code:`nw` is the network object, -* :code:`init_file` is the .csv-file you want to use for initialisation, -* :code:`design_file` is the .csv-file which holds the information of your plants design point, -* :code:`mode` is the calculation mode (design-calculation or offdesign-calculation), -* :code:`dec` is the decimal separator in the .csv-files, -* :code:`max_iter` is the maximum amount of iterations performed by the solver and finally -* :code:`parallel` parallel computation of components (True/False). +The next three steps are applied, if the user did not specify an init_file and the iteration count is lower than 3, thus in the first three iteration steps of the algorithm only. In other cases this convergence check is skipped. -There are two modes available (:code:`'design'` and :code:`'offdesign'`). If you choose :code:`offdesign` as calculation mode a design file must be specified. The initialisation file is always optional but very valuable, if you specify it to be :code:`None`, the initialisation from .csv-file will be skipped. Parallel computation for the components might slow down the computation for smaller networks. + * Check, if the fluid properties (pressure, enthalpy and temperature) are within the user specified boundaries (:code:`p_range, h_range, T_range`) and if not, cut off higher/lower values. + * Check the fluid properties of the connections based on the components they are connecting. E. g. check if the pressure at the outlet of a turbine is lower than the pressure at the inlet or if the flue gas composition at a combustion chamber's + outlet is within the range of a "typical" flue gas composition. If there are any violations, the corresponding variables are manipulated. If you want to look up, what exactly the convergence check for a specific component does, + look out for the :code:`convergence_check` methods in the tespy.components.components module. + * A second check of the fluid properties towards the specified boundaries to cut off bad values generated by the component convergence check. -Initialisation -^^^^^^^^^^^^^^ +In most cases the algorithm has found a near enough solution after the third iteration, further checks are usually not required. -The newton algorithm requires starting values for all variables of the system. A high quality of initial values (low deveiation from solution) improves convergence speed and stability, whereas bad starting values might lead to instabilty and diverging calculation can be the result. In order to provide good initial values you can choose between three different initialisation options: +Troubleshooting ++++++++++++++++ -* initialise with standard values, -* provide starting values on your connections (see connection d in the subsystem example, usage: :code:`m0, p0, h0`) and -* provide a .csv-file of a previously calculated network. +In this section we show you how you can troubleshoot your calculation and list up common mistakes. -The last option usually yields the best performance and is highly receommended. In order to initialise your calculation from a .csv-file, you need to provide the filename (e. g. myfile_conn.csv). The file does not need to contain all connections of your network, thus you can build up your network bit by bit and initialise the existing parts of your network from the .csv-file. Be aware that a change within the fluid vector does not allow this practice. Thus, if you plan to use additional fluids in parts of the network you have not touched until now, you will need to state all fluids from the beginning. +First of all, make sure your network topology is set up correctly, TESPy will prompt an Error, if not. +Also, TESPy will prompt an error, if you did not provide enough or if you provide too many parameters for your calculation, but at the moment you will not be given an information which parameters are under- or overdetermined. -Postprocessing -^^^^^^^^^^^^^^ +.. note:: + Always keep in mind, that the system has to find a value for mass flow, pressure, enthalpy and the fluid mass fractions. Try to build up your network step by step and have in mind, what parameters will be determined + by adding an additional component without any parametrisation. This way, you can easily find out, which parameters are still to be determined. -The postprocessing has three functions you can apply to your calculation: +When using multiple fluids in your network, e. g. water, air and methane and at some point you want to have water only, you still need to specify the mass fractions for both air and methane (although beeing zero) at that point. +Also, setting :code:`fluid={water: 1}, fluid_balance=True` will still not be sufficent, as the fluid_balance parameter adds only one equation to your system. -* plot the convergence history (:code:`nw.plot_convergence()`), -* print the results to prompt (:code:`nw.print_results()`) and -* save the results in a .csv-file (:code:`nw.save(filename, dec='.')`). +.. note:: + + If you are modeling a cycle, e. g. the clausius rankine cylce, you need to make a cut in the cycle using a sink and a source not to overdetermine the system. Have a look in the :ref:`heat pump tutorial ` + to understand why this is important. -The main purpose of the plotting function is trouble shooting when your calculation does not converge. Therefore you specify a maximum number of iterations for the solver (:code:`max_iter`). As a result you get a plot of mass flow, pressure and enthalpy on all connections of your network. From there it might be possible to identify e. g. oscillating values or values that stay beyond the specified bounds of the fluid properties. +If you have provided the correct number of parameters in your system and the calculations stops after or even before the first iteration, there are four frequent reasons for that: -You can print the components and its properties to the prompt or, if you choose to save your results in a .csv-file, open the file and look up the components results in the file 'filename_comp.csv'. The mass flows and fluid properties of all connections are stored in the file 'filename_conn.csv'. On top, you can specify the decimal separator with :code:`nw.save(filename, dec='.')`. + * Sometimes, the fluid property database does not find a specific fluid property in the initialisation process, have you specified the values in the correct unit? + * Also, fluid property calculation might fail, if the fluid propagation failed. Provide starting values for the fluid composition, especially, if you are using drums, merges and splitters. + * A linear dependency in the jacobian matrix due to bad parameter settings stops the calculation (overdetermining one variable, while missing out on another). + * A linear dependency in the jacobian matrix due to bad starting values stops the calculation. -Offdesign calculation -^^^^^^^^^^^^^^^^^^^^^ - -After designing your process you might want to gain information on offdesign behaviour. By stating :code:`'offdesing'` as calculation mode, you can auto-switch the components and connections to offdesign mode. This means, that all parameters given in :code:`component.design` will be unset and instead all parameters provided in :code:`component.offdesign` will be set. The same action is performed for the connections. +The first reason can be eleminated by carefully choosing the parametrisation. **A linear dependendy due to bad starting values is often more difficult to resolve and it may require some experience.** +In many cases, the linear dependency is caused by equations, that require the **calculation of a temperature**, e. g. specifying a temperature at some point of the network, terminal temperature differences at heat exchangers, etc.. +In this case, **the starting enthalpy should be adjusted in a way, that the fluid state is not within the two-phase region:** The specification of temperature and pressure in a two-phase region does not yield a distict value for the enthalpy. +Even if this specific case appears after some iterations, better starting values often do the trick. -The default design and offdesign parameters for components can be found in the components documentation. For connections, there are no default design and offdesign parameters. For example, in order to specify custom design and offdesign parameters for a turbine use +Did you experience other errors frequently and have a workaround/tips for resolving them? You are very welcome to contact us and share your experience for other users! -.. code-block:: python +Postprocessing +-------------- - turbine.set_attr(design=['P', 'eta_s'], offdesign=['cone', 'char']) - -and for connections it works in the same way. +A postprocessing is performed automatically after the calculation finished. You have two further options: -.. code-block:: python + * print the results to prompt (:code:`nw.print_results()`) and + * save the results in a .csv-file (:code:`nw.save('savename')`). - connection.set_attr(design=['h'], offdesign=['T']) +You can print the components and its properties to the prompt and the connections and its properties as well. If you choose to save your results in a .csv-file, open the file and look up the **connection parameters in the results file**. +**If you want to export up the parameters of the components, too, you have to save the network structure.** In order to do this, add this line to your code: :code:`nw.save('savename', structure=True)`. +In both cases TESPy will create a new folder 'savename' in your working directory containing the results.csv file and subfolders with the component results. + +In order to perform calculations based on your results, you can access all components' and connections' parameters: + +For the components this is the way to go + +.. code:: python + + eff = mycomp.eta_s.val # isentropic efficiency of mycomp + s_irr = mycomp.Sirr.val # entropy production of mycomp due to irreveribility -The table below contains frequently used offdesign parameters of the components. +Use this code for connection parameters: -======================= ====================== =================================================== - component parameter affects -======================= ====================== =================================================== - vessel zeta pressure drop ------------------------ ---------------------- --------------------------------------------------- - pipe | zeta | pressure drop - | k_s, D, L | pressure drop (via dimensions and roughness) - | kA, t_a | heat flux (using constant ambient temperature) ------------------------ ---------------------- --------------------------------------------------- - simple heat exchanger see pipe ------------------------ ---------------------- --------------------------------------------------- - heat exchanger | zeta1 | pressure drop hot side - | zeta2 | pressure drop cold side - | kA | heat flux ------------------------ ---------------------- --------------------------------------------------- - pump char isentropic efficiency ------------------------ ---------------------- --------------------------------------------------- - turbine | cone | pressure drop, volumetric flow - | char | isentropic efficiency ------------------------ ---------------------- --------------------------------------------------- - compressor | char | mass flow, pressure rise, isentropic efficiency - | vigv :sup:`1` | see above, one arbitrary parameter less -======================= ====================== =================================================== +.. code:: python -1: When setting the vigv angle the characteristic map will be used for a specific vigv angle. The vigv angle is a result of the calculation, if you use the characteristic map only. + mass_flow = myconn.m.val # value in specified network unit + mass_flow_SI = myconn.m.val_SI # value in SI unit + mass_fraction_oxy = myconn.fluid.val['O2'] # for the mass fraction of oxygen + +Additionally TESPy can calculate cycle process performance figures for you, if you define busses with the labels 'P_res' (components with power input/output) and 'Q_diss' +(add components with heat input/output for total dissipated heat) in your network: Thermal efficiency for a right-handed process, COP for left-handed processes. -.. _tespy_components_label: TESPy components ================ -Available components --------------------- +In this section we will introduce you into the details of component parametrisation and component characteristics. At the end of the section we show you, how to create custom components. + +List of components +------------------ More information on the components can be gathered from the code documentation. We have linked the base class containing a figure and basic informations as well as the equations. @@ -415,18 +512,80 @@ More information on the components can be gathered from the code documentation. - :py:class:`Drum ` (:py:meth:`equations `) - :py:class:`Subsystem interface ` (:py:meth:`equations `) + +Component parametrisation +------------------------- + +Component parameters can be set and accessed in various ways. All parameters of components are objects of a data_container class. The data container for component parameters it is called dc_cp and dc_cc for component characteristics. +The main purpose of having a data container for the parameters (instead of pure numbers), is added flexibility for the user. + +There are different ways for you to specify a component parameter, we use a heat exchanger as an example. + +Parameters +^^^^^^^^^^ + +.. code-block:: python + + from tespy import cmp, hlp + import numpy as np + + he = cmp.heat_exchanger('evaporator') + + # ways to specify (and set) value + he.set_attr(kA=1e5) + # specify data container (same result as above) + he.set_attr(kA=hlp.dc_cp(val=1e5, is_set=True)) + + # ways to unset value + he.set_attr(kA=np.nan) + he.kA.set_attr(is_set=False) + + # to come in TESPy v0.0.4 + pipe = cmp.pipe('my pipe') + + # make diameter variable of system + pipe.set_attr(D='var') + # data container specification with identical result, + # benefit: val is the starting value in this case + pipe.set_attr(D=hlp.dc_cp(val=0.2, is_set=True, is_var=True)) + + +Characteristics +^^^^^^^^^^^^^^^ + +.. code-block:: python + + from tespy import cmp, hlp + import numpy as np + + he = cmp.heat_exchanger('evaporator') + + # specify name of predefined method + he.set_attr(kA_char1='EVA_HOT') + he.set_attr(kA_char2='EVA_COLD') + + # specify data container (yields same result) + he.set_attr(kA_char1=hlp.dc_cc(method='EVA_HOT', param='m')) + + # specify data container (custom interpolation points x and y) + x = np.array([0, 0.5, 1, 2]) + y = np.array([0, 0.8, 1, 1.2]) + he.set_attr(kA_char1=hlp.dc_cc(method='EVA_HOT', param='m', x=x, y=y)) + + Component characteristics ------------------------- Characteristics are available for the following components and parameters: -- pump (isentropic efficiency, non customizable at the moment) -- compressor (component map for isentropic efficiency and pressure rise, non customizable at the moment) +- pump (isentropic efficiency, not customizable at the moment, pressure rise vs. volumetric flow characteristic, customizable) +- compressor (component map for isentropic efficiency and pressure rise, not customizable at the moment) - turbine (isentropic efficiency, various predefined methods and specification parameters, customizable) - heat exchangers (heat transfer coefficient, various predefined types, mass flows as specification parameters, customizable) - simple heat exchangers (e. g. pipe, see heat exchangers) -There are two ways for specifying the customizable characteristic line of a component (turbine and heat exchangers only). You can specify the method directly by stating the methods name or you define the whole data container for this parameter. The data container for component characteristics is called dc_cc, for component parameters it is called dc_cp. The main purpose of having a data container for the parameters, too, lies in the possibility to add component parameters as variables to your system. This is a planned feature for the next release and thus we will not look at the component parameter specification at this point. +There are two ways for specifying the customizable characteristic line of a component. +You can specify the method directly by stating the methods name or you define the whole data container for this parameter. .. code-block:: python @@ -448,19 +607,21 @@ There are two ways for specifying the customizable characteristic line of a comp he.set_attr(kA_char1='EVA_HOT') he.set_attr(kA_char2='EVA_COLD') -All of these components are supplied with default characteristic lines, which can be found in the :py:class:`documentation `. +Turbines, pumps (isentropic efficiency characteristic) and heat exchangers are supplied with default characteristic lines, which can be found in the :py:class:`documentation `. Custom components ----------------- -If required, you can add custom components. These components should inherit from tespy.components.components class or its children. In order to do that, create a python file in your working directory and import the tespy.components.components module. The most important functions are +If required, you can add custom components. These components should inherit from tespy.components.components class or its children. +In order to do that, create a python file in your working directory and import the tespy.components.components module. The most important methods are - :code:`attr(self)`, +- :code:`attr_prop(self)`, - :code:`inlets(self)`, - :code:`outlets(self)`, -- :code:`equations(self, nw)`, +- :code:`equations(self)`, - :code:`derivatives(self, nw)` and -- :code:`calc_parameters(self, nw)`, +- :code:`calc_parameters(self, nw, mode)`, where :code:`nw` is a tespy.networks.network object. @@ -477,7 +638,17 @@ The starting lines of your file would look like this: Attributes ^^^^^^^^^^ -:code:`attr(self)` must return a list with strings in it. These are the attributes you can specify when you want to parametrize your component. +The attr method must return a list with strings in it. These are the attributes you can specify when you want to parametrize your component. +The attr_prop method returns a dictionary with the same keys as the elements in the attr method. The values for each key are the type of data_container this parameter should hold. + +.. code:: python + + def attr(self): + return ['par1', 'par2'] + + def attr_prop(self): + return {'par1': dc_cp(), 'par2': dc_cc()} + Inlets and outlets ^^^^^^^^^^^^^^^^^^ @@ -506,7 +677,7 @@ The number of inlets and outlets might even be generic, e. g. if you have added Equations ^^^^^^^^^ -The equations contain the information on the changes to the fluid properties within the component. Each equations must formulated in a way, that the correct result will be zero, e. g.: +The equations contain the information on the changes to the fluid properties within the component. Each equation must be defined in a way, that the correct result is zero, e. g.: .. math:: @@ -529,23 +700,74 @@ The equations are added to a list one after another, which will be returned at t Derivatives ^^^^^^^^^^^ -You need to calculate the partial derivatives of the equations to all variables of the network. This means, that you have to calculate the partial derivatives to mass flow, pressure, enthalpy and all fluids in the fluid vector on each incomming or outgoing connection of the component. +You need to calculate the partial derivatives of the equations to all variables of the network. +This means, that you have to calculate the partial derivatives to mass flow, pressure, enthalpy and all fluids in the fluid vector on each incomming or outgoing connection of the component. -Add all derivatives to a list (in the same order as the equations) and return the list as numpy array (:code:`np.asarray(list)`). The derivatives can be calculated analytically or numerically by using the inbuilt function :code:`ddx_func(self, func, dx, pos)`. +Add all derivatives to a list (in the same order as the equations) and return the list as numpy array (:code:`np.asarray(list)`). +The derivatives can be calculated analytically or numerically by using the inbuilt function :code:`ddx_func(self, func, dx, pos)`. -- :code:`inlets` and :code:`outlets` are a list of the connections at the inlets and the outlets, - :code:`func` is the function you want to calculate the derivatives for, - :code:`dx` is the variable you want to calculate the derivative to and -- :code:`pos` indicates the connection you want to calculate the derivative for, e. g. :code:`pos=1` means, that counting your inlets and outlets from low index to high index (first inlets, then outlets), the connection to be used is the second connection in that list. +- :code:`pos` indicates the connection you want to calculate the derivative for, e. g. :code:`pos=1` means, that counting your inlets and outlets from low index to high index (first inlets, then outlets), + the connection to be used is the second connection in that list. For a good start just look into the source code of the inbuilt components. If you have further questions feel free to contact us. + .. _tespy_subsystems_label: + TESPy subsystems/component groups ================================= -You can use subsystems in order to represent groups of different components. These are highly customizable and thus a very powerful tool, if you require to use specific component groups frequently. You will learn how to create your own subsystems. Create a .py file in your working-directory with the class-definition of your custom subsystem. This usually includes the following methods: +Usage +----- + +Subsystems are an easy way to add frequently used component groups such as a drum with evaporator or a preheater with desuperheater to your system. +You can use the predefined subsystems or :ref:`create a subsytem yourself `. Every subsystem must have two interfaces, an inlet interface and an outlet interface. +These interfaces have a variable number of connections, which can be connected with the rest of your network. The example below uses the predefined subsystem preheater with desuperheater (:code:`ph_desup_cond()`). +The subsystems interfaces are subsystem.inlet and subsystem.outlet, both with two connections. All connections (and components) of the subsystem have to be added to the network in order to start a simulation. +This can easily be done by adding the whole subsystem object to your network. + +.. code-block:: python + + from tespy import subsys, cmp + ext = cmp.source(label='extraction steam') + cond = cmp.sink(label='condensate') + fwc = cmp.source(label='feed water cold') + fww = cmp.sink(label='feed water warm') + + # a preheater with desuperheater part + preheater = subsys.ph_desup(label='sub1') + + # connections into the subsystem are attached to subsystem.inlet, connections out of the subsystem to subsystem.outlet + ext_pre = connection(ext, 'out1', subsystem.inlet, 'in1') + pre_cond = connection(subsystem.outlet, 'out1', cond, 'in1') + fwc_pre = connection(fwc, 'out1',subsystem.inlet,'in2') + pre_fwc = connection(subsystem.outlet, 'out2', fww, 'in1') + + # parametrisation + preheater.set_attr(ttd=8, pr1_desup=1, pr2_desup=1, pr1_cond=1, pr2_cond=1) + + ext_pre.set_attr(m=5, p=4, h=29e5, fluid={'water': 1}) + fwc_pre.set_attr(p=50, h=3e5, fluid={'water': 1}) + pre_fwc.set_attr(p0=50) + + # create the network and connections and subsystems + my_plant.add_conns(ext_pre, pre_cond, fwc_pre, pre_fwc) + my_plant.add_subsys(subsys) + + +.. figure:: api/_images/intro_subsys.svg + :align: center + + Figure 3: Topology of the subsystem. + +Custom subsystems +----------------- + +You can use subsystems in order to represent groups of different components. These are highly customizable and thus a very powerful tool, if you require to use specific component groups frequently. +You will learn how to create your own subsystems. Create a .py file in your working-directory with the class-definition of your custom subsystem. This usually includes the following methods: - :code:`attr`: list of subsystem attributes, - :code:`create_comps`: define the number of interfaces and create the necessary components, @@ -561,7 +783,7 @@ Your file will start with the following lines: from tespy import con, cmp, subsys - + class dr_eva_natural (subsys.subsystem): Add the attr method: @@ -576,7 +798,7 @@ Add the attr method: ['dp1_eva', 'PP', 'circ_num']) Create the components ---------------------- +^^^^^^^^^^^^^^^^^^^^^ The inlet and the outlet of the subsystem must be an attribute of the subsystem in order to reference to these when you are creating a network and want to connect the subsystem to the rest of the network. @@ -597,9 +819,8 @@ The inlet and the outlet of the subsystem must be an attribute of the subsystem As specific attributes refer to specific components in the subsystem, it is necessery, that the evaporator is stored as attribute of the subsystem as well. Else it would not be possible to set values for the parametrization. - Parametrize the components --------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python @@ -610,9 +831,9 @@ Parametrize the components self.evaporator.set_attr(pr1=self.pr1_eva) Create the connections ----------------------- +^^^^^^^^^^^^^^^^^^^^^^ -In this example the components are saved in a list which is an attribute of the subsystem. As only the fourth and the last connections must be referenced in :code:`set_conns` it would be sufficient to store those connection as attributes of the subsystem. +Create a list called :code:`self.conns` and add the connections to that list. .. code-block:: python @@ -629,9 +850,10 @@ In this example the components are saved in a list which is an attribute of the self.conns += [con.connection(self.drum, 'out2', self.outlet, 'in2')] Parametrize the connections ---------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The connection gets a ref object as attribute, thus it is necessary to look, if the subsystems attribute is set or not. For parametrization with specific values simply use :code:`self.conns[3].set_attr(m=self.mass_flow)`. :code:`self.mass_flow` must be a subsystem attribute in this example. +The connection gets a ref object as attribute, thus it is necessary to look, if the subsystems attribute is set or not. +For parametrization with specific values simply use :code:`self.conns[3].set_attr(m=self.mass_flow)`. :code:`self.mass_flow` must be a subsystem attribute in this example. .. code-block:: python def set_conns(self): @@ -643,9 +865,128 @@ The connection gets a ref object as attribute, thus it is necessary to look, if self.conns[3].set_attr(m=np.nan) Add more felxibility --------------------- +^^^^^^^^^^^^^^^^^^^^ + +If you want to add even more flexibility, you might need to manipulate the :code:`__init__()` method. +For example, if you want a variable number of inlets and outlets because you have a variable number of components groups within your subsystem, +you may introduce an attribute which is set on initialisation and lets you create and parametrize components and connections generically. +This might be very interesting for district heating systems, turbines with several sections of equal topology, etc.. +For a good start, you can have a look into the sub_consumer.py at the `tespy_examples repository `_. + + +TESPy connections +================= + +This section provides an overview of the parametrisation of connections, the usage of references and busses (connections for energy flow). + +Parametrisation +--------------- + +As mentioned in the introduction, for each connection you can specify the following parameters: + + * mass flow* (m), + * pressure* (p), + * enthalpy* (h), + * temperature* (T), + * vapour mass fraction for pure fluids (x), + * a fluid vector (fluid) and + * a balance closer for the fluid vector (fluid_balance). + +It is possible to specify values, starting values, references and data containers. The data containers for connections are dc_prop for fluid properties (mass flow, pressure, enthalpy, temperature and vapour mass fraction) +and dc_flu for fluid composition. You need to import the :code:`hlp` module, if you want to specify data_containers. + +.. code-block:: python + + # set pressure and vapour mass fraction by value, temperature and enthalpy analogously + myconn.set_attr(p=7, x=0.5) + + # set starting values for mass flow, pressure and enthalpy (has no effect on temperature and vapour mass fraction!) + myconn.set_attr(m0=10, p0=15, h0=100) + + # do the same with a data container + myconn.set_attr(p=hlp.dc_prop(val=7, val_set=True), x=hlp.dc_prop(val=0.5, val_set=True)) + myconn.set_attr(m=hlp.dc_prop(val0=10), p=hlp.dc_prop(val0=15), h=hlp.dc_prop(val0=100)) + + # specify a value in a different unit for a specific parameter + myconn.set_attr(p=hlp.dc_prop(val=7, val_set=True, unit='MPa', unit_set=True) + + # specify a referenced value: pressure of myconn is 1.2 times pressure at myotherconn minus 5 Pa (always SI unit here) + myconn.set_attr(p=con.ref(myotherconn, 1.2, -5)) + + # specify value and reference at the same time + myconn.set_attr(p=hlp.dc_prop(val=7, val_set=True, ref=con.ref(myotherconn, 1.2, -5), ref_set=True)) + + # unset value and reference + myconn.set_attr(p=np.nan) + myconn.p.set_attr(val_set=False, ref_set=False) + +If you want to specify the fluid vector you can do it in the following way: + +.. code-block:: python + + # set both elements of the fluid vector + myconn.set_attr(fluid={'water': 1, 'air': 0}) + # same thing, but using data container + myconn.set_attr(fluid=dc_flu(val={'water': 1, 'air': 0}, val_set:{'water': True, 'air': True})) + + # set starting values + myconn.set_attr(fluid0={'water': 1, 'air': 0}) + # same thing, but using data container + myconn.set_attr(fluid=dc_flu(val0={'water': 1, 'air': 0})) + + # unset values + myconn.fluid.set_attr(val_set={'water': False, 'air': False}) + +References can not be used for fluid composition at the moment! + + +Busses +------ + +Busses can be used to add up the power of different turbomachinery or to add up heat flow of different heat exchangers within your network. +The handling is very similar to connections and components. You need to add components to your busses as a list containing the component object and a factor, the power of the component will be multiplied with. +Do not forget to add the busses to you network. + +This can be used for easy post processing, e. g. to calculate thermal efficiency or you can build up relations between components in your network. +If you want to use the busses for postprocessing only, you do not specify the sum of the power or heat flux on your bus. +If you set a value for P (equal parameter for heat flux or power), an additional equation will be added to your network. +This could be useful, e. g. for establishing relations between different components, for instance when using a steam turbine powered feed water pump. +In the code example the power of the turbine and the feed water pump is added up and set to zero, as the turbines and feed water pumps power have to be equal in absolute value but have different sign. +The sign can be manipulated, e. g. in order to design two turbines with equal power output. + +.. code-block:: python + + from tespy import nwk, con + + ... + + fwp_bus = con.bus('feed water pump', P=0) # set a value for the total power on this bus. + fwp_bus.add_comps([turbine_fwp, 1], [fwp, 1]) + + turbine_bus = con.bus('turbines', P=0) # set a value for the total power on this bus + turbine_bus.add_comps([turbine_hp, 1], [turbine_lp, -1]) + # the values for the busses power can be altered by using .set_attr() + + power = con.bus('power output') # bus for postprocessing, no power (or heat flux) specified + power.add_comps([turbine_hp, 1], [turbine_lp, 1]) + + my_network.add_busses(fwp_bus, turbine_bus, power) + +Two labels for busses have a predefined function in the postprocessing analysis: 'P_res' and 'Q_diss'. +If you specify these labels for your busses, 'P_res' will be interpreted as the total power of your process and 'Q_diss' as total amount of dissipated heat flow (from the process, not internally). +Given these key figures, thermal efficiency or COP will be calculated and an entropy analysis for your systems components will be performed.* + +*Planned feature, not implemented yet! + + +How can TESPy contribute to your energy system calculations? +============================================================ + +In this part you learn how you can use TESPy for your energy system calculations: In energy system calculations, for instance in oemof-solph, plants are usually modelled as abstract components on a much lower level of detail. +In order to represent a plant within an abstract component it is possible to supply characteristics establishing a connection between your energy system model and a specific plant model. +Thus the characteristics are a representation of a specific plant layout in terms of topology and process parameters. In the examples section we have an example of a heat pump COP at different loads and ambient temperatures +as well as a CHP unit with backpressure turbine operating at different loads and varying feed flow temperatures of a heating system. -If you want to add even more flexibility, you might need to manipulate the :code:`__init__()` method. For example, if you want a variable number of inlets and outlets because you have a variable number of components groups within your subsystem, you may introduce an attribute which is set on initialisation and lets you create and parametrize components and connections generically. This might be very interesting for district heating systems, turbines with several sections of equal topology, etc.. .. _tespy_fluid_properties_label: @@ -657,12 +998,15 @@ The basic fluid properties are handled by `CoolProp `_ Pure and pseudo-pure fluids --------------------------- -If you use pure fluids, TESPy directly uses CoolProp functions to gather all fluid properties. CoolProp covers the most important fluids such as water, air as a pseudo-pure fluid as well as its components, several fuels and refrigerants etc.. Look for the aliases in the `list of fluids `_. All fluids provided in this list cover liquid and gaseous state and the two-phase region. +If you use pure fluids, TESPy directly uses CoolProp functions to gather all fluid properties. +CoolProp covers the most important fluids such as water, air as a pseudo-pure fluid as well as its components, several fuels and refrigerants etc.. +Look for the aliases in the `list of fluids `_. All fluids provided in this list cover liquid and gaseous state and the two-phase region. Incompressible fluids --------------------- -If you are looking for heat transer fluids, the `list of incompressible fluids `_ might be interesting for you. In contrast to the pure fluids, the properties cover liquid state only. +If you are looking for heat transer fluids, the `list of incompressible fluids `_ might be interesting for you. +In contrast to the pure fluids, the properties cover liquid state only. Fluid mixtures -------------- @@ -672,9 +1016,12 @@ CoolProp provides fluid properties for two component mixtures. BUT: These are NO Ideal mixtures of gaseous fluids ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -TESPy can handle mixtures of gaseous fluids, by using the single fluid properties from CoolProp together with corresponding equations for mixtures. The equations can be found in the :py:mod:`tespy.helpers module ` and are applied automatically to the fluid vector. +TESPy can handle mixtures of gaseous fluids, by using the single fluid properties from CoolProp together with corresponding equations for mixtures. +The equations can be found in the :py:mod:`tespy.helpers module ` and are applied automatically to the fluid vector. Other mixtures ^^^^^^^^^^^^^^ -It is NOT POSSIBLE to use mixtures of liquid and other liquid or gaseous fluids AT THE MOMENT! If you try to use a mixture of two liquid or gaseous fluids and liquid fluids, e. g. water and methanol or liquid water and air, the equations will still be applied, but obviously return bad values. If you have ideas for the implementation of new kinds of mixtures we appreciate you contacting us. +It is **not possible** to use mixtures of liquid and other liquid or gaseous fluids **at the moment**! +If you try to use a mixture of two liquid or gaseous fluids and liquid fluids, e. g. water and methanol or liquid water and air, the equations will still be applied, but obviously return bad values. +If you have ideas for the implementation of new kinds of mixtures we appreciate you contacting us. From abfeae6c5d0c70e912c7c65cefb157cef76a993b Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 14 Aug 2018 15:49:14 +0200 Subject: [PATCH 41/63] modification in newton printout and combustion chamber convergence check --- tespy/components/components.py | 8 ++++---- tespy/helpers.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index e9dfe375a..efed0597a 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -3332,14 +3332,14 @@ def convergence_check(self, nw): o.fluid.val[f] = 0.05 elif f == self.co2: - if o.fluid.val[f] > 0.10: - o.fluid.val[f] = 0.10 + if o.fluid.val[f] > 0.075: + o.fluid.val[f] = 0.075 if o.fluid.val[f] < 0.02: o.fluid.val[f] = 0.02 elif f == self.h2o: - if o.fluid.val[f] > 0.10: - o.fluid.val[f] = 0.10 + if o.fluid.val[f] > 0.075: + o.fluid.val[f] = 0.075 if o.fluid.val[f] < 0.02: o.fluid.val[f] = 0.02 diff --git a/tespy/helpers.py b/tespy/helpers.py index 618542922..dc587db41 100644 --- a/tespy/helpers.py +++ b/tespy/helpers.py @@ -584,8 +584,8 @@ def newton(func, deriv, params, k, **kwargs): i += 1 if i > imax: - print('Newton algorithm was not able to find a feasible' - 'value for function ' + str(func) + '.') +# print('Newton algorithm was not able to find a feasible ' +# 'value for function ' + str(func) + '.') break From 4f5ccc3e148187b993a25687a7973bbc2e56ce93 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 14 Aug 2018 15:57:19 +0200 Subject: [PATCH 42/63] added combined cycle example --- doc/getting_started.rst | 1 + doc/getting_started/ccbp.rst | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 doc/getting_started/ccbp.rst diff --git a/doc/getting_started.rst b/doc/getting_started.rst index ac6425ecd..117773acc 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -23,6 +23,7 @@ You can find all examples in the `tespy examples github repository `. + +.. figure:: https://github.com/fwitte/tespy_examples/blob/master/ccbp/flow_diagram.svg + :align: center + Figure: Topology of the chp unit. + +The example file handles the plant's design as well as the offdesign performance. From ea99879251fd252f45ddb75a3fb94f64a2dafd69 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 14 Aug 2018 16:21:07 +0200 Subject: [PATCH 43/63] modified docs to fix errors --- doc/getting_started.rst | 18 +++++++++--------- doc/getting_started/basic.rst | 5 ++++- doc/getting_started/ccbp.rst | 3 ++- doc/getting_started/chp.rst | 11 +++++++++-- doc/getting_started/district_heating.rst | 7 ++++++- doc/getting_started/heat_pump.rst | 3 ++- .../tutorial_combustion_chamber.rst | 4 ++-- doc/getting_started/tutorial_heat_pump.rst | 8 ++++---- doc/index.rst | 2 +- doc/using_tespy.rst | 9 +++++++-- 10 files changed, 46 insertions(+), 24 deletions(-) diff --git a/doc/getting_started.rst b/doc/getting_started.rst index 117773acc..2133e81f6 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -18,14 +18,14 @@ You can find all examples in the `tespy examples github repository `. +This example provides a model for a basic clausius rankine cycle. +The process flow diagram is shown in the image below, the source code can be found at the `tespy_examples repository `_. .. figure:: https://github.com/fwitte/tespy_examples/blob/master/basic/flow_diagram.svg :align: center + Figure: Topology of the basic clausius rankine cycle. The basic clausius rankine cycle is built up of a steam turbine, a condenser, the feed water pump and the steam generator. The ideal process' isentropic efficiencies of the steam turbine and the pump are at a value of 100 %, and pressure losses in the condenser and the steam generator are non-existent, which would result in the thermal efficiency beeing equal to the carnot efficiency. For this example realistic figures have been chosen. @@ -33,6 +35,7 @@ After the plant design an offdesign calculation with 90 % rated power is perform .. figure:: https://github.com/fwitte/tespy_examples/blob/master/basic/efficiency.svg :align: center + Figure: Efficiency of the basic clausius rankine cycle in design case. For a good start you can try to modify or exchange parameters. E. g. adjust the value of the upper terminal temperature difference at the condenser, or replace this parameter with a pressure at the turbine's outlet. In oder to get more familiar with how TESPy works you could try to insert more components, maybe add an extraction of the steam turbine for preheating the feed water. It is strongly recommended to add new components bit per bit, troubleshooting is much easier this way. diff --git a/doc/getting_started/ccbp.rst b/doc/getting_started/ccbp.rst index 8f9dd4004..251eb2d48 100644 --- a/doc/getting_started/ccbp.rst +++ b/doc/getting_started/ccbp.rst @@ -5,10 +5,11 @@ combined cycle with backpressure turbine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Another example for chp units, this one is a combined cycle power plant using a backpressure steam turbine. Additionally to the heat extraction at the steam turbine condenser, -the distrcit heating water extracts energy from the waste heat of the waste heat steam generator. You will find the source code :download:`here `. +the distrcit heating water extracts energy from the waste heat of the waste heat steam generator. You will find the source code `here `_. .. figure:: https://github.com/fwitte/tespy_examples/blob/master/ccbp/flow_diagram.svg :align: center + Figure: Topology of the chp unit. The example file handles the plant's design as well as the offdesign performance. diff --git a/doc/getting_started/chp.rst b/doc/getting_started/chp.rst index 7b2c25748..6bc13b4c1 100644 --- a/doc/getting_started/chp.rst +++ b/doc/getting_started/chp.rst @@ -4,15 +4,22 @@ CHP with backpressure turbine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We have set up a simple combined heat and power unit for this example. A backpressure steam turbine is operated with steam extraction for preheating purposes. You will find the source code :download:`here `. +We have set up a simple combined heat and power unit for this example. A backpressure steam turbine is operated with steam extraction for preheating purposes. +You will find the source code `here `_. .. figure:: https://github.com/fwitte/tespy_examples/blob/master/chp/flow_diagram.svg :align: center + Figure: Topology of the chp unit. -At first, a plant design is chosen: The unit provides a total power of 5 MW and heating at a temperature of 110 °C for the feed flow. After that, the temperature at feed flow and live steam mass flow are altered (70 °C to 120 °C and 60 % to 105 % in respect to design mass flow) to cover the unit's range of operation. Thus, the calculation mode is switched to offdesign and the temperature and mass flow are altered in two embedded loops. The latest calculated case of the same mass flow is selected for initialisation in order to archieve better and faster convergence. The results are saved to .csv-files and the following plot of backpressure lines will be created. +At first, a plant design is chosen: The unit provides a total power of 5 MW and heating at a temperature of 110 °C for the feed flow. +After that, the temperature at feed flow and live steam mass flow are altered (70 °C to 120 °C and 60 % to 105 % in respect to design mass flow) to cover the unit's range of operation. +Thus, the calculation mode is switched to offdesign and the temperature and mass flow are altered in two embedded loops. +The latest calculated case of the same mass flow is selected for initialisation in order to archieve better and faster convergence. +The results are saved to .csv-files and the following plot of backpressure lines will be created. .. figure:: https://github.com/fwitte/tespy_examples/blob/master/chp/PQ_diagram.svg :align: center + Figure: Backpressure lines of a CHP unit. diff --git a/doc/getting_started/district_heating.rst b/doc/getting_started/district_heating.rst index bcc1531b4..9b824c3f0 100644 --- a/doc/getting_started/district_heating.rst +++ b/doc/getting_started/district_heating.rst @@ -6,10 +6,12 @@ Distric heating system The district heating system is a great example for the usage of flexible user-defined subsystems. The example system and data are based on the district heating system Hamburg Wilhelmsburg [1]. The source code for this example can be found `here `_. -Although the structure of the system (see the Figure below) does not seem very complex, it has more than 120 components. But we can easily determine repeating structures for the consumers and this is, where the subsystems come in place. +Although the structure of the system (see the Figure below) does not seem very complex, it has more than 120 components. +But we can easily determine repeating structures for the consumers and this is, where the subsystems come in place. .. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/flow_diagram.svg :align: center + Figure: Topology of the heating system. The single consumers are connected to the main grid with a controle valve at the outlet and each fork is connected with a pipe to the next fork. @@ -18,14 +20,17 @@ Additionally, each branch of the main grid is connected to the upstream part wit .. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/subsys_closed.svg :align: center + Figure: Generic topology of the dead end subsystem. .. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/subsys_open.svg :align: center + Figure: Generic topology of the open subsystem. .. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/forks.svg :align: center + Figure: Generic topology of the forks (variable number of branches). After the system has been set up, we designed the pipes' insulation in a way, that the feed flow the temperature gradient is at 1 K / 100 m and the back flow gradient is at 0.5 K / 100 m. diff --git a/doc/getting_started/heat_pump.rst b/doc/getting_started/heat_pump.rst index 83c008bb8..172860e6d 100644 --- a/doc/getting_started/heat_pump.rst +++ b/doc/getting_started/heat_pump.rst @@ -10,6 +10,7 @@ You will find the source code `in this repository `. +the fluid composition. We will demonstrate how to handle the combustion chamber in a very small, simple example. You can download the full code from the `tespy_examples repository `_. First of all you need to define the network containing all fluid components used for the combustion chamber. **These are at least the fuel, oxygen, carbon-dioxide and water**. For this example we added Argon, and of course - as we are using Air for the combustion - Nitrogen. On top, it is recommended to specify reasonable ranges for the fluid properties. @@ -96,7 +96,7 @@ It is also possible to make modifications on the fluid composition, for example stoichiometric combustion chamber --------------------------------- -The example for the stoichiometric combustion chamber can as well be taken from the :download:`tespy_examples repository `. +The example for the stoichiometric combustion chamber can as well be taken from the `tespy_examples repository `_. Again, the network must have the information, which fluids will be part of the fluid vector. In contrast to the normal combustion chamber, you will need the following fluids: **Air, Fuel and Flue Gas**. For this tutorial we will call them: **"TESPy::myAir", "TESPy::myFuel" and "TESPy::myFuel_fg"**, we will see, why we chose these names for the fluids later. Do not forget to specify the ranges for pressure and temperature. This is a very important stept for this specific component, we will explain later, why it is. diff --git a/doc/getting_started/tutorial_heat_pump.rst b/doc/getting_started/tutorial_heat_pump.rst index c7aeac654..c76f84f23 100644 --- a/doc/getting_started/tutorial_heat_pump.rst +++ b/doc/getting_started/tutorial_heat_pump.rst @@ -14,7 +14,7 @@ Task This tutorial introduces you in how to model a heat pump in TESPy. You can see the plants topology in figure 1. Also, you will find a fully working model in the last chapter of this tutorial. -.. figure:: api/_images/tutorial_heat_pump.svg +.. figure:: https://github.com/fwitte/tespy_examples/tree/master/heat_pump/flow_diagram.svg :align: center Figure 1: Topology of the heat pump. @@ -123,7 +123,7 @@ The last step is to define the fluid's state after the consumer, this is done wi Solve ^^^^^ -After creating the system, we want to solve our network. First, we calculate the design case and directly after we can perform the offdesign calculation at a different value for our key parameter. For general information on the solving process in TESPy and available parameters check the corresponding section in :ref:`Using TESPy `. +After creating the system, we want to solve our network. First, we calculate the design case and directly after we can perform the offdesign calculation at a different value for our key parameter. For general information on the solving process in TESPy and available parameters check the corresponding section in :ref:`Using TESPy `. .. code-block:: python @@ -195,7 +195,7 @@ As we already redefined our variable "ves" to be a vessel instead of a sink (see Parametrization ^^^^^^^^^^^^^^^ -Previous parametrization stays untouched. For the vessel we set the calculation mode to "manual" for the offdesign, otherwise the zeta-value would be fixed for offdesign calculation and flexible pressure adjustments would not be possible on the evaporator side. Regarding the evaporator, we specify pressure ratios on hot and cold side as well as the lower terminal temperature difference. We use the hot side pressure ratio and the lower terminal temperature difference as design parameteres and choose zeta as well as the area independet heat transition coefficient as its offdesign parameters. On top of that, the characteristic function of the evaporator should follow the predefined methods 'EVA_HOT' and 'EVA_COLD'. If you want to learn more about handling characteristic functions you should have a glance at the :ref:`TESPy components section `. The superheater will also use the pressure ratios on hot and cold side. Further we set a value for the upper terminal temperature difference. For the pump we set the isentropic efficiency. +Previous parametrization stays untouched. For the vessel we set the calculation mode to "manual" for the offdesign, otherwise the zeta-value would be fixed for offdesign calculation and flexible pressure adjustments would not be possible on the evaporator side. Regarding the evaporator, we specify pressure ratios on hot and cold side as well as the lower terminal temperature difference. We use the hot side pressure ratio and the lower terminal temperature difference as design parameteres and choose zeta as well as the area independet heat transition coefficient as its offdesign parameters. On top of that, the characteristic function of the evaporator should follow the predefined methods 'EVA_HOT' and 'EVA_COLD'. If you want to learn more about handling characteristic functions you should have a glance at the :ref:`TESPy components section `. The superheater will also use the pressure ratios on hot and cold side. Further we set a value for the upper terminal temperature difference. For the pump we set the isentropic efficiency. .. code-block:: python @@ -315,6 +315,6 @@ Here again, using the saved results from previous calculations is always favoura Further tasks ============= -After successfully modeling the heat pump in design and offdesign cases, you can now start using your model for further calculations. E. g., if you have a time series of required heat flux of your consumer, you can loop over the series and perform offdesign calculation adjusting the heat flux every time. Of course, this is possible with every offdesign parameter. We provide the scripts after each of the three steps of the tutorial: :download:`Step 1 <../tutorial/step_1.py>`, :download:`Step 2 <../tutorial/step_2.py>`, :download:`Step 3 <../tutorial/step_3.py>`. +After successfully modeling the heat pump in design and offdesign cases, you can now start using your model for further calculations. E. g., if you have a time series of required heat flux of your consumer, you can loop over the series and perform offdesign calculation adjusting the heat flux every time. Of course, this is possible with every offdesign parameter. We provide the scripts after each of the three steps of the tutorial: :download:`Step 1 <../../tutorial/step_1.py>`, :download:`Step 2 <../../tutorial/step_2.py>`, :download:`Step 3 <../../tutorial/step_3.py>`. Have fun working with TESPy! diff --git a/doc/index.rst b/doc/index.rst index 05f76660c..1fbec2211 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -15,8 +15,8 @@ Contents: introduction installation + getting_started using_tespy - tutorial developing_tespy whats_new api diff --git a/doc/using_tespy.rst b/doc/using_tespy.rst index ba4213b5c..630678218 100644 --- a/doc/using_tespy.rst +++ b/doc/using_tespy.rst @@ -65,14 +65,14 @@ Now you can start to create the components of the network. Set up components ----------------- -Available components can be found :ref:`here `. If you set up a component you have to specify a (within one network) unique label. +Available components can be found :ref:`here `. If you set up a component you have to specify a (within one network) unique label. Moreover, it is possible to specify parameters for the component, for example power P for a turbine or upper terminal temperature difference ttd_u of a heat exchanger. The full list of parameters for a specific component (e. g. a vessel) is stated in the classes documentation. .. note:: Parameters for components are generally optional. Only the components label and in case you want to use a combustion chamber, the combustion chambers fuel, are mandatory parameters to provide. If an optional parameter is not specified by the user, it will be a result of the plants simulation. In this way, the set of equations a component returns is determined by which parameters you specify. - You can find all equations in the :ref:`components documentation ` as well. The example below shows how to create a component with specific parameters, set or reset and how to unset a parameter: + You can find all equations in the :ref:`components documentation ` as well. The example below shows how to create a component with specific parameters, set or reset and how to unset a parameter: .. code-block:: python @@ -143,6 +143,8 @@ Please be aware, that the execution of the lines of code above will not create a In order to get a good overview of the TESPy functionalities, the following sections will walk you through the different TESPy modules in detail. +.. _using_tespy_networks_label: + TESPy networks ============== @@ -482,6 +484,8 @@ Additionally TESPy can calculate cycle process performance figures for you, if y (add components with heat input/output for total dissipated heat) in your network: Thermal efficiency for a right-handed process, COP for left-handed processes. +.. _using_tespy_components_label: + TESPy components ================ @@ -856,6 +860,7 @@ The connection gets a ref object as attribute, thus it is necessary to look, if For parametrization with specific values simply use :code:`self.conns[3].set_attr(m=self.mass_flow)`. :code:`self.mass_flow` must be a subsystem attribute in this example. .. code-block:: python + def set_conns(self): # set connection parameters From 2158dd8c489f9ae9ff3051eb16619e6a7656e022 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 14 Aug 2018 16:46:36 +0200 Subject: [PATCH 44/63] more fixes in the docs --- doc/api/_images/PQ_diagram.svg | 1807 ----------------- doc/api/_images/basic.svg | 916 +++++++++ doc/api/_images/basic_efficiency.svg | 1043 ++++++++++ doc/api/_images/cc_bp.svg | 1218 +++++++++++ doc/api/_images/{CHP.svg => chp.svg} | 549 +++-- doc/api/_images/chp_PQ.svg | 1579 ++++++++++++++ doc/api/_images/dhs.svg | 745 +++++++ doc/api/_images/dhs_closed.svg | 407 ++++ doc/api/_images/dhs_forks.svg | 289 +++ doc/api/_images/dhs_open.svg | 437 ++++ doc/api/_images/heat_pump.svg | 596 ++++++ doc/api/_images/heat_pump_COP.svg | 1426 +++++++++++++ doc/getting_started.rst | 3 +- doc/getting_started/basic.rst | 8 +- doc/getting_started/ccbp.rst | 7 +- doc/getting_started/chp.rst | 15 +- doc/getting_started/district_heating.rst | 11 +- doc/getting_started/heat_pump.rst | 7 +- .../tutorial_combustion_chamber.rst | 8 +- doc/getting_started/tutorial_heat_pump.rst | 51 +- 20 files changed, 8967 insertions(+), 2155 deletions(-) delete mode 100644 doc/api/_images/PQ_diagram.svg create mode 100644 doc/api/_images/basic.svg create mode 100644 doc/api/_images/basic_efficiency.svg create mode 100644 doc/api/_images/cc_bp.svg rename doc/api/_images/{CHP.svg => chp.svg} (69%) create mode 100644 doc/api/_images/chp_PQ.svg create mode 100644 doc/api/_images/dhs.svg create mode 100644 doc/api/_images/dhs_closed.svg create mode 100644 doc/api/_images/dhs_forks.svg create mode 100644 doc/api/_images/dhs_open.svg create mode 100644 doc/api/_images/heat_pump.svg create mode 100644 doc/api/_images/heat_pump_COP.svg diff --git a/doc/api/_images/PQ_diagram.svg b/doc/api/_images/PQ_diagram.svg deleted file mode 100644 index d627f7f2c..000000000 --- a/doc/api/_images/PQ_diagram.svg +++ /dev/null @@ -1,1807 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/api/_images/basic.svg b/doc/api/_images/basic.svg new file mode 100644 index 000000000..d0e2192c2 --- /dev/null +++ b/doc/api/_images/basic.svg @@ -0,0 +1,916 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + G + + + + + + + + + + + + + + diff --git a/doc/api/_images/basic_efficiency.svg b/doc/api/_images/basic_efficiency.svg new file mode 100644 index 000000000..081cd4e24 --- /dev/null +++ b/doc/api/_images/basic_efficiency.svg @@ -0,0 +1,1043 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/api/_images/cc_bp.svg b/doc/api/_images/cc_bp.svg new file mode 100644 index 000000000..4e2047d09 --- /dev/null +++ b/doc/api/_images/cc_bp.svg @@ -0,0 +1,1218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + G + + + + + + + + + + + + + + + + + + + + + G + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/api/_images/CHP.svg b/doc/api/_images/chp.svg similarity index 69% rename from doc/api/_images/CHP.svg rename to doc/api/_images/chp.svg index ba3082e39..4cbf0cbe2 100644 --- a/doc/api/_images/CHP.svg +++ b/doc/api/_images/chp.svg @@ -10,13 +10,13 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="122.4617mm" - height="91.115463mm" - viewBox="0 0 433.91941 322.85011" + width="93.308426mm" + height="72.726562mm" + viewBox="0 0 330.62041 257.69258" id="svg2" version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="CHP.svg"> + inkscape:version="0.92.3 (2405546, 2018-03-11)" + sodipodi:docname="clausius_rankine.svg"> + transform="scale(-0.6)" /> + style="overflow:visible" + id="marker5320" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow2Mend"> + + + + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" + id="path5014" + inkscape:connector-curvature="0" /> - - - + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> @@ -182,7 +180,7 @@ orient="auto" inkscape:stockid="Arrow2Mend"> @@ -231,7 +229,7 @@ id="path12334" style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" - transform="scale(-0.6,-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> @@ -408,7 +406,7 @@ orient="auto" inkscape:stockid="Arrow2Mend"> @@ -529,8 +527,8 @@ inkscape:stockid="Arrow1Mend"> @@ -546,7 +544,7 @@ id="path4545" style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" - transform="scale(-0.6,-0.6)" + transform="scale(-0.6)" inkscape:connector-curvature="0" /> @@ -614,7 +612,7 @@ clipPathUnits="userSpaceOnUse" id="clipPath542"> @@ -653,7 +651,7 @@ clipPathUnits="userSpaceOnUse" id="clipPath504"> @@ -670,7 +668,7 @@ id="path4568-6" style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" - transform="scale(-0.6,-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> @@ -742,7 +740,7 @@ inkscape:stockid="Arrow2Mend"> @@ -760,7 +758,7 @@ id="path5983" style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" - transform="scale(-0.6,-0.6)" /> + transform="scale(-0.6)" /> + transform="scale(-0.6)" /> + + + + + + + + + + + + + originx="-187.53361" + originy="-398.52406" /> @@ -851,7 +909,7 @@ image/svg+xml - + @@ -859,268 +917,179 @@ inkscape:label="Ebene 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-187.28352,-406.90636)"> + transform="translate(-187.53367,-396.14563)"> + cy="487.04968" + cx="-479.43756" /> G + x="470.07477" + y="496.15002" + id="tspan4364-5-6-1-6-2" + style="font-size:25px;line-height:1.25">G + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccc" /> - - + transform="rotate(-90)" + ry="14.062499" + rx="14.0625" + cy="346.87509" + cx="-621.11224" /> - + sodipodi:nodetypes="cccccc" /> - + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.87500012;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect4680" + width="37.5" + height="60.937504" + x="206.25006" + y="489.86218" /> + + + sodipodi:nodetypes="cc" /> - - - - - + sodipodi:nodetypes="cc" /> + - fs - extr - condenser - preheater - to_hs - from_hs + style="fill:none;stroke:#000000;stroke-width:1.87500015;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#marker5016)" + d="M 403.12509,653.92472 H 496.8751 V 546.11221 h -56.25001" + id="path4980" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + - diff --git a/doc/api/_images/chp_PQ.svg b/doc/api/_images/chp_PQ.svg new file mode 100644 index 000000000..1107b49ad --- /dev/null +++ b/doc/api/_images/chp_PQ.svg @@ -0,0 +1,1579 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/api/_images/dhs.svg b/doc/api/_images/dhs.svg new file mode 100644 index 000000000..230fe0ae9 --- /dev/null +++ b/doc/api/_images/dhs.svg @@ -0,0 +1,745 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + heatgeneration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + industrialarea + housingarea 1 + + + sportscentre + + housingarea 2 + + housingarea 3 + + housingarea 4 + + pipes + + fork + + K1 + K2 + K3 + K4 + + diff --git a/doc/api/_images/dhs_closed.svg b/doc/api/_images/dhs_closed.svg new file mode 100644 index 000000000..a507fbb44 --- /dev/null +++ b/doc/api/_images/dhs_closed.svg @@ -0,0 +1,407 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + connectingpipes + + + + + + + + + + + + + consumer + + + controlvalve + Subsystem 0 + + + + + + + + + + + + Subsystem 1, 2, ... + + Subsystem n + inlet 1 + outlet 1 + + + + + + diff --git a/doc/api/_images/dhs_forks.svg b/doc/api/_images/dhs_forks.svg new file mode 100644 index 000000000..087c33a75 --- /dev/null +++ b/doc/api/_images/dhs_forks.svg @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + controlvalves + + + + + + + + feed flow + back flow + + + + + + + + diff --git a/doc/api/_images/dhs_open.svg b/doc/api/_images/dhs_open.svg new file mode 100644 index 000000000..b8511772f --- /dev/null +++ b/doc/api/_images/dhs_open.svg @@ -0,0 +1,437 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + connectingpipes + + + + + + + + + + + + + consumer + + + controlvalve + Subsystem 0 + + + + + + + + + + + + + + + Subsystem 1, 2, ... + + Subsystem n + inlet 1 + outlet 1 + outlet 2 + inlet 2 + + + + + diff --git a/doc/api/_images/heat_pump.svg b/doc/api/_images/heat_pump.svg new file mode 100644 index 000000000..4c5cc64ea --- /dev/null +++ b/doc/api/_images/heat_pump.svg @@ -0,0 +1,596 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + source intercool + sink intercool + consumerback flow + consumerfeed flow + sink ambient + source ambient + + + + + coolant in + coolant out + condenser + vessel + drum withevaporator + superheater + compressor train + + diff --git a/doc/api/_images/heat_pump_COP.svg b/doc/api/_images/heat_pump_COP.svg new file mode 100644 index 000000000..b9b4f3cf6 --- /dev/null +++ b/doc/api/_images/heat_pump_COP.svg @@ -0,0 +1,1426 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/getting_started.rst b/doc/getting_started.rst index 2133e81f6..885b4d0dc 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -33,7 +33,8 @@ Tutorials ========= We provide two different tutorials for you to better understand how to work with TESPy. -You will learn how to create basic models and get the idea of designing a plant and simulating the offdesign behaviour in the heat pump tutorial (this is the bases for the COP calculation of the heat pump in the examples). +You will learn how to create basic models and get the idea of designing a plant and simulating the offdesign behaviour in the heat pump tutorial +(this is the bases for the COP calculation of the heat pump in the examples). On top of that, we created a tutorial for the usage of the combustion chamber: It is an important component for thermal power plants while beeing a source for many errors in the calculation. .. contents:: `Tutorials` diff --git a/doc/getting_started/basic.rst b/doc/getting_started/basic.rst index 20063d784..ff94b1875 100644 --- a/doc/getting_started/basic.rst +++ b/doc/getting_started/basic.rst @@ -1,13 +1,12 @@ .. _basic_example_label: -~~~~~~~~~~~~~~~~~~~~~~ Clausius rankine cycle -~~~~~~~~~~~~~~~~~~~~~~ +---------------------- This example provides a model for a basic clausius rankine cycle. The process flow diagram is shown in the image below, the source code can be found at the `tespy_examples repository `_. -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/basic/flow_diagram.svg +.. figure:: api/_images/basic.svg :align: center Figure: Topology of the basic clausius rankine cycle. @@ -32,8 +31,7 @@ After the plant design an offdesign calculation with 90 % rated power is perform \text{indices: in = input, out = output} - -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/basic/efficiency.svg +.. figure:: api/_images/basic_efficiency.svg :align: center Figure: Efficiency of the basic clausius rankine cycle in design case. diff --git a/doc/getting_started/ccbp.rst b/doc/getting_started/ccbp.rst index 251eb2d48..7c0c213a4 100644 --- a/doc/getting_started/ccbp.rst +++ b/doc/getting_started/ccbp.rst @@ -1,15 +1,14 @@ .. _chp_example_label: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ combined cycle with backpressure turbine -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------------------- Another example for chp units, this one is a combined cycle power plant using a backpressure steam turbine. Additionally to the heat extraction at the steam turbine condenser, the distrcit heating water extracts energy from the waste heat of the waste heat steam generator. You will find the source code `here `_. -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/ccbp/flow_diagram.svg +.. figure:: api/_images/cc_bp.svg :align: center - + Figure: Topology of the chp unit. The example file handles the plant's design as well as the offdesign performance. diff --git a/doc/getting_started/chp.rst b/doc/getting_started/chp.rst index 6bc13b4c1..6ab99733c 100644 --- a/doc/getting_started/chp.rst +++ b/doc/getting_started/chp.rst @@ -1,15 +1,15 @@ .. _chp_example_label: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CHP with backpressure turbine -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------------- We have set up a simple combined heat and power unit for this example. A backpressure steam turbine is operated with steam extraction for preheating purposes. You will find the source code `here `_. -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/chp/flow_diagram.svg +.. figure:: api/_images/chp.svg :align: center - + Figure: Topology of the chp unit. At first, a plant design is chosen: The unit provides a total power of 5 MW and heating at a temperature of 110 °C for the feed flow. @@ -17,9 +17,8 @@ After that, the temperature at feed flow and live steam mass flow are altered (7 Thus, the calculation mode is switched to offdesign and the temperature and mass flow are altered in two embedded loops. The latest calculated case of the same mass flow is selected for initialisation in order to archieve better and faster convergence. The results are saved to .csv-files and the following plot of backpressure lines will be created. - - -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/chp/PQ_diagram.svg - :align: center + +.. figure:: api/_images/chp_PQ.svg + :align: center Figure: Backpressure lines of a CHP unit. diff --git a/doc/getting_started/district_heating.rst b/doc/getting_started/district_heating.rst index 9b824c3f0..f264d9760 100644 --- a/doc/getting_started/district_heating.rst +++ b/doc/getting_started/district_heating.rst @@ -1,15 +1,14 @@ .. _dh_example_label: -~~~~~~~~~~~~~~~~~~~~~~ Distric heating system -~~~~~~~~~~~~~~~~~~~~~~ +---------------------- The district heating system is a great example for the usage of flexible user-defined subsystems. The example system and data are based on the district heating system Hamburg Wilhelmsburg [1]. The source code for this example can be found `here `_. Although the structure of the system (see the Figure below) does not seem very complex, it has more than 120 components. But we can easily determine repeating structures for the consumers and this is, where the subsystems come in place. -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/flow_diagram.svg +.. figure:: api/_images/dhs.svg :align: center Figure: Topology of the heating system. @@ -18,17 +17,17 @@ The single consumers are connected to the main grid with a controle valve at the Also, the main grid may have a dead end (e. g. in the housing areas, see subsystem closed) or is open to connect to another part of the grid (industrial area, see subsystem open). Additionally, each branch of the main grid is connected to the upstream part with the fork subsystem (Ki, see subsystem fork). -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/subsys_closed.svg +.. figure:: api/_images/dhs_closed.svg :align: center Figure: Generic topology of the dead end subsystem. -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/subsys_open.svg +.. figure:: api/_images/dhs_open.svg :align: center Figure: Generic topology of the open subsystem. -.. figure:: https://github.com/fwitte/tespy_examples/blob/master/district_heating/forks.svg +.. figure:: api/_images/dhs_forks.svg :align: center Figure: Generic topology of the forks (variable number of branches). diff --git a/doc/getting_started/heat_pump.rst b/doc/getting_started/heat_pump.rst index 172860e6d..3c6994190 100644 --- a/doc/getting_started/heat_pump.rst +++ b/doc/getting_started/heat_pump.rst @@ -1,14 +1,13 @@ .. _heat_pump_cop_label: -~~~~~~~~~~~~~~~~~~ COP of a heat pump -~~~~~~~~~~~~~~~~~~ +------------------ This example is based on the :ref:`heat pump tutorial ` and shows how to calculate the COP of a heat pump at different ambient temperatures and different loads of the plant. The idea is very similar to the :ref:`CHP example `, thus you should have a look at the tutorial and the CHP example first. You will find the source code `in this repository `_. -.. figure:: https://github.com/fwitte/tespy_examples/tree/master/heat_pump/flow_diagram.svg +.. figure:: api/_images/heat_pump.svg :align: center Figure: Topology of the heat pump unit. @@ -18,7 +17,7 @@ Generally, if you are performing offdesign calculation, keep in mind, that a goo from the calculation of the same load and the nearest ambient temperature possible (in this case always the calculation one step before). This helps the algorithm to stabilize and find a solution. If you skip out on a large range of temperature or power, you might run into convergence issues. The figure below shows the COP of the heat pump for the different temperature levels and at different loads. -.. figure:: https://github.com/fwitte/tespy_examples/tree/master/heat_pump/COP.svg +.. figure:: api/_images/heat_pump_COP.svg :align: center Figure: COP of the heat pump. diff --git a/doc/getting_started/tutorial_combustion_chamber.rst b/doc/getting_started/tutorial_combustion_chamber.rst index 1ebd493f5..24c66e3fa 100644 --- a/doc/getting_started/tutorial_combustion_chamber.rst +++ b/doc/getting_started/tutorial_combustion_chamber.rst @@ -1,6 +1,6 @@ -~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Combustion Chamber Tutorial -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------- .. contents:: :depth: 1 @@ -10,7 +10,7 @@ Combustion Chamber Tutorial There are two different types of combustion chambers available. The combustion chamber can handle varying fluid compositions for the air and the fuel and calculates the fluid composition of the flue gas. Thus, it is possible to e. g. specify the oxygen mass fraction in the flue gas in a calculation. In contrast, the stoichiometric combustion chamber uses fuel and air as pseudo pure gases for the input at calculates a mixture of stoichiometric flue gas and air at the outlet. The sacrifice of flexibility for parametrisation results in a faster solution process. Thus, if the air composition and the fuel composition are known prior to calculation, it is always recommended to use the stoichiometric combustion chamber. We provide a tutorial for both components, where you learn how they work, and what the differences are. combustion chamber ------------------- +^^^^^^^^^^^^^^^^^^ The combustion chamber is an important component within thermal power plants, but unfortunately is the reason for many issues, as the solving algorithm is very sensitive to small changes e. g. the fluid composition. We will demonstrate how to handle the combustion chamber in a very small, simple example. You can download the full code from the `tespy_examples repository `_. @@ -94,7 +94,7 @@ Of course, you can change the parametrisation in any desired way. For example in It is also possible to make modifications on the fluid composition, for example stating the oxygen content of the flue gas. stoichiometric combustion chamber ---------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The example for the stoichiometric combustion chamber can as well be taken from the `tespy_examples repository `_. diff --git a/doc/getting_started/tutorial_heat_pump.rst b/doc/getting_started/tutorial_heat_pump.rst index c76f84f23..293334e2d 100644 --- a/doc/getting_started/tutorial_heat_pump.rst +++ b/doc/getting_started/tutorial_heat_pump.rst @@ -1,8 +1,7 @@ .. _heat_pump_tutorial_label: -~~~~~~~~~~~~~~~~~~ Heat pump tutorial -~~~~~~~~~~~~~~~~~~ +------------------ .. contents:: :depth: 1 @@ -10,11 +9,11 @@ Heat pump tutorial :backlinks: top Task -==== +^^^^ This tutorial introduces you in how to model a heat pump in TESPy. You can see the plants topology in figure 1. Also, you will find a fully working model in the last chapter of this tutorial. -.. figure:: https://github.com/fwitte/tespy_examples/tree/master/heat_pump/flow_diagram.svg +.. figure:: api/_images/heat_pump.svg :align: center Figure 1: Topology of the heat pump. @@ -24,7 +23,7 @@ Generally, if systems are getting more complex, it is highly recommended to set Set up a Network -================ +^^^^^^^^^^^^^^^^ In order to simulate our heat pump we have to create an instance of the tespy.network class. The network is the main container of the model and will be required in all following sections. First, it is necessary to specify a list of the fluids used in the plant. In this example we will work with water (H\ :sub:`2`\O) and ammonia (NH\ :sub:`3`\). Water is used for the cold side of the heat exchanger, for the consumer and for the hot side of the environmental temperature. Ammonia is used as coolant within the heat pump circuit. @@ -40,14 +39,11 @@ Further it is possible to choose a unit system and a value range for mass flow, We suggest using °C, bar and kJ/kg as units, and set the pressure range from 0.1 bar to 100 bar, temperature range from 1 °C to 500 °C, enthalpy range from 10 kJ/kg to 5000 kJ/kg . -Modeling the heat pump -====================== - -Consumer system ---------------- +Modeling the heat pump: Consumer system +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Components -^^^^^^^^^^ +++++++++++ We will start with the consumer as the plant will be designed to deliver a specific heat flux. From figure 1 you can determine the components of the consumer system: condenser, pump and the consumer (heat-exchanger-simple). Additionally we need a source and a sink for the consumer and the heat pump circuit respectively. We label the sink for the coolant "vessel", as for our next calculation the vessel (labeled "vessel") will be attached there. In this way, the fluid properties can be initialised by csv at the interface-connection, too. @@ -68,7 +64,7 @@ We will start with the consumer as the plant will be designed to deliver a speci cons = cmp.heat_exchanger_simple('consumer') Connections -^^^^^^^^^^^ ++++++++++++ In the next steps we will connect the components in order to form a network. Every connection requires the source, the source id, the target and the target id as arguments: the source is the component from which the connection originates, the source id is the outlet id of that component. This applies analogously to the target. To find all inlet and outlet ids of a component look up the class documentation. @@ -93,7 +89,7 @@ In the next steps we will connect the components in order to form a network. Eve Parametrization -^^^^^^^^^^^^^^^ ++++++++++++++++ For the condenser we set pressure ratios on hot and cold side and additionally we set a value for the upper terminal temperature difference. The consumer will have a pressure ratio, too. Further we set the isentropic efficiency for the pump and as the pump is in automatic mode, the offdesign efficiency is calculated with a characteristic function. In offdesign calculation the consumer's pressure ratio will be a function of the mass flow, thus as offdesign parameter we select zeta. The most important parameter is the consumers heat flux. We marked this setting as key parameter. @@ -121,7 +117,7 @@ The last step is to define the fluid's state after the consumer, this is done wi cons.set_attr(Q=-230e3) Solve -^^^^^ ++++++ After creating the system, we want to solve our network. First, we calculate the design case and directly after we can perform the offdesign calculation at a different value for our key parameter. For general information on the solving process in TESPy and available parameters check the corresponding section in :ref:`Using TESPy `. @@ -139,12 +135,12 @@ After creating the system, we want to solve our network. First, we calculate the Vessel and evaporator system ----------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Next we will add the vessel and the evaporator system to our existing network. Components -^^^^^^^^^^ +++++++++++ This part contains of a vessel followed by a drum with evaporator in forced flow and a superheater. Do not forget to change the old sink labeled "vessel" to an actual vessel and the sink used in the previous calculation will represent the first compressor, labeled "compressor 1". Add the following components to the script. @@ -166,7 +162,7 @@ This part contains of a vessel followed by a drum with evaporator in forced flow pu = cmp.pump('pump evaporator') Connections -^^^^^^^^^^^ ++++++++++++ As we already redefined our variable "ves" to be a vessel instead of a sink (see above), we do not need any adjustments to the connection between the condenser and the former sink "cd_ves". The vessel connects to the drum at the inlet 'in1'. The pump of the forced flow evaporation system connects to the drum's outlet 'out1', the evaporator's cold side connects to the drum's inlet 'in2' and the superheater's cold side connects to the drum's outlet 'out2'. This will add the following connections to the model: @@ -193,7 +189,7 @@ As we already redefined our variable "ves" to be a vessel instead of a sink (see su_cp1 = con.connection(su, 'out2', cp1, 'in1') Parametrization -^^^^^^^^^^^^^^^ ++++++++++++++++ Previous parametrization stays untouched. For the vessel we set the calculation mode to "manual" for the offdesign, otherwise the zeta-value would be fixed for offdesign calculation and flexible pressure adjustments would not be possible on the evaporator side. Regarding the evaporator, we specify pressure ratios on hot and cold side as well as the lower terminal temperature difference. We use the hot side pressure ratio and the lower terminal temperature difference as design parameteres and choose zeta as well as the area independet heat transition coefficient as its offdesign parameters. On top of that, the characteristic function of the evaporator should follow the predefined methods 'EVA_HOT' and 'EVA_COLD'. If you want to learn more about handling characteristic functions you should have a glance at the :ref:`TESPy components section `. The superheater will also use the pressure ratios on hot and cold side. Further we set a value for the upper terminal temperature difference. For the pump we set the isentropic efficiency. @@ -224,18 +220,18 @@ Next step is the connetion parametrization: The pressure in the drum and the ent ev_amb_out.set_attr(T=9) Solve -^^^^^ ++++++ Again, you should calculate your network after you added these parts. As we have already calculated one part of our network, this time we can use the :code:`init_file` for the design calculation and load the results from the previous network. This step is not required, but in larger, more complex networks, it might help, to archieve better convergence. For the offdesign calculation see part 3.1.4. Compressor system ------------------ +^^^^^^^^^^^^^^^^^ To complete the heat pump, we will add the compressor system to our existing network. Components -^^^^^^^^^^ +++++++++++ This part contains two compressors with an intercooler between them. The cold side of the intercooler requires a source and a sink. Again, remember redefining the former sink "cp1" to a compressor and add a sink for the outlet of the coolant after the compressor system. @@ -255,7 +251,7 @@ This part contains two compressors with an intercooler between them. The cold si he = cmp.heat_exchanger('heat_exchanger') Connections -^^^^^^^^^^^ ++++++++++++ As done before, add the new connections to the script. After the second compressor we need to install a sink, because closing a circuit will always lead to linear dependency. Just make sure, the fluid properties at the sink after the compressor are identical to the fluid properties at the source connected to the condenser. Another way of doing this, is adding a merge and a splitter at some point of your network. Nevertheless, you will require a sink and a source. @@ -273,7 +269,7 @@ As done before, add the new connections to the script. After the second compress nw.add_conns(cp1_he, he_cp2, ic_in_he, he_ic_out, cp2_c_out) Parametrization -^^^^^^^^^^^^^^^ ++++++++++++++++ For the two compressor we defined an isentropic efficency and for the offdesign calculation the "manual" mode, as we do not want to use the characteristic maps in this tutorial. The first compressor has a fixed pressure ratio, the seconds compressor pressure ratio will result from the required pressure at the condenser. The heat exchanger comes with pressure ratios on both sides. The parametrization of all other components remains identical. @@ -303,7 +299,7 @@ The last step leads to a necessary redefinition of the parametrization of the ex Solve -^^^^^ ++++++ Here again, using the saved results from previous calculations is always favourable, but with the manually adjusted starting values, the calculation should still converge. Also see section 3.2.4. If you want to use the previous part to initialise start the solver with @@ -313,8 +309,11 @@ Here again, using the saved results from previous calculations is always favoura Further tasks -============= +^^^^^^^^^^^^^ -After successfully modeling the heat pump in design and offdesign cases, you can now start using your model for further calculations. E. g., if you have a time series of required heat flux of your consumer, you can loop over the series and perform offdesign calculation adjusting the heat flux every time. Of course, this is possible with every offdesign parameter. We provide the scripts after each of the three steps of the tutorial: :download:`Step 1 <../../tutorial/step_1.py>`, :download:`Step 2 <../../tutorial/step_2.py>`, :download:`Step 3 <../../tutorial/step_3.py>`. +After successfully modeling the heat pump in design and offdesign cases, you can now start using your model for further calculations. +E. g., if you have a time series of required heat flux of your consumer, you can loop over the series and perform offdesign calculation adjusting the heat flux every time. +Of course, this is possible with every offdesign parameter. We provide the scripts after each of the three steps of the tutorial: +:download:`Step 1 <../tutorial/step_1.py>`, :download:`Step 2 <../tutorial/step_2.py>`, :download:`Step 3 <../tutorial/step_3.py>`. Have fun working with TESPy! From f87c35a72bb97d7d81b48598475a223d64b2e415 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 16 Aug 2018 13:31:11 +0200 Subject: [PATCH 45/63] fixed flow diagram --- doc/api/_images/chp.svg | 50 ++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/doc/api/_images/chp.svg b/doc/api/_images/chp.svg index 4cbf0cbe2..a8c397868 100644 --- a/doc/api/_images/chp.svg +++ b/doc/api/_images/chp.svg @@ -11,12 +11,12 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="93.308426mm" - height="72.726562mm" - viewBox="0 0 330.62041 257.69258" + height="80.643715mm" + viewBox="0 0 330.62041 285.74549" id="svg2" version="1.1" inkscape:version="0.92.3 (2405546, 2018-03-11)" - sodipodi:docname="clausius_rankine.svg"> + sodipodi:docname="flow_diagram.svg"> + originx="-187.53359" + originy="-375.09611" /> @@ -909,7 +909,7 @@ image/svg+xml - + @@ -917,7 +917,7 @@ inkscape:label="Ebene 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-187.53367,-396.14563)"> + transform="translate(-187.53366,-391.52072)"> @@ -1014,7 +1014,7 @@ rx="9.3749981" ry="9.3750267" /> @@ -1038,29 +1038,23 @@ x="262.50009" y="602.36224" /> - From 6ad64f44f5c9b25d2a59e742f7dfe51c5f39ab0e Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 16 Aug 2018 13:32:05 +0200 Subject: [PATCH 46/63] fixed equation --- doc/using_tespy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/using_tespy.rst b/doc/using_tespy.rst index 630678218..e850c0caa 100644 --- a/doc/using_tespy.rst +++ b/doc/using_tespy.rst @@ -685,7 +685,7 @@ The equations contain the information on the changes to the fluid properties wit .. math:: - 0 = \dot{m}_{in} - \dot{m}_{out} + 0 = \dot{m}_{in} - \dot{m}_{out}\\ 0 = \dot{p}_{in} - \dot{p}_{out} - \Delta p The connections connected to your component are available as a list in :code:`self.inl` and :code:`self.outl` respectively. From 12d9abf1283c292d53c8c11c334e870678fbdb5a Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 23 Aug 2018 10:42:28 +0200 Subject: [PATCH 47/63] updated docstrings for simple_heat_exchanger, pipe, solar_collecot --- tespy/components/components.py | 50 +++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 845525135..b46df6ee8 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -4340,7 +4340,7 @@ class heat_exchanger_simple(component): r""" **available parameters** - - Q: heat flux, :math:`[Q]=\text{W}` + - Q: heat flow, :math:`[Q]=\text{W}` - pr: outlet to inlet pressure ratio, :math:`[pr]=1` - zeta: geometry independent friction coefficient :math:`[\zeta]=\frac{\text{Pa}}{\text{m}^4}`, also see @@ -4349,8 +4349,8 @@ class heat_exchanger_simple(component): factor is used - D: diameter of the pipes, :math:`[D]=\text{m}` - L: length of the pipes, :math:`[L]=\text{m}` - - ks: pipes roughness , :math:`[ks]=\text{m}` in case of darcy friction, - :math:`[ks]=\text{1}` in case of hazen williams + - ks: pipes roughness, :math:`[ks]=\text{m}` for darcy friiction + , :math:`[ks]=\text{1}` for hazen-williams equation - kA: area independent heat transition coefficient, :math:`[kA]=\frac{\text{W}}{\text{K}}` - t_a: ambient temperature, provide parameter in network's temperature unit @@ -4363,7 +4363,7 @@ class heat_exchanger_simple(component): - D, L and ks, if you want to calculate pressure drop from darcy friction factor or hazen williams equation and - - kA and t_a, if you want to calculate the heat flux on basis of the + - kA and t_a, if you want to calculate the heat flow on basis of the ambient conditions **equations** @@ -4958,8 +4958,7 @@ class pipe is an alias of class heat_exchanger_simple **available parameters** - - - Q: heat flux, :math:`[Q]=\text{W}` + - Q: heat flow, :math:`[Q]=\text{W}` - pr: outlet to inlet pressure ratio, :math:`[pr]=1` - zeta: geometry independent friction coefficient :math:`[\zeta]=\frac{\text{Pa}}{\text{m}^4}`, also see @@ -4968,13 +4967,23 @@ class pipe is an alias of class heat_exchanger_simple factor is used - D: diameter of the pipes, :math:`[D]=\text{m}` - L: length of the pipes, :math:`[L]=\text{m}` - - ks: pipes roughness + - ks: pipes roughness, :math:`[ks]=\text{m}` for darcy friiction + , :math:`[ks]=\text{1}` for hazen-williams equation - kA: area independent heat transition coefficient, :math:`[kA]=\frac{\text{W}}{\text{K}}` - t_a: ambient temperature, provide parameter in network's temperature unit - t_a_design: ambient temperature design case, provide parameter in network's temperature unit + .. note:: + for now, it is not possible to make these parameters part of the + variable space. Thus you need to provide + + - D, L and ks, if you want to calculate pressure drop from darcy + friction factor or hazen williams equation and + - kA and t_a, if you want to calculate the heat flow on basis of the + ambient conditions + **equations** see :func:`tespy.components.components.heat_exchager_simple.equations` @@ -5020,16 +5029,26 @@ class solar collector - zeta: geometry independent friction coefficient :math:`[\zeta]=\frac{\text{Pa}}{\text{m}^4}`, also see :func:`tespy.components.components.component.zeta_func` - - D: diameter of the pipes - - L: length of the pipes - - ks: pipes roughness + - D: diameter of the pipes, :math:`[D]=\text{m}` + - L: length of the pipes, :math:`[L]=\text{m}` + - ks: pipes roughness, :math:`[ks]=\text{m}` for darcy friiction + , :math:`[ks]=\text{1}` for hazen-williams equation - E: global solar radiation, :math:`[E] = \frac{\text{W}}{\text{m}^2}` - lkf_lin: linear loss key figure, :math:`[\alpha_1]=\frac{\text{W}}{\text{K} \cdot \text{m}}` - lkf_quad: quadratic loss key figure, :math:`[\alpha_2]=\frac{\text{W}}{\text{K}^2 \cdot \text{m}^2}` - A: collector surface area :math:`[A]=\text{m}^2` - - t_a: ambient temperature + - t_a: ambient temperature, provide parameter in network's temperature unit + + .. note:: + for now, it is not possible to make these parameters part of the + variable space. Thus you need to provide + + - D, L and ks, if you want to calculate pressure drop from darcy + friction factor or hazen williams equation and + - E, A, lkf_lin, lkf_quad and t_a, if you want to calculate the heat + flow on basis of the radiation and ambient conditions **equations** @@ -5182,7 +5201,7 @@ def energy_func(self): T_m = \frac{T_{out} + T_{in}}{2}\\ 0 = \dot{m} \cdot \left( h_{out} - h_{in} \right) - - \left\{E \cdot A - \left(T_m - T_{amb} \right) \cdot A \cdot + A \cdot \left\{E - \left(T_m - T_{amb} \right) \cdot \left[ \alpha_1 + \alpha_2 \cdot A \cdot \left(\ T_m - T_{amb}\right) \right] \right\} """ @@ -5192,11 +5211,10 @@ def energy_func(self): T_m = (T_mix_ph(i) + T_mix_ph(o)) / 2 - return (i[0] * (o[2] - i[2]) - (self.E.val * self.A.val - + return (i[0] * (o[2] - i[2]) - self.A.val * (self.E.val - (T_m - self.t_a.val_SI) * - self.A.val * (self.lkf_lin.val + - self.lkf_quad.val * self.A.val * - (T_m - self.t_a.val_SI)))) + (self.lkf_lin.val + self.lkf_quad.val * self.A.val * + (T_m - self.t_a.val_SI)))) def calc_parameters(self, nw, mode): From 21625da3331fd92f357ba94e912894afb0f1a5b2 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 23 Aug 2018 10:46:20 +0200 Subject: [PATCH 48/63] added solar collector to component list --- doc/using_tespy.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/using_tespy.rst b/doc/using_tespy.rst index e850c0caa..706287839 100644 --- a/doc/using_tespy.rst +++ b/doc/using_tespy.rst @@ -513,6 +513,7 @@ More information on the components can be gathered from the code documentation. * :py:class:`Desuperheater ` (:py:meth:`equations `) * :py:class:`Heat exchanger simple ` (:py:meth:`equations `) * :py:class:`Pipe ` (:py:meth:`equations `) + * :py:class:`Solar collector ` (:py:meth:`equations `) - :py:class:`Drum ` (:py:meth:`equations `) - :py:class:`Subsystem interface ` (:py:meth:`equations `) From ee58e2367fe7fed03579afd91c6a16be601583ea Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 23 Aug 2018 11:10:15 +0200 Subject: [PATCH 49/63] updated what's new for next version --- doc/whats_new.rst | 1 + doc/whats_new/v0-0-4.rst | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 doc/whats_new/v0-0-4.rst diff --git a/doc/whats_new.rst b/doc/whats_new.rst index 41bae08f8..5b09a7a09 100644 --- a/doc/whats_new.rst +++ b/doc/whats_new.rst @@ -8,6 +8,7 @@ Discover noteable new features and improvements in each release :local: :backlinks: top +.. include:: whats_new/v0-0-4.rst .. include:: whats_new/v0-0-3.rst .. include:: whats_new/v0-0-2.rst .. include:: whats_new/v0-0-1.rst diff --git a/doc/whats_new/v0-0-4.rst b/doc/whats_new/v0-0-4.rst new file mode 100644 index 000000000..4f3b67c7e --- /dev/null +++ b/doc/whats_new/v0-0-4.rst @@ -0,0 +1,34 @@ +v0.0.3 (July 05, 2018) +++++++++++++++++++++++ + +New Features +############ +- added new component: solar collector (`b5990e1 `_). +- added a printlevel property for the network and improved the printouts in the calculation process (`44d9f30 https://github.com/oemof/tespy/commit/44d9f3066683107fb314fa1d941e76db377bea71`_). +- improved convergence speed (`9cc1d8f `_) +- improved fluid initialisation (`20ae7b3 `_) + + +Documentation +############# +- improved the online-documentation with new structure and new examples (`fa5e7ca `_, `6ef4d99 `_). +- fixed some typos in different modules and the online-documentation + +Testing +####### + +Bug fixes +######### +- fixed bugs in subsystem logic (initialisation, set_attr, set_conns, `321308f `_) +- fixed bugs in bus.set_attr (`2294261 `_), + h_mix_pQ (`eac08cc `_), + starting values for fluid composition (`e3b2a77 `_, `8f92821 `_) + +Other changes +############# + +Contributors +############ + +- Francesco Witte From 0f13a632f41d7ed07130f81fb00dbc00e4cd8f91 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 23 Aug 2018 11:33:58 +0200 Subject: [PATCH 50/63] adjusted version number --- doc/whats_new/v0-0-4.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/whats_new/v0-0-4.rst b/doc/whats_new/v0-0-4.rst index 4f3b67c7e..9b64f7ffa 100644 --- a/doc/whats_new/v0-0-4.rst +++ b/doc/whats_new/v0-0-4.rst @@ -1,10 +1,10 @@ -v0.0.3 (July 05, 2018) +v0.0.4 (September, 2018) ++++++++++++++++++++++ New Features ############ - added new component: solar collector (`b5990e1 `_). -- added a printlevel property for the network and improved the printouts in the calculation process (`44d9f30 https://github.com/oemof/tespy/commit/44d9f3066683107fb314fa1d941e76db377bea71`_). +- added a printlevel property for the network and improved the printouts in the calculation process (`44d9f30 `_). - improved convergence speed (`9cc1d8f `_) - improved fluid initialisation (`20ae7b3 `_) @@ -22,7 +22,7 @@ Bug fixes - fixed bugs in subsystem logic (initialisation, set_attr, set_conns, `321308f `_) - fixed bugs in bus.set_attr (`2294261 `_), h_mix_pQ (`eac08cc `_), - starting values for fluid composition (`e3b2a77 `_, `8f92821 `_, `8f92821 `_), given heat flow of heat exchangers (`4b24651 `_) Other changes From 82ca0b2db23cc9a5f4955b01b252a3bdca6218e8 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 23 Aug 2018 11:34:44 +0200 Subject: [PATCH 51/63] typo fix --- doc/getting_started/ccbp.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/getting_started/ccbp.rst b/doc/getting_started/ccbp.rst index 7c0c213a4..4a8dc295b 100644 --- a/doc/getting_started/ccbp.rst +++ b/doc/getting_started/ccbp.rst @@ -1,6 +1,6 @@ .. _chp_example_label: -combined cycle with backpressure turbine +Combined cycle with backpressure turbine ---------------------------------------- Another example for chp units, this one is a combined cycle power plant using a backpressure steam turbine. Additionally to the heat extraction at the steam turbine condenser, From 4972d31cdb4a74d0a5bfb9b06cda17d1756417bc Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 23 Aug 2018 13:57:36 +0200 Subject: [PATCH 52/63] adjusted iterinfo printout --- tespy/networks.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index 13d117f85..30073e458 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -1147,8 +1147,8 @@ def solve_loop(self): msg = (str(self.iter + 1)) # should this be f(x_i) or the dx_i? # -> accounts for self.res, too. - msg += '\t| ' + '{:.2e}'.format(norm(self.vec_res)) - if not self.lin_dep: + if not self.lin_dep and not math.isnan(norm(self.vec_res)): + msg += '\t| ' + '{:.2e}'.format(norm(self.vec_res)) msg += ' | ' + '{:.2e}'.format(norm( self.vec_z[0::self.num_vars])) msg += ' | ' + '{:.2e}'.format(norm( @@ -1161,6 +1161,10 @@ def solve_loop(self): msg += ' | ' + '{:.2e}'.format(norm(ls)) else: + if math.isnan(norm(self.vec_res)): + msg += '\t| nan'.format(norm(self.vec_res)) + else: + msg += '\t| ' + '{:.2e}'.format(norm(self.vec_res)) msg += ' | nan' msg += ' | nan' msg += ' | nan' From 95eb92d63f3c2127624ad46a91a1bd0880bfe1b6 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 23 Aug 2018 14:16:11 +0200 Subject: [PATCH 53/63] additions to troubleshooting section --- doc/using_tespy.rst | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/doc/using_tespy.rst b/doc/using_tespy.rst index 706287839..4b9fef5fa 100644 --- a/doc/using_tespy.rst +++ b/doc/using_tespy.rst @@ -446,9 +446,30 @@ If you have provided the correct number of parameters in your system and the cal The first reason can be eleminated by carefully choosing the parametrisation. **A linear dependendy due to bad starting values is often more difficult to resolve and it may require some experience.** In many cases, the linear dependency is caused by equations, that require the **calculation of a temperature**, e. g. specifying a temperature at some point of the network, terminal temperature differences at heat exchangers, etc.. -In this case, **the starting enthalpy should be adjusted in a way, that the fluid state is not within the two-phase region:** The specification of temperature and pressure in a two-phase region does not yield a distict value for the enthalpy. +In this case, **the starting enthalpy and pressure should be adjusted in a way, that the fluid state is not within the two-phase region:** The specification of temperature and pressure in a two-phase region does not yield a distict value for the enthalpy. Even if this specific case appears after some iterations, better starting values often do the trick. +Another frequent error is that fluid properties move out of the bounds given by the fluid property database. The calculation will stop immediately. **Adjusting pressure and enthalpy ranges for the convergence check** might help in this case. + +.. note:: + + If you experience slow convergence or instability within the convergence process, it is sometimes helpful to have a look at the iterinformation. This is printed by default and provides + information on the residuals of your systems' equations and on the increments of the systems' variables. Maybe it is only one variable causing the instability, thus its increment is much larger + than the incerement of the other variables. + + iter | residual | massflow | pressure | enthalpy | fluid + --------+----------+----------+----------+----------+--------- + 1 | 1.12e+07 | 2.97e+01 | 1.43e+07 | 5.59e+06 | 1.75e-15 + 2 | 3.41e+07 | 1.07e+02 | 1.58e+04 | 4.45e+06 | 6.65e-15 + 3 | 2.17e+07 | 6.86e+01 | 7.24e+04 | 8.27e+05 | 1.86e-15 + 4 | 4.46e+06 | 1.87e+01 | 1.59e+05 | 8.46e+05 | 1.17e-16 + 5 | 1.16e+06 | 2.19e+00 | 1.38e+05 | 1.34e+05 | 1.59e-16 + 6 | 2.86e+04 | 7.25e-02 | 3.41e+04 | 3.00e+03 | 3.96e-17 + 7 | 6.32e+02 | 1.40e-03 | 1.25e+03 | 6.98e+01 | 4.01e-17 + 8 | 7.61e-01 | 1.53e-06 | 1.51e+00 | 8.03e-02 | 4.02e-17 + 9 | 1.11e-06 | 1.99e-11 | 2.20e-06 | 1.40e-07 | 8.03e-17 + --------+----------+----------+----------+----------+--------- + Did you experience other errors frequently and have a workaround/tips for resolving them? You are very welcome to contact us and share your experience for other users! Postprocessing From 2da8f256b41167fc7f0dc96ddfff5b023dc9516a Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 23 Aug 2018 14:18:45 +0200 Subject: [PATCH 54/63] removed iterinfo example table --- doc/using_tespy.rst | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/doc/using_tespy.rst b/doc/using_tespy.rst index 4b9fef5fa..cc32cb4d3 100644 --- a/doc/using_tespy.rst +++ b/doc/using_tespy.rst @@ -456,19 +456,6 @@ Another frequent error is that fluid properties move out of the bounds given by If you experience slow convergence or instability within the convergence process, it is sometimes helpful to have a look at the iterinformation. This is printed by default and provides information on the residuals of your systems' equations and on the increments of the systems' variables. Maybe it is only one variable causing the instability, thus its increment is much larger than the incerement of the other variables. - - iter | residual | massflow | pressure | enthalpy | fluid - --------+----------+----------+----------+----------+--------- - 1 | 1.12e+07 | 2.97e+01 | 1.43e+07 | 5.59e+06 | 1.75e-15 - 2 | 3.41e+07 | 1.07e+02 | 1.58e+04 | 4.45e+06 | 6.65e-15 - 3 | 2.17e+07 | 6.86e+01 | 7.24e+04 | 8.27e+05 | 1.86e-15 - 4 | 4.46e+06 | 1.87e+01 | 1.59e+05 | 8.46e+05 | 1.17e-16 - 5 | 1.16e+06 | 2.19e+00 | 1.38e+05 | 1.34e+05 | 1.59e-16 - 6 | 2.86e+04 | 7.25e-02 | 3.41e+04 | 3.00e+03 | 3.96e-17 - 7 | 6.32e+02 | 1.40e-03 | 1.25e+03 | 6.98e+01 | 4.01e-17 - 8 | 7.61e-01 | 1.53e-06 | 1.51e+00 | 8.03e-02 | 4.02e-17 - 9 | 1.11e-06 | 1.99e-11 | 2.20e-06 | 1.40e-07 | 8.03e-17 - --------+----------+----------+----------+----------+--------- Did you experience other errors frequently and have a workaround/tips for resolving them? You are very welcome to contact us and share your experience for other users! From c7366c50b9812054e42f35ee7b8ed91f03e7f4cc Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 13:36:14 +0200 Subject: [PATCH 55/63] moved solar_collector example to examples repository --- examples/solar_collector.py | 93 ------------------------------------- 1 file changed, 93 deletions(-) delete mode 100644 examples/solar_collector.py diff --git a/examples/solar_collector.py b/examples/solar_collector.py deleted file mode 100644 index 7514e2bc6..000000000 --- a/examples/solar_collector.py +++ /dev/null @@ -1,93 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Aug 4 10:37:36 2017 - -@author: witte -""" - -from tespy import con, cmp, nwk -import numpy as np -from matplotlib import pyplot as plt -import pandas as pd - -# %% network - -fluid_list = ['H2O'] -nw = nwk.network(fluids=fluid_list, p_unit='bar', T_unit='C', - p_range=[4, 10], T_range=[10, 200]) - -# %% components - -# sinks & sources -back = cmp.source('to collector') -feed = cmp.sink('from collector') - -# collector -coll = cmp.solar_collector(label='solar thermal collector') - -# %% connections - -b_c = con.connection(back, 'out1', coll, 'in1') -c_f = con.connection(coll, 'out1', feed, 'in1') - -nw.add_conns(b_c, c_f) - -# %% component parameters - -# set combustion chamber fuel, air to stoichometric air ratio and thermal input -coll.set_attr(pr=0.99, Q=8e3, lkf_lin=1, lkf_quad=0.005, A=10, t_a=10) - -# %% connection parameters - -b_c.set_attr(p=5, T=20, fluid={'H2O': 1}) -c_f.set_attr(p0=2, T=120) - -# %% solving - -# going through several parametrisation possibilities -mode = 'design' -nw.solve(mode=mode) -nw.print_results() - -coll.set_attr(Q=7e3, E=9e3) -c_f.set_attr(T=np.nan) - -nw.solve(mode=mode) -nw.print_results() - -coll.set_attr(Q=np.nan, E=np.nan) -c_f.set_attr(T=100, m=1e-2) - -nw.solve(mode=mode) -nw.print_results() - -# looping over different temperature differences (assuming constant mass flow) -# and global radiation () - -c_f.set_attr(m=np.nan) -T_amb = np.linspace(0, 60, 14, dtype=float) -E_glob = np.linspace(100, 1000, 14, dtype=float) - -df = pd.DataFrame(columns=(60 - T_amb)) - -for E in E_glob: - eta = [] - coll.set_attr(E=E) - for T in T_amb: - coll.set_attr(t_a=T) - nw.solve(mode=mode) - eta += [coll.Q.val / (coll.E.val * coll.A.val)] - if eta[-1] < 0: - eta[-1] = np.nan - - df.loc[E] = eta - -E, T = np.meshgrid(60 - T_amb, E_glob) - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') -ax.plot_wireframe(E, T, df.as_matrix()) -ax.set_xlabel('Temperaturdifferenz') -ax.set_ylabel('Globalstrahlung auf die schiefe Ebene') -ax.set_zlabel('Wirkungsgrad (nur thermische Verluste)') -plt.show() From a74038ba8571b6d531958d606246e339a1a383d6 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 14:10:53 +0200 Subject: [PATCH 56/63] added component initialisation for non-changed topologies in design-case calculation --- tespy/networks.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tespy/networks.py b/tespy/networks.py index 30073e458..f417fe020 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -513,7 +513,13 @@ def initialise(self): raise hlp.MyNetworkError(msg) if self.mode == 'offdesign': - self.init_offdesign() # characteristics for offdesign + # characteristics for offdesign + self.init_offdesign() + else: + # component initialisation for design case if no topological + # changes have been applied + for cp in self.comps.index: + cp.comp_init(self) self.init_fluids() # start standard fluid initialisation self.init_properties() # start standard property initialisation From 738dc73431f1605179fcba3e9d6fa96d284e3fa8 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 14:11:37 +0200 Subject: [PATCH 57/63] changed error handling in group parameters -> now warnings --- tespy/components/components.py | 44 +++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index b46df6ee8..340ffbaea 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -4423,11 +4423,14 @@ def comp_init(self, nw): if is_set: self.hydro_group.set_attr(is_set=True) - elif self.hydro_group.is_set: - msg = ('All parameters of the component group have to be ' + elif self.hydro_group.is_set and nw.compwarn: + msg = ('##### WARNING #####\n' + 'All parameters of the component group have to be ' 'specified! This component group uses the following ' - 'parameters: L, ks, D') - raise MyComponentError(msg) + 'parameters: L, ks, D at ' + self.label + '. ' + 'Group will be set to False') + print(msg) + self.hydro_group.set_attr(is_set=False) else: self.hydro_group.set_attr(is_set=False) @@ -4441,11 +4444,14 @@ def comp_init(self, nw): if is_set: self.kA_group.set_attr(is_set=True) - elif self.kA_group.is_set: - msg = ('All parameters of the component group have to be ' + elif self.kA_group.is_set and nw.compwarn: + msg = ('##### WARNING #####\n' + 'All parameters of the component group have to be ' 'specified! This component group uses the following ' - 'parameters: kA, t_a') - raise MyComponentError(msg) + 'parameters: kA, t_a at ' + self.label + '. ' + 'Group will be set to False') + print(msg) + self.kA_group.set_attr(is_set=False) else: self.kA_group.set_attr(is_set=False) @@ -5088,11 +5094,14 @@ def comp_init(self, nw): if is_set: self.hydro_group.set_attr(is_set=True) - elif self.hydro_group.is_set: - msg = ('All parameters of the component group have to be ' + elif self.hydro_group.is_set and nw.compwarn: + msg = ('##### WARNING #####\n' + 'All parameters of the component group have to be ' 'specified! This component group uses the following ' - 'parameters: L, ks, D at ' + self.label) - raise MyComponentError(msg) + 'parameters: L, ks, D at ' + self.label + '. ' + 'Group will be set to False') + print(msg) + self.hydro_group.set_attr(is_set=False) else: self.hydro_group.set_attr(is_set=False) @@ -5107,11 +5116,14 @@ def comp_init(self, nw): if is_set: self.energy_group.set_attr(is_set=True) - elif self.energy_group.is_set: - msg = ('All parameters of the component group have to be ' + elif self.energy_group.is_set and nw.compwarn: + msg = ('##### WARNING #####\n' + 'All parameters of the component group have to be ' 'specified! This component group uses the following ' - 'parameters: E, lkf_lin, lkf_quad, A, t_a at ' + self.label) - raise MyComponentError(msg) + 'parameters: E, lkf_lin, lkf_quad, A, t_a at ' + self.label + + '. Group will be set to False') + print(msg) + self.energy_group.set_attr(is_set=False) else: self.energy_group.set_attr(is_set=False) From e101e3f522a47c315b3a7d031adf6db1bd218259 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 14:49:22 +0200 Subject: [PATCH 58/63] added printout, if maximum iteration count is reached --- tespy/networks.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tespy/networks.py b/tespy/networks.py index f417fe020..fce6ff180 100644 --- a/tespy/networks.py +++ b/tespy/networks.py @@ -1177,8 +1177,6 @@ def solve_loop(self): msg += ' | nan' print(msg) - self.iter += 1 - # stop calculation after rediculous amount of iterations if self.iter > 3 and self.res[-1] < hlp.err ** (1 / 2): break @@ -1189,12 +1187,19 @@ def solve_loop(self): if self.nwkwarn: print('##### WARNING #####\n' 'Convergence is making no progress, calculation ' - 'stopped.') + 'stopped, residual value is ' + '{:.2e}'.format(norm(self.vec_res))) break if self.lin_dep: break + if self.iter == self.max_iter - 1: + print('##### WARNING #####\n' + 'Reached maximum iteration count, calculation ' + 'stopped, residual value is ' + '{:.2e}'.format(norm(self.vec_res))) + def solve_control(self): """ calculation step of newton algorithm From b51ce847bf1cb729402640cec60dd2b3b91793a3 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 16:06:02 +0200 Subject: [PATCH 59/63] added an example for the solar_collector --- doc/api/_images/solar_collector.svg | 1318 +++++++++++++++++++++++ doc/getting_started.rst | 1 + doc/getting_started/solar_collector.rst | 28 + 3 files changed, 1347 insertions(+) create mode 100644 doc/api/_images/solar_collector.svg create mode 100644 doc/getting_started/solar_collector.rst diff --git a/doc/api/_images/solar_collector.svg b/doc/api/_images/solar_collector.svg new file mode 100644 index 000000000..3ace2abc1 --- /dev/null +++ b/doc/api/_images/solar_collector.svg @@ -0,0 +1,1318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/getting_started.rst b/doc/getting_started.rst index 885b4d0dc..98a220194 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -26,6 +26,7 @@ You can find all examples in the `tespy examples github repository `_. + +.. figure:: api/_images/solar_collector.svg + :align: center + + Figure: Topology of the solar collector. + +The solarthermal collector is used to transfer heat from the solar radiation to the collector fluid. +The TESPy component :py:class:`solar_collector ` inherits from the :py:class:`solar_collector ` component. +An energy balance is applied according to the :py:class:`solar_collector ` method, which takes the collector's +* surface area :code:`A`, +* loss key figures :code:`lkf_lin` (linear) and :code:`lkf_quad` (quadratic), +* ambient temperature :code:`t_a` as well as +* area independent absorped energy :code:`E` (radiation on inclined surface minus optical losses) +into account. + +In the script different ways of parametrisation are shown. In the last part a collector is designed and the offdesign performance at different rates of absorption and ambient temperatures is calculated subsequently. +Assuming a constant mass flow through the collector, the outlet temperature and the pressure losses of the collector are calculated. + +E. g., if you want to calculate the performance of the collector within a specifc period of time, you could have the absorped energy and the ambient temperature as input time series and iterate over said series. +As the absorped energy of the collector is a function of the global radiation on the inclined surface, datetime and location only (optiacal losses are not temperature dependet), you could calculate the absorption in a preprocessing script. +If you are to create such a script, we would appreciate you sharing and adding it to TESpy! From 937d5b4ddce16345277a6c7103215435ee96629a Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 16:06:32 +0200 Subject: [PATCH 60/63] made some fixes in the docstrings --- tespy/components/components.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 340ffbaea..893c741cc 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -4377,7 +4377,7 @@ class heat_exchanger_simple(component): **default offdesign parameters** - kA (method: HE_COLD, param: m): *be aware that you must provide t_a and - t_a_design, if you want the heat flux calculated by this method* + t_a_design, if you want the heat flow calculated by this method* **inlets and outlets** @@ -4701,10 +4701,10 @@ def hw_func(self): def kA_func(self): r""" - equation for heat flux from ambient conditions + equation for heat flow from ambient conditions - determine hot side and cold side of the heat exchanger - - calculate heat flux + - calculate heat flow :returns: val (*float*) - residual value of equation @@ -5001,7 +5001,7 @@ class pipe is an alias of class heat_exchanger_simple **default offdesign parameters** - kA (method: HE_COLD, param: m): *be aware that you must provide t_a and - t_a_design, if you want the heat flux calculated by this method* + t_a_design, if you want the heat flow calculated by this method* **inlets and outlets** @@ -5030,16 +5030,21 @@ class solar collector **available parameters** - - Q: heat flux - - pr: outlet to inlet pressure ratio + - Q: heat flow, :math:`[Q]=\text{W}` + - pr: outlet to inlet pressure ratio, :math:`[pr]=1` - zeta: geometry independent friction coefficient :math:`[\zeta]=\frac{\text{Pa}}{\text{m}^4}`, also see :func:`tespy.components.components.component.zeta_func` + - hydro_group: choose 'HW' for hazen-williams equation, else darcy friction + factor is used - D: diameter of the pipes, :math:`[D]=\text{m}` - L: length of the pipes, :math:`[L]=\text{m}` - ks: pipes roughness, :math:`[ks]=\text{m}` for darcy friiction , :math:`[ks]=\text{1}` for hazen-williams equation - - E: global solar radiation, :math:`[E] = \frac{\text{W}}{\text{m}^2}` + - energy_group: grouped parameters for solarthermal collector energy + balance + - E: absorption on the inclined surface, + :math:`[E] = \frac{\text{W}}{\text{m}^2}` - lkf_lin: linear loss key figure, :math:`[\alpha_1]=\frac{\text{W}}{\text{K} \cdot \text{m}}` - lkf_quad: quadratic loss key figure, @@ -5054,7 +5059,7 @@ class solar collector - D, L and ks, if you want to calculate pressure drop from darcy friction factor or hazen williams equation and - E, A, lkf_lin, lkf_quad and t_a, if you want to calculate the heat - flow on basis of the radiation and ambient conditions + flow on basis of the absorption and ambient conditions **equations** @@ -5160,7 +5165,7 @@ def additional_equations(self): r""" additional equations for solar collectors - - calculates collector heat flux from global solar radiation + - calculates collector heat flux from area independent absorption :param nw: network using this component object :type nw: tespy.networks.network From 857f0b5e3e99afa56220762b91e29b35dc757ed1 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 16:11:30 +0200 Subject: [PATCH 61/63] some typo fixes --- doc/getting_started/solar_collector.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/getting_started/solar_collector.rst b/doc/getting_started/solar_collector.rst index 11f862f9f..4a5c7847f 100644 --- a/doc/getting_started/solar_collector.rst +++ b/doc/getting_started/solar_collector.rst @@ -13,11 +13,13 @@ The process flow diagram is shown in the image below, the source code can be fou The solarthermal collector is used to transfer heat from the solar radiation to the collector fluid. The TESPy component :py:class:`solar_collector ` inherits from the :py:class:`solar_collector ` component. -An energy balance is applied according to the :py:class:`solar_collector ` method, which takes the collector's -* surface area :code:`A`, -* loss key figures :code:`lkf_lin` (linear) and :code:`lkf_quad` (quadratic), -* ambient temperature :code:`t_a` as well as -* area independent absorped energy :code:`E` (radiation on inclined surface minus optical losses) +An energy balance is applied according to the :py:class:`solar collector energy func ` method, which takes the collector's + +- surface area :code:`A`, +- loss key figures :code:`lkf_lin` (linear) and :code:`lkf_quad` (quadratic), +- ambient temperature :code:`t_a` as well as +- area independent absorped energy :code:`E` (radiation on inclined surface minus optical losses) + into account. In the script different ways of parametrisation are shown. In the last part a collector is designed and the offdesign performance at different rates of absorption and ambient temperatures is calculated subsequently. @@ -25,4 +27,4 @@ Assuming a constant mass flow through the collector, the outlet temperature and E. g., if you want to calculate the performance of the collector within a specifc period of time, you could have the absorped energy and the ambient temperature as input time series and iterate over said series. As the absorped energy of the collector is a function of the global radiation on the inclined surface, datetime and location only (optiacal losses are not temperature dependet), you could calculate the absorption in a preprocessing script. -If you are to create such a script, we would appreciate you sharing and adding it to TESpy! +If you are to create such a script, we would appreciate you sharing and adding it to TESPy! From 51580908708a96292973a75aa71d09e7794a9820 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 16:15:35 +0200 Subject: [PATCH 62/63] fixed another typo --- doc/getting_started/solar_collector.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/getting_started/solar_collector.rst b/doc/getting_started/solar_collector.rst index 4a5c7847f..b41184cf0 100644 --- a/doc/getting_started/solar_collector.rst +++ b/doc/getting_started/solar_collector.rst @@ -12,7 +12,7 @@ The process flow diagram is shown in the image below, the source code can be fou Figure: Topology of the solar collector. The solarthermal collector is used to transfer heat from the solar radiation to the collector fluid. -The TESPy component :py:class:`solar_collector ` inherits from the :py:class:`solar_collector ` component. +The TESPy component :py:class:`solar_collector ` inherits from the :py:class:`simple_heat_exchanger ` component. An energy balance is applied according to the :py:class:`solar collector energy func ` method, which takes the collector's - surface area :code:`A`, From e09d8847e681dc4a25e187d82cee8f6a308beed7 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Mon, 27 Aug 2018 16:20:03 +0200 Subject: [PATCH 63/63] adjusted version number --- VERSION | 2 +- doc/conf.py | 4 ++-- doc/whats_new/v0-0-4.rst | 1 + setup.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index fe4bbd957..81f0fdecc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -__version__ = "0.0.3 dev" +__version__ = "0.0.4" diff --git a/doc/conf.py b/doc/conf.py index bac1398eb..ba2b5852e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -63,9 +63,9 @@ # built documents. # # The short X.Y version. -version = '0.0.3-001' +version = '0.0.4' # The full version, including alpha/beta/rc tags. -release = '0.0.3-001 dev' +release = '0.0.4 master' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/whats_new/v0-0-4.rst b/doc/whats_new/v0-0-4.rst index 9b64f7ffa..c993e3ca0 100644 --- a/doc/whats_new/v0-0-4.rst +++ b/doc/whats_new/v0-0-4.rst @@ -12,6 +12,7 @@ New Features Documentation ############# - improved the online-documentation with new structure and new examples (`fa5e7ca `_, `6ef4d99 `_). + You will find the new examples in :ref:`this section `. - fixed some typos in different modules and the online-documentation Testing diff --git a/setup.py b/setup.py index 76842f1df..f6d4048b2 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def read(fname): setup(name='TESPy', - version='0.0.3-001 dev', + version='0.0.4', description='Thermal Engineering Systems in Python (TESPy)', url='http://github.com/oemof/tespy', author='Francesco Witte',