def test_HeatExchanger(self): """Test component properties of heat exchanger.""" instance = HeatExchanger('heat exchanger') self.setup_HeatExchanger_network(instance) # design specification instance.set_attr(pr1=0.98, pr2=0.98, ttd_u=5, design=['pr1', 'pr2', 'ttd_u'], offdesign=['zeta1', 'zeta2', 'kA_char']) self.c1.set_attr(T=120, p=3, fluid={'Ar': 0, 'H2O': 1, 'S800': 0}) self.c2.set_attr(T=70) self.c3.set_attr(T=40, p=5, fluid={'Ar': 1, 'H2O': 0, 'S800': 0}) b = Bus('heat transfer', P=-80e3) b.add_comps({'comp': instance}) self.nw.add_busses(b) self.nw.solve('design') convergence_check(self.nw.lin_dep) self.nw.save('tmp') Q_design = instance.Q.val # test specified kA value instance.set_attr(kA=instance.kA.val * 2 / 3) b.set_attr(P=None) self.nw.solve('design') convergence_check(self.nw.lin_dep) # test heat transfer Q = self.c1.m.val_SI * (self.c2.h.val_SI - self.c1.h.val_SI) msg = ( 'Value of heat flow must be ' + str(round(Q_design * 2 / 3, 0)) + ', is ' + str(round(Q, 0)) + '.') assert round(Q, 1) == round(Q_design * 2 / 3, 1), msg # back to design case instance.set_attr(kA=None) b.set_attr(P=Q_design) self.nw.solve('design') convergence_check(self.nw.lin_dep) # check heat transfer Q = self.c1.m.val_SI * (self.c2.h.val_SI - self.c1.h.val_SI) td_log = ((self.c2.T.val - self.c3.T.val - self.c1.T.val + self.c4.T.val) / np.log((self.c2.T.val - self.c3.T.val) / (self.c1.T.val - self.c4.T.val))) kA = round(-Q / td_log, 0) msg = ('Value of heat transfer must be ' + str(round(Q, 0)) + ', is ' + str(round(instance.Q.val, 0)) + '.') assert round(Q, 0) == round(instance.Q.val, 0), msg # check upper terminal temperature difference msg = ('Value of terminal temperature difference must be ' + str(round(instance.ttd_u.val, 1)) + ', is ' + str(round(self.c1.T.val - self.c4.T.val, 1)) + '.') ttd_u_calc = round(self.c1.T.val - self.c4.T.val, 1) ttd_u = round(instance.ttd_u.val, 1) assert ttd_u_calc == ttd_u, msg # check lower terminal temperature difference 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) + '.') ttd_l_calc = round(self.c2.T.val - self.c3.T.val, 1) ttd_l = round(instance.ttd_l.val, 1) assert ttd_l_calc == ttd_l, msg # check specified kA value (by offdesign parameter), reset temperatures # to design state 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)) + '.') assert round(Q, 0) == round(instance.Q.val, 0), msg msg = ('Value of heat transfer coefficient must be ' + str(kA) + ', is ' + str(round(instance.kA.val, 0)) + '.') assert kA == round(instance.kA.val, 0), msg # trigger negative lower terminal temperature difference as result 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)) + '.') assert instance.ttd_l.val < 0, msg # trigger negative upper terminal temperature difference as result self.c4.set_attr(T=100) self.c2.set_attr(h=200e3, T=np.nan) instance.set_attr(pr1=0.98, pr2=0.98, ttd_u=np.nan, design=['pr1', 'pr2']) 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)) + '.') assert instance.ttd_u.val < 0, msg shutil.rmtree('./tmp', ignore_errors=True)
class TestClausiusRankine: def setup(self): """Set up clausis rankine cycle with turbine driven feed water pump.""" self.Tamb = 20 self.pamb = 1 fluids = ['water'] self.nw = Network(fluids=fluids) self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg') # create components splitter1 = Splitter('splitter 1') merge1 = Merge('merge 1') turb = Turbine('turbine') fwp_turb = Turbine('feed water pump turbine') condenser = HeatExchangerSimple('condenser') fwp = Pump('pump') steam_generator = HeatExchangerSimple('steam generator') cycle_close = CycleCloser('cycle closer') # create busses # power output bus self.power = Bus('power_output') self.power.add_comps({'comp': turb, 'char': 1}) # turbine driven feed water pump internal bus self.fwp_power = Bus('feed water pump power', P=0) self.fwp_power.add_comps({ 'comp': fwp_turb, 'char': 1 }, { 'comp': fwp, 'char': 1, 'base': 'bus' }) # heat input bus self.heat = Bus('heat_input') self.heat.add_comps({'comp': steam_generator, 'base': 'bus'}) self.nw.add_busses(self.power, self.fwp_power, self.heat) # create connections fs_in = Connection(cycle_close, 'out1', splitter1, 'in1', label='fs') fs_fwpt = Connection(splitter1, 'out1', fwp_turb, 'in1') fs_t = Connection(splitter1, 'out2', turb, 'in1') fwpt_ws = Connection(fwp_turb, 'out1', merge1, 'in1') t_ws = Connection(turb, 'out1', merge1, 'in2') ws = Connection(merge1, 'out1', condenser, 'in1') cond = Connection(condenser, 'out1', fwp, 'in1', label='cond') fw = Connection(fwp, 'out1', steam_generator, 'in1', label='fw') fs_out = Connection(steam_generator, 'out1', cycle_close, 'in1') self.nw.add_conns(fs_in, fs_fwpt, fs_t, fwpt_ws, t_ws, ws, cond, fw, fs_out) # component parameters turb.set_attr(eta_s=1) fwp_turb.set_attr(eta_s=1) condenser.set_attr(pr=1) fwp.set_attr(eta_s=1) steam_generator.set_attr(pr=1) # connection parameters fs_in.set_attr(m=10, p=120, T=600, fluid={'water': 1}) cond.set_attr(T=self.Tamb, x=0) # solve network self.nw.solve('design') convergence_check(self.nw.lin_dep) def test_exergy_analysis_perfect_cycle(self): """Test exergy analysis in the perfect clausius rankine cycle.""" self.nw.exergy_analysis(self.pamb, self.Tamb, E_P=[self.power], E_F=[self.heat], internal_busses=[self.fwp_power]) msg = ('Exergy destruction of this network must be 0 (smaller than ' + str(err**0.5) + ') for this test but is ' + str(round(abs(self.nw.E_D), 4)) + ' .') assert abs(self.nw.E_D) <= err**0.5, msg msg = ('Exergy efficiency of this network must be 1 for this test but ' 'is ' + str(round(self.nw.epsilon, 4)) + ' .') assert round(self.nw.epsilon, 4) == 1, msg exergy_balance = self.nw.E_F - self.nw.E_P - self.nw.E_L - self.nw.E_D msg = ('Exergy balance must be closed (residual value smaller than ' + str(err**0.5) + ') for this test but is ' + str(round(abs(exergy_balance), 4)) + ' .') assert abs(exergy_balance) <= err**0.5, msg msg = ( 'Fuel exergy and product exergy must be identical for this test. ' 'Fuel exergy value: ' + str(round(self.nw.E_F, 4)) + '. Product exergy value: ' + str(round(self.nw.E_P, 4)) + '.') assert round(abs(self.nw.E_F - self.nw.E_P), 4) < err**0.5, msg def test_entropy_perfect_cycle(self): """Test entropy values in the perfect clausius rankine cycle.""" labels = [ 'turbine', 'feed water pump turbine', 'condenser', 'steam generator', 'pump' ] for label in labels: cp = self.nw.get_comp(label) msg = ( 'Entropy production due to irreversibility must be 0 for all ' 'components in this test but is ' + str(round(cp.S_irr, 4)) + ' at component ' + label + ' of type ' + cp.component() + '.') assert round(cp.S_irr, 4) == 0, msg sg = self.nw.get_comp('steam generator') cd = self.nw.get_comp('condenser') msg = ( 'Value of entropy production due to heat input at steam generator ' '(S_Q=' + str(round(sg.S_Q, 4)) + ') must equal the negative ' 'value of entropy reduction in condenser (S_Q=' + str(round(cd.S_Q, 4)) + ').') assert round(sg.S_Q, 4) == -round(cd.S_Q, 4), msg def test_exergy_analysis_violated_balance(self): """Test exergy analysis with violated balance.""" # specify efficiency values for the internal bus self.nw.del_busses(self.fwp_power) self.fwp_power = Bus('feed water pump power', P=0) self.fwp_power.add_comps( { 'comp': self.nw.get_comp('feed water pump turbine'), 'char': 0.99 }, { 'comp': self.nw.get_comp('pump'), 'char': 0.98, 'base': 'bus' }) self.nw.add_busses(self.fwp_power) self.nw.solve('design') convergence_check(self.nw.lin_dep) # miss out on internal bus in exergy_analysis self.nw.exergy_analysis(self.pamb, self.Tamb, E_P=[self.power], E_F=[self.heat]) exergy_balance = self.nw.E_F - self.nw.E_P - self.nw.E_L - self.nw.E_D msg = ('Exergy balance must be violated for this test (larger than ' + str(err**0.5) + ') but is ' + str(round(abs(exergy_balance), 4)) + ' .') assert abs(exergy_balance) > err**0.5, msg def test_exergy_analysis_bus_conversion(self): """Test exergy analysis bus conversion factors.""" # specify efficiency values for the internal bus self.nw.del_busses(self.fwp_power) self.fwp_power = Bus('feed water pump power', P=0) self.fwp_power.add_comps( { 'comp': self.nw.get_comp('feed water pump turbine'), 'char': 0.99 }, { 'comp': self.nw.get_comp('pump'), 'char': 0.98, 'base': 'bus' }) self.nw.add_busses(self.fwp_power) self.nw.solve('design') convergence_check(self.nw.lin_dep) # no exergy losses in this case self.nw.exergy_analysis(self.pamb, self.Tamb, E_P=[self.power], E_F=[self.heat], internal_busses=[self.fwp_power]) label = 'pump on bus feed water pump power' eps = self.nw.component_exergy_data.loc[label, 'epsilon'] msg = ('Pump exergy efficiency must be 0.98 but is ' + str(round(eps, 4)) + ' .') assert round(eps, 4) == 0.98, msg label = 'feed water pump turbine on bus feed water pump power' eps = self.nw.component_exergy_data.loc[label, 'epsilon'] eps = self.nw.component_exergy_data.loc[label, 'epsilon'] msg = ( 'Feed water pump turbine exergy efficiency must be 0.99 but is ' + str(round(eps, 4)) + ' .') assert round(eps, 4) == 0.99, msg def test_exergy_analysis_missing_E_F_E_P_information(self): """Test exergy analysis errors with missing information.""" with raises(TESPyNetworkError): self.nw.exergy_analysis(self.pamb, self.Tamb, E_P=[self.power], E_F=[]) with raises(TESPyNetworkError): self.nw.exergy_analysis(self.pamb, self.Tamb, E_P=[], E_F=[self.heat]) def test_exergy_analysis_component_on_two_busses(self): """Test exergy analysis errors with components on more than one bus.""" with raises(TESPyNetworkError): self.nw.exergy_analysis(self.pamb, self.Tamb, E_P=[self.power], E_F=[self.heat, self.power])
nw.add_conns(k4_pif21, pif21_h4) nw.add_conns(h4_pib21, pib21_k4) nw.add_subsys(h4) # %% busses # heat_losses = Bus('network losses') heat_consumer = Bus('network consumer') nw.check_network() for comp in nw.comps.index: if isinstance(comp, Pipe): comp.set_attr(Tamb=0) heat_losses.add_comps({'comp': comp}) if (isinstance(comp, HeatExchangerSimple) and not isinstance(comp, Pipe)): heat_consumer.add_comps({'comp': comp}) nw.add_busses(heat_losses, heat_consumer) # %% solve # design case: 0 °C ambient temperature nw.solve('design') nw.save('grid') document_model(nw) # no documentation of offedesign state added, as report creation takes # quite long with all characteristics applied, try it out yourself :)
class TestHeatPump: def setup(self): # %% network setup self.nw = Network(fluids=['water', 'NH3'], T_unit='C', p_unit='bar', h_unit='kJ / kg', m_unit='kg / s') # %% components # sources & sinks cc_coolant = CycleCloser('coolant cycle closer') cc_consumer = CycleCloser('consumer cycle closer') amb_in = Source('source ambient') amb_out = Sink('sink ambient') ic_in = Source('source intercool') ic_out = Sink('sink intercool') # consumer system cd = HeatExchanger('condenser') rp = Pump('recirculation pump') cons = HeatExchangerSimple('consumer') # evaporator system va = Valve('valve') dr = Drum('drum') ev = HeatExchanger('evaporator') su = HeatExchanger('superheater') pu = Pump('pump evaporator') # compressor-system cp1 = Compressor('compressor 1') cp2 = Compressor('compressor 2') he = HeatExchanger('intercooler') # busses self.power = Bus('total compressor power') self.power.add_comps({ 'comp': cp1, 'base': 'bus' }, { 'comp': cp2, 'base': 'bus' }) self.heat = Bus('total delivered heat') self.heat.add_comps({'comp': cd, 'char': -1}) self.nw.add_busses(self.power, self.heat) # %% connections # consumer system c_in_cd = Connection(cc_coolant, 'out1', cd, 'in1') cb_rp = Connection(cc_consumer, 'out1', rp, 'in1') rp_cd = Connection(rp, 'out1', cd, 'in2') self.cd_cons = Connection(cd, 'out2', cons, 'in1') cons_cf = Connection(cons, 'out1', cc_consumer, 'in1') self.nw.add_conns(c_in_cd, cb_rp, rp_cd, self.cd_cons, cons_cf) # connection condenser - evaporator system cd_va = Connection(cd, 'out1', va, 'in1') self.nw.add_conns(cd_va) # evaporator system va_dr = Connection(va, 'out1', dr, 'in1') dr_pu = Connection(dr, 'out1', pu, 'in1') pu_ev = Connection(pu, 'out1', ev, 'in2') ev_dr = Connection(ev, 'out2', dr, 'in2') dr_su = Connection(dr, 'out2', su, 'in2') self.nw.add_conns(va_dr, dr_pu, pu_ev, ev_dr, dr_su) self.amb_in_su = Connection(amb_in, 'out1', su, 'in1') su_ev = Connection(su, 'out1', ev, 'in1') ev_amb_out = Connection(ev, 'out1', amb_out, 'in1') self.nw.add_conns(self.amb_in_su, su_ev, ev_amb_out) # connection evaporator system - compressor system su_cp1 = Connection(su, 'out2', cp1, 'in1') self.nw.add_conns(su_cp1) # compressor-system cp1_he = Connection(cp1, 'out1', he, 'in1') he_cp2 = Connection(he, 'out1', cp2, 'in1') cp2_c_out = Connection(cp2, 'out1', cc_coolant, 'in1') ic_in_he = Connection(ic_in, 'out1', he, 'in2') he_ic_out = Connection(he, 'out2', ic_out, 'in1') self.nw.add_conns(cp1_he, he_cp2, ic_in_he, he_ic_out, cp2_c_out) # %% component parametrization # condenser system x = np.array([ 0, 0.0625, 0.125, 0.1875, 0.25, 0.3125, 0.375, 0.4375, 0.5, 0.5625, 0.6375, 0.7125, 0.7875, 0.9, 0.9875, 1, 1.0625, 1.125, 1.175, 1.2125, 1.2375, 1.25 ]) y = np.array([ 0.0076, 0.1390, 0.2731, 0.4003, 0.5185, 0.6263, 0.7224, 0.8056, 0.8754, 0.9312, 0.9729, 1.0006, 1.0203, 1.0158, 1.0051, 1.0000, 0.9746, 0.9289, 0.8832, 0.8376, 0.7843, 0.7614 ]) rp.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char'], eta_s_char={ 'char_func': CharLine(x, y), 'param': 'm' }) cons.set_attr(pr=1, design=['pr'], offdesign=['zeta']) # evaporator system x = np.linspace(0, 2.5, 26) y = np.array([ 0.000, 0.164, 0.283, 0.389, 0.488, 0.581, 0.670, 0.756, 0.840, 0.921, 1.000, 1.078, 1.154, 1.228, 1.302, 1.374, 1.446, 1.516, 1.585, 1.654, 1.722, 1.789, 1.855, 1.921, 1.986, 2.051 ]) kA_char1 = {'char_func': CharLine(x, y), 'param': 'm'} x = np.array([ 0.0100, 0.0400, 0.0700, 0.1100, 0.1500, 0.2000, 0.2500, 0.3000, 0.3500, 0.4000, 0.4500, 0.5000, 0.5500, 0.6000, 0.6500, 0.7000, 0.7500, 0.8000, 0.8500, 0.9000, 0.9500, 1.0000, 1.5000, 2.0000 ]) y = np.array([ 0.0185, 0.0751, 0.1336, 0.2147, 0.2997, 0.4118, 0.5310, 0.6582, 0.7942, 0.9400, 0.9883, 0.9913, 0.9936, 0.9953, 0.9966, 0.9975, 0.9983, 0.9988, 0.9992, 0.9996, 0.9998, 1.0000, 1.0008, 1.0014 ]) kA_char2 = {'char_func': CharLine(x, y), 'param': 'm'} ev.set_attr(pr1=1, pr2=.999, ttd_l=5, design=['ttd_l'], offdesign=['kA_char'], kA_char1=kA_char1, kA_char2=kA_char2) # no kA modification for hot side! x = np.array([0, 1]) y = np.array([1, 1]) kA_char1 = {'char_func': CharLine(x, y), 'param': 'm'} # characteristic line for superheater kA x = np.array( [0, 0.045, 0.136, 0.244, 0.43, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2]) y = np.array( [0, 0.037, 0.112, 0.207, 0.5, 0.8, 0.85, 0.9, 0.95, 1, 1.04, 1.07]) kA_char2 = {'char_func': CharLine(x, y), 'param': 'm'} su.set_attr(kA_char1=kA_char1, kA_char2=kA_char2, offdesign=['zeta1', 'zeta2', 'kA_char']) x = np.array([ 0, 0.0625, 0.125, 0.1875, 0.25, 0.3125, 0.375, 0.4375, 0.5, 0.5625, 0.6375, 0.7125, 0.7875, 0.9, 0.9875, 1, 1.0625, 1.125, 1.175, 1.2125, 1.2375, 1.25 ]) y = np.array([ 0.0076, 0.1390, 0.2731, 0.4003, 0.5185, 0.6263, 0.7224, 0.8056, 0.8754, 0.9312, 0.9729, 1.0006, 1.0203, 1.0158, 1.0051, 1.0000, 0.9746, 0.9289, 0.8832, 0.8376, 0.7843, 0.7614 ]) pu.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char'], eta_s_char={ 'char_func': CharLine(x, y), 'param': 'm' }) # compressor system x = np.array([0, 0.4, 1, 1.2]) y = np.array([0.5, 0.9, 1, 1.1]) cp1.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char'], eta_s_char={ 'char_func': CharLine(x, y), 'param': 'm' }) cp2.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char'], eta_s_char={ 'char_func': CharLine(x, y), 'param': 'm' }) # characteristic line for intercooler kA x = np.linspace(0, 2.5, 26) y = np.array([ 0.0000, 0.2455, 0.3747, 0.4798, 0.5718, 0.6552, 0.7323, 0.8045, 0.8727, 0.9378, 1.0000, 1.0599, 1.1176, 1.1736, 1.2278, 1.2806, 1.3320, 1.3822, 1.4313, 1.4792, 1.5263, 1.5724, 1.6176, 1.6621, 1.7058, 1.7488 ]) kA_char1 = {'char_func': CharLine(x, y), 'param': 'm'} x = np.linspace(0, 2.5, 26) y = np.array([ 0.000, 0.164, 0.283, 0.389, 0.488, 0.581, 0.670, 0.756, 0.840, 0.921, 1.000, 1.078, 1.154, 1.228, 1.302, 1.374, 1.446, 1.516, 1.585, 1.654, 1.722, 1.789, 1.855, 1.921, 1.986, 2.051 ]) kA_char2 = {'char_func': CharLine(x, y), 'param': 'm'} he.set_attr(kA_char1=kA_char1, kA_char2=kA_char2, offdesign=['zeta1', 'zeta2', 'kA_char']) # characteristic line for condenser kA x = np.linspace(0, 2.5, 26) y = np.array([ 0.0000, 0.2455, 0.3747, 0.4798, 0.5718, 0.6552, 0.7323, 0.8045, 0.8727, 0.9378, 1.0000, 1.0599, 1.1176, 1.1736, 1.2278, 1.2806, 1.3320, 1.3822, 1.4313, 1.4792, 1.5263, 1.5724, 1.6176, 1.6621, 1.7058, 1.7488 ]) kA_char1 = {'char_func': CharLine(x, y), 'param': 'm'} x = np.linspace(0, 2.5, 26) y = np.array([ 0.000, 0.164, 0.283, 0.389, 0.488, 0.581, 0.670, 0.756, 0.840, 0.921, 1.000, 1.078, 1.154, 1.228, 1.302, 1.374, 1.446, 1.516, 1.585, 1.654, 1.722, 1.789, 1.855, 1.921, 1.986, 2.051 ]) kA_char2 = {'char_func': CharLine(x, y), 'param': 'm'} cd.set_attr(kA_char1=kA_char1, kA_char2=kA_char2, pr2=0.9998, design=['pr2'], offdesign=['zeta2', 'kA_char']) # %% connection parametrization # condenser system c_in_cd.set_attr(fluid={'water': 0, 'NH3': 1}, p=60) rp_cd.set_attr(T=60, fluid={'water': 1, 'NH3': 0}, p=10) self.cd_cons.set_attr(T=105) cd_va.set_attr(p=Ref(c_in_cd, 1, -0.01), Td_bp=-5, design=['Td_bp']) # evaporator system cold side pu_ev.set_attr(m=Ref(va_dr, 10, 0), p0=5) dr_su.set_attr(p0=5, T=5) su_cp1.set_attr(p=Ref(dr_su, 1, -0.05), Td_bp=5, design=['Td_bp', 'p']) # evaporator system hot side self.amb_in_su.set_attr(m=20, T=12, p=1, fluid={'water': 1, 'NH3': 0}) su_ev.set_attr(p=Ref(self.amb_in_su, 1, -0.001), design=['p']) ev_amb_out.set_attr() # compressor-system cp1_he.set_attr(p=15) he_cp2.set_attr(T=40, p=Ref(cp1_he, 1, -0.01), design=['T', 'p']) ic_in_he.set_attr(p=1, T=20, m=5, fluid={'water': 1, 'NH3': 0}) he_ic_out.set_attr(p=Ref(ic_in_he, 1, -0.002), design=['p']) def test_model(self): """ Test the operating points of the heat pump against a different model. By now, not all characteristic functions of the original model are available in detail, thus perfect matching is not possible! """ self.nw.solve('design') self.nw.save('tmp') self.nw.print_results() # input values from ebsilon T = [105, 100, 90, 80] m_source = np.array([[23, 22, 20, 18, 16], [27, 24, 20, 16, 12], [31, 25, 20, 15, 10], [33, 26, 20, 15, 10]]) COP = np.array([[2.436, 2.414, 2.368, 2.338, 2.287], [2.591, 2.523, 2.448, 2.355, 2.216], [2.777, 2.635, 2.557, 2.442, 2.243], [2.866, 2.711, 2.629, 2.528, 2.351]]) i = 0 for T in T: j = 0 self.cd_cons.set_attr(T=T) for m in m_source[i]: self.amb_in_su.set_attr(m=m) if j == 0: self.nw.solve('offdesign', design_path='tmp', init_path='tmp') else: self.nw.solve('offdesign', design_path='tmp') # relative deviation should not exceed 6.5 % # this should be much less, unfortunately not all ebsilon # characteristics are available, thus it is # difficult/impossible to match the models perfectly! d_rel_COP = abs(self.heat.P.val / self.power.P.val - COP[i, j]) / COP[i, j] msg = ('The deviation in COP should be less than 0.065, is ' + str(d_rel_COP) + ' at mass flow ' + str(m) + ' and temperature ' + str(T) + '.') assert d_rel_COP < 0.065, msg j += 1 i += 1 shutil.rmtree('./tmp', ignore_errors=True)
class PowerPlant(): def __init__(self): self.nw = Network(fluids=['BICUBIC::water'], p_unit='bar', T_unit='C', h_unit='kJ / kg', iterinfo=False) # components # main cycle eco = HeatExchangerSimple('economizer') eva = HeatExchangerSimple('evaporator') sup = HeatExchangerSimple('superheater') cc = CycleCloser('cycle closer') hpt = Turbine('high pressure turbine') sp1 = Splitter('splitter 1', num_out=2) mpt = Turbine('mid pressure turbine') sp2 = Splitter('splitter 2', num_out=2) lpt = Turbine('low pressure turbine') con = Condenser('condenser') pu1 = Pump('feed water pump') fwh1 = Condenser('feed water preheater 1') fwh2 = Condenser('feed water preheater 2') dsh = Desuperheater('desuperheater') me2 = Merge('merge2', num_in=2) pu2 = Pump('feed water pump 2') pu3 = Pump('feed water pump 3') me = Merge('merge', num_in=2) # cooling water cwi = Source('cooling water source') cwo = Sink('cooling water sink') # connections # main cycle cc_hpt = Connection(cc, 'out1', hpt, 'in1', label='feed steam') hpt_sp1 = Connection(hpt, 'out1', sp1, 'in1', label='extraction1') sp1_mpt = Connection(sp1, 'out1', mpt, 'in1', state='g') mpt_sp2 = Connection(mpt, 'out1', sp2, 'in1', label='extraction2') sp2_lpt = Connection(sp2, 'out1', lpt, 'in1') lpt_con = Connection(lpt, 'out1', con, 'in1') con_pu1 = Connection(con, 'out1', pu1, 'in1') pu1_fwh1 = Connection(pu1, 'out1', fwh1, 'in2') fwh1_me = Connection(fwh1, 'out2', me, 'in1', state='l') me_fwh2 = Connection(me, 'out1', fwh2, 'in2', state='l') fwh2_dsh = Connection(fwh2, 'out2', dsh, 'in2', state='l') dsh_me2 = Connection(dsh, 'out2', me2, 'in1') me2_eco = Connection(me2, 'out1', eco, 'in1', state='l') eco_eva = Connection(eco, 'out1', eva, 'in1') eva_sup = Connection(eva, 'out1', sup, 'in1') sup_cc = Connection(sup, 'out1', cc, 'in1') self.nw.add_conns(cc_hpt, hpt_sp1, sp1_mpt, mpt_sp2, sp2_lpt, lpt_con, con_pu1, pu1_fwh1, fwh1_me, me_fwh2, fwh2_dsh, dsh_me2, me2_eco, eco_eva, eva_sup, sup_cc) # cooling water cwi_con = Connection(cwi, 'out1', con, 'in2') con_cwo = Connection(con, 'out2', cwo, 'in1') self.nw.add_conns(cwi_con, con_cwo) # preheating sp1_dsh = Connection(sp1, 'out2', dsh, 'in1') dsh_fwh2 = Connection(dsh, 'out1', fwh2, 'in1') fwh2_pu2 = Connection(fwh2, 'out1', pu2, 'in1') pu2_me2 = Connection(pu2, 'out1', me2, 'in2') sp2_fwh1 = Connection(sp2, 'out2', fwh1, 'in1') fwh1_pu3 = Connection(fwh1, 'out1', pu3, 'in1') pu3_me = Connection(pu3, 'out1', me, 'in2') self.nw.add_conns(sp1_dsh, dsh_fwh2, fwh2_pu2, pu2_me2, sp2_fwh1, fwh1_pu3, pu3_me) # busses # power bus self.power = Bus('power') self.power.add_comps({ 'comp': hpt, 'char': -1 }, { 'comp': mpt, 'char': -1 }, { 'comp': lpt, 'char': -1 }, { 'comp': pu1, 'char': -1 }, { 'comp': pu2, 'char': -1 }, { 'comp': pu3, 'char': -1 }) # heating bus self.heat = Bus('heat') self.heat.add_comps({ 'comp': eco, 'char': 1 }, { 'comp': eva, 'char': 1 }, { 'comp': sup, 'char': 1 }) self.nw.add_busses(self.power, self.heat) # parametrization # components hpt.set_attr(eta_s=0.9) mpt.set_attr(eta_s=0.9) lpt.set_attr(eta_s=0.9) pu1.set_attr(eta_s=0.8) pu2.set_attr(eta_s=0.8) pu3.set_attr(eta_s=0.8) eco.set_attr(pr=0.99) eva.set_attr(pr=0.99) sup.set_attr(pr=0.99) con.set_attr(pr1=1, pr2=0.99, ttd_u=5) fwh1.set_attr(pr1=1, pr2=0.99, ttd_u=5) fwh2.set_attr(pr1=1, pr2=0.99, ttd_u=5) dsh.set_attr(pr1=0.99, pr2=0.99) # connections eco_eva.set_attr(x=0) eva_sup.set_attr(x=1) cc_hpt.set_attr(m=200, T=650, p=100, fluid={'water': 1}) hpt_sp1.set_attr(p=20) mpt_sp2.set_attr(p=3) lpt_con.set_attr(p=0.05) cwi_con.set_attr(T=20, p=10, fluid={'water': 1}) # test run self.nw.solve('design') document_model(self.nw) def calculate_efficiency(self, x): # set extraction pressure self.nw.get_conn('extraction1').set_attr(p=x[0]) self.nw.get_conn('extraction2').set_attr(p=x[1]) self.nw.solve('design') for cp in self.nw.comps['object']: if isinstance(cp, Condenser) or isinstance(cp, Desuperheater): if cp.Q.val > 0: return np.nan elif isinstance(cp, Pump): if cp.P.val < 0: return np.nan elif isinstance(cp, Turbine): if cp.P.val > 0: return np.nan if self.nw.res[-1] > 1e-3 or self.nw.lin_dep: return np.nan else: return self.nw.busses['power'].P.val / self.nw.busses['heat'].P.val
class TestRefrigerator: def setup(self): """Set up simple refrigerator.""" self.Tamb = 20 self.pamb = 1 fluids = ['R134a'] self.nw = Network(fluids=fluids) self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg') # create components va = Valve('expansion valve') cp = Compressor('compressor') cond = HeatExchangerSimple('condenser') eva = HeatExchangerSimple('evaporator') cc = CycleCloser('cycle closer') # create busses # power output bus self.power = Bus('power input') self.power.add_comps({'comp': cp, 'char': 1, 'base': 'bus'}) # cooling bus self.cool = Bus('heat from fridge') self.cool.add_comps({'comp': eva}) # heat input bus self.heat = Bus('heat to ambient') self.heat.add_comps({'comp': cond}) self.nw.add_busses(self.power, self.cool, self.heat) # create connections cc_cp = Connection(cc, 'out1', cp, 'in1', label='from eva') cp_cond = Connection(cp, 'out1', cond, 'in1', label='to cond') cond_va = Connection(cond, 'out1', va, 'in1', label='from cond') va_eva = Connection(va, 'out1', eva, 'in1', label='to eva') eva_cc = Connection(eva, 'out1', cc, 'in1') self.nw.add_conns(cc_cp, cp_cond, cond_va, va_eva, eva_cc) # component parameters cp.set_attr(eta_s=0.9) cond.set_attr(pr=0.97) eva.set_attr(pr=0.96) # connection parameters cc_cp.set_attr(m=1, x=1, T=-25, fluid={'R134a': 1}) cond_va.set_attr(x=0, T=self.Tamb + 1) # solve network self.nw.solve('design') convergence_check(self.nw.lin_dep) def test_exergy_analysis_bus_conversion(self): """Test exergy analysis at product exergy with T < Tamb.""" # no exergy losses in this case ean = ExergyAnalysis(self.nw, E_P=[self.cool], E_F=[self.power]) ean.analyse(pamb=self.pamb, Tamb=self.Tamb) exergy_balance = (ean.network_data.E_F - ean.network_data.E_P - ean.network_data.E_L - ean.network_data.E_D) msg = ('Exergy balance must be closed (residual value smaller than ' + str(err**0.5) + ') for this test but is ' + str(round(abs(exergy_balance), 4)) + ' .') assert abs(exergy_balance) <= err**0.5, msg
class TestCompressedAirOut: def setup(self): """Set up air compressed air turbine.""" self.Tamb = 20 self.pamb = 1 fluids = ['Air'] # turbine part self.nw = Network(fluids=fluids) self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg') # components cas = Source('compressed air storage') reheater = HeatExchangerSimple('reheating') turb = Turbine('turbine') amb = Sink('air outlet') # power ouput bus self.power_out = Bus('power output') self.power_out.add_comps({'comp': turb, 'char': 1}) # compressed air bus self.cas_out = Bus('exergy in') self.cas_out.add_comps({ 'comp': cas, 'base': 'bus' }, { 'comp': reheater, 'base': 'bus' }) # exergy loss bus self.ex_loss = Bus('exergy loss') self.ex_loss.add_comps({'comp': amb, 'base': 'component'}) self.nw.add_busses(self.power_out, self.cas_out) # create connections cas_reheater = Connection(cas, 'out1', reheater, 'in1') reheater_turb = Connection(reheater, 'out1', turb, 'in1') turb_amb = Connection(turb, 'out1', amb, 'in1', label='outlet') self.nw.add_conns(cas_reheater, reheater_turb, turb_amb) # component parameters turb.set_attr(eta_s=1) reheater.set_attr(pr=1) # connection parameters cas_reheater.set_attr(m=2, T=self.Tamb, p=10, fluid={'Air': 1}) reheater_turb.set_attr() turb_amb.set_attr(p=self.pamb, T=self.Tamb) # solve network self.nw.solve('design') convergence_check(self.nw.lin_dep) def test_exergy_analysis_bus_conversion(self): """Test exergy analysis at product exergy with T < Tamb.""" ean = ExergyAnalysis(self.nw, E_P=[self.power_out], E_F=[self.cas_out], E_L=[self.ex_loss]) ean.analyse(pamb=self.pamb, Tamb=self.Tamb) exergy_balance = (ean.network_data.E_F - ean.network_data.E_P - ean.network_data.E_L - ean.network_data.E_D) msg = ('Exergy balance must be closed (residual value smaller than ' + str(err**0.5) + ') for this test but is ' + str(round(abs(exergy_balance), 4)) + '.') assert abs(exergy_balance) <= err**0.5, msg msg = ('Exergy efficiency must be equal to 1.0 for this test but is ' + str(round(ean.network_data.epsilon, 4)) + '.') assert round(ean.network_data.epsilon, 4) == 1, msg c = self.nw.get_conn('outlet') c.set_attr(T=self.Tamb - 20) self.nw.solve('design') convergence_check(self.nw.lin_dep) ean.analyse(pamb=self.pamb, Tamb=self.Tamb) msg = ( 'Exergy destruction must be equal to 0.0 for this test but is ' + str(round(ean.network_data.E_D, 4)) + '.') assert round(ean.network_data.E_D, 4) == 0, msg msg = ('Exergy loss must be equal to ' + str(round(c.Ex_physical, 4)) + ' for this test but is ' + str(round(ean.network_data.E_L, 4)) + '.') assert round(ean.network_data.E_L, 4) == round(c.Ex_physical, 4), msg
gen1 = CharLine(x=x, y=y) gen2 = CharLine(x=x, y=y) gen3 = CharLine(x=x, y=y) power = Bus('power output') power.add_comps({ 'c': g_turb, 'char': gen1 }, { 'c': comp, 'char': 1 }, { 'c': comp_fuel, 'char': mot1 }, { 'c': turb_hp, 'char': gen2 }, { 'c': pump1, 'char': mot2 }, { 'c': turb_lp, 'char': gen3 }, { 'c': pump2, 'char': mot3 }) gt_power = Bus('gas turbine power output') gt_power.add_comps({'c': g_turb}, {'c': comp}) heat_out = Bus('heat output')
# district heating dh_Source_con = Connection(dh_Source, 'out1', con, 'in2') con_dh_Sink = Connection(con, 'out2', dh_Sink, 'in1') nw.add_conns(dh_Source_con, con_dh_Sink) # %% Busses # power Bus power = Bus('power output') x = np.array([0.2, 0.4, 0.6, 0.8, 1.0, 1.1]) y = np.array([0.85, 0.93, 0.95, 0.96, 0.97, 0.96]) # create a characteristic line for a generator gen = CharLine(x=x, y=y) power.add_comps({'comp': st, 'char': gen}, {'comp': pu, 'char': gen}) nw.add_busses(power) # %% parameterisation # components st.set_attr(eta_s=0.9, design=['eta_s'], offdesign=['eta_s_char', 'cone']) con.set_attr(pr1=0.99, pr2=0.99, ttd_u=5, design=['pr2', 'ttd_u'], offdesign=['kA_char']) pu.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) sg1.set_attr(pr=0.99) sg2.set_attr(pr=0.99) sg3.set_attr(pr=.99)
he_tes = Connection(he, 'out2', tesin, 'in1') hs_he = Connection(hsout, 'out1', he, 'in1') he_hs = Connection(he, 'out1', hsin, 'in1') nw.add_conns(tes_he, he_tes, hs_he, he_hs) # %% busses heat = Bus('heat system') heat_sto = Bus('heat storage') # needs these busses, even if they are empty power = Bus('power') ti = Bus('ti') heat.add_comps({'comp': he, 'char': -1}) heat_sto.add_comps({'comp': he, 'char': -1}) nw.add_busses(heat, heat_sto, power, ti) # %% parameters he.set_attr(pr1=0.98, pr2=0.98, design=['pr1', 'pr2'], offdesign=['zeta1', 'zeta2', 'kA_char']) # %% Schnittstellenparameter hs_he.set_attr(T=90, p=10, fluid={'water': 1}) he_hs.set_attr(T=60, design=['T']) tes_he.set_attr(T=40, p=10, fluid={'water': 1}) he_tes.set_attr(T=75)
class TestExpansion: def setup(self): self.Tamb = 20 self.pamb = 1 fluids = ['Air'] # turbine part self.nw = Network(fluids=fluids) self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg') # components so = Source('inlet') tu = Turbine('compressor') si = Sink('outlet') # fuel exergy bus self.exergy_fuel = Bus('fuel exergy') self.exergy_fuel.add_comps({'comp': si}, {'comp': so, 'base': 'bus'}) # product exergy bus self.exergy_prod = Bus('product exergy') self.exergy_prod.add_comps({'comp': tu, 'char': 0.9}) # create connections c1 = Connection(so, 'out1', tu, 'in1', '1') c2 = Connection(tu, 'out1', si, 'in1', '2') self.nw.add_conns(c1, c2) # component parameters tu.set_attr(eta_s=0.85, pr=1 / 5) # connection parameters c1.set_attr(m=2, p=10, fluid={'Air': 1}) c2.set_attr(T=self.Tamb) # solve network self.nw.solve('design') def test_larger_T0(self): self.nw.get_conn('2').set_attr(T=self.Tamb + 10) self.nw.solve('design') self.run_analysis() def test_T0_cross(self): self.nw.get_conn('2').set_attr(T=self.Tamb - 30) self.nw.solve('design') self.run_analysis() def test_smaller_T0(self): self.nw.get_conn('1').set_attr(T=self.Tamb - 10) self.nw.get_conn('2').set_attr(T=None) self.nw.solve('design') self.run_analysis() def run_analysis(self): ean = ExergyAnalysis(self.nw, E_P=[self.exergy_prod], E_F=[self.exergy_fuel]) ean.analyse(pamb=self.pamb, Tamb=self.Tamb) exergy_balance = (ean.network_data.E_F - ean.network_data.E_P - ean.network_data.E_L - ean.network_data.E_D) msg = ('Exergy balance must be closed (residual value smaller than ' + str(err**0.5) + ') for this test but is ' + str(round(abs(exergy_balance), 4)) + '.') assert abs(exergy_balance) <= err**0.5, msg E_D_agg = ean.aggregation_data['E_D'].sum() E_D_nw = ean.network_data.loc['E_D'] msg = ('The exergy destruction of the aggregated components and ' 'respective busses (' + str(round(E_D_agg)) + ') must be equal to ' 'the exergy destruction of the network (' + str(round(E_D_nw)) + ').') assert E_D_agg == E_D_nw, msg
def setup(self): """Set up the model.""" # %% network setup fluid_list = ['Ar', 'N2', 'O2', 'CO2', 'CH4', 'H2O'] self.nw = Network(fluids=fluid_list, p_unit='bar', T_unit='C', p_range=[0.5, 20]) # %% components amb = Source('ambient') sf = Source('fuel') cc = CombustionChamber('combustion') cp = Compressor('compressor') gt = Turbine('turbine') fg = Sink('flue gas outlet') # %% connections amb_cp = Connection(amb, 'out1', cp, 'in1', label='ambient air flow') cp_cc = Connection(cp, 'out1', cc, 'in1') sf_cc = Connection(sf, 'out1', cc, 'in2') cc_gt = Connection(cc, 'out1', gt, 'in1') gt_fg = Connection(gt, 'out1', fg, 'in1') self.nw.add_conns(amb_cp, cp_cc, sf_cc, cc_gt, gt_fg) # %% component parameters cc.set_attr(lamb=3) cp.set_attr(eta_s=0.9, pr=15) gt.set_attr(eta_s=0.9) # %% connection parameters amb_cp.set_attr(T=20, p=1, m=100, fluid={ 'Ar': 0.0129, 'N2': 0.7553, 'H2O': 0, 'CH4': 0, 'CO2': 0.0004, 'O2': 0.2314 }) sf_cc.set_attr(T=20, fluid={ 'CO2': 0.04, 'Ar': 0, 'N2': 0, 'O2': 0, 'H2O': 0, 'CH4': 0.96 }) gt_fg.set_attr(p=1) # motor efficiency x = np.array([ 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.05, 1.1, 1.15, 1.2, 10 ]) y = np.array([ 0.01, 0.3148, 0.5346, 0.6843, 0.7835, 0.8477, 0.8885, 0.9145, 0.9318, 0.9443, 0.9546, 0.9638, 0.9724, 0.9806, 0.9878, 0.9938, 0.9982, 0.999, 0.9995, 0.9999, 1, 0.9977, 0.9947, 0.9909, 0.9853, 0.9644 ]) * 0.975 self.motor_bus_based = CharLine(x=x, y=y) self.motor_comp_based = CharLine(x=x, y=1 / y) # generator efficiency x = np.array([ 0.100, 0.345, 0.359, 0.383, 0.410, 0.432, 0.451, 0.504, 0.541, 0.600, 0.684, 0.805, 1.000, 1.700, 10 ]) y = np.array([ 0.976, 0.989, 0.990, 0.991, 0.992, 0.993, 0.994, 0.995, 0.996, 0.997, 0.998, 0.999, 1.000, 0.999, 0.99 ]) * 0.975 self.generator = CharLine(x=x, y=y) power_bus_total = Bus('total power output') power_bus_total.add_comps( { 'comp': cp, 'char': self.motor_bus_based, 'base': 'bus' }, { 'comp': gt, 'char': self.generator }) thermal_input = Bus('thermal input') thermal_input.add_comps({'comp': cc}) compressor_power_comp = Bus('compressor power input') compressor_power_comp.add_comps({ 'comp': cp, 'char': self.motor_comp_based }) compressor_power_bus = Bus('compressor power input bus based') compressor_power_bus.add_comps({ 'comp': cp, 'char': self.motor_bus_based, 'base': 'bus' }) self.nw.add_busses(power_bus_total, thermal_input, compressor_power_comp, compressor_power_bus) # %% solving self.nw.solve('design') self.nw.save('tmp')
def test_CombustionEngine(self): """Test component properties of combustion engine.""" instance = CombustionEngine('combustion engine') self.setup_CombustionEngine_network(instance) air = { 'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129, 'H2O': 0, 'CO2': 0, 'CH4': 0 } fuel = {'N2': 0, 'O2': 0, 'Ar': 0, 'H2O': 0, 'CO2': 0.04, 'CH4': 0.96} water1 = {'N2': 0, 'O2': 0, 'Ar': 0, 'H2O': 1, 'CO2': 0, 'CH4': 0} water2 = {'N2': 0, 'O2': 0, 'Ar': 0, 'H2O': 1, 'CO2': 0, 'CH4': 0} # connection parametrisation instance.set_attr(pr1=0.99, pr2=0.99, lamb=1.0, design=['pr1', 'pr2'], offdesign=['zeta1', 'zeta2']) self.c1.set_attr(p=5, T=30, fluid=air) self.c2.set_attr(T=30, fluid=fuel) self.c4.set_attr(p=3, T=60, m=50, fluid=water1) self.c5.set_attr(p=3, T=80, m=50, fluid=water2) # create busses TI = Bus('thermal input') Q1 = Bus('heat output 1') Q2 = Bus('heat output 2') Q = Bus('heat output') Qloss = Bus('thermal heat loss') TI.add_comps({'comp': instance, 'param': 'TI'}) Q1.add_comps({'comp': instance, 'param': 'Q1'}) Q2.add_comps({'comp': instance, 'param': 'Q2'}) Q.add_comps({'comp': instance, 'param': 'Q'}) Qloss.add_comps({'comp': instance, 'param': 'Qloss'}) self.nw.add_busses(TI, Q1, Q2, Q, Qloss) # test specified thermal input bus value 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) + '.') assert round(TI.P.val, 1) == round(instance.ti.val, 1), msg # test specified thermal input in component 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) + '.') assert round(ti, 1) == round(instance.ti.val, 1), msg instance.set_attr(ti=None) # 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 ' + str(instance.ti.val) + '.') assert round(ti, 1) == round(instance.ti.val, 1), msg # calculate heat output over cooling loop heat1 = self.c4.m.val_SI * (self.c6.h.val_SI - self.c4.h.val_SI) msg = ('Value of heat output 1 must be ' + str(-heat1) + ', is ' + str(instance.Q1.val) + '.') assert round(heat1, 1) == -round(instance.Q1.val, 1), msg Q1.set_attr(P=np.nan) # 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) msg = ('Value of heat output 2 must be ' + str(-heat2) + ', is ' + str(instance.Q2.val) + '.') assert round(heat2, 1) == -round(instance.Q2.val, 1), msg # test specified heat output 2 in component 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) + '.') assert round(heat2, 1) == -round(instance.Q2.val, 1), msg # test total heat output bus value 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) + ', is ' + str(-heat) + '.') assert round(Q.P.val, 1) == -round(heat, 1), msg # test specified heat loss bus value 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) + '.') assert round(Qloss.P.val, 1) == round(instance.Qloss.val, 1), msg shutil.rmtree('./tmp', ignore_errors=True)
# cooling water cw_in = Connection(source_cw, 'out1', cond, 'in2') cw_out = Connection(cond, 'out2', sink_cw, 'in1') nw.add_conns(cw_in, cw_out) # %% busses x = np.array([0, 0.2, 0.4, 0.6, 0.8, 1, 1.2]) y = np.array([0.5, 0.87, 0.91, 0.94, 0.96, 0.97, 0.96]) char = CharLine(x, y) # power bus power_bus = Bus('power') power_bus.add_comps( {'comp': turbine_hp, 'char': char, 'base': 'component'}, {'comp': turbine_lp, 'char': char, 'base': 'component'}, {'comp': pump, 'char': char, 'base': 'bus'}) # heating bus heat_bus = Bus('heat') heat_bus.add_comps({'comp': cond, 'char': -1}) nw.add_busses(power_bus, heat_bus) # %% parametrization of components turbine_hp.set_attr(eta_s=0.9, design=['eta_s'], offdesign=['eta_s_char', 'cone']) turbine_lp.set_attr(eta_s=0.9, design=['eta_s'], offdesign=['eta_s_char', 'cone'])
def test_HeatExhangerSimple(self): """Test component properties of simple heat exchanger.""" instance = HeatExchangerSimple('heat exchanger') self.setup_HeatExchangerSimple_network(instance) fl = {'Ar': 0, 'H2O': 1, 'S800': 0} self.c1.set_attr(fluid=fl, m=1, p=10, T=100) # trigger heat exchanger parameter groups instance.set_attr(hydro_group='HW', L=100, ks=100, pr=0.99, Tamb=20) # test grouped parameter settings with missing parameters instance.hydro_group.is_set = True instance.kA_group.is_set = True instance.kA_char_group.is_set = True self.nw.solve('design', init_only=True) msg = ('Hydro group must no be set, if one parameter is missing!') assert instance.hydro_group.is_set is False, msg msg = ('kA group must no be set, if one parameter is missing!') assert instance.kA_group.is_set is False, msg msg = ('kA char group must no be set, if one parameter is missing!') assert instance.kA_char_group.is_set is False, msg # test diameter calculation from specified dimensions (as pipe) # with Hazen-Williams method instance.set_attr(hydro_group='HW', D='var', L=100, ks=100, pr=0.99, Tamb=20) b = Bus('heat', P=-1e5) b.add_comps({'comp': 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) + '.') assert pr == round(instance.pr.val, 3), msg # make zeta system variable and use previously calculated diameter # to calculate zeta. The value for zeta must not change zeta = round(instance.zeta.val, 0) 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)) + '.') assert zeta == round(instance.zeta.val, 0), msg # same test with pressure ratio as sytem variable 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)) + '.') assert pr == round(instance.pr.val, 3), msg # test heat transfer coefficient as variable of the system (ambient # temperature required) 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) msg = ('Value of heat transfer coefficient must be 677, is ' + str(instance.kA.val) + '.') assert 677 == round(instance.kA.val, 0), msg # test heat transfer as variable of the system instance.set_attr(Q='var', kA=np.nan) 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) + '.') assert Q == round(instance.Q.val, 0), msg
def test_ORCEvaporator(self): """Test component properties of orc evaporator.""" # design specification self.instance.set_attr(pr1=0.95, pr2=0.975, pr3=0.975, design=['pr1', 'pr2', 'pr3'], offdesign=['zeta1', 'zeta2', 'zeta3']) self.c1.set_attr(T=146.6, p=4.34, m=20.4, state='g', fluid={ 'water': 1, 'Isopentane': 0 }) self.c3.set_attr(T=146.6, p=10.2, fluid={'water': 1, 'Isopentane': 0}) self.c4.set_attr(T=118.6) self.c5.set_attr(T=111.6, p=10.8, fluid={'water': 0, 'Isopentane': 1}) # test heat transfer Q = -6.64e+07 self.instance.set_attr(Q=Q) self.nw.solve('design') convergence_check(self.nw.lin_dep) Q_is = -self.c5.m.val_SI * (self.c6.h.val_SI - self.c5.h.val_SI) msg = ('Value of heat flow must be ' + str(round(Q, 0)) + ', is ' + str(round(Q_is, 0)) + '.') assert round(Q, 0) == round(Q_is, 0), msg # test bus self.instance.set_attr(Q=np.nan) P = -6.64e+07 b = Bus('heat transfer', P=P) b.add_comps({'comp': self.instance}) self.nw.add_busses(b) self.nw.solve('design') convergence_check(self.nw.lin_dep) self.nw.save('tmp') Q_is = -self.c5.m.val_SI * (self.c6.h.val_SI - self.c5.h.val_SI) msg = ('Value of heat flow must be ' + str(round(P, 0)) + ', is ' + str(round(Q_is, 0)) + '.') assert round(P, 0) == round(Q_is, 0), msg # Check the state of the steam and working fluid outlet: x_outl1_calc = self.c2.x.val x_outl3_calc = self.c6.x.val zeta1 = self.instance.zeta1.val zeta2 = self.instance.zeta2.val zeta3 = self.instance.zeta3.val msg = ('Vapor mass fraction of steam outlet must be 0.0, is ' + str(round(x_outl1_calc, 1)) + '.') assert round(x_outl1_calc, 1) == 0.0, msg msg = ('Vapor mass fraction of working fluid outlet must be 1.0, is ' + str(round(x_outl3_calc, 1)) + '.') assert round(x_outl3_calc, 1) == 1.0, msg # Check offdesign by zeta values # geometry independent friction coefficient self.nw.solve('offdesign', design_path='tmp') convergence_check(self.nw.lin_dep) msg = ('Geometry independent friction coefficient ' 'at hot side 1 (steam) ' 'must be ' + str(round(zeta1, 1)) + ', is ' + str(round(self.instance.zeta1.val, 1)) + '.') assert round(self.instance.zeta1.val, 1) == round(zeta1, 1), msg msg = ('Geometry independent friction coefficient at ' 'hot side 2 (brine) ' 'must be ' + str(round(zeta2, 1)) + ', is ' + str(round(self.instance.zeta2.val, 1)) + '.') assert round(self.instance.zeta2.val, 1) == round(zeta2, 1), msg msg = ('Geometry independent friction coefficient at cold side ' '(Isopentane) must be ' + str(round(zeta3, 1)) + ', is ' + str(round(self.instance.zeta3.val, 1)) + '.') assert round(self.instance.zeta3.val, 1) == round(zeta3, 1), msg # test parameters of 'subcooling' and 'overheating' self.instance.set_attr(subcooling=True, overheating=True) dT = 0.5 self.c2.set_attr(Td_bp=-dT) self.c6.set_attr(Td_bp=dT) self.nw.solve('offdesign', design_path='tmp') convergence_check(self.nw.lin_dep) T_steam = T_bp_p(self.c2.get_flow()) - dT T_isop = T_bp_p(self.c6.get_flow()) + dT msg = ('Temperature of working fluid outlet must be ' + str(round(T_isop, 1)) + ', is ' + str(round(self.c6.T.val_SI, 1)) + '.') assert round(T_isop, 1) == round(self.c6.T.val_SI, 1), msg msg = ('Temperature of steam outlet must be ' + str(round(T_steam, 1)) + ', is ' + str(round(self.c2.T.val_SI, 1)) + '.') assert round(T_steam, 1) == round(self.c2.T.val_SI, 1), msg shutil.rmtree('./tmp', ignore_errors=True)
def test_WaterElectrolyzer(self): """Test component properties of water electrolyzer.""" # check bus function: # power output on component and bus must be indentical self.nw.get_conn('h2o').set_attr(T=25, p=1) self.nw.get_conn('h2').set_attr(T=25) power = Bus('power') power.add_comps({'comp': self.instance, 'param': 'P', 'base': 'bus'}) power.set_attr(P=2.5e6) 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) + '.') assert round(power.P.val, 1) == round(self.instance.P.val), msg # effieciency was set to 100 % with inlet and outlet states of the # reaction educts and products beeing identical to reference state # therefore Q must be equal to 0 msg = ('Value of heat output must be 0.0, is ' + str(self.instance.Q.val) + '.') assert round(self.instance.Q.val, 4) == 0.0, msg # reset power, change efficiency value and specify heat bus value power.set_attr(P=np.nan) self.nw.get_conn('h2o').set_attr(T=25, p=1) self.nw.get_conn('h2').set_attr(T=50) self.instance.set_attr(eta=0.8) # check bus function: # heat output on component and bus must be indentical heat = Bus('heat') heat.add_comps({'comp': self.instance, 'param': 'Q'}) heat.set_attr(P=-8e5) 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) + '.') assert round(heat.P.val, 1) == round(self.instance.Q.val), msg self.nw.save('tmp') # check bus function: # heat output on component and bus must identical (offdesign test) 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) + '.') assert round(Q, 1) == round(self.instance.Q.val), msg # delete both busses again self.nw.del_busses(heat, power) # test efficiency vs. specific energy consumption self.nw.get_conn('h2').set_attr(m=0.1) 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) + '.') eta = round(self.instance.eta.val, 2) eta_calc = round(self.instance.e0 / self.instance.e.val, 2) assert eta == eta_calc, msg # test efficiency value > 1, Q must be larger than 0 e = 130e6 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) # test efficiency msg = ('Value of efficiency must be ' + str(self.instance.e0 / e) + ', is ' + str(self.instance.eta.val) + '.') eta = round(self.instance.e0 / e, 2) eta_calc = round(self.instance.eta.val, 2) assert eta == eta_calc, msg # test Q msg = ('Value of heat must be larger than zero, is ' + str(self.instance.Q.val) + '.') assert self.instance.Q.val > 0, msg # test specific energy consumption e = 150e6 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) + '.') assert round(e, 1) == round(self.instance.e.val, 1), msg # test cooling loop pressure ratio, zeta as variable value pr = 0.95 self.instance.set_attr(pr=pr, e=None, eta=None, zeta='var', P=2e7, design=['pr']) 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.val) + '.') assert round(pr, 2) == round(self.instance.pr.val, 2), msg # use zeta as offdesign parameter, at design point pressure # 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.val) + '.') assert round(pr, 2) == round(self.instance.pr.val, 2), msg # test heat output specification in offdesign mode 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) + '.') assert round(Q, 0) == round(self.instance.Q.val, 0), msg shutil.rmtree('./tmp', ignore_errors=True)
gen = CharLine(x=x, y=y) power = Bus('power output') power.add_comps({ 'comp': g_turb, 'char': gen }, { 'comp': comp, 'char': 1 }, { 'comp': comp_fuel, 'char': mot, 'base': 'bus' }, { 'comp': turb_hp, 'char': gen }, { 'comp': pump1, 'char': mot, 'base': 'bus' }, { 'comp': turb_lp, 'char': gen }, { 'comp': pump2, 'char': mot, 'base': 'bus' }) gt_power = Bus('gas turbine power output') gt_power.add_comps({'comp': g_turb}, {'comp': comp})
class TestClausiusRankine: def setup(self): """Set up clausis rankine cycle with turbine driven feed water pump.""" self.Tamb = 20 self.pamb = 1 fluids = ['water'] self.nw = Network(fluids=fluids) self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg') # create components splitter1 = Splitter('splitter 1') merge1 = Merge('merge 1') turb = Turbine('turbine') fwp_turb = Turbine('feed water pump turbine') condenser = HeatExchangerSimple('condenser') fwp = Pump('pump') steam_generator = HeatExchangerSimple('steam generator') cycle_close = CycleCloser('cycle closer') # create busses # power output bus self.power = Bus('power_output') self.power.add_comps({'comp': turb, 'char': 1}) # turbine driven feed water pump internal bus self.fwp_power = Bus('feed water pump power', P=0) self.fwp_power.add_comps({ 'comp': fwp_turb, 'char': 1 }, { 'comp': fwp, 'char': 1, 'base': 'bus' }) # heat input bus self.heat = Bus('heat_input') self.heat.add_comps({'comp': steam_generator, 'base': 'bus'}) self.nw.add_busses(self.power, self.fwp_power, self.heat) # create connections fs_in = Connection(cycle_close, 'out1', splitter1, 'in1', label='fs') fs_fwpt = Connection(splitter1, 'out1', fwp_turb, 'in1') fs_t = Connection(splitter1, 'out2', turb, 'in1') fwpt_ws = Connection(fwp_turb, 'out1', merge1, 'in1') t_ws = Connection(turb, 'out1', merge1, 'in2') ws = Connection(merge1, 'out1', condenser, 'in1') cond = Connection(condenser, 'out1', fwp, 'in1', label='cond') fw = Connection(fwp, 'out1', steam_generator, 'in1', label='fw') fs_out = Connection(steam_generator, 'out1', cycle_close, 'in1') self.nw.add_conns(fs_in, fs_fwpt, fs_t, fwpt_ws, t_ws, ws, cond, fw, fs_out) # component parameters turb.set_attr(eta_s=1) fwp_turb.set_attr(eta_s=1) condenser.set_attr(pr=1) fwp.set_attr(eta_s=1) steam_generator.set_attr(pr=1) # connection parameters fs_in.set_attr(m=10, p=120, T=600, fluid={'water': 1}) cond.set_attr(T=self.Tamb, x=0) # solve network self.nw.solve('design') convergence_check(self.nw.lin_dep) def test_exergy_analysis_perfect_cycle(self): """Test exergy analysis in the perfect clausius rankine cycle.""" ean = ExergyAnalysis(self.nw, E_P=[self.power], E_F=[self.heat], internal_busses=[self.fwp_power]) ean.analyse(pamb=self.pamb, Tamb=self.Tamb) msg = ('Exergy destruction of this network must be 0 (smaller than ' + str(err**0.5) + ') for this test but is ' + str(round(abs(ean.network_data.E_D), 4)) + ' .') assert abs(ean.network_data.E_D) <= err**0.5, msg msg = ('Exergy efficiency of this network must be 1 for this test but ' 'is ' + str(round(ean.network_data.epsilon, 4)) + ' .') assert round(ean.network_data.epsilon, 4) == 1, msg exergy_balance = (ean.network_data.E_F - ean.network_data.E_P - ean.network_data.E_L - ean.network_data.E_D) msg = ('Exergy balance must be closed (residual value smaller than ' + str(err**0.5) + ') for this test but is ' + str(round(abs(exergy_balance), 4)) + ' .') assert abs(exergy_balance) <= err**0.5, msg msg = ( 'Fuel exergy and product exergy must be identical for this test. ' 'Fuel exergy value: ' + str(round(ean.network_data.E_F, 4)) + '. Product exergy value: ' + str(round(ean.network_data.E_P, 4)) + '.') delta = round(abs(ean.network_data.E_F - ean.network_data.E_P), 4) assert delta < err**0.5, msg def test_exergy_analysis_plotting_data(self): """Test exergy analysis plotting.""" self.nw.get_comp('steam generator').set_attr(pr=0.9) self.nw.get_comp('turbine').set_attr(eta_s=0.9) self.nw.get_comp('feed water pump turbine').set_attr(eta_s=0.85) self.nw.get_comp('pump').set_attr(eta_s=0.75) self.nw.get_conn('cond').set_attr(T=self.Tamb + 3) # specify efficiency values for the internal bus and power bus self.nw.del_busses(self.fwp_power, self.power) self.fwp_power = Bus('feed water pump power', P=0) self.fwp_power.add_comps( { 'comp': self.nw.get_comp('feed water pump turbine'), 'char': 0.99 }, { 'comp': self.nw.get_comp('pump'), 'char': 0.98, 'base': 'bus' }) self.power = Bus('power_output') self.power.add_comps({ 'comp': self.nw.get_comp('turbine'), 'char': 0.98 }) self.nw.add_busses(self.fwp_power, self.power) # solve network self.nw.solve('design') convergence_check(self.nw.lin_dep) ean = ExergyAnalysis(self.nw, E_P=[self.power], E_F=[self.heat], internal_busses=[self.fwp_power]) ean.analyse(pamb=self.pamb, Tamb=self.Tamb) exergy_balance = (ean.network_data.E_F - ean.network_data.E_P - ean.network_data.E_L - ean.network_data.E_D) msg = ('Exergy balance must be closed (residual value smaller than ' + str(err**0.5) + ') for this test but is ' + str(round(abs(exergy_balance), 4)) + ' .') assert abs(exergy_balance) <= err**0.5, msg nodes = [ 'E_F', 'steam generator', 'splitter 1', 'feed water pump turbine', 'turbine', 'merge 1', 'condenser', 'pump', 'E_D', 'E_P' ] links, nodes = ean.generate_plotly_sankey_input(node_order=nodes) # checksum for targets and source checksum = sum(links['target'] + links['source']) msg = ('The checksum of all target and source values in the link lists' 'must be 148, but is ' + str(checksum) + '.') assert 148 == checksum, msg def test_exergy_analysis_violated_balance(self): """Test exergy analysis with violated balance.""" # specify efficiency values for the internal bus self.nw.del_busses(self.fwp_power) self.fwp_power = Bus('feed water pump power', P=0) self.fwp_power.add_comps( { 'comp': self.nw.get_comp('feed water pump turbine'), 'char': 0.99 }, { 'comp': self.nw.get_comp('pump'), 'char': 0.98, 'base': 'bus' }) self.nw.add_busses(self.fwp_power) self.nw.solve('design') convergence_check(self.nw.lin_dep) # miss out on internal bus in exergy_analysis ean = ExergyAnalysis(self.nw, E_P=[self.power], E_F=[self.heat]) ean.analyse(pamb=self.pamb, Tamb=self.Tamb) exergy_balance = (ean.network_data.E_F - ean.network_data.E_P - ean.network_data.E_L - ean.network_data.E_D) msg = ('Exergy balance must be violated for this test (larger than ' + str(err**0.5) + ') but is ' + str(round(abs(exergy_balance), 4)) + ' .') assert abs(exergy_balance) > err**0.5, msg def test_exergy_analysis_bus_conversion(self): """Test exergy analysis bus conversion factors.""" # specify efficiency values for the internal bus self.nw.del_busses(self.fwp_power) self.fwp_power = Bus('feed water pump power', P=0) self.fwp_power.add_comps( { 'comp': self.nw.get_comp('feed water pump turbine'), 'char': 0.99 }, { 'comp': self.nw.get_comp('pump'), 'char': 0.98, 'base': 'bus' }) self.nw.add_busses(self.fwp_power) self.nw.solve('design') convergence_check(self.nw.lin_dep) # no exergy losses in this case ean = ExergyAnalysis(self.nw, E_P=[self.power], E_F=[self.heat], internal_busses=[self.fwp_power]) ean.analyse(pamb=self.pamb, Tamb=self.Tamb) label = 'pump' eps = ean.bus_data.loc[label, 'epsilon'] msg = ('Pump exergy efficiency must be 0.98 but is ' + str(round(eps, 4)) + ' .') assert round(eps, 4) == 0.98, msg label = 'feed water pump turbine' eps = ean.bus_data.loc[label, 'epsilon'] msg = ( 'Feed water pump turbine exergy efficiency must be 0.99 but is ' + str(round(eps, 4)) + ' .') assert round(eps, 4) == 0.99, msg def test_exergy_analysis_missing_E_F_E_P_information(self): """Test exergy analysis errors with missing information.""" with raises(TESPyNetworkError): ExergyAnalysis(self.nw, E_P=[self.power], E_F=[]) with raises(TESPyNetworkError): ExergyAnalysis(self.nw, E_P=[], E_F=[self.heat]) def test_exergy_analysis_component_on_two_busses(self): """Test exergy analysis errors with components on more than one bus.""" with raises(TESPyNetworkError): ean = ExergyAnalysis(self.nw, E_P=[self.power], E_F=[self.heat, self.power]) ean.analyse(pamb=self.pamb, Tamb=self.Tamb)
class TestClausiusRankine: def setup(self): """Set up clausis rankine cycle with turbine driven feed water pump.""" self.Tamb = 20 self.pamb = 1 fluids = ['water'] self.nw = Network(fluids=fluids) self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg') # create components splitter1 = Splitter('splitter 1') merge1 = Merge('merge 1') turb = Turbine('turbine') fwp_turb = Turbine('feed water pump turbine') condenser = HeatExchangerSimple('condenser') fwp = Pump('pump') steam_generator = HeatExchangerSimple('steam generator') cycle_close = CycleCloser('cycle closer') # create busses # power output bus self.power = Bus('power_output') self.power.add_comps({'comp': turb, 'char': 1}) # turbine driven feed water pump internal bus self.fwp_power = Bus('feed water pump power', P=0) self.fwp_power.add_comps({ 'comp': fwp_turb, 'char': 1 }, { 'comp': fwp, 'char': 1, 'base': 'bus' }) # heat input bus self.heat = Bus('heat_input') self.heat.add_comps({'comp': steam_generator, 'base': 'bus'}) self.nw.add_busses(self.power, self.fwp_power, self.heat) # create connections fs_in = Connection(cycle_close, 'out1', splitter1, 'in1', label='fs') fs_fwpt = Connection(splitter1, 'out1', fwp_turb, 'in1') fs_t = Connection(splitter1, 'out2', turb, 'in1') fwpt_ws = Connection(fwp_turb, 'out1', merge1, 'in1') t_ws = Connection(turb, 'out1', merge1, 'in2') ws = Connection(merge1, 'out1', condenser, 'in1') cond = Connection(condenser, 'out1', fwp, 'in1', label='cond') fw = Connection(fwp, 'out1', steam_generator, 'in1', label='fw') fs_out = Connection(steam_generator, 'out1', cycle_close, 'in1') self.nw.add_conns(fs_in, fs_fwpt, fs_t, fwpt_ws, t_ws, ws, cond, fw, fs_out) # component parameters turb.set_attr(eta_s=1) fwp_turb.set_attr(eta_s=1) condenser.set_attr(pr=1) fwp.set_attr(eta_s=1) steam_generator.set_attr(pr=1) # connection parameters fs_in.set_attr(m=10, p=120, T=600, fluid={'water': 1}) cond.set_attr(T=self.Tamb, x=0) # solve network self.nw.solve('design') convergence_check(self.nw.lin_dep) def test_entropy_perfect_cycle(self): """Test entropy values in the perfect clausius rankine cycle.""" labels = [ 'turbine', 'feed water pump turbine', 'condenser', 'steam generator', 'pump' ] for label in labels: cp = self.nw.get_comp(label) msg = ( 'Entropy production due to irreversibility must be 0 for all ' 'components in this test but is ' + str(round(cp.S_irr, 4)) + ' at component ' + label + ' of type ' + cp.component() + '.') assert round(cp.S_irr, 4) == 0, msg sg = self.nw.get_comp('steam generator') cd = self.nw.get_comp('condenser') msg = ( 'Value of entropy production due to heat input at steam generator ' '(S_Q=' + str(round(sg.S_Q, 4)) + ') must equal the negative ' 'value of entropy reduction in condenser (S_Q=' + str(round(cd.S_Q, 4)) + ').') assert round(sg.S_Q, 4) == -round(cd.S_Q, 4), msg
def __init__(self, working_fluid): """Set up model.""" self.working_fluid = working_fluid fluids = ['water', self.working_fluid, 'air'] self.nw = Network(fluids=fluids) self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg') # geo parameters self.geo_mass_flow = 200 geo_steam_share = 0.1 self.T_brine_in = 140 # ambient parameters self.T_amb = 5 self.p_amb = 0.6 # main components geo_steam = Source('geosteam source') geo_brine = Source('geobrine source') geo_reinjection = Sink('re-injection') air_in = Source('air source') air_out = Sink('air sink') air_fan = Compressor('air fan') air_cond = Condenser('condenser') orc_cc = CycleCloser('orc cycle closer') evap_splitter = Splitter('splitter evaporation') evap_merge = Merge('merge evaporation') evap_steam = Condenser('geosteam evaporator') evap_brine = HeatExchanger('geobrine evaporator') dr = Drum('drum') geo_merge = Merge('merge brine') pre = HeatExchanger('preheater') feed_working_fluid_pump = Pump('feed pump') tur = Turbine('turbine') ihe = HeatExchanger('internal heat exchanger') # busses net_power = Bus('net power output') net_power.add_comps( {'comp': tur, 'char': 0.97}, {'comp': feed_working_fluid_pump, 'char': 0.97, 'base': 'bus'}, {'comp': air_fan, 'char': 0.97, 'base': 'bus'} ) ORC_power_bus = Bus('cycle gross power output') ORC_power_bus.add_comps( {'comp': tur}, {'comp': feed_working_fluid_pump} ) geothermal_bus = Bus('thermal input') geothermal_bus.add_comps( {'comp': pre, 'char': -1}, {'comp': evap_brine, 'char': -1}, {'comp': evap_steam, 'char': -1} ) self.nw.add_busses(net_power, ORC_power_bus, geothermal_bus) # turbine to condenser c1 = Connection(orc_cc, 'out1', tur, 'in1', label='1') c2 = Connection(tur, 'out1', ihe, 'in1', label='2') c3 = Connection(ihe, 'out1', air_cond, 'in1', label='3') self.nw.add_conns(c1, c2, c3) # condenser to steam generator c4 = Connection(air_cond, 'out1', feed_working_fluid_pump, 'in1', label='4') c5 = Connection(feed_working_fluid_pump, 'out1', ihe, 'in2', label='5') self.nw.add_conns(c4, c5) # steam generator c6 = Connection(ihe, 'out2', pre, 'in2', label='6') c7 = Connection(pre, 'out2', dr, 'in1', label='7') c8 = Connection(dr, 'out1', evap_splitter, 'in1', label='8') c9 = Connection(evap_splitter, 'out2', evap_steam, 'in2', label='9') c10 = Connection(evap_steam, 'out2', evap_merge, 'in2', label='10') c11 = Connection(evap_splitter, 'out1', evap_brine, 'in2', label='11') c12 = Connection(evap_brine, 'out2', evap_merge, 'in1', label='12') c13 = Connection(evap_merge, 'out1', dr, 'in2', label='13') c0 = Connection(dr, 'out2', orc_cc, 'in1', label='0') self.nw.add_conns(c6, c7, c8, c11, c9, c12, c10, c13, c0) # condenser cold side c20 = Connection(air_in, 'out1', air_fan, 'in1', label='20') c21 = Connection(air_fan, 'out1', air_cond, 'in2', label='21') c22 = Connection(air_cond, 'out2', air_out, 'in1', label='22') self.nw.add_conns(c20, c21, c22) # geo source c30 = Connection(geo_steam, 'out1', evap_steam, 'in1', label='30') c31 = Connection(evap_steam, 'out1', geo_merge, 'in1', label='31') c32 = Connection(geo_brine, 'out1', geo_merge, 'in2', label='32') c33 = Connection(geo_merge, 'out1', evap_brine, 'in1', label='33') self.nw.add_conns(c30, c31, c32, c33) c34 = Connection(evap_brine, 'out1', pre, 'in1', label='34') c35 = Connection(pre, 'out1', geo_reinjection, 'in1', label='35') self.nw.add_conns(c34, c35) # generate a set of stable starting values of every working fluid # fluid settings c6.set_attr(fluid={self.working_fluid: 1.0, 'air': 0.0, 'water': 0.0}) c20.set_attr(fluid={self.working_fluid: 0.0, 'air': 1.0, 'water': 0.0}) c30.set_attr(fluid={self.working_fluid: 0.0, 'air': 0.0, 'water': 1.0}) c32.set_attr(fluid={self.working_fluid: 0.0, 'air': 0.0, 'water': 1.0}) # connection parameters p0 = PSI('P', 'T', self.T_brine_in + 273.15, 'Q', 1, self.working_fluid) c1.set_attr(p0=p0 / 1e5) ws_stable_h0 = ( PSI('H', 'T', self.T_amb + 273.15, 'Q', 1, self.working_fluid) + 0.5 * ( PSI('H', 'T', self.T_brine_in + 273.15, 'Q', 1, self.working_fluid) - PSI('H', 'T', self.T_amb + 273.15, 'Q', 1, self.working_fluid) ) ) / 1e3 c2.set_attr(h=ws_stable_h0) p0 = PSI('P', 'T', self.T_amb + 273.15, 'Q', 1, self.working_fluid) c3.set_attr(Td_bp=5, design=['Td_bp'], p0=p0 / 1e5) c5.set_attr(h=Ref(c4, 1, 1)) # steam generator c30.set_attr( m=self.geo_mass_flow * geo_steam_share, T=self.T_brine_in, x=1, p0=5) c32.set_attr( m=self.geo_mass_flow * (1 - geo_steam_share), T=self.T_brine_in, x=0) c13.set_attr() c12.set_attr(x=0.5) c10.set_attr(x=0.5, design=['x']) c34.set_attr(h=Ref(c33, 1, -50)) c7.set_attr(Td_bp=-2) # main condenser c20.set_attr(p=self.p_amb, T=self.T_amb) c22.set_attr(T=self.T_amb + 15, p=self.p_amb) # component parameters # condensing ihe.set_attr(pr1=0.98, pr2=0.98) air_cond.set_attr(pr1=1, pr2=0.995, ttd_u=10) air_fan.set_attr(eta_s=0.6) # steam generator evap_brine.set_attr(pr1=0.98, ttd_l=8) pre.set_attr(pr1=0.98, pr2=0.98) self.nw.set_attr(iterinfo=False) self.nw.solve('design') self.nw.save('stable_' + self.working_fluid) # specify actual parameters tur.set_attr(eta_s=0.9) feed_working_fluid_pump.set_attr(eta_s=0.75) c2.set_attr(h=None) c5.set_attr(h=None) c34.set_attr(h=None, T=Ref(c33, 1, -10)) self.nw.solve('design') c22.set_attr(T=None) c3.set_attr(Td_bp=None) self.ude_IHE_size = UserDefinedEquation( label='ihe deshuperheat ratio', func=desuperheat, deriv=desuperheat_deriv, latex={ 'equation': r'0 = h_3 - h_2 - x_\mathrm{IHE} \cdot \left(h_3 -' r'h\left(p_2, T_5 + \Delta T_\mathrm{t,u,min} \right)' r'\right)'}, conns=[ self.nw.get_conn('2'), self.nw.get_conn('3'), self.nw.get_conn('5')], params={'distance': 0.0, 'ttd_min': 2} ) if self.nw.lin_dep or self.nw.res[-1] > 1e-3: msg = 'No stable solution found.' raise TESPyNetworkError(msg) print( 'Generated stable starting values for working fluid ' + self.working_fluid + '.')
1.002, 1.0015, 1, 0.9977, 0.9947, 0.9909, 0.9853, 0.9644 ]) * 0.98) mot1 = CharLine(x=x, y=y) mot2 = CharLine(x=x, y=y) mot3 = CharLine(x=x, y=y) mot4 = CharLine(x=x, y=y) power = Bus('total compressor power') power.add_comps({ 'comp': cp, 'char': mot1 }, { 'comp': pu, 'char': mot2 }, { 'comp': dhp, 'char': mot3 }, { 'comp': erp, 'char': mot4 }) heat = Bus('total delivered heat') heat.add_comps({'comp': cd}) nw.add_busses(power, heat) # %% component parametrization # condenser system
0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.05, 1.1, 1.15, 1.2, 10 ]) y = np.array([ 0.01, 0.3148, 0.5346, 0.6843, 0.7835, 0.8477, 0.8885, 0.9145, 0.9318, 0.9443, 0.9546, 0.9638, 0.9724, 0.9806, 0.9878, 0.9938, 0.9982, 1.0009, 1.002, 1.0015, 1, 0.9977, 0.9947, 0.9909, 0.9853, 0.9644 ]) * 0.975 mot = CharLine(x=x, y=y) power = Bus('power') power.add_comps({ 'comp': cp, 'char': mot, 'base': 'bus' }, { 'comp': pump_ev, 'char': mot, 'base': 'bus' }) heat = Bus('heat system') heat.add_comps({'comp': cd, 'char': -1}) heat_storage = Bus('heat storage') heat_storage.add_comps({'comp': ev, 'char': -1}, {'comp': su, 'char': -1}) ti = Bus('ti') nw.add_busses(power, heat, ti, heat_storage) # %% characteristics
cw_in = Connection(so_cw, 'out1', con, 'in2') cw_out = Connection(con, 'out2', si_cw, 'in1') nw.add_conns(cw_in, cw_out) # %% busses # characteristic function for generator efficiency x = np.array([0, 0.2, 0.4, 0.6, 0.8, 1, 1.2]) y = np.array([0, 0.86, 0.9, 0.93, 0.95, 0.96, 0.95]) char = CharLine(x=x, y=y) # motor of pump has a constant efficiency power = Bus('total output power') power.add_comps( {'comp': turb, 'char': char}, {'comp': pu, 'char': char, 'base': 'bus'}) nw.add_busses(power) # %% parametrization of components turb.set_attr(eta_s=0.9, design=['eta_s'], offdesign=['eta_s_char', 'cone']) con.set_attr(pr1=1, pr2=0.98, ttd_u=5, design=['pr2', 'ttd_u'], offdesign=['zeta2', 'kA_char']) pu.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) steam_generator.set_attr(pr=0.95) # %% parametrization of connections # offdesign calculation: use parameter design for auto deactivation # turbine inlet pressure is deriven by stodolas law, outlet pressure by
# %% create busses # characteristic function for motor efficiency x = np.array([0, 0.2, 0.4, 0.6, 0.8, 1, 1.2, 1.4]) y = np.array([0, 0.86, 0.9, 0.93, 0.95, 0.96, 0.95, 0.93]) # power bus char = CharLine(x=x, y=y) power = Bus('power input') power.add_comps({ 'comp': cp, 'char': char, 'base': 'bus' }, { 'comp': ghp, 'char': char, 'base': 'bus' }, { 'comp': hsp, 'char': char, 'base': 'bus' }) # consumer heat bus heat_cons = Bus('heating system') heat_cons.add_comps({'comp': hs_ret, 'base': 'bus'}, {'comp': hs_feed}) # geothermal heat bus heat_geo = Bus('geothermal heat') heat_geo.add_comps({'comp': gh_in, 'base': 'bus'}, {'comp': gh_out})
x = np.array([ 0.100, 0.345, 0.359, 0.383, 0.410, 0.432, 0.451, 0.504, 0.541, 0.600, 0.684, 0.805, 1.000, 1.700, 10 ]) y = np.array([ 0.976, 0.989, 0.990, 0.991, 0.992, 0.993, 0.994, 0.995, 0.996, 0.997, 0.998, 0.999, 1.000, 0.999, 0.99 ]) * -0.984 gen1 = CharLine(x=x, y=y) gen2 = CharLine(x=x, y=y) power = Bus('power') power.add_comps({ 'comp': pump, 'char': mot }, { 'comp': ice, 'param': 'P', 'char': gen1 }) ice_power = Bus('ice_power') ice_power.add_comps({'comp': ice, 'param': 'P', 'char': gen2}) heat = Bus('heat') heat.add_comps({'comp': ice, 'param': 'Q'}, {'comp': fgc}) heat_cond = Bus('heat_cond') ti = Bus('ti') ti.add_comps({'comp': ice, 'param': 'TI'}) nw.add_busses(power, heat, ti, ice_power, heat_cond)