Skip to content

Commit

Permalink
Add NaCl recovery value to crystallizer model (#1120)
Browse files Browse the repository at this point in the history
* add NaCl value

* change opex domain

* update solutions

* update solutions

* update more solutions

* macOS fail fix trial

* Update watertap/unit_models/tests/test_crystallizer.py

Change to absolute tolerance for zeros.

Co-authored-by: Adam Atia <aatia@keylogic.com>

* Update watertap/unit_models/tests/test_crystallizer.py

Try GHA again; assert closer to 0

---------

Co-authored-by: Zhuoran Zhang <zhuoranzhang@Zhuorans-MBP.askey.com>
Co-authored-by: Zhuoran Zhang <zhuoranzhang@Zhuorans-MacBook-Pro.local>
Co-authored-by: bknueven <30801372+bknueven@users.noreply.github.com>
Co-authored-by: Adam Atia <aatia@keylogic.com>
  • Loading branch information
5 people authored Sep 8, 2023
1 parent 3b37c9c commit c49bf30
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 10 deletions.
12 changes: 12 additions & 0 deletions watertap/costing/units/crystallizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,15 @@ def build_crystallizer_cost_param_block(blk):
doc="Steam cost, Panagopoulos (2019)",
)

blk.NaCl_recovery_value = pyo.Var(
initialize=0,
units=pyo.units.USD_2018 / pyo.units.kg,
doc="Unit recovery value of NaCl",
)

costing = blk.parent_block()
costing.add_defined_flow("steam", blk.steam_cost)
costing.add_defined_flow("NaCl", blk.NaCl_recovery_value)


def cost_crystallizer(blk, cost_type=CrystallizerCostType.default):
Expand Down Expand Up @@ -135,6 +142,11 @@ def _cost_crystallizer_flows(blk):
"steam",
)

blk.costing_package.cost_flow(
blk.unit_model.solids.flow_mass_phase_comp[0, "Sol", "NaCl"],
"NaCl",
)


@register_costing_parameter_block(
build_rule=build_crystallizer_cost_param_block,
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 @@ -252,7 +252,7 @@ def build_process_costs(self):
)
self.total_operating_cost = pyo.Var(
initialize=1e3,
domain=pyo.NonNegativeReals,
domain=pyo.Reals,
doc="Total operating cost",
units=self.base_currency / self.base_period,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_flowsheet_NF():
) == pytest.approx(0.7667, rel=1e-3)
assert value(
m.fs.tb_pretrt_to_desal.properties_in[0].flow_mass_phase_comp["Liq", "Ca"]
) == pytest.approx(1.15808e-4, rel=1e-3)
) == pytest.approx(1.1636e-4, rel=1e-3)


@pytest.mark.component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,24 +72,24 @@ def test_ideal_naocl_chlorination():
def test_ideal_naocl_chlorination_full_block():
model = run_chlorination_block_example(fix_free_chlorine=True)
assert model.fs.ideal_naocl_mixer_unit.dosing_rate.value == pytest.approx(
1.7296113311683092e-09, rel=1e-3
9.505698559578499e-07, rel=1e-3
)
assert model.fs.ideal_naocl_mixer_unit.outlet.flow_mol[0].value == pytest.approx(
25.000025535888078, rel=1e-3
)
assert model.fs.ideal_naocl_mixer_unit.outlet.mole_frac_comp[
0, "OCl_-"
].value == pytest.approx(1.6123004572288052e-07, rel=1e-3)
].value == pytest.approx(5.107133802822922e-07, rel=1e-3)

assert model.fs.ideal_naocl_chlorination_unit.free_chlorine.value == pytest.approx(
2, rel=1e-3
)
assert model.fs.ideal_naocl_chlorination_unit.outlet.mole_frac_comp[
0, "OCl_-"
].value == pytest.approx(5.0027242332010015e-08, rel=1e-3)
].value == pytest.approx(4.5088044031726496e-07, rel=1e-3)
assert model.fs.ideal_naocl_chlorination_unit.outlet.mole_frac_comp[
0, "H_+"
].value == pytest.approx(1.49011416785194e-10, rel=1e-3)
].value == pytest.approx(5.4260427865375997e-11, rel=1e-3)


@pytest.mark.component
Expand Down
8 changes: 4 additions & 4 deletions watertap/examples/flowsheets/nf_dspmde/tests/test_nf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
def test_main():
m = main()
test_dict = {
"lcow": [m.fs.costing.LCOW, 0.16811587158493219],
"pressure": [m.fs.NF.pump.outlet.pressure[0] / 1e5, 6.56],
"area": [m.fs.NF.nfUnit.area, 285.6900547389303],
"lcow": [m.fs.costing.LCOW, 0.15058960529129017],
"pressure": [m.fs.NF.pump.outlet.pressure[0] / 1e5, 8.13],
"area": [m.fs.NF.nfUnit.area, 423.8956418211484],
"recovery": [
m.fs.NF.nfUnit.recovery_vol_phase[0.0, "Liq"] * 100,
73.47934090302432,
94.99999441324391,
],
}
for (model_result, testval) in test_dict.values():
Expand Down
29 changes: 29 additions & 0 deletions watertap/unit_models/tests/test_crystallizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,9 @@ def test_solution2_operatingcost(self, Crystallizer_frame_2):
assert pytest.approx(30666.67, rel=1e-3) == value(
m.fs.costing.aggregate_flow_costs["steam"]
)
assert pytest.approx(0, rel=1e-3) == value(
m.fs.costing.aggregate_flow_costs["NaCl"]
)

@pytest.mark.component
def test_solution2_operatingcost_steampressure(self, Crystallizer_frame_2):
Expand All @@ -589,3 +592,29 @@ def test_solution2_operatingcost_steampressure(self, Crystallizer_frame_2):
assert pytest.approx(21451.91, rel=1e-3) == value(
m.fs.costing.aggregate_flow_costs["steam"]
)
assert pytest.approx(0, abs=1e-6) == value(
m.fs.costing.aggregate_flow_costs["NaCl"]
)

@pytest.mark.component
def test_solution2_operatingcost_NaCl_revenue(self, Crystallizer_frame_2):
m = Crystallizer_frame_2
m.fs.costing.crystallizer.steam_pressure.fix(3)
m.fs.costing.crystallizer.NaCl_recovery_value.fix(-0.07)

results = solver.solve(m)

# Check for optimal solution
assert results.solver.termination_condition == TerminationCondition.optimal
assert results.solver.status == SolverStatus.ok

# Operating cost validation
assert pytest.approx(835.41, rel=1e-3) == value(
m.fs.costing.aggregate_flow_costs["electricity"]
)
assert pytest.approx(30666.67, rel=1e-3) == value(
m.fs.costing.aggregate_flow_costs["steam"]
)
assert pytest.approx(-187858.2, rel=1e-3) == value(
m.fs.costing.aggregate_flow_costs["NaCl"]
)

0 comments on commit c49bf30

Please sign in to comment.