def test_config(): m = ConcreteModel() m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.properties = PhysicalParameterTestBlock() m.fs.prop_steam = iapws95.Iapws95ParameterBlock() m.fs.prop_fluegas = FlueGasParameterBlock() m.fs.unit = BoilerHeatExchanger( default={ "side_1_property_package": m.fs.prop_steam, "side_2_property_package": m.fs.prop_fluegas, "has_pressure_change": True, "has_holdup": False, "delta_T_method": DeltaTMethod.counterCurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Liq", "has_radiation": True }) # Check unit config arguments # There are 8 to 10 arguments since you can add a side 1 and 2 config by # side_1, side_2, or whatever the user named them assert len(m.fs.unit.config) >= 12 and len(m.fs.unit.config) <= 16 assert not m.fs.unit.config.dynamic assert not m.fs.unit.config.has_holdup assert m.fs.unit.config.delta_T_method == \ DeltaTMethod.counterCurrent
def test_deprecated_delta_T_method(caplog): m = ConcreteModel() m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.prop_steam = iapws95.Iapws95ParameterBlock() m.fs.prop_fluegas = FlueGasParameterBlock() caplog.clear() m.fs.unit = BoilerHeatExchanger( default={ "delta_temperature_callback": delta_temperature_lmtd_callback, "tube": { "property_package": m.fs.prop_steam }, "shell": { "property_package": m.fs.prop_fluegas }, "has_pressure_change": True, "has_holdup": True, "delta_T_method": HeatExchangerFlowPattern.countercurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Liq", "has_radiation": True, }) n_warn = 0 n_depreacted = 0 for record in caplog.records: if record.levelno == idaeslog.WARNING: n_warn += 1 if "deprecated" in record.msg: n_depreacted += 1 assert n_warn == 1 assert n_depreacted == 1 assert m.fs.unit.config.flow_pattern == HeatExchangerFlowPattern.countercurrent
def tu(delta_temperature_callback=delta_temperature_underwood_tune_callback): m = ConcreteModel() m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.properties = PhysicalParameterTestBlock() m.fs.prop_steam = iapws95.Iapws95ParameterBlock() m.fs.prop_fluegas = FlueGasParameterBlock() m.fs.unit = BoilerHeatExchanger( default={ "delta_temperature_callback": delta_temperature_callback, "tube": { "property_package": m.fs.prop_steam }, "shell": { "property_package": m.fs.prop_fluegas }, "has_pressure_change": True, "has_holdup": False, "flow_pattern": HeatExchangerFlowPattern.countercurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Liq", "has_radiation": True, }) assert_units_consistent(m)
def test_units(): m = ConcreteModel() m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.properties = PhysicalParameterTestBlock() m.fs.prop_steam = iapws95.Iapws95ParameterBlock() m.fs.prop_fluegas = FlueGasParameterBlock() m.fs.unit = BoilerHeatExchanger(default={ "side_1_property_package": m.fs.prop_steam, "side_2_property_package": m.fs.prop_fluegas, "has_pressure_change": True, "has_holdup": False, "delta_T_method": DeltaTMethod.counterCurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Liq", "has_radiation": True}) assert_units_consistent(m)
def build_boiler(fs): # Add property packages to flowsheet library fs.prop_fluegas = FlueGasParameterBlock() # Create unit models # Boiler Economizer fs.ECON = BoilerHeatExchanger( default={ "side_1_property_package": fs.prop_water, "side_2_property_package": fs.prop_fluegas, "has_pressure_change": True, "has_holdup": False, "delta_T_method": DeltaTMethod.counterCurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Liq", "has_radiation": False }) # Primary Superheater fs.PrSH = BoilerHeatExchanger( default={ "side_1_property_package": fs.prop_water, "side_2_property_package": fs.prop_fluegas, "has_pressure_change": True, "has_holdup": False, "delta_T_method": DeltaTMethod.counterCurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Vap", "has_radiation": True }) # Finishing Superheater fs.FSH = BoilerHeatExchanger( default={ "side_1_property_package": fs.prop_water, "side_2_property_package": fs.prop_fluegas, "has_pressure_change": True, "has_holdup": False, "delta_T_method": DeltaTMethod.counterCurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Vap", "has_radiation": True }) # Reheater fs.RH = BoilerHeatExchanger( default={ "side_1_property_package": fs.prop_water, "side_2_property_package": fs.prop_fluegas, "has_pressure_change": True, "has_holdup": False, "delta_T_method": DeltaTMethod.counterCurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Vap", "has_radiation": True }) # Platen Superheater fs.PlSH = Heater(default={"property_package": fs.prop_water}) # Boiler Water Wall fs.Water_wall = Heater(default={"property_package": fs.prop_water}) # Boiler Splitter (splits FSH flue gas outlet to Reheater and PrSH) fs.Spl1 = Separator( default={ "property_package": fs.prop_fluegas, "split_basis": SplittingType.totalFlow, "energy_split_basis": EnergySplittingType.equal_temperature }) # Flue gas mixer (mixing FG from Reheater and Primary SH, inlet to ECON) fs.mix1 = Mixer( default={ "property_package": fs.prop_fluegas, "inlet_list": ['Reheat_out', 'PrSH_out'], "dynamic": False }) # Mixer for Attemperator #1 (between PrSH and PlSH) fs.ATMP1 = Mixer( default={ "property_package": fs.prop_water, "inlet_list": ['Steam', 'SprayWater'], "dynamic": False }) # Build connections (streams) # Steam Route (side 1 = tube side = steam/water side) # Boiler feed water to Economizer (to be imported in full plant model) # fs.bfw2econ = Arc(source=fs.FWH8.outlet, # destination=fs.ECON.side_1_inlet) fs.econ2ww = Arc(source=fs.ECON.side_1_outlet, destination=fs.Water_wall.inlet) fs.ww2prsh = Arc(source=fs.Water_wall.outlet, destination=fs.PrSH.side_1_inlet) fs.prsh2plsh = Arc(source=fs.PrSH.side_1_outlet, destination=fs.PlSH.inlet) fs.plsh2fsh = Arc(source=fs.PlSH.outlet, destination=fs.FSH.side_1_inlet) fs.FSHtoATMP1 = Arc(source=fs.FSH.side_1_outlet, destination=fs.ATMP1.Steam) # fs.fsh2hpturbine=Arc(source=fs.ATMP1.outlet, # destination=fs.HPTinlet) # (to be imported in full plant model) # Flue gas route --------------------------------------------------------- # water wall connected with boiler block (to fix the heat duty) # platen SH connected with boiler block (to fix the heat duty) # Finishing superheater connected with a flowsheet level constraint fs.fg_fsh2_separator = Arc(source=fs.FSH.side_2_outlet, destination=fs.Spl1.inlet) fs.fg_fsh2rh = Arc(source=fs.Spl1.outlet_1, destination=fs.RH.side_2_inlet) fs.fg_fsh2PrSH = Arc(source=fs.Spl1.outlet_2, destination=fs.PrSH.side_2_inlet) fs.fg_rhtomix = Arc(source=fs.RH.side_2_outlet, destination=fs.mix1.Reheat_out) fs.fg_prsh2mix = Arc(source=fs.PrSH.side_2_outlet, destination=fs.mix1.PrSH_out) fs.fg_mix2econ = Arc(source=fs.mix1.outlet, destination=fs.ECON.side_2_inlet)
def test_boiler_hx(): m = ConcreteModel() m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.properties = PhysicalParameterTestBlock() m.fs.prop_steam = iapws95.Iapws95ParameterBlock() m.fs.prop_fluegas = FlueGasParameterBlock() m.fs.unit = BoilerHeatExchanger( default={ "side_1_property_package": m.fs.prop_steam, "side_2_property_package": m.fs.prop_fluegas, "has_pressure_change": True, "has_holdup": False, "delta_T_method": DeltaTMethod.counterCurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Liq", "has_radiation": True }) # Set inputs h = iapws95.htpx(773.15, 2.5449e7) print(h) m.fs.unit.side_1_inlet.flow_mol[0].fix(24678.26) # mol/s m.fs.unit.side_1_inlet.enth_mol[0].fix(h) # J/mol m.fs.unit.side_1_inlet.pressure[0].fix(2.5449e7) # Pascals # FLUE GAS Inlet from Primary Superheater FGrate = 28.3876e3 * 0.18 # mol/s equivalent of ~1930.08 klb/hr # Use FG molar composition to set component flow rates (baseline report) m.fs.unit.side_2_inlet.flow_mol_comp[0, "H2O"].fix(FGrate * 8.69 / 100) m.fs.unit.side_2_inlet.flow_mol_comp[0, "CO2"].fix(FGrate * 14.49 / 100) m.fs.unit.side_2_inlet.flow_mol_comp[0, "N2"].fix(FGrate * 74.34 / 100) m.fs.unit.side_2_inlet.flow_mol_comp[0, "O2"].fix(FGrate * 2.47 / 100) m.fs.unit.side_2_inlet.flow_mol_comp[0, "NO"].fix(FGrate * 0.0006) m.fs.unit.side_2_inlet.flow_mol_comp[0, "SO2"].fix(FGrate * 0.002) m.fs.unit.side_2_inlet.temperature[0].fix(1102.335) m.fs.unit.side_2_inlet.pressure[0].fix(100145) # Primary Superheater ITM = 0.0254 # inch to meter conversion m.fs.unit.tube_di.fix((2.5 - 2 * 0.165) * ITM) m.fs.unit.tube_thickness.fix(0.165 * ITM) m.fs.unit.pitch_x.fix(3 * ITM) # gas path transverse width 54.78 ft / number of columns m.fs.unit.pitch_y.fix(54.78 / 108 * 12 * ITM) m.fs.unit.tube_length.fix(53.13 * 12 * ITM) m.fs.unit.tube_nrow.fix(20 * 2) m.fs.unit.tube_ncol.fix(108) m.fs.unit.nrow_inlet.fix(4) m.fs.unit.delta_elevation.fix(50) m.fs.unit.tube_r_fouling = 0.000176 # (0.001 h-ft^2-F/BTU) m.fs.unit.tube_r_fouling = 0.003131 # (0.03131 - 0.1779 h-ft^2-F/BTU) if m.fs.unit.config.has_radiation is True: m.fs.unit.emissivity_wall.fix(0.7) # wall emissivity # correction factor for overall heat transfer coefficient m.fs.unit.fcorrection_htc.fix(1.5) # correction factor for pressure drop calc tube side m.fs.unit.fcorrection_dp_tube.fix(1.0) # correction factor for pressure drop calc shell side m.fs.unit.fcorrection_dp_shell.fix(1.0) assert degrees_of_freedom(m) == 0 m.fs.unit.initialize() # Create a solver solver = SolverFactory('ipopt') results = solver.solve(m) # Check for optimal solution assert results.solver.termination_condition == \ TerminationCondition.optimal assert results.solver.status == SolverStatus.ok assert value(m.fs.unit.side_1.properties_out[0].temperature) == \ pytest.approx(588.07, 1) assert value(m.fs.unit.side_2.properties_out[0].temperature) == \ pytest.approx(573.07, 1)
def th( delta_temperature_callback=delta_temperature_underwood_tune_callback, tout_1=809.55, tout_2=788.53, ): m = ConcreteModel() m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.properties = PhysicalParameterTestBlock() m.fs.prop_steam = iapws95.Iapws95ParameterBlock() m.fs.prop_fluegas = FlueGasParameterBlock() m.fs.unit = BoilerHeatExchanger( default={ "delta_temperature_callback": delta_temperature_callback, "tube": { "property_package": m.fs.prop_steam }, "shell": { "property_package": m.fs.prop_fluegas }, "has_pressure_change": True, "has_holdup": False, "flow_pattern": HeatExchangerFlowPattern.countercurrent, "tube_arrangement": TubeArrangement.inLine, "side_1_water_phase": "Liq", "has_radiation": True, }) # Set inputs h = value(iapws95.htpx(773.15 * pyunits.K, 2.5449e7 * pyunits.Pa)) m.fs.unit.side_1_inlet.flow_mol[0].fix(24678.26) # mol/s m.fs.unit.side_1_inlet.enth_mol[0].fix(h) # J/mol m.fs.unit.side_1_inlet.pressure[0].fix(2.5449e7) # Pascals # FLUE GAS Inlet from Primary Superheater FGrate = 28.3876e3 * 0.18 # mol/s equivalent of ~1930.08 klb/hr # Use FG molar composition to set component flow rates (baseline report) m.fs.unit.side_2_inlet.flow_mol_comp[0, "H2O"].fix(FGrate * 8.69 / 100) m.fs.unit.side_2_inlet.flow_mol_comp[0, "CO2"].fix(FGrate * 14.49 / 100) m.fs.unit.side_2_inlet.flow_mol_comp[0, "N2"].fix(FGrate * 74.34 / 100) m.fs.unit.side_2_inlet.flow_mol_comp[0, "O2"].fix(FGrate * 2.47 / 100) m.fs.unit.side_2_inlet.flow_mol_comp[0, "NO"].fix(FGrate * 0.0006) m.fs.unit.side_2_inlet.flow_mol_comp[0, "SO2"].fix(FGrate * 0.002) m.fs.unit.side_2_inlet.temperature[0].fix(1102.335) m.fs.unit.side_2_inlet.pressure[0].fix(100145) # Primary Superheater ITM = 0.0254 # inch to meter conversion m.fs.unit.tube_di.fix((2.5 - 2 * 0.165) * ITM) m.fs.unit.tube_thickness.fix(0.165 * ITM) m.fs.unit.pitch_x.fix(3 * ITM) # gas path transverse width 54.78 ft / number of columns m.fs.unit.pitch_y.fix(54.78 / 108 * 12 * ITM) m.fs.unit.tube_length.fix(53.13 * 12 * ITM) m.fs.unit.tube_nrow.fix(20 * 2) m.fs.unit.tube_ncol.fix(108) m.fs.unit.nrow_inlet.fix(4) m.fs.unit.delta_elevation.fix(50) m.fs.unit.tube_r_fouling = 0.000176 # (0.001 h-ft^2-F/BTU) m.fs.unit.tube_r_fouling = 0.003131 # (0.03131 - 0.1779 h-ft^2-F/BTU) if m.fs.unit.config.has_radiation is True: m.fs.unit.emissivity_wall.fix(0.7) # wall emissivity # correction factor for overall heat transfer coefficient m.fs.unit.fcorrection_htc.fix(1.5) # correction factor for pressure drop calc tube side m.fs.unit.fcorrection_dp_tube.fix(1.0) # correction factor for pressure drop calc shell side m.fs.unit.fcorrection_dp_shell.fix(1.0) assert degrees_of_freedom(m) == 0 iscale.calculate_scaling_factors(m) m.fs.unit.initialize() results = solver.solve(m) # Check for optimal solution assert check_optimal_termination(results) assert value( m.fs.unit.side_1.properties_out[0].temperature) == pytest.approx( tout_1, abs=0.5) assert value( m.fs.unit.side_2.properties_out[0].temperature) == pytest.approx( tout_2, abs=0.5)