diff --git a/docs/reference_guides/model_libraries/power_generation/unit_models/cross_flow_heat_exchanger_1D.rst b/docs/reference_guides/model_libraries/power_generation/unit_models/cross_flow_heat_exchanger_1D.rst index d695698545..6408b09652 100644 --- a/docs/reference_guides/model_libraries/power_generation/unit_models/cross_flow_heat_exchanger_1D.rst +++ b/docs/reference_guides/model_libraries/power_generation/unit_models/cross_flow_heat_exchanger_1D.rst @@ -6,8 +6,10 @@ CrossFlowHeatExchanger1D .. module:: idaes.models_extra.power_generation.unit_models.cross_flow_heat_exchanger_1D -This model is for a cross flow heat exchanger between two gases. The gas in the shell has a straight path, -while the gas in the tubes snakes back and forth across the shell's path. +This model is for a cross flow heat exchanger between two gases. The gas in the shell has a straight path, +while the gas in the tubes snakes back and forth across the shell's path. Note that the ``finite_elements`` +option in the shell and tube control volume configs should be set to an integer factor of ``number_passes`` +in order for the discretization equations to make sense as a cross-flow heat exchanger. Example ------- @@ -145,14 +147,14 @@ Example Heat Exchanger Geometry ----------------------- -=========================== =========== ================================================================================= +=========================== =========== =================================================================================================== Variable Index Sets Doc -=========================== =========== ================================================================================= +=========================== =========== =================================================================================================== ``number_columns_per_pass`` None Number of columns of tube per pass ``number_rows_per_pass`` None Number of rows of tube per pass ``number_passes`` None Number of tube banks of ``nrow_tube * ncol_inlet`` tubes -``pitch_x`` None Distance between columns (TODO rows?) of tubes, measured from center-of-tube to center-of-tube -``pitch_y`` None Distance between rows (TODO columns?) of tubes, measured from center-of-tube to center-of-tube +``pitch_x`` None Distance between tubes parallel to shell flow, measured from center-of-tube to center-of-tube +``pitch_y`` None Distance between tubes perpendicular to shell flow, measured from center-of-tube to center-of-tube ``length_tube_seg`` None Length of tube segment perpendicular to flow in each pass ``area_flow_shell`` None Reference to flow area on shell control volume ``length_flow_shell`` None Reference to flow length on shell control volume @@ -161,7 +163,7 @@ Variable Index Sets Doc ``thickness_tube`` None Thickness of tube wall. ``area_flow_tube`` None Reference to flow area on tube control volume ``length_flow_tube`` None Reference to flow length on tube control volume -=========================== =========== ================================================================================= +=========================== =========== =================================================================================================== ============================ =========== =========================================================================== Expression Index Sets Doc @@ -222,7 +224,7 @@ Constraint Index Sets Doc ``conv_heat_transfer_coeff_shell_eqn`` time, length Calculates the shell-side convective heat transfer coefficient ``v_tube_eqn`` time, length Calculates gas velocity in tube ``N_Re_tube_eqn`` time, length Calculates the tube-side Reynolds number -``heat_transfer_coeff_tube_eqn`` time, length Calcualtes the tube-side heat transfer coefficient +``heat_transfer_coeff_tube_eqn`` time, length Calculates the tube-side heat transfer coefficient ``N_Nu_shell_eqn`` time, length Calculate the shell-side Nusselt number ``N_Nu_tube_eqn`` time, length Calculate the tube-side Nusselt number ``heat_tube_eqn`` time, length Calculates heat transfer per unit length @@ -309,7 +311,7 @@ Initialization First, the shell and tube control volumes are initialized without heat transfer. Next the total possible heat transfer between streams is estimated based on heat capacity, flow rate, and inlet/outlet temperatures. The actual temperature change is set to be -half the theoretical maximum, and the shell and tube are initalized with linear +half the theoretical maximum, and the shell and tube are initialized with linear temperature profiles. Finally, temperatures besides the inlets are unfixed and the performance equations are activated before a full solve of the system model. diff --git a/docs/reference_guides/model_libraries/power_generation/unit_models/heater_1D.rst b/docs/reference_guides/model_libraries/power_generation/unit_models/heater_1D.rst index 914459c743..988e6a8a22 100644 --- a/docs/reference_guides/model_libraries/power_generation/unit_models/heater_1D.rst +++ b/docs/reference_guides/model_libraries/power_generation/unit_models/heater_1D.rst @@ -7,7 +7,9 @@ Heater1D .. module:: idaes.models_extra.power_generation.unit_models.heater_1D This model is for a gas trim heater modeled as gas being blown perpendicularly across banks of hollow tubes, -which are heated by resistive heating. +which are heated by resistive heating. Note that the ``finite_elements`` option in the control +volume config should be set to an integer factor of ``number_passes`` in order for the +discretization equations to make sense as a cross-flow heat exchanger. Example ------- @@ -116,21 +118,21 @@ Example Heater Geometry --------------- -=========================== =========== ================================================================================= +=========================== =========== ============================================================================================= Variable Index Sets Doc -=========================== =========== ================================================================================= +=========================== =========== ============================================================================================= ``number_columns_per_pass`` None Number of columns of tube per pass ``number_rows_per_pass`` None Number of rows of tube per pass ``number_passes`` None Number of tube banks of ``nrow_tube * ncol_inlet`` tubes -``pitch_x`` None Distance between columns (TODO rows?) of tubes, measured from center-of-tube to center-of-tube -``pitch_y`` None Distance between rows (TODO columns?) of tubes, measured from center-of-tube to center-of-tube +``pitch_x`` None Distance between tubes parallel to flow, measured from center-of-tube to center-of-tube +``pitch_y`` None Distance between tubes perpendicular to flow, measured from center-of-tube to center-of-tube ``length_tube_seg`` None Length of tube segment perpendicular to flow in each pass ``area_flow_shell`` None Reference to flow area on control volume ``length_flow_shell`` None Reference to flow length on control volume ``area_flow_shell_min`` None Minimum flow area on shell side ``di_tube`` None Inner diameter of tubes ``thickness_tube`` None Thickness of tube wall. -=========================== =========== ================================================================================= +=========================== =========== ============================================================================================= ============================ =========== =========================================================================== Expression Index Sets Doc diff --git a/idaes/models_extra/power_generation/unit_models/cross_flow_heat_exchanger_1D.py b/idaes/models_extra/power_generation/unit_models/cross_flow_heat_exchanger_1D.py index 8f4a9c3e5b..7ee1802c46 100644 --- a/idaes/models_extra/power_generation/unit_models/cross_flow_heat_exchanger_1D.py +++ b/idaes/models_extra/power_generation/unit_models/cross_flow_heat_exchanger_1D.py @@ -50,6 +50,7 @@ __author__ = "Jinliang Ma, Douglas Allan" + class CrossFlowHeatExchanger1DInitializer(SingleControlVolumeUnitInitializer): """ Initializer for Cross Flow Heat Exchanger 1D units. @@ -100,7 +101,9 @@ def initialize_main_model( ) hot_units = model.config.hot_side.property_package.get_metadata().derived_units - cold_units = model.config.cold_side.property_package.get_metadata().derived_units + cold_units = ( + model.config.cold_side.property_package.get_metadata().derived_units + ) if model.config.shell_is_hot: shell = model.hot_side @@ -274,7 +277,9 @@ def initialize_main_model( / 2 ) - model.temp_wall_shell[t, z].set_value(model.temp_wall_center[t, z].value) + model.temp_wall_shell[t, z].set_value( + model.temp_wall_center[t, z].value + ) model.temp_wall_tube[t, z].set_value( pyunits.convert_value( model.temp_wall_center[t, z].value, @@ -354,6 +359,7 @@ def initialize_main_model( init_log.info("Initialization Complete.") return res + @declare_process_block_class("CrossFlowHeatExchanger1D") class CrossFlowHeatExchanger1DData(HeatExchanger1DData): """Standard Cross Flow Heat Exchanger Unit Model Class.""" @@ -455,7 +461,9 @@ def _make_geometry(self): add_object_reference(self, "length_flow_shell", shell.length) add_object_reference(self, "length_flow_tube", tube.length) - heat_exchanger_common.make_geometry_common(self, shell=shell, shell_units=shell_units) + heat_exchanger_common.make_geometry_common( + self, shell=shell, shell_units=shell_units + ) heat_exchanger_common.make_geometry_tube(self, shell_units=shell_units) def _make_performance(self): @@ -764,10 +772,14 @@ def cst(con, sf): ssf(self.temp_wall_shell[t, z], sf_T_shell) cst(self.temp_wall_shell_eqn[t, z], sf_T_shell) - sf_conv_heat_transfer_coeff_shell = gsf(self.conv_heat_transfer_coeff_shell[t, z]) + sf_conv_heat_transfer_coeff_shell = gsf( + self.conv_heat_transfer_coeff_shell[t, z] + ) s_Q_shell = sgsf( shell.heat[t, z], - sf_conv_heat_transfer_coeff_shell * sf_area_per_length_shell * sf_T_shell, + sf_conv_heat_transfer_coeff_shell + * sf_area_per_length_shell + * sf_T_shell, ) cst( self.heat_shell_eqn[t, z], s_Q_shell * value(self.length_flow_shell) diff --git a/idaes/models_extra/power_generation/unit_models/heat_exchanger_common.py b/idaes/models_extra/power_generation/unit_models/heat_exchanger_common.py index 67ca7ad9fe..dc3665c636 100644 --- a/idaes/models_extra/power_generation/unit_models/heat_exchanger_common.py +++ b/idaes/models_extra/power_generation/unit_models/heat_exchanger_common.py @@ -137,7 +137,10 @@ def length_flow_shell_eqn(b): def area_flow_shell_eqn(b): return ( b.length_flow_shell * b.area_flow_shell - == b.length_tube_seg * b.length_flow_shell * b.pitch_y * b.number_columns_per_pass + == b.length_tube_seg + * b.length_flow_shell + * b.pitch_y + * b.number_columns_per_pass - b.number_columns_per_pass * b.nrow_tube * 0.25 @@ -169,7 +172,11 @@ def length_flow_tube_eqn(b): def area_flow_tube_eqn(b): return ( b.area_flow_tube - == 0.25 * const.pi * b.di_tube**2.0 * b.number_columns_per_pass * b.number_rows_per_pass + == 0.25 + * const.pi + * b.di_tube**2.0 + * b.number_columns_per_pass + * b.number_rows_per_pass ) @@ -702,9 +709,13 @@ def cst(con, sf): cst(blk.N_Nu_shell_eqn[t, z], sf_N_Nu_shell) sf_conv_heat_transfer_coeff_shell = sgsf( - blk.conv_heat_transfer_coeff_shell[t, z], sf_N_Nu_shell * sf_k_shell / sf_do_tube + blk.conv_heat_transfer_coeff_shell[t, z], + sf_N_Nu_shell * sf_k_shell / sf_do_tube, + ) + cst( + blk.conv_heat_transfer_coeff_shell_eqn[t, z], + sf_conv_heat_transfer_coeff_shell * sf_do_tube, ) - cst(blk.conv_heat_transfer_coeff_shell_eqn[t, z], sf_conv_heat_transfer_coeff_shell * sf_do_tube) # FIXME estimate from parameters if blk.config.has_holdup: @@ -751,6 +762,10 @@ def cst(con, sf): cst(blk.N_Nu_tube_eqn[t, z], sf_N_Nu_tube) sf_heat_transfer_coeff_tube = sgsf( - blk.heat_transfer_coeff_tube[t, z], sf_N_Nu_tube * sf_k_tube / sf_di_tube + blk.heat_transfer_coeff_tube[t, z], + sf_N_Nu_tube * sf_k_tube / sf_di_tube, + ) + cst( + blk.heat_transfer_coeff_tube_eqn[t, z], + sf_heat_transfer_coeff_tube * sf_di_tube, ) - cst(blk.heat_transfer_coeff_tube_eqn[t, z], sf_heat_transfer_coeff_tube * sf_di_tube) diff --git a/idaes/models_extra/power_generation/unit_models/heater_1D.py b/idaes/models_extra/power_generation/unit_models/heater_1D.py index 61923e2610..04f2ad4b43 100644 --- a/idaes/models_extra/power_generation/unit_models/heater_1D.py +++ b/idaes/models_extra/power_generation/unit_models/heater_1D.py @@ -53,10 +53,12 @@ __author__ = "Jinliang Ma, Douglas Allan" + class Heater1DInitializer(SingleControlVolumeUnitInitializer): """ Initializer for Heater 1D units. """ + def initialize_main_model( self, model: Block, @@ -144,6 +146,7 @@ def initialize_main_model( return res + @declare_process_block_class("Heater1D") class Heater1DData(UnitModelBlockData): """Standard Trim Heater Model Class Class.""" @@ -510,7 +513,10 @@ def cst(con, sf): for t in self.flowsheet().time: for z in self.control_volume.length_domain: sf_hconv_conv = gsf(self.conv_heat_transfer_coeff_shell[t, z]) - cst(self.conv_heat_transfer_coeff_shell_eqn[t, z], sf_hconv_conv * sf_d_tube) + cst( + self.conv_heat_transfer_coeff_shell_eqn[t, z], + sf_hconv_conv * sf_d_tube, + ) sf_T = gsf(self.control_volume.properties[t, z].temperature) ssf(self.temp_wall_shell[t, z], sf_T) diff --git a/idaes/models_extra/power_generation/unit_models/tests/test_cross_flow_heat_exchanger_1D.py b/idaes/models_extra/power_generation/unit_models/tests/test_cross_flow_heat_exchanger_1D.py index edb0b2b357..b9b89139b2 100644 --- a/idaes/models_extra/power_generation/unit_models/tests/test_cross_flow_heat_exchanger_1D.py +++ b/idaes/models_extra/power_generation/unit_models/tests/test_cross_flow_heat_exchanger_1D.py @@ -25,7 +25,7 @@ ) from idaes.models_extra.power_generation.unit_models import ( CrossFlowHeatExchanger1D, - CrossFlowHeatExchanger1DInitializer + CrossFlowHeatExchanger1DInitializer, ) import idaes.core.util.model_statistics as mstat from idaes.core.util.model_statistics import degrees_of_freedom @@ -215,8 +215,7 @@ def test_initialization(model_no_dP): _check_model_statistics(m, deltaP=False) initializer = m.fs.heat_exchanger.default_initializer( - solver="ipopt", - solver_options=optarg + solver="ipopt", solver_options=optarg ) assert isinstance(initializer, CrossFlowHeatExchanger1DInitializer) initializer.initialize(model=m.fs.heat_exchanger) @@ -250,8 +249,7 @@ def test_initialization_dP(model_dP): _check_model_statistics(m, deltaP=True) initializer = m.fs.heat_exchanger.default_initializer( - solver="ipopt", - solver_options=optarg + solver="ipopt", solver_options=optarg ) assert isinstance(initializer, CrossFlowHeatExchanger1DInitializer) initializer.initialize(m.fs.heat_exchanger) diff --git a/idaes/models_extra/power_generation/unit_models/tests/test_heater_1D.py b/idaes/models_extra/power_generation/unit_models/tests/test_heater_1D.py index 0052381b4c..d0b70849ae 100644 --- a/idaes/models_extra/power_generation/unit_models/tests/test_heater_1D.py +++ b/idaes/models_extra/power_generation/unit_models/tests/test_heater_1D.py @@ -22,7 +22,10 @@ get_prop, EosType, ) -from idaes.models_extra.power_generation.unit_models import Heater1D, Heater1DInitializer +from idaes.models_extra.power_generation.unit_models import ( + Heater1D, + Heater1DInitializer, +) import idaes.core.util.model_statistics as mstat from idaes.core.util.model_statistics import degrees_of_freedom from idaes.core.solvers import get_solver @@ -178,10 +181,7 @@ def test_initialization(model_no_dP): assert degrees_of_freedom(m) == 0 _check_model_statistics(m, deltaP=False) - initializer = m.fs.heater.default_initializer( - solver="ipopt", - solver_options=optarg - ) + initializer = m.fs.heater.default_initializer(solver="ipopt", solver_options=optarg) assert isinstance(initializer, Heater1DInitializer) initializer.initialize(model=m.fs.heater) @@ -210,10 +210,7 @@ def test_initialization_dP(model_dP): assert degrees_of_freedom(m) == 0 _check_model_statistics(m, deltaP=True) - initializer = m.fs.heater.default_initializer( - solver="ipopt", - solver_options=optarg - ) + initializer = m.fs.heater.default_initializer(solver="ipopt", solver_options=optarg) assert isinstance(initializer, Heater1DInitializer) initializer.initialize(model=m.fs.heater)