def test_initialize(self, Crystallizer_frame):
        # Add costing function, then initialize
        m = Crystallizer_frame
        m.fs.costing = WaterTAPCosting()
        m.fs.unit.costing = UnitModelCostingBlock(default={
            "flowsheet_costing_block":
            m.fs.costing,
            "costing_method_arguments": {
                "cost_type": CrystallizerCostType.mass_basis
            },
        }, )
        m.fs.costing.cost_process()

        initialization_tester(Crystallizer_frame)
Beispiel #2
0
def build(number_of_stages=2):
    # ---building model---
    m = ConcreteModel()

    m.fs = FlowsheetBlock(default={"dynamic": False})
    m.fs.properties = props.NaClParameterBlock()
    m.fs.costing = WaterTAPCosting()

    m.fs.NumberOfStages = Param(initialize=number_of_stages)
    m.fs.StageSet = RangeSet(m.fs.NumberOfStages)
    m.fs.LSRRO_StageSet = RangeSet(2, m.fs.NumberOfStages)
    m.fs.NonFinal_StageSet = RangeSet(m.fs.NumberOfStages-1)

    m.fs.feed = Feed(default={'property_package': m.fs.properties})
    m.fs.product = Product(default={'property_package': m.fs.properties})
    m.fs.disposal = Product(default={'property_package': m.fs.properties})

    # Add the mixers
    m.fs.Mixers = Mixer(m.fs.NonFinal_StageSet, default={
            "property_package": m.fs.properties,
            "momentum_mixing_type": MomentumMixingType.equality,  # booster pump will match pressure
            "inlet_list": ['upstream', 'downstream']})

    total_pump_work = 0
    # Add the pumps
    m.fs.PrimaryPumps = Pump(m.fs.StageSet, default={"property_package": m.fs.properties})
    for pump in m.fs.PrimaryPumps.values():
        pump.costing = UnitModelCostingBlock(default={
                "flowsheet_costing_block":m.fs.costing})
        m.fs.costing.cost_flow(pyunits.convert(pump.work_mechanical[0], to_units=pyunits.kW), "electricity")

    # Add the equalizer pumps
    m.fs.BoosterPumps = Pump(m.fs.LSRRO_StageSet, default={"property_package": m.fs.properties})
    for pump in m.fs.BoosterPumps.values():
        pump.costing = UnitModelCostingBlock(default={
                "flowsheet_costing_block":m.fs.costing})
        m.fs.costing.cost_flow(pyunits.convert(pump.work_mechanical[0], to_units=pyunits.kW), "electricity")

    # Add the stages ROs
    m.fs.ROUnits = ReverseOsmosis0D(m.fs.StageSet, default={
            "property_package": m.fs.properties,
            "has_pressure_change": True,
            "pressure_change_type": PressureChangeType.calculated,
            "mass_transfer_coefficient": MassTransferCoefficient.calculated,
            "concentration_polarization_type": ConcentrationPolarizationType.calculated})
    for ro_unit in m.fs.ROUnits.values():
        ro_unit.costing = UnitModelCostingBlock(default={
                "flowsheet_costing_block":m.fs.costing})

    # Add EnergyRecoveryDevice
    m.fs.EnergyRecoveryDevice = Pump(default={"property_package": m.fs.properties})
    m.fs.EnergyRecoveryDevice.costing = UnitModelCostingBlock(default={
            "flowsheet_costing_block":m.fs.costing,
            "costing_method_arguments":{"pump_type":PumpType.energy_recovery_device}})
    m.fs.costing.cost_flow(pyunits.convert(m.fs.EnergyRecoveryDevice.work_mechanical[0], to_units=pyunits.kW), "electricity")

    # additional variables or expressions
    # system water recovery
    m.fs.water_recovery = Var(
            initialize=0.5,
            bounds=(0, 1),
            domain=NonNegativeReals,
            units=pyunits.dimensionless,
            doc='System Water Recovery')
    m.fs.eq_water_recovery = Constraint(expr=\
              sum(m.fs.feed.flow_mass_phase_comp[0,'Liq',:]) * m.fs.water_recovery == \
              sum(m.fs.product.flow_mass_phase_comp[0,'Liq',:]) )

    # costing
    m.fs.costing.cost_process()
    product_flow_vol_total = m.fs.product.properties[0].flow_vol
    m.fs.costing.add_LCOW(product_flow_vol_total)
    m.fs.costing.add_specific_energy_consumption(product_flow_vol_total)

    # objective
    m.fs.objective = Objective(expr=m.fs.costing.LCOW)

    # connections

    # Connect the feed to the first pump
    m.fs.feed_to_pump = Arc(source=m.fs.feed.outlet, destination=m.fs.PrimaryPumps[1].inlet)

    # Connect the primary RO permeate to the product
    m.fs.primary_RO_to_product = Arc(source=m.fs.ROUnits[1].permeate, destination=m.fs.product.inlet)

    # Connect the Pump n to the Mixer n
    m.fs.pump_to_mixer = Arc(m.fs.NonFinal_StageSet,
            rule=lambda fs,n : {'source':fs.PrimaryPumps[n].outlet,
                                'destination':fs.Mixers[n].upstream})

    # Connect the Mixer n to the Stage n
    m.fs.mixer_to_stage = Arc(m.fs.NonFinal_StageSet,
            rule=lambda fs,n : {'source':fs.Mixers[n].outlet,
                                'destination':fs.ROUnits[n].inlet})

    # Connect the Stage n to the Pump n+1
    m.fs.stage_to_pump = Arc(m.fs.NonFinal_StageSet,
            rule=lambda fs,n : {'source':fs.ROUnits[n].retentate,
                                'destination':fs.PrimaryPumps[n+1].inlet})

    # Connect the Stage n to the Eq Pump n
    m.fs.stage_to_eq_pump = Arc(m.fs.LSRRO_StageSet,
            rule=lambda fs,n : {'source':fs.ROUnits[n].permeate,
                                'destination':fs.BoosterPumps[n].inlet})

    # Connect the Eq Pump n to the Mixer n-1
    m.fs.eq_pump_to_mixer = Arc(m.fs.LSRRO_StageSet,
            rule=lambda fs,n : {'source':fs.BoosterPumps[n].outlet,
                                'destination':fs.Mixers[n-1].downstream})

    # Connect the Pump N to the Stage N
    last_stage = m.fs.StageSet.last()
    m.fs.pump_to_stage = Arc(source=m.fs.PrimaryPumps[last_stage].outlet,
            destination=m.fs.ROUnits[last_stage].inlet)

    # Connect Final Stage to EnergyRecoveryDevice Pump
    m.fs.stage_to_erd = Arc(source=m.fs.ROUnits[last_stage].retentate,
            destination=m.fs.EnergyRecoveryDevice.inlet)

    # Connect the EnergyRecoveryDevice to the disposal
    m.fs.erd_to_disposal = Arc(source=m.fs.EnergyRecoveryDevice.outlet,
            destination=m.fs.disposal.inlet)

    # additional bounding
    for b in m.component_data_objects(Block, descend_into=True):
        # NaCl solubility limit
        if hasattr(b, 'mass_frac_phase_comp'):
            b.mass_frac_phase_comp['Liq', 'NaCl'].setub(0.26)

    TransformationFactory("network.expand_arcs").apply_to(m)

    return m
