def setup(self): self.nw = nwk.network(['water', 'air']) self.comp = cmp.cogeneration_unit('cogeneration unit') self.pipe = cmp.pipe('pipe') self.conn = con.connection(self.comp, 'out1', self.pipe, 'in1') self.bus = con.bus('mybus') self.sub = subsys.subsystem('MySub')
def test_heat_ex_simple(self): """ Test component properties of simple heat exchanger. """ instance = cmp.heat_exchanger_simple('heat exchanger') c1, c2 = self.setup_network_11(instance) fl = {'N2': 0, 'O2': 0, 'Ar': 0, 'INCOMP::DowQ': 0, 'H2O': 1, 'NH3': 0, 'CO2': 0, 'CH4': 0} 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) instance.hydro_group.is_set = True instance.kA_group.is_set = True self.nw.solve('design', init_only=True) eq_(instance.hydro_group.is_set, False, 'Hydro group must no be set, if one parameter is missing!') eq_(instance.kA_group.is_set, False, 'kA group must no be set, if one parameter is missing!') instance.set_attr(hydro_group='HW', D='var', L=100, ks=100, pr=0.99, Tamb=20) b = con.bus('heat', P=-1e5) b.add_comps({'c': instance}) self.nw.add_busses(b) self.nw.solve('design') eq_(round(c2.p.val_SI / c1.p.val_SI, 2), round(instance.pr.val, 2), 'Value of pressure ratio must be ' + str(c2.p.val_SI / c1.p.val_SI) + ', is ' + str(instance.pr.val) + '.') zeta = instance.zeta.val instance.set_attr(D=instance.D.val, zeta='var', pr=np.nan) instance.D.is_var = False self.nw.solve('design') eq_(round(zeta, 1), round(instance.zeta.val, 1), 'Value of zeta must be ' + str(zeta) + ', is ' + str(instance.zeta.val) + '.') pr = instance.pr.val instance.set_attr(zeta=np.nan, pr='var') self.nw.solve('design') eq_(round(pr, 2), round(instance.pr.val, 2), 'Value of pressure ratio must be ' + str(pr) + ', is ' + str(instance.pr.val) + '.') instance.set_attr(kA='var', pr=np.nan) b.set_attr(P=-5e4) self.nw.solve('design') # due to heat output being half of reference (for Tamb) kA should be somewhere near to that (actual value is 677) eq_(677, round(instance.kA.val, 0), 'Value of heat transfer coefficient must be ' + str(677) + ', is ' + str(instance.kA.val) + '.')
def test_combustion_chamber(self): """ Test component properties of combustion chambers. """ instance = cmp.combustion_chamber('combustion chamber', fuel='CH4') c1, c2, c3 = self.setup_network_21(instance) air = {'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129, 'INCOMP::DowQ': 0, 'H2O': 0, 'NH3': 0, 'CO2': 0, 'CH4': 0} fuel = {'N2': 0, 'O2': 0, 'Ar': 0, 'INCOMP::DowQ': 0, 'H2O': 0, 'NH3': 0, 'CO2': 0.04, 'CH4': 0.96} c1.set_attr(fluid=air, p=1, T=30) c2.set_attr(fluid=fuel, T=30) c3.set_attr(T=1200) b = con.bus('thermal input', P=1e6) b.add_comps({'c': instance}) self.nw.add_busses(b) self.nw.solve('design') self.nw.print_results() eq_(round(b.P.val, 1), round(instance.ti.val, 1), 'Value of thermal input must be ' + str(b.P.val) + ', is ' + str(instance.ti.val) + '.') # test unspecified fuel instance.set_attr(fuel=np.nan) try: self.nw.solve('design') except hlp.TESPyComponentError: pass # test wrongly specified fuel instance.set_attr(fuel='Ar') try: self.nw.solve('design') except hlp.TESPyComponentError: pass
def construct_busses(c, *args): """ creates busses :param c: bus information :type c: pandas.core.series.Series :returns: b (*tespy.connection.bus*) - TESPy bus object """ # set up bus with label and specify value for power b = con.bus(c.label, P=c.P) b.P.val_set = c.P_set return b
def test_heat_ex(self): """ Test component properties of heat exchanger. """ tesin = cmp.sink('TES in') tesout = cmp.source('TES out') hsin = cmp.sink('HS in') hsout = cmp.source('HS out') he = cmp.heat_exchanger('heat exchanger') tes_he = con.connection(tesout, 'out1', he, 'in2') he_tes = con.connection(he, 'out2', tesin, 'in1') hs_he = con.connection(hsout, 'out1', he, 'in1') he_hs = con.connection(he, 'out1', hsin, 'in1') self.nw.add_conns(tes_he, he_tes, hs_he, he_hs) # design specification he.set_attr(pr1=0.98, pr2=0.98, ttd_u=5, design=['pr1', 'pr2', 'ttd_u'], offdesign=['zeta1', 'zeta2', 'kA']) hs_he.set_attr(T=120, p=3, fluid={'N2': 0, 'O2': 0, 'Ar': 0, 'INCOMP::DowQ': 0, 'H2O': 1, 'NH3': 0, 'CO2': 0, 'CH4': 0}) he_hs.set_attr(T=70) tes_he.set_attr(T=40, p=5, fluid={'N2': 0, 'O2': 0, 'Ar': 1, 'INCOMP::DowQ': 0, 'H2O': 0, 'NH3': 0, 'CO2': 0, 'CH4': 0}) b = con.bus('heat transfer', P=-80e3) b.add_comps({'c': he}) self.nw.add_busses(b) self.nw.solve('design') # check heat flow Q = hs_he.m.val_SI * (he_hs.h.val_SI - hs_he.h.val_SI) self.nw.save('tmp') eq_(round(hs_he.T.val - he_tes.T.val, 1), round(he.ttd_u.val, 1), 'Value of terminal temperature difference must be ' + str(he.ttd_u.val) + ', is ' + str(hs_he.T.val - he_tes.T.val) + '.') # check lower terminal temperature difference he_hs.set_attr(T=np.nan) he.set_attr(ttd_l=20) self.nw.solve('design') eq_(round(he_hs.T.val - tes_he.T.val, 1), round(he.ttd_l.val, 1), 'Value of terminal temperature difference must be ' + str(he.ttd_l.val) + ', is ' + str(he_hs.T.val - tes_he.T.val) + '.') # check kA value self.nw.solve('offdesign', design_path='tmp') eq_(round(Q, 1), round(he.Q.val, 1), 'Value of heat flow must be ' + str(he.Q.val) + ', is ' + str(Q) + '.') # trigger errors for negative terminal temperature differences at given kA-value he.set_attr(ttd_l=np.nan) # ttd_l he_hs.set_attr(T=30) try: self.nw.solve('offdesign', design_path='tmp') except ValueError: pass # ttd_u he_hs.set_attr(T=np.nan) he_tes.set_attr(T=130) try: self.nw.solve('offdesign', design_path='tmp') except ValueError: pass shutil.rmtree('./tmp', ignore_errors=True)
def construct_busses(c, *args): r""" Creates busses of the network. Parameters ---------- c : pandas.core.series.Series Bus information from .csv-file. Returns ------- b : tespy.connections.bus TESPy bus object. """ # set up bus with label and specify value for power b = con.bus(c.label, P=c.P) b.P.val_set = c.P_set return b
def test_network_buslabel_duplicate(self): bus = con.bus('mybus') self.nw.add_busses(self.bus) self.nw.add_busses(bus)
def setup(self): self.nw = nwk.network(['water', 'air']) self.comp = cmp.cogeneration_unit('cogeneration unit') self.bus = con.bus('power') self.bus.add_comps({'c': self.comp, 'p': 'Param'})
sp_bhe2 = con.connection(sp, 'out2', bhe2, 'in1') sp_bhe3 = con.connection(sp, 'out3', bhe3, 'in1') bhe1_mg = con.connection(bhe1, 'out1', mg, 'in1') bhe2_mg = con.connection(bhe2, 'out1', mg, 'in2') bhe3_mg = con.connection(bhe3, 'out1', mg, 'in3') mg_cons = con.connection(mg, 'out1', cons, 'in1') cons_fc = con.connection(cons, 'out1', fc_out, 'in1') btes.add_conns(fc_pu, pu_sp, sp_bhe1, sp_bhe2, sp_bhe3, bhe1_mg, bhe2_mg, bhe3_mg, mg_cons, cons_fc) # busses heat = con.bus('consumer heat demand') heat.add_comps({'c': cons, 'p': 'P'}) btes.add_busses(heat) # flow_char # provide volumetric flow in m^3 / s x = np.array([ 0.00, 0.00001952885971862, 0.00390577194372, 0.005858657915586, 0.007811543887448, 0.00976442985931, 0.011717315831173, 0.013670201803035, 0.015623087774897, 0.017575973746759, 0.019528859718621, 0.021481745690483, 0.023434631662345, 0.025387517634207, 0.027340403606069, 0.029293289577931, 0.031246175549793, 0.033199061521655, 0.035151947493517, 0.037104833465379, 0.039057719437241, 0.041010605409104, 0.042963491380966, 0.044916377352828, 0.04686926332469, 0.048822149296552, 0.050775035268414, 0.052727921240276, 0.054680807212138, 0.056633693184 ])
def createTESPynet(self, name): """ This function creates a district heating network with the python package TESPy, calculates it and saves it. Parameters ---------- name: str name of the network to be created --> for storing! +++ TODO: - split this function into several ones... - import all parameters into the components +++ """ fluids = ['water'] T_unit = 'C' Tamb = 0 Tamb_ref = 0 # This should be parametrized to! self.xmls['flowNetworkLevel'] self.nw = nwk.network(fluids=fluids, T_unit=T_unit, p_unit='bar', h_unit='kJ / kg', p_range=[2, 30], T_range=[10, 100], h_range=[10, 380]) print('Starting to create a TESPy district heating network.') # %% deadend-check self.nodeadends = False # TODO: put this into a function, to be able to iterate through dead-pipes!!! print('Terminal has {} entries bedore deadendcheck.'.format( str(len(self.xmls['flowTerminal'])))) self.numberofdels = 0 def deadendcheck(): # Check for deadends first and delete them! - else there will be errors! allcon = self.xmls['flowTerminal'].groupby('Node_ID')['Element_ID'] ball = allcon.count() self.df_filter = pd.DataFrame(data=ball, index=ball.index, columns=['Element_ID']) self.df_filter.columns = ['count_all'] # dead-ends # network has errors - how to resolve? deadend = self.df_filter['count_all'][self.df_filter['count_all'] == 1] if len(deadend) > 0: print('deadend check found some dead_ends:') for node in ball[deadend.index].index: # get the element connected to the node: element = allcon.get_group(node) print('deadend in node{}, with element {}.'.format( node, element[0])) # delete all connections with the element a = self.xmls['flowTerminal'] a = a.T # print(len(a[element[0]].loc['Element_ID'])) self.numberofdels += len(a[element[0]].loc['Element_ID']) del a[element[0]] a = a.T self.xmls['flowTerminal'] = a # delete the element a = self.xmls['flowElement'] a = a.T # print(a[element[0]]['Element_ID']) del a[element[0]] a = a.T self.xmls['flowElement'] = a # delete the line a = self.xmls['flowLine'] a = a.T # print(a[element[0]]['Element_ID']) del a[element[0]] a = a.T self.xmls['flowLine'] = a else: print('network cleaned from deadends!') self.nodeadends = True while self.nodeadends == False: deadendcheck() # print(self.numberofdels) print('Terminal has {} entries after deadendcheck.'.format( str(len(self.xmls['flowTerminal'])))) # locate and save the input pressure! p_in = 15 # %% components # sources and sinks in the Infeeder print('Creating infeeder...') infeed = {} for i in self.xmls['flowInfeeder'].index: infeed["infeed" + str(i)] = infeeder("infeed" + str(i)) self.infeed = infeed # %% construction part print('Creating pipes...') # pipe_feedings and backs pipes = {} for i in self.xmls['flowLine'].index: ks = float(self.xmls['flowLine']['SandRoughness'] [i]) / 1000 # TESPy parameter is in m! L = float(self.xmls['flowLine']['LineLength'][i]) D = float(self.xmls['flowLine']['Diameter'] [i]) / 1000 # TESPy parameter is in m! # TODO: kA is not yet implemented in the pipes component!!! # HeatingCond(uctivity) is in W/mK, whereas kA is in W/K kA = float(self.xmls['flowLine']['HeatingCond'][i]) * L kA = 2 pipes['pipe' + str(i)] = pipe(label='pipe' + str(i), ks_pb=ks, L_pb=L, D_pb=D, ks_pf=ks, L_pf=L, D_pf=D, kA_pb=kA, kA_pf=kA, Tamb=Tamb, design_pf=['kA'], design_pb=['kA']) self.pipes = pipes # %% subsystems for consumers print('Creating consumers...') consumers = {} for i in self.xmls['flowConsumer'].index: consumers['con' + str(i)] = sc( 'con' + str(i)) # name of consumer; class defined above # consumer attributes ### TODO: import values from the xml-dataframes! # The xmls include Q1 to Q4, while Q1-Q3 are always empty, # Q4 sometimes is filled.. significance?? Q = self.xmls['flowConsumer'].loc[i, 'Power'] # pDiffMin and pRelMin are given... prelmin = float(self.xmls['flowConsumer'].loc[i, 'pRelMin']) nodeid = self.xmls['flowTerminal'].loc[i, 'Node_ID'] results = self.xmls['flowHSNodeResult'].loc[nodeid] results = results[results['Circuit'] == '1']['pDiff'] pdiff = results.values pdiff = float(pdiff[0]) pr = (p_in - prelmin) / p_in T_out = self.xmls['flowConsumer'].loc[i, 'T'] consumers['con' + str(i)].set_attr(Q=float(Q) * (-1000000), pr=pdiff, T_out=float(T_out)) self.consumers = consumers print('Creating pressure regulators...') preg = {} for i in self.xmls['flowPressureReg'].index: preg["preg" + str(i)] = pressurereg("preg" + str(i)) p_in = self.xmls['flowPressureReg'].loc[i, 'pInlet'] p_out = self.xmls['flowPressureReg'].loc[i, 'pOutlet'] pr = float(p_out) - float(p_in) zeta = 1 preg["preg" + str(i)].set_attr(pr_vf=pr, pr_vb=pr, zeta_vf=zeta, zeta_vb=zeta) self.preg = preg # %% connections print('Creating connections...') # create a dict for connection objects stored inside conns = {} allcon = self.xmls['flowTerminal'].groupby('Node_ID')['Element_ID'] ball = allcon.count() self.df_filter = pd.DataFrame(data=ball, index=ball.index, columns=['Element_ID']) self.df_filter.columns = ['count_all'] # two-way connections twoway = self.df_filter['count_all'][self.df_filter['count_all'] == 2] self.twoway = twoway componentnames = { 'FlowLine': 'pipe', 'FlowConsumer': 'con', 'FlowPressureReg': 'preg', 'FlowInfeederH': 'infeed' } for node in ball[twoway.index].index: elements = allcon.get_group(node) eltype = {} comps = [] for i in elements: a = self.xmls['flowTerminal'].loc[i] if type(a) == type(pd.Series()): side = int(a['TerminalNo']) else: side = int(a[a['Node_ID'] == node]['TerminalNo']) el = { i: [ componentnames[self.xmls['flowElement'].loc[i] ['Type']], side ] } eltype.update(el) # print(eltype) # Components get sorted, so that pipes always are first to be connected # else a consumer could get connected to the input of the fork... # Another solution is very welcome! sorted_eltype = sorted(eltype.items(), key=operator.itemgetter(1), reverse=True) # print('Sorted: '+str(sorted_eltype)) for comp in sorted_eltype: if comp[1][0] == 'pipe': comps += [pipes["pipe" + str(comp[0])]] elif comp[1][0] == 'con': comps += [consumers["con" + str(comp[0])]] elif comp[1][0] == 'preg': comps += [preg["preg" + str(comp[0])]] elif comp[1][0] == 'infeed': comps += [infeed["infeed" + str(comp[0])]] # create connections: conns.update( self.link(comps[0], comps[1], num1=int(sorted_eltype[0][1][1]), num2=int(sorted_eltype[1][1][1]))) # three-way connections threeway = self.df_filter['count_all'][self.df_filter['count_all'] == 3] self.threeway = threeway forks = {} componentnames = { 'FlowLine': 'pipe', 'FlowConsumer': 'con', 'FlowPressureReg': 'preg', 'FlowInfeederH': 'infeed' } for node in ball[threeway.index].index: elements = allcon.get_group(node) eltype = {} comps = [] for i in elements: a = self.xmls['flowTerminal'].loc[i] if type(a) == type(pd.Series()): side = int(a['TerminalNo']) else: side = int(a[a['Node_ID'] == node]['TerminalNo']) el = { i: [ componentnames[self.xmls['flowElement'].loc[i] ['Type']], side ] } eltype.update(el) # print(eltype) # Components get sorted, so that pipes always are first to be connected # else a consumer could get connected to the input of the fork... # Another solution is very welcome! sorted_eltype = sorted(eltype.items(), key=operator.itemgetter(1), reverse=True) # print('Sorted: '+str(sorted_eltype)) for comp in sorted_eltype: if comp[1][0] == 'pipe': comps += [pipes["pipe" + str(comp[0])]] elif comp[1][0] == 'con': comps += [consumers["con" + str(comp[0])]] elif comp[1][0] == 'preg': comps += [preg["preg" + str(comp[0])]] elif comp[1][0] == 'infeed': comps += [infeed["infeed" + str(comp[0])]] # create fork: forks['fork' + str(node)] = sf('fork' + str(node)) # create connections: conns.update( self.link(comps[0], forks['fork' + str(node)], num1=int(sorted_eltype[0][1][1]), num2=1)) conns.update( self.link(forks['fork' + str(node)], comps[1], num1=2, num2=int(sorted_eltype[1][1][1]))) conns.update( self.link(forks['fork' + str(node)], comps[2], num1=3, num2=int(sorted_eltype[2][1][1]))) # print('node{} does not fit!!!!!!!!!!!!'.format(str(node))) # four-way connections fourway = self.df_filter['count_all'][self.df_filter['count_all'] == 4] self.fourway = fourway componentnames = { 'FlowLine': 'pipe', 'FlowConsumer': 'con', 'FlowPressureReg': 'preg', 'FlowInfeederH': 'infeed' } for node in ball[fourway.index].index: elements = allcon.get_group(node) eltype = {} comps = [] for i in elements: a = self.xmls['flowTerminal'].loc[i] if type(a) == type(pd.Series()): side = int(a['TerminalNo']) else: side = int(a[a['Node_ID'] == node]['TerminalNo']) el = { i: [ componentnames[self.xmls['flowElement'].loc[i] ['Type']], side ] } eltype.update(el) # print(eltype) # Components get sorted, so that pipes always are first to be connected # else a consumer could get connected to the input of the fork... # Another solution is very welcome! sorted_eltype = sorted(eltype.items(), key=operator.itemgetter(1), reverse=True) # print('Sorted: '+str(sorted_eltype)) for comp in sorted_eltype: if comp[1][0] == 'pipe': comps += [pipes["pipe" + str(comp[0])]] elif comp[1][0] == 'con': comps += [consumers["con" + str(comp[0])]] elif comp[1][0] == 'preg': print('oho') # comps += preg["preg"+str(comp[0])] elif comp[1][0] == 'infeed': # comps += infeed["infeed"+str(comp[0])] print('oho') # create two forks: forks['fork' + str(node) + '_1'] = sf('fork' + str(node) + '_1') forks['fork' + str(node) + '_2'] = sf('fork' + str(node) + '_2') # print(sorted_eltype[0][1][1][0]) conns.update( self.link(comps[0], forks['fork' + str(node) + '_1'], num1=int(sorted_eltype[0][1][1]), num2=1)) conns.update( self.link(forks['fork' + str(node) + '_1'], comps[1], num1=2, num2=int(sorted_eltype[1][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_1'], forks['fork' + str(node) + '_2'], num1=3, num2=1)) conns.update( self.link(forks['fork' + str(node) + '_2'], comps[2], num1=2, num2=int(sorted_eltype[2][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_2'], comps[3], num1=3, num2=int(sorted_eltype[3][1][1]))) # ES GIBT NOCH KNOTEN MIT 5 UND 6 ELEMENTEN!!!! # Außerdem können forks mit beliebig vielen Ausgängen erstellt werden... # Das auf lange Sicht komplett umschreiben... ist so viel zu langer Code... # fiveway fiveway = self.df_filter['count_all'][self.df_filter['count_all'] == 5] componentnames = { 'FlowLine': 'pipe', 'FlowConsumer': 'con', 'FlowPressureReg': 'preg', 'FlowInfeederH': 'infeed' } for node in ball[fiveway.index].index: elements = allcon.get_group(node) eltype = {} comps = [] for i in elements: a = self.xmls['flowTerminal'].loc[i] if type(a) == type(pd.Series()): side = int(a['TerminalNo']) else: side = int(a[a['Node_ID'] == node]['TerminalNo']) el = { i: [ componentnames[self.xmls['flowElement'].loc[i] ['Type']], side ] } eltype.update(el) # print(eltype) # Components get sorted, so that pipes always are first to be connected # else a consumer could get connected to the input of the fork... # Another solution is very welcome! sorted_eltype = sorted(eltype.items(), key=operator.itemgetter(1), reverse=True) # print('Sorted: '+str(sorted_eltype)) for comp in sorted_eltype: if comp[1][0] == 'pipe': comps += [pipes["pipe" + str(comp[0])]] elif comp[1][0] == 'con': comps += [consumers["con" + str(comp[0])]] elif comp[1][0] == 'preg': print('oho') # comps += preg["preg"+str(comp[0])] elif comp[1][0] == 'infeed': # comps += infeed["infeed"+str(comp[0])] print('oho') # create three forks: forks['fork' + str(node) + '_1'] = sf('fork' + str(node) + '_1') forks['fork' + str(node) + '_2'] = sf('fork' + str(node) + '_2') forks['fork' + str(node) + '_3'] = sf('fork' + str(node) + '_3') # print(sorted_eltype[0][1][1][0]) conns.update( self.link(comps[0], forks['fork' + str(node) + '_1'], num1=int(sorted_eltype[0][1][1]), num2=1)) conns.update( self.link(forks['fork' + str(node) + '_1'], comps[1], num1=2, num2=int(sorted_eltype[1][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_1'], forks['fork' + str(node) + '_2'], num1=3, num2=1)) conns.update( self.link(forks['fork' + str(node) + '_2'], comps[2], num1=2, num2=int(sorted_eltype[2][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_2'], forks['fork' + str(node) + '_3'], num1=3, num2=1)) conns.update( self.link(forks['fork' + str(node) + '_3'], comps[3], num1=2, num2=int(sorted_eltype[3][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_3'], comps[4], num1=3, num2=int(sorted_eltype[4][1][1]))) # sixway sixway = self.df_filter['count_all'][self.df_filter['count_all'] == 6] componentnames = { 'FlowLine': 'pipe', 'FlowConsumer': 'con', 'FlowPressureReg': 'preg', 'FlowInfeederH': 'infeed' } for node in ball[sixway.index].index: elements = allcon.get_group(node) eltype = {} comps = [] for i in elements: a = self.xmls['flowTerminal'].loc[i] if type(a) == type(pd.Series()): side = int(a['TerminalNo']) else: side = int(a[a['Node_ID'] == node]['TerminalNo']) el = { i: [ componentnames[self.xmls['flowElement'].loc[i] ['Type']], side ] } eltype.update(el) # print(eltype) # Components get sorted, so that pipes always are first to be connected # else a consumer could get connected to the input of the fork... # Another solution is very welcome! sorted_eltype = sorted(eltype.items(), key=operator.itemgetter(1), reverse=True) # print('Sorted: '+str(sorted_eltype)) for comp in sorted_eltype: if comp[1][0] == 'pipe': comps += [pipes["pipe" + str(comp[0])]] elif comp[1][0] == 'con': comps += [consumers["con" + str(comp[0])]] elif comp[1][0] == 'preg': print('oho') # comps += preg["preg"+str(comp[0])] elif comp[1][0] == 'infeed': # comps += infeed["infeed"+str(comp[0])] print('oho') # create two forks: forks['fork' + str(node) + '_1'] = sf('fork' + str(node) + '_1') forks['fork' + str(node) + '_2'] = sf('fork' + str(node) + '_2') forks['fork' + str(node) + '_3'] = sf('fork' + str(node) + '_3') forks['fork' + str(node) + '_4'] = sf('fork' + str(node) + '_4') # print(sorted_eltype[0][1][1][0]) conns.update( self.link(comps[0], forks['fork' + str(node) + '_1'], num1=int(sorted_eltype[0][1][1]), num2=1)) conns.update( self.link(forks['fork' + str(node) + '_1'], comps[1], num1=2, num2=int(sorted_eltype[1][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_1'], forks['fork' + str(node) + '_2'], num1=3, num2=1)) conns.update( self.link(forks['fork' + str(node) + '_2'], comps[2], num1=2, num2=int(sorted_eltype[2][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_2'], forks['fork' + str(node) + '_3'], num1=3, num2=1)) conns.update( self.link(forks['fork' + str(node) + '_3'], comps[3], num1=2, num2=int(sorted_eltype[3][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_3'], forks['fork' + str(node) + '_4'], num1=3, num2=1)) conns.update( self.link(forks['fork' + str(node) + '_4'], comps[4], num1=2, num2=int(sorted_eltype[4][1][1]))) conns.update( self.link(forks['fork' + str(node) + '_4'], comps[5], num1=3, num2=int(sorted_eltype[5][1][1]))) print('implementing the connections') self.conns = conns i = 0 for conn in conns: # print('implement: '+str(conn)) try: self.nw.add_conns(conns[conn]) # print(str(conn)) except: print("problems with connection {}; atm {} conns implemented.". format(str(conn), str(i))) raise i = i + 1 print('implementing the consumers') for comp in consumers: self.nw.add_subsys(consumers[comp]) # print(str(comp)+' added') print('implementing the pipes') for comp in pipes: self.nw.add_subsys(pipes[comp]) print('implementing the infeeds') for comp in infeed: self.nw.add_subsys(infeed[comp]) print('implementing the pressure-regulators') for comp in preg: self.nw.add_subsys(preg[comp]) print('implementing all forks') for comp in forks: self.nw.add_subsys(forks[comp]) # %% busses # heat_losses = con.bus('network losses') heat_consumer = con.bus('network consumer') print('checking network') self.nw.check_network() Tamb = 0 for comp in self.nw.comps.index: if isinstance(comp, cmp.pipe): comp.set_attr(Tamb=Tamb) heat_losses.add_comps({'c': comp}) if (isinstance(comp, cmp.heat_exchanger_simple) and not isinstance(comp, cmp.pipe)): heat_consumer.add_comps({'c': comp}) self.nw.add_busses(heat_losses, heat_consumer) self.nw.max_iter = 10 self.nw.solve('design') self.nw.save(name, structure=True) print('Heat demand consumer:', heat_consumer.P.val) print('network losses at 0 °C outside temperature (design):', heat_losses.P.val)
ls = con.connection(ls_in, 'out1', turb, 'in1') ws = con.connection(turb, 'out1', cond, 'in1') c_p = con.connection(cond, 'out1', pump, 'in1') fw = con.connection(pump, 'out1', eco, 'in2') nw.add_conns(suph_ls, ls, ws, c_p, fw) # district heating dh_c = con.connection(dh_in, 'out1', cond, 'in2') dh_i = con.connection(cond, 'out2', dh_whr, 'in2') dh_w = con.connection(dh_whr, 'out2', dh_out, 'in1') nw.add_conns(dh_c, dh_i, dh_w) # %% busses power = con.bus('power output') power.add_comps({'c': g_turb}, {'c': comp}, {'c': turb}, {'c': pump}) heat_out = con.bus('heat output') heat_out.add_comps({'c': cond}, {'c': dh_whr}) heat_in = con.bus('heat input') heat_in.add_comps({'c': c_c}) nw.add_busses(power, heat_out, heat_in) # %% component parameters # gas turbine comp.set_attr(pr=14, eta_s=0.91, design=['pr', 'eta_s'],
def test_water_electrolyzer(self): """ Test component properties of water electrolyzer. """ self.nw = nwk.network(['H2O', 'O2']) instance = cmp.water_electrolyzer('electrolyzer') self.setup_network_electrolyzer(instance) try: self.nw.solve('design') except ValueError: pass self.nw = nwk.network(['H2O', 'H2']) instance = cmp.water_electrolyzer('electrolyzer') self.setup_network_electrolyzer(instance) try: self.nw.solve('design') except ValueError: pass self.nw = nwk.network(['O2', 'H2']) instance = cmp.water_electrolyzer('electrolyzer') self.setup_network_electrolyzer(instance) try: self.nw.solve('design') except ValueError: pass self.nw = nwk.network(['O2', 'H2', 'H2O'], T_unit='C', p_unit='bar') instance = cmp.water_electrolyzer('electrolyzer') self.setup_network_electrolyzer(instance) # check bus functions power = con.bus('power') power.add_comps({'c': instance, 'p': 'P'}) power.set_attr(P=2.5e6) self.nw.add_busses(power) self.nw.solve('design') msg = ('Value of power must be ' + str(power.P.val) + ', is ' + str(instance.P.val) + '.') eq_(round(power.P.val, 1), round(instance.P.val), msg) # check bus functions power.set_attr(P=np.nan) heat = con.bus('heat') heat.add_comps({'c': instance, 'p': 'Q'}) heat.set_attr(P=-8e5) self.nw.add_busses(heat) self.nw.solve('design') msg = ('Value of heat flow must be ' + str(heat.P.val) + ', is ' + str(instance.Q.val) + '.') eq_(round(heat.P.val, 1), round(instance.Q.val), msg) self.nw.save('tmp') Q = heat.P.val * 0.9 heat.set_attr(P=Q) self.nw.solve('offdesign', design_path='tmp') msg = ('Value of heat flow must be ' + str(Q) + ', is ' + str(instance.Q.val) + '.') eq_(round(Q, 1), round(instance.Q.val), msg) self.nw.del_busses(heat, power) # check invalid bus parameter error some_bus = con.bus('some_bus') some_bus.add_comps({'c': instance, 'p': 'G'}) some_bus.set_attr(P=-8e5) self.nw.add_busses(some_bus) try: self.nw.solve('design') except ValueError: pass # check invald bus parameter error in derivatives try: instance.bus_deriv(some_bus.comps.loc[instance]) except ValueError: pass self.nw.del_busses(some_bus) instance.set_attr(eta=0.9, e='var') self.nw.solve('design') msg = ('Value of efficiency must be ' + str(instance.eta.val) + ', is ' + str(instance.e0 / instance.e.val) + '.') eq_(round(instance.eta.val, 2), round(instance.e0 / instance.e.val, 2), msg) # isentropic efficiency value > 1 e = 130e6 instance.set_attr(e=np.nan, eta=np.nan) instance.set_attr(e=e) self.nw.solve('design') msg = ('Value of efficiency must be ' + str(instance.e0 / e) + ', is ' + str(instance.eta.val) + '.') eq_(round(instance.e0 / e, 2), round(instance.eta.val, 2), msg) e = 150e6 instance.set_attr(e=np.nan, eta=np.nan) instance.set_attr(e=e) self.nw.solve('design') msg = ('Value of efficiency must be ' + str(e) + ', is ' + str(instance.e.val) + '.') eq_(round(e, 1), round(instance.e.val, 1), msg) pr = 0.95 instance.set_attr(pr_c=pr, e=np.nan, zeta='var', P=2.5e6, design=['pr_c']) self.nw.solve('design') self.nw.save('tmp') msg = ('Value of pressure ratio must be ' + str(pr) + ', is ' + str(instance.pr_c.val) + '.') eq_(round(pr, 2), round(instance.pr_c.val, 2), msg) instance.set_attr(zeta=np.nan, offdesign=['zeta']) self.nw.solve('offdesign', design_path='tmp') msg = ('Value of pressure ratio must be ' + str(pr) + ', is ' + str(instance.pr_c.val) + '.') eq_(round(pr, 2), round(instance.pr_c.val, 2), msg) Q = instance.Q.val * 0.9 instance.set_attr(Q=Q, P=np.nan) self.nw.solve('offdesign', design_path='tmp') msg = ('Value of heat must be ' + str(Q) + ', is ' + str(instance.Q.val) + '.') eq_(round(Q, 0), round(instance.Q.val, 0), msg) shutil.rmtree('./tmp', ignore_errors=True)
# cooling water cw_in = con.connection(source_cw, 'out1', condenser, 'in2') cw_out = con.connection(condenser, 'out2', sink_cw, 'in1') nw.add_conns(cw_in, cw_out) # %% busses # characteristic function for generator efficiency x = np.array([0, 0.2, 0.4, 0.6, 0.8, 1, 1.2]) y = np.array([0, 0.86, 0.9, 0.93, 0.95, 0.96, 0.95]) gen = cmp_char.characteristics(x=x, y=y) # motor of pump has a constant efficiency power = con.bus('total output power') power.add_comps({ 'c': turbine, 'p': 'P', 'char': gen }, { 'c': pump, 'char': 1 / 0.95 }) nw.add_busses(power) # %% parametrization of components turbine.set_attr(eta_s=0.9, design=['eta_s'], offdesign=['eta_s_char', 'cone']) condenser.set_attr(pr1=0.5, pr2=0.98,
def setup(self): # %% network self.nw = nwk.network(fluids=['water', 'NH3'], T_unit='C', p_unit='bar', h_unit='kJ / kg', m_unit='kg / s') # %% components # sources & sinks c_in = cmp.source('coolant in') cb = cmp.source('consumer back flow') cf = cmp.sink('consumer feed flow') amb_in = cmp.source('source ambient') amb_out = cmp.sink('sink ambient') ic_in = cmp.source('source intercool') ic_out = cmp.sink('sink intercool') c_out = cmp.sink('coolant out') # consumer system cd = cmp.heat_exchanger('condenser') rp = cmp.pump('recirculation pump') cons = cmp.heat_exchanger_simple('consumer') # evaporator system va = cmp.valve('valve') dr = cmp.drum('drum') ev = cmp.heat_exchanger('evaporator') su = cmp.heat_exchanger('superheater') pu = cmp.pump('pump evaporator') # compressor-system cp1 = cmp.compressor('compressor 1') cp2 = cmp.compressor('compressor 2') he = cmp.heat_exchanger('intercooler') # busses x = np.array([0, 0.7, 1, 1.3]) y = 1 / np.array([0.8, 0.95, 1, 0.98]) / 0.9583794 motor = cmp_char.characteristics(x=x, y=y) self.power = con.bus('total compressor power') self.power.add_comps({ 'c': cp1, 'char': motor }, { 'c': cp2, 'char': motor }) self.heat = con.bus('total delivered heat') self.heat.add_comps({'c': cd, 'char': -1}) self.nw.add_busses(self.power, self.heat) # %% connections # consumer system c_in_cd = con.connection(c_in, 'out1', cd, 'in1') cb_rp = con.connection(cb, 'out1', rp, 'in1') rp_cd = con.connection(rp, 'out1', cd, 'in2') self.cd_cons = con.connection(cd, 'out2', cons, 'in1') cons_cf = con.connection(cons, 'out1', cf, 'in1') self.nw.add_conns(c_in_cd, cb_rp, rp_cd, self.cd_cons, cons_cf) # connection condenser - evaporator system cd_va = con.connection(cd, 'out1', va, 'in1') self.nw.add_conns(cd_va) # evaporator system va_dr = con.connection(va, 'out1', dr, 'in1') dr_pu = con.connection(dr, 'out1', pu, 'in1') pu_ev = con.connection(pu, 'out1', ev, 'in2') ev_dr = con.connection(ev, 'out2', dr, 'in2') dr_su = con.connection(dr, 'out2', su, 'in2') self.nw.add_conns(va_dr, dr_pu, pu_ev, ev_dr, dr_su) self.amb_in_su = con.connection(amb_in, 'out1', su, 'in1') su_ev = con.connection(su, 'out1', ev, 'in1') ev_amb_out = con.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 = con.connection(su, 'out2', cp1, 'in1') self.nw.add_conns(su_cp1) # compressor-system cp1_he = con.connection(cp1, 'out1', he, 'in1') he_cp2 = con.connection(he, 'out1', cp2, 'in1') cp2_c_out = con.connection(cp2, 'out1', c_out, 'in1') ic_in_he = con.connection(ic_in, 'out1', he, 'in2') he_ic_out = con.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 rp.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) cons.set_attr(pr=1, design=['pr'], offdesign=['zeta']) # evaporator system ev.set_attr(pr1=1, pr2=.999, ttd_l=5, design=['ttd_l'], offdesign=['kA'], kA_char1='EVA_HOT', kA_char2='EVA_COLD') # 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]) su_char = hlp.dc_cc(x=x, y=y, param='m') su.set_attr(kA_char1='default', kA_char2=su_char, offdesign=['zeta1', 'zeta2', 'kA']) pu.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) # compressor system cp1.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) cp2.set_attr(eta_s=0.8, design=['eta_s'], offdesign=['eta_s_char']) # characteristic line for intercooler kA 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 ]) he_char_cold = hlp.dc_cc(x=x, y=y, param='m') he.set_attr(kA_char1='default', kA_char2=he_char_cold, offdesign=['zeta1', 'zeta2', 'kA']) cd.set_attr(pr2=0.998, design=['pr2'], offdesign=['zeta2', 'kA']) # %% connection parametrization # condenser system c_in_cd.set_attr(fluid={'water': 0, 'NH3': 1}, p=60) cb_rp.set_attr(T=60, p=10, fluid={'water': 1, 'NH3': 0}) self.cd_cons.set_attr(T=105) cons_cf.set_attr(h=con.ref(cb_rp, 1, 0), p=con.ref(cb_rp, 1, 0)) cd_va.set_attr(p=con.ref(c_in_cd, 1, -1000), Td_bp=-5, h0=500, design=['Td_bp']) # evaporator system cold side pu_ev.set_attr(m=con.ref(va_dr, 10, 0), p0=5) dr_su.set_attr(p0=5, T=5) su_cp1.set_attr(p=con.ref(dr_su, 1, -5000), Td_bp=5, h0=1700, 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=con.ref(self.amb_in_su, 1, -100), design=['p']) ev_amb_out.set_attr() # compressor-system cp1_he.set_attr(p=15) he_cp2.set_attr(T=40, p=con.ref(cp1_he, 1, -1000), 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=con.ref(ic_in_he, 1, -200), design=['p']) cp2_c_out.set_attr(p=con.ref(c_in_cd, 1, 0), h=con.ref(c_in_cd, 1, 0))
def test_cogeneration_unit(self): """ Test component properties of cogeneration unit. """ amb = cmp.source('ambient') sf = cmp.source('fuel') fg = cmp.sink('flue gas outlet') cw_in1 = cmp.source('cooling water inlet1') cw_in2 = cmp.source('cooling water inlet2') cw_out1 = cmp.sink('cooling water outlet1') cw_out2 = cmp.sink('cooling water outlet2') chp = cmp.cogeneration_unit('cogeneration unit', fuel='CH4') amb_comb = con.connection(amb, 'out1', chp, 'in3') sf_comb = con.connection(sf, 'out1', chp, 'in4') comb_fg = con.connection(chp, 'out3', fg, 'in1') self.nw.add_conns(sf_comb, amb_comb, comb_fg) cw1_chp1 = con.connection(cw_in1, 'out1', chp, 'in1') cw2_chp2 = con.connection(cw_in2, 'out1', chp, 'in2') self.nw.add_conns(cw1_chp1, cw2_chp2) chp1_cw = con.connection(chp, 'out1', cw_out1, 'in1') chp2_cw = con.connection(chp, 'out2', cw_out2, 'in1') self.nw.add_conns(chp1_cw, chp2_cw) air = { 'N2': 0.7556, 'O2': 0.2315, 'Ar': 0.0129, 'INCOMP::DowQ': 0, 'H2O': 0, 'NH3': 0, 'CO2': 0, 'CH4': 0 } fuel = { 'N2': 0, 'O2': 0, 'Ar': 0, 'INCOMP::DowQ': 0, 'H2O': 0, 'NH3': 0, 'CO2': 0.04, 'CH4': 0.96 } water1 = { 'N2': 0, 'O2': 0, 'Ar': 0, 'INCOMP::DowQ': 0, 'H2O': 1, 'NH3': 0, 'CO2': 0, 'CH4': 0 } water2 = { 'N2': 0, 'O2': 0, 'Ar': 0, 'INCOMP::DowQ': 0, 'H2O': 1, 'NH3': 0, 'CO2': 0, 'CH4': 0 } chp.set_attr(fuel='CH4', pr1=0.99, pr2=0.99, lamb=1.2, design=['pr1', 'pr2'], offdesign=['zeta1', 'zeta2']) amb_comb.set_attr(p=5, T=30, fluid=air) sf_comb.set_attr(T=30, fluid=fuel) cw1_chp1.set_attr(p=3, T=60, m=50, fluid=water1) cw2_chp2.set_attr(p=3, T=80, m=50, fluid=water2) TI = con.bus('thermal input') Q1 = con.bus('heat output 1') Q2 = con.bus('heat output 2') Q = con.bus('heat output') Qloss = con.bus('thermal heat loss') TI.add_comps({'c': chp, 'p': 'TI'}) Q1.add_comps({'c': chp, 'p': 'Q1'}) Q2.add_comps({'c': chp, 'p': 'Q2'}) Q.add_comps({'c': chp, 'p': 'Q'}) Qloss.add_comps({'c': chp, 'p': 'Qloss'}) self.nw.add_busses(TI, Q1, Q2, Q, Qloss) ti = 1e6 TI.set_attr(P=ti) # design point self.nw.solve('design') self.nw.save('tmp') self.nw.solve('offdesign', init_path='tmp', design_path='tmp') eq_( round(TI.P.val, 1), round(chp.ti.val, 1), 'Value of thermal input must be ' + str(TI.P.val) + ', is ' + str(chp.ti.val) + '.') # ti via component TI.set_attr(P=np.nan) chp.set_attr(ti=ti) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') eq_( round(ti, 1), round(chp.ti.val, 1), 'Value of thermal input must be ' + str(ti) + ', is ' + str(chp.ti.val) + '.') chp.set_attr(ti=np.nan) # Q1 via bus Q1.set_attr(P=chp.Q1.val) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') eq_( round(ti, 1), round(chp.ti.val, 1), 'Value of thermal input must be ' + str(ti) + ', is ' + str(chp.ti.val) + '.') heat1 = chp1_cw.m.val_SI * (chp1_cw.h.val_SI - cw1_chp1.h.val_SI) eq_( round(heat1, 1), round(chp.Q1.val, 1), 'Value of thermal input must be ' + str(heat1) + ', is ' + str(chp.Q1.val) + '.') Q1.set_attr(P=np.nan) # Q2 via bus Q2.set_attr(P=1.2 * chp.Q2.val) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') # due to characteristic function Q1 is equal to Q2 for this cogeneration unit heat1 = chp1_cw.m.val_SI * (chp1_cw.h.val_SI - cw1_chp1.h.val_SI) eq_( round(heat1, 1), round(chp.Q2.val, 1), 'Value of heat output 2 must be ' + str(heat1) + ', is ' + str(chp.Q2.val) + '.') # Q2 via component Q2.set_attr(P=np.nan) chp.set_attr(Q2=heat1) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') heat1 = chp1_cw.m.val_SI * (chp1_cw.h.val_SI - cw1_chp1.h.val_SI) eq_( round(heat1, 1), round(chp.Q2.val, 1), 'Value of heat output 2 must be ' + str(heat1) + ', is ' + str(chp.Q2.val) + '.') # Q via bus chp.set_attr(Q2=np.nan) Q.set_attr(P=1.5 * chp.Q1.val) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') eq_( round(Q.P.val, 1), round(2 * chp.Q2.val, 1), 'Value of total heat output (' + str(Q.P.val) + ') must be twice as much as value of heat output 2 (' + str(chp.Q2.val) + ').') # Qloss via bus Q.set_attr(P=np.nan) Qloss.set_attr(P=1e5) self.nw.solve('offdesign', init_path='tmp', design_path='tmp') eq_( round(Qloss.P.val, 1), round(chp.Qloss.val, 1), 'Value of heat loss must be ' + str(Qloss.P.val) + ', is ' + str(chp.Qloss.val) + '.') shutil.rmtree('./tmp', ignore_errors=True)
# back h4_pib21 = con.connection(h4.outlet, 'out1', pib21, 'in1') pib21_k4 = con.connection(pib21, 'out1', k4.inlet, 'in3', T=con.ref(h4_pib21, 1, -1 * pib21.L.val / 200), design=['T']) nw.add_conns(k4_pif21, pif21_h4) nw.add_conns(h4_pib21, pib21_k4) nw.add_subsys(h4) # %% busses # heat_losses = con.bus('network losses') heat_consumer = con.bus('network consumer') nw.check_network() for comp in nw.comps.index: if isinstance(comp, cmp.pipe): comp.set_attr(Tamb=0) heat_losses.add_comps({'c': comp}) if (isinstance(comp, cmp.heat_exchanger_simple) and not isinstance(comp, cmp.pipe)): heat_consumer.add_comps({'c': comp}) nw.add_busses(heat_losses, heat_consumer)
# feed water cond = con.connection(condenser, 'out1', pump, 'in1') fw_c = con.connection(pump, 'out1', preheater, 'in2') fw_w = con.connection(preheater, 'out2', steam_generator, 'in1') fs_out = con.connection(steam_generator, 'out1', sink, 'in1') nw.add_conns(cond, fw_c, fw_w, fs_out) # cooling water cw_in = con.connection(source_cw, 'out1', condenser, 'in2') cw_out = con.connection(condenser, 'out2', sink_cw, 'in1') nw.add_conns(cw_in, cw_out) # %% busses # power bus power_bus = con.bus('power') power_bus.add_comps({ 'c': turbine_hp, 'char': -1 }, { 'c': turbine_lp, 'char': -1 }, { 'c': pump, 'char': -1 }) # heating bus heat_bus = con.bus('heat') heat_bus.add_comps({'c': condenser, 'char': -1})
mot1 = cmp_char.characteristics(x=x, y=y) mot2 = cmp_char.characteristics(x=x, y=y) mot3 = cmp_char.characteristics(x=x, y=y) mot4 = cmp_char.characteristics(x=x, y=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.984 gen1 = cmp_char.characteristics(x=x, y=y) gen2 = cmp_char.characteristics(x=x, y=y) power = con.bus('power output') power.add_comps({'c': g_turb, 'char': gen1}, {'c': comp, 'char': 1}, {'c': comp_fuel, 'char': mot1}, {'c': turb_hp, 'char': gen2}, {'c': pump1, 'char': mot2}, {'c': turb_lp, 'char': mot3}, {'c': pump2, 'char': mot4}) gt_power = con.bus('gas turbine power output') gt_power.add_comps({'c': g_turb}, {'c': comp}) heat_out = con.bus('heat output') heat_out.add_comps({'c': cond_dh}) heat_in = con.bus('heat input') heat_in.add_comps({'c': c_c}) nw.add_busses(power, heat_out, heat_in, gt_power)