Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended BSM2 Flowsheet Costing #1405

Merged
merged 6 commits into from
May 24, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
print("\n\n=============OPTIMIZATION RESULTS=============\n\n")
# display_results(m)
display_costing(m)
display_performance_metrics(m)

Check warning on line 101 in watertap/examples/flowsheets/case_studies/full_water_resource_recovery_facility/BSM2.py

View check run for this annotation

Codecov / codecov/patch

watertap/examples/flowsheets/case_studies/full_water_resource_recovery_facility/BSM2.py#L101

Added line #L101 was not covered by tests

return m, results

Expand Down Expand Up @@ -485,7 +486,6 @@

# process costing and add system level metrics
m.fs.costing.cost_process()
m.fs.costing.add_electricity_intensity(m.fs.FeedWater.properties[0].flow_vol)
m.fs.costing.add_annual_water_production(m.fs.Treated.properties[0].flow_vol)
m.fs.costing.add_LCOW(m.fs.FeedWater.properties[0].flow_vol)
m.fs.costing.add_specific_energy_consumption(m.fs.FeedWater.properties[0].flow_vol)
Expand Down Expand Up @@ -622,6 +622,59 @@
"Total annualized cost: %.2f $/yr"
% pyo.value(m.fs.costing.total_annualized_cost)
)
print(
"capital cost R1",
pyo.value(m.fs.R1.costing.capital_cost),
pyo.units.get_units(m.fs.R1.costing.capital_cost),
)
print(
"capital cost R2",
pyo.value(m.fs.R2.costing.capital_cost),
pyo.units.get_units(m.fs.R2.costing.capital_cost),
)
print(
"capital cost R3",
pyo.value(m.fs.R3.costing.capital_cost),
pyo.units.get_units(m.fs.R3.costing.capital_cost),
)
print(
"capital cost R4",
pyo.value(m.fs.R4.costing.capital_cost),
pyo.units.get_units(m.fs.R4.costing.capital_cost),
)
print(
"capital cost R5",
pyo.value(m.fs.R5.costing.capital_cost),
pyo.units.get_units(m.fs.R5.costing.capital_cost),
)
print(
"capital cost primary clarifier",
pyo.value(m.fs.CL.costing.capital_cost),
pyo.units.get_units(m.fs.CL.costing.capital_cost),
)
print(
"capital cost secondary clarifier",
pyo.value(m.fs.CL1.costing.capital_cost),
pyo.units.get_units(m.fs.CL1.costing.capital_cost),
)
print(
"capital cost AD",
pyo.value(m.fs.RADM.costing.capital_cost),
pyo.units.get_units(m.fs.RADM.costing.capital_cost),
)
print(
"capital cost dewatering Unit",
pyo.value(m.fs.DU.costing.capital_cost),
pyo.units.get_units(m.fs.DU.costing.capital_cost),
)
print(
"capital cost thickener unit",
pyo.value(m.fs.TU.costing.capital_cost),
pyo.units.get_units(m.fs.TU.costing.capital_cost),
)


def display_performance_metrics(m):
print(
"Specific energy consumption with respect to influent flowrate: %.1f kWh/m3"
% pyo.value(m.fs.costing.specific_energy_consumption)
Expand Down Expand Up @@ -683,57 +736,6 @@
pyo.units.get_units(m.fs.RADM.liquid_phase.properties_in[0].flow_vol),
)

print(
"capital cost R1",
pyo.value(m.fs.R1.costing.capital_cost),
pyo.units.get_units(m.fs.R1.costing.capital_cost),
)
print(
"capital cost R2",
pyo.value(m.fs.R2.costing.capital_cost),
pyo.units.get_units(m.fs.R2.costing.capital_cost),
)
print(
"capital cost R3",
pyo.value(m.fs.R3.costing.capital_cost),
pyo.units.get_units(m.fs.R3.costing.capital_cost),
)
print(
"capital cost R4",
pyo.value(m.fs.R4.costing.capital_cost),
pyo.units.get_units(m.fs.R4.costing.capital_cost),
)
print(
"capital cost R5",
pyo.value(m.fs.R5.costing.capital_cost),
pyo.units.get_units(m.fs.R5.costing.capital_cost),
)
print(
"capital cost primary clarifier",
pyo.value(m.fs.CL.costing.capital_cost),
pyo.units.get_units(m.fs.CL.costing.capital_cost),
)
print(
"capital cost secondary clarifier",
pyo.value(m.fs.CL1.costing.capital_cost),
pyo.units.get_units(m.fs.CL1.costing.capital_cost),
)
print(
"capital cost AD",
pyo.value(m.fs.RADM.costing.capital_cost),
pyo.units.get_units(m.fs.RADM.costing.capital_cost),
)
print(
"capital cost dewatering Unit",
pyo.value(m.fs.DU.costing.capital_cost),
pyo.units.get_units(m.fs.DU.costing.capital_cost),
)
print(
"capital cost thickener unit",
pyo.value(m.fs.TU.costing.capital_cost),
pyo.units.get_units(m.fs.TU.costing.capital_cost),
)


