def iapws_underwood(self):
        m = ConcreteModel()
        m.fs = FlowsheetBlock(default={"dynamic": False})

        m.fs.properties = iapws95.Iapws95ParameterBlock()

        m.fs.unit = HeatExchanger(
            default={
                "shell": {
                    "property_package": m.fs.properties
                },
                "tube": {
                    "property_package": m.fs.properties
                },
                "delta_temperature_callback":
                delta_temperature_underwood_callback,
                "flow_pattern": HeatExchangerFlowPattern.countercurrent
            })

        m.fs.unit.inlet_1.flow_mol[0].fix(100)
        m.fs.unit.inlet_1.enth_mol[0].fix(4000)
        m.fs.unit.inlet_1.pressure[0].fix(101325)

        m.fs.unit.inlet_2.flow_mol[0].fix(100)
        m.fs.unit.inlet_2.enth_mol[0].fix(3500)
        m.fs.unit.inlet_2.pressure[0].fix(101325)

        m.fs.unit.area.fix(1000)
        m.fs.unit.overall_heat_transfer_coefficient.fix(100)

        return m
Пример #2
0
    def btx(self):
        m = ConcreteModel()
        m.fs = FlowsheetBlock(default={"dynamic": False})

        m.fs.properties = BTXParameterBlock(default={"valid_phase": 'Liq'})

        m.fs.unit = HeatExchanger(default={
                "hot_side_name":"hot",
                "cold_side_name":"cold",
                "hot": {"property_package": m.fs.properties},
                "cold": {"property_package": m.fs.properties},
                "flow_pattern": HeatExchangerFlowPattern.cocurrent})

        m.fs.unit.hot_inlet.flow_mol[0].fix(5)  # mol/s
        m.fs.unit.hot_inlet.temperature[0].fix(365)  # K
        m.fs.unit.hot_inlet.pressure[0].fix(101325)  # Pa
        m.fs.unit.hot_inlet.mole_frac_comp[0, "benzene"].fix(0.5)
        m.fs.unit.hot_inlet.mole_frac_comp[0, "toluene"].fix(0.5)

        m.fs.unit.cold_inlet.flow_mol[0].fix(1)  # mol/s
        m.fs.unit.cold_inlet.temperature[0].fix(300)  # K
        m.fs.unit.cold_inlet.pressure[0].fix(101325)  # Pa
        m.fs.unit.cold_inlet.mole_frac_comp[0, "benzene"].fix(0.5)
        m.fs.unit.cold_inlet.mole_frac_comp[0, "toluene"].fix(0.5)

        m.fs.unit.area.fix(1)
        m.fs.unit.overall_heat_transfer_coefficient.fix(100)

        return m
Пример #3
0
def basic_model(cb=delta_temperature_lmtd_callback):
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})

    m.fs.properties = iapws95.Iapws95ParameterBlock()

    m.fs.unit = HeatExchanger(default={
                "shell": {"property_package": m.fs.properties},
                "tube": {"property_package": m.fs.properties},
                "delta_temperature_callback": cb,
                "flow_pattern": HeatExchangerFlowPattern.countercurrent})
    #   Set inputs
    m.fs.unit.inlet_1.flow_mol[0].fix(100)
    m.fs.unit.inlet_1.enth_mol[0].fix(4000)
    m.fs.unit.inlet_1.pressure[0].fix(101325)

    m.fs.unit.inlet_2.flow_mol[0].fix(100)
    m.fs.unit.inlet_2.enth_mol[0].fix(3500)
    m.fs.unit.inlet_2.pressure[0].fix(101325)

    m.fs.unit.area.fix(1000)
    m.fs.unit.overall_heat_transfer_coefficient.fix(100)

    assert degrees_of_freedom(m) == 0
    m.fs.unit.get_costing()
    m.fs.unit.initialize()
    return m
