Utility Functions for Costing

The module watertap.costing.util contains several utility functions for building unit model costing methods.

Utility for Defining Global-Level Parameters Specific to a Unit Model

The register_costing_parameter_block() is a decorator for unit model costing functions. It works by automatically inserting a specified parameter block onto the costing package. It includes additional safeguards for ensuring a costing parameter block is not overwritten during model creation. Below is an example of the functionality:

import pyomo.environ as pyo
import idaes.core as idc
from watertap.costing import (
    WaterTAPCosting,
    register_costing_parameter_block,
    make_capital_cost_var,
)

@idc.declare_process_block_class("MyUnitModel")
class MyUnitModelData(idc.UnitModelBlockData):

    def build(self):
        super().build()
        self.foo_flow = pyo.Var(
            initialize=10,
            units=pyo.units.kg / pyo.units.s,
            doc="foo",
        )

    @property
    def default_costing_method(self):
        # could point to a static method on
        # this class, could be function in
        # a different module even
        return cost_my_unit_model

def build_my_unit_model_param_block(blk):
    """
    This function builds the global parameters and custom flows for MyUnitModel.
    """
    blk.fixed_capital_cost = pyo.Var(
        initialize=42,
        doc="Fixed capital cost for all of my units",
        units=pyo.units.USD_2020,
    )

    blk.foo_flow_cost = pyo.Var(
        initialize=3,
        doc="Foo flow cost",
        units=pyo.units.USD_2016 / pyo.units.kg,
    )
    blk.parent_block().register_flow_type("foo", blk.foo_flow_cost)

# This decorator ensures that the function
# `build_my_unit_model_param_block` is only
# added to the costing package once.
# It registers it as a sub-block with the
# name `my_unit`.
@register_costing_parameter_block(
    build_rule=build_my_unit_model_param_block,
    parameter_block_name="my_unit",
)
def cost_my_unit_model(blk):
    """
    Cost an instance of MyUnitModel
    """
    # creates the `capital_cost` Var
    make_capital_cost_var(blk)

    # here we reference the `fixed_capital_cost` parameter
    # automatically added by the `register_costing_parameter_block`
    # decorator.
    blk.capital_cost_constraint = pyo.Constraint(
        expr=blk.capital_cost == blk.costing_package.my_unit.fixed_capital_cost,
        name="fixed capital cost constraint",
    )

    blk.costing_package.cost_flow(blk.unit_model.foo_flow, "foo")

m = pyo.ConcreteModel()
m.fs = idc.FlowsheetBlock(dynamic=False)
m.fs.costing = WaterTAPCosting()

m.fs.my_unit_1 = MyUnitModel()

# The `default_costing_method_attribute` on the
# unit model is checked, and the function
# `cost_my_unit_model` returned then build the costing block.
# This method also adds the `my_unit` global parameter block,
# so the global costing parameter m.fs.costing.my_unit.fixed_capital_cost
# is the same for all instances of MyUnitModel.
m.fs.my_unit_1.costing = idc.UnitModelCostingBlock(
    flowsheet_costing_block=m.fs.costing,
)

m.fs.my_unit_2 = MyUnitModel()

# Here everythin as before, but the global parameter block
# m.fs.costing.my_unit is not re-built.
m.fs.my_unit_2.costing = idc.UnitModelCostingBlock(
    flowsheet_costing_block=m.fs.costing,
)
watertap.costing.util.register_costing_parameter_block(build_rule, parameter_block_name)[source]

Decorator to create a global-level parameter block on the costing package.

This decorator is meant to be utilized on a costing method or function in order to register parameters (including flows specific to a unit model) which should have global-scope within a costing package. In this way, all instances of a unit model can share the same global parameters, such as baseline capital costs.

The decorator takes a build_rule, which is a rule for constructing a Pyomo Block, and a parameter_block_name, which determines the name of the constructed block. The decorator then puts the new Block onto the costing package. For a unit model costing block named blk, this is symmatically equivalent to:

blk.costing_package.parameter_block_name = Block(rule=build_rule)

However, this decorator contains a few additional safeguards.

  1. If the wrapped unit model costing function is called more than once, the associated parameter block will only be added one time.

  2. Similarly, if a component named parameter_block_name already exists on the costing_package, this decorator will raise an error if the parameter block was built with a different build_rule. This prevents inadvertent name clashes between different parameter blocks.

  3. The decorator fixes any Pyomo Vars it finds on the constructed parameter block.

Use of this decorator allows for defining unit-level costing parameters in the same module as the unit-level costing method. It also ensures that unit-level costing parameters are only added to the costing package if and only if that particular unit model is costed.

Parameters:
  • build_rule (callable) – A function defining a “build rule” for a Pyomo Block.

  • parameter_block_name (str) – The name for the built Block on the costing package.

Utilities for Common Variable Creation

The IDAES costing framework utilizes specific names on a unit model’s costing block to calculate aggregates like aggregate_capital_cost and aggregate_fixed_operating_cost. The make_capital_cost_var() and make_fixed_operating_cost_var() are utilities to create a Pyomo Var with these standard names.

watertap.costing.util.make_capital_cost_var(blk)[source]

Generic function for adding a Var named capital_cost to the unit model costing block.

Creates a pyomo Var named capital_cost with units blk.costing_package.base_currency.

watertap.costing.util.make_fixed_operating_cost_var(blk)[source]

Generic function for adding a Var named fixed_operating_cost to the unit model costing block.

Creates a pyomo Var named fixed_operating_cost with units blk.costing_package.base_currency / blk.costing_package.base_period.

Utilities for Common Cost Calculations

Some costing functions/methods share similar structure. For example, costing for nanofiltration and reverse osmosis is largely a function of the membrane total area. Therefore, WaterTAP has a few utility functions to capture these similar features.

watertap.costing.util.cost_by_flow_volume(blk, flow_cost, flow_to_cost)[source]

Generic function for costing by flow volume.

Parameters:
  • flow_cost – The cost of the device in [currency]/([volume]/[time])

  • flow_to_cost – The flow costed in [volume]/[time]

watertap.costing.util.cost_membrane(blk, membrane_cost, factor_membrane_replacement)[source]

Generic function for costing a membrane. Assumes the unit_model has an area variable or parameter.

Parameters:
  • membrane_cost – The cost of the membrane in currency per area

  • factor_membrane_replacement – Membrane replacement factor (fraction of membrane replaced/year)

watertap.costing.util.cost_rectifier(blk, power=<pyomo.core.expr.numeric_expr.NPV_ProductExpression object>, ac_dc_conversion_efficiency=0.9)[source]

Method to cost rectifiers for electrified process units that require direct current which must be converted from an alternating current source. Note that this should be used solely for units that require the conversion, and should not be used universally for electricity requirements. Assumes the unit_model has a power variable or parameter.

Parameters:

ac_dc_conversion_efficiency – Efficiency of the conversion from AC to DC current