Skip to content

Commit

Permalink
Merge branch 'main' into pm_docs
Browse files Browse the repository at this point in the history
  • Loading branch information
k1nshuk authored Dec 1, 2023
2 parents 44dda0b + 66eb651 commit a04f14c
Show file tree
Hide file tree
Showing 8 changed files with 325 additions and 62 deletions.
20 changes: 20 additions & 0 deletions tutorials/BSM2.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,26 @@
"m.fs.DU.hydraulic_retention_time.fix(1800 * pyo.units.s)"
]
},
{
"cell_type": "markdown",
"id": "f9d6d962",
"metadata": {},
"source": [
"Similarly, the thickener unit includes the same equation, as well as an equation relating the thickener's dimensions. Here, we fix hydraulic retention time and thickener diameter to satisfy 0 degrees of freedom."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dfb8d9a3",
"metadata": {},
"outputs": [],
"source": [
"# Thickener unit\n",
"m.fs.TU.hydraulic_retention_time.fix(86400 * pyo.units.s)\n",
"m.fs.TU.diameter.fix(10 * pyo.units.m)"
]
},
{
"cell_type": "markdown",
"id": "663b9e78-c1b3-41b5-b9d1-fe1fc5acd274",
Expand Down
64 changes: 64 additions & 0 deletions watertap/costing/unit_models/thickener.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#################################################################################
# WaterTAP Copyright (c) 2020-2023, 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/"
#################################################################################

import pyomo.environ as pyo
from ..util import (
register_costing_parameter_block,
make_capital_cost_var,
)

"""
Ref: W. McGivney, S. Kawamura, Cost estimating manual for water treatment facilities, John Wiley & Sons, 2008. http://onlinelibrary.wiley.com/book/10.1002/9780470260036.
"""


def build_cost_param_block(blk):
# NOTE: costing data are for gravity sludge thickener for McGivney & Kawamura, 2008
blk.capital_a_parameter = pyo.Var(
initialize=4729.8,
doc="A parameter for capital cost",
units=pyo.units.USD_2007 / (pyo.units.feet),
)
blk.capital_b_parameter = pyo.Var(
initialize=37068,
doc="B parameter for capital cost",
units=pyo.units.USD_2007,
)


@register_costing_parameter_block(
build_rule=build_cost_param_block,
parameter_block_name="thickener",
)
def cost_thickener(blk, cost_electricity_flow=True):
"""
Gravity Sludge Thickener costing method
"""
make_capital_cost_var(blk)
cost_blk = blk.costing_package.thickener
t0 = blk.flowsheet().time.first()
x = diameter = pyo.units.convert(blk.unit_model.diameter, to_units=pyo.units.feet)
blk.capital_cost_constraint = pyo.Constraint(
expr=blk.capital_cost
== pyo.units.convert(
cost_blk.capital_a_parameter * x + cost_blk.capital_b_parameter,
to_units=blk.costing_package.base_currency,
)
)
if cost_electricity_flow:
blk.costing_package.cost_flow(
pyo.units.convert(
blk.unit_model.electricity_consumption[t0],
to_units=pyo.units.kW,
),
"electricity",
)
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,10 @@ def set_operating_conditions(m):
# Dewatering Unit - fix either HRT or volume.
m.fs.DU.hydraulic_retention_time.fix(1800 * pyo.units.s)

# Thickener unit
m.fs.TU.hydraulic_retention_time.fix(86400 * pyo.units.s)
m.fs.TU.diameter.fix(10 * pyo.units.m)


def initialize_system(m):
# Initialize flowsheet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Var,
value,
Constraint,
Expression,
Objective,
TransformationFactory,
assert_optimal_termination,
Expand Down Expand Up @@ -71,7 +72,7 @@ def main():
)
m.fs.EDstack.voltage_applied[0].fix(10)
m.fs.recovery_vol_H2O.fix(0.7)
condition_base(m)
_condition_base(m)

# Initialize and solve the model
initialize_system(m, solver=solver)
Expand All @@ -88,9 +89,6 @@ def main():
* ed.current_dens_lim_x[0, 0].value
* 1.5
)
m.fs.prod.properties[0].conc_mol_phase_comp["Liq", "Na_+"].fix(34.188 * 0.2)
m.fs.EDstack.voltage_applied[0].unfix()
solve(m, solver=solver, tee=False)
m.fs.EDstack.voltage_applied[0].unfix()
m.fs.EDstack.voltage_applied[0].setlb(0.1)
m.fs.EDstack.voltage_applied[0].setub(ulim)
Expand Down Expand Up @@ -189,56 +187,53 @@ def build():
units=pyunits.dimensionless,
doc="flowsheet level water recovery calculated by volumeric flow rate",
)
m.fs.mem_area = Var(
initialize=1,
bounds=(0, 1e3),
units=pyunits.meter**2,
doc="Total membrane area for cem (or aem) in one stack",
)
m.fs.product_salinity = Var(
initialize=1,
bounds=(0, 1000),
units=pyunits.kg * pyunits.meter**-3,
doc="Salinity of the product water",
)
m.fs.disposal_salinity = Var(

m.fs.feed_salinity = Var(
initialize=1,
bounds=(0, 1e6),
units=pyunits.kg * pyunits.meter**-3,
doc="Salinity of disposal water",
domain=NonNegativeReals,
units=pyunits.kg * pyunits.m**-3,
doc="Salinity of feed solution. Tested only with NaCl.",
)

m.fs.eq_recovery_vol_H2O = Constraint(
expr=m.fs.recovery_vol_H2O
expr=m.fs.recovery_vol_H2O * m.fs.feed.properties[0].flow_vol_phase["Liq"]
== m.fs.prod.properties[0].flow_vol_phase["Liq"]
* m.fs.feed.properties[0].flow_vol_phase["Liq"] ** -1
)

m.fs.eq_electrodialysis_equal_flow = Constraint(
expr=m.fs.EDstack.diluate.properties[0, 0].flow_vol_phase["Liq"]
== m.fs.EDstack.concentrate.properties[0, 0].flow_vol_phase["Liq"]
)

m.fs.eq_product_salinity = Constraint(
expr=m.fs.product_salinity
m.fs.eq_feed_salinity = Constraint(
expr=m.fs.feed_salinity
== sum(
m.fs.feed.properties[0].conc_mass_phase_comp["Liq", j]
for j in m.fs.properties.ion_set
)
)
m.fs.product_salinity = Expression(
expr=sum(
m.fs.prod.properties[0].conc_mass_phase_comp["Liq", j]
for j in m.fs.properties.ion_set | m.fs.properties.solute_set
for j in m.fs.properties.solute_set
)
)
m.fs.eq_disposal_salinity = Constraint(
expr=m.fs.disposal_salinity
== sum(
m.fs.disposal_salinity = Expression(
expr=sum(
m.fs.disp.properties[0].conc_mass_phase_comp["Liq", j]
for j in m.fs.properties.ion_set | m.fs.properties.solute_set
for j in m.fs.properties.solute_set
)
)

m.fs.eq_mem_area = Constraint(
expr=m.fs.mem_area
== m.fs.EDstack.cell_width
m.fs.mem_area = Expression(
expr=m.fs.EDstack.cell_width
* m.fs.EDstack.cell_length
* m.fs.EDstack.cell_pair_num
)

m.fs.voltage_per_cp = Expression(
expr=m.fs.EDstack.voltage_applied[0] / m.fs.EDstack.cell_pair_num
)

# Add Arcs
m.fs.arc0 = Arc(source=m.fs.feed.outlet, destination=m.fs.sepa0.inlet)
m.fs.arc1b = Arc(source=m.fs.sepa0.to_dil_in, destination=m.fs.pump1.inlet)
Expand All @@ -262,13 +257,13 @@ def build():
return m


def condition_base(m):
def _condition_base(m):
# ---specifications---
# Here is simulated a scenario of a defined EDstack and
# specific water recovery and product salinity.
m.fs.feed.properties[0].pressure.fix(101325)
m.fs.feed.properties[0].temperature.fix(298.15)
m.fs.pump1.control_volume.properties_in[0].pressure.fix(101325)
m.fs.pump0.control_volume.properties_in[0].pressure.fix(101325)
m.fs.pump1.efficiency_pump.fix(0.8)
m.fs.pump0.efficiency_pump.fix(0.8)

Expand Down Expand Up @@ -310,7 +305,7 @@ def condition_base(m):
m.fs.EDstack.current_utilization.fix(1)
m.fs.EDstack.diffus_mass.fix(1.6e-9)

mstat.report_statistics(m)
assert mstat.degrees_of_freedom(m) == 0


def solve(m, solver=None, tee=True, check_termination=True):
Expand Down Expand Up @@ -344,6 +339,20 @@ def initialize_system(m, solver=None):
iscale.set_scaling_factor(m.fs.pump0.control_volume.work, 1e1)
iscale.set_scaling_factor(m.fs.pump1.control_volume.work, 1e1)
iscale.calculate_scaling_factors(m)
iscale.constraint_scaling_transform(
m.fs.eq_recovery_vol_H2O,
10 * iscale.get_scaling_factor(m.fs.feed.properties[0].flow_vol_phase["Liq"]),
)
iscale.constraint_scaling_transform(
m.fs.eq_electrodialysis_equal_flow,
10 * iscale.get_scaling_factor(m.fs.feed.properties[0].flow_vol_phase["Liq"]),
)
iscale.constraint_scaling_transform(
m.fs.eq_feed_salinity,
iscale.get_scaling_factor(
m.fs.feed.properties[0].conc_mass_phase_comp["Liq", "Na_+"]
),
)

# populate intitial properties throughout the system
m.fs.feed.initialize(optarg=optarg)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,19 @@ def ED1D1Stack_conc_recirc(self):
def test_specific_operating_conditions(self, ED1D1Stack_conc_recirc):
m = ED1D1Stack_conc_recirc
solver = get_solver()
# Testing a feeding salinity of 2g/L.
init_arg = {
("flow_vol_phase", ("Liq")): 5.2e-4,
("conc_mol_phase_comp", ("Liq", "Na_+")): 34.188,
("conc_mol_phase_comp", ("Liq", "Cl_-")): 34.188,
} # Corresponding to C_feed = 2g/L
}
m.fs.feed.properties.calculate_state(
init_arg,
hold_state=True,
)
m.fs.EDstack.voltage_applied[0].fix(10)
m.fs.recovery_vol_H2O.fix(0.7)
edfs.condition_base(m)
edfs._condition_base(m)
check_dof(m)

# Initialize and solve the model
Expand All @@ -66,9 +67,9 @@ def test_specific_operating_conditions(self, ED1D1Stack_conc_recirc):
assert value(m.fs.disposal_salinity) == pytest.approx(4.0223, rel=1e-3)
assert value(m.fs.mem_area) == pytest.approx(18.5338, rel=1e-3)
assert value(m.fs.costing.specific_energy_consumption) == pytest.approx(
0.1192, abs=0.001
0.1348, abs=0.001
)
assert value(m.fs.costing.LCOW) == pytest.approx(0.397526, rel=1e-3)
assert value(m.fs.costing.LCOW) == pytest.approx(0.3996, rel=1e-3)
assert value(m.fs.EDstack.inlet_concentrate.pressure[0]) == pytest.approx(
169278.127, rel=1e-3
)
Expand Down Expand Up @@ -114,24 +115,22 @@ def test_optimization(self):
) == pytest.approx(2.00, rel=1e-3)
assert value(m.fs.product_salinity) == pytest.approx(0.1000, rel=1e-3)
assert value(m.fs.disposal_salinity) == pytest.approx(6.4333, rel=1e-3)
assert value(m.fs.mem_area) == pytest.approx(13.8159, rel=1e-3)
assert value(m.fs.EDstack.cell_pair_num) == pytest.approx(14, rel=1e-8)
assert value(m.fs.EDstack.cell_length) == pytest.approx(5.0094, rel=1e-3)
assert value(m.fs.EDstack.voltage_applied[0]) == pytest.approx(
16.8753, rel=1e-3
)
assert value(m.fs.mem_area) == pytest.approx(15.1283, rel=1e-3)
assert value(m.fs.EDstack.cell_pair_num) == pytest.approx(16, rel=1e-8)
assert value(m.fs.EDstack.cell_length) == pytest.approx(4.800, rel=1e-3)
assert value(m.fs.EDstack.voltage_applied[0]) == pytest.approx(18.785, rel=1e-3)
assert value(m.fs.costing.specific_energy_consumption) == pytest.approx(
1.6044, rel=1e-3
1.7775, rel=1e-3
)
assert value(m.fs.costing.LCOW) == pytest.approx(0.5904, rel=1e-3)
assert value(m.fs.costing.LCOW) == pytest.approx(0.6254, rel=1e-3)
assert value(m.fs.EDstack.inlet_concentrate.pressure[0]) == pytest.approx(
916385.788228, rel=1e-3
784785.332, rel=1e-3
)
assert value(m.fs.EDstack.outlet_concentrate.pressure[0]) == pytest.approx(
101325.00, rel=1e-3
)
assert value(m.fs.EDstack.inlet_diluate.pressure[0]) == pytest.approx(
916385.788228, rel=1e-3
784785.332, rel=1e-3
)
assert value(m.fs.EDstack.outlet_diluate.pressure[0]) == pytest.approx(
101325.00, rel=1e-3
Expand Down
1 change: 0 additions & 1 deletion watertap/unit_models/tests/test_dewatering_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
DewateringType,
)
from idaes.core import UnitModelCostingBlock
from watertap.costing import WaterTAPCosting


__author__ = "Alejandro Garciadiego, Adam Atia"
Expand Down
Loading

0 comments on commit a04f14c

Please sign in to comment.