Пример #4
0
    def sapon(self):
        m = ConcreteModel()
        m.fs = FlowsheetBlock(default={"dynamic": False})

        m.fs.properties = SaponificationParameterBlock()

        m.fs.unit = HeatExchanger(default={
                "shell": {"property_package": m.fs.properties},
                "tube": {"property_package": m.fs.properties},
                "flow_pattern": HeatExchangerFlowPattern.crossflow})

        m.fs.unit.inlet_1.flow_vol[0].fix(1e-3)
        m.fs.unit.inlet_1.temperature[0].fix(320)
        m.fs.unit.inlet_1.pressure[0].fix(101325)
        m.fs.unit.inlet_1.conc_mol_comp[0, "H2O"].fix(55388.0)
        m.fs.unit.inlet_1.conc_mol_comp[0, "NaOH"].fix(100.0)
        m.fs.unit.inlet_1.conc_mol_comp[0, "EthylAcetate"].fix(100.0)
        m.fs.unit.inlet_1.conc_mol_comp[0, "SodiumAcetate"].fix(0.0)
        m.fs.unit.inlet_1.conc_mol_comp[0, "Ethanol"].fix(0.0)

        m.fs.unit.inlet_2.flow_vol[0].fix(1e-3)
        m.fs.unit.inlet_2.temperature[0].fix(300)
        m.fs.unit.inlet_2.pressure[0].fix(101325)
        m.fs.unit.inlet_2.conc_mol_comp[0, "H2O"].fix(55388.0)
        m.fs.unit.inlet_2.conc_mol_comp[0, "NaOH"].fix(100.0)
        m.fs.unit.inlet_2.conc_mol_comp[0, "EthylAcetate"].fix(100.0)
        m.fs.unit.inlet_2.conc_mol_comp[0, "SodiumAcetate"].fix(0.0)
        m.fs.unit.inlet_2.conc_mol_comp[0, "Ethanol"].fix(0.0)

        m.fs.unit.area.fix(1000)
        m.fs.unit.overall_heat_transfer_coefficient.fix(100)
        m.fs.unit.crossflow_factor.fix(0.6)

        return m
def test_discharge():
    m = ConcreteModel()

    m.fs = FlowsheetBlock(default={"dynamic": False})

    m.fs.steam_prop = Iapws95ParameterBlock()
    m.fs.therminol66_prop = ThermalOilParameterBlock()

    m.fs.discharge_hx = HeatExchanger(
        default={
            "hot_side_name": "tube",
            "cold_side_name": "shell",
            "tube": {
                "property_package": m.fs.therminol66_prop
            },
            "shell": {
                "property_package": m.fs.steam_prop
            },
            "flow_pattern": HeatExchangerFlowPattern.countercurrent
        })

    # Set inputs
    # Steam
    m.fs.discharge_hx.inlet_2.flow_mol[0].fix(4163)
    m.fs.discharge_hx.inlet_2.pressure[0].fix(1.379e+6)
    m.fs.discharge_hx.inlet_2.enth_mol[0].fix(
        htpx(T=300.15 * units.K, P=1.379e+6 * units.Pa))

    # Thermal Oil
    m.fs.discharge_hx.inlet_1.flow_mass[0].fix(833.3)
    m.fs.discharge_hx.inlet_1.temperature[0].fix(256 + 273.15)
    m.fs.discharge_hx.inlet_1.pressure[0].fix(101325)

    # Designate the Area
    m.fs.discharge_hx.area.fix(12180)
    m.fs.discharge_hx.overall_heat_transfer_coefficient.fix(432.677)

    # Designate the U Value.
    m.fs.discharge_hx.initialize()
    m.fs.discharge_hx.heat_duty.fix(1.066e+08)
    m.fs.discharge_hx.area.unfix()

    solver = get_solver()
    results = solver.solve(m, tee=False)

    # Check for optimal solution
    assert results.solver.termination_condition == \
        TerminationCondition.optimal
    assert results.solver.status == SolverStatus.ok

    # Tests to make sure the discharge cycle is functioning properly.
    assert value(m.fs.discharge_hx.outlet_1.temperature[0]) == \
        pytest.approx(473.5, rel=1e-1)
    assert value(m.fs.discharge_hx.outlet_2.enth_mol[0]) == \
        pytest.approx(27668.5, rel=1e-1)
