Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 90 additions & 1 deletion docs/technical_reference/costing/costing_base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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::

Expand Down Expand Up @@ -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)
++++++++++++++++++++++++++++++

Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same breakdown for LCOW is applied to SEC and can be applied to other metrics by using the function that performs the breakdown (forgetting its name, but used for LCOW and SEC breakdowns)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_add_flow_component_breakdowns

Yes as I was writing this I was thinking maybe we could "unhide" this method and provide docs for it here so it is useful for other flows?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure- and I would double-check that we are testing the function with another dummy flow (or just another tracked quantity on a flowsheet)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bknueven wondering if you see any issues with unhiding _add_flow_component_breakdowns

+++++++++++++++++++++++++++

Expand All @@ -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
++++++++++++++++++++++++++++++++++++

Expand All @@ -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
+++++++++++++++++++++++

Expand Down
2 changes: 1 addition & 1 deletion watertap/costing/watertap_costing_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading