diff --git a/docs/technical_reference/costing/costing_base.rst b/docs/technical_reference/costing/costing_base.rst index 1babe9db64..10e5498beb 100644 --- a/docs/technical_reference/costing/costing_base.rst +++ b/docs/technical_reference/costing/costing_base.rst @@ -56,7 +56,8 @@ The WaterTAP Costing Framework extends the functionality of the `IDAES Process C ) -2. The method ``register_flow_type`` will create a new Expression if a costing component is not already defined *and* the costing component is not constant. The default behavior in IDAES is to always create a new Var. This allows the user to specify intermediate values in ``register_flow_type``. +2. The method ``register_flow_type`` will create a new Expression if a costing component is not already defined *and* the costing component is not constant. +The default behavior in IDAES is to always create a new Var. This allows the user to specify intermediate values in ``register_flow_type``. .. testcode:: @@ -360,6 +361,28 @@ The total variable operating cost is the sum of the total variable operating cos Aggregate Metrics ------------------ +Built in methods can be used to add expressions for common aggregate metrics used in technoeconomic analyses of water systems. +The following metrics can be added to the costing block by calling the respective method: + +.. csv-table:: + :header: "Method", "Default Expression Name", "Description" + + "``add_LCOW``", "``LCOW``", "Adds LCOW variable and constraint" + "``add_specific_energy_consumption``", "``specific_energy_consumption``", "Adds specific energy consumption variable and constraint" + "``add_specific_electrical_carbon_intensity``", "``specific_electrical_carbon_intensity``", "Adds specific electrical carbon intensity variable and constraint" + "``add_annual_water_production``", "``annual_water_production``", "Adds annual water production variable and constraint" + +Each of these methods requires the user pass a volumetric flow rate :math:`Q` (with units of volume per time) to be used as the basis for the calculation. +Users can optionally provide custom names for the created expression via the `name` keyword argument. For example, creating an expression called ``SEC`` on ``m.fs.costing`` +based on ``flow_rate`` would be: + +.. code-block:: python + + m.fs.costing.add_specific_energy_consumption( + flow_rate, + name="SEC", + ) + Levelized Cost of Water (LCOW) ++++++++++++++++++++++++++++++ @@ -369,6 +392,54 @@ For a given volumetric flow :math:`Q`, the LCOW, :math:`LCOW_{Q}` is calculated LCOW_{Q} = \frac{f_{crf} C_{ca,tot} + C_{op,tot}}{f_{util} Q} +In addition to creating the LCOW expression at the system level, the ``add_LCOW`` method will create the following indexed expressions +to further break down the cost components contributing to the LCOW: + +.. csv-table:: + :header: "Description", "Default Expression Name :sup:`1`", "Index", "Equation :sup:`2`" + + "Direct capital expenditure by flowsheet component", "``LCOW_component_direct_capex``", "Unit model flowsheet name ", ":math:`\cfrac{f_{crf} C_{dir,i}}{f_{util} Q}`" + "Indirect capital expenditure by flowsheet component", "``LCOW_component_indirect_capex``", "Unit model flowsheet name", ":math:`\cfrac{f_{crf} C_{indir,i}}{f_{util} Q}`" + "Fixed operating expenditure by flowsheet component", "``LCOW_component_fixed_opex``", "Unit model flowsheet name", ":math:`\cfrac{f_{crf} C_{fop,i}}{f_{util} Q}`" + "Variable operating expenditure by flowsheet component", "``LCOW_component_variable_opex``", "Unit model flowsheet name *or* flow name", ":math:`\cfrac{f_{crf} C_{vop,i}}{f_{util} Q}`" + "Aggregate direct capital expenditure by unit type", "``LCOW_aggregate_direct_capex``", "Unit model class name", ":math:`\cfrac{f_{crf} \sum C_{dir,u}}{f_{util} Q}`" + "Aggregate indirect capital expenditure by unit type", "``LCOW_aggregate_indirect_capex``", "Unit model class name", ":math:`\cfrac{f_{crf} \sum C_{indir,u}}{f_{util} Q}`" + "Aggregate fixed operating expenditure by unit type", "``LCOW_aggregate_fixed_opex``", "Unit model class name", ":math:`\cfrac{f_{crf} \sum C_{fop,u}}{f_{util} Q}`" + "Aggregate variable operating expenditure by unit type", "``LCOW_aggregate_variable_opex``", "Unit model class name *or* flow name", ":math:`\cfrac{f_{crf} \sum C_{vop,u}}{f_{util} Q}`" + +.. note:: + :sup:`1` The default expression names prepend the method argument `name` to the extended variable name; e.g., ``add_LCOW(flow_rate, name="MyLCOW")``, will result in ``MyLCOW_component_direct_capex``. + + :sup:`2` The index :math:`i` refers to individual unit model instances on the flowsheet, while :math:`u` refers to unit model classes. + +Note, the difference between the "component" and "aggregate" expressions: the component expressions break down costs by individual unit model instances, +while the aggregate expressions sum costs by unit model class (e.g., Mixer, Pump, ReverseOsmosis0D, etc.). So, if there are multiple pumps on the flowsheet, the individual contributions +to LCOW from each pump would be available in the ``LCOW_component_*`` expressions, while the total contribution from all pumps would be available as ``LCOW_aggregate_*`` expressions. +The ``LCOW_component_*`` expressions are indexed by the unit model flowsheet name. This is the name that is assigned when the unit model +is added to the flowsheet. For example, if you add a unit model as ``m.fs.unit1 = MyUnitModel()``, the name used in the LCOW component expressions will be ``fs.unit1``. +The indexes for the ``LCOW_aggregate_*`` expressions are by unit model class name, which is the string representation of the class used to define the unit model (e.g., "ReverseOsmosis0D", "Pump"). + +Importantly, both ``LCOW_component_variable_opex`` and ``LCOW_aggregate_variable_opex`` expressions are also indexed by flow name for registered flows. +Energy (e.g., ``electricity``) and material (e.g., ``naocl``, ``caustic``) flows registered with the costing package will have their variable operating costs +broken out in these expressions. This allows the user to see the contribution of individual flow costs to the overall LCOW. + +For an example of the breakdowns presented by each of these expressions, consider a flowsheet that has two pump units (``m.fs.pump1``, ``m.fs.pump2``) and one chemical addition +unit (``m.fs.chem_add``), and has registered ``electricity`` and ``anti_scalant`` flows. The user adds the LCOW via ``m.fs.costing.add_LCOW(flow_rate)``. The following expressions and indexes would be available on ``m.fs.costing``: + +* ``LCOW_component_direct_capex``: ``fs.pump1``, ``fs.pump2``, ``fs.chem_add`` +* ``LCOW_component_indirect_capex``: ``fs.pump1``, ``fs.pump2``, ``fs.chem_add`` +* ``LCOW_component_fixed_opex``: ``fs.pump1``, ``fs.pump2``, ``fs.chem_add`` +* ``LCOW_component_variable_opex``: ``fs.pump1``, ``fs.pump2``, ``fs.chem_add``, ``electricity``, ``anti_scalant`` +* ``LCOW_aggregate_direct_capex``: ``Pump``, ``ChemicalAdditionZO`` +* ``LCOW_aggregate_indirect_capex``: ``Pump``, ``ChemicalAdditionZO`` +* ``LCOW_aggregate_fixed_opex``: ``Pump``, ``ChemicalAdditionZO`` +* ``LCOW_aggregate_variable_opex``: ``Pump``, ``ChemicalAdditionZO``, ``electricity``, ``anti_scalant`` + +The contribution of flows to the LCOW is found as both an individual contribution to individual unit model breakdown *and* as separate entries. For example, ``electricity`` is counted both in +``LCOW_component_variable_opex['electricity']`` as well as part of the ``LCOW_component_variable_opex`` for each of the unit models that contribute electricity flow (e.g., ``LCOW_component_variable_opex['fs.pump1']`` includes the electricity cost for pump1). +Similarly, ``electricity`` is counted both as ``LCOW_aggregate_variable_opex['electricity']`` as well as part of the ``LCOW_aggregate_variable_opex['Pump']``. For this reason, the system LCOW +is the summation of all indexes in any of the component or aggregate expressions *except* those indexed by flow. + Specific Energy Consumption +++++++++++++++++++++++++++ @@ -378,6 +449,15 @@ For a given volumetric flow `Q`, the specific energy consumption, :math:`E_{spec E_{spec,Q} = \frac{C_{el,tot}}{Q} +Additionally, the specific energy consumption will be broken down by unit model. An expression is created with ``_component`` prepended to the name provided by the user (or ``specific_energy_consumption`` by default). +This expression is indexed by unit model flowsheet name and is calculated as + + .. math:: + + E^{component}_{spec,Q,i} = \frac{C_{el,i}}{Q} + +For a flowsheet with two pump units (``m.fs.pump1``, ``m.fs.pump2``), calling ``m.fs.costing.add_specific_energy_consumption(flow_rate, name="SEC")`` would create ``m.fs.costing.SEC`` and ``m.fs.costing.SEC_component`` indexed by ``fs.pump1`` and ``fs.pump2``. + Specific Electrical Carbon Intensity ++++++++++++++++++++++++++++++++++++ @@ -387,6 +467,15 @@ For a given volumetric flow `Q`, the specific electrical carbon intensity, :math E^{C}_{spec,Q} = \frac{f_{eci} C_{el,tot}}{Q} +Additionally, the specific electrical carbon intensity will be broken down by unit model. An expression is created with ``_component`` prepended to the name provided by the user (or ``specific_electrical_carbon_intensity`` by default). +This expression is indexed by unit model flowsheet name and is calculated as + + .. math:: + + E^{C,comp}_{spec,Q,i} = \frac{f_{eci} C_{el,i}}{Q} + +For a flowsheet with two pump units (``m.fs.pump1``, ``m.fs.pump2``), calling ``m.fs.costing.add_specific_electrical_carbon_intensity(flow_rate, name="SECI")`` would create ``m.fs.costing.SECI`` and ``m.fs.costing.SECI_component`` indexed by ``fs.pump1`` and ``fs.pump2``. + Annual Water Production +++++++++++++++++++++++ diff --git a/watertap/costing/watertap_costing_package.py b/watertap/costing/watertap_costing_package.py index 37e367f27d..6b0245f463 100644 --- a/watertap/costing/watertap_costing_package.py +++ b/watertap/costing/watertap_costing_package.py @@ -314,7 +314,7 @@ def add_annual_water_production(self, flow_rate, name="annual_water_production") Args: flow_rate - flow rate of water (volumetric) to be used in calculating annual water production - name (optional) - name for the annual water productionvariable + name (optional) - name for the annual water production Expression (default: annual_water_production) """ self.add_component(