Пример #6
0
def test_charge():
    m = ConcreteModel()

    m.fs = FlowsheetBlock(default={"dynamic": False})

    m.fs.steam_prop = Iapws95ParameterBlock()
    m.fs.therminol66_prop = ThermalOilParameterBlock()

    m.fs.charge_hx = HeatExchanger(
        default={
            "shell": {
                "property_package": m.fs.steam_prop
            },
            "tube": {
                "property_package": m.fs.therminol66_prop
            },
            "flow_pattern": HeatExchangerFlowPattern.countercurrent
        })

    # Set inputs
    # Steam
    m.fs.charge_hx.inlet_1.flow_mol[0].fix(4163)
    m.fs.charge_hx.inlet_1.enth_mol[0].fix(
        htpx(T=573.15 * units.K, P=5.0e+6 * units.Pa))
    m.fs.charge_hx.inlet_1.pressure[0].fix(5.0e+6)

    # Thermal Oil
    m.fs.charge_hx.inlet_2.flow_mass[0].fix(833.3)
    m.fs.charge_hx.inlet_2.temperature[0].fix(200 + 273.15)
    m.fs.charge_hx.inlet_2.pressure[0].fix(101325)

    m.fs.charge_hx.area.fix(12180)
    m.fs.charge_hx.overall_heat_transfer_coefficient.fix(432.677)

    m.fs.charge_hx.initialize()
    m.fs.charge_hx.heat_duty.fix(1.066e+08)

    # Needed to make the system solve.
    m.fs.charge_hx.overall_heat_transfer_coefficient.unfix()

    solver = get_solver()
    results = solver.solve(m)

    # Check for optimal solution
    assert results.solver.termination_condition == \
        TerminationCondition.optimal
    assert results.solver.status == SolverStatus.ok

    # Testing the exit values of the heat exchanger.
    assert value(m.fs.charge_hx.outlet_2.temperature[0]) == \
        pytest.approx(528.83, rel=1e-1)
    assert value(m.fs.charge_hx.outlet_1.enth_mol[0]) == \
        pytest.approx(27100.28, rel=1e-1)
def test_config():
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})

    m.fs.properties = PhysicalParameterTestBlock()

    m.fs.unit = HeatExchanger(
        default={
            "shell": {
                "property_package": m.fs.properties
            },
            "tube": {
                "property_package": m.fs.properties
            }
        })

    # 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) >= 8 and len(m.fs.unit.config) <= 10

    assert not m.fs.unit.config.dynamic
    assert not m.fs.unit.config.has_holdup
    assert isinstance(m.fs.unit.config.shell, ConfigBlock)
    assert isinstance(m.fs.unit.config.tube, ConfigBlock)
    assert m.fs.unit.config.delta_temperature_callback is \
        delta_temperature_lmtd_callback
    assert m.fs.unit.config.flow_pattern == \
        HeatExchangerFlowPattern.countercurrent

    # Check shell config
    assert len(m.fs.unit.config.shell) == 7
    assert m.fs.unit.config.shell.material_balance_type == \
        MaterialBalanceType.useDefault
    assert m.fs.unit.config.shell.energy_balance_type == \
        EnergyBalanceType.useDefault
    assert m.fs.unit.config.shell.momentum_balance_type == \
        MomentumBalanceType.pressureTotal
    assert not m.fs.unit.config.shell.has_phase_equilibrium
    assert not m.fs.unit.config.shell.has_pressure_change
    assert m.fs.unit.config.shell.property_package is m.fs.properties

    # Check tube config
    assert len(m.fs.unit.config.tube) == 7
    assert m.fs.unit.config.tube.material_balance_type == \
        MaterialBalanceType.useDefault
    assert m.fs.unit.config.tube.energy_balance_type == \
        EnergyBalanceType.useDefault
    assert m.fs.unit.config.tube.momentum_balance_type == \
        MomentumBalanceType.pressureTotal
    assert not m.fs.unit.config.tube.has_phase_equilibrium
    assert not m.fs.unit.config.tube.has_pressure_change
    assert m.fs.unit.config.tube.property_package is m.fs.properties
Пример #8
0
def test_costing():
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})

    m.fs.properties = iapws95.Iapws95ParameterBlock()

    m.fs.unit = HeatExchanger(
        default={
            "shell": {
                "property_package": m.fs.properties
            },
            "tube": {
                "property_package": m.fs.properties
            },
            "flow_pattern": HeatExchangerFlowPattern.countercurrent
        })
    #   Set inputs
    m.fs.unit.inlet_1.flow_mol[0].fix(100)
    m.fs.unit.inlet_1.enth_mol[0].fix(4000)
    m.fs.unit.inlet_1.pressure[0].fix(101325)

    m.fs.unit.inlet_2.flow_mol[0].fix(100)
    m.fs.unit.inlet_2.enth_mol[0].fix(3500)
    m.fs.unit.inlet_2.pressure[0].fix(101325)

    m.fs.unit.area.fix(1000)
    m.fs.unit.overall_heat_transfer_coefficient.fix(100)

    assert degrees_of_freedom(m) == 0

    m.fs.unit.initialize()

    m.fs.unit.get_costing()
    calculate_variable_from_constraint(m.fs.unit.costing.base_cost,
                                       m.fs.unit.costing.base_cost_eq)

    calculate_variable_from_constraint(m.fs.unit.costing.purchase_cost,
                                       m.fs.unit.costing.cp_cost_eq)

    assert_units_consistent(m.fs.unit.costing)

    results = solver.solve(m)

    # Check for optimal solution
    assert results.solver.termination_condition == TerminationCondition.optimal
    assert results.solver.status == SolverStatus.ok

    assert m.fs.unit.costing.purchase_cost.value == \
        pytest.approx(529738.6793, 1e-5)
Пример #9
0
    def btx(self):
        m = ConcreteModel()
        m.fs = FlowsheetBlock(default={"dynamic": False})

        m.fs.properties = BTXParameterBlock(default={"valid_phase": 'Liq'})

        m.fs.unit = HeatExchanger(
            default={
                "shell": {
                    "property_package": m.fs.properties
                },
                "tube": {
                    "property_package": m.fs.properties
                },
                "flow_pattern": HeatExchangerFlowPattern.cocurrent
            })

        return m
Пример #10
0
    def sapon(self):
        m = ConcreteModel()
        m.fs = FlowsheetBlock(default={"dynamic": False})

        m.fs.properties = SaponificationParameterBlock()

        m.fs.unit = HeatExchanger(
            default={
                "shell": {
                    "property_package": m.fs.properties
                },
                "tube": {
                    "property_package": m.fs.properties
                },
                "flow_pattern": HeatExchangerFlowPattern.crossflow
            })

        return m
Пример #11
0
    def iapws(self):
        m = ConcreteModel()
        m.fs = FlowsheetBlock(default={"dynamic": False})

        m.fs.properties = iapws95.Iapws95ParameterBlock()

        m.fs.unit = HeatExchanger(
            default={
                "shell": {
                    "property_package": m.fs.properties
                },
                "tube": {
                    "property_package": m.fs.properties
                },
                "flow_pattern": HeatExchangerFlowPattern.countercurrent
            })

        return m
def test_costing_book():
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})
    m.fs.properties = iapws95.Iapws95ParameterBlock()
    m.fs.unit = HeatExchanger(
        default={
            "shell": {
                "property_package": m.fs.properties
            },
            "tube": {
                "property_package": m.fs.properties
            },
            "flow_pattern": HeatExchangerFlowPattern.countercurrent
        })
    #   Set inputs
    m.fs.unit.inlet_1.flow_mol[0].fix(100)
    m.fs.unit.inlet_1.enth_mol[0].fix(4000)
    m.fs.unit.inlet_1.pressure[0].fix(101325)

    m.fs.unit.inlet_2.flow_mol[0].fix(100)
    m.fs.unit.inlet_2.enth_mol[0].fix(3500)
    m.fs.unit.inlet_2.pressure[0].fix(101325)

    m.fs.unit.area.fix(1000)
    m.fs.unit.overall_heat_transfer_coefficient.fix(100)
    # costing
    m.fs.unit.get_costing(hx_type='floating_head',
                          length_factor='20ft',
                          year='2018')
    m.fs.unit.area.fix(669.738)  # m2
    m.fs.unit.costing.pressure_factor.fix(1.19)
    m.fs.unit.costing.material_factor.fix(4.05)
    m.fs.costing.CE_index = 550
    m.fs.unit.costing.hx_os = 1.0
    calculate_variable_from_constraint(m.fs.unit.costing.base_cost_per_unit,
                                       m.fs.unit.costing.base_cost_per_unit_eq)

    calculate_variable_from_constraint(m.fs.unit.costing.purchase_cost,
                                       m.fs.unit.costing.cp_cost_eq)

    assert value(m.fs.unit.costing.base_cost) == \
        pytest.approx(78802.0518, 1e-5)
    assert m.fs.unit.costing.purchase_cost.value == \
        pytest.approx(417765.1377, 1e-5)
