From 8ffdc9d44ca10bb6f1741e8993cf40d70e0480a2 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 11 Feb 2020 10:59:30 +0100 Subject: [PATCH 01/11] Add linewrap --- README.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index ba2becf28..54a6f7899 100644 --- a/README.rst +++ b/README.rst @@ -64,8 +64,9 @@ Examples For a short introduction on how TESPy works and how you can use it, we provide some `examples and tutorials `_, -go and check them out. You can download the python scripts of all example plants from -the `tespy_examples `_ +go and check them out. You can download the python scripts of all example plants +from the +`tespy_examples `_ repository. License From c302ab6f1e61f3e7ebfb793c1cdfd0e02cc17f63 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 11 Feb 2020 11:17:18 +0100 Subject: [PATCH 02/11] Add missing network tests --- tespy/networks/networks.py | 7 +--- tests/network_tests/network_tests.py | 59 ++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/tespy/networks/networks.py b/tespy/networks/networks.py index 8a278e259..064652e4b 100644 --- a/tespy/networks/networks.py +++ b/tespy/networks/networks.py @@ -1160,12 +1160,7 @@ def init_fluids(self): # 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 fluid not in tmp_set.keys(): c.fluid.val[fluid] = tmp0[fluid] c.fluid.val0[fluid] = tmp0[fluid] c.fluid.val_set[fluid] = False diff --git a/tests/network_tests/network_tests.py b/tests/network_tests/network_tests.py index 30cf772bb..41bf37304 100644 --- a/tests/network_tests/network_tests.py +++ b/tests/network_tests/network_tests.py @@ -187,6 +187,34 @@ def test_network_missing_data_in_individual_design_case_file(): shutil.rmtree('./tmp', ignore_errors=True) shutil.rmtree('./tmp2', ignore_errors=True) +def test_network_missing_connection_in_design_path(): + """ + Test for missing connection data in design case files. + """ + nw = network(['water']) + source = basics.source('source') + pipe = piping.pipe('pipe', Q=0, pr=0.95, design=['pr'], offdesign=['zeta']) + sink = basics.sink('sink') + a = connection(source, 'out1', pipe, 'in1', m=1, p=1e5, T=293.15, + fluid={'water': 1}) + b = connection(pipe, 'out1', sink, 'in1') + nw.add_conns(a, b) + nw.solve('design') + nw.save('tmp') + + inputs = open('./tmp/conn.csv') + all_lines = inputs.readlines() + all_lines.pop(len(all_lines) - 1) + inputs.close() + + with open('./tmp/conn.csv', 'w') as out: + for line in all_lines: + out.write(line.strip() + '\n') + + offdesign_TESPyNetworkError(nw, design_path='tmp', init_only=True) + + shutil.rmtree('./tmp', ignore_errors=True) + class test_network_individual_offdesign: @@ -340,3 +368,34 @@ def test_local_offdesign_on_connections_and_components(self): shutil.rmtree('./design1', ignore_errors=True) shutil.rmtree('./design2', ignore_errors=True) + + def test_missing_design_path_local_offdesign_on_connections(self): + """ + Test missing design path specification on connections in local + offdesign mode. + """ + self.setup_network_individual_offdesign() + self.nw.solve('design') + self.sc2_v2.set_attr(m=0) + self.nw.solve('design') + self.nw.save('design1') + v1_design = self.sc1_v1.v.val_SI + zeta_sc1_design = self.sc1.zeta.val + + self.sc1_v1.set_attr(design=['T'], offdesign=['v'], state='l') + self.sc2_v2.set_attr(design=['T'], offdesign=['v'], state='l') + + self.sc1.set_attr(local_offdesign=True, design_path='design1') + self.pump1.set_attr(local_offdesign=True, design_path='design1') + self.sp_p1.set_attr(local_offdesign=True, design_path='design1') + self.p1_sc1.set_attr(local_offdesign=True, design_path='design1') + self.sc1_v1.set_attr(local_offdesign=True) + self.sc1.set_attr(E=500) + + self.sc2_v2.set_attr(T=95, m=np.nan) + try: + self.nw.solve('design', init_only=True) + except TESPyNetworkError: + pass + + shutil.rmtree('./design1', ignore_errors=True) From f23ba93c3efd7e635a977352cc390bb2985045eb Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 11 Feb 2020 11:30:05 +0100 Subject: [PATCH 03/11] Fix offdesign TESPyNetworkError trigger --- tests/network_tests/network_tests.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/network_tests/network_tests.py b/tests/network_tests/network_tests.py index 41bf37304..289698591 100644 --- a/tests/network_tests/network_tests.py +++ b/tests/network_tests/network_tests.py @@ -123,7 +123,7 @@ def test_network_reader_deleted_chars(self): @raises(TESPyNetworkError) def offdesign_TESPyNetworkError(nw, **kwargs): - nw.solve('offdesign', kwargs) + nw.solve('offdesign', **kwargs) def test_network_missing_data_in_design_case_files(): @@ -187,6 +187,7 @@ def test_network_missing_data_in_individual_design_case_file(): shutil.rmtree('./tmp', ignore_errors=True) shutil.rmtree('./tmp2', ignore_errors=True) + def test_network_missing_connection_in_design_path(): """ Test for missing connection data in design case files. @@ -211,7 +212,7 @@ def test_network_missing_connection_in_design_path(): for line in all_lines: out.write(line.strip() + '\n') - offdesign_TESPyNetworkError(nw, design_path='tmp', init_only=True) + offdesign_TESPyNetworkError(nw, design_path='tmp') shutil.rmtree('./tmp', ignore_errors=True) From e1ce8a981e4282a233d12a52abb74d7d574c9b77 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Tue, 11 Feb 2020 11:48:01 +0100 Subject: [PATCH 04/11] Remove unused functionality in modify_path_os --- tespy/tools/helpers.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tespy/tools/helpers.py b/tespy/tools/helpers.py index 58b62ae7f..f02fdd0cb 100644 --- a/tespy/tools/helpers.py +++ b/tespy/tools/helpers.py @@ -442,16 +442,12 @@ def modify_path_os(path): if path[0] != '\\' and path[1:2] != ':' and path[0] != '.': # relative path path = '.\\' + path - elif os.name == 'posix': - # linux, max + else: + # linux, mac path = path.replace('\\', '/') if path[0] != '/' and path[0] != '.': - # absolute path + # relative path path = './' + path - else: - # unkown os - msg = 'Unknown operating system, using posix pathing logic.' - logging.warning(msg) return path From b479ea8dcfe71ce540ed00faa191aeb1c2188e37 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Wed, 12 Feb 2020 17:51:41 +0100 Subject: [PATCH 05/11] Adjust fluid_deriv method of class component to work with subclasses --- tespy/components/basics.py | 20 +------------------- tespy/components/components.py | 13 ++++++------- tespy/components/heat_exchangers.py | 28 +--------------------------- 3 files changed, 8 insertions(+), 53 deletions(-) diff --git a/tespy/components/basics.py b/tespy/components/basics.py index e1f7e872d..9bd241580 100644 --- a/tespy/components/basics.py +++ b/tespy/components/basics.py @@ -494,24 +494,6 @@ def derivatives(self, vek_z): ###################################################################### # all derivatives are static - def fluid_deriv(self): - r""" - Calculate the partial derivatives for all fluid balance equations. - - Returns - ------- - deriv : list - Matrix with partial derivatives for the fluid equations. - """ - deriv = np.zeros((self.num_nw_fluids * self.num_i, - 2 * self.num_i, - self.num_nw_vars)) - for i in range(self.num_i): - for j in range(self.num_nw_fluids): - deriv[i * self.num_nw_fluids + j, i, j + 3] = 1 - deriv[i * self.num_nw_fluids + j, self.num_i + i, j + 3] = -1 - return deriv.tolist() - def inout_deriv(self, pos): r""" Calculate partial derivatives. @@ -534,4 +516,4 @@ def inout_deriv(self, pos): deriv[i, i, pos] = 1 for j in range(self.num_i): deriv[j, j + self.num_i, pos] = -1 - return deriv.tolist() + return deriv diff --git a/tespy/components/components.py b/tespy/components/components.py index 63a1c21d7..b26270191 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -510,14 +510,13 @@ def fluid_deriv(self): deriv : list Matrix with partial derivatives for the fluid equations. """ - deriv = np.zeros((self.num_nw_fluids, - 2 + self.num_vars, + deriv = np.zeros((self.num_nw_fluids * self.num_i, + 2 * self.num_i + self.num_vars, self.num_nw_vars)) - i = 0 - for fluid in self.nw_fluids: - deriv[i, 0, i + 3] = 1 - deriv[i, 1, i + 3] = -1 - i += 1 + for i in range(self.num_i): + for j in range(self.num_nw_fluids): + deriv[i * self.num_nw_fluids + j, i, j + 3] = 1 + deriv[i * self.num_nw_fluids + j, self.num_i + i, j + 3] = -1 return deriv # %% diff --git a/tespy/components/heat_exchangers.py b/tespy/components/heat_exchangers.py index 6e795badc..df1973259 100644 --- a/tespy/components/heat_exchangers.py +++ b/tespy/components/heat_exchangers.py @@ -1592,32 +1592,6 @@ def mass_flow_func(self): vec_res += [self.inl[i].m.val_SI - self.outl[i].m.val_SI] return vec_res - def fluid_deriv(self): - r""" - Calculate partial derivatives for all fluid balance equations. - - Returns - ------- - deriv : list - Matrix with partial derivatives for the fluid equations. - """ - deriv = np.zeros((self.num_nw_fluids * 2, - 4 + self.num_vars, - self.num_nw_vars)) - # hot side - i = 0 - for fluid in self.nw_fluids: - deriv[i, 0, i + 3] = 1 - deriv[i, 2, i + 3] = -1 - i += 1 - # cold side - j = 0 - for fluid in self.nw_fluids: - deriv[i + j, 1, j + 3] = 1 - deriv[i + j, 3, j + 3] = -1 - j += 1 - return deriv.tolist() - def mass_flow_deriv(self): r""" Calculate partial derivatives for all mass flow balance equations. @@ -1633,7 +1607,7 @@ def mass_flow_deriv(self): deriv[i, i, 0] = 1 for j in range(self.num_o): deriv[j, j + i + 1, 0] = -1 - return deriv.tolist() + return deriv def energy_func(self): r""" From 23c32bd2ad6e8cce820561e596ea29b64852d1f9 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 13 Feb 2020 09:23:13 +0100 Subject: [PATCH 06/11] Add convergence checks for all component tests --- tests/component_tests/combustion_tests.py | 17 +++++++++++ tests/component_tests/heat_exchanger_tests.py | 25 ++++++++++++++++ tests/component_tests/piping_tests.py | 9 ++++++ tests/component_tests/reactors_tests.py | 16 ++++++++++ tests/component_tests/turbomachinery_tests.py | 30 +++++++++++++++++++ 5 files changed, 97 insertions(+) diff --git a/tests/component_tests/combustion_tests.py b/tests/component_tests/combustion_tests.py index c306eb683..d0ac9f2d0 100644 --- a/tests/component_tests/combustion_tests.py +++ b/tests/component_tests/combustion_tests.py @@ -21,6 +21,12 @@ import shutil +def convergence_check(lin_dep): + """Check convergence status of a simulation.""" + msg = 'Calculation did not converge!' + eq_(lin_dep, False, msg) + + class component_tests: def setup(self): @@ -75,6 +81,7 @@ def test_combustion_chamber(self): b.add_comps({'c': instance}) self.nw.add_busses(b) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of thermal input must be ' + str(b.P.val) + ', is ' + str(instance.ti.val) + '.') eq_(round(b.P.val, 1), round(instance.ti.val, 1), msg) @@ -83,6 +90,7 @@ def test_combustion_chamber(self): # test specified thermal input for combustion_chamber instance.set_attr(ti=1e6) self.nw.solve('design') + convergence_check(self.nw.lin_dep) ti = (self.c2.m.val_SI * self.c2.fluid.val['CH4'] * instance.fuels['CH4']['LHV']) msg = ('Value of thermal input must be ' + str(instance.ti.val) + @@ -93,6 +101,7 @@ def test_combustion_chamber(self): self.c3.set_attr(T=np.nan) instance.set_attr(lamb=1) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of oxygen in flue gas must be 0.0, is ' + str(round(self.c3.fluid.val['O2'], 4)) + '.') eq_(0.0, round(self.c3.fluid.val['O2'], 4), msg) @@ -137,9 +146,11 @@ def test_combustion_engine(self): ti = 1e6 TI.set_attr(P=ti) self.nw.solve('design') + convergence_check(self.nw.lin_dep) self.nw.save('tmp') # calculate in offdesign mode self.nw.solve('offdesign', init_path='tmp', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of thermal input must be ' + str(TI.P.val) + ', is ' + str(instance.ti.val) + '.') eq_(round(TI.P.val, 1), round(instance.ti.val, 1), msg) @@ -148,6 +159,7 @@ def test_combustion_engine(self): TI.set_attr(P=np.nan) instance.set_attr(ti=ti) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of thermal input must be ' + str(ti) + ', is ' + str(instance.ti.val) + '.') eq_(round(ti, 1), round(instance.ti.val, 1), msg) @@ -156,6 +168,7 @@ def test_combustion_engine(self): # test specified heat output 1 bus value Q1.set_attr(P=instance.Q1.val) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') + convergence_check(self.nw.lin_dep) # heat output is at design point value, thermal input must therefore # not have changed msg = ('Value of thermal input must be ' + str(ti) + ', is ' + @@ -172,6 +185,7 @@ def test_combustion_engine(self): # test specified heat output 2 bus value Q2.set_attr(P=1.2 * instance.Q2.val) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') + convergence_check(self.nw.lin_dep) # calculate heat output over cooling loop heat2 = self.c5.m.val_SI * (self.c7.h.val_SI - self.c5.h.val_SI) @@ -183,6 +197,7 @@ def test_combustion_engine(self): Q2.set_attr(P=np.nan) instance.set_attr(Q2=heat2) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') + convergence_check(self.nw.lin_dep) heat2 = self.c5.m.val_SI * (self.c7.h.val_SI - self.c5.h.val_SI) msg = ('Value of heat output 2 must be ' + str(heat2) + ', is ' + str(instance.Q2.val) + '.') @@ -192,6 +207,7 @@ def test_combustion_engine(self): instance.set_attr(Q2=np.nan) Q.set_attr(P=1.5 * instance.Q1.val) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') + convergence_check(self.nw.lin_dep) heat = (self.c4.m.val_SI * (self.c6.h.val_SI - self.c4.h.val_SI) + self.c5.m.val_SI * (self.c7.h.val_SI - self.c5.h.val_SI)) msg = ('Value of total heat output must be ' + str(Q.P.val) + @@ -202,6 +218,7 @@ def test_combustion_engine(self): Q.set_attr(P=np.nan) Qloss.set_attr(P=1e5) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of heat loss must be ' + str(Qloss.P.val) + ', is ' + str(instance.Qloss.val) + '.') eq_(round(Qloss.P.val, 1), round(instance.Qloss.val, 1), msg) diff --git a/tests/component_tests/heat_exchanger_tests.py b/tests/component_tests/heat_exchanger_tests.py index e0ac6022a..f0d8e7719 100644 --- a/tests/component_tests/heat_exchanger_tests.py +++ b/tests/component_tests/heat_exchanger_tests.py @@ -26,6 +26,12 @@ import shutil +def convergence_check(lin_dep): + """Check convergence status of a simulation.""" + msg = 'Calculation did not converge!' + eq_(lin_dep, False, msg) + + class heat_exchanger_tests: def setup(self): @@ -82,6 +88,7 @@ def test_heat_ex_simple(self): b.add_comps({'c': instance}) self.nw.add_busses(b) self.nw.solve('design') + convergence_check(self.nw.lin_dep) pr = round(self.c2.p.val_SI / self.c1.p.val_SI, 3) msg = ('Value of pressure ratio must be ' + str(pr) + ', is ' + str(instance.pr.val) + '.') @@ -93,6 +100,7 @@ def test_heat_ex_simple(self): instance.set_attr(D=instance.D.val, zeta='var', pr=np.nan) instance.D.is_var = False self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of zeta must be ' + str(zeta) + ', is ' + str(round(instance.zeta.val, 0)) + '.') eq_(zeta, round(instance.zeta.val, 0), msg) @@ -101,6 +109,7 @@ def test_heat_ex_simple(self): pr = round(instance.pr.val, 3) instance.set_attr(zeta=np.nan, pr='var') self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of pressure ratio must be ' + str(pr) + ', is ' + str(round(instance.pr.val, 3)) + '.') eq_(pr, round(instance.pr.val, 3), msg) @@ -110,6 +119,7 @@ def test_heat_ex_simple(self): instance.set_attr(kA='var', pr=np.nan) b.set_attr(P=-5e4) self.nw.solve('design') + convergence_check(self.nw.lin_dep) # due to heat output being half of reference (for Tamb) kA should be # somewhere near to that (actual value is 677) @@ -122,6 +132,7 @@ def test_heat_ex_simple(self): Q = -5e4 b.set_attr(P=Q) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of heat transfer must be ' + str(Q) + ', is ' + str(instance.Q.val) + '.') eq_(Q, round(instance.Q.val, 0), msg) @@ -149,6 +160,7 @@ def test_solar_collector(self): instance.set_attr(E=1e3, lkf_lin=1.0, lkf_quad=0.005, A='var', eta_opt=0.9, Q=1e5, Tamb=20, pr=0.99) self.nw.solve('design') + convergence_check(self.nw.lin_dep) # heat loss must be identical to Q - E * A (internal heat loss # calculation) T_diff = (self.c2.T.val + self.c1.T.val) / 2 - instance.Tamb.val @@ -164,26 +176,31 @@ def test_solar_collector(self): # test all parameters of the energy group: E instance.set_attr(A=instance.A.val, E='var') self.nw.solve('design') + convergence_check(self.nw.lin_dep) eq_(Q_loss, round(instance.Q_loss.val, 0), msg) # test all parameters of the energy group: eta_opt instance.set_attr(E=instance.E.val, eta_opt='var') self.nw.solve('design') + convergence_check(self.nw.lin_dep) eq_(Q_loss, round(instance.Q_loss.val, 0), msg) # test all parameters of the energy group: lkf_lin instance.set_attr(eta_opt=instance.eta_opt.val, lkf_lin='var') self.nw.solve('design') + convergence_check(self.nw.lin_dep) eq_(Q_loss, round(instance.Q_loss.val, 0), msg) # test all parameters of the energy group: lkf_quad instance.set_attr(lkf_lin=instance.lkf_lin.val, lkf_quad='var') self.nw.solve('design') + convergence_check(self.nw.lin_dep) eq_(Q_loss, round(instance.Q_loss.val, 0), msg) # test all parameters of the energy group: Tamb instance.set_attr(lkf_lin=instance.lkf_lin.val, lkf_quad='var') self.nw.solve('design') + convergence_check(self.nw.lin_dep) eq_(Q_loss, round(instance.Q_loss.val, 0), msg) def test_heat_ex(self): @@ -204,6 +221,7 @@ def test_heat_ex(self): b.add_comps({'c': instance}) self.nw.add_busses(b) self.nw.solve('design') + convergence_check(self.nw.lin_dep) self.nw.save('tmp') # check heat transfer @@ -228,6 +246,7 @@ def test_heat_ex(self): self.c2.set_attr(T=np.nan) instance.set_attr(ttd_l=20) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of terminal temperature difference must be ' + str(instance.ttd_l.val) + ', is ' + str(self.c2.T.val - self.c3.T.val) + '.') @@ -239,6 +258,7 @@ def test_heat_ex(self): self.c2.set_attr(T=70) instance.set_attr(ttd_l=np.nan) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of heat flow must be ' + str(instance.Q.val) + ', is ' + str(round(Q, 0)) + '.') eq_(round(Q, 0), round(instance.Q.val, 0), msg) @@ -250,6 +270,7 @@ def test_heat_ex(self): self.c4.set_attr(T=np.nan) self.c2.set_attr(T=30) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of upper terminal temperature differences must be ' 'smaller than zero, is ' + str(round(instance.ttd_l.val, 1)) + '.') @@ -263,6 +284,7 @@ def test_heat_ex(self): self.c1.set_attr(h=150e3, T=np.nan) self.c3.set_attr(T=40) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of upper terminal temperature differences must be ' 'smaller than zero, is ' + str(round(instance.ttd_u.val, 1)) + '.') @@ -285,6 +307,7 @@ def test_condenser(self): self.c4.set_attr(T=40) instance.set_attr(Q=-80e3) self.nw.solve('design') + convergence_check(self.nw.lin_dep) self.nw.save('tmp') # test heat transfer @@ -306,6 +329,7 @@ def test_condenser(self): # test lower terminal temperature difference instance.set_attr(ttd_l=20, ttd_u=np.nan, design=['pr2', 'ttd_l']) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of terminal temperature difference must be ' + str(instance.ttd_l.val) + ', is ' + str(self.c2.T.val - self.c3.T.val) + '.') @@ -315,6 +339,7 @@ def test_condenser(self): # check kA value with condensing pressure in offdesign mode: # no changes to design point means: identical pressure self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of condensing pressure be ' + str(p) + ', is ' + str(round(self.c1.p.val_SI, 5)) + '.') eq_(p, round(self.c1.p.val_SI, 5), msg) diff --git a/tests/component_tests/piping_tests.py b/tests/component_tests/piping_tests.py index 6289a51e2..9d59f0588 100644 --- a/tests/component_tests/piping_tests.py +++ b/tests/component_tests/piping_tests.py @@ -23,6 +23,12 @@ import shutil +def convergence_check(lin_dep): + """Check convergence status of a simulation.""" + msg = 'Calculation did not converge!' + eq_(lin_dep, False, msg) + + class piping_tests: def setup_piping_network(self, instance): @@ -47,6 +53,7 @@ def test_valve(self): # test variable pressure ration instance.set_attr(pr='var') self.nw.solve('design') + convergence_check(self.nw.lin_dep) pr = round(self.c2.p.val_SI / self.c1.p.val_SI, 2) msg = ('Value of pressure ratio must be ' + str(pr) + ', is ' + str(round(instance.pr.val, 2)) + '.') @@ -56,6 +63,7 @@ def test_valve(self): zeta = round(instance.zeta.val, 0) instance.set_attr(zeta='var', pr=np.nan) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of dimension independent zeta value must be ' + str(zeta) + ', is ' + str(round(instance.zeta.val, 0)) + '.') eq_(zeta, round(instance.zeta.val, 0), msg) @@ -70,6 +78,7 @@ def test_valve(self): self.c1.set_attr(m=m) self.c2.set_attr(p=np.nan) self.nw.solve('design') + convergence_check(self.nw.lin_dep) self.nw.print_results() dp = round(-dp_char.evaluate(m), 0) dp_act = round(self.c2.p.val_SI - self.c1.p.val_SI) diff --git a/tests/component_tests/reactors_tests.py b/tests/component_tests/reactors_tests.py index 47e8ceaa6..a040f87af 100644 --- a/tests/component_tests/reactors_tests.py +++ b/tests/component_tests/reactors_tests.py @@ -21,6 +21,12 @@ import shutil +def convergence_check(lin_dep): + """Check convergence status of a simulation.""" + msg = 'Calculation did not converge!' + eq_(lin_dep, False, msg) + + class reactors_tests: def setup(self): @@ -62,6 +68,7 @@ def test_water_electrolyzer(self): self.nw.add_busses(power) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of power must be ' + str(power.P.val) + ', is ' + str(self.instance.P.val) + '.') eq_(round(power.P.val, 1), round(self.instance.P.val), msg) @@ -75,6 +82,7 @@ def test_water_electrolyzer(self): self.nw.add_busses(heat) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of heat flow must be ' + str(heat.P.val) + ', is ' + str(self.instance.Q.val) + '.') eq_(round(heat.P.val, 1), round(self.instance.Q.val), msg) @@ -85,6 +93,7 @@ def test_water_electrolyzer(self): Q = heat.P.val * 0.9 heat.set_attr(P=Q) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of heat flow must be ' + str(Q) + ', is ' + str(self.instance.Q.val) + '.') eq_(round(Q, 1), round(self.instance.Q.val), msg) @@ -95,6 +104,7 @@ def test_water_electrolyzer(self): # test efficiency vs. specific energy consumption self.instance.set_attr(eta=0.9, e='var') self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of efficiency must be ' + str(self.instance.eta.val) + ', is ' + str(self.instance.e0 / self.instance.e.val) + '.') eq_(round(self.instance.eta.val, 2), @@ -106,6 +116,7 @@ def test_water_electrolyzer(self): self.instance.set_attr(e=np.nan, eta=np.nan) self.instance.set_attr(e=e) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of efficiency must be ' + str(self.instance.e0 / e) + ', is ' + str(self.instance.eta.val) + '.') eq_(round(self.instance.e0 / e, 2), round(self.instance.eta.val, 2), @@ -116,6 +127,7 @@ def test_water_electrolyzer(self): self.instance.set_attr(e=np.nan, eta=np.nan) self.instance.set_attr(e=e) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of specific energy consumption e must be ' + str(e) + ', is ' + str(self.instance.e.val) + '.') eq_(round(e, 1), round(self.instance.e.val, 1), msg) @@ -125,7 +137,9 @@ def test_water_electrolyzer(self): self.instance.set_attr(pr_c=pr, e=np.nan, zeta='var', P=2.5e6, design=['pr_c']) self.nw.solve('design') + shutil.rmtree('./tmp', ignore_errors=True) self.nw.save('tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of pressure ratio must be ' + str(pr) + ', is ' + str(self.instance.pr_c.val) + '.') eq_(round(pr, 2), round(self.instance.pr_c.val, 2), msg) @@ -134,6 +148,7 @@ def test_water_electrolyzer(self): # ratio must not change self.instance.set_attr(zeta=np.nan, offdesign=['zeta']) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of pressure ratio must be ' + str(pr) + ', is ' + str(self.instance.pr_c.val) + '.') eq_(round(pr, 2), round(self.instance.pr_c.val, 2), msg) @@ -142,6 +157,7 @@ def test_water_electrolyzer(self): Q = self.instance.Q.val * 0.9 self.instance.set_attr(Q=Q, P=np.nan) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of heat must be ' + str(Q) + ', is ' + str(self.instance.Q.val) + '.') eq_(round(Q, 0), round(self.instance.Q.val, 0), msg) diff --git a/tests/component_tests/turbomachinery_tests.py b/tests/component_tests/turbomachinery_tests.py index 5d27d4692..cbbc4d81e 100644 --- a/tests/component_tests/turbomachinery_tests.py +++ b/tests/component_tests/turbomachinery_tests.py @@ -25,6 +25,12 @@ import shutil +def convergence_check(lin_dep): + """Check convergence status of a simulation.""" + msg = 'Calculation did not converge!' + eq_(lin_dep, False, msg) + + class turbomachinery_tests: def setup_network(self, instance): @@ -47,6 +53,7 @@ def test_compressor(self): self.c2.set_attr(p=7) instance.set_attr(eta_s=0.8) self.nw.solve('design') + convergence_check(self.nw.lin_dep) self.nw.save('tmp') # test isentropic efficiency value @@ -59,6 +66,7 @@ def test_compressor(self): # trigger invalid value for isentropic efficiency instance.set_attr(eta_s=1.1) self.nw.solve('design') + convergence_check(self.nw.lin_dep) # test calculated value eta_s = ((instance.h_os('') - self.c1.h.val_SI) / @@ -76,6 +84,7 @@ def test_compressor(self): # offdesign test, efficiency value should be at design value self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of isentropic efficiency (' + str(instance.eta_s.val) + ') must be identical to design case (' + str(eta_s) + ').') eq_(round(eta_s_d, 2), round(instance.eta_s.val, 2), msg) @@ -84,6 +93,7 @@ def test_compressor(self): # at that line self.c1.set_attr(v=np.nan, m=self.c1.m.val * 0.8, T=30) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) # should be value eta_s = eta_s_d * instance.char_map.func.z2[6, 0] msg = ('Value of isentropic efficiency (' + str(instance.eta_s.val) + @@ -94,6 +104,7 @@ def test_compressor(self): # that line self.c1.set_attr(T=300) self.nw.solve('offdesign', design_path='tmp', init_path='tmp') + convergence_check(self.nw.lin_dep) # should be value eta_s = eta_s_d * instance.char_map.func.z2[0, 9] msg = ('Value of isentropic efficiency (' + str(instance.eta_s.val) + @@ -110,6 +121,7 @@ def test_compressor(self): is_set=True, param='m')) instance.char_map.is_set = False self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of isentropic efficiency must be ' + str(eta_s_d) + ', is ' + str(instance.eta_s.val) + '.') eq_(round(eta_s_d, 3), round(instance.eta_s.val, 3), msg) @@ -117,6 +129,7 @@ def test_compressor(self): # move up in volumetric flow self.c1.set_attr(v=1.5) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) eta_s = round(eta_s_d * instance.eta_s_char.func.evaluate( self.c1.m.val_SI / self.c1.m.design), 3) msg = ('Value of isentropic efficiency must be ' + str(eta_s) + @@ -128,6 +141,7 @@ def test_compressor(self): self.c1.set_attr(v=1) self.c2.set_attr(p=7.5) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) expr = (self.c2.p.val_SI * self.c1.p.design / (self.c2.p.design * self.c1.p.val_SI)) eta_s = round(eta_s_d * instance.eta_s_char.func.evaluate(expr), 3) @@ -146,6 +160,7 @@ def test_pump(self): self.c2.set_attr(p=7) instance.set_attr(eta_s=1) self.nw.solve('design') + convergence_check(self.nw.lin_dep) # test calculated value for efficiency eta_s = ((instance.h_os('') - self.c1.h.val_SI) / @@ -168,6 +183,7 @@ def test_pump(self): eta_s_d = 0.8 instance.set_attr(eta_s=eta_s_d) self.nw.solve('design') + convergence_check(self.nw.lin_dep) self.nw.save('tmp') self.c2.set_attr(p=np.nan) @@ -181,6 +197,7 @@ def test_pump(self): 'DEFAULT', char_line), is_set=True)) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) # value for difference pressure dp = 650000.0 @@ -191,6 +208,7 @@ def test_pump(self): # test ohter volumetric flow on flow char self.c1.set_attr(v=0.9) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) dp = 775000.0 msg = ('Value of pressure rise must be ' + str(dp) + ', is ' + str(self.c2.p.val_SI - self.c1.p.val_SI) + '.') @@ -209,6 +227,7 @@ def test_pump(self): self.c2.set_attr(T=ref(self.c1, 0, 20)) self.c1.set_attr(v=-0.1) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of power must be ' + str(14e5) + ', is ' + str(self.c2.p.val_SI - self.c1.p.val_SI) + '.') eq_(self.c2.p.val_SI - self.c1.p.val_SI, 14e5, msg) @@ -216,6 +235,7 @@ def test_pump(self): # upper boundary self.c1.set_attr(v=1.5) self.nw.solve('design') + convergence_check(self.nw.lin_dep) msg = ('Value of power must be ' + str(0) + ', is ' + str(self.c2.p.val_SI - self.c1.p.val_SI) + '.') eq_(self.c2.p.val_SI - self.c1.p.val_SI, 0, msg) @@ -231,6 +251,7 @@ def test_turbine(self): self.c2.set_attr(p=1, T=20) instance.set_attr(eta_s=0.85) self.nw.solve('design') + convergence_check(self.nw.lin_dep) self.nw.save('tmp') # design value of isentropic efficiency @@ -244,6 +265,7 @@ def test_turbine(self): # trigger invalid value for isentropic efficiency instance.set_attr(eta_s=1.1) self.nw.solve('design') + convergence_check(self.nw.lin_dep) eta_s = round((self.c2.h.val_SI - self.c1.h.val_SI) / (instance.h_os('') - self.c1.h.val_SI), 3) msg = ('Value of isentropic efficiency must be ' + str(eta_s) + @@ -258,6 +280,7 @@ def test_turbine(self): instance.eta_s_char.is_set = True instance.eta_s.is_set = False self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) # check efficiency msg = ('Value of isentropic efficiency (' + str(instance.eta_s.val) + ') must be identical to design case (' + str(eta_s_d) + ').') @@ -271,6 +294,7 @@ def test_turbine(self): # lowering mass flow, inlet pressure must sink according to cone law self.c1.set_attr(m=self.c1.m.val * 0.8) self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) msg = ('Value of pressure ratio (' + str(instance.pr.val) + ') must be at (' + str(0.128) + ').') eq_(0.128, round(instance.pr.val, 3), msg) @@ -280,6 +304,7 @@ def test_turbine(self): self.c1.set_attr(m=10) instance.eta_s_char.param = 'v' self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) expr = self.c1.v.val_SI / self.c1.v.design eta_s = round(eta_s_d * instance.eta_s_char.func.evaluate(expr), 3) msg = ('Value of isentropic efficiency (' + @@ -290,6 +315,7 @@ def test_turbine(self): # test parameter specification pr instance.eta_s_char.param = 'pr' self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) expr = (self.c2.p.val_SI * self.c1.p.design / (self.c2.p.design * self.c1.p.val_SI)) eta_s = round(eta_s_d * instance.eta_s_char.func.evaluate(expr), 3) @@ -301,6 +327,7 @@ def test_turbine(self): # test parameter specification dh_s instance.eta_s_char.param = 'dh_s' self.nw.solve('offdesign', design_path='tmp') + convergence_check(self.nw.lin_dep) expr = (instance.h_os('') - self.c1.h.val_SI) / instance.dh_s_ref eta_s = round(eta_s_d * instance.eta_s_char.func.evaluate(expr), 3) msg = ('Value of isentropic efficiency (' + @@ -324,6 +351,7 @@ def test_turbomachine(self): # pressure ratio and power are the basic functions for turbomachines, # these are inherited by all children, thus only tested here self.nw.solve('design') + convergence_check(self.nw.lin_dep) power = self.c1.m.val_SI * (self.c2.h.val_SI - self.c1.h.val_SI) pr = self.c2.p.val_SI / self.c1.p.val_SI msg = ('Value of power must be ' + str(power) + ', is ' + @@ -337,6 +365,7 @@ def test_turbomachine(self): self.c2.set_attr(p=np.nan) instance.set_attr(pr=5) self.nw.solve('design') + convergence_check(self.nw.lin_dep) pr = self.c2.p.val_SI / self.c1.p.val_SI msg = ('Value of power must be ' + str(pr) + ', is ' + str(instance.pr.val) + '.') @@ -346,6 +375,7 @@ def test_turbomachine(self): self.c2.set_attr(h=np.nan) instance.set_attr(P=1e5) self.nw.solve('design') + convergence_check(self.nw.lin_dep) power = self.c1.m.val_SI * (self.c2.h.val_SI - self.c1.h.val_SI) msg = ('Value of power must be ' + str(power) + ', is ' + str(instance.P.val) + '.') From 4a439753253e7d9afeb18afed83bef623ee2079d Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 13 Feb 2020 09:24:01 +0100 Subject: [PATCH 07/11] Add universal zeta_func --- tespy/components/components.py | 58 ++++------------------------------ 1 file changed, 7 insertions(+), 51 deletions(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index b26270191..8496ec755 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -650,7 +650,7 @@ def numeric_deriv(self, func, dx, pos, **kwargs): # %% - def zeta_func(self): + def zeta_func(self, zeta='', conn=0): r""" Calculate residual value of :math:`\zeta`-function. @@ -680,59 +680,15 @@ def zeta_func(self): \frac{\zeta}{D^4} = \frac{\Delta p \cdot \pi^2} {8 \cdot \dot{m}^2 \cdot v} """ - i = self.inl[0].to_flow() - o = self.outl[0].to_flow() - if hasattr(self, 'zeta'): - val = self.zeta.val - else: - val = self.zeta1.val + zeta = self.get_attr(zeta).val + i = self.inl[conn].to_flow() + o = self.outl[conn].to_flow() if abs(i[0]) < 1e-4: return i[1] - o[1] else: - v_i = v_mix_ph(i, T0=self.inl[0].T.val_SI) - v_o = v_mix_ph(o, T0=self.outl[0].T.val_SI) - return (val - (i[1] - o[1]) * np.pi ** 2 / - (8 * abs(i[0]) * i[0] * (v_i + v_o) / 2)) - - def zeta2_func(self): - r""" - Calculate residual value of :math:`\zeta_2`-function. - - Returns - ------- - val : float - Residual value of function. - - .. math:: - - val = \begin{cases} - p_{in} - p_{out} & |\dot{m}| < \epsilon \\ - \frac{\zeta_2}{D^4} - \frac{(p_{2,in} - p_{2,out}) \cdot \pi^2} - {8 \cdot \dot{m}_{2,in} \cdot |\dot{m}_{2,in}| \cdot - \frac{v_{2,in} + v_{2,out}}{2}} & - |\dot{m}| > \epsilon - \end{cases} - - Note - ---- - The zeta value is caluclated on the basis of a given pressure loss at - a given flow rate in the design case. As the cross sectional area A - will not change, it is possible to handle the equation in this way: - - .. math:: - - \frac{\zeta_2}{D^4} = \frac{\Delta p_2 \cdot \pi^2} - {8 \cdot \dot{m}_2^2 \cdot v} - """ - i = self.inl[1].to_flow() - o = self.outl[1].to_flow() - - if abs(i[0]) < 1e-4: - return i[1] - o[1] - else: - v_i = v_mix_ph(i, T0=self.inl[1].T.val_SI) - v_o = v_mix_ph(o, T0=self.outl[1].T.val_SI) - return (self.zeta2.val - (i[1] - o[1]) * np.pi ** 2 / + v_i = v_mix_ph(i, T0=self.inl[conn].T.val_SI) + v_o = v_mix_ph(o, T0=self.outl[conn].T.val_SI) + return (zeta - (i[1] - o[1]) * np.pi ** 2 / (8 * abs(i[0]) * i[0] * (v_i + v_o) / 2)) From 8b608b4712e0becc3fd8d13ccc15338d994971d0 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 13 Feb 2020 09:25:24 +0100 Subject: [PATCH 08/11] Modify zeta_func calls to match new function of class component --- tespy/components/combustion.py | 44 +++++++++++-------- tespy/components/heat_exchangers.py | 67 ++++++++++++++++++----------- tespy/components/piping.py | 19 +++++--- tespy/components/reactors.py | 20 ++++++--- 4 files changed, 93 insertions(+), 57 deletions(-) diff --git a/tespy/components/combustion.py b/tespy/components/combustion.py index 783e89e3d..c81462e76 100644 --- a/tespy/components/combustion.py +++ b/tespy/components/combustion.py @@ -2040,8 +2040,8 @@ class combustion_engine(combustion_chamber): 0 = p_{1,in} \cdot pr1 - p_{1,out}\\ 0 = p_{2,in} \cdot pr2 - p_{2,out} - - :func:`tespy.components.components.component.zeta_func` - - :func:`tespy.components.components.component.zeta2_func` + - loop 1 :func:`tespy.components.components.component.zeta_func` + - loop 2 :func:`tespy.components.components.component.zeta_func` Available fuels @@ -2116,11 +2116,11 @@ class combustion_engine(combustion_chamber): Pressure ratio heat outlet 2, :math:`pr/1`. zeta1 : str/float/tespy.helpers.dc_cp - Pressure ratio heat outlet 2, + Geometry independent friction coefficient heating loop 1, :math:`\zeta/\frac{1}{\text{m}^4}`. zeta2 : str/float/tespy.helpers.dc_cp - Pressure ratio heat outlet 2, + Geometry independent friction coefficient heating loop 2, :math:`\zeta/\frac{1}{\text{m}^4}`. tiP_char : str/tespy.helpers.dc_cc @@ -2391,12 +2391,12 @@ def equations(self): # equations for specified zeta values at cooling loops if self.zeta1.is_set: if np.absolute(self.vec_res[k]) > err ** 2 or self.it % 4 == 0: - self.vec_res[k] = self.zeta_func() + self.vec_res[k] = self.zeta_func(zeta='zeta1', conn=0) k += 1 if self.zeta2.is_set: if np.absolute(self.vec_res[k]) > err ** 2 or self.it % 4 == 0: - self.vec_res[k] = self.zeta2_func() + self.vec_res[k] = self.zeta_func(zeta='zeta2', conn=1) k += 1 def derivatives(self, vec_z): @@ -2601,29 +2601,39 @@ def derivatives(self, vec_z): if self.zeta1.is_set: f = self.zeta_func if not vec_z[0, 0]: - self.mat_deriv[k, 0, 0] = self.numeric_deriv(f, 'm', 0) + self.mat_deriv[k, 0, 0] = self.numeric_deriv( + f, 'm', 0, zeta='zeta1', conn=0) if not vec_z[0, 1]: - self.mat_deriv[k, 0, 1] = self.numeric_deriv(f, 'p', 0) + self.mat_deriv[k, 0, 1] = self.numeric_deriv( + f, 'p', 0, zeta='zeta1', conn=0) if not vec_z[0, 2]: - self.mat_deriv[k, 0, 2] = self.numeric_deriv(f, 'h', 0) + self.mat_deriv[k, 0, 2] = self.numeric_deriv( + f, 'h', 0, zeta='zeta1', conn=0) if not vec_z[4, 1]: - self.mat_deriv[k, 4, 1] = self.numeric_deriv(f, 'p', 4) + self.mat_deriv[k, 4, 1] = self.numeric_deriv( + f, 'p', 4, zeta='zeta1', conn=0) if not vec_z[4, 2]: - self.mat_deriv[k, 4, 2] = self.numeric_deriv(f, 'h', 4) + self.mat_deriv[k, 4, 2] = self.numeric_deriv( + f, 'h', 4, zeta='zeta1', conn=0) k += 1 if self.zeta2.is_set: - f = self.zeta2_func + f = self.zeta_func if not vec_z[1, 0]: - self.mat_deriv[k, 1, 0] = self.numeric_deriv(f, 'm', 1) + self.mat_deriv[k, 1, 0] = self.numeric_deriv( + f, 'm', 1, zeta='zeta2', conn=1) if not vec_z[1, 1]: - self.mat_deriv[k, 1, 1] = self.numeric_deriv(f, 'p', 1) + self.mat_deriv[k, 1, 1] = self.numeric_deriv( + f, 'p', 1, zeta='zeta2', conn=1) if not vec_z[1, 2]: - self.mat_deriv[k, 1, 2] = self.numeric_deriv(f, 'h', 1) + self.mat_deriv[k, 1, 2] = self.numeric_deriv( + f, 'h', 1, zeta='zeta2', conn=1) if not vec_z[5, 1]: - self.mat_deriv[k, 5, 1] = self.numeric_deriv(f, 'p', 5) + self.mat_deriv[k, 5, 1] = self.numeric_deriv( + f, 'p', 5, zeta='zeta2', conn=1) if not vec_z[5, 2]: - self.mat_deriv[k, 5, 2] = self.numeric_deriv(f, 'h', 5) + self.mat_deriv[k, 5, 2] = self.numeric_deriv( + f, 'h', 5, zeta='zeta2', conn=1) k += 1 def fluid_func(self): diff --git a/tespy/components/heat_exchangers.py b/tespy/components/heat_exchangers.py index df1973259..4006865e1 100644 --- a/tespy/components/heat_exchangers.py +++ b/tespy/components/heat_exchangers.py @@ -348,7 +348,7 @@ def equations(self): # equations for specified zeta if self.zeta.is_set: if np.absolute(self.vec_res[k]) > err ** 2 or self.it % 4 == 0: - self.vec_res[k] = self.zeta_func() + self.vec_res[k] = self.zeta_func(zeta='zeta', conn=0) k += 1 ###################################################################### @@ -425,19 +425,24 @@ def derivatives(self, vec_z): if self.zeta.is_set: f = self.zeta_func if not vec_z[0, 0]: - self.mat_deriv[k, 0, 0] = self.numeric_deriv(f, 'm', 0) + self.mat_deriv[k, 0, 0] = self.numeric_deriv( + f, 'm', 0, zeta='zeta', conn=0) if not vec_z[0, 2]: - self.mat_deriv[k, 0, 1] = self.numeric_deriv(f, 'p', 0) + self.mat_deriv[k, 0, 1] = self.numeric_deriv( + f, 'p', 0, zeta='zeta', conn=0) if not vec_z[0, 2]: - self.mat_deriv[k, 0, 2] = self.numeric_deriv(f, 'h', 0) + self.mat_deriv[k, 0, 2] = self.numeric_deriv( + f, 'h', 0, zeta='zeta', conn=0) if not vec_z[1, 1]: - self.mat_deriv[k, 1, 1] = self.numeric_deriv(f, 'p', 1) + self.mat_deriv[k, 1, 1] = self.numeric_deriv( + f, 'p', 1, zeta='zeta', conn=0) if not vec_z[1, 2]: - self.mat_deriv[k, 1, 2] = self.numeric_deriv(f, 'h', 1) + self.mat_deriv[k, 1, 2] = self.numeric_deriv( + f, 'h', 1, zeta='zeta', conn=0) # custom variable zeta if self.zeta.is_var: self.mat_deriv[k, 2 + self.zeta.var_pos, 0] = ( - self.numeric_deriv(f, 'zeta', 2)) + self.numeric_deriv(f, 'zeta', 2, zeta='zeta', conn=0)) k += 1 ###################################################################### @@ -1166,8 +1171,8 @@ class heat_exchanger(component): 0 = p_{1,in} \cdot pr1 - p_{1,out}\\ 0 = p_{2,in} \cdot pr2 - p_{2,out} - - :func:`tespy.components.components.component.zeta_func` - - :func:`tespy.components.components.component.zeta2_func` + - hot side :func:`tespy.components.components.component.zeta_func` + - cold side :func:`tespy.components.components.component.zeta_func` **additional equations** @@ -1411,13 +1416,13 @@ def equations(self): ###################################################################### # equations for specified zeta at hot side if self.zeta1.is_set: - self.vec_res[k] = self.zeta_func() + self.vec_res[k] = self.zeta_func(zeta='zeta1', conn=0) k += 1 ###################################################################### # equations for specified zeta at cold side if self.zeta2.is_set: - self.vec_res[k] = self.zeta2_func() + self.vec_res[k] = self.zeta_func(zeta='zeta2', conn=1) k += 1 ###################################################################### @@ -1517,31 +1522,41 @@ def derivatives(self, vec_z): if self.zeta1.is_set: f = self.zeta_func if not vec_z[0, 0]: - self.mat_deriv[k, 0, 0] = self.numeric_deriv(f, 'm', 0) + self.mat_deriv[k, 0, 0] = self.numeric_deriv( + f, 'm', 0, zeta='zeta1', conn=0) if not vec_z[0, 1]: - self.mat_deriv[k, 0, 1] = self.numeric_deriv(f, 'p', 0) + self.mat_deriv[k, 0, 1] = self.numeric_deriv( + f, 'p', 0, zeta='zeta1', conn=0) if not vec_z[0, 2]: - self.mat_deriv[k, 0, 2] = self.numeric_deriv(f, 'h', 0) + self.mat_deriv[k, 0, 2] = self.numeric_deriv( + f, 'h', 0, zeta='zeta1', conn=0) if not vec_z[2, 1]: - self.mat_deriv[k, 2, 1] = self.numeric_deriv(f, 'p', 2) + self.mat_deriv[k, 2, 1] = self.numeric_deriv( + f, 'p', 2, zeta='zeta1', conn=0) if not vec_z[2, 2]: - self.mat_deriv[k, 2, 2] = self.numeric_deriv(f, 'h', 2) + self.mat_deriv[k, 2, 2] = self.numeric_deriv( + f, 'h', 2, zeta='zeta1', conn=0) k += 1 ###################################################################### # derivatives for specified zeta at cold side if self.zeta2.is_set: - f = self.zeta2_func + f = self.zeta_func if not vec_z[1, 0]: - self.mat_deriv[k, 1, 0] = self.numeric_deriv(f, 'm', 1) + self.mat_deriv[k, 1, 0] = self.numeric_deriv( + f, 'm', 1, zeta='zeta2', conn=1) if not vec_z[1, 1]: - self.mat_deriv[k, 1, 1] = self.numeric_deriv(f, 'p', 1) + self.mat_deriv[k, 1, 1] = self.numeric_deriv( + f, 'p', 1, zeta='zeta2', conn=1) if not vec_z[1, 2]: - self.mat_deriv[k, 1, 2] = self.numeric_deriv(f, 'h', 1) + self.mat_deriv[k, 1, 2] = self.numeric_deriv( + f, 'h', 1, zeta='zeta2', conn=1) if not vec_z[3, 1]: - self.mat_deriv[k, 3, 1] = self.numeric_deriv(f, 'p', 3) + self.mat_deriv[k, 3, 1] = self.numeric_deriv( + f, 'p', 3, zeta='zeta2', conn=1) if not vec_z[3, 2]: - self.mat_deriv[k, 3, 2] = self.numeric_deriv(f, 'h', 3) + self.mat_deriv[k, 3, 2] = self.numeric_deriv( + f, 'h', 3, zeta='zeta2', conn=1) k += 1 ###################################################################### @@ -2014,8 +2029,8 @@ class condenser(heat_exchanger): 0 = p_{1,in} \cdot pr1 - p_{1,out}\\ 0 = p_{2,in} \cdot pr2 - p_{2,out} - - :func:`tespy.components.components.component.zeta_func` - - :func:`tespy.components.components.component.zeta2_func` + - hot side :func:`tespy.components.components.component.zeta_func` + - cold side :func:`tespy.components.components.component.zeta_func` **additional equations** @@ -2385,8 +2400,8 @@ class desuperheater(heat_exchanger): 0 = p_{1,in} \cdot pr1 - p_{1,out}\\ 0 = p_{2,in} \cdot pr2 - p_{2,out} - - :func:`tespy.components.components.component.zeta_func` - - :func:`tespy.components.components.component.zeta2_func` + - hot side :func:`tespy.components.components.component.zeta_func` + - cold side :func:`tespy.components.components.component.zeta_func` **additional equations** diff --git a/tespy/components/piping.py b/tespy/components/piping.py index 6433021ba..332961a93 100644 --- a/tespy/components/piping.py +++ b/tespy/components/piping.py @@ -384,7 +384,7 @@ def equations(self): # eqation specified zeta if self.zeta.is_set: if np.absolute(self.vec_res[k]) > err ** 2 or self.it % 4 == 0: - self.vec_res[k] = self.zeta_func() + self.vec_res[k] = self.zeta_func(zeta='zeta', conn=0) k += 1 ###################################################################### @@ -422,18 +422,23 @@ def derivatives(self, vec_z): if self.zeta.is_set: f = self.zeta_func if not vec_z[0, 0]: - self.mat_deriv[k, 0, 0] = self.numeric_deriv(f, 'm', 0) + self.mat_deriv[k, 0, 0] = self.numeric_deriv( + f, 'm', 0, zeta='zeta', conn=0) if not vec_z[0, 1]: - self.mat_deriv[k, 0, 1] = self.numeric_deriv(f, 'p', 0) + self.mat_deriv[k, 0, 1] = self.numeric_deriv( + f, 'p', 0, zeta='zeta', conn=0) if not vec_z[0, 2]: - self.mat_deriv[k, 0, 2] = self.numeric_deriv(f, 'h', 0) + self.mat_deriv[k, 0, 2] = self.numeric_deriv( + f, 'h', 0, zeta='zeta', conn=0) if not vec_z[1, 1]: - self.mat_deriv[k, 1, 1] = self.numeric_deriv(f, 'p', 1) + self.mat_deriv[k, 1, 1] = self.numeric_deriv( + f, 'p', 1, zeta='zeta', conn=0) if not vec_z[1, 2]: - self.mat_deriv[k, 1, 2] = self.numeric_deriv(f, 'h', 1) + self.mat_deriv[k, 1, 2] = self.numeric_deriv( + f, 'h', 1, zeta='zeta', conn=0) if self.zeta.is_var: self.mat_deriv[k, 2 + self.zeta.var_pos, 0] = ( - self.numeric_deriv(f, 'zeta', 2)) + self.numeric_deriv(f, 'zeta', 2, zeta='zeta', conn=0)) k += 1 ###################################################################### diff --git a/tespy/components/reactors.py b/tespy/components/reactors.py index 53f5f8323..345957815 100644 --- a/tespy/components/reactors.py +++ b/tespy/components/reactors.py @@ -419,7 +419,7 @@ def equations(self): # specified zeta value if self.zeta.is_set: if np.absolute(self.vec_res[k]) > err ** 2 or self.it % 4 == 0: - self.vec_res[k] = self.zeta_func() + self.vec_res[k] = self.zeta_func(zeta='zeta', conn=0) k += 1 # equation for heat transfer @@ -532,20 +532,26 @@ def derivatives(self, vec_z): if self.zeta.is_set: f = self.zeta_func if not vec_z[0, 0]: - self.mat_deriv[k, 0, 0] = self.numeric_deriv(f, 'm', 0) + self.mat_deriv[k, 0, 0] = self.numeric_deriv( + f, 'm', 0, zeta='zeta', conn=0) if not vec_z[0, 1]: - self.mat_deriv[k, 0, 1] = self.numeric_deriv(f, 'p', 0) + self.mat_deriv[k, 0, 1] = self.numeric_deriv( + f, 'p', 0, zeta='zeta', conn=0) if not vec_z[0, 2]: - self.mat_deriv[k, 0, 2] = self.numeric_deriv(f, 'h', 0) + self.mat_deriv[k, 0, 2] = self.numeric_deriv( + f, 'h', 0, zeta='zeta', conn=0) if not vec_z[2, 1]: - self.mat_deriv[k, 2, 1] = self.numeric_deriv(f, 'p', 2) + self.mat_deriv[k, 2, 1] = self.numeric_deriv( + f, 'p', 2, zeta='zeta', conn=0) if not vec_z[2, 2]: - self.mat_deriv[k, 2, 2] = self.numeric_deriv(f, 'h', 2) + self.mat_deriv[k, 2, 2] = self.numeric_deriv( + f, 'h', 2, zeta='zeta', conn=0) # derivatives for variable zeta if self.zeta.is_var: self.mat_deriv[k, 5 + self.zeta.var_pos, 0] = ( - self.numeric_deriv(f, 'zeta', 5)) + self.numeric_deriv( + f, 'zeta', 5, zeta='zeta', conn=0)) k += 1 ###################################################################### From 2d70293871920bcd314aad7377da7b812e7bb82b Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 13 Feb 2020 09:42:59 +0100 Subject: [PATCH 09/11] Update What's New --- doc/whats_new/v0-2-2.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/whats_new/v0-2-2.rst b/doc/whats_new/v0-2-2.rst index 3402c2761..a30863156 100644 --- a/doc/whats_new/v0-2-2.rst +++ b/doc/whats_new/v0-2-2.rst @@ -12,13 +12,25 @@ Parameter renaming Testing ####### +- Add convergence checks for all component tests. Some tests did not fail, even + if the calculation did not converge + (`PR #153 `_). +- Improve coverage of the networks module +(`PR #153 `_). Bug fixes ######### Other changes ############# +- Use the method :py:meth:`tespy.components.components.component.fluid_deriv` + for all components, that do not change composition between an inlet and the + respective outlet (`PR #153 `_). +- Adjust the method :py:meth:`tespy.components.components.component.zeta_func` + to work with all zeta value specifications + (`PR #153 `_). Contributors ############ +- Francesco Witte (@fwitte) From 04773d6cb6db07950e81098f9014ea05e5fea1f8 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 13 Feb 2020 10:04:45 +0100 Subject: [PATCH 10/11] Fix indendation --- doc/whats_new/v0-2-2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/whats_new/v0-2-2.rst b/doc/whats_new/v0-2-2.rst index a30863156..d4a6c40ac 100644 --- a/doc/whats_new/v0-2-2.rst +++ b/doc/whats_new/v0-2-2.rst @@ -16,7 +16,7 @@ Testing if the calculation did not converge (`PR #153 `_). - Improve coverage of the networks module -(`PR #153 `_). + (`PR #153 `_). Bug fixes ######### From 9582cae3e43f8c8031057f3b8867d6a24d251eb6 Mon Sep 17 00:00:00 2001 From: Francesco Witte Date: Thu, 13 Feb 2020 10:10:27 +0100 Subject: [PATCH 11/11] Update documentation --- tespy/components/components.py | 12 ++++++++++++ tespy/components/reactors.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tespy/components/components.py b/tespy/components/components.py index 8496ec755..74afa2232 100644 --- a/tespy/components/components.py +++ b/tespy/components/components.py @@ -654,6 +654,18 @@ def zeta_func(self, zeta='', conn=0): r""" Calculate residual value of :math:`\zeta`-function. + Parameters + ---------- + zeta : str + Component parameter to evaluate the zeta_func on, e. g. + :code:`zeta1`. + + conn : int + Connection number of inlet and corresponding outlet. + In order to use the zeta function, the index of the inlet and + the corresponding outlet within the components :code:`ìnl` and + :code:`outl` respectively must be identical! + Returns ------- val : float diff --git a/tespy/components/reactors.py b/tespy/components/reactors.py index 345957815..fb19889aa 100644 --- a/tespy/components/reactors.py +++ b/tespy/components/reactors.py @@ -179,7 +179,7 @@ class water_electrolyzer(component): pressure is 25 bars. The electrolysis efficiency is at 80 % and the compressor isentropic efficiency at 85 %. After designing the plant the offdesign electrolysis efficiency is predicted by the characteristic line. - TODO: LINKTODEFAULTCHAR? + The default characteristic line can be found here: :py:mod:`tespy.data`. >>> fw_el = connection(fw, 'out1', el, 'in2') >>> el_o = connection(el, 'out2', oxy, 'in1')