def build():
    # flowsheet set up
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={'dynamic': False})
    m.fs.properties = props.NaClParameterBlock()
    m.fs.costing = WaterTAPCosting()

    # unit models
    m.fs.feed = Feed(default={'property_package': m.fs.properties})
    m.fs.S1 = Separator(default={
        "property_package": m.fs.properties,
        "outlet_list": ['P1', 'PXR']
    })
    m.fs.P1 = Pump(default={'property_package': m.fs.properties})
    m.fs.PXR = PressureExchanger(default={'property_package': m.fs.properties})
    m.fs.P2 = Pump(default={'property_package': m.fs.properties})
    m.fs.M1 = Mixer(
        default={
            "property_package": m.fs.properties,
            "momentum_mixing_type":
            MomentumMixingType.equality,  # booster pump will match pressure
            "inlet_list": ['P1', 'P2']
        })
    m.fs.RO = ReverseOsmosis0D(
        default={
            "property_package":
            m.fs.properties,
            "has_pressure_change":
            True,
            "pressure_change_type":
            PressureChangeType.calculated,
            "mass_transfer_coefficient":
            MassTransferCoefficient.calculated,
            "concentration_polarization_type":
            ConcentrationPolarizationType.calculated,
        })
    m.fs.product = Product(default={'property_package': m.fs.properties})
    m.fs.disposal = Product(default={'property_package': m.fs.properties})

    # costing
    m.fs.costing.cost_flow(
        pyunits.convert(m.fs.P1.work_mechanical[0], to_units=pyunits.kW),
        "electricity")
    m.fs.costing.cost_flow(
        pyunits.convert(m.fs.P2.work_mechanical[0], to_units=pyunits.kW),
        "electricity")
    m.fs.P1.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.costing})
    m.fs.P2.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.costing})

    m.fs.RO.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.costing})
    m.fs.PXR.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.costing})
    m.fs.costing.cost_process()
    m.fs.costing.add_LCOW(m.fs.product.properties[0].flow_vol)
    m.fs.costing.add_specific_energy_consumption(
        m.fs.product.properties[0].flow_vol)

    # connections
    m.fs.s01 = Arc(source=m.fs.feed.outlet, destination=m.fs.S1.inlet)
    m.fs.s02 = Arc(source=m.fs.S1.P1, destination=m.fs.P1.inlet)
    m.fs.s03 = Arc(source=m.fs.P1.outlet, destination=m.fs.M1.P1)
    m.fs.s04 = Arc(source=m.fs.M1.outlet, destination=m.fs.RO.inlet)
    m.fs.s05 = Arc(source=m.fs.RO.permeate, destination=m.fs.product.inlet)
    m.fs.s06 = Arc(source=m.fs.RO.retentate,
                   destination=m.fs.PXR.high_pressure_inlet)
    m.fs.s07 = Arc(source=m.fs.PXR.high_pressure_outlet,
                   destination=m.fs.disposal.inlet)
    m.fs.s08 = Arc(source=m.fs.S1.PXR, destination=m.fs.PXR.low_pressure_inlet)
    m.fs.s09 = Arc(source=m.fs.PXR.low_pressure_outlet,
                   destination=m.fs.P2.inlet)
    m.fs.s10 = Arc(source=m.fs.P2.outlet, destination=m.fs.M1.P2)
    TransformationFactory("network.expand_arcs").apply_to(m)

    # scaling
    # set default property values
    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', 'NaCl'))
    # set unit model values
    iscale.set_scaling_factor(m.fs.P1.control_volume.work, 1e-3)
    iscale.set_scaling_factor(m.fs.P2.control_volume.work, 1e-3)
    iscale.set_scaling_factor(m.fs.PXR.low_pressure_side.work, 1e-3)
    iscale.set_scaling_factor(m.fs.PXR.high_pressure_side.work, 1e-3)
    # touch properties used in specifying and initializing the model
    m.fs.feed.properties[0].flow_vol_phase['Liq']
    m.fs.feed.properties[0].mass_frac_phase_comp['Liq', 'NaCl']
    m.fs.S1.mixed_state[0].mass_frac_phase_comp
    m.fs.S1.PXR_state[0].flow_vol_phase['Liq']
    # unused scaling factors needed by IDAES base costing module
    # calculate and propagate scaling factors
    iscale.calculate_scaling_factors(m)

    return m
    log_close_to_bounds,
    log_infeasible_bounds,
    log_infeasible_constraints,
)
from pyomo.common.log import LoggingIntercept
import logging