Пример #13
0
def test_same_name():
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})
    with pytest.raises(NameError):
        m.fs.unit = HeatExchanger(default={"cold_side_name": "shell"})
Пример #14
0
    HeatExchangerFlowPattern)
from idaes.core.util.model_statistics import degrees_of_freedom
# Import steam property package
from idaes.generic_models.properties.iapws95 import htpx, Iapws95ParameterBlock
from thermal_oil import ThermalOilParameterBlock

m = ConcreteModel()

m.fs = FlowsheetBlock(default={"dynamic": False})

m.fs.steam_prop = Iapws95ParameterBlock()
m.fs.therminol66_prop = ThermalOilParameterBlock()

m.fs.discharge_hx = HeatExchanger(
    default={"hot_side_name": "tube", "cold_side_name": "shell",
             "tube": {"property_package": m.fs.therminol66_prop},
             "shell": {"property_package": m.fs.steam_prop},
             "flow_pattern": HeatExchangerFlowPattern.countercurrent})

# Set inputs
#Steam
m.fs.discharge_hx.inlet_2.flow_mol[0].fix(4163)
m.fs.discharge_hx.inlet_2.pressure[0].fix(1.379e+6)
m.fs.discharge_hx.inlet_2.enth_mol[0].fix(htpx(T=300.15*units.K,
                                               P=1.379e+6*units.Pa))


#Thermal Oil
m.fs.discharge_hx.inlet_1.flow_mass[0].fix(833.3)
m.fs.discharge_hx.inlet_1.temperature[0].fix(256 + 273.15)
m.fs.discharge_hx.inlet_1.pressure[0].fix(101325)
Пример #15
0
    HeatExchangerFlowPattern)

# Import steam property package
from idaes.generic_models.properties.iapws95 import htpx, Iapws95ParameterBlock
from thermal_oil import ThermalOilParameterBlock
from idaes.core.util.model_statistics import degrees_of_freedom

m = ConcreteModel()

m.fs = FlowsheetBlock(default={"dynamic": False})

m.fs.steam_prop = Iapws95ParameterBlock()
m.fs.therminol66_prop = ThermalOilParameterBlock()

m.fs.charge_hx = HeatExchanger(
    default={"shell": {"property_package": m.fs.steam_prop},
             "tube": {"property_package": m.fs.therminol66_prop},
             "flow_pattern": HeatExchangerFlowPattern.countercurrent})

# Set inputs
#Steam
m.fs.charge_hx.inlet_1.flow_mol[0].fix(4163)
m.fs.charge_hx.inlet_1.enth_mol[0].fix(htpx(T=573.15*units.K,
                                            P=5.0e+6*units.Pa))
m.fs.charge_hx.inlet_1.pressure[0].fix(5.0e+6)

#Thermal Oil
m.fs.charge_hx.inlet_2.flow_mass[0].fix(833.3)
m.fs.charge_hx.inlet_2.temperature[0].fix(200 + 273.15)
m.fs.charge_hx.inlet_2.pressure[0].fix(101325)

m.fs.charge_hx.area.fix(12180)
Пример #16
0
def test_bad_option():
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})
    with pytest.raises(KeyError):
        m.fs.unit = HeatExchanger(default={"I'm a bad option": "hot"})