if __name__ == "__main__":
m, results = main()
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from idaes.core import (
FlowsheetBlock,
UnitModelCostingBlock,
)
from idaes.models.unit_models import (
CSTR,
Expand Down Expand Up @@ -74,7 +75,14 @@
Thickener,
ActivatedSludgeModelType as thickener_type,
)
from watertap.core.util.initialization import check_solve

from watertap.core.util.initialization import check_solve, assert_degrees_of_freedom
from watertap.costing import WaterTAPCosting
from watertap.costing.unit_models.clarifier import (
cost_circular_clarifier,
cost_primary_clarifier,
)


# Set up logger
_log = idaeslog.getLogger(__name__)
Expand Down Expand Up @@ -116,6 +124,17 @@ def main(bio_P=True):
fail_flag=True,
)

add_costing(m)
m.fs.costing.initialize()

assert_degrees_of_freedom(m, 0)

results = solve(m)
pyo.assert_optimal_termination(results)

display_costing(m)
display_performance_metrics(m)

return m, results


Expand Down Expand Up @@ -471,6 +490,8 @@ def set_operating_conditions(m):
m.fs.CL2.split_fraction[0, "effluent", "X_PP"].fix(0.00187)
m.fs.CL2.split_fraction[0, "effluent", "X_S"].fix(0.00187)

m.fs.CL2.surface_area.fix(1500 * pyo.units.m**2)

# Sludge purge separator
m.fs.SP2.split_fraction[:, "recycle"].fix(0.985)

Expand Down Expand Up @@ -658,6 +679,186 @@ def solve(m, solver=None):
return results


def add_costing(m):
m.fs.costing = WaterTAPCosting()
m.fs.costing.base_currency = pyo.units.USD_2020
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this what we use on the conventional BSM2? I think we should avoid 2020 as the base currency because of the pandemic's impact.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Conventional also used 2020... should I switch both to 2018 or 2019?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good question. I'm not sure that we've selected a standard year that we want to go by. I think I can recall using 2018 for a few of the IEDO models previously. I suppose we can put a pin it this for now if we'd rather settle on a year.

Copy link
Contributor

Choose a reason for hiding this comment

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

But perhaps the best route is to go with the latest year that we have the CEPCI index for

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The latest year in IDAES is 2022, but it appears that hasn't made it's way into WaterTAP yet. I tried 2021 as well, but the solver failed to return an optimal solution... not sure why that would happen.


# Costing Blocks
m.fs.R1.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.R2.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.R3.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.R4.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.R5.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.R6.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.R7.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.CL.costing = UnitModelCostingBlock(
flowsheet_costing_block=m.fs.costing,
costing_method=cost_primary_clarifier,
)

m.fs.CL2.costing = UnitModelCostingBlock(
flowsheet_costing_block=m.fs.costing,
costing_method=cost_circular_clarifier,
)

m.fs.AD.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.dewater.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
m.fs.thickener.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)

# TODO: Leaving out mixer costs; consider including later

# process costing and add system level metrics
m.fs.costing.cost_process()
m.fs.costing.add_annual_water_production(m.fs.Treated.properties[0].flow_vol)
m.fs.costing.add_LCOW(m.fs.FeedWater.properties[0].flow_vol)
m.fs.costing.add_specific_energy_consumption(m.fs.FeedWater.properties[0].flow_vol)

m.fs.objective = pyo.Objective(expr=m.fs.costing.LCOW)
iscale.set_scaling_factor(m.fs.costing.LCOW, 1e3)
iscale.set_scaling_factor(m.fs.costing.total_capital_cost, 1e-7)
iscale.set_scaling_factor(m.fs.costing.total_capital_cost, 1e-5)

iscale.calculate_scaling_factors(m.fs)


def display_costing(m):
print("Levelized cost of water: %.2f $/m3" % pyo.value(m.fs.costing.LCOW))

print(
"Total operating cost: %.2f $/yr" % pyo.value(m.fs.costing.total_operating_cost)
)
print("Total capital cost: %.2f $" % pyo.value(m.fs.costing.total_capital_cost))

