def test_init(): m, solver = blr.main() # initialize each unit at the time blr.initialize(m) blr.unfix_inlets(m) # check that the model solved properly and has 0 degrees of freedom assert(degrees_of_freedom(m)==0)
def test_boiler(): m, solver = blr.main() # initialize each unit at the time blr.initialize(m) # unfix inlets to build arcs at the flowsheet level blr.unfix_inlets(m) m.fs.ATMP1.outlet.enth_mol[0].fix(62710.01) m.fs.ATMP1.SprayWater.flow_mol[0].unfix() result = solver.solve(m, tee=False) assert result.solver.termination_condition == \ pyo.TerminationCondition.optimal assert result.solver.status == pyo.SolverStatus.ok assert pyo.value(m.fs.ECON.side_1.properties_out[0].temperature) == \ pytest.approx(521.009,1)
def main(): # import steam cycle and build concrete model m, solver = import_steam_cycle() print(degrees_of_freedom(m)) # at this point we have a flowsheet with "steam cycle" that solves # correctly, with 0 degrees of freedom. # next step is to import and build the boiler heat exchanger network # importing the boiler heat exchanger network # from (boiler_subflowsheet_build.py) # this step appends all the boiler unit models into our model ("m") # model "m" has been created a few lines above import idaes.power_generation.flowsheets.\ supercritical_power_plant.boiler_subflowsheet_build as blr # import the models (ECON, WW, PrSH, PlSH, FSH, Spliter, Mixer, Reheater) # see boiler_subflowhseet_build.py for a beter description blr.build_boiler(m.fs) # initialize boiler network models (one by one) blr.initialize(m) # at this point we have both flowsheets (steam cycle + boiler network) # in the same model/concrete object ("m"), however they are disconnected. # Here we want to solve them at the same time # this is a square problem (i.e. degrees of freedom = 0) print('solving square problem disconnected') results = solver.solve(m, tee=True) # at this point we want to connect the units in both flowsheets # Economizer inlet = Feed water heater 8 outlet (water) # HP inlet = Attemperator outlet (steam) # Reheater inlet (steam) = HP split 7 outlet (last stage of HP turbine) # IP inlet = Reheater outlet steam7 blr.unfix_inlets(m) print('unfix inlet conditions, degreeso of freedom = ' + str(degrees_of_freedom(m))) # user can save the initialization to a json file (uncomment next line) # MS.to_json(m, fname = 'SCPC_full.json') # later user can use the json file to initialize the model # if this is the case comment out previous MS.to_json and uncomment next line # MS.from_json(m, fname = 'SCPC_full.json') # deactivate constraints linking the FWH8 to HP turbine m.fs.boiler_pressure_drop.deactivate() m.fs.close_flow.deactivate() m.fs.turb.constraint_reheat_flow.deactivate() m.fs.turb.constraint_reheat_press.deactivate() m.fs.turb.constraint_reheat_temp.deactivate() m.fs.turb.inlet_split.inlet.enth_mol.unfix() m.fs.turb.inlet_split.inlet.pressure.unfix() # user can fix the boiler feed water pump pressure (uncomenting next line) # m.fs.bfp.outlet.pressure[:].fix(26922222.222)) m.fs.FHWtoECON = Arc(source=m.fs.fwh8.desuperheat.outlet_2, destination=m.fs.ECON.side_1_inlet) m.fs.Att2HP = Arc(source=m.fs.ATMP1.outlet, destination=m.fs.turb.inlet_split.inlet) m.fs.HPout2RH = Arc(source=m.fs.turb.hp_split[7].outlet_1, destination=m.fs.RH.side_1_inlet) m.fs.RHtoIP = Arc(source=m.fs.RH.side_1_outlet, destination=m.fs.turb.ip_stages[1].inlet) pyo.TransformationFactory("network.expand_arcs").apply_to(m) # unfix boiler connections m.fs.ECON.side_1_inlet.flow_mol.unfix() m.fs.ECON.side_1_inlet.enth_mol[0].unfix() m.fs.ECON.side_1_inlet.pressure[0].unfix() m.fs.RH.side_1_inlet.flow_mol.unfix() m.fs.RH.side_1_inlet.enth_mol[0].unfix() m.fs.RH.side_1_inlet.pressure[0].unfix() m.fs.hotwell.makeup.flow_mol[:].setlb(-1.0) # if user has trouble with infeasible solutions, an easy test # is to deactivate the link to HP turbine # (m.fs.Att2HP_expanded "enth_mol and pressure" equalities) # and fix inlet pressure and enth_mol to turbine # (m.fs.turb.inlet_split.inlet) # (then double check the values from m.fs.ATMP1.outlet) # m.fs.Att2HP_expanded.enth_mol_equality.deactivate() # m.fs.Att2HP_expanded.pressure_equality.deactivate() m.fs.turb.inlet_split.inlet.pressure.fix(2.423e7) # m.fs.turb.inlet_split.inlet.enth_mol.fix(62710.01) # finally, since we want to maintain High Pressure (HP) inlet temperature # constant (~866 K), we need to fix Attemperator enthalpy outlet # and unfix heat duty to Platen superheater, note that fixing enthalpy # to control temperature is only valid because pressure is also fixed m.fs.ATMP1.outlet.enth_mol[0].fix(62710.01) m.fs.PlSH.heat_duty[:].unfix() # fix(5.5e7) # m.fs.ATMP1.SprayWater.flow_mol[0].unfix() print('connecting flowsheets, degrees of freedom = ' + str(degrees_of_freedom(m))) print('solving full plant model') solver.options = { "tol": 1e-6, "linear_solver": "ma27", "max_iter": 40, } # square problems tend to work better without bounds strip_bounds = pyo.TransformationFactory('contrib.strip_var_bounds') strip_bounds.apply_to(m, reversible=True) # this is the final solve with both flowsheets connected results = solver.solve(m, tee=True) return m, results
def test_init(boiler): # initialize each unit at the time blr.initialize(boiler) blr.unfix_inlets(boiler) # check that the model solved properly and has 0 degrees of freedom assert (degrees_of_freedom(boiler) == 0)
def boiler_flowsheet(): """ Make the flowsheet object, fix some variables, and solve the problem """ import idaes.power_generation.flowsheets.\ supercritical_power_plant.boiler_subflowsheet_build as blr # First, we import the boler model from boiler_subflowsheet_build m, solver = blr.main() # initialize boiler flowsheet blr.initialize(m) # unfix inlet values to each unit and build the flowsheet connectivity blr.unfix_inlets(m) results = solver.solve(m, tee=True) print(results) # adding boiler block # The model solved in line 116 considers heat to platen superheater, # heat to water wall, and flue gas inlet to finishing superheater as fixed # variables. This code uses simplified surrogate models for these inputs. # Therefore, in the new model, these variables will change with plant load, # More details regarding this methodology can be found in Zamarripa et al., # FOCAPD 2 page paper included in this directory as FOCAPD_paper.pdf m.fs.SR = pyo.Var(initialize=1.15, doc='stoichiometric ratio') m.fs.SR.setlb(1.0) m.fs.SR.setub(1.25) m.fs.SR.fix(1.15) m.fs.coal_flow = pyo.Var(initialize=54.01, doc='coal flowrate kg.s') m.fs.FGET = pyo.Var(initialize=1300.00, doc='flue gas exit temperature K') m.fs.FG_flow = pyo.Var(m.fs.prop_fluegas.component_list, initialize=2000, domain=pyo.Reals, doc='component molar flow rate') init_fg = { "H2O": (8.69 / 100), "CO2": (14.49 / 100), "N2": (0.6999), "O2": (2.47 / 100), "NO": 0.0006, "SO2": (0.002) } m.fs.fg_frac = pyo.Var(m.fs.prop_fluegas.component_list, initialize=init_fg, doc='flue gas molar fraction') # surrogate models valid for coal flowrate (30 to 70 kg/s) and SR (1-2.5) def fg_mol_frac_rule(b, i): if i == 'CO2': return m.fs.fg_frac[i] == 0.23235491508307534735955\ * m.fs.SR**-0.5 elif i == 'H2O': return m.fs.fg_frac[i] == 0.45009703293861530459807E-001 \ * m.fs.SR elif i == 'N2': return m.fs.fg_frac[i] == 1 - ( m.fs.fg_frac['CO2'] + m.fs.fg_frac['H2O'] + m.fs.fg_frac['NO'] + m.fs.fg_frac['O2'] + m.fs.fg_frac['SO2']) elif i == 'NO': return m.fs.fg_frac[i] == 0.38148020722713599076938E-005 \ * m.fs.coal_flow elif i == 'O2': return m.fs.fg_frac[i] == 0.60149266916011525155317E-003 \ * m.fs.coal_flow elif i == 'SO2': return m.fs.fg_frac[i] == 0.21991717807021016347323E-004 \ * m.fs.coal_flow m.fs.fg_mol_cn = pyo.Constraint(m.fs.prop_fluegas.component_list, rule=fg_mol_frac_rule) def fg_flow_rule(b, i): mass_flow = 0.87757407893140026988732 * m.fs.coal_flow \ - 0.68240933480416252066014E-001 * m.fs.SR + 8.6386912890637752582\ * m.fs.coal_flow*m.fs.SR - 0.11737247790640543564002E-003 \ * (m.fs.coal_flow/m.fs.SR)**2 # ~ 28.3876e3 kg/s # flow mol component in mol/s = kg/s/kg/mol return m.fs.FSH.side_2.properties_in[0].flow_mol_comp[i] == \ (mass_flow / sum(m.fs.fg_frac[c]*m.fs.prop_fluegas.mw_comp[c] for c in m.fs.prop_fluegas.component_list))\ * m.fs.fg_frac[i] m.fs.fg_flow_cn = pyo.Constraint(m.fs.prop_fluegas.component_list, rule=fg_flow_rule) def fg_temp_rule(b): return m.fs.FSH.side_2.properties_in[0].temperature == \ 59836.381548557488713413 * m.fs.coal_flow**-0.5 \ + 791.74814907302368283126 * m.fs.coal_flow**0.5 \ + 0.60200443235342349090899 * m.fs.coal_flow**1.5 \ + 285.74858049626226375040 * m.fs.SR**3 - 29865.27413896147845662\ * (m.fs.coal_flow*m.fs.SR)**-.5 - 518.58090213915738786454 \ * (m.fs.coal_flow*m.fs.SR)**0.5 - 0.86351166781748790735040E-002 \ * (m.fs.coal_flow*m.fs.SR)**2 - 30141.308801694875000976 \ * (m.fs.SR/m.fs.coal_flow)**0.5 - 18.109911868067683826666 \ * m.fs.coal_flow/m.fs.SR + 1017.0807559525446777116 \ * m.fs.SR/m.fs.coal_flow m.fs.fg_temp_cn = pyo.Constraint(rule=fg_temp_rule) def heat_2_PLSH_rule(b): return m.fs.PlSH.heat_duty[0] == - 42149808.046329699456692 \ * pyo.log(m.fs.coal_flow) + 13125913.817196270450950 \ * pyo.exp(m.fs.SR) - 82168941.403612509369850 \ * m.fs.coal_flow**-.5 - 20751.165176131220505340 \ * (m.fs.coal_flow*m.fs.SR)**1.5 + 35303843.583323523402214 \ * (m.fs.coal_flow/m.fs.SR)**0.5 m.fs.heat_2_PLSH_cn = pyo.Constraint(rule=heat_2_PLSH_rule) def heat_2_ww_rule(b): heat_total = 15383517.068522246554494 * m.fs.coal_flow \ - 145195958.70188459753990 * m.fs.SR**0.5 + 91548063.4268338829278\ * (m.fs.coal_flow*m.fs.SR)**0.5 - 11732787.822234204038978 \ * m.fs.coal_flow*m.fs.SR - 19639.552666366322227987 \ * (m.fs.coal_flow/m.fs.SR)**2 return m.fs.Water_wall.heat_duty[0] == heat_total \ - m.fs.PlSH.heat_duty[0] m.fs.heat_2_ww_cn = pyo.Constraint(rule=heat_2_ww_rule) # unfix variables to build flowsheet connections m.fs.PlSH.heat_duty.unfix() m.fs.Water_wall.heat_duty.unfix() m.fs.FSH.side_2.properties_in[:].flow_mol_comp.unfix() m.fs.FSH.side_2.properties_in[:].temperature.unfix() m.fs.coal_flow.fix(50.15) print('degrees of freedom = ' + str(degrees_of_freedom(m))) # initialize surrogate models (better initial point before solve) calculate_variable_from_constraint(m.fs.PlSH.heat_duty[0], m.fs.heat_2_PLSH_cn) calculate_variable_from_constraint(m.fs.Water_wall.heat_duty[0], m.fs.heat_2_ww_cn) # set scaling parameters iscale.calculate_scaling_factors(m) # final solve results = solver.solve(m, tee=False) return m
# import steam cycle and build concrete model m, solver = import_steam_cycle() print(degrees_of_freedom(m)) #at this point we have a flowsheet with "steam cycle" that solves # correctly, with 0 degrees of freedom. # next step is to import and build the boiler heat exchanger network # importing the boiler heat exchanger network from (boiler_subflowsheet_build.py) # will basically append all the unit models into our model ("m") # model "m" has been created a few lines above import idaes.power_generation.flowsheets.supercritical_power_plant.boiler_subflowsheet_build as blr # import the models (ECON, WW, PrSH, PlSH, FSH, Spliter, Mixer, Reheater) # see boiler_subflowhseet_build.py for a beter description blr.build_boiler(m.fs) #initialize boiler network models (one by one) blr.initialize(m) # at this point we have both flowsheets (steam cycle + boiler network) # in the same model/concrete object ("m") # however they are disconnected. Here we want to solve them at the same time # this is a square problem (i.e. degrees of freedom = 0) print('solving square problem disconnected') results = solver.solve(m, tee=True) # at this point we want to connect the units in both flowsheets # Economizer inlet = Feed water heater 8 outlet (water) # HP inlet = Attemperator outlet (steam) # Reheater inlet (steam) = HP split 7 outlet (last stage of HP turbine) # IP inlet = Reheater outlet steam7 blr.unfix_inlets(m) print('unfix inlet conditions, degreeso of freedom = ' + str(degrees_of_freedom(m)))
def test_power_plan(): # import steam cycle and build concrete model m, solver = steam_cycle.main() print(degrees_of_freedom(m)) #at this point we have a flowsheet with "steam cycle" that solves # correctly, with 0 degrees of freedom. # next step is to import and build the boiler heat exchanger network # importing the boiler heat exchanger network from (boiler_subflowsheet_build.py) # will basically append all the unit models into our model ("m") # model "m" has been created a few lines above # import the models (ECON, WW, PrSH, PlSH, FSH, Spliter, Mixer, Reheater) # see boiler_subflowhseet_build.py for a beter description blr.build_boiler(m.fs) #initialize boiler network models (one by one) blr.initialize(m) # at this point we have both flowsheets (steam cycle + boiler network) # in the same model/concrete object ("m") # however they are disconnected. Here we want to solve them at the same time # this is a square problem (i.e. degrees of freedom = 0) # print('solving square problem disconnected') results = solver.solve(m, tee=True) # at this point we want to connect the units in both flowsheets # Economizer inlet = Feed water heater 8 outlet (water) # HP inlet = Attemperator outlet (steam) # Reheater inlet (steam) = HP split 7 outlet (last stage of HP turbine) # IP inlet = Reheater outlet steam7 blr.unfix_inlets(m) # deactivate constraints linking the FWH8 to HP turbine m.fs.boiler_pressure_drop.deactivate() m.fs.close_flow.deactivate() m.fs.turb.constraint_reheat_flow.deactivate() m.fs.turb.constraint_reheat_press.deactivate() m.fs.turb.constraint_reheat_temp.deactivate() m.fs.turb.inlet_split.inlet.enth_mol.unfix() m.fs.turb.inlet_split.inlet.pressure.unfix() m.fs.FHWtoECON = Arc(source = m.fs.fwh8.desuperheat.outlet_2, destination = m.fs.ECON.side_1_inlet) m.fs.Att2HP = Arc(source = m.fs.ATMP1.outlet, destination = m.fs.turb.inlet_split.inlet) m.fs.HPout2RH = Arc(source = m.fs.turb.hp_split[7].outlet_1, destination = m.fs.RH.side_1_inlet) m.fs.RHtoIP = Arc(source = m.fs.RH.side_1_outlet, destination =m.fs.turb.ip_stages[1].inlet) pyo.TransformationFactory("network.expand_arcs").apply_to(m) #unfix boiler connections m.fs.ECON.side_1_inlet.flow_mol.unfix() m.fs.ECON.side_1_inlet.enth_mol[0].unfix() m.fs.ECON.side_1_inlet.pressure[0].unfix() m.fs.RH.side_1_inlet.flow_mol.unfix() m.fs.RH.side_1_inlet.enth_mol[0].unfix() m.fs.RH.side_1_inlet.pressure[0].unfix() m.fs.hotwell.makeup.flow_mol[:].setlb(-1.0) # if user has trouble with infeasible solutions, an easy test # is to deactivate the link to HP turbine (m.fs.Att2HP_expanded "enth_mol and pressure" equalities) # and fix inlet pressure and enth_mol to turbine (m.fs.turb.inlet_split.inlet) m.fs.turb.inlet_split.inlet.pressure.fix(2.423e7) # finally, since we want to maintain High Pressure (HP) inlet temperature constant (~866 K) # we need to fix Attemperator enthalpy outlet and unfix heat duty to Platen superheater # note fixing enthalpy to control temperature is only valid because pressure is also fixed m.fs.ATMP1.outlet.enth_mol[0].fix(62710.01) m.fs.PlSH.heat_duty[:].unfix() # print(degrees_of_freedom(m)) solver.options = { "tol": 1e-6, "linear_solver": "ma27", "max_iter": 40, } #square problems tend to work better without bounds strip_bounds = pyo.TransformationFactory('contrib.strip_var_bounds') strip_bounds.apply_to(m, reversible=True) # this is the final solve with both flowsheets connected results = solver.solve(m, tee=True) strip_bounds.revert(m)