def test_heat_exchanger():
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})
    m.fs.properties = props.SeawaterParameterBlock()
    m.fs.unit = HeatExchanger(
        default={
            "hot_side_name": "hot",
            "cold_side_name": "cold",
            "hot": {
                "property_package": m.fs.properties
            },
            "cold": {
                "property_package": m.fs.properties
            },
            "delta_temperature_callback": delta_temperature_chen_callback,
            "flow_pattern": HeatExchangerFlowPattern.countercurrent,
        })

    # scaling
    m.fs.properties.set_default_scaling("flow_mass_phase_comp",
                                        1,
                                        index=("Liq", "H2O"))
    m.fs.properties.set_default_scaling("flow_mass_phase_comp",
                                        1e2,
                                        index=("Liq", "TDS"))
    iscale.set_scaling_factor(m.fs.unit.hot.heat, 1e-3)
    iscale.set_scaling_factor(m.fs.unit.cold.heat, 1e-3)
    iscale.set_scaling_factor(m.fs.unit.overall_heat_transfer_coefficient,
                              1e-3)
    iscale.set_scaling_factor(m.fs.unit.area, 1)
    iscale.calculate_scaling_factors(m)

    # ---specifications---
    # state variables
    m.fs.unit.hot_inlet.flow_mass_phase_comp[0, "Liq", "H2O"].fix(1)
    m.fs.unit.hot_inlet.flow_mass_phase_comp[0, "Liq", "TDS"].fix(0.01)
    m.fs.unit.hot_inlet.temperature[0].fix(350)
    m.fs.unit.hot_inlet.pressure[0].fix(2e5)

    m.fs.unit.cold_inlet.flow_mass_phase_comp[0, "Liq", "H2O"].fix(0.5)
    m.fs.unit.cold_inlet.flow_mass_phase_comp[0, "Liq", "TDS"].fix(0.01)
    m.fs.unit.cold_inlet.temperature[0].fix(298)
    m.fs.unit.cold_inlet.pressure[0].fix(2e5)

    m.fs.unit.area.fix(5)
    m.fs.unit.overall_heat_transfer_coefficient.fix(1000)

    # solving
    assert_units_consistent(m)
    degrees_of_freedom(m)

    m.fs.unit.initialize()

    solver = get_solver()
    results = solver.solve(m, tee=False)
    assert_optimal_termination(results)

    report_io = StringIO()
    m.fs.unit.report(ostream=report_io)
    output = """
====================================================================================
Unit : fs.unit                                                             Time: 0.0
------------------------------------------------------------------------------------
    Unit Performance

    Variables: 

    Key            : Value  : Fixed : Bounds
           HX Area : 5.0000 :  True : (0, None)
    HX Coefficient : 1000.0 :  True : (0, None)
         Heat Duty : 89050. : False : (None, None)

    Expressions: 

    Key             : Value
    Delta T Driving : 17.810
         Delta T In : 9.2239
        Delta T Out : 30.689

------------------------------------------------------------------------------------
    Stream Table
                                         Hot Inlet  Hot Outlet  Cold Inlet  Cold Outlet
    flow_mass_phase_comp ('Liq', 'H2O')     1.0000      1.0000     0.50000     0.50000 
    flow_mass_phase_comp ('Liq', 'TDS')   0.010000    0.010000    0.010000    0.010000 
    temperature                             350.00      328.69      298.00      340.78 
    pressure                            2.0000e+05  2.0000e+05  2.0000e+05  2.0000e+05 
====================================================================================
"""
    assert output == report_io.getvalue()
Пример #18
0
    def btx(self):
        m = ConcreteModel()
        m.fs = FlowsheetBlock(default={"dynamic": False})

        # As we lack other example prop packs with units, take the generic
        # BT-PR package and change the base units
        configuration2 = {
            # Specifying components
            "components": {
                'benzene': {
                    "type": Component,
                    "enth_mol_ig_comp": RPP,
                    "entr_mol_ig_comp": RPP,
                    "pressure_sat_comp": RPP,
                    "phase_equilibrium_form": {("Vap", "Liq"): log_fugacity},
                    "parameter_data": {
                        "mw": (78.1136E-3, pyunits.kg/pyunits.mol),  # [1]
                        "pressure_crit": (48.9e5, pyunits.Pa),  # [1]
                        "temperature_crit": (562.2, pyunits.K),  # [1]
                        "omega": 0.212,  # [1]
                        "cp_mol_ig_comp_coeff": {
                            'A': (-3.392E1, pyunits.J/pyunits.mol/pyunits.K),  # [1]
                            'B': (4.739E-1, pyunits.J/pyunits.mol/pyunits.K**2),
                            'C': (-3.017E-4, pyunits.J/pyunits.mol/pyunits.K**3),
                            'D': (7.130E-8, pyunits.J/pyunits.mol/pyunits.K**4)},
                        "enth_mol_form_vap_comp_ref": (
                            82.9e3, pyunits.J/pyunits.mol),  # [3]
                        "entr_mol_form_vap_comp_ref": (
                            -269, pyunits.J/pyunits.mol/pyunits.K),  # [3]
                        "pressure_sat_comp_coeff": {'A': (-6.98273, None),  # [1]
                                                    'B': (1.33213, None),
                                                    'C': (-2.62863, None),
                                                    'D': (-3.33399, None)}}},
                'toluene': {
                    "type": Component,
                    "enth_mol_ig_comp": RPP,
                    "entr_mol_ig_comp": RPP,
                    "pressure_sat_comp": RPP,
                    "phase_equilibrium_form": {("Vap", "Liq"): log_fugacity},
                    "parameter_data": {
                        "mw": (92.1405E-3, pyunits.kg/pyunits.mol),  # [1]
                        "pressure_crit": (41e5, pyunits.Pa),  # [1]
                        "temperature_crit": (591.8, pyunits.K),  # [1]
                        "omega": 0.263,  # [1]
                        "cp_mol_ig_comp_coeff": {
                            'A': (-2.435E1, pyunits.J/pyunits.mol/pyunits.K),  # [1]
                            'B': (5.125E-1, pyunits.J/pyunits.mol/pyunits.K**2),
                            'C': (-2.765E-4, pyunits.J/pyunits.mol/pyunits.K**3),
                            'D': (4.911E-8, pyunits.J/pyunits.mol/pyunits.K**4)},
                        "enth_mol_form_vap_comp_ref": (
                            50.1e3, pyunits.J/pyunits.mol),  # [3]
                        "entr_mol_form_vap_comp_ref": (
                            -321, pyunits.J/pyunits.mol/pyunits.K),  # [3]
                        "pressure_sat_comp_coeff": {'A': (-7.28607, None),  # [1]
                                                    'B': (1.38091, None),
                                                    'C': (-2.83433, None),
                                                    'D': (-2.79168, None)}}}},
            # Specifying phases
            "phases":  {'Liq': {"type": LiquidPhase,
                                "equation_of_state": Cubic,
                                "equation_of_state_options": {
                                    "type": CubicType.PR}},
                        'Vap': {"type": VaporPhase,
                                "equation_of_state": Cubic,
                                "equation_of_state_options": {
                                    "type": CubicType.PR}}},
            # Set base units of measurement
            "base_units": {"time": pyunits.s,
                           "length": pyunits.m,
                           "mass": pyunits.t,
                           "amount": pyunits.mol,
                           "temperature": pyunits.degR},
            # Specifying state definition
            "state_definition": FTPx,
            "state_bounds": {"flow_mol": (0, 100, 1000, pyunits.mol/pyunits.s),
                             "temperature": (273.15, 300, 500, pyunits.K),
                             "pressure": (5e4, 1e5, 1e6, pyunits.Pa)},
            "pressure_ref": (101325, pyunits.Pa),
            "temperature_ref": (298.15, pyunits.K),
            # Defining phase equilibria
            "phases_in_equilibrium": [("Vap", "Liq")],
            "phase_equilibrium_state": {("Vap", "Liq"): SmoothVLE},
            "bubble_dew_method": LogBubbleDew,
            "parameter_data": {"PR_kappa": {("benzene", "benzene"): 0.000,
                                            ("benzene", "toluene"): 0.000,
                                            ("toluene", "benzene"): 0.000,
                                            ("toluene", "toluene"): 0.000}}}

        m.fs.properties = GenericParameterBlock(default=configuration)
        m.fs.properties2 = GenericParameterBlock(default=configuration2)

        m.fs.unit = HeatExchanger(default={
                "shell": {"property_package": m.fs.properties},
                "tube": {"property_package": m.fs.properties2},
                "flow_pattern": HeatExchangerFlowPattern.cocurrent})

        m.fs.unit.inlet_1.flow_mol[0].fix(5)  # mol/s
        m.fs.unit.inlet_1.temperature[0].fix(365)  # K
        m.fs.unit.inlet_1.pressure[0].fix(101325)  # Pa
        m.fs.unit.inlet_1.mole_frac_comp[0, "benzene"].fix(0.5)
        m.fs.unit.inlet_1.mole_frac_comp[0, "toluene"].fix(0.5)

        m.fs.unit.inlet_2.flow_mol[0].fix(1)  # mol/s
        m.fs.unit.inlet_2.temperature[0].fix(540)  # degR
        m.fs.unit.inlet_2.pressure[0].fix(101.325)  # kPa
        m.fs.unit.inlet_2.mole_frac_comp[0, "benzene"].fix(0.5)
        m.fs.unit.inlet_2.mole_frac_comp[0, "toluene"].fix(0.5)

        m.fs.unit.area.fix(1)
        m.fs.unit.overall_heat_transfer_coefficient.fix(100)

        m.fs.unit.side_2.scaling_factor_pressure = 1

        return m