if __name__ == "__main__":

    # create model, flowsheet
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})
    # attach property package
    m.fs.properties = props.NaClParameterBlock()
    m.fs.costing = WaterTAPCosting()
    # build the unit model
    m.fs.crystallizer = Crystallization(default={"property_package": m.fs.properties})

    # now specify the model
    print("DOF before specifying:", degrees_of_freedom(m.fs))

    # Specify the Feed
    m.fs.crystallizer.inlet.flow_mass_phase_comp[0, "Liq", "NaCl"].fix(10.5119)
    m.fs.crystallizer.inlet.flow_mass_phase_comp[0, "Liq", "H2O"].fix(38.9326)
    # m.fs.crystallizer.properties_in[0].flow_vol_phase['Liq'].fix(150/3600)
    # m.fs.crystallizer.properties_in[0].mass_frac_phase_comp['Liq', 'NaCl'].fix(0.2126)
    m.fs.crystallizer.inlet.flow_mass_phase_comp[0, "Sol", "NaCl"].fix(1e-6)
    m.fs.crystallizer.inlet.flow_mass_phase_comp[0, "Vap", "H2O"].fix(1e-6)
    m.fs.crystallizer.inlet.pressure[0].fix(101325)
    m.fs.crystallizer.inlet.temperature[0].fix(273.15 + 20)
