pu_ev.set_attr(m=Ref(valve_dr, 4, 0), p0=5) su_cp.set_attr(p0=5, h0=1700, Td_bp=5) # evaporator system hot side sto_IF.set_attr(T=40, p=5, fluid={'NH3': 0, 'H2O': 1}) IF_sto.set_attr(T=20, design=['T']) heat.set_attr(P=1000e3) # %% Calculation nw.solve('design') nw.print_results() save_file = 'hp_discharge' nw.save(save_file) mass_flow_hp = c_in_cd.m.val_SI print('COP_design:', heat.P.val / power.P.val) heat.set_attr(P=1100e3) nw.solve('offdesign', design_path=save_file, init_path=save_file) nw.print_results() print('Refrigerant mass flow maximum load:', c_in_cd.m.val_SI / mass_flow_hp) print('COP_maximum load:', heat.P.val / power.P.val) heat.set_attr(P=400e3) nw.solve('offdesign', design_path=save_file, init_path=save_file) nw.print_results() nw.save(save_file + '_low') print('Refrigerant mass flow minimum load:', c_in_cd.m.val_SI / mass_flow_hp) print('COP_minimum load:', heat.P.val / power.P.val)
class TestNetworks: def setup_Network_tests(self): self.nw = Network(['water'], p_unit='bar', v_unit='m3 / s') self.source = Source('source') self.sink = Sink('sink') def offdesign_TESPyNetworkError(self, **kwargs): with raises(TESPyNetworkError): self.nw.solve('offdesign', **kwargs) def test_Network_linear_dependency(self): """Test network linear dependency.""" self.setup_Network_tests() a = Connection(self.source, 'out1', self.sink, 'in1', m=1, p=1, x=1, T=280) self.nw.add_conns(a) self.nw.solve('design') msg = ('This test must result in a linear dependency of the jacobian ' 'matrix.') assert self.nw.lin_dep, msg def test_Network_no_progress(self): """Test no convergence progress.""" self.setup_Network_tests() pi = Pipe('pipe', pr=1, Q=-100e3) a = Connection(self.source, 'out1', pi, 'in1', m=1, p=1, T=280, fluid={'water': 1}) b = Connection(pi, 'out1', self.sink, 'in1') self.nw.add_conns(a, b) self.nw.solve('design') msg = ('This test must result in a calculation making no progress, as ' 'the pipe\'s outlet enthalpy is below fluid property range.') assert self.nw.progress is False, msg def test_Network_max_iter(self): """Test reaching maximum iteration count.""" self.setup_Network_tests() pi = Pipe('pipe', pr=1, Q=100e3) a = Connection(self.source, 'out1', pi, 'in1', m=1, p=1, T=280, fluid={'water': 1}) b = Connection(pi, 'out1', self.sink, 'in1') self.nw.add_conns(a, b) self.nw.solve('design', max_iter=2) msg = ('This test must result in the itercount being equal to the max ' 'iter statement.') assert self.nw.max_iter == self.nw.iter + 1, msg def test_Network_delete_conns(self): """Test deleting a network's connection.""" self.setup_Network_tests() a = Connection(self.source, 'out1', self.sink, 'in1') self.nw.add_conns(a) self.nw.check_network() msg = ('After the network check, the .checked-property must be True.') assert self.nw.checked, msg self.nw.del_conns(a) msg = ('A connection has been deleted, the network consistency check ' 'must be repeated (.checked-property must be False).') assert self.nw.checked is False, msg def test_Network_missing_connection_in_init_path(self): """Test debug message for missing connection in init_path.""" self.setup_Network_tests() IF = SubsystemInterface('IF') a = Connection(self.source, 'out1', self.sink, 'in1') self.nw.add_conns(a) self.nw.solve('design', init_only=True) self.nw.save('tmp') msg = ('After the network check, the .checked-property must be True.') assert self.nw.checked, msg self.nw.del_conns(a) a = Connection(self.source, 'out1', IF, 'in1') b = Connection(IF, 'out1', self.sink, 'in1') self.nw.add_conns(a, b) self.nw.solve('design', init_path='tmp', init_only=True) msg = ('After the network check, the .checked-property must be True.') assert self.nw.checked, msg shutil.rmtree('./tmp', ignore_errors=True) def test_Network_export_no_chars_busses(self): """Test export of network without characteristics or busses.""" self.setup_Network_tests() a = Connection(self.source, 'out1', self.sink, 'in1') self.nw.add_conns(a) self.nw.solve('design', init_only=True) self.nw.save('tmp') msg = ('The exported network does not contain any char_line, there ' 'must be no file char_line.csv!') assert os.path.isfile('tmp/components/char_line.csv') is False, msg msg = ('The exported network does not contain any char_map, there ' 'must be no file char_map.csv!') assert os.path.isfile('tmp/components/char_map.csv') is False, msg msg = ('The exported network does not contain any busses, there ' 'must be no file bus.csv!') assert os.path.isfile('tmp/components/bus.csv') is False, msg shutil.rmtree('./tmp', ignore_errors=True) def test_Network_reader_no_chars_busses(self): """Test import of network without characteristics or busses.""" self.setup_Network_tests() a = Connection(self.source, 'out1', self.sink, 'in1') self.nw.add_conns(a) self.nw.solve('design', init_only=True) self.nw.save('tmp') imported_nwk = load_network('tmp') imported_nwk.solve('design', init_only=True) msg = ('If the network import was successful the network check ' 'should have been successful, too, but it is not.') assert imported_nwk.checked, msg shutil.rmtree('./tmp', ignore_errors=True) def test_Network_reader_deleted_chars(self): """Test import of network with missing characteristics.""" self.setup_Network_tests() comp = Compressor('compressor') a = Connection(self.source, 'out1', comp, 'in1') b = Connection(comp, 'out1', self.sink, 'in1') self.nw.add_conns(a, b) self.nw.solve('design', init_only=True) self.nw.save('tmp') # # remove char_line and char_map folders os.unlink('tmp/components/char_line.csv') os.unlink('tmp/components/char_map.csv') # import network with missing files imported_nwk = load_network('tmp') imported_nwk.solve('design', init_only=True) msg = ('If the network import was successful the network check ' 'should have been successful, too, but it is not.') assert imported_nwk.checked, msg shutil.rmtree('./tmp', ignore_errors=True) def test_Network_missing_data_in_design_case_files(self): """Test for missing data in design case files.""" self.setup_Network_tests() pi = Pipe('pipe', Q=0, pr=0.95, design=['pr'], offdesign=['zeta']) a = Connection(self.source, 'out1', pi, 'in1', m=1, p=1, T=293.15, fluid={'water': 1}) b = Connection(pi, 'out1', self.sink, 'in1') self.nw.add_conns(a, b) self.nw.solve('design') self.nw.save('tmp') self.nw.save('tmp2') inputs = open('./tmp/connections.csv') all_lines = inputs.readlines() all_lines.pop(len(all_lines) - 1) inputs.close() with open('./tmp2/connections.csv', 'w') as out: for line in all_lines: out.write(line.strip() + '\n') self.offdesign_TESPyNetworkError(design_path='tmp2', init_only=True) shutil.rmtree('./tmp', ignore_errors=True) shutil.rmtree('./tmp2', ignore_errors=True) def test_Network_missing_data_in_individual_design_case_file(self): """Test for missing data in individual design case files.""" self.setup_Network_tests() pi = Pipe('pipe', Q=0, pr=0.95, design=['pr'], offdesign=['zeta']) a = Connection(self.source, 'out1', pi, 'in1', m=1, p=1, T=293.15, fluid={'water': 1}) b = Connection(pi, 'out1', self.sink, 'in1', design_path='tmp2') self.nw.add_conns(a, b) self.nw.solve('design') self.nw.save('tmp') self.nw.save('tmp2') inputs = open('./tmp/connections.csv') all_lines = inputs.readlines() all_lines.pop(len(all_lines) - 1) inputs.close() with open('./tmp2/connections.csv', 'w') as out: for line in all_lines: out.write(line.strip() + '\n') self.offdesign_TESPyNetworkError(design_path='tmp', init_only=True) shutil.rmtree('./tmp', ignore_errors=True) shutil.rmtree('./tmp2', ignore_errors=True) def test_Network_missing_connection_in_design_path(self): """Test for missing connection data in design case files.""" self.setup_Network_tests() pi = Pipe('pipe', Q=0, pr=0.95, design=['pr'], offdesign=['zeta']) a = Connection(self.source, 'out1', pi, 'in1', m=1, p=1, T=293.15, fluid={'water': 1}) b = Connection(pi, 'out1', self.sink, 'in1') self.nw.add_conns(a, b) self.nw.solve('design') self.nw.save('tmp') inputs = open('./tmp/connections.csv') all_lines = inputs.readlines() all_lines.pop(len(all_lines) - 1) inputs.close() with open('./tmp/connections.csv', 'w') as out: for line in all_lines: out.write(line.strip() + '\n') self.offdesign_TESPyNetworkError(design_path='tmp') shutil.rmtree('./tmp', ignore_errors=True)
pu_ev.set_attr(m=Ref(valve_dr, 4, 0), p0=5) su_cp.set_attr(p0=5, h0=1700, Td_bp=5) # evaporator system hot side sto_IF.set_attr(T=10, p=5, fluid={'NH3': 0, 'H2O': 1}) IF_sto.set_attr(T=5, design=['T']) heat.set_attr(P=1000e3) # %% Calculation nw.solve('design') nw.print_results() save_file = 'hp_discharge' nw.save(save_file) mass_flow_hp = c_in_cd.m.val_SI print('COP_design:', heat.P.val / power.P.val) nw.set_attr(iterinfo=False) # heat.set_attr(P=850e3) for T in np.linspace(25, 5, 21): sto_IF.set_attr(T=T) nw.solve('offdesign', design_path=save_file) COP_C = (IF_sys.T.val_SI) / (IF_sys.T.val_SI - sto_IF.T.val_SI) COP = heat.P.val / power.P.val print('COP:', COP) print('COP Carnot:', COP_C) print('Gütefaktor:', COP / COP_C) print('Delta T:', sto_IF.T.val - IF_sto.T.val) print('Kompressor Wirkungsgrad:', cp.eta_s.val)
ev_amb_out.set_attr(p=1, T=9, design=['T']) # compressor-system he_cp2.set_attr(Td_bp=5, p0=20, design=['Td_bp']) ic_out.set_attr(T=30, design=['T']) # %% key paramter cons.set_attr(Q=-200e3) # %% Calculation nw.solve('design') nw.print_results() nw.save('heat_pump_air') document_model(nw, filename='report_air_design.tex') # offdesign test nw.solve('offdesign', design_path='heat_pump_air') document_model(nw, filename='report_air_offdesign.tex') T_range = [6, 12, 18, 24, 30] Q_range = np.array([100e3, 120e3, 140e3, 160e3, 180e3, 200e3, 220e3]) df = pd.DataFrame(columns=Q_range / -cons.Q.val) for T in T_range: amb_fan.set_attr(T=T) eps = [] for Q in Q_range:
class TestReactors: def setup(self): """Set up network for electrolyzer tests.""" self.nw = Network(['O2', 'H2', 'H2O'], T_unit='C', p_unit='bar') self.instance = WaterElectrolyzer('electrolyzer') fw = Source('feed water') cw_in = Source('cooling water') o2 = Sink('oxygen sink') h2 = Sink('hydrogen sink') cw_out = Sink('cooling water sink') self.instance.set_attr(pr=0.99, eta=1) cw_el = Connection(cw_in, 'out1', self.instance, 'in1', fluid={ 'H2O': 1, 'H2': 0, 'O2': 0 }, T=20, p=1) el_cw = Connection(self.instance, 'out1', cw_out, 'in1', T=45) self.nw.add_conns(cw_el, el_cw) fw_el = Connection(fw, 'out1', self.instance, 'in2', label='h2o') el_o2 = Connection(self.instance, 'out2', o2, 'in1') el_h2 = Connection(self.instance, 'out3', h2, 'in1', label='h2') self.nw.add_conns(fw_el, el_o2, el_h2) 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)
class PowerPlant(): 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 + '.') def run_simulation( self, p_before_tur=None, Q_ihe=None, Q_brine_ev=None, T_before_tur=None, T_reinjection=None, brine_evap_Td=None, dT_air=None, IHE_sizing=None, geo_steam_share=None): """Run simulation on specified parameter set.""" self.nw.get_comp('internal heat exchanger').set_attr(Q=Q_ihe) self.nw.get_conn('1').set_attr(p=p_before_tur, T=T_before_tur) self.nw.get_conn('35').set_attr(T=T_reinjection) self.nw.get_comp('geobrine evaporator').set_attr(Q=Q_brine_ev) if geo_steam_share is not None: self.nw.get_conn('30').set_attr( m=self.geo_mass_flow * geo_steam_share) self.nw.get_conn('32').set_attr( m=self.geo_mass_flow * (1 - geo_steam_share)) if brine_evap_Td is not None: self.nw.get_conn('34').set_attr( T=Ref(self.nw.get_conn('33'), 1, brine_evap_Td)) else: self.nw.get_conn('34').set_attr(T=None) if dT_air is not None: self.nw.get_conn('22').set_attr(T=Ref(self.nw.get_conn('21'), 1, dT_air)) else: self.nw.get_conn('22').set_attr(T=None) if IHE_sizing is None: if self.ude_IHE_size in self.nw.user_defined_eq.values(): self.nw.del_ude(self.ude_IHE_size) self.nw.get_comp('internal heat exchanger').set_attr(pr1=0.98, pr2=0.98) else: if self.ude_IHE_size not in self.nw.user_defined_eq.values(): self.nw.add_ude(self.ude_IHE_size) self.ude_IHE_size.params['distance'] = IHE_sizing if IHE_sizing == 0: self.nw.get_comp('internal heat exchanger').set_attr(pr1=1, pr2=1) else: self.nw.get_comp('internal heat exchanger').set_attr(pr1=0.98, pr2=0.98) try: self.nw.solve('design') # self.nw.print_results() except ValueError: self.nw.res = [1] pass def check_simulation(self, value): """Check if simulation converged.""" if self.nw.lin_dep or self.nw.res[-1] > 1e-3: self.nw.solve( 'design', init_path='stable_' + self.working_fluid, init_only=True) return np.nan else: for cp in self.nw.comps['object']: if isinstance(cp, HeatExchanger): if cp.Q.val > 0: print(cp.label) return np.nan elif cp.kA.val <= 0 or (np.isnan(cp.kA.val) and cp.Q.val != 0): print(cp.label) return np.nan return value def get_power(self): """Calculate ORC gross power (main cycle only).""" return self.check_simulation(self.nw.busses['cycle gross power output'].P.val) def get_net_power(self): """Calculate net power.""" return self.check_simulation(self.nw.busses['net power output'].P.val) def get_thermal_efficiency(self): """Calculate thermal efficiency.""" return self.check_simulation( -self.nw.busses['cycle gross power output'].P.val / self.nw.busses['thermal input'].P.val) def get_net_efficiency(self): """Calculate net efficiency.""" return self.check_simulation( -self.nw.busses['net power output'].P.val / self.nw.busses['thermal input'].P.val) def get_geosteam_share(self): """Return a geosteam share.""" return self.check_simulation( self.nw.get_conn('geosteam').m.val_SI / self.geo_mass_flow) def get_connection_param(self, conn, param): """Return a connection parameter.""" return self.check_simulation( self.nw.get_conn(conn).get_attr(param).val) def get_component_param(self, comp, param): """Return a component parameter.""" return self.check_simulation( self.nw.get_comp(comp).get_attr(param).val) def get_misc_param(self, param): """Get non component or connection parameters.""" if param == 'gross power output': return self.get_power() elif param == 'net power output': return self.get_net_power() elif param == 'thermal efficiency': return self.get_thermal_efficiency() elif param == 'net efficiency': return self.get_net_efficiency() elif param == 'IHE sizing factor': return self.ude_IHE_size.params['distance'] def get_objective_func(self, objective): """Return corresponding objective function.""" if objective == 'net power output': return self.get_net_power elif objective == 'gross power output': return self.get_power else: msg = ( 'Please specify valid objective function: "net power output" ' 'or "gross power output".') raise ValueError(msg)
class TestBusses: 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_model(self): """Test the bus functionalities in a gas turbine model.""" tpo = self.nw.busses['total power output'] ti = self.nw.busses['thermal input'] cpi = self.nw.busses['compressor power input'] cpibb = self.nw.busses['compressor power input bus based'] cp = self.nw.components['compressor'] gt = self.nw.components['turbine'] cc = self.nw.components['combustion'] # test results of design case eta_cpi = round(1 / cp.calc_bus_efficiency(cpi), 6) eta_cp_tpo = round(cp.calc_bus_efficiency(tpo), 6) msg = ('The efficiency value of the compressor on the bus ' + tpo.label + ' (' + str(eta_cp_tpo) + ') must be identical to the efficiency ' 'on the bus ' + cpi.label + ' (' + str(eta_cpi) + ').') assert eta_cp_tpo == eta_cpi, msg P_cp_tpo = cp.calc_bus_value(tpo) eta_cp_tpo = cp.calc_bus_efficiency(tpo) P_cp = round(P_cp_tpo * eta_cp_tpo, 0) msg = ('The compressor power must be ' + str(round(cp.P.val, 0)) + ' on ' 'the bus ' + tpo.label + ' but is ' + str(P_cp) + ').') assert round(cp.P.val, 0) == P_cp, msg P_cp_tpo = round( cp.calc_bus_value(tpo) * cp.calc_bus_efficiency(tpo), 0) P_cp_cpi = round( cp.calc_bus_value(cpi) / cp.calc_bus_efficiency(cpi), 0) P_cp_cpibb = round( cp.calc_bus_value(cpibb) * cp.calc_bus_efficiency(cpibb), 0) msg = ( 'The busses\' component power value for the compressor on bus ' + tpo.label + ' (' + str(P_cp_tpo) + ') must be equal to the ' 'component power on all other busses. Bus ' + cpi.label + ' (' + str(P_cp_cpi) + ') and bus ' + cpibb.label + ' (' + str(P_cp_cpibb) + ').') assert P_cp_tpo == P_cp_cpi and P_cp_tpo == P_cp_cpibb, msg eta_gt_tpo = gt.calc_bus_efficiency(tpo) msg = ('The efficiency value of the turbine on the bus ' + tpo.label + ' (' + str(eta_gt_tpo) + ') must be equal to 0.975.') assert eta_gt_tpo == 0.975, msg eta_ti = cc.calc_bus_efficiency(ti) msg = ('The efficiency value of the combustion chamber on the bus ' + ti.label + ' (' + str(eta_ti) + ') must be equal to 1.0.') assert eta_ti == 1.0, msg # test partload for bus functions # first test in identical conditions self.nw.connections['ambient air flow'].set_attr(m=None) P_design = cpibb.P.val cpibb.set_attr(P=P_design) self.nw.solve('offdesign', design_path='tmp') eta_cpi = round(1 / cp.calc_bus_efficiency(cpi), 6) eta_cp_tpo = round(cp.calc_bus_efficiency(tpo), 6) msg = ('The efficiency value of the compressor on the bus ' + tpo.label + ' (' + str(eta_cp_tpo) + ') must be identical to the efficiency ' 'on the bus ' + cpi.label + ' (' + str(eta_cpi) + ').') assert eta_cp_tpo == eta_cpi, msg eta_gt_tpo = gt.calc_bus_efficiency(tpo) msg = ('The efficiency value of the turbine on the bus ' + tpo.label + ' (' + str(eta_gt_tpo) + ') must be equal to 0.975.') assert eta_gt_tpo == 0.975, msg P_cp_tpo = round( cp.calc_bus_value(tpo) * cp.calc_bus_efficiency(tpo), 0) P_cp_cpi = round( cp.calc_bus_value(cpi) / cp.calc_bus_efficiency(cpi), 0) P_cp_cpibb = round( cp.calc_bus_value(cpibb) * cp.calc_bus_efficiency(cpibb), 0) msg = ( 'The busses\' component power value for the compressor on bus ' + tpo.label + ' (' + str(P_cp_tpo) + ') must be equal to the ' 'component power on all other busses. Bus ' + cpi.label + ' (' + str(P_cp_cpi) + ') and bus ' + cpibb.label + ' (' + str(P_cp_cpibb) + ').') assert P_cp_tpo == P_cp_cpi and P_cp_tpo == P_cp_cpibb, msg # 60 % load load = 0.6 cpibb.set_attr(P=P_design * load) self.nw.solve('offdesign', design_path='tmp') eta_cp_tpo = round(cp.calc_bus_efficiency(tpo), 6) eta_cp_char = self.motor_bus_based.evaluate(load) msg = ('The efficiency value of the compressor on the bus ' + tpo.label + ' (' + str(eta_cp_tpo) + ') must be identical to the efficiency ' 'on the characteristic line (' + str(eta_cp_char) + ').') assert eta_cp_tpo == eta_cp_char, msg load_frac = round( cp.calc_bus_value(tpo) / tpo.comps.loc[cp, 'P_ref'], 6) msg = ('The load fraction value of the compressor on the bus ' + tpo.label + ' (' + str(load_frac) + ') must be identical to the ' 'load fraction value on the bus ' + cpibb.label + ' (' + str(load) + ').') assert load == load_frac, msg eta_cpi = round(1 / cp.calc_bus_efficiency(cpi), 6) eta_cp_tpo = round(cp.calc_bus_efficiency(tpo), 6) msg = ('The efficiency value of the compressor on the bus ' + tpo.label + ' (' + str(eta_cp_tpo) + ') must be higher than the efficiency ' 'on the bus ' + cpi.label + ' (' + str(eta_cpi) + ').') assert eta_cp_tpo > eta_cpi, msg P_cp_tpo = round( cp.calc_bus_value(tpo) * cp.calc_bus_efficiency(tpo), 0) P_cp_cpi = round( cp.calc_bus_value(cpi) / cp.calc_bus_efficiency(cpi), 0) P_cp_cpibb = round( cp.calc_bus_value(cpibb) * cp.calc_bus_efficiency(cpibb), 0) msg = ( 'The busses\' component power value for the compressor on bus ' + tpo.label + ' (' + str(P_cp_tpo) + ') must be equal to the ' 'component power on all other busses. Bus ' + cpi.label + ' (' + str(P_cp_cpi) + ') and bus ' + cpibb.label + ' (' + str(P_cp_cpibb) + ').') assert P_cp_tpo == P_cp_cpi and P_cp_tpo == P_cp_cpibb, msg shutil.rmtree('tmp', ignore_errors=True)
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 :) print('Heat demand consumer:', heat_consumer.P.val) print('network losses at 0 °C outside temperature (design):', heat_losses.P.val) # offdesign case: 10 °C ambient temperature for comp in nw.comps.index: if isinstance(comp, Pipe): comp.set_attr(Tamb=10) nw.solve('offdesign', design_path='grid')
m=50, fluid={ 'CO2': 0, 'Ar': 0, 'N2': 0, 'O2': 0, 'H2O': 1, 'CH4': 0 }) # %% solving mode = 'design' nw.set_attr(iterinfo=False) nw.solve(mode=mode) nw.save('chp') chp.P.design = chp.P.val load = chp.P.val / chp.P.design power = chp.P.val heat = chp.Q1.val + chp.Q2.val ti = chp.ti.val print('Load: ' + '{:.3f}'.format(load)) print('Power generation: ' + '{:.3f}'.format(abs(chp.P.val / chp.ti.val))) print('Heat generation: ' + '{:.3f}'.format(abs((chp.Q1.val + chp.Q2.val) / chp.ti.val))) print('Fuel utilization: ' + '{:.3f}'.format(abs((chp.P.val + chp.Q1.val + chp.Q2.val) / chp.ti.val))) mode = 'offdesign' for P in np.linspace(1, 0.4, 5) * chp.P.design:
class TestCombustion: def setup(self): self.nw = Network(['H2O', 'N2', 'O2', 'Ar', 'CO2', 'CH4'], T_unit='C', p_unit='bar', v_unit='m3 / s') self.fuel = Source('fuel') self.air = Source('ambient air') self.fg = Sink('flue gas') def setup_CombustionChamber_network(self, instance): self.c1 = Connection(self.air, 'out1', instance, 'in1') self.c2 = Connection(self.fuel, 'out1', instance, 'in2') self.c3 = Connection(instance, 'out1', self.fg, 'in1') self.nw.add_conns(self.c1, self.c2, self.c3) def setup_CombustionEngine_network(self, instance): self.cw1_in = Source('cooling water 1 source') self.cw2_in = Source('cooling water 2 source') self.cw1_out = Sink('cooling water 1 sink') self.cw2_out = Sink('cooling water 2 sink') self.c1 = Connection(self.air, 'out1', instance, 'in3') self.c2 = Connection(self.fuel, 'out1', instance, 'in4') self.c3 = Connection(instance, 'out3', self.fg, 'in1') self.c4 = Connection(self.cw1_in, 'out1', instance, 'in1') self.c5 = Connection(self.cw2_in, 'out1', instance, 'in2') self.c6 = Connection(instance, 'out1', self.cw1_out, 'in1') self.c7 = Connection(instance, 'out2', self.cw2_out, 'in1') self.nw.add_conns(self.c1, self.c2, self.c3, self.c4, self.c5, self.c6, self.c7) def test_CombustionChamber(self): """ Test component properties of combustion chamber. """ instance = CombustionChamber('combustion chamber') self.setup_CombustionChamber_network(instance) # connection parameter specification 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} self.c1.set_attr(fluid=air, p=1, T=30) self.c2.set_attr(fluid=fuel, T=30) self.c3.set_attr(T=1200) # test specified bus value on CombustionChamber (must be equal to ti) b = Bus('thermal input', P=1e6) b.add_comps({'comp': 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) + '.') assert round(b.P.val, 1) == round(instance.ti.val, 1), msg b.set_attr(P=np.nan) # test specified thermal input for CombustionChamber 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) + ', is ' + str(ti) + '.') assert round(ti, 1) == round(instance.ti.val, 1), msg # test specified lamb for CombustionChamber 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)) + '.') assert 0.0 == round(self.c3.fluid.val['O2'], 4), msg 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)
fgc_cons.set_attr(T=90, fluid0={'H2O': 1}) # splitting mass flow in half sp_ice1.set_attr(m=Ref(sp_ice2, 1, 0)) # cycle closing cons_out.set_attr(p=Ref(cw_pu, 1, 0), h=Ref(cw_pu, 1, 0)) # %% solving heat.set_attr(P=Q_N) # ice.set_attr(P=-1e6) mode = 'design' nw.solve(mode=mode) # , init_path='ice_design') nw.print_results() nw.save('ice_design') print(power.P.val, heat.P.val, -power.P.val / ti.P.val, -heat.P.val / ti.P.val) Q_in = ti.P.val ice_P_design = ice.P.val ice.set_attr(P=ice_P_design) heat.set_attr(P=np.nan) mode = 'offdesign' nw.solve(mode=mode, design_path='ice_design') nw.print_results() # P_L += [abs(power.P.val)] # Q_L += [abs(heat.P.val)]
ev_amb_out.set_attr(p=2, T=9, design=['T']) # compressor-system he_cp2.set_attr(Td_bp=5, p0=20, design=['Td_bp']) ic_out.set_attr(T=30, design=['T']) # %% key paramter cons.set_attr(Q=-200e3) # %% Calculation nw.solve('design') nw.print_results() nw.save('heat_pump_water') document_model(nw, filename='report_water_design.tex') # offdesign test nw.solve('offdesign', design_path='heat_pump_water') document_model(nw, filename='report_water_offdesign.tex') T_range = [6, 12, 18, 24, 30] Q_range = np.array([100e3, 120e3, 140e3, 160e3, 180e3, 200e3, 220e3]) df = pd.DataFrame(columns=Q_range / -cons.Q.val) for T in T_range: amb_p.set_attr(T=T) eps = [] for Q in Q_range:
# evaporator system cold side pu_ev.set_attr(m=Ref(va_dr, 0.75, 0)) su_cp1.set_attr(state='g') # evaporator system hot side amb_in_su.set_attr(T=12, p=1, fluid={'water': 1, 'NH3': 0}) ev_amb_out.set_attr(T=9) he_cp2.set_attr(T=40, p0=10) ic_in_he.set_attr(p=5, T=20, fluid={'water': 1, 'NH3': 0}) he_ic_out.set_attr(T=30, design=['T']) # %% key paramter cons.set_attr(Q=-230e3) # %% Calculation nw.solve('design') # alternatively use: nw.solve('design', init_path='condenser_eva') nw.print_results() nw.save('heat_pump') cons.set_attr(Q=-200e3) nw.solve('offdesign', design_path='heat_pump') nw.print_results()
# low temp water system lt_so_pu.set_attr(p=10, T=T_source_vl, fluid={'air': 0, 'NH3': 0, 'water': 1}) # pu_ev.set_attr(offdesign=['v']) ev_lt_si.set_attr(p=10, T=T_source_rl) # %% key paramter heat.set_attr(P=Q_N) # %% Calculation nw.solve('design') nw.print_results() nw.save('hp_water') document_model(nw) cop = abs(heat.P.val) / power.P.val print('COP:', cop) print('P_out:', power.P.val / 1e6) print('Q_out:', heat.P.val / 1e6) cp.eta_s_char.char_func.extrapolate = True h = np.arange(0, 3000 + 1, 200) T_max = 300 T = np.arange(-75, T_max + 1, 25).round(8) # Diagramm Q_values = np.linspace(0, 100, 11)
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) heat.set_attr(P=1000e3) heat_design = heat.P.val # %% Calculation nw.solve('design') nw.save('he_charge') nw.print_results() # testin lower loads heat.set_attr(P=heat_design / 2) nw.solve('offdesign', design_path='he_charge') # save this for interface initialisation heat.set_attr(P=heat_design / 4) nw.solve('offdesign', design_path='he_charge') heat.set_attr(P=heat_design / 8) nw.solve('offdesign', design_path='he_charge') nw.save('he_charge_low') # low temperature in storage
# %% parametrization of connections # offdesign calculation: use parameter design for auto deactivation # turbine inlet pressure is deriven by stodolas law, outlet pressure by # characteristic of condenser fs_in.set_attr(p=100, T=500, fluid={'water': 1}, design=['p']) cw_in.set_attr(T=20, p=5, fluid={'water': 1}) cw_out.set_attr(T=30) # total output power as input parameter power.set_attr(P=-10e6) # %% solving # solve the network, print the results to prompt and save nw.solve('design') nw.print_results() nw.save('design') document_model(nw, filename='report_design.tex') # reset power input power.set_attr(P=-9e6) # the design file holds the information on the design case # initialisation from previously design process nw.solve('offdesign', design_path='design') nw.print_results() document_model(nw, filename='report_offdesign.tex')
# condenser system c_in_cd.set_attr(T=170, fluid={'water': 0, 'NH3': 1}) close_rp.set_attr(T=60, p=10, fluid={'water': 1, 'NH3': 0}) cd_cons.set_attr(T=90) # evaporator system cold side pu_ev.set_attr(m=Ref(va_dr, 0.75, 0), p0=5) su_cp1.set_attr(p0=5, state='g') # evaporator system hot side amb_in_su.set_attr(T=12, p=1, fluid={'water': 1, 'NH3': 0}) ev_amb_out.set_attr(T=9) # %% key paramter cons.set_attr(Q=-230e3) # %% Calculation nw.solve('design') nw.print_results() nw.save('condenser_eva') cons.set_attr(Q=-200e3) nw.solve('offdesign', design_path='condenser_eva') nw.print_results()
class TestHeatExchangers: def setup(self): self.nw = Network( ['H2O', 'Ar', 'INCOMP::S800'], T_unit='C', p_unit='bar', v_unit='m3 / s') self.inl1 = Source('inlet 1') self.outl1 = Sink('outlet 1') def setup_HeatExchangerSimple_network(self, instance): self.c1 = Connection(self.inl1, 'out1', instance, 'in1') self.c2 = Connection(instance, 'out1', self.outl1, 'in1') self.nw.add_conns(self.c1, self.c2) def setup_HeatExchanger_network(self, instance): self.inl2 = Source('inlet 2') self.outl2 = Sink('outlet 2') self.c1 = Connection(self.inl1, 'out1', instance, 'in1') self.c2 = Connection(instance, 'out1', self.outl1, 'in1') self.c3 = Connection(self.inl2, 'out1', instance, 'in2') self.c4 = Connection(instance, 'out2', self.outl2, 'in1') self.nw.add_conns(self.c1, self.c2, self.c3, self.c4) 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_ParabolicTrough(self): """Test component properties of parabolic trough.""" instance = ParabolicTrough('parabolic trough') self.setup_HeatExchangerSimple_network(instance) fl = {'Ar': 0, 'H2O': 0, 'S800': 1} self.c1.set_attr(fluid=fl, p=2, T=200) self.c2.set_attr(T=350) # test grouped parameter settings with missing parameters instance.hydro_group.is_set = True instance.energy_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 = ('Energy group must no be set, if one parameter is missing!') assert instance.energy_group.is_set is False, msg # test solar collector params as system variables instance.set_attr( pr=1, aoi=10, doc=0.95, Q=1e6, Tamb=25, A='var', eta_opt=0.816, c_1=0.0622, c_2=0.00023, E=8e2, iam_1=-1.59e-3, iam_2=9.77e-5) self.nw.solve('design') convergence_check(self.nw.lin_dep) # heat loss must be identical to E * A - Q (internal heat loss # calculation) T_diff = (self.c2.T.val + self.c1.T.val) / 2 - instance.Tamb.val iam = ( 1 - instance.iam_1.val * abs(instance.aoi.val) - instance.iam_2.val * instance.aoi.val ** 2) Q_loss = -round(instance.A.val * ( instance.E.val * ( 1 - instance.eta_opt.val * instance.doc.val ** 1.5 * iam ) + T_diff * instance.c_1.val + T_diff ** 2 * instance.c_2.val), 0) msg = ( 'Value for heat loss of parabolic trough must be ' + str(Q_loss) + ', is ' + str(round(instance.Q_loss.val, 0)) + '.') assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: E # going to a different operating point first area = instance.A.val instance.set_attr(A=area * 1.2, E='var') self.nw.solve('design') instance.set_attr(A=area) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: eta_opt instance.set_attr(E=5e2, eta_opt='var') self.nw.solve('design') instance.set_attr(E=8e2) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: c_1 instance.set_attr(E=5e2, eta_opt=instance.eta_opt.val, c_1='var') self.nw.solve('design') instance.set_attr(E=8e2) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: c_2 instance.set_attr(E=5e2, c_1=instance.c_1.val, c_2='var') self.nw.solve('design') instance.set_attr(E=8e2) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: iam_1 instance.set_attr(E=5e2, c_2=instance.c_2.val, iam_1='var') self.nw.solve('design') instance.set_attr(E=8e2) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: iam_2 instance.set_attr(E=5e2, iam_1=instance.iam_1.val, iam_2='var') self.nw.solve('design') instance.set_attr(E=8e2) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: aoi instance.set_attr(E=5e2, iam_2=instance.iam_2.val, aoi='var') self.nw.solve('design') instance.set_attr(E=8e2) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: doc instance.set_attr(E=5e2, aoi=instance.aoi.val, doc='var') self.nw.solve('design') instance.set_attr(E=8e2) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg def test_SolarCollector(self): """Test component properties of solar collector.""" instance = SolarCollector('solar collector') self.setup_HeatExchangerSimple_network(instance) fl = {'Ar': 0, 'H2O': 1, 'S800': 0} self.c1.set_attr(fluid=fl, p=10, T=30) self.c2.set_attr(T=70) # test grouped parameter settings with missing parameters instance.hydro_group.is_set = True instance.energy_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 = ('Energy group must no be set, if one parameter is missing!') assert instance.energy_group.is_set is False, msg # test solar collector params as system variables 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 E * A - Q (internal heat loss # calculation) T_diff = (self.c2.T.val + self.c1.T.val) / 2 - instance.Tamb.val Q_loss = -round(instance.A.val * ( instance.E.val * (1 - instance.eta_opt.val) + T_diff * instance.lkf_lin.val + T_diff ** 2 * instance.lkf_quad.val), 0) msg = ('Value for heat loss of solar collector must be ' + str(Q_loss) + ', is ' + str(round(instance.Q_loss.val, 0)) + '.') assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: E area = instance.A.val instance.set_attr(A=area * 1.2, E='var') self.nw.solve('design') instance.set_attr(A=area) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: eta_opt instance.set_attr(E=8e2, eta_opt='var') self.nw.solve('design') instance.set_attr(E=1e3) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: lkf_lin instance.set_attr(E=8e2, eta_opt=instance.eta_opt.val, lkf_lin='var') self.nw.solve('design') instance.set_attr(E=1e3) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: lkf_quad instance.set_attr(E=8e2, lkf_lin=instance.lkf_lin.val, lkf_quad='var') self.nw.solve('design') instance.set_attr(E=1e3) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg # test all parameters of the energy group: Tamb instance.set_attr(E=8e2, lkf_lin=instance.lkf_lin.val, lkf_quad='var') self.nw.solve('design') instance.set_attr(E=1e3) self.nw.solve('design') convergence_check(self.nw.lin_dep) assert Q_loss == round(instance.Q_loss.val, 0), msg 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) def test_Condenser(self): """Test component properties of Condenser.""" instance = Condenser('condenser') self.setup_HeatExchanger_network(instance) # design specification instance.set_attr(pr1=0.98, pr2=0.98, ttd_u=5, offdesign=['zeta2', 'kA_char']) self.c1.set_attr(T=100, p0=0.5, fluid={'Ar': 0, 'H2O': 1, 'S800': 0}) self.c3.set_attr(T=30, p=5, fluid={'Ar': 0, 'H2O': 1, 'S800': 0}) 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') Q_design = instance.Q.val # test specified kA value instance.set_attr(kA=instance.kA.val * 2 / 3, Q=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, Q=Q_design) 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(instance.Q.val, 0)) + ', is ' + str(round(Q, 0)) + '.') assert round(Q, 1) == round(instance.Q.val, 1), msg # test upper terminal temperature difference. For the component # condenser the temperature of the condensing fluid is relevant. ttd_u = round(T_bp_p(self.c1.get_flow()) - self.c4.T.val_SI, 1) p = round(self.c1.p.val_SI, 5) msg = ('Value of terminal temperature difference must be ' + str(round(instance.ttd_u.val, 1)) + ', is ' + str(ttd_u) + '.') assert ttd_u == round(instance.ttd_u.val, 1), msg # 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) + '.') 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 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)) + '.') assert p == round(self.c1.p.val_SI, 5), msg shutil.rmtree('./tmp', ignore_errors=True)
sg3.set_attr(pr=.99) sg1_sg2.set_attr(x=0) sg2_sg3.set_attr(x=1) # Connections cc_st.set_attr(T=500, p=100, fluid={'H2O': 1}) dh_Source_con.set_attr(T=T_dh_in, p=10, fluid={'H2O': 1}) con_dh_Sink.set_attr(T=T_dh_out) # %% keyparameter con.set_attr(Q=-30e6) # %% solving design mode nw.solve('design') nw.save('bpt') print(power.P.val) # plotting Ts-Diagram results = results() plot_Ts(results) # %% offdesign con.set_attr(Q=np.nan) power.set_attr(P=-10263542) nw.solve('offdesign', init_path='bpt', design_path='bpt') print(power.P.val)
class TestTurbomachinery: def setup_network(self, instance): self.nw = Network(['INCOMP::DowQ', 'NH3', 'N2', 'O2', 'Ar'], T_unit='C', p_unit='bar', v_unit='m3 / s') self.source = Source('source') self.sink = Sink('sink') self.c1 = Connection(self.source, 'out1', instance, 'in1') self.c2 = Connection(instance, 'out1', self.sink, 'in1') self.nw.add_conns(self.c1, self.c2) def test_Compressor(self): """Test component properties of compressors.""" instance = Compressor('compressor') self.setup_network(instance) # compress NH3, other fluids in network are for turbine, pump, ... fl = {'N2': 1, 'O2': 0, 'Ar': 0, 'DowQ': 0, 'NH3': 0} self.c1.set_attr(fluid=fl, v=1, p=1, T=5) self.c2.set_attr(p=6) 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 eta_s_d = ((isentropic(self.c1.get_flow(), self.c2.get_flow()) - self.c1.h.val_SI) / (self.c2.h.val_SI - self.c1.h.val_SI)) msg = ('Value of isentropic efficiency must be ' + str(eta_s_d) + ', is ' + str(instance.eta_s.val) + '.') assert round(eta_s_d, 3) == round(instance.eta_s.val, 3), msg # 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 = ((isentropic(self.c1.get_flow(), self.c2.get_flow()) - self.c1.h.val_SI) / (self.c2.h.val_SI - self.c1.h.val_SI)) msg = ('Value of isentropic efficiency must be ' + str(eta_s) + ', is ' + str(instance.eta_s.val) + '.') assert round(eta_s, 3) == round(instance.eta_s.val, 3), msg # remove pressure at outlet, use characteristic map for pressure # rise calculation self.c2.set_attr(p=np.nan) instance.set_attr(char_map_pr={ 'char_func': ldc('compressor', 'char_map_pr', 'DEFAULT', CharMap), 'is_set': True }, char_map_eta_s={ 'char_func': ldc('compressor', 'char_map_eta_s', 'DEFAULT', CharMap), 'is_set': True }, eta_s=np.nan, igva=0) # 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) + ').') assert round(eta_s_d, 2) == round(instance.eta_s.val, 2), msg # move to highest available speedline, mass flow below lowest value # 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_eta_s.char_func.z[6, 0] msg = ('Value of isentropic efficiency (' + str(instance.eta_s.val) + ') must be at (' + str(round(eta_s, 4)) + ').') assert round(eta_s, 4) == round(instance.eta_s.val, 4), msg # going below lowest available speedline, above highest mass flow at # that line self.c1.set_attr(T=175) 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_eta_s.char_func.z[0, 9] msg = ('Value of isentropic efficiency (' + str(instance.eta_s.val) + ') must be at (' + str(round(eta_s, 4)) + ').') assert round(eta_s, 4) == round(instance.eta_s.val, 4), msg # back to design properties, test eta_s_char self.c2.set_attr(p=6) self.c1.set_attr(v=1, T=5, m=np.nan) # test parameter specification for eta_s_char with unset char map instance.set_attr( eta_s_char={ 'char_func': ldc('compressor', 'eta_s_char', 'DEFAULT', CharLine), 'is_set': True, 'param': 'm' }) instance.char_map_eta_s.is_set = False instance.char_map_pr.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) + '.') assert round(eta_s_d, 3) == round(instance.eta_s.val, 3), msg # 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.char_func.evaluate( self.c1.m.val_SI / self.c1.m.design), 3) msg = ('Value of isentropic efficiency must be ' + str(eta_s) + ', is ' + str(round(instance.eta_s.val, 3)) + '.') assert eta_s == round(instance.eta_s.val, 3), msg # test parameter specification for pr instance.eta_s_char.set_attr(param='pr') self.c1.set_attr(v=1) self.c2.set_attr(p=6) 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.char_func.evaluate(expr), 3) msg = ('Value of isentropic efficiency must be ' + str(eta_s) + ', is ' + str(round(instance.eta_s.val, 3)) + '.') assert eta_s == round(instance.eta_s.val, 3), msg shutil.rmtree('./tmp', ignore_errors=True) def test_Pump(self): """Test component properties of pumps.""" instance = Pump('pump') self.setup_network(instance) fl = {'N2': 0, 'O2': 0, 'Ar': 0, 'DowQ': 1, 'NH3': 0} self.c1.set_attr(fluid=fl, v=1, p=5, T=50) 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 = ((isentropic(self.c1.get_flow(), self.c2.get_flow()) - self.c1.h.val_SI) / (self.c2.h.val_SI - self.c1.h.val_SI)) msg = ('Value of isentropic efficiency must be ' + str(eta_s) + ', is ' + str(instance.eta_s.val) + '.') assert eta_s == instance.eta_s.val, msg # isentropic efficiency of 1 means inlet and outlet entropy are # identical s1 = round(s_mix_ph(self.c1.get_flow()), 4) s2 = round(s_mix_ph(self.c2.get_flow()), 4) msg = ('Value of entropy must be identical for inlet (' + str(s1) + ') and outlet (' + str(s2) + ') at 100 % isentropic efficiency.') assert s1 == s2, msg # specify realistic value for efficiency, outlet pressure from flow # char 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) # flow char (pressure rise vs. volumetric flow) x = [0, 0.2, 0.4, 0.6, 0.8, 1, 1.2, 1.4] y = np.array([14, 13.5, 12.5, 11, 9, 6.5, 3.5, 0]) * 1e5 char = {'char_func': CharLine(x, y), 'is_set': True} # apply flow char and eta_s char instance.set_attr(flow_char=char, eta_s=np.nan, eta_s_char={ 'char_func': ldc('pump', 'eta_s_char', 'DEFAULT', CharLine), 'is_set': True }) self.nw.solve('offdesign', design_path='tmp') convergence_check(self.nw.lin_dep) # value for difference pressure dp = 650000.0 msg = ('Value of pressure rise must be ' + str(dp) + ', is ' + str(self.c2.p.val_SI - self.c1.p.val_SI) + '.') assert round(self.c2.p.val_SI - self.c1.p.val_SI, 0) == dp, msg # 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(round(self.c2.p.val_SI - self.c1.p.val_SI, 0)) + '.') assert round(self.c2.p.val_SI - self.c1.p.val_SI, 0) == dp, msg # test value of isentropic efficiency eta_s = round( eta_s_d * instance.eta_s_char.char_func.evaluate( self.c1.v.val_SI / self.c1.v.design), 3) msg = ('Value of isentropic efficiency must be ' + str(eta_s) + ', is ' + str(instance.eta_s.val) + '.') assert eta_s == round(instance.eta_s.val, 3), msg instance.eta_s_char.is_set = False # test boundaries of characteristic line: # lower boundary instance.set_attr(eta_s=0.8) self.c1.set_attr(m=0, v=None) self.nw.solve('design') convergence_check(self.nw.lin_dep) msg = ('Value of power must be ' + str(14e5) + ', is ' + str(round(self.c2.p.val_SI - self.c1.p.val_SI, 0)) + '.') assert round(self.c2.p.val_SI - self.c1.p.val_SI, 0) == 14e5, msg # upper boundary self.c1.set_attr(v=1.5, m=None) self.nw.solve('design') convergence_check(self.nw.lin_dep) msg = ('Value of power must be 0, is ' + str(round(self.c2.p.val_SI - self.c1.p.val_SI, 0)) + '.') assert round(self.c2.p.val_SI - self.c1.p.val_SI, 0) == 0, msg shutil.rmtree('./tmp', ignore_errors=True) def test_Turbine(self): """Test component properties of turbines.""" instance = Turbine('turbine') self.setup_network(instance) fl = {'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129, 'DowQ': 0, 'NH3': 0} self.c1.set_attr(fluid=fl, m=15, p=10) self.c2.set_attr(p=1, T=25) 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 eta_s_d = ((self.c2.h.val_SI - self.c1.h.val_SI) / (isentropic(self.c1.get_flow(), self.c2.get_flow()) - self.c1.h.val_SI)) msg = ('Value of isentropic efficiency must be ' + str(round(eta_s_d, 3)) + ', is ' + str(instance.eta_s.val) + '.') assert round(eta_s_d, 3) == round(instance.eta_s.val, 3), msg # 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 = ((self.c2.h.val_SI - self.c1.h.val_SI) / (isentropic(self.c1.get_flow(), self.c2.get_flow()) - self.c1.h.val_SI)) msg = ('Value of isentropic efficiency must be ' + str(eta_s) + ', is ' + str(instance.eta_s.val) + '.') assert round(eta_s, 3) == round(instance.eta_s.val, 3), msg # unset isentropic efficiency and inlet pressure, # use characteristcs and cone law instead, parameters have to be in # design state self.c1.set_attr(p=np.nan) instance.cone.is_set = True 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) + ').') assert round(eta_s_d, 2) == round(instance.eta_s.val, 2), msg # check pressure msg = ('Value of inlet pressure (' + str(round(self.c1.p.val_SI)) + ') must be identical to design case (' + str(round(self.c1.p.design)) + ').') assert round(self.c1.p.design) == round(self.c1.p.val_SI), msg # 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) + ').') assert 0.128 == round(instance.pr.val, 3), msg # testing more parameters for eta_s_char # test parameter specification v 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.char_func.evaluate(expr), 3) msg = ('Value of isentropic efficiency (' + str(round(instance.eta_s.val, 3)) + ') must be (' + str(eta_s) + ').') assert eta_s == round(instance.eta_s.val, 3), msg # 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.char_func.evaluate(expr), 3) msg = ('Value of isentropic efficiency (' + str(round(instance.eta_s.val, 3)) + ') must be (' + str(eta_s) + ').') assert eta_s == round(instance.eta_s.val, 3), msg shutil.rmtree('./tmp', ignore_errors=True) def test_Turbomachine(self): """Test component properties of turbomachines.""" instance = Turbomachine('turbomachine') msg = ('Component name must be turbomachine, is ' + instance.component() + '.') assert 'turbomachine' == instance.component(), msg self.setup_network(instance) fl = {'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129, 'DowQ': 0, 'NH3': 0} self.c1.set_attr(fluid=fl, m=10, p=1, h=1e5) self.c2.set_attr(p=3, h=2e5) # 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 ' + str(instance.P.val) + '.') assert power == instance.P.val, msg msg = ('Value of power must be ' + str(pr) + ', is ' + str(instance.pr.val) + '.') assert pr == instance.pr.val, msg # test pressure ratio specification 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) + '.') assert pr == instance.pr.val, msg # test power specification 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) + '.') assert power == instance.P.val, msg
class TestOrcEvaporator: def setup(self): self.nw = Network(['water', 'Isopentane'], T_unit='C', p_unit='bar', h_unit='kJ / kg') self.inl1 = Source('inlet 1') self.outl1 = Sink('outlet 1') self.inl2 = Source('inlet 2') self.outl2 = Sink('outlet 2') self.inl3 = Source('inlet 3') self.outl3 = Sink('outlet 3') self.instance = ORCEvaporator('orc evaporator') self.c1 = Connection(self.inl1, 'out1', self.instance, 'in1') self.c2 = Connection(self.instance, 'out1', self.outl1, 'in1') self.c3 = Connection(self.inl2, 'out1', self.instance, 'in2') self.c4 = Connection(self.instance, 'out2', self.outl2, 'in1') self.c5 = Connection(self.inl3, 'out1', self.instance, 'in3') self.c6 = Connection(self.instance, 'out3', self.outl3, 'in1') self.nw.add_conns(self.c1, self.c2, self.c3, self.c4, self.c5, self.c6) 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)
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)
'O2': 0, 'H2O': 1, 'CH4': 0 }) cw_o.set_attr(T=30, design=['T'], offdesign=['m']) # %% design case 1: # district heating condeser layout # Q_N=65 heat_out.set_attr(P=Q_N) nw.solve(mode='design', init_path='cet_stable') nw.print_results() nw.save('cet_design_maxQ') gt_power_design = gt_power.P.val print(heat_out.P.val / heat_in.P.val, power.P.val / heat_in.P.val) print(heat_out.P.val, power.P.val, heat_in.P.val) print(gt_power.P.val) # %% design case 2: # maximum gas turbine minimum heat extraction (cet_design_minQ) gt_power.set_attr(P=gt_power_design) heat_out.set_attr(P=-10e6) # local offdesign for district heating condenser cond_dh.set_attr(local_offdesign=True, design_path='cet_design_maxQ') pump1.set_attr(local_offdesign=True, design_path='cet_design_maxQ') mp_ws.set_attr(local_offdesign=True, design_path='cet_design_maxQ') mp_c.set_attr(local_offdesign=True, design_path='cet_design_maxQ')
nw.add_busses(power, heat_cons, heat_geo) # %% key parameter cd.set_attr(Q=-4e3) # %% design calculation path = 'R410A' nw.solve('design') # alternatively use: # nw.solve('design', init_path=path) print("\n##### DESIGN CALCULATION #####\n") nw.print_results() nw.save(path) # %% plot h_log(p) diagram # generate plotting data result_dict = {} result_dict.update({ev.label: ev.get_plotting_data()[2]}) result_dict.update({cp.label: cp.get_plotting_data()[1]}) result_dict.update({cd.label: cd.get_plotting_data()[1]}) result_dict.update({va.label: va.get_plotting_data()[1]}) # create plot diagram = FluidPropertyDiagram('R410A') diagram.set_unit_system(T='°C', p='bar', h='kJ/kg') for key, data in result_dict.items():
class TestNetworkIndividualOffdesign: def setup_Network_individual_offdesign(self): """Set up network for individual offdesign tests.""" self.nw = Network(['H2O'], T_unit='C', p_unit='bar', v_unit='m3 / s') so = Source('source') sp = Splitter('splitter', num_out=2) self.pump1 = Pump('pump 1') self.sc1 = SolarCollector('collector field 1') v1 = Valve('valve1') self.pump2 = Pump('pump 2') self.sc2 = SolarCollector('collector field 2') v2 = Valve('valve2') me = Merge('merge', num_in=2) si = Sink('sink') self.pump1.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) self.pump2.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) self.sc1.set_attr(pr=0.95, lkf_lin=3.33, lkf_quad=0.011, A=1252, E=700, Tamb=20, eta_opt=0.92, design=['pr'], offdesign=['zeta']) self.sc2.set_attr(pr=0.95, lkf_lin=3.5, lkf_quad=0.011, A=700, E=800, Tamb=20, eta_opt=0.92, design=['pr'], offdesign=['zeta']) fl = {'H2O': 1} inlet = Connection(so, 'out1', sp, 'in1', T=50, p=3, fluid=fl) outlet = Connection(me, 'out1', si, 'in1', p=3) self.sp_p1 = Connection(sp, 'out1', self.pump1, 'in1') self.p1_sc1 = Connection(self.pump1, 'out1', self.sc1, 'in1') self.sc1_v1 = Connection(self.sc1, 'out1', v1, 'in1', p=3.1, T=90) v1_me = Connection(v1, 'out1', me, 'in1') self.sp_p2 = Connection(sp, 'out2', self.pump2, 'in1') self.p2_sc2 = Connection(self.pump2, 'out1', self.sc2, 'in1') self.sc2_v2 = Connection(self.sc2, 'out1', v2, 'in1', p=3.1, m=0.1) v2_me = Connection(v2, 'out1', me, 'in2') self.nw.add_conns(inlet, outlet, self.sp_p1, self.p1_sc1, self.sc1_v1, v1_me, self.sp_p2, self.p2_sc2, self.sc2_v2, v2_me) def test_individual_design_path_on_connections_and_components(self): """Test individual design path specification.""" self.setup_Network_individual_offdesign() self.nw.solve('design') convergence_check(self.nw.lin_dep) self.sc2_v2.set_attr(m=0) self.nw.solve('design') convergence_check(self.nw.lin_dep) self.nw.save('design1') v1_design = self.sc1_v1.v.val_SI zeta_sc1_design = self.sc1.zeta.val self.sc2_v2.set_attr(T=95, state='l', m=None) self.sc1_v1.set_attr(m=0.001, T=None) self.nw.solve('design') convergence_check(self.nw.lin_dep) self.nw.save('design2') v2_design = self.sc2_v2.v.val_SI zeta_sc2_design = self.sc2.zeta.val self.sc1_v1.set_attr(m=np.nan) self.sc1_v1.set_attr(design=['T'], offdesign=['v'], state='l') self.sc2_v2.set_attr(design=['T'], offdesign=['v'], state='l') self.sc2.set_attr(design_path='design2') self.pump2.set_attr(design_path='design2') self.sp_p2.set_attr(design_path='design2') self.p2_sc2.set_attr(design_path='design2') self.sc2_v2.set_attr(design_path='design2') self.nw.solve('offdesign', design_path='design1') convergence_check(self.nw.lin_dep) self.sc1.set_attr(E=500) self.sc2.set_attr(E=950) self.nw.solve('offdesign', design_path='design1') convergence_check(self.nw.lin_dep) self.sc2_v2.set_attr(design_path=np.nan) # volumetric flow comparison msg = ('Design path was set to None, is ' + str(self.sc2_v2.design_path) + '.') assert self.sc2_v2.design_path is None, msg # volumetric flow comparison msg = ('Value of volumetric flow must be ' + str(v1_design) + ', is ' + str(self.sc1_v1.v.val_SI) + '.') assert round(v1_design, 5) == round(self.sc1_v1.v.val_SI, 5), msg msg = ('Value of volumetric flow must be ' + str(v2_design) + ', is ' + str(self.sc2_v2.v.val_SI) + '.') assert round(v2_design, 5) == round(self.sc2_v2.v.val_SI, 5), msg # zeta value of solar collector comparison msg = ('Value of zeta must be ' + str(zeta_sc1_design) + ', is ' + str(self.sc1.zeta.val) + '.') assert round(zeta_sc1_design, 0) == round(self.sc1.zeta.val, 0), msg msg = ('Value of zeta must be ' + str(zeta_sc2_design) + ', is ' + str(self.sc2.zeta.val) + '.') assert round(zeta_sc2_design, 0) == round(self.sc2.zeta.val, 0), msg shutil.rmtree('./design1', ignore_errors=True) shutil.rmtree('./design2', ignore_errors=True) def test_local_offdesign_on_connections_and_components(self): """Test local offdesign feature.""" self.setup_Network_individual_offdesign() self.nw.solve('design') convergence_check(self.nw.lin_dep) self.sc2_v2.set_attr(m=0) self.nw.solve('design') convergence_check(self.nw.lin_dep) self.nw.save('design1') 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, design_path='design1') self.sc1.set_attr(E=500) self.sc2_v2.set_attr(T=95, m=np.nan) self.nw.solve('design') convergence_check(self.nw.lin_dep) self.nw.save('design2') # connections and components on side 1 must have switched to offdesign msg = ('Solar collector outlet temperature must be different from ' + 'design value ' + str(round(self.sc1_v1.T.design - 273.15, 1)) + ', is ' + str(round(self.sc1_v1.T.val, 1)) + '.') assert self.sc1_v1.T.design > self.sc1_v1.T.val, msg msg = ('Parameter eta_s_char must be set for pump one.') assert self.pump1.eta_s_char.is_set, msg msg = ('Parameter v must be set for connection from solar collector1 ' 'to pump1.') assert self.sc1_v1.v.val_set, msg 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 on connections in local offdesign mode.""" self.setup_Network_individual_offdesign() self.nw.solve('design') convergence_check(self.nw.lin_dep) self.sc2_v2.set_attr(m=0) self.nw.solve('design') convergence_check(self.nw.lin_dep) self.nw.save('design1') 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)
p=5, fluid={ 'CO2': 0, 'Ar': 0, 'N2': 0, 'O2': 0, 'H2O': 1, 'CH4': 0 }) dh_w.set_attr(T=90) # %% nw.solve(mode='design') nw.print_results() nw.save('design_point') document_model(nw, filename='report_design.tex') power.set_attr(P=-100e6) nw.solve(mode='offdesign', init_path='design_point', design_path='design_point') nw.print_results() document_model(nw, filename='report_offdesign.tex') power.set_attr(P=1 / 0.9 * 0.8 * power.P.val) nw.solve(mode='offdesign', design_path='design_point') nw.print_results()
nw.add_conns(cd_va) # %% component parametrization cd.set_attr(pr1=0.99, pr2=0.99, ttd_u=5, design=['pr2', 'ttd_u'], offdesign=['zeta2', 'kA_char']) rp.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) cons.set_attr(pr=0.99, design=['pr'], offdesign=['zeta']) # %% connection parametrization c_in_cd.set_attr(T=170, fluid={'water': 0, 'NH3': 1}) close_rp.set_attr(T=60, p=10, fluid={'water': 1, 'NH3': 0}) cd_cons.set_attr(T=90) # %% key paramter cons.set_attr(Q=-230e3) # %% Calculation nw.solve('design') nw.print_results() nw.save('condenser') cons.set_attr(Q=-200e3) nw.solve('offdesign', design_path='condenser') nw.print_results()