def test_heat_exchanger():
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})
    m.fs.properties = props.SeawaterParameterBlock()
    m.fs.unit = HeatExchanger(
        default={
            "hot_side_name": "hot",
            "cold_side_name": "cold",
            "hot": {
                "property_package": m.fs.properties
            },
            "cold": {
                "property_package": m.fs.properties
            },
            "delta_temperature_callback": delta_temperature_chen_callback,
            "flow_pattern": HeatExchangerFlowPattern.countercurrent,
        })

    # scaling
    m.fs.properties.set_default_scaling("flow_mass_phase_comp",
                                        1,
                                        index=("Liq", "H2O"))
    m.fs.properties.set_default_scaling("flow_mass_phase_comp",
                                        1e2,
                                        index=("Liq", "TDS"))
    iscale.set_scaling_factor(m.fs.unit.hot.heat, 1e-3)
    iscale.set_scaling_factor(m.fs.unit.cold.heat, 1e-3)
    iscale.set_scaling_factor(m.fs.unit.overall_heat_transfer_coefficient,
                              1e-3)
    iscale.set_scaling_factor(m.fs.unit.area, 1)
    iscale.calculate_scaling_factors(m)

    # ---specifications---
    # state variables
    m.fs.unit.hot_inlet.flow_mass_phase_comp[0, "Liq", "H2O"].fix(1)
    m.fs.unit.hot_inlet.flow_mass_phase_comp[0, "Liq", "TDS"].fix(0.01)
    m.fs.unit.hot_inlet.temperature[0].fix(350)
    m.fs.unit.hot_inlet.pressure[0].fix(2e5)

    m.fs.unit.cold_inlet.flow_mass_phase_comp[0, "Liq", "H2O"].fix(0.5)
    m.fs.unit.cold_inlet.flow_mass_phase_comp[0, "Liq", "TDS"].fix(0.01)
    m.fs.unit.cold_inlet.temperature[0].fix(298)
    m.fs.unit.cold_inlet.pressure[0].fix(2e5)

    m.fs.unit.area.fix(5)
    m.fs.unit.overall_heat_transfer_coefficient.fix(1000)

    # solving
    assert_units_consistent(m)
    degrees_of_freedom(m)

    m.fs.unit.initialize()

    solver = get_solver()
    results = solver.solve(m, tee=False)
    assert_optimal_termination(results)

    assert pytest.approx(89050.0, rel=1e-4) == value(m.fs.unit.heat_duty[0])
    assert pytest.approx(1.0, rel=1e-4) == value(
        m.fs.unit.hot_outlet.flow_mass_phase_comp[0, "Liq", "H2O"])
    assert pytest.approx(0.01, rel=1e-4) == value(
        m.fs.unit.hot_outlet.flow_mass_phase_comp[0, "Liq", "TDS"])
    assert pytest.approx(328.69, rel=1e-4) == value(
        m.fs.unit.hot_outlet.temperature[0])
    assert pytest.approx(2.0e5,
                         rel=1e-4) == value(m.fs.unit.hot_outlet.pressure[0])
    assert pytest.approx(0.5, rel=1e-4) == value(
        m.fs.unit.cold_outlet.flow_mass_phase_comp[0, "Liq", "H2O"])
    assert pytest.approx(0.01, rel=1e-4) == value(
        m.fs.unit.cold_outlet.flow_mass_phase_comp[0, "Liq", "TDS"])
    assert pytest.approx(340.78, rel=1e-4) == value(
        m.fs.unit.cold_outlet.temperature[0])
    assert pytest.approx(2.0e5,
                         rel=1e-4) == value(m.fs.unit.cold_outlet.pressure[0])

    m.fs.unit.report()