###############################################################################
# WaterTAP Copyright (c) 2021, The Regents of the University of California,
# through Lawrence Berkeley National Laboratory, Oak Ridge National
# Laboratory, National Renewable Energy Laboratory, and National Energy
# Technology Laboratory (subject to receipt of any required approvals from
# the U.S. Dept. of Energy). All rights reserved.
#
# Please see the files COPYRIGHT.md and LICENSE.md for full copyright and license
# information, respectively. These files are also available online at the URL
# "https://github.com/watertap-org/watertap/"
#
###############################################################################
from pyomo.environ import (
Block,
ConcreteModel,
Constraint,
Expression,
Var,
Param,
value,
TransformationFactory,
units as pyunits,
)
import watertap.examples.flowsheets.full_treatment_train.flowsheet_components.financials as financials
from watertap.examples.flowsheets.full_treatment_train.flowsheet_components import (
feed_block,
)
from watertap.examples.flowsheets.full_treatment_train.model_components import (
unit_separator,
unit_0DRO,
unit_1DRO,
property_models,
)
from watertap.examples.flowsheets.full_treatment_train.flowsheet_components.desalination import (
build_desalination,
solve_desalination,
scale_desalination,
initialize_desalination,
display_desalination,
)
# from watertap.examples.flowsheets.full_treatment_train.analysis.flowsheet_softening_two_stage import build, scale, initialize
from watertap.examples.flowsheets.full_treatment_train.util import (
solve_with_user_scaling,
check_dof,
)
import idaes.core.util.scaling as iscale
from idaes.core import FlowsheetBlock
cost_capacity_flag = False
[docs]def build_costing(m, module=financials, **kwargs):
"""Add costing to a given flowsheet. This function will:
1) call the get_costing method for each unit model (note: unit model must have a get_costing method
to be detected), and
2) call get_system_costing which will tally up all capex and opex for each process
Args:
m: model
module: financials module
"""
crf = m.fs.costing_param.factor_capital_annualization
# call get_costing for each unit model
# TODO: add in other components as they become available
m.fs.costing = Block()
# Nanofiltration
if hasattr(m.fs, "NF"):
if kwargs["NF_type"] == "ZO":
m.fs.NF.get_costing(module=module, section="pretreatment")
elif kwargs["NF_type"] == "Sep":
raise NotImplementedError(
"get_costing will not be implemented for the NF separator model."
)
if hasattr(m.fs, "pump_NF"):
m.fs.pump_NF.get_costing(
module=module,
section="pretreatment",
pump_type="low pressure",
cost_capacity=cost_capacity_flag,
)
# Reverse Osmosis
if hasattr(m.fs, "RO"):
if kwargs["RO_type"] == "0D" or kwargs["RO_type"] == "1D":
m.fs.RO.get_costing(module=module, section="primary")
elif kwargs["RO_type"] == "Sep":
raise NotImplementedError(
"get_costing will not be implemented for the RO separator model."
)
# Stage 2 RO
if hasattr(m.fs, "RO2"):
m.fs.RO2.get_costing(module=module, RO_type="high pressure", section="primary")
# Pump
if hasattr(m.fs, "pump_RO"):
m.fs.pump_RO.get_costing(
module=module, section="primary", pump_type="high pressure"
)
# Stage 2 pump
if hasattr(m.fs, "pump_RO2"):
m.fs.pump_RO2.get_costing(
module=module, section="primary", pump_type="high pressure"
)
# ERD
if hasattr(m.fs, "ERD"):
m.fs.ERD.get_costing(
module=module, section="primary", pump_type="pressure exchanger"
)
# Pretreatment
if hasattr(m.fs, "stoich_softening_mixer_unit"):
m.fs.stoich_softening_mixer_unit.get_costing(
module=module,
section="pretreatment",
mixer_type="lime_softening",
cost_capacity=cost_capacity_flag,
)
m.fs.lime_softening_unit_capex = Expression(
expr=m.fs.stoich_softening_mixer_unit.costing.capital_cost
/ m.fs.annual_water_production
* crf
)
m.fs.lime_softening_unit_opex = Expression(
expr=m.fs.stoich_softening_mixer_unit.costing.operating_cost
/ m.fs.annual_water_production
)
else:
m.fs.lime_softening_unit_capex = Expression(expr=0)
m.fs.lime_softening_unit_opex = Expression(expr=0)
# Post-treatment
if hasattr(m.fs, "ideal_naocl_mixer_unit"):
# print('FOUND CHLORINATION UNIT')
m.fs.ideal_naocl_mixer_unit.get_costing(
module=module,
section="post_treatment",
mixer_type="naocl_mixer",
cost_capacity=cost_capacity_flag,
)
m.fs.chlorination_unit_capex = Expression(
expr=m.fs.ideal_naocl_mixer_unit.costing.capital_cost
/ m.fs.annual_water_production
* crf
)
m.fs.chlorination_unit_opex = Expression(
expr=m.fs.ideal_naocl_mixer_unit.costing.operating_cost
/ m.fs.annual_water_production
)
else:
m.fs.chlorination_unit_capex = Expression(expr=0)
m.fs.chlorination_unit_opex = Expression(expr=0)
if hasattr(m.fs, "mixer_permeate"):
m.fs.mixer_permeate.get_costing(
module=module, section="primary", cost_capacity=cost_capacity_flag
)
# call get_system_costing for whole flowsheet
module.get_system_costing(m.fs)
# apply scaling to cost variables and constraints
scale_costing(m)
def scale_costing(self):
for b_unit in self.component_objects(Block, descend_into=True):
if hasattr(b_unit, "costing"):
base = b_unit.costing
for var in base.component_objects(Var):
if iscale.get_scaling_factor(var) is None:
iscale.set_scaling_factor(var, 1e-3)
for con in base.component_objects(Constraint):
sf = iscale.get_scaling_factor(var)
iscale.constraint_scaling_transform(con, sf)
def display_costing(m):
crf = m.fs.costing_param.factor_capital_annualization
if not hasattr(m.fs, "pump_RO2"):
m.fs.pump_RO2 = Block()
m.fs.pump_RO2.costing = Block()
m.fs.pump_RO2.costing.operating_cost = Param(initialize=0)
if not hasattr(m.fs, "NF"):
m.fs.NF = Block()
m.fs.NF.costing = Block()
m.fs.NF.costing.operating_cost = Param(initialize=0)
if not hasattr(m.fs, "RO2"):
m.fs.RO2 = Block()
m.fs.RO2.costing = Block()
m.fs.RO2.costing.operating_cost = Param(initialize=0)
# UNITS FOR ALL COST COMPONENTS [=] $/m3 of permeate water produced
cost_dict = {
"LCOW": m.fs.costing.LCOW, # Total LCOW
"Total CAPEX": m.fs.costing.investment_cost_total
* crf
/ m.fs.annual_water_production, # Direct + Indirect CAPEX
"Direct CAPEX": m.fs.costing.capital_cost_total
* crf
/ m.fs.annual_water_production, # Direct CAPEX for all system components
"Indirect CAPEX": (
m.fs.costing.investment_cost_total - m.fs.costing.capital_cost_total
)
* crf
/ m.fs.annual_water_production, # Indirect CAPEX for miscellaneous items
"Total OPEX": m.fs.costing.operating_cost_total
/ m.fs.annual_water_production, # Total OPEX
"Labor & Maintenance Costs": m.fs.costing.operating_cost_labor_maintenance
/ m.fs.annual_water_production,
"Total Electricity Cost": m.fs.costing.electricity_cost_total
/ m.fs.annual_water_production,
"Total Membrane Replacement Cost": (
m.fs.NF.costing.operating_cost
+ m.fs.RO.costing.operating_cost
+ m.fs.RO2.costing.operating_cost
)
/ m.fs.annual_water_production,
"Total Pretreatment Cost": m.fs.costing.pretreatment_cost_total
/ m.fs.annual_water_production,
"Total Primary Cost": m.fs.costing.primary_cost_total
/ m.fs.annual_water_production,
"Total Post-treatment Cost": m.fs.costing.post_treatment_cost_total
/ m.fs.annual_water_production,
"Lime softener CAPEX": m.fs.lime_softening_unit_capex,
"Lime softener OPEX": m.fs.lime_softening_unit_opex,
"Chlorination CAPEX": m.fs.chlorination_unit_capex,
"Chlorination OPEX": m.fs.chlorination_unit_opex,
}
for item, val in cost_dict.items():
print(f"{item} = {value(val)}")
return cost_dict