Skip to content

Commit

Permalink
Costing Unification: parameter consistency across costing packages (#…
Browse files Browse the repository at this point in the history
…1174)

* redefining factor_maintenance_labor_chemical as fraction of total equipment cost

* using higher-level parameters in ZO Costing Package

* consistent expressions / vars across both costing packages

* update CSTR baseline

---------

Co-authored-by: Ludovico Bianchi <lbianchi@lbl.gov>
  • Loading branch information
bknueven and lbianchi-lbl authored Dec 13, 2023
1 parent 47bac1f commit fa126c5
Show file tree
Hide file tree
Showing 19 changed files with 81 additions and 161 deletions.
3 changes: 2 additions & 1 deletion docs/technical_reference/costing/watertap_costing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Technoeconomic factors used to calculate various system metrics, capital, and op
============================================= ==================== ======================================= =============== ==============================================================================
Plant capacity utilization factor :math:`f_{util}` ``utilization_factor`` 90% Percentage of year plant is operating
Total investment factor :math:`f_{toti}` ``factor_total_investment`` 2.0 Total investment factor (investment cost / equipment cost)
Maintenance-labor-chemical factor :math:`f_{mlc}` ``factor_maintenance_labor_chemical`` 0.03 Maintenance, labor, and chemical factor (fraction of investment cost / year)
Maintenance-labor-chemical factor :math:`f_{mlc}` ``factor_maintenance_labor_chemical`` 0.03 Maintenance, labor, and chemical factor (fraction of equipment cost / year)
Captial annualization factor :math:`f_{caf}` ``factor_capital_annualization`` 0.1 Capital annualization factor (fraction of investment cost / year)
Capital recovery factor :math:`f_{crf}` ``capital_recovery_factor`` 0.1 Identical to `factor_capital_annualization`
============================================= ==================== ======================================= =============== ==============================================================================
Expand Down Expand Up @@ -166,3 +166,4 @@ Class Documentation
-------------------

* :class:`WaterTAPCostingData`

30 changes: 3 additions & 27 deletions watertap/costing/tests/test_zero_order_costing.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,12 +411,10 @@ def test_process_costing(self, model):
assert isinstance(model.fs.costing.aggregate_flow_electricity, Var)
assert isinstance(model.fs.costing.aggregate_flow_costs, Var)

assert isinstance(model.fs.costing.land_cost, Var)
assert isinstance(model.fs.costing.working_capital, Var)
assert isinstance(model.fs.costing.land_cost, Expression)
assert isinstance(model.fs.costing.working_capital, Expression)
assert isinstance(model.fs.costing.total_capital_cost, Var)

assert isinstance(model.fs.costing.land_cost_constraint, Constraint)
assert isinstance(model.fs.costing.working_capital_constraint, Constraint)
assert isinstance(model.fs.costing.total_capital_cost_constraint, Constraint)

assert_units_consistent(model.fs)
Expand Down Expand Up @@ -451,34 +449,12 @@ def test_initialize(self, model):

model.fs.costing.initialize()

assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.land_cost_constraint
)
assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.working_capital_constraint
)
assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.total_capital_cost_constraint
)

assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.salary_cost_constraint
)
assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.benefits_cost_constraint
)
assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.maintenance_cost_constraint
)
assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.laboratory_cost_constraint
)
assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.insurance_and_taxes_cost_constraint
)

assert pytest.approx(0, abs=1e-5) == value(
model.fs.costing.total_fixed_operating_cost_constraint
model.fs.costing.total_operating_cost_constraint
)