Beispiel #5
0
def add_costing(m):
    prtrt = m.fs.pretreatment
    desal = m.fs.desalination
    psttrt = m.fs.posttreatment

    # Add costing package for zero-order units
    m.fs.zo_costing = ZeroOrderCosting()
    m.fs.ro_costing = WaterTAPCosting()

    # Add costing to zero order units
    # Pre-treatment units
    # This really looks like it should be a feed block in its own right
    # prtrt.intake.costing = UnitModelCostingBlock(default={
    #     "flowsheet_costing_block": m.fs.zo_costing})

    prtrt.ferric_chloride_addition.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    prtrt.chlorination.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    prtrt.static_mixer.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    prtrt.storage_tank_1.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    prtrt.media_filtration.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    prtrt.backwash_handling.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    prtrt.anti_scalant_addition.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    prtrt.cartridge_filtration.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )

    # RO Train
    # RO equipment is costed using more detailed costing package
    desal.P1.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.ro_costing}
    )
    desal.RO.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.ro_costing}
    )
    if m.erd_type == "pressure_exchanger":
        # desal.S1.costing = UnitModelCostingBlock(default={
        #     "flowsheet_costing_block": m.fs.ro_costing})
        desal.M1.costing = UnitModelCostingBlock(
            default={"flowsheet_costing_block": m.fs.ro_costing}
        )
        desal.PXR.costing = UnitModelCostingBlock(
            default={"flowsheet_costing_block": m.fs.ro_costing}
        )
        desal.P2.costing = UnitModelCostingBlock(
            default={"flowsheet_costing_block": m.fs.ro_costing}
        )
    elif m.erd_type == "pump_as_turbine":
        pass
        # desal.ERD.costing = UnitModelCostingBlock(default={
        #     "flowsheet_costing_block": m.fs.ro_costing})
    else:
        raise ConfigurationError(
            f"erd_type was {m.erd_type}, costing only implemented "
            "for pressure_exchanger or pump_as_turbine"
        )

    # For non-zero order unit operations, we need to register costed flows
    # separately.
    # However, to keep costs consistent, we will register these with the ZO
    # Costing package
    m.fs.zo_costing.cost_flow(desal.P1.work_mechanical[0], "electricity")
    if m.erd_type == "pressure_exchanger":
        m.fs.zo_costing.cost_flow(desal.P2.work_mechanical[0], "electricity")
    elif m.erd_type == "pump_as_turbine":
        pass
        # m.fs.zo_costing.cost_flow(
        #     desal.ERD.work_mechanical[0], "electricity")
    else:
        raise ConfigurationError(
            f"erd_type was {m.erd_type}, costing only implemented "
            "for pressure_exchanger or pump_as_turbine"
        )

    # Post-treatment units
    psttrt.storage_tank_2.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    psttrt.uv_aop.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    psttrt.co2_addition.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    psttrt.lime_addition.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    psttrt.storage_tank_3.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )

    # Product and disposal
    m.fs.municipal.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )
    m.fs.landfill.costing = UnitModelCostingBlock(
        default={"flowsheet_costing_block": m.fs.zo_costing}
    )

    # Aggregate unit level costs and calculate overall process costs
    m.fs.zo_costing.cost_process()
    m.fs.ro_costing.cost_process()

    # Combine results from costing packages and calculate overall metrics
    @m.Expression()
    def total_capital_cost(b):
        return (
            pyunits.convert(
                m.fs.zo_costing.total_capital_cost, to_units=pyunits.USD_2018
            )
            + m.fs.ro_costing.total_investment_cost
        )

    @m.Expression()
    def total_operating_cost(b):
        return (
            pyunits.convert(
                m.fs.zo_costing.total_fixed_operating_cost,
                to_units=pyunits.USD_2018 / pyunits.year,
            )
            + pyunits.convert(
                m.fs.zo_costing.total_variable_operating_cost,
                to_units=pyunits.USD_2018 / pyunits.year,
            )
            + m.fs.ro_costing.total_operating_cost
        )

    @m.Expression()
    def LCOW(b):
        return (
            b.total_capital_cost * b.fs.zo_costing.capital_recovery_factor
            + b.total_operating_cost
        ) / (
            pyunits.convert(
                b.fs.municipal.properties[0].flow_vol,
                to_units=pyunits.m**3 / pyunits.year,
            )
            * b.fs.zo_costing.utilization_factor
        )

    assert_units_consistent(m)