print(
"Total annualized cost: %.2f $/yr"
% pyo.value(m.fs.costing.total_annualized_cost)
)

print(
"capital cost R1",
pyo.value(m.fs.R1.costing.capital_cost),
pyo.units.get_units(m.fs.R1.costing.capital_cost),
)
print(
"capital cost R2",
pyo.value(m.fs.R2.costing.capital_cost),
pyo.units.get_units(m.fs.R2.costing.capital_cost),
)
print(
"capital cost R3",
pyo.value(m.fs.R3.costing.capital_cost),
pyo.units.get_units(m.fs.R3.costing.capital_cost),
)
print(
"capital cost R4",
pyo.value(m.fs.R4.costing.capital_cost),
pyo.units.get_units(m.fs.R4.costing.capital_cost),
)
print(
"capital cost R5",
pyo.value(m.fs.R5.costing.capital_cost),
pyo.units.get_units(m.fs.R5.costing.capital_cost),
)
print(
"capital cost R6",
pyo.value(m.fs.R6.costing.capital_cost),
pyo.units.get_units(m.fs.R6.costing.capital_cost),
)
print(
"capital cost R7",
pyo.value(m.fs.R7.costing.capital_cost),
pyo.units.get_units(m.fs.R7.costing.capital_cost),
)
print(
"capital cost primary clarifier",
pyo.value(m.fs.CL.costing.capital_cost),
pyo.units.get_units(m.fs.CL.costing.capital_cost),
)
print(
"capital cost secondary clarifier",
pyo.value(m.fs.CL2.costing.capital_cost),
pyo.units.get_units(m.fs.CL2.costing.capital_cost),
)
print(
"capital cost AD",
pyo.value(m.fs.AD.costing.capital_cost),
pyo.units.get_units(m.fs.AD.costing.capital_cost),
)
print(
"capital cost dewatering Unit",
pyo.value(m.fs.dewater.costing.capital_cost),
pyo.units.get_units(m.fs.dewater.costing.capital_cost),
)
print(
"capital cost thickener unit",
pyo.value(m.fs.thickener.costing.capital_cost),
pyo.units.get_units(m.fs.thickener.costing.capital_cost),
)


def display_performance_metrics(m):
print(
"Specific energy consumption with respect to influent flowrate: %.1f kWh/m3"
% pyo.value(m.fs.costing.specific_energy_consumption)
)

print(
"electricity consumption R5",
pyo.value(m.fs.R5.electricity_consumption[0]),
pyo.units.get_units(m.fs.R5.electricity_consumption[0]),
)
print(
"electricity consumption R6",
pyo.value(m.fs.R6.electricity_consumption[0]),
pyo.units.get_units(m.fs.R6.electricity_consumption[0]),
)
print(
"electricity consumption R7",
pyo.value(m.fs.R7.electricity_consumption[0]),
pyo.units.get_units(m.fs.R7.electricity_consumption[0]),
)
print(
"electricity consumption primary clarifier",
pyo.value(m.fs.CL.electricity_consumption[0]),
pyo.units.get_units(m.fs.CL.electricity_consumption[0]),
)
print(
"electricity consumption secondary clarifier",
pyo.value(m.fs.CL2.electricity_consumption[0]),
pyo.units.get_units(m.fs.CL2.electricity_consumption[0]),
)
print(
"electricity consumption AD",
pyo.value(m.fs.AD.electricity_consumption[0]),
pyo.units.get_units(m.fs.AD.electricity_consumption[0]),
)
print(
"electricity consumption dewatering Unit",
pyo.value(m.fs.dewater.electricity_consumption[0]),
pyo.units.get_units(m.fs.dewater.electricity_consumption[0]),
)
print(
"electricity consumption thickening Unit",
pyo.value(m.fs.thickener.electricity_consumption[0]),
pyo.units.get_units(m.fs.thickener.electricity_consumption[0]),
)
print(
"Influent flow",
pyo.value(m.fs.FeedWater.flow_vol[0]),
pyo.units.get_units(m.fs.FeedWater.flow_vol[0]),
)
print(
"flow into R3",
pyo.value(m.fs.R3.control_volume.properties_in[0].flow_vol),
pyo.units.get_units(m.fs.R3.control_volume.properties_in[0].flow_vol),
)
print(
"flow into RADM",
pyo.value(m.fs.AD.liquid_phase.properties_in[0].flow_vol),
pyo.units.get_units(m.fs.AD.liquid_phase.properties_in[0].flow_vol),
)


if __name__ == "__main__":
# This method builds and runs a steady state activated sludge flowsheet.
m, results = main()
Expand Down
Loading