@pytest.mark.solver
Expand Down
58 changes: 25 additions & 33 deletions watertap/costing/watertap_costing_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def build_global_params(self):
)
self.factor_maintenance_labor_chemical = pyo.Var(
initialize=0.03,
doc="Maintenance-labor-chemical factor [fraction of investment cost/year]",
doc="Maintenance-labor-chemical factor [fraction of equipment cost/year]",
units=pyo.units.year**-1,
)
self.factor_capital_annualization = pyo.Var(
Expand All @@ -59,42 +59,38 @@ def build_process_costs(self):
# add total_captial_cost and total_operating_cost
self._build_common_process_costs()

self.maintenance_labor_chemical_operating_cost = pyo.Var(
initialize=1e3,
doc="Maintenance-labor-chemical operating cost",
units=self.base_currency / self.base_period,
)

self.total_capital_cost_constraint = pyo.Constraint(
expr=self.total_capital_cost
== self.factor_total_investment * self.aggregate_capital_cost
)
self.maintenance_labor_chemical_operating_cost_constraint = pyo.Constraint(
expr=self.maintenance_labor_chemical_operating_cost
== self.factor_maintenance_labor_chemical * self.total_capital_cost

self.maintenance_labor_chemical_operating_cost = pyo.Expression(
expr=self.factor_maintenance_labor_chemical * self.aggregate_capital_cost,
doc="Maintenance-labor-chemical operating cost",
)

self.total_fixed_operating_cost = pyo.Expression(
expr=self.aggregate_fixed_operating_cost
+ self.maintenance_labor_chemical_operating_cost,
doc="Total fixed operating costs",
)

if (
pyo.units.get_units(sum(self.aggregate_flow_costs.values()))
) == pyo.units.dimensionless:
self.total_operating_cost_constraint = pyo.Constraint(
expr=self.total_operating_cost
== self.maintenance_labor_chemical_operating_cost
+ self.aggregate_fixed_operating_cost
+ self.aggregate_variable_operating_cost
+ sum(self.aggregate_flow_costs.values())
* self.base_currency
/ self.base_period
self.total_variable_operating_cost = pyo.Expression(
expr=(
self.aggregate_variable_operating_cost
+ sum(self.aggregate_flow_costs[f] for f in self.used_flows)
* self.utilization_factor
)
else:
self.total_operating_cost_constraint = pyo.Constraint(
expr=self.total_operating_cost
== self.maintenance_labor_chemical_operating_cost
+ self.aggregate_fixed_operating_cost
+ self.aggregate_variable_operating_cost
+ sum(self.aggregate_flow_costs.values()) * self.utilization_factor
)
if self.used_flows
else self.aggregate_variable_operating_cost,
doc="Total variable operating cost of process per operating period",
)

self.total_operating_cost_constraint = pyo.Constraint(
expr=self.total_operating_cost
== (self.total_fixed_operating_cost + self.total_variable_operating_cost),
doc="Total operating cost of process per operating period",
)

self.total_annualized_cost = pyo.Expression(
expr=(
Expand All @@ -108,10 +104,6 @@ def initialize_build(self):
calculate_variable_from_constraint(
self.total_capital_cost, self.total_capital_cost_constraint
)
calculate_variable_from_constraint(
self.maintenance_labor_chemical_operating_cost,
self.maintenance_labor_chemical_operating_cost_constraint,
)
calculate_variable_from_constraint(
self.total_operating_cost, self.total_operating_cost_constraint
)
130 changes: 44 additions & 86 deletions watertap/costing/zero_order_costing.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ def build_global_params(self):
/ self.base_period
)

self.factor_total_investment = pyo.Expression(
expr=1.0 + self.working_capital_percent_FCI + self.land_cost_percent_FCI,
doc="Total investment factor [investment cost/equipment cost]",
)

# Fix all Vars from database
for v in global_params:
try:
Expand All @@ -141,97 +146,71 @@ def build_process_costs(self):
self._build_common_process_costs()

# Other capital costs
self.land_cost = pyo.Var(
initialize=0,
units=self.base_currency,
self.land_cost = pyo.Expression(
expr=self.land_cost_percent_FCI * self.aggregate_capital_cost,
doc="Land costs - based on aggregate capital costs",
)
self.working_capital = pyo.Var(
initialize=0,
units=self.base_currency,
self.working_capital = pyo.Expression(
expr=self.working_capital_percent_FCI * self.aggregate_capital_cost,
doc="Working capital - based on aggregate capital costs",
)

self.land_cost_constraint = pyo.Constraint(
expr=self.land_cost
== self.aggregate_capital_cost * self.land_cost_percent_FCI
)
self.working_capital_constraint = pyo.Constraint(
expr=self.working_capital
== self.aggregate_capital_cost * self.working_capital_percent_FCI
)
self.total_capital_cost_constraint = pyo.Constraint(
expr=self.total_capital_cost
== self.aggregate_capital_cost + self.land_cost + self.working_capital
== self.factor_total_investment * self.aggregate_capital_cost
)

# Other fixed costs
self.salary_cost = pyo.Var(
initialize=0,
units=self.base_currency / self.base_period,
self.salary_cost = pyo.Expression(
expr=self.salaries_percent_FCI * self.aggregate_capital_cost,
doc="Salary costs - based on aggregate capital costs",
)
self.benefits_cost = pyo.Var(
initialize=0,
units=self.base_currency / self.base_period,
self.benefits_cost = pyo.Expression(
expr=self.benefit_percent_of_salary
* self.salaries_percent_FCI
* self.aggregate_capital_cost,
doc="Benefits costs - based on percentage of salary costs",
)
self.maintenance_cost = pyo.Var(
initialize=0,
units=self.base_currency / self.base_period,
self.maintenance_cost = pyo.Expression(
expr=self.maintenance_costs_percent_FCI * self.aggregate_capital_cost,
doc="Maintenance costs - based on aggregate capital costs",
)
self.laboratory_cost = pyo.Var(
initialize=0,
units=self.base_currency / self.base_period,
self.laboratory_cost = pyo.Expression(
expr=self.laboratory_fees_percent_FCI * self.aggregate_capital_cost,
doc="Laboratory costs - based on aggregate capital costs",
)
self.insurance_and_taxes_cost = pyo.Var(
initialize=0,
units=self.base_currency / self.base_period,
self.insurance_and_taxes_cost = pyo.Expression(
expr=self.insurance_and_taxes_percent_FCI * self.aggregate_capital_cost,
doc="Insurance and taxes costs - based on aggregate capital costs",
)
self.total_fixed_operating_cost = pyo.Var(
initialize=0,
units=self.base_currency / self.base_period,
doc="Total fixed operating costs",
self.factor_maintenance_labor_chemical = pyo.Expression(
expr=self.salaries_percent_FCI
+ self.benefit_percent_of_salary * self.salaries_percent_FCI
+ self.maintenance_costs_percent_FCI
+ self.laboratory_fees_percent_FCI
+ self.insurance_and_taxes_percent_FCI,
doc="Maintenance-labor-chemical factor [fraction of equipment cost/year]",
)

self.salary_cost_constraint = pyo.Constraint(
expr=self.salary_cost
== self.aggregate_capital_cost * self.salaries_percent_FCI
)
self.benefits_cost_constraint = pyo.Constraint(
expr=self.benefits_cost == self.salary_cost * self.benefit_percent_of_salary
)
self.maintenance_cost_constraint = pyo.Constraint(
expr=self.maintenance_cost
== self.aggregate_capital_cost * self.maintenance_costs_percent_FCI
)
self.laboratory_cost_constraint = pyo.Constraint(
expr=self.laboratory_cost
== self.aggregate_capital_cost * self.laboratory_fees_percent_FCI
)
self.insurance_and_taxes_cost_constraint = pyo.Constraint(
expr=self.insurance_and_taxes_cost
== self.aggregate_capital_cost * self.insurance_and_taxes_percent_FCI
self.maintenance_labor_chemical_operating_cost = pyo.Expression(
expr=self.factor_maintenance_labor_chemical * self.aggregate_capital_cost,
doc="Maintenance-labor-chemical operating cost",
)

self.total_fixed_operating_cost_constraint = pyo.Constraint(
expr=self.total_fixed_operating_cost
== self.aggregate_fixed_operating_cost
+ self.salary_cost
+ self.benefits_cost
+ self.maintenance_cost
+ self.laboratory_cost
+ self.insurance_and_taxes_cost
self.total_fixed_operating_cost = pyo.Expression(
expr=self.aggregate_fixed_operating_cost
+ self.maintenance_labor_chemical_operating_cost,
doc="Total fixed operating costs",
)

# Other variable costs
self.total_variable_operating_cost = pyo.Expression(
expr=self.aggregate_variable_operating_cost
+ sum(self.aggregate_flow_costs[f] for f in self.used_flows)
* self.utilization_factor,
expr=(
self.aggregate_variable_operating_cost
+ sum(self.aggregate_flow_costs[f] for f in self.used_flows)
* self.utilization_factor
)
if self.used_flows
else self.aggregate_variable_operating_cost,
doc="Total variable operating cost of process per operating period",
)

Expand All @@ -253,32 +232,11 @@ def initialize_build(self):
"""
Basic initialization for flowsheet level quantities
"""
calculate_variable_from_constraint(self.land_cost, self.land_cost_constraint)
calculate_variable_from_constraint(
self.working_capital, self.working_capital_constraint
)
calculate_variable_from_constraint(
self.total_capital_cost, self.total_capital_cost_constraint
)

calculate_variable_from_constraint(
self.salary_cost, self.salary_cost_constraint
)
calculate_variable_from_constraint(
self.benefits_cost, self.benefits_cost_constraint
)
calculate_variable_from_constraint(
self.maintenance_cost, self.maintenance_cost_constraint
)
calculate_variable_from_constraint(
self.laboratory_cost, self.laboratory_cost_constraint
)
calculate_variable_from_constraint(
self.insurance_and_taxes_cost, self.insurance_and_taxes_cost_constraint
)

calculate_variable_from_constraint(
self.total_fixed_operating_cost, self.total_fixed_operating_cost_constraint
self.total_operating_cost, self.total_operating_cost_constraint
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ def test_build(self, system_frame):

var_str_list = [
"total_capital_cost",
"maintenance_labor_chemical_operating_cost",
"total_operating_cost",
]
for var_str in var_str_list:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ def test_build_model(self, electrodialysis_1D1stack):

var_str_list = [
"total_capital_cost",
"maintenance_labor_chemical_operating_cost",
"total_operating_cost",
]
for var_str in var_str_list:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ def test_build_model(self, ix_0D):

var_str_list = [
"total_capital_cost",
"maintenance_labor_chemical_operating_cost",
"total_operating_cost",
]
for var_str in var_str_list:
Expand Down
2 changes: 2 additions & 0 deletions watertap/examples/flowsheets/lsrro/lsrro.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ def stage_recovery_mass_H2O(fs, stage):
* (
1
+ m.fs.costing.factor_maintenance_labor_chemical
/ m.fs.costing.factor_total_investment
/ m.fs.costing.factor_capital_annualization
)
+ m.fs.costing.electricity_lcow
Expand Down Expand Up @@ -454,6 +455,7 @@ def stage_recovery_mass_H2O(fs, stage):
* (
1
+ m.fs.costing.factor_maintenance_labor_chemical
/ m.fs.costing.factor_total_investment
/ m.fs.costing.factor_capital_annualization
)
+ m.fs.costing.membrane_replacement_lcow
Expand Down
1 change: 0 additions & 1 deletion watertap/examples/flowsheets/mvc/mvc_single_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,6 @@ def scale_costs(m):
calculate_cost_sf(m.fs.costing.aggregate_capital_cost)
calculate_cost_sf(m.fs.costing.aggregate_flow_costs["electricity"])
calculate_cost_sf(m.fs.costing.total_capital_cost)
calculate_cost_sf(m.fs.costing.maintenance_labor_chemical_operating_cost)
calculate_cost_sf(m.fs.costing.total_operating_cost)

iscale.calculate_scaling_factors(m)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ def test_build_model(self, mvc_single_stage):

var_str_list = [
"total_capital_cost",
"maintenance_labor_chemical_operating_cost",
"total_operating_cost",
]
for var_str in var_str_list:
Expand Down
2 changes: 1 addition & 1 deletion watertap/examples/flowsheets/nf_dspmde/nf_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def export_variables(flowsheet=None, exports=None, build_options=None, **kwargs)
obj=fs.costing.factor_maintenance_labor_chemical,
name="Maintenance-labor-chemical factor",
ui_units=pyunits.year**-1,
display_units="fraction of investment cost/year",
display_units="fraction of equipment cost/year",
rounding=4,
description="NF OPEX",
is_input=True,
Expand Down
Loading

0 comments on commit fa126c5

Please sign in to comment.