From cbc888258347306aff73bc7771475b18f1075941 Mon Sep 17 00:00:00 2001 From: Adam Atia Date: Thu, 31 Oct 2024 13:02:39 -0400 Subject: [PATCH] MCAS w/generic crystallizer (#1487) * paste Mayo's surrogate_crystallizer unit in raw form * start code cleanup in surrogate crystallizer, add vapor prop pack config option, rename class * git rid of self.vap_prop instances * paste in seawater relationship for enth_mass_phase * pass tests for enth_mass_phase implementation in MCAS * check value of enthalpy in test * bring in test code * get test crystallizer surrogate file to pass * only construct enthalpy params once * blk * add surrogate files * clean up imports and other code * track costing file * bk * raise config error instead of string * remove unused imports * changed filename and started cleanup * make folder for crystallizer surrogate defaults to test; finish cleanup * blk * add folder * cleanup imports and copyright headers * fix file path * updated some var names to make a bit more user friendly and closer to idaes naming; also described a config option that was unclear * blk * delete erronous code in mcas during initialization of enthalpy * fix issue with file path? * blk * black * move surrogate defaults * refine test file * add pressure_sat and add some scaling * black * add to MCAS tech ref; revise subtlety in sw prop model tech ref * revise docs * fix rst * adjust th equation in rst * remove units inclusion for D * fix fraction * fix path issue * blk * fix relatiosnhip * try to fix path issues now having created init files * revise doc * tinker with docs * cleanup * add init files! * regression tests for enth flow and pressure sat --- .../property_models/mc_aq_sol.rst | 18 +- .../property_models/seawater.rst | 12 +- .../unit_models/surrogate_crystallizer.py | 189 ++++++ watertap/data/surrogate_defaults/__init__.py | 0 .../Anhydrite_g.json | 1 + .../Calcite_g.json | 1 + .../Glauberite_g.json | 1 + .../Halite_g.json | 1 + .../Vapor_Pressure.json | 1 + .../__init__.py | 0 .../multicomp_aq_sol_prop_pack.py | 355 ++++++++++- .../tests/test_multicomp_aq_sol_prop_pack.py | 17 +- .../unit_models/surrogate_crystallizer.py | 568 ++++++++++++++++++ .../tests/test_surrogate_crystallizer.py | 231 +++++++ 14 files changed, 1366 insertions(+), 29 deletions(-) create mode 100644 watertap/costing/unit_models/surrogate_crystallizer.py create mode 100644 watertap/data/surrogate_defaults/__init__.py create mode 100644 watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Anhydrite_g.json create mode 100644 watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Calcite_g.json create mode 100644 watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Glauberite_g.json create mode 100644 watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Halite_g.json create mode 100644 watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Vapor_Pressure.json create mode 100644 watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/__init__.py create mode 100644 watertap/unit_models/surrogate_crystallizer.py create mode 100644 watertap/unit_models/tests/test_surrogate_crystallizer.py diff --git a/docs/technical_reference/property_models/mc_aq_sol.rst b/docs/technical_reference/property_models/mc_aq_sol.rst index d3a5f5f2a3..78bbd0a705 100644 --- a/docs/technical_reference/property_models/mc_aq_sol.rst +++ b/docs/technical_reference/property_models/mc_aq_sol.rst @@ -70,7 +70,7 @@ Parameters .. csv-table:: :header: "Description", "Symbol", "Parameter", "Index", "Units" - "Component molecular weight :sup:`1`", ":math:`m_N`", "mw_comp", "[j]", ":math:`\text{kg mol}^{-1}`" + "Component molecular weight :sup:`1`", ":math:`MW`", "mw_comp", "[j]", ":math:`\text{kg mol}^{-1}`" "Stokes radius of solute", ":math:`r_h`", "radius_stokes_comp", "[j]", ":math:`\text{m}`" "Molar volume of solute", ":math:`V`", "molar_volume_phase_comp", "[p, j]", ":math:`\text{m}^3 \text{ mol}^{-1}`" "Dynamic viscosity", ":math:`\mu`", "visc_d_phase", "[p]", ":math:`\text{Pa s}`" @@ -112,7 +112,11 @@ Properties "Debye-Huckel constant A", ":math:`A`", "deby_huckel_constant", "none", ":math:`\text{dimensionless}`" "Ionic Strength", ":math:`I`", "ionic_strength_molal", "none", ":math:`\text{mol kg}^{-1}`" "Mass diffusivity of solute", ":math:`D`", "diffus_phase_comp", "[p, j]", ":math:`\text{m}^2 \text{ s}^{-1}`" - + "Specific enthalpy", ":math:`\widehat{H}`", "enth_mass_phase", "[p]", ":math:`\text{J/kg}`" + "Enthalpy flow", ":math:`H`", "enth_flow", "None", ":math:`\text{J/s}`" + "Saturation pressure", ":math:`P_v`", "pressure_sat", "None", ":math:`\text{Pa}`" + "Total hardness as CaCO3",":math:`TH`","total_hardness", "None", ":math:`\text{mg/L}`" + "Total dissolved solids",":math:`TDS`","total_dissolved_solids", "None", ":math:`\text{mg/L}`" Relationships @@ -137,7 +141,13 @@ Relationships "Phase electrical conductivity", ":math:`\lambda=\Lambda\sum_{j\in cation}{\left|z_j\right|n_j}`" "Debye-Huckel constant", ":math:`A=\frac{\left(2 \pi N_A\right)^{0.5}}{log(10)} \left(\frac{\textbf{e}^2}{4 \pi \epsilon \epsilon_0 kT}\right)^{\frac{3}{2}}`" "Ionic strength", ":math:`I=0.5\sum_{j\in ion}{z_j^2b_j}`" - "Component mass diffusivity :sup:`5`", ":math:`D\text{ specified in data argument}` or :math:`D \text{ }[\text{m}^2 \text{ s}^{-1}]=\frac{\chi_{1}}{(\mu \text{ }[\text{cP}])^{\chi_{2}}(V \text{ }[\text{cm}^3 \text{ mol}^{-1}])^{\chi_{3}}}`" + "Component mass diffusivity :sup:`5`", ":math:`D\text{ specified in data argument}` or :math:`D \text{ }=\frac{\chi_{1}}{(\mu \text{ }[\text{cP}])^{\chi_{2}}(V \text{ }[\text{cm}^3 \text{ mol}^{-1}])^{\chi_{3}}}`" + "Specific enthalpy", "Equations 25-27 in Nayar et al. (2016)" + "Enthalpy flow", ":math:`H = \sum_{j} M_j \cdotp \widehat{H}`" + "Saturation pressure", "Equations 5 and 6 in Nayar et al. (2016)" + "Total hardness as CaCO3",":math:`TH = \sum_{j \in \text{polyvalent cation set}} {n_j z_j} \frac{MW_{CaCO3}}{z_{CaCO3}}`" + "Total dissolved solids",":math:`TDS = \sum_{j \in \text{ion set}} m_j`" + .. note:: @@ -166,7 +176,7 @@ Physical/chemical constants Scaling ------- -A comprehensive scaling factor calculation method is coded in this property package. Among the state variables (:math:`N, T, \text{and } p`), default scaling factors for :math:`T` and :math:`p` were set and do not need users' input, while, for :math:`N`, usually require a user input via an interface. The coding interface to set defalut scaling factor for :math:`N` and call the scaling calculation for other variables is the following. +A comprehensive scaling factor calculation method is coded in this property package. Among the state variables (:math:`N, T, \text{and } p`), default scaling factors for :math:`T` and :math:`p` were set and do not need users' input, while, for :math:`N`, usually require a user input via an interface. The coding interface to set default scaling factor for :math:`N` and call the scaling calculation for other variables is the following. .. code-block:: diff --git a/docs/technical_reference/property_models/seawater.rst b/docs/technical_reference/property_models/seawater.rst index ea369b99e7..715eebcbc9 100644 --- a/docs/technical_reference/property_models/seawater.rst +++ b/docs/technical_reference/property_models/seawater.rst @@ -51,18 +51,14 @@ Properties "Latent heat of vaporization", ":math:`h_{vap}`", "dh_vap_mass", "None", ":math:`\text{J/kg}`" "Diffusivity", ":math:`D`", "diffus_phase_comp", "[p]", ":math:`\text{m}^2\text{/s}`" "Boiling point elevation", ":math:`BPE`", "boiling_point_elevation_phase", "[p]", ":math:`\text{K}`" - - -**The properties make use of the average molecular weight of sea salt, ≈ 31.40 g/mol, reported in the Reference-Composition Salinity Scale (Millero et al., 2008) to convert to moles.** - -.. csv-table:: - :header: "Description", "Symbol", "Variable", "Index", "Units" - "Component mole flowrate", ":math:`N_j`", "flow_mol_phase_comp", "[p, j]", ":math:`\text{mole/s}`" "Component mole fraction", ":math:`y_j`", "mole_frac_phase_comp", "[p, j]", ":math:`\text{dimensionless}`" "Molality", ":math:`Cm`", "molality_phase_comp", "['TDS']", ":math:`\text{mole/kg}`" "Osmotic pressure", ":math:`\pi`", "pressure_osm_phase", "None", ":math:`\text{Pa}`" +**The properties make use of the average molecular weight of sea salt, ≈ 31.40 g/mol, reported in the Reference-Composition Salinity Scale (Millero et al., 2008) to convert to moles.** + + Relationships ------------- .. csv-table:: @@ -87,8 +83,6 @@ Relationships "Diffusivity", "Equation 6 in Bartholomew et al. (2019)" "Boiling point elevation", "Equation 36 in Sharqawy et al. (2010)" - - Note: Osmotic pressure calculation (based on equation 48 in Nayar et al. (2016)) uses the density of water as a function of temperature (:math:`\rho_w`) and the ideal gas constant (:math:`R\text{, 8.314 J/mol}\cdotp\text{K}`), in addition to previously defined variables. Scaling diff --git a/watertap/costing/unit_models/surrogate_crystallizer.py b/watertap/costing/unit_models/surrogate_crystallizer.py new file mode 100644 index 0000000000..f7acc9899e --- /dev/null +++ b/watertap/costing/unit_models/surrogate_crystallizer.py @@ -0,0 +1,189 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2024, 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 watertap.costing.util import ( + register_costing_parameter_block, + make_capital_cost_var, +) + + +def build_surrogate_crystallizer_cost_param_block(blk): + + blk.steam_pressure = pyo.Var( + initialize=3, + units=pyo.units.bar, + doc="Steam pressure (gauge) for crystallizer heating: 3 bar default based on Dutta example", + ) + + blk.efficiency_pump = pyo.Var( + initialize=0.7, + units=pyo.units.dimensionless, + doc="Crystallizer pump efficiency - assumed", + ) + + blk.pump_head_height = pyo.Var( + initialize=1, + units=pyo.units.m, + doc="Crystallizer pump head height - assumed, unvalidated", + ) + + # Crystallizer operating cost information from literature + blk.fob_unit_cost = pyo.Var( + initialize=675000, + doc="Forced circulation crystallizer reference free-on-board cost (Woods, 2007)", + units=pyo.units.USD_2007, + ) + + blk.ref_capacity = pyo.Var( + initialize=1, + doc="Forced circulation crystallizer reference crystal capacity (Woods, 2007)", + units=pyo.units.kg / pyo.units.s, + ) + + blk.ref_exponent = pyo.Var( + initialize=0.53, + doc="Forced circulation crystallizer cost exponent factor (Woods, 2007)", + units=pyo.units.dimensionless, + ) + + blk.iec_percent = pyo.Var( + initialize=1.43, + doc="Forced circulation crystallizer installed equipment cost (Diab and Gerogiorgis, 2017)", + units=pyo.units.dimensionless, + ) + + blk.steam_cost = pyo.Var( + initialize=0.004, + units=pyo.units.USD_2018 / (pyo.units.meter**3), + doc="Steam cost, Panagopoulos (2019)", + ) + + costing = blk.parent_block() + costing.register_flow_type("steam", blk.steam_cost) + + +def cost_surrogate_crystallizer(blk): + """ + Function for costing the surrogate crystallizer by the mass flow of produced crystals. + The operating cost model assumes that heat is supplied via condensation of saturated steam (see Dutta et al.) + """ + cost_crystallizer_by_crystal_mass(blk) + + +def _cost_crystallizer_flows(blk): + blk.costing_package.cost_flow( + pyo.units.convert( + (blk.unit_model.heat_required / _compute_steam_properties(blk)), + to_units=pyo.units.m**3 / pyo.units.s, + ), + "steam", + ) + + +@register_costing_parameter_block( + build_rule=build_surrogate_crystallizer_cost_param_block, + parameter_block_name="surrogate_crystallizer", +) +def cost_crystallizer_by_crystal_mass(blk): + """ + Mass-based capital cost for FC crystallizer + """ + make_capital_cost_var(blk) + blk.costing_package.add_cost_factor(blk, "TIC") + blk.cost_factor = 1 # blk.costing_package.add_cost_factor(blk, "TIC") + blk.capital_cost_constraint = pyo.Constraint( + expr=blk.capital_cost + == blk.cost_factor + * pyo.units.convert( + ( + blk.costing_package.surrogate_crystallizer.iec_percent + * blk.costing_package.surrogate_crystallizer.fob_unit_cost + * ( + blk.unit_model.flow_mass_sol_total + / blk.costing_package.surrogate_crystallizer.ref_capacity + ) + ** blk.costing_package.surrogate_crystallizer.ref_exponent + ), + to_units=blk.costing_package.base_currency, + ) + ) + _cost_crystallizer_flows(blk) + + +def _compute_steam_properties(blk): + """ + Function for computing saturated steam properties for thermal heating estimation. + + Args: + pressure_sat: Steam gauge pressure in bar + + Out: + Steam thermal capacity (latent heat of condensation * density) in kJ/m3 + """ + pressure_sat = blk.costing_package.surrogate_crystallizer.steam_pressure + # 1. Compute saturation temperature of steam: computed from El-Dessouky expression + tsat_constants = [ + 42.6776 * pyo.units.K, + -3892.7 * pyo.units.K, + 1000 * pyo.units.kPa, + -9.48654 * pyo.units.dimensionless, + ] + psat = ( + pyo.units.convert(pressure_sat, to_units=pyo.units.kPa) + + 101.325 * pyo.units.kPa + ) + temperature_sat = tsat_constants[0] + tsat_constants[1] / ( + pyo.log(psat / tsat_constants[2]) + tsat_constants[3] + ) + + # 2. Compute latent heat of condensation/vaporization: computed from Sharqawy expression + t = temperature_sat - 273.15 * pyo.units.K + enth_mass_units = pyo.units.J / pyo.units.kg + t_inv_units = pyo.units.K**-1 + dh_constants = [ + 2.501e6 * enth_mass_units, + -2.369e3 * enth_mass_units * t_inv_units**1, + 2.678e-1 * enth_mass_units * t_inv_units**2, + -8.103e-3 * enth_mass_units * t_inv_units**3, + -2.079e-5 * enth_mass_units * t_inv_units**4, + ] + dh_vap = ( + dh_constants[0] + + dh_constants[1] * t + + dh_constants[2] * t**2 + + dh_constants[3] * t**3 + + dh_constants[4] * t**4 + ) + dh_vap = pyo.units.convert(dh_vap, to_units=pyo.units.kJ / pyo.units.kg) + + # 3. Compute specific volume: computed from Affandi expression (Eq 5) + t_critical = 647.096 * pyo.units.K + t_red = temperature_sat / t_critical # Reduced temperature + sp_vol_constants = [ + -7.75883 * pyo.units.dimensionless, + 3.23753 * pyo.units.dimensionless, + 2.05755 * pyo.units.dimensionless, + -0.06052 * pyo.units.dimensionless, + 0.00529 * pyo.units.dimensionless, + ] + log_sp_vol = ( + sp_vol_constants[0] + + sp_vol_constants[1] * (pyo.log(1 / t_red)) ** 0.4 + + sp_vol_constants[2] / (t_red**2) + + sp_vol_constants[3] / (t_red**4) + + sp_vol_constants[4] / (t_red**5) + ) + sp_vol = pyo.exp(log_sp_vol) * pyo.units.m**3 / pyo.units.kg + + # 4. Return specific energy: density * latent heat + return dh_vap / sp_vol diff --git a/watertap/data/surrogate_defaults/__init__.py b/watertap/data/surrogate_defaults/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Anhydrite_g.json b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Anhydrite_g.json new file mode 100644 index 0000000000..25d7e2b5e3 --- /dev/null +++ b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Anhydrite_g.json @@ -0,0 +1 @@ +{"model_encoding": {"Anhydrite_g": {"attr": {"x_data_columns": ["Temperature", "Evaporation percent"], "x_data": [[0.0, 0.0], [0.043478260869565216, 0.4918032786885246], [0.08695652173913043, 0.2459016393442623], [0.11594202898550725, 0.7540983606557377], [0.15942028985507245, 0.13114754098360656], [0.2028985507246377, 0.6229508196721312], [0.2463768115942029, 0.3770491803278688], [0.2753623188405797, 0.8688524590163934], [0.3188405797101449, 0.06557377049180328], [0.36231884057971014, 0.5573770491803278], [0.4057971014492754, 0.3114754098360656], [0.43478260869565216, 0.819672131147541], [0.4782608695652174, 0.18032786885245902], [0.5217391304347826, 0.6885245901639344], [0.5652173913043478, 0.4426229508196721], [0.6086956521739131, 0.9344262295081968], [0.6376811594202898, 0.03278688524590164], [0.6811594202898551, 0.5245901639344263], [0.7246376811594203, 0.2786885245901639], [0.7536231884057971, 0.7868852459016393], [0.7971014492753623, 0.16393442622950818], [0.8405797101449275, 0.6557377049180327], [0.8840579710144928, 0.4098360655737705], [0.9130434782608695, 0.9016393442622951], [0.9565217391304348, 0.09836065573770492], [0.17391304347826086, 1.0], [0.0, 0.9672131147540983], [0.4492753623188406, 1.0], [1.0, 0.0], [0.028985507246376812, 0.9672131147540983], [0.2463768115942029, 0.9508196721311475], [0.4057971014492754, 0.9344262295081968], [0.3333333333333333, 0.9836065573770492], [0.21739130434782608, 0.9508196721311475], [0.42028985507246375, 0.9344262295081968], [0.2028985507246377, 0.9508196721311475], [0.4492753623188406, 0.9344262295081968], [0.18840579710144928, 0.9508196721311475], [0.2463768115942029, 0.9836065573770492], [0.0, 0.7868852459016393], [0.0, 0.9836065573770492], [0.36231884057971014, 0.9344262295081968], [0.17391304347826086, 0.9508196721311475], [0.4782608695652174, 0.9344262295081968], [0.34782608695652173, 0.9344262295081968], [0.30434782608695654, 0.7868852459016393], [0.15942028985507245, 0.9508196721311475], [1.0, 0.9180327868852459], [0.07246376811594203, 0.8852459016393442], [1.0, 0.7540983606557377], [0.18840579710144928, 0.9016393442622951], [0.5797101449275363, 0.8524590163934426], [0.0, 0.9016393442622951], [0.391304347826087, 0.8852459016393442], [0.028985507246376812, 0.9836065573770492], [0.4782608695652174, 0.8852459016393442], [0.2318840579710145, 0.9180327868852459], [0.028985507246376812, 0.9344262295081968], [0.2028985507246377, 0.8360655737704918], [0.14492753623188406, 0.9180327868852459], [0.10144927536231885, 0.9508196721311475], [0.7971014492753623, 0.9344262295081968], [0.7101449275362319, 0.8852459016393442], [0.0, 0.9508196721311475], [0.3188405797101449, 0.9016393442622951], [0.18840579710144928, 0.9344262295081968], [1.0, 0.8524590163934426], [0.08695652173913043, 0.9344262295081968], [0.43478260869565216, 0.9180327868852459], [0.6086956521739131, 0.7704918032786885], [0.5942028985507246, 0.9016393442622951], [0.36231884057971014, 0.9180327868852459], [0.014492753623188406, 0.9508196721311475], [0.2463768115942029, 0.9344262295081968], [0.30434782608695654, 0.9344262295081968]], "centres": [[0.0, 0.0], [0.043478260869565216, 0.4918032786885246], [0.08695652173913043, 0.2459016393442623], [0.11594202898550725, 0.7540983606557377], [0.15942028985507245, 0.13114754098360656], [0.2028985507246377, 0.6229508196721312], [0.2463768115942029, 0.3770491803278688], [0.2753623188405797, 0.8688524590163934], [0.3188405797101449, 0.06557377049180328], [0.36231884057971014, 0.5573770491803278], [0.4057971014492754, 0.3114754098360656], [0.43478260869565216, 0.819672131147541], [0.4782608695652174, 0.18032786885245902], [0.5217391304347826, 0.6885245901639344], [0.5652173913043478, 0.4426229508196721], [0.6086956521739131, 0.9344262295081968], [0.6376811594202898, 0.03278688524590164], [0.6811594202898551, 0.5245901639344263], [0.7246376811594203, 0.2786885245901639], [0.7536231884057971, 0.7868852459016393], [0.7971014492753623, 0.16393442622950818], [0.8405797101449275, 0.6557377049180327], [0.8840579710144928, 0.4098360655737705], [0.9130434782608695, 0.9016393442622951], [0.9565217391304348, 0.09836065573770492], [0.17391304347826086, 1.0], [0.0, 0.9672131147540983], [0.4492753623188406, 1.0], [1.0, 0.0], [0.028985507246376812, 0.9672131147540983], [0.2463768115942029, 0.9508196721311475], [0.4057971014492754, 0.9344262295081968], [0.3333333333333333, 0.9836065573770492], [0.21739130434782608, 0.9508196721311475], [0.42028985507246375, 0.9344262295081968], [0.2028985507246377, 0.9508196721311475], [0.4492753623188406, 0.9344262295081968], [0.18840579710144928, 0.9508196721311475], [0.2463768115942029, 0.9836065573770492], [0.0, 0.7868852459016393], [0.0, 0.9836065573770492], [0.36231884057971014, 0.9344262295081968], [0.17391304347826086, 0.9508196721311475], [0.4782608695652174, 0.9344262295081968], [0.34782608695652173, 0.9344262295081968], [0.30434782608695654, 0.7868852459016393], [0.15942028985507245, 0.9508196721311475], [1.0, 0.9180327868852459], [0.07246376811594203, 0.8852459016393442], [1.0, 0.7540983606557377], [0.18840579710144928, 0.9016393442622951], [0.5797101449275363, 0.8524590163934426], [0.0, 0.9016393442622951], [0.391304347826087, 0.8852459016393442], [0.028985507246376812, 0.9836065573770492], [0.4782608695652174, 0.8852459016393442], [0.2318840579710145, 0.9180327868852459], [0.028985507246376812, 0.9344262295081968], [0.2028985507246377, 0.8360655737704918], [0.14492753623188406, 0.9180327868852459], [0.10144927536231885, 0.9508196721311475], [0.7971014492753623, 0.9344262295081968], [0.7101449275362319, 0.8852459016393442], [0.0, 0.9508196721311475], [0.3188405797101449, 0.9016393442622951], [0.18840579710144928, 0.9344262295081968], [1.0, 0.8524590163934426], [0.08695652173913043, 0.9344262295081968], [0.43478260869565216, 0.9180327868852459], [0.6086956521739131, 0.7704918032786885], [0.5942028985507246, 0.9016393442622951], [0.36231884057971014, 0.9180327868852459], [0.014492753623188406, 0.9508196721311475], [0.2463768115942029, 0.9344262295081968], [0.30434782608695654, 0.9344262295081968]], "basis_function": "cubic", "weights": [[-0.6732717955073102], [4.088039390336431], [3.5307136619731994], [-31.611835836204435], [0.8125543937821926], [1.4353762144223097], [-7.9215455574810605], [-215.96243684665782], [0.5801902190589648], [-0.18132308657453033], [1.2243101177450115], [-163.3565490365561], [-1.700031299703339], [4.903310961197916], [-1.0988539182046106], [-402.09264023764695], [2.4263157521229806], [-2.0533998716809494], [-1.864754385284016], [-15.06295268663419], [-2.599953714323168], [-7.271530363845078], [1.669355796464635], [63.075018553975866], [8.960635467884268], [1351.8927271764808], [-6056.300816453516], [527.6465849882121], [-5.651343945265079], [-5698.920302879956], [-2764.265450389739], [-354.7100057266871], [575.3067481675504], [-727.3611080244159], [-703.3448556084011], [-787.7032238724911], [-830.0821944718095], [-1097.5198575695774], [786.901475336273], [15.556113513885325], [2603.9664035995766], [-899.877867148789], [-964.2443007258508], [-470.73261757567883], [-910.9375749951078], [-24.090916213218676], [-1080.417078192017], [-113.44474267472162], [-416.9529044054785], [-10.44361848060095], [-430.4457772762919], [-83.27101999425224], [-27.93134069647148], [-58.88892452806865], [3453.780562948067], [352.3843156908424], [809.838661818357], [1255.9784722965903], [-142.7045030246078], [1053.6040095435383], [-1453.6993890694764], [-62.105639481894144], [109.4727723115501], [2107.288107008953], [630.6437979001136], [2672.0505291172753], [94.71086703086257], [1390.2688863726053], [1476.1280284418103], [-26.17117500161121], [441.1680239110773], [1451.0828588053885], [2385.08607756828], [1920.5913915104868], [-495.9988864272275]], "sigma": 0.0, "regularization_parameter": 1e-05, "rmse": 0.013684536722487104, "R2": 0.9948782429686583, "x_data_min": [[303.15, 30.0]], "x_data_max": [[372.15, 91.0]], "y_data_min": [0.0], "y_data_max": [33.559020776980006]}, "map": {"x_data_columns": "list", "x_data": "numpy", "centres": "numpy", "basis_function": "str", "weights": "numpy", "sigma": "str", "regularization_parameter": "str", "rmse": "str", "R2": "str", "x_data_min": "numpy", "x_data_max": "numpy", "y_data_min": "numpy", "y_data_max": "numpy"}}}, "input_labels": ["Temperature", "Evaporation percent"], "output_labels": ["Anhydrite_g"], "input_bounds": {"Temperature": [303.15, 372.15], "Evaporation percent": [30, 98]}, "surrogate_type": "rbf"} \ No newline at end of file diff --git a/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Calcite_g.json b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Calcite_g.json new file mode 100644 index 0000000000..29d6e29aaa --- /dev/null +++ b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Calcite_g.json @@ -0,0 +1 @@ +{"model_encoding": {"Calcite_g": {"attr": {"x_data_columns": ["Temperature", "Evaporation percent"], "x_data": [[0.0, 0.0], [0.043478260869565216, 0.5], [0.08695652173913043, 0.25], [0.11594202898550725, 0.7666666666666667], [0.15942028985507245, 0.13333333333333333], [0.2028985507246377, 0.6333333333333333], [0.2463768115942029, 0.38333333333333336], [0.2753623188405797, 0.8833333333333333], [0.3188405797101449, 0.06666666666666667], [0.36231884057971014, 0.5666666666666667], [0.4057971014492754, 0.31666666666666665], [0.43478260869565216, 0.8333333333333334], [0.4782608695652174, 0.18333333333333332], [0.5217391304347826, 0.7], [0.5652173913043478, 0.45], [0.6086956521739131, 0.95], [0.6376811594202898, 0.03333333333333333], [0.6811594202898551, 0.5333333333333333], [0.7246376811594203, 0.2833333333333333], [0.7536231884057971, 0.8], [0.7971014492753623, 0.16666666666666666], [0.8405797101449275, 0.6666666666666666], [0.8840579710144928, 0.4166666666666667], [0.9130434782608695, 0.9166666666666666], [0.9565217391304348, 0.1], [1.0, 0.0], [1.0, 0.05], [0.9420289855072463, 0.0], [0.8985507246376812, 0.03333333333333333], [0.6956521739130435, 0.016666666666666666], [0.8695652173913043, 0.0], [0.9855072463768116, 0.05], [1.0, 0.2], [1.0, 0.38333333333333336], [0.9855072463768116, 0.0], [0.0, 1.0], [0.9710144927536232, 0.05], [0.927536231884058, 0.0], [0.8840579710144928, 0.03333333333333333], [0.8985507246376812, 0.0], [0.9130434782608695, 0.03333333333333333], [0.7101449275362319, 0.016666666666666666], [0.6376811594202898, 0.13333333333333333], [0.855072463768116, 0.0], [0.8695652173913043, 0.03333333333333333], [0.8840579710144928, 0.0], [1.0, 0.18333333333333332], [0.927536231884058, 0.2833333333333333], [0.927536231884058, 0.13333333333333333], [1.0, 0.9333333333333333], [0.927536231884058, 0.03333333333333333], [0.9130434782608695, 0.0], [0.855072463768116, 0.03333333333333333], [0.7971014492753623, 0.0], [0.7681159420289855, 0.016666666666666666], [0.9565217391304348, 0.05], [0.9565217391304348, 0.0], [0.9130434782608695, 0.11666666666666667], [0.782608695652174, 0.0], [0.6666666666666666, 0.0], [0.6666666666666666, 0.1], [0.9420289855072463, 0.03333333333333333], [0.782608695652174, 0.016666666666666666], [0.7681159420289855, 0.0], [0.9420289855072463, 0.11666666666666667], [0.8840579710144928, 0.21666666666666667], [1.0, 0.03333333333333333], [0.7536231884057971, 0.016666666666666666], [0.8115942028985508, 0.0], [0.8405797101449275, 0.03333333333333333], [0.8840579710144928, 0.1], [0.9710144927536232, 0.0], [0.0, 0.13333333333333333], [0.782608695652174, 0.06666666666666667], [0.8405797101449275, 0.2833333333333333]], "centres": [[0.0, 0.0], [0.043478260869565216, 0.5], [0.08695652173913043, 0.25], [0.11594202898550725, 0.7666666666666667], [0.15942028985507245, 0.13333333333333333], [0.2028985507246377, 0.6333333333333333], [0.2463768115942029, 0.38333333333333336], [0.2753623188405797, 0.8833333333333333], [0.3188405797101449, 0.06666666666666667], [0.36231884057971014, 0.5666666666666667], [0.4057971014492754, 0.31666666666666665], [0.43478260869565216, 0.8333333333333334], [0.4782608695652174, 0.18333333333333332], [0.5217391304347826, 0.7], [0.5652173913043478, 0.45], [0.6086956521739131, 0.95], [0.6376811594202898, 0.03333333333333333], [0.6811594202898551, 0.5333333333333333], [0.7246376811594203, 0.2833333333333333], [0.7536231884057971, 0.8], [0.7971014492753623, 0.16666666666666666], [0.8405797101449275, 0.6666666666666666], [0.8840579710144928, 0.4166666666666667], [0.9130434782608695, 0.9166666666666666], [0.9565217391304348, 0.1], [1.0, 0.0], [1.0, 0.05], [0.9420289855072463, 0.0], [0.8985507246376812, 0.03333333333333333], [0.6956521739130435, 0.016666666666666666], [0.8695652173913043, 0.0], [0.9855072463768116, 0.05], [1.0, 0.2], [1.0, 0.38333333333333336], [0.9855072463768116, 0.0], [0.0, 1.0], [0.9710144927536232, 0.05], [0.927536231884058, 0.0], [0.8840579710144928, 0.03333333333333333], [0.8985507246376812, 0.0], [0.9130434782608695, 0.03333333333333333], [0.7101449275362319, 0.016666666666666666], [0.6376811594202898, 0.13333333333333333], [0.855072463768116, 0.0], [0.8695652173913043, 0.03333333333333333], [0.8840579710144928, 0.0], [1.0, 0.18333333333333332], [0.927536231884058, 0.2833333333333333], [0.927536231884058, 0.13333333333333333], [1.0, 0.9333333333333333], [0.927536231884058, 0.03333333333333333], [0.9130434782608695, 0.0], [0.855072463768116, 0.03333333333333333], [0.7971014492753623, 0.0], [0.7681159420289855, 0.016666666666666666], [0.9565217391304348, 0.05], [0.9565217391304348, 0.0], [0.9130434782608695, 0.11666666666666667], [0.782608695652174, 0.0], [0.6666666666666666, 0.0], [0.6666666666666666, 0.1], [0.9420289855072463, 0.03333333333333333], [0.782608695652174, 0.016666666666666666], [0.7681159420289855, 0.0], [0.9420289855072463, 0.11666666666666667], [0.8840579710144928, 0.21666666666666667], [1.0, 0.03333333333333333], [0.7536231884057971, 0.016666666666666666], [0.8115942028985508, 0.0], [0.8405797101449275, 0.03333333333333333], [0.8840579710144928, 0.1], [0.9710144927536232, 0.0], [0.0, 0.13333333333333333], [0.782608695652174, 0.06666666666666667], [0.8405797101449275, 0.2833333333333333]], "basis_function": "cubic", "weights": [[-14.08083379991705], [0.8548048851424608], [-6.245860125278844], [1.7533465801022259], [7.6813664236510615], [-1.8382434864366741], [-0.5303901649236958], [1.174962181772742], [0.5089027959739207], [0.1687201727169328], [-1.402279326050564], [-0.8830523543530008], [-0.9161005474558825], [0.7913117204840583], [-0.5053824284695276], [0.22002604301965337], [110.62975792227459], [-6.416972427676056], [-12.108009656212879], [-2.3609776592601674], [-19.625225366995025], [3.345228473549836], [17.835085469317075], [7.878559181889302], [212.674832160372], [31.832204136637046], [-193.05670079616533], [50.263841227629314], [-164.49846470572174], [-19.46461538046271], [67.46569691511988], [-88.54317234866407], [60.97320544651122], [-13.891241429871895], [43.276236224461066], [-0.8739933509005716], [-24.464298202332362], [63.06403063145422], [-108.79340330473636], [80.13890463272696], [-129.4496887995927], [-17.558589922813116], [-34.04411130432844], [54.14170774779839], [-85.20184668980903], [77.14548749548118], [22.86774733774003], [-54.52622245465737], [-55.02946910510429], [-6.134899863770012], [-106.04980226996545], [74.80084650563862], [-85.09524493172985], [101.51135120073671], [-287.0603300776771], [40.87566894814853], [42.524436120931036], [38.50699408811356], [198.4114931393712], [-144.00525631397733], [92.0308402178496], [-96.40925808580239], [-314.9639047174431], [319.0939543419295], [42.092988115311165], [-127.61769695701872], [-5.146244604564577], [-146.131515753483], [46.4257921757533], [-100.52143741850679], [228.01234735966617], [42.18023156771078], [14.481408268484952], [204.5776633838738], [75.93861016456545]], "sigma": 0.0, "regularization_parameter": 0.0001, "rmse": 0.010085489200648768, "R2": 0.9987842099272065, "x_data_min": [[303.15, 30.0]], "x_data_max": [[372.15, 90.0]], "y_data_min": [0.0], "y_data_max": [1.400326892536694]}, "map": {"x_data_columns": "list", "x_data": "numpy", "centres": "numpy", "basis_function": "str", "weights": "numpy", "sigma": "str", "regularization_parameter": "str", "rmse": "str", "R2": "str", "x_data_min": "numpy", "x_data_max": "numpy", "y_data_min": "numpy", "y_data_max": "numpy"}}}, "input_labels": ["Temperature", "Evaporation percent"], "output_labels": ["Calcite_g"], "input_bounds": {"Temperature": [303.15, 372.15], "Evaporation percent": [30, 98]}, "surrogate_type": "rbf"} \ No newline at end of file diff --git a/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Glauberite_g.json b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Glauberite_g.json new file mode 100644 index 0000000000..3c7fb11d3a --- /dev/null +++ b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Glauberite_g.json @@ -0,0 +1 @@ +{"model_encoding": {"Glauberite_g": {"attr": {"x_data_columns": ["Temperature", "Evaporation percent"], "x_data": [[0.0, 0.0], [0.043478260869565216, 0.4918032786885246], [0.08695652173913043, 0.2459016393442623], [0.11594202898550725, 0.7540983606557377], [0.15942028985507245, 0.13114754098360656], [0.2028985507246377, 0.6229508196721312], [0.2463768115942029, 0.3770491803278688], [0.2753623188405797, 0.8688524590163934], [0.3188405797101449, 0.06557377049180328], [0.36231884057971014, 0.5573770491803278], [0.4057971014492754, 0.3114754098360656], [0.43478260869565216, 0.819672131147541], [0.4782608695652174, 0.18032786885245902], [0.5217391304347826, 0.6885245901639344], [0.5652173913043478, 0.4426229508196721], [0.6086956521739131, 0.9344262295081968], [0.6376811594202898, 0.03278688524590164], [0.6811594202898551, 0.5245901639344263], [0.7246376811594203, 0.2786885245901639], [0.7536231884057971, 0.7868852459016393], [0.7971014492753623, 0.16393442622950818], [0.8405797101449275, 0.6557377049180327], [0.8840579710144928, 0.4098360655737705], [0.9130434782608695, 0.9016393442622951], [0.9565217391304348, 0.09836065573770492], [0.4492753623188406, 1.0], [0.0, 0.9672131147540983], [0.17391304347826086, 1.0], [0.2463768115942029, 0.9508196721311475], [0.42028985507246375, 0.9344262295081968], [0.028985507246376812, 0.9672131147540983], [0.21739130434782608, 0.9508196721311475], [0.3333333333333333, 0.9836065573770492], [0.4057971014492754, 0.9344262295081968], [0.2028985507246377, 0.9508196721311475], [0.4492753623188406, 0.9344262295081968], [0.0, 0.7868852459016393], [0.18840579710144928, 0.9508196721311475], [0.2463768115942029, 0.9836065573770492], [0.36231884057971014, 0.9344262295081968], [1.0, 0.9180327868852459], [0.08695652173913043, 0.8852459016393442], [1.0, 0.7540983606557377], [0.3188405797101449, 0.7540983606557377], [0.2028985507246377, 0.9016393442622951], [0.5797101449275363, 0.8688524590163934], [0.0, 0.9180327868852459], [0.014492753623188406, 0.9836065573770492], [0.391304347826087, 0.8852459016393442], [0.13043478260869565, 0.9508196721311475], [0.028985507246376812, 0.9836065573770492], [0.2318840579710145, 0.9180327868852459], [0.14492753623188406, 0.9180327868852459], [0.463768115942029, 0.8852459016393442], [0.5217391304347826, 0.9344262295081968], [0.7971014492753623, 0.9344262295081968], [0.2028985507246377, 0.819672131147541], [0.043478260869565216, 0.9180327868852459], [0.028985507246376812, 0.8524590163934426], [0.7536231884057971, 0.8852459016393442], [1.0, 0.0], [0.014492753623188406, 0.9508196721311475], [0.3333333333333333, 0.9016393442622951], [0.5942028985507246, 0.7868852459016393], [0.2028985507246377, 0.9344262295081968], [0.08695652173913043, 0.9344262295081968], [0.0, 0.9836065573770492], [1.0, 0.8524590163934426], [0.42028985507246375, 0.9180327868852459], [0.5217391304347826, 0.9016393442622951], [0.3188405797101449, 0.9344262295081968], [0.2753623188405797, 0.9180327868852459], [0.0, 0.9508196721311475], [0.6231884057971014, 0.9016393442622951], [0.6956521739130435, 0.9344262295081968]], "centres": [[0.0, 0.0], [0.043478260869565216, 0.4918032786885246], [0.08695652173913043, 0.2459016393442623], [0.11594202898550725, 0.7540983606557377], [0.15942028985507245, 0.13114754098360656], [0.2028985507246377, 0.6229508196721312], [0.2463768115942029, 0.3770491803278688], [0.2753623188405797, 0.8688524590163934], [0.3188405797101449, 0.06557377049180328], [0.36231884057971014, 0.5573770491803278], [0.4057971014492754, 0.3114754098360656], [0.43478260869565216, 0.819672131147541], [0.4782608695652174, 0.18032786885245902], [0.5217391304347826, 0.6885245901639344], [0.5652173913043478, 0.4426229508196721], [0.6086956521739131, 0.9344262295081968], [0.6376811594202898, 0.03278688524590164], [0.6811594202898551, 0.5245901639344263], [0.7246376811594203, 0.2786885245901639], [0.7536231884057971, 0.7868852459016393], [0.7971014492753623, 0.16393442622950818], [0.8405797101449275, 0.6557377049180327], [0.8840579710144928, 0.4098360655737705], [0.9130434782608695, 0.9016393442622951], [0.9565217391304348, 0.09836065573770492], [0.4492753623188406, 1.0], [0.0, 0.9672131147540983], [0.17391304347826086, 1.0], [0.2463768115942029, 0.9508196721311475], [0.42028985507246375, 0.9344262295081968], [0.028985507246376812, 0.9672131147540983], [0.21739130434782608, 0.9508196721311475], [0.3333333333333333, 0.9836065573770492], [0.4057971014492754, 0.9344262295081968], [0.2028985507246377, 0.9508196721311475], [0.4492753623188406, 0.9344262295081968], [0.0, 0.7868852459016393], [0.18840579710144928, 0.9508196721311475], [0.2463768115942029, 0.9836065573770492], [0.36231884057971014, 0.9344262295081968], [1.0, 0.9180327868852459], [0.08695652173913043, 0.8852459016393442], [1.0, 0.7540983606557377], [0.3188405797101449, 0.7540983606557377], [0.2028985507246377, 0.9016393442622951], [0.5797101449275363, 0.8688524590163934], [0.0, 0.9180327868852459], [0.014492753623188406, 0.9836065573770492], [0.391304347826087, 0.8852459016393442], [0.13043478260869565, 0.9508196721311475], [0.028985507246376812, 0.9836065573770492], [0.2318840579710145, 0.9180327868852459], [0.14492753623188406, 0.9180327868852459], [0.463768115942029, 0.8852459016393442], [0.5217391304347826, 0.9344262295081968], [0.7971014492753623, 0.9344262295081968], [0.2028985507246377, 0.819672131147541], [0.043478260869565216, 0.9180327868852459], [0.028985507246376812, 0.8524590163934426], [0.7536231884057971, 0.8852459016393442], [1.0, 0.0], [0.014492753623188406, 0.9508196721311475], [0.3333333333333333, 0.9016393442622951], [0.5942028985507246, 0.7868852459016393], [0.2028985507246377, 0.9344262295081968], [0.08695652173913043, 0.9344262295081968], [0.0, 0.9836065573770492], [1.0, 0.8524590163934426], [0.42028985507246375, 0.9180327868852459], [0.5217391304347826, 0.9016393442622951], [0.3188405797101449, 0.9344262295081968], [0.2753623188405797, 0.9180327868852459], [0.0, 0.9508196721311475], [0.6231884057971014, 0.9016393442622951], [0.6956521739130435, 0.9344262295081968]], "basis_function": "cubic", "weights": [[2.006675191749156], [-1.3467679053333947], [-4.2915285231642954], [22.594624791318182], [-2.3135604760579414], [-0.14178736419863114], [6.961017224743689], [360.65940563836665], [-0.5780581434707717], [0.44484390106421046], [-0.2555699923152247], [186.87772329618096], [1.0951132264628147], [-8.659266966869666], [1.6594585790479572], [225.72279892403276], [-2.1453160519777246], [1.4017073872528272], [1.946345397094838], [34.63974666585614], [1.312411095417076], [3.7758993965072385], [-1.5776264341988488], [-54.28932652835974], [-8.920485775655418], [-575.2928421723925], [6082.4082645957715], [-1180.710063301194], [2161.559225883936], [918.8056865571443], [5398.639428668604], [882.5696542175031], [-514.1819018711108], [591.2554930848818], [1142.966612561361], [757.0286359284178], [-51.49629961896623], [1542.7351560413476], [-938.5395106523927], [514.2060982216841], [102.6792660952047], [263.28618756214524], [8.440605644508933], [6.128618184437009], [517.123963759243], [76.2006365425741], [-278.71387701524145], [-1911.9737205014699], [-73.11523548689836], [1580.0992522886895], [-2242.966203776546], [-1252.282435899975], [-1202.0651406209995], [-179.91585767035485], [564.5111931062384], [68.93232544034299], [98.19990962907929], [-542.9120484971199], [220.84523816588285], [-125.08538656554242], [6.493093819184857], [-2816.298146566678], [-902.8237929462825], [47.84622996292896], [-2630.5558429077287], [-737.9823948557068], [-1648.4810618790407], [-83.19912326661837], [-1698.8116133729138], [-587.6605303322257], [1047.4983381835475], [-922.641982016378], [-2050.0105901370625], [-317.02351797059146], [90.34947648333957]], "sigma": 0.0, "regularization_parameter": 1e-05, "rmse": 0.012868041395747222, "R2": 0.9955859545447684, "x_data_min": [[303.15, 30.0]], "x_data_max": [[372.15, 91.0]], "y_data_min": [0.0], "y_data_max": [65.81164013705659]}, "map": {"x_data_columns": "list", "x_data": "numpy", "centres": "numpy", "basis_function": "str", "weights": "numpy", "sigma": "str", "regularization_parameter": "str", "rmse": "str", "R2": "str", "x_data_min": "numpy", "x_data_max": "numpy", "y_data_min": "numpy", "y_data_max": "numpy"}}}, "input_labels": ["Temperature", "Evaporation percent"], "output_labels": ["Glauberite_g"], "input_bounds": {"Temperature": [303.15, 372.15], "Evaporation percent": [30, 98]}, "surrogate_type": "rbf"} \ No newline at end of file diff --git a/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Halite_g.json b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Halite_g.json new file mode 100644 index 0000000000..0a493e04dd --- /dev/null +++ b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Halite_g.json @@ -0,0 +1 @@ +{"model_encoding": {"Halite_g": {"attr": {"x_data_columns": ["Temperature", "Evaporation percent"], "x_data": [[0.0, 0.0], [0.043478260869565216, 0.4918032786885246], [0.08695652173913043, 0.2459016393442623], [0.11594202898550725, 0.7540983606557377], [0.15942028985507245, 0.13114754098360656], [0.2028985507246377, 0.6229508196721312], [0.2463768115942029, 0.3770491803278688], [0.2753623188405797, 0.8688524590163934], [0.3188405797101449, 0.06557377049180328], [0.36231884057971014, 0.5573770491803278], [0.4057971014492754, 0.3114754098360656], [0.43478260869565216, 0.819672131147541], [0.4782608695652174, 0.18032786885245902], [0.5217391304347826, 0.6885245901639344], [0.5652173913043478, 0.4426229508196721], [0.6086956521739131, 0.9344262295081968], [0.6376811594202898, 0.03278688524590164], [0.6811594202898551, 0.5245901639344263], [0.7246376811594203, 0.2786885245901639], [0.7536231884057971, 0.7868852459016393], [0.7971014492753623, 0.16393442622950818], [0.8405797101449275, 0.6557377049180327], [0.8840579710144928, 0.4098360655737705], [0.9130434782608695, 0.9016393442622951], [0.9565217391304348, 0.09836065573770492], [1.0, 0.0], [0.0, 0.9836065573770492], [0.028985507246376812, 0.9672131147540983], [0.17391304347826086, 1.0], [0.2463768115942029, 0.9836065573770492], [0.028985507246376812, 0.8688524590163934], [1.0, 0.04918032786885246], [0.8840579710144928, 0.03278688524590164], [0.855072463768116, 0.0], [0.15942028985507245, 0.9508196721311475], [0.057971014492753624, 0.9836065573770492], [0.0, 0.9672131147540983], [0.7681159420289855, 0.01639344262295082], [1.0, 0.7049180327868853], [1.0, 0.19672131147540983], [0.17391304347826086, 0.8852459016393442], [0.014492753623188406, 0.9836065573770492], [0.3188405797101449, 0.9344262295081968], [0.4492753623188406, 1.0], [0.0, 0.9016393442622951], [0.7536231884057971, 0.08196721311475409], [0.0, 0.7868852459016393], [0.043478260869565216, 0.9180327868852459], [0.7536231884057971, 0.0], [0.10144927536231885, 0.9508196721311475], [0.855072463768116, 0.08196721311475409], [0.6376811594202898, 0.0], [1.0, 0.9180327868852459], [0.014492753623188406, 0.9344262295081968], [0.2463768115942029, 0.9344262295081968], [0.11594202898550725, 0.9180327868852459], [0.9130434782608695, 0.19672131147540983], [0.9130434782608695, 0.0], [1.0, 0.09836065573770492], [0.5217391304347826, 0.03278688524590164], [0.4782608695652174, 0.9180327868852459], [0.7246376811594203, 0.01639344262295082], [0.6376811594202898, 0.16393442622950818], [0.028985507246376812, 0.9836065573770492], [0.8985507246376812, 0.06557377049180328], [0.927536231884058, 0.03278688524590164], [0.7681159420289855, 0.04918032786885246], [0.0, 0.9508196721311475], [0.11594202898550725, 0.8524590163934426], [1.0, 0.8360655737704918], [1.0, 0.32786885245901637], [0.028985507246376812, 0.9508196721311475], [0.21739130434782608, 0.0], [0.7101449275362319, 0.04918032786885246], [0.9710144927536232, 0.06557377049180328]], "centres": [[0.0, 0.0], [0.043478260869565216, 0.4918032786885246], [0.08695652173913043, 0.2459016393442623], [0.11594202898550725, 0.7540983606557377], [0.15942028985507245, 0.13114754098360656], [0.2028985507246377, 0.6229508196721312], [0.2463768115942029, 0.3770491803278688], [0.2753623188405797, 0.8688524590163934], [0.3188405797101449, 0.06557377049180328], [0.36231884057971014, 0.5573770491803278], [0.4057971014492754, 0.3114754098360656], [0.43478260869565216, 0.819672131147541], [0.4782608695652174, 0.18032786885245902], [0.5217391304347826, 0.6885245901639344], [0.5652173913043478, 0.4426229508196721], [0.6086956521739131, 0.9344262295081968], [0.6376811594202898, 0.03278688524590164], [0.6811594202898551, 0.5245901639344263], [0.7246376811594203, 0.2786885245901639], [0.7536231884057971, 0.7868852459016393], [0.7971014492753623, 0.16393442622950818], [0.8405797101449275, 0.6557377049180327], [0.8840579710144928, 0.4098360655737705], [0.9130434782608695, 0.9016393442622951], [0.9565217391304348, 0.09836065573770492], [1.0, 0.0], [0.0, 0.9836065573770492], [0.028985507246376812, 0.9672131147540983], [0.17391304347826086, 1.0], [0.2463768115942029, 0.9836065573770492], [0.028985507246376812, 0.8688524590163934], [1.0, 0.04918032786885246], [0.8840579710144928, 0.03278688524590164], [0.855072463768116, 0.0], [0.15942028985507245, 0.9508196721311475], [0.057971014492753624, 0.9836065573770492], [0.0, 0.9672131147540983], [0.7681159420289855, 0.01639344262295082], [1.0, 0.7049180327868853], [1.0, 0.19672131147540983], [0.17391304347826086, 0.8852459016393442], [0.014492753623188406, 0.9836065573770492], [0.3188405797101449, 0.9344262295081968], [0.4492753623188406, 1.0], [0.0, 0.9016393442622951], [0.7536231884057971, 0.08196721311475409], [0.0, 0.7868852459016393], [0.043478260869565216, 0.9180327868852459], [0.7536231884057971, 0.0], [0.10144927536231885, 0.9508196721311475], [0.855072463768116, 0.08196721311475409], [0.6376811594202898, 0.0], [1.0, 0.9180327868852459], [0.014492753623188406, 0.9344262295081968], [0.2463768115942029, 0.9344262295081968], [0.11594202898550725, 0.9180327868852459], [0.9130434782608695, 0.19672131147540983], [0.9130434782608695, 0.0], [1.0, 0.09836065573770492], [0.5217391304347826, 0.03278688524590164], [0.4782608695652174, 0.9180327868852459], [0.7246376811594203, 0.01639344262295082], [0.6376811594202898, 0.16393442622950818], [0.028985507246376812, 0.9836065573770492], [0.8985507246376812, 0.06557377049180328], [0.927536231884058, 0.03278688524590164], [0.7681159420289855, 0.04918032786885246], [0.0, 0.9508196721311475], [0.11594202898550725, 0.8524590163934426], [1.0, 0.8360655737704918], [1.0, 0.32786885245901637], [0.028985507246376812, 0.9508196721311475], [0.21739130434782608, 0.0], [0.7101449275362319, 0.04918032786885246], [0.9710144927536232, 0.06557377049180328]], "basis_function": "cubic", "weights": [[0.28587094514684885], [0.10330976585867174], [0.03032262240349093], [-1.040702258441734], [0.5374407985695981], [-0.3584497212101133], [-0.40681297272716815], [-13.930408443294946], [0.6463147420333044], [0.21115295509801724], [-0.09472857551300712], [-2.2404499829361493], [0.0827658399172897], [-0.25220297860614227], [-0.07095764878465957], [-0.030273822691307545], [19.974079338458523], [-0.12471011028216983], [0.9847081758815694], [0.22911327649668078], [-4.564032007124544], [-0.5684826509467629], [-0.25391072118133073], [1.0027335984218375], [-20.979041966959016], [38.98655888201392], [168.6348190573578], [-580.4976033692469], [69.46279254545387], [-69.73020720413177], [-23.35382526780359], [-107.74468049562708], [-122.15249837339914], [37.75173910340744], [-73.65807692630169], [48.5183088583816], [-512.8318375289837], [-215.58854732971574], [-0.37322469482836196], [-1.4654200264426294], [12.682047157455033], [191.58645568861994], [15.862670357948621], [-5.197506019649032], [3.898376254736706], [-19.042114811382504], [4.049049770690448], [23.590186508705084], [179.4678063067201], [-88.63742193492982], [11.684058898503164], [-21.65117330859392], [-3.363411632303308], [115.11146151130451], [49.559734737535116], [92.75285555175046], [-5.805777555722103], [71.50164140672258], [39.08776348780057], [1.3470043733418215], [5.684486000006121], [-92.36643419478386], [-3.5675245763075747], [205.30739754849787], [90.99674176067974], [-92.06063102842268], [101.42394646098579], [185.9524070239802], [-17.66030322782746], [3.2081545777977394], [1.2262057020165318], [196.61779574434198], [-1.0621651271470895], [40.85264384596934], [72.03747410214947]], "sigma": 0.0, "regularization_parameter": 1e-05, "rmse": 0.0011497228755673206, "R2": 0.9999910920443553, "x_data_min": [[303.15, 30.0]], "x_data_max": [[372.15, 91.0]], "y_data_min": [0.0], "y_data_max": [2886.4013929283155]}, "map": {"x_data_columns": "list", "x_data": "numpy", "centres": "numpy", "basis_function": "str", "weights": "numpy", "sigma": "str", "regularization_parameter": "str", "rmse": "str", "R2": "str", "x_data_min": "numpy", "x_data_max": "numpy", "y_data_min": "numpy", "y_data_max": "numpy"}}}, "input_labels": ["Temperature", "Evaporation percent"], "output_labels": ["Halite_g"], "input_bounds": {"Temperature": [303.15, 372.15], "Evaporation percent": [30, 98]}, "surrogate_type": "rbf"} \ No newline at end of file diff --git a/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Vapor_Pressure.json b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Vapor_Pressure.json new file mode 100644 index 0000000000..14be4d20ce --- /dev/null +++ b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/Vapor_Pressure.json @@ -0,0 +1 @@ +{"model_encoding": {"Vapor Pressure (atm)": {"attr": {"x_data_columns": ["Temperature", "Evaporation percent"], "x_data": [[0.0, 0.0], [0.043478260869565216, 0.4918032786885246], [0.08695652173913043, 0.2459016393442623], [0.11594202898550725, 0.7540983606557377], [0.15942028985507245, 0.13114754098360656], [0.2028985507246377, 0.6229508196721312], [0.2463768115942029, 0.3770491803278688], [0.2753623188405797, 0.8688524590163934], [0.3188405797101449, 0.06557377049180328], [0.36231884057971014, 0.5573770491803278], [0.4057971014492754, 0.3114754098360656], [0.43478260869565216, 0.819672131147541], [0.4782608695652174, 0.18032786885245902], [0.5217391304347826, 0.6885245901639344], [0.5652173913043478, 0.4426229508196721], [0.6086956521739131, 0.9344262295081968], [0.6376811594202898, 0.03278688524590164], [0.6811594202898551, 0.5245901639344263], [0.7246376811594203, 0.2786885245901639], [0.7536231884057971, 0.7868852459016393], [0.7971014492753623, 0.16393442622950818], [0.8405797101449275, 0.6557377049180327], [0.8840579710144928, 0.4098360655737705], [0.9130434782608695, 0.9016393442622951], [0.9565217391304348, 0.09836065573770492], [0.0, 0.9836065573770492], [1.0, 0.7377049180327869], [0.4492753623188406, 1.0], [1.0, 0.2786885245901639], [1.0, 0.0], [0.8985507246376812, 0.03278688524590164], [0.43478260869565216, 0.9344262295081968], [1.0, 0.04918032786885246], [0.9130434782608695, 0.2459016393442623], [0.9855072463768116, 0.9180327868852459], [1.0, 0.8524590163934426], [0.15942028985507245, 0.0], [0.7971014492753623, 0.9344262295081968], [0.7681159420289855, 0.8852459016393442], [0.927536231884058, 0.7377049180327869], [1.0, 0.14754098360655737], [0.043478260869565216, 0.8852459016393442], [0.5072463768115942, 0.0], [0.8405797101449275, 0.06557377049180328], [0.7681159420289855, 0.01639344262295082], [0.8840579710144928, 0.0], [1.0, 0.4918032786885246], [0.7246376811594203, 0.06557377049180328], [0.927536231884058, 0.5409836065573771], [0.3333333333333333, 0.9836065573770492], [0.17391304347826086, 1.0], [1.0, 0.08196721311475409], [0.9130434782608695, 0.06557377049180328], [0.9710144927536232, 0.8032786885245902], [0.0, 0.2459016393442623], [0.6231884057971014, 0.819672131147541], [0.5217391304347826, 0.9344262295081968], [0.18840579710144928, 0.9508196721311475], [0.5072463768115942, 0.06557377049180328], [0.4782608695652174, 0.8688524590163934], [1.0, 0.9016393442622951], [0.7681159420289855, 0.0], [0.6521739130434783, 0.0], [0.6521739130434783, 0.14754098360655737], [0.7681159420289855, 0.04918032786885246], [0.8260869565217391, 0.01639344262295082], [0.9565217391304348, 0.39344262295081966], [1.0, 0.01639344262295082], [0.9420289855072463, 0.16393442622950818], [0.9420289855072463, 0.03278688524590164], [0.927536231884058, 0.01639344262295082], [0.8260869565217391, 0.9016393442622951], [0.391304347826087, 0.8852459016393442], [0.8260869565217391, 0.8360655737704918], [1.0, 0.39344262295081966]], "centres": [[0.0, 0.0], [0.043478260869565216, 0.4918032786885246], [0.08695652173913043, 0.2459016393442623], [0.11594202898550725, 0.7540983606557377], [0.15942028985507245, 0.13114754098360656], [0.2028985507246377, 0.6229508196721312], [0.2463768115942029, 0.3770491803278688], [0.2753623188405797, 0.8688524590163934], [0.3188405797101449, 0.06557377049180328], [0.36231884057971014, 0.5573770491803278], [0.4057971014492754, 0.3114754098360656], [0.43478260869565216, 0.819672131147541], [0.4782608695652174, 0.18032786885245902], [0.5217391304347826, 0.6885245901639344], [0.5652173913043478, 0.4426229508196721], [0.6086956521739131, 0.9344262295081968], [0.6376811594202898, 0.03278688524590164], [0.6811594202898551, 0.5245901639344263], [0.7246376811594203, 0.2786885245901639], [0.7536231884057971, 0.7868852459016393], [0.7971014492753623, 0.16393442622950818], [0.8405797101449275, 0.6557377049180327], [0.8840579710144928, 0.4098360655737705], [0.9130434782608695, 0.9016393442622951], [0.9565217391304348, 0.09836065573770492], [0.0, 0.9836065573770492], [1.0, 0.7377049180327869], [0.4492753623188406, 1.0], [1.0, 0.2786885245901639], [1.0, 0.0], [0.8985507246376812, 0.03278688524590164], [0.43478260869565216, 0.9344262295081968], [1.0, 0.04918032786885246], [0.9130434782608695, 0.2459016393442623], [0.9855072463768116, 0.9180327868852459], [1.0, 0.8524590163934426], [0.15942028985507245, 0.0], [0.7971014492753623, 0.9344262295081968], [0.7681159420289855, 0.8852459016393442], [0.927536231884058, 0.7377049180327869], [1.0, 0.14754098360655737], [0.043478260869565216, 0.8852459016393442], [0.5072463768115942, 0.0], [0.8405797101449275, 0.06557377049180328], [0.7681159420289855, 0.01639344262295082], [0.8840579710144928, 0.0], [1.0, 0.4918032786885246], [0.7246376811594203, 0.06557377049180328], [0.927536231884058, 0.5409836065573771], [0.3333333333333333, 0.9836065573770492], [0.17391304347826086, 1.0], [1.0, 0.08196721311475409], [0.9130434782608695, 0.06557377049180328], [0.9710144927536232, 0.8032786885245902], [0.0, 0.2459016393442623], [0.6231884057971014, 0.819672131147541], [0.5217391304347826, 0.9344262295081968], [0.18840579710144928, 0.9508196721311475], [0.5072463768115942, 0.06557377049180328], [0.4782608695652174, 0.8688524590163934], [1.0, 0.9016393442622951], [0.7681159420289855, 0.0], [0.6521739130434783, 0.0], [0.6521739130434783, 0.14754098360655737], [0.7681159420289855, 0.04918032786885246], [0.8260869565217391, 0.01639344262295082], [0.9565217391304348, 0.39344262295081966], [1.0, 0.01639344262295082], [0.9420289855072463, 0.16393442622950818], [0.9420289855072463, 0.03278688524590164], [0.927536231884058, 0.01639344262295082], [0.8260869565217391, 0.9016393442622951], [0.391304347826087, 0.8852459016393442], [0.8260869565217391, 0.8360655737704918], [1.0, 0.39344262295081966]], "basis_function": "cubic", "weights": [[0.572000029766303], [-0.012280465785422745], [-0.7048778464882365], [-0.06490663210121106], [0.2197106629734321], [0.033776226134503595], [0.009417997719553428], [-1.0981049806417502], [0.5060798815722338], [0.06005263395675586], [0.03426166485622826], [0.7415801537584343], [-0.4042667325833602], [0.11079940854977366], [-0.05710738862714937], [0.7043136470830049], [6.138124877822957], [0.15628160366971144], [0.41376523943166177], [-1.0937233755806233], [-2.1734935829182405], [0.7144946641289067], [1.2650953268602052], [4.548027526401597], [0.8183855984181108], [2.2161101656054414], [1.5092601557160776], [-8.390623134032253], [0.9402965257635287], [-10.127278773165841], [-44.19387169151182], [9.80997387747962], [-54.84674843354075], [-0.09919628078478127], [-23.266922674869022], [3.4179852919491793], [-0.6159804420388811], [-8.195707087747678], [5.505291449814877], [-1.4798675499441174], [-0.3510726071712753], [-1.8812980331385378], [-1.080294638332278], [8.536135447889414], [-44.20819895865587], [12.935020647017257], [1.094067119956435], [0.9910299201934452], [-1.6304276919226481], [2.4206724595230753], [-2.537575810596925], [30.15221340296469], [29.008060643616375], [-4.417687844296552], [0.49750546129167694], [-0.8901272720322311], [3.8823867291105856], [2.694446195794363], [1.3161807779418826], [-4.36328507505684], [19.51200823072128], [29.898839845295985], [-6.471186806423756], [-1.1076517205233571], [17.21435138577426], [-7.467743305161402], [-4.463284592465698], [38.93349437461858], [-4.671648175997648], [-29.89487879359399], [29.864282874731423], [6.376379099686801], [-3.11862719810091], [-2.8733463548810505], [2.70541104113272]], "sigma": 0.0, "regularization_parameter": 1e-05, "rmse": 0.00014176135908527726, "R2": 0.9999998266982607, "x_data_min": [[303.15, 30.0]], "x_data_max": [[372.15, 91.0]], "y_data_min": [0.0239921062042404], "y_data_max": [0.7044561190297868]}, "map": {"x_data_columns": "list", "x_data": "numpy", "centres": "numpy", "basis_function": "str", "weights": "numpy", "sigma": "str", "regularization_parameter": "str", "rmse": "str", "R2": "str", "x_data_min": "numpy", "x_data_max": "numpy", "y_data_min": "numpy", "y_data_max": "numpy"}}}, "input_labels": ["Temperature", "Evaporation percent"], "output_labels": ["Vapor Pressure (atm)"], "input_bounds": {"Temperature": [303.15, 372.15], "Evaporation percent": [30, 98]}, "surrogate_type": "rbf"} \ No newline at end of file diff --git a/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/__init__.py b/watertap/data/surrogate_defaults/surrogate_crystallizer_defaults/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/watertap/property_models/multicomp_aq_sol_prop_pack.py b/watertap/property_models/multicomp_aq_sol_prop_pack.py index 984c0ba351..63d6926efa 100644 --- a/watertap/property_models/multicomp_aq_sol_prop_pack.py +++ b/watertap/property_models/multicomp_aq_sol_prop_pack.py @@ -42,6 +42,7 @@ value, check_optimal_termination, units as pyunits, + exp, ) from pyomo.common.config import ConfigValue, In, Bool from pyomo.util.calc_var_value import calculate_variable_from_constraint @@ -618,6 +619,8 @@ def build(self): self.set_default_scaling("visc_d_phase", 1e3, index="Liq") self.set_default_scaling("diffus_phase_comp", 1e10, index="Liq") self.set_default_scaling("visc_k_phase", 1e6, index="Liq") + self.set_default_scaling("enth_mass_phase", 1e-5, index="Liq") + self.set_default_scaling("pressure_sat", 1e-5) @classmethod def define_metadata(cls, obj): @@ -643,6 +646,8 @@ def define_metadata(cls, obj): "pressure_osm_phase": {"method": "_pressure_osm_phase"}, "mw_comp": {"method": "_mw_comp"}, "act_coeff_phase_comp": {"method": "_act_coeff_phase_comp"}, + "enth_mass_phase": {"method": "_enth_mass_phase"}, + "pressure_sat": {"method": "_pressure_sat"}, } ) @@ -656,6 +661,7 @@ def define_metadata(cls, obj): "dens_mass_solvent": {"method": "_dens_mass_solvent"}, "dielectric_constant": {"method": "_dielectric_constant"}, "debye_huckel_constant": {"method": "_debye_huckel_constant"}, + "enth_flow": {"method": "_enth_flow"}, "ionic_strength_molal": {"method": "_ionic_strength_molal"}, "molar_volume_phase_comp": {"method": "_molar_volume_phase_comp"}, "radius_stokes_comp": {"method": "_radius_stokes_comp"}, @@ -896,6 +902,17 @@ def initialize( else: self[k].total_dissolved_solids = 0 + if self[k].is_property_constructed("enth_mass_phase"): + calculate_variable_from_constraint( + self[k].enth_mass_phase["Liq"], + self[k].eq_enth_mass_phase["Liq"], + ) + if self[k].is_property_constructed("pressure_sat"): + calculate_variable_from_constraint( + self[k].pressure_sat, + self[k].eq_pressure_sat, + ) + # Check when the state vars are fixed already result in dof 0 for k in self.keys(): dof = degrees_of_freedom(self[k]) @@ -1911,6 +1928,314 @@ def rule_total_dissolved_solids(b): ) return + def _enth_mass_phase(self): + params = self.params + if not hasattr(params, "enth_mass_param_A1"): + # specific enthalpy parameters, 10-120 C, 0-120 g/kg, 0-12 MPa + # Table 9 in Nayar et al. (2016) + enth_mass_units = pyunits.J / pyunits.kg + P_inv_units = pyunits.MPa**-1 + t_inv_units = pyunits.K**-1 + params.enth_mass_param_A1 = Var( + within=Reals, + initialize=996.7767, + units=enth_mass_units * P_inv_units, + doc="Specific enthalpy parameter A1", + ) + params.enth_mass_param_A2 = Var( + within=Reals, + initialize=-3.2406, + units=enth_mass_units * P_inv_units * t_inv_units, + doc="Specific enthalpy parameter A2", + ) + params.enth_mass_param_A3 = Var( + within=Reals, + initialize=0.0127, + units=enth_mass_units * P_inv_units * t_inv_units**2, + doc="Specific enthalpy parameter A3", + ) + params.enth_mass_param_A4 = Var( + within=Reals, + initialize=-4.7723e-5, + units=enth_mass_units * P_inv_units * t_inv_units**3, + doc="Specific enthalpy parameter A4", + ) + params.enth_mass_param_A5 = Var( + within=Reals, + initialize=-1.1748, + units=enth_mass_units * P_inv_units, + doc="Specific enthalpy parameter A5", + ) + params.enth_mass_param_A6 = Var( + within=Reals, + initialize=0.01169, + units=enth_mass_units * P_inv_units * t_inv_units, + doc="Specific enthalpy parameter A6", + ) + params.enth_mass_param_A7 = Var( + within=Reals, + initialize=-2.6185e-5, + units=enth_mass_units * P_inv_units * t_inv_units**2, + doc="Specific enthalpy parameter A7", + ) + params.enth_mass_param_A8 = Var( + within=Reals, + initialize=7.0661e-8, + units=enth_mass_units * P_inv_units * t_inv_units**3, + doc="Specific enthalpy parameter A8", + ) + params.enth_mass_param_B1 = Var( + within=Reals, + initialize=-2.34825e4, + units=enth_mass_units, + doc="Specific enthalpy parameter B1", + ) + params.enth_mass_param_B2 = Var( + within=Reals, + initialize=3.15183e5, + units=enth_mass_units, + doc="Specific enthalpy parameter B2", + ) + params.enth_mass_param_B3 = Var( + within=Reals, + initialize=2.80269e6, + units=enth_mass_units, + doc="Specific enthalpy parameter B3", + ) + params.enth_mass_param_B4 = Var( + within=Reals, + initialize=-1.44606e7, + units=enth_mass_units, + doc="Specific enthalpy parameter B4", + ) + params.enth_mass_param_B5 = Var( + within=Reals, + initialize=7.82607e3, + units=enth_mass_units * t_inv_units, + doc="Specific enthalpy parameter B5", + ) + params.enth_mass_param_B6 = Var( + within=Reals, + initialize=-4.41733, + units=enth_mass_units * t_inv_units**2, + doc="Specific enthalpy parameter B6", + ) + params.enth_mass_param_B7 = Var( + within=Reals, + initialize=2.1394e-1, + units=enth_mass_units * t_inv_units**3, + doc="Specific enthalpy parameter B7", + ) + params.enth_mass_param_B8 = Var( + within=Reals, + initialize=-1.99108e4, + units=enth_mass_units * t_inv_units, + doc="Specific enthalpy parameter B8", + ) + params.enth_mass_param_B9 = Var( + within=Reals, + initialize=2.77846e4, + units=enth_mass_units * t_inv_units, + doc="Specific enthalpy parameter B9", + ) + params.enth_mass_param_B10 = Var( + within=Reals, + initialize=9.72801, + units=enth_mass_units * t_inv_units**2, + doc="Specific enthalpy parameter B10", + ) + params.enth_mass_param_C1 = Var( + within=Reals, + initialize=141.355, + units=enth_mass_units, + doc="Specific enthalpy parameter C1", + ) + params.enth_mass_param_C2 = Var( + within=Reals, + initialize=4202.07, + units=enth_mass_units * t_inv_units, + doc="Specific enthalpy parameter C2", + ) + params.enth_mass_param_C3 = Var( + within=Reals, + initialize=-0.535, + units=enth_mass_units * t_inv_units**2, + doc="Specific enthalpy parameter C3", + ) + params.enth_mass_param_C4 = Var( + within=Reals, + initialize=0.004, + units=enth_mass_units * t_inv_units**3, + doc="Specific enthalpy parameter C4", + ) + + for v in params.component_objects(Var): + v.fix() + + self.enth_mass_phase = Var( + self.params.phase_list, + initialize=1e6, + bounds=(1, 1e9), + units=pyunits.J * pyunits.kg**-1, + doc="Specific enthalpy", + ) + + # Nayar et al. (2016), eq. 25 and 26, 10-120 C, 0-120 g/kg, 0-12 MPa + def rule_enth_mass_phase(b, p): + # temperature in degC, but pyunits in K + t = b.temperature - 273.15 * pyunits.K + S_kg_kg = ( + pyunits.convert( + b.total_dissolved_solids, to_units=pyunits.kg / pyunits.m**3 + ) + / b.dens_mass_phase[p] + ) + S_g_kg = S_kg_kg * 1000 + P = b.pressure - 101325 * pyunits.Pa + P_MPa = pyunits.convert(P, to_units=pyunits.MPa) + + h_w = ( + b.params.enth_mass_param_C1 + + b.params.enth_mass_param_C2 * t + + b.params.enth_mass_param_C3 * t**2 + + b.params.enth_mass_param_C4 * t**3 + ) + h_sw0 = h_w - S_kg_kg * ( + b.params.enth_mass_param_B1 + + b.params.enth_mass_param_B2 * S_kg_kg + + b.params.enth_mass_param_B3 * S_kg_kg**2 + + b.params.enth_mass_param_B4 * S_kg_kg**3 + + b.params.enth_mass_param_B5 * t + + b.params.enth_mass_param_B6 * t**2 + + b.params.enth_mass_param_B7 * t**3 + + b.params.enth_mass_param_B8 * S_kg_kg * t + + b.params.enth_mass_param_B9 * S_kg_kg**2 * t + + b.params.enth_mass_param_B10 * S_kg_kg * t**2 + ) + h_sw = h_sw0 + P_MPa * ( + b.params.enth_mass_param_A1 + + b.params.enth_mass_param_A2 * t + + b.params.enth_mass_param_A3 * t**2 + + b.params.enth_mass_param_A4 * t**3 + + S_g_kg + * ( + +b.params.enth_mass_param_A5 + + b.params.enth_mass_param_A6 * t + + b.params.enth_mass_param_A7 * t**2 + + b.params.enth_mass_param_A8 * t**3 + ) + ) + return b.enth_mass_phase[p] == h_sw + + self.eq_enth_mass_phase = Constraint( + self.params.phase_list, rule=rule_enth_mass_phase + ) + + def _enth_flow(self): + # enthalpy flow expression for get_enthalpy_flow_terms method + + def rule_enth_flow(b): # enthalpy flow [J/s] + return ( + sum(b.flow_mass_phase_comp["Liq", j] for j in b.params.component_list) + * b.enth_mass_phase["Liq"] + ) + + self.enth_flow = Expression(rule=rule_enth_flow) + + def _pressure_sat(self): + params = self.params + if not hasattr(params, "pressure_sat_param_psatw_A1"): + t_inv_units = pyunits.K**-1 + s_inv_units = pyunits.kg / pyunits.g + # vapor pressure parameters, 0-180 C, 0-160 g/kg + # eq. 5 and 6 in Nayar et al.(2016) + params.pressure_sat_param_psatw_A1 = Var( + within=Reals, + initialize=-5.8002206e3, + units=pyunits.K, + doc="Vapor pressure of pure water parameter A1", + ) + params.pressure_sat_param_psatw_A2 = Var( + within=Reals, + initialize=1.3914993, + units=pyunits.dimensionless, + doc="Vapor pressure of pure water parameter A2", + ) + params.pressure_sat_param_psatw_A3 = Var( + within=Reals, + initialize=-4.8640239e-2, + units=t_inv_units, + doc="Vapor pressure of pure water parameter A3", + ) + params.pressure_sat_param_psatw_A4 = Var( + within=Reals, + initialize=4.1764768e-5, + units=t_inv_units**2, + doc="Vapor pressure of pure water parameter A4", + ) + params.pressure_sat_param_psatw_A5 = Var( + within=Reals, + initialize=-1.4452093e-8, + units=t_inv_units**3, + doc="Vapor pressure of pure water parameter A5", + ) + params.pressure_sat_param_psatw_A6 = Var( + within=Reals, + initialize=6.5459673, + units=pyunits.dimensionless, + doc="Vapor pressure of pure water parameter A6", + ) + params.pressure_sat_param_B1 = Var( + within=Reals, + initialize=-4.5818e-4, + units=s_inv_units, + doc="Vapor pressure of seawater parameter B1", + ) + params.pressure_sat_param_B2 = Var( + within=Reals, + initialize=-2.0443e-6, + units=s_inv_units**2, + doc="Vapor pressure of seawater parameter B2", + ) + + for v in params.component_objects(Var): + v.fix() + + self.pressure_sat = Var( + initialize=1e3, + bounds=(1, 1e8), + units=pyunits.Pa, + doc="Saturation vapor pressure", + ) + + # Nayar et al.(2016), eq. 5 and 6, 0-180 C, 0-160 g/kg + def rule_pressure_sat(b): + t = b.temperature + S_kg_kg = ( + pyunits.convert( + b.total_dissolved_solids, to_units=pyunits.kg / pyunits.m**3 + ) + / b.dens_mass_phase["Liq"] + ) + S_g_kg = S_kg_kg * 1000 * pyunits.g / pyunits.kg + psatw = ( + exp( + b.params.pressure_sat_param_psatw_A1 * t**-1 + + b.params.pressure_sat_param_psatw_A2 + + b.params.pressure_sat_param_psatw_A3 * t + + b.params.pressure_sat_param_psatw_A4 * t**2 + + b.params.pressure_sat_param_psatw_A5 * t**3 + + b.params.pressure_sat_param_psatw_A6 * log(t / pyunits.K) + ) + * pyunits.Pa + ) + return b.pressure_sat == psatw * exp( + b.params.pressure_sat_param_B1 * S_g_kg + + b.params.pressure_sat_param_B2 * S_g_kg**2 + ) + + self.eq_pressure_sat = Constraint(rule=rule_pressure_sat) + # ----------------------------------------------------------------------------- # General Methods # NOTE: For scaling in the control volume to work properly, these methods must @@ -2119,7 +2444,7 @@ def calculate_scaling_factors(self): # default scaling factors have already been set with # idaes.core.property_base.calculate_scaling_factors() # for the following variables: pressure, - # temperature, dens_mass, visc_d_phase, diffus_phase_comp + # temperature, dens_mass, visc_d_phase, diffus_phase_comp, enth_mass_phase, pressure_sat for j, v in self.mw_comp.items(): if iscale.get_scaling_factor(v) is None: @@ -2375,18 +2700,20 @@ def calculate_scaling_factors(self): iscale.set_scaling_factor(self.elec_cond_phase[ind], sf) if self.is_property_constructed("flow_vol_phase"): - sf = ( - iscale.get_scaling_factor( - self.flow_mol_phase_comp["Liq", "H2O"], default=1 + if iscale.get_scaling_factor(self.flow_vol_phase) is None: + sf = ( + iscale.get_scaling_factor( + self.flow_mol_phase_comp["Liq", "H2O"], default=1 + ) + * iscale.get_scaling_factor(self.mw_comp["H2O"]) + / iscale.get_scaling_factor(self.dens_mass_phase["Liq"]) ) - * iscale.get_scaling_factor(self.mw_comp["H2O"]) - / iscale.get_scaling_factor(self.dens_mass_phase["Liq"]) - ) - iscale.set_scaling_factor(self.flow_vol_phase, sf) + iscale.set_scaling_factor(self.flow_vol_phase, sf) if self.is_property_constructed("flow_vol"): - sf = iscale.get_scaling_factor(self.flow_vol_phase) - iscale.set_scaling_factor(self.flow_vol, sf) + if iscale.get_scaling_factor(self.flow_vol) is None: + sf = iscale.get_scaling_factor(self.flow_vol_phase) + iscale.set_scaling_factor(self.flow_vol, sf) if self.is_property_constructed("molality_phase_comp"): for j in self.params.solute_set: @@ -2443,6 +2770,14 @@ def calculate_scaling_factors(self): else: sf = 1 / value(self.total_dissolved_solids) iscale.set_scaling_factor(self.total_dissolved_solids, sf) + + if self.is_property_constructed("enth_flow"): + iscale.set_scaling_factor( + self.enth_flow, + iscale.get_scaling_factor(self.flow_mass_phase_comp["Liq", "H2O"]) + * iscale.get_scaling_factor(self.enth_mass_phase["Liq"]), + ) + # transforming constraints transform_property_constraints(self) diff --git a/watertap/property_models/tests/test_multicomp_aq_sol_prop_pack.py b/watertap/property_models/tests/test_multicomp_aq_sol_prop_pack.py index 50ebf07feb..5ce08ad6c4 100644 --- a/watertap/property_models/tests/test_multicomp_aq_sol_prop_pack.py +++ b/watertap/property_models/tests/test_multicomp_aq_sol_prop_pack.py @@ -445,6 +445,8 @@ def test_build(model3): "act_coeff_phase_comp", "total_hardness", "total_dissolved_solids", + "enth_mass_phase", + "pressure_sat", ] # test on demand constraints @@ -454,9 +456,9 @@ def test_build(model3): c = getattr(m.fs.stream[0], "eq_" + v) assert isinstance(c, Constraint) - assert number_variables(m) == 94 - assert number_total_constraints(m) == 71 - assert number_unused_variables(m) == 6 + assert number_variables(m) == 126 + assert number_total_constraints(m) == 73 + assert number_unused_variables(m) == 5 @pytest.mark.unit @@ -488,6 +490,8 @@ def test_default_scaling(model3): ("visc_d_phase", "Liq"): 1e3, ("diffus_phase_comp", "Liq"): 1e10, ("visc_k_phase", "Liq"): 1e6, + ("enth_mass_phase", "Liq"): 1e-5, + ("pressure_sat", None): 1e-5, } assert len(default_scaling_var_dict) == len(m.fs.properties.default_scaling_factor) @@ -499,7 +503,6 @@ def test_default_scaling(model3): @pytest.mark.unit def test_scaling(model3): m = model3 - # m.fs.stream.initialize() metadata = m.fs.properties.get_metadata().properties for v in metadata.list_supported_properties(): @@ -602,7 +605,6 @@ def test_seawater_data(): metadata = m.fs.properties.get_metadata().properties for v in metadata.list_supported_properties(): getattr(stream[0], v.name) - assert stream[0].is_property_constructed("conc_mol_phase_comp") assert_units_consistent(m) @@ -799,7 +801,10 @@ def test_seawater_data(): * 100.0869 ) ) - assert value(stream[0].total_dissolved_solids) == pytest.approx(35974.42) + assert value(stream[0].total_dissolved_solids) == pytest.approx(35974.42, rel=1e-3) + assert value(stream[0].enth_mass_phase["Liq"]) == pytest.approx(98938.56, rel=1e-3) + assert value(stream[0].enth_flow) == pytest.approx(98918.931, rel=1e-3) + assert value(stream[0].pressure_sat) == pytest.approx(3110.73, rel=1e-3) @pytest.mark.component diff --git a/watertap/unit_models/surrogate_crystallizer.py b/watertap/unit_models/surrogate_crystallizer.py new file mode 100644 index 0000000000..e4e65b0b7b --- /dev/null +++ b/watertap/unit_models/surrogate_crystallizer.py @@ -0,0 +1,568 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2024, 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 libraries +from pyomo.environ import ( + Set, + Var, + check_optimal_termination, + Param, + Constraint, + Suffix, + units as pyunits, + NonNegativeReals, +) +from pyomo.common.config import ConfigBlock, ConfigValue, In + +# Import IDAES cores +from idaes.core import ( + declare_process_block_class, + UnitModelBlockData, + useDefault, +) +from idaes.core.solvers import get_solver +from idaes.core.util.tables import create_stream_table_dataframe +from idaes.core.util.config import is_physical_parameter_block +from idaes.core.util.exceptions import InitializationError +import idaes.core.util.scaling as iscale +import idaes.logger as idaeslog +from watertap.costing.unit_models.surrogate_crystallizer import ( + cost_surrogate_crystallizer, +) + +_log = idaeslog.getLogger(__name__) + +__author__ = "Oluwamayowa Amusat, Adam Atia" + + +@declare_process_block_class("SurrogateCrystallizer") +class SurrogateCrystallizerData(UnitModelBlockData): + """ + ML-based crystallizer model + """ + + CONFIG = ConfigBlock() + + CONFIG.declare( + "dynamic", + ConfigValue( + domain=In([False]), + default=False, + description="Dynamic model flag - must be False", + doc="""Indicates whether this model will be dynamic or not, + **default** = False. NF units do not support dynamic + behavior.""", + ), + ) + CONFIG.declare( + "has_holdup", + ConfigValue( + default=False, + domain=In([False]), + description="Holdup construction flag - must be False", + doc="""Indicates whether holdup terms should be constructed or not. + **default** - False. NF units do not have defined volume, thus + this must be False.""", + ), + ) + CONFIG.declare( + "property_package", + ConfigValue( + default=useDefault, + domain=is_physical_parameter_block, + description="Property package to use for control volume", + doc="""Property parameter object used to define property calculations, + **default** - useDefault. + **Valid values:** { + **useDefault** - use default package from parent model or flowsheet, + **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", + ), + ) + CONFIG.declare( + "property_package_args", + ConfigBlock( + implicit=True, + description="Arguments to use for constructing property packages", + doc="""A ConfigBlock with arguments to be passed to a property block(s) + and used when constructing these, + **default** - None. + **Valid values:** { + see property package for documentation.}""", + ), + ) + CONFIG.declare( + "vapor_property_package", + ConfigValue( + default=useDefault, + domain=is_physical_parameter_block, + description="Property package to use for vapor phase", + doc="""Property parameter object used to define property calculations + for the vapor phase, + **default** - useDefault. + **Valid values:** { + **useDefault** - use default package from parent model or flowsheet, + **PropertyParameterObject** - a PropertyParameterBlock object.}""", + ), + ) + CONFIG.declare( + "vapor_property_package_args", + ConfigBlock( + implicit=True, + description="Arguments to use for constructing vapor phase properties", + doc="""A ConfigBlock with arguments to be passed to vapor phase +property block(s) and used when constructing these, +**default** - None. +**Valid values:** { +see property package for documentation.}""", + ), + ) + CONFIG.declare( + "solids_ions_dict", + ConfigValue( + default={}, + domain=dict, + description="Specification of ion makeup of each solid in system. e.g., {'NaCl': {'Na_+': 1, 'Cl_-': -1} }", + doc="""Solids makeup; keys are solid names, and values include subkeys of ion names and values of charge.""", + ), + ) + + def build(self): + + # Call UnitModel.build to setup dynamics + super().build() + + self.scaling_factor = Suffix(direction=Suffix.EXPORT) + units_meta = self.config.property_package.get_metadata().get_derived_units + + self.solids_list = Set(initialize=self.config.solids_ions_dict.keys()) + + # Add ion in solid ratios as parameters + dict1 = dict() + for m in self.solids_list: + for p in self.config.property_package.ion_set: + dict1[m, p] = float( + self.config.solids_ions_dict[m][p] + if p in self.config.solids_ions_dict[m] + else 0.0 + ) + self.mwc = Param( + self.solids_list, self.config.property_package.ion_set, initialize=dict1 + ) + + # Add other variables + self.temperature_operating = Var( + initialize=298.15, + domain=NonNegativeReals, + units=pyunits.K, + bounds=(273, 1000), + doc="Crystallizer operating temperature in K", + ) + self.pressure_operating = Var( + initialize=101325, + domain=NonNegativeReals, + units=pyunits.Pa, + bounds=(0.001, 1e6), + doc="Crystallizer pressure in Pa", + ) + self.evaporation_percent = Var( + initialize=50, + domain=NonNegativeReals, + units=pyunits.dimensionless, + bounds=(10, 95), + doc="Crystallizer percentage of water evaporation", + ) + self.flow_mass_sol_comp_true = Var( + self.config.property_package.ion_set, + initialize=0, + domain=NonNegativeReals, + units=pyunits.kg / pyunits.s, + doc="True species solid mass flow (kg)", + ) + self.flow_mass_sol_comp_apparent = Var( + self.solids_list, + initialize=0, + domain=NonNegativeReals, + units=pyunits.kg / pyunits.s, + doc="Apparent species solid mass flow (kg)", + ) + self.flow_mass_sol_total = Var( + initialize=0, + domain=NonNegativeReals, + units=pyunits.kg / pyunits.s, + doc="Total solid flow", + ) + self.flow_mass_liq_total = Var( + initialize=0, + domain=NonNegativeReals, + units=pyunits.kg / pyunits.s, + doc="Total liquid flow", + ) + self.flow_mass_vap_total = Var( + initialize=0, + domain=NonNegativeReals, + units=pyunits.kg / pyunits.s, + doc="Total vapor flow", + ) + + self.heat_required = Var( + initialize=1e5, + domain=NonNegativeReals, + units=pyunits.kW, + bounds=(-5e6, 5e6), + doc="Heat requirement for crystallizer (kW)", + ) + + # # Add state blocks for inlet and three outlets + # Add inlet block + tmp_dict = dict(**self.config.property_package_args) + tmp_dict["has_phase_equilibrium"] = False + tmp_dict["parameters"] = self.config.property_package + tmp_dict["defined_state"] = True # inlet block is an inlet + self.properties_in = self.config.property_package.state_block_class( + self.flowsheet().config.time, doc="Material properties of inlet", **tmp_dict + ) + + # Add liquid outlet + tmp_dict["defined_state"] = False # outlet and waste block is not an inlet + self.properties_out_liq = self.config.property_package.state_block_class( + self.flowsheet().config.time, + doc="Material properties of liquid outlet", + **tmp_dict, + ) + + # Add vapor outlet + tmp_dict2 = dict() + tmp_dict2["has_phase_equilibrium"] = False + tmp_dict2["parameters"] = self.config.vapor_property_package + tmp_dict["defined_state"] = False + self.properties_out_vapor = ( + self.config.vapor_property_package.state_block_class( + self.flowsheet().config.time, + doc="Material properties of vapor outlet", + **tmp_dict2, + ) + ) + + # Add ports - oftentimes users interact with these rather than the state blocks + self.add_port(name="inlet", block=self.properties_in) + self.add_port(name="outlet", block=self.properties_out_liq) + self.add_port(name="vapor", block=self.properties_out_vapor) + + # Add constraints + # 1. Fix empty flows: + + for p in self.config.vapor_property_package.phase_list: + if p == "Liq": + self.properties_out_vapor[0].flow_mass_phase_comp[p, "H2O"].fix(0.0) + + # 2. Material balances + @self.Constraint( + self.config.property_package.component_list, + doc="Mass balance for components", + ) + def eq_mass_balance_constraints(b, j): + if j != "H2O": + return ( + sum( + b.properties_in[0].flow_mass_phase_comp[p, j] + for p in self.config.property_package.phase_list + ) + == sum( + b.properties_out_liq[0].flow_mass_phase_comp[p, j] + for p in self.config.property_package.phase_list + ) + + b.flow_mass_sol_comp_true[j] + ) + else: + return sum( + b.properties_in[0].flow_mass_phase_comp[p, j] + for p in self.config.property_package.phase_list + ) == sum( + b.properties_out_liq[0].flow_mass_phase_comp[p, j] + for p in self.config.property_package.phase_list + ) + sum( + b.properties_out_vapor[0].flow_mass_phase_comp[p, j] + for p in self.config.vapor_property_package.phase_list + ) + + # 3. Temperature and pressure balances: + @self.Constraint() + def eq_T_con1(b): + return self.temperature_operating == b.properties_out_liq[0].temperature + + @self.Constraint() + def eq_T_con2(b): + return self.temperature_operating == b.properties_out_vapor[0].temperature + + @self.Constraint() + def eq_P_con1(b): + return self.pressure_operating == b.properties_out_liq[0].pressure + + @self.Constraint() + def eq_P_con2(b): + return self.pressure_operating == b.properties_out_vapor[0].pressure + + @self.Constraint( + self.config.property_package.component_list, + doc="Liquid-Vapour phase water split", + ) + def eq_water_balance_constraints(b, j): + if j == "H2O": + return ( + sum( + b.properties_out_vapor[0].flow_mass_phase_comp[p, j] + for p in self.config.vapor_property_package.phase_list + ) + == b.properties_in[0].flow_mass_phase_comp["Liq", j] + * b.evaporation_percent + / 100 + ) + else: + return Constraint.Skip + + @self.Constraint( + self.config.property_package.component_list, + doc="Mass balance for components", + ) + def eq_solid_amount(b, j): + if j == "H2O": + return Constraint.Skip + else: + return b.flow_mass_sol_comp_true[j] == sum( + (b.mwc[q, j] * b.properties_in[0].params.mw_comp[j]) + / sum( + b.mwc[q, j] * b.properties_in[0].params.mw_comp[j] + for j in b.config.property_package.ion_set + ) + * b.flow_mass_sol_comp_apparent[q] + for q in b.solids_list + ) + + # 5. Constraints calculating total flows for each outlet stream + @self.Constraint( + doc="Total solid flow constraint", + ) + def eq_total_solids_constraint(b): + return b.flow_mass_sol_total == sum( + b.flow_mass_sol_comp_apparent[k] for k in b.solids_list + ) + + @self.Constraint( + doc="Total liquid flow constraint", + ) + def eq_total_liquid_constraint(b): + return b.flow_mass_liq_total == sum( + sum( + b.properties_out_liq[0].flow_mass_phase_comp[p, j] + for p in self.config.property_package.phase_list + ) + for j in self.config.property_package.component_list + ) + + @self.Constraint( + doc="Total vapor flow constraint", + ) + def eq_total_vapor_constraint(b): + return b.flow_mass_vap_total == sum( + b.properties_out_vapor[0].flow_mass_phase_comp[p, "H2O"] + for p in self.config.vapor_property_package.phase_list + ) + + # 6. Constraints for computing heat requirement + @self.Constraint( + doc="Energy balance", + ) + def eq_energy_balance_constraint(b): + return pyunits.convert( + b.heat_required, to_units=pyunits.W + ) + b.properties_in[0].enth_flow == b.properties_out_liq[0].enth_flow + sum( + b.properties_out_vapor[0].enth_flow_phase[p] + for p in self.config.vapor_property_package.phase_list + ) + + def initialize_build( + self, + state_args=None, + outlvl=idaeslog.NOTSET, + solver=None, + optarg=None, + ): + """ + General wrapper for pressure changer initialization routines + + Keyword Arguments: + state_args : a dict of arguments to be passed to the property + package(s) to provide an initial state for + initialization (see documentation of the specific + property package) (default = {}). + outlvl : sets output level of initialization routine + optarg : solver options dictionary object (default=None) + solver : str indicating which solver to use during + initialization (default = None) + + Returns: None + """ + init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit") + solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit") + + opt = get_solver(solver, optarg) + + # --------------------------------------------------------------------- + # Initialize holdup block + flags = self.properties_in.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args, + hold_state=True, + ) + init_log.info_high("Initialization Step 1 Complete.") + + # --------------------------------------------------------------------- + # Initialize other state blocks + # Set state_args from inlet state + if state_args is None: + state_args = {} + state_dict = self.properties_in[ + self.flowsheet().config.time.first() + ].define_port_members() + + for k in state_dict.keys(): + if state_dict[k].is_indexed(): + state_args[k] = {} + for m in state_dict[k].keys(): + state_args[k][m] = state_dict[k][m].value + else: + state_args[k] = state_dict[k].value + + self.properties_out_liq.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args, + ) + + state_args_vapor = {} + state_dict_vapor = self.properties_out_vapor[ + self.flowsheet().config.time.first() + ].define_port_members() + + for k in state_dict_vapor.keys(): + if state_dict_vapor[k].is_indexed(): + state_args_vapor[k] = {} + for m in state_dict_vapor[k].keys(): + state_args_vapor[k][m] = state_dict_vapor[k][m].value + else: + state_args_vapor[k] = state_dict_vapor[k].value + for p in self.config.vapor_property_package.phase_list: + if p == "Vap": + state_args_vapor["flow_mass_phase_comp"][p, "H2O"] = state_args[ + "flow_mass_phase_comp" + ]["Liq", "H2O"] + else: + state_args_vapor["flow_mass_phase_comp"][p, "H2O"] = 0 + self.properties_out_vapor.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + state_args=state_args_vapor, + ) + init_log.info_high("Initialization Step 2 Complete.") + # --------------------------------------------------------------------- + # Solve unit + with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: + res = opt.solve(self, tee=slc.tee) + + init_log.info_high("Initialization Step 3 {}.".format(idaeslog.condition(res))) + # --------------------------------------------------------------------- + # Release Inlet state + self.properties_in.release_state(flags, outlvl=outlvl) + init_log.info("Initialization Complete: {}".format(idaeslog.condition(res))) + if not check_optimal_termination(res): + raise InitializationError(f"Unit model {self.name} failed to initialize") + + def _get_stream_table_contents(self, time_point=0): + return create_stream_table_dataframe( + { + "Feed Inlet": self.inlet, + "Liquid Outlet": self.outlet, + "Vapor Outlet": self.vapor, + }, + time_point=time_point, + ) + + def _get_performance_contents(self, time_point=0): + var_dict = {} + var_dict["Operating Temperature (K)"] = self.temperature_operating + var_dict["Vapor Pressure (Pa)"] = self.pressure_operating + var_dict["Total solids at outlet (Kg)"] = self.flow_mass_sol_total + var_dict["Total liquid flow at outlet (Kg)"] = self.flow_mass_liq_total + var_dict["Total water vapor flow at outlet (Kg)"] = self.flow_mass_vap_total + var_dict["Heat requirement"] = self.heat_required + return {"vars": var_dict} + + def calculate_scaling_factors(self): + + super().calculate_scaling_factors() + + iscale.set_scaling_factor( + self.temperature_operating, + iscale.get_scaling_factor(self.properties_in[0].temperature), + ) + iscale.set_scaling_factor(self.pressure_operating, 1e-3) + iscale.set_scaling_factor( + self.heat_required, + iscale.get_scaling_factor( + self.properties_in[0].flow_mass_phase_comp["Liq", "H2O"] + ) + * iscale.get_scaling_factor(self.properties_in[0].enth_mass_phase["Liq"]) + * 1e3, + ) + + # transforming constraints + for ind, c in self.eq_T_con1.items(): + sf = iscale.get_scaling_factor(self.properties_in[0].temperature) + iscale.constraint_scaling_transform(c, sf) + + for ind, c in self.eq_T_con2.items(): + sf = iscale.get_scaling_factor(self.properties_in[0].temperature) + iscale.constraint_scaling_transform(c, sf) + + for ind, c in self.eq_P_con1.items(): + sf = iscale.get_scaling_factor(self.properties_in[0].pressure) + iscale.constraint_scaling_transform(c, sf) + + for ind, c in self.eq_P_con2.items(): + sf = iscale.get_scaling_factor(self.properties_in[0].pressure) + iscale.constraint_scaling_transform(c, sf) + + for j, c in self.eq_mass_balance_constraints.items(): + sf = iscale.get_scaling_factor( + self.properties_in[0].flow_mass_phase_comp["Liq", j] + ) + iscale.constraint_scaling_transform(c, sf) + + for j, c in self.eq_water_balance_constraints.items(): + sf = iscale.get_scaling_factor( + self.properties_in[0].flow_mass_phase_comp["Liq", "H2O"] + ) + iscale.constraint_scaling_transform(c, sf) + + for j, c in self.eq_energy_balance_constraint.items(): + sf = iscale.get_scaling_factor( + self.properties_in[0].flow_mass_phase_comp["Liq", "H2O"] + ) * iscale.get_scaling_factor(self.properties_in[0].enth_mass_phase["Liq"]) + iscale.constraint_scaling_transform(c, sf) + + @property + def default_costing_method(self): + return cost_surrogate_crystallizer diff --git a/watertap/unit_models/tests/test_surrogate_crystallizer.py b/watertap/unit_models/tests/test_surrogate_crystallizer.py new file mode 100644 index 0000000000..4e42b29519 --- /dev/null +++ b/watertap/unit_models/tests/test_surrogate_crystallizer.py @@ -0,0 +1,231 @@ +################################################################################# +# WaterTAP Copyright (c) 2020-2024, 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 os +import pytest +from pyomo.environ import ( + ConcreteModel, + Var, + Constraint, + units as pyunits, + assert_optimal_termination, +) +from pyomo.util.check_units import assert_units_consistent +from idaes.core import FlowsheetBlock +from idaes.core.util.exceptions import ConfigurationError +from watertap.property_models.water_prop_pack import WaterParameterBlock +from watertap.property_models.multicomp_aq_sol_prop_pack import ( + MCASParameterBlock, + MaterialFlowBasis, +) +from idaes.core.util.model_statistics import degrees_of_freedom + +from idaes.core.surrogate.surrogate_block import SurrogateBlock +from idaes.core.surrogate.pysmo_surrogate import ( + PysmoSurrogate, +) +from watertap.unit_models.surrogate_crystallizer import SurrogateCrystallizer + +from idaes.core import UnitModelCostingBlock +from watertap.costing import WaterTAPCosting +from watertap.core.solvers import get_solver + +__author__ = "Oluwamayowa Amusat, Adam Atia" + + +# TODO: can consider adding NN model surrogate function and test +def add_crystallizer_rbf_model( + blk, + surrogate_inputs_with_bounds, + surrogate_outputs, + surrogate_to_flowsheet_basis_ratio, +): + ############################################################################################################## + # block loading into IDAES Surrogate Block + ############################################################################################################## + crystallizer_inputs = [ + surrogate_inputs_with_bounds[k]["flowsheet_var"] + for k in surrogate_inputs_with_bounds.keys() + ] + crystallizer_outputs = [ + surrogate_outputs[k]["flowsheet_var"] for k in surrogate_outputs.keys() + ] + + try: + assert set(list(blk.solids_list)).issubset(surrogate_outputs.keys()) + except AssertionError: + raise ConfigurationError( + "List of solids provided in crystallizer model must match surrogate output keys for solids. Please check." + ) + + filename = [ + "Vapor_Pressure", + "Calcite_g", + "Anhydrite_g", + "Glauberite_g", + "Halite_g", + ] + + for sm in range(0, len(filename)): + block_name = "crystallizer_surrogate" + "_" + filename[sm] + blk.add_component(block_name, SurrogateBlock(concrete=True)) + surrogate_name = f"{filename[sm]}.json" + surrogate_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "..", + "data", + "surrogate_defaults", + "surrogate_crystallizer_defaults", + surrogate_name, + ) + current_surrogate = PysmoSurrogate.load_from_file(surrogate_path) + + getattr(blk, block_name).build_model( + current_surrogate, + input_vars=crystallizer_inputs, + output_vars=crystallizer_outputs[sm], + ) + + # Add constraints tying provided mass variables with crystallizer unit variables - done to avoid unit challenges + def eq_mass_conversion_constraint(b, j): + return ( + b.flow_mass_sol_comp_apparent[j] + == pyunits.convert( + surrogate_outputs[j]["flowsheet_var"], to_units=pyunits.kg / pyunits.s + ) + / surrogate_to_flowsheet_basis_ratio + ) + + blk.mass_conversion_constraints = Constraint( + blk.solids_list, rule=eq_mass_conversion_constraint + ) + + # Add a constraint to tie pressures together after unit conversion + blk.pressure_conversion_constraints = Constraint( + expr=blk.pressure_operating + == pyunits.convert( + surrogate_outputs["Vapor Pressure (atm)"]["flowsheet_var"], + to_units=pyunits.Pa, + ) + ) + + +@pytest.mark.component +def test_rbf_surrogate(): + m = ConcreteModel() + m.case = "BGW1" + m.fs = FlowsheetBlock(dynamic=False) + + input_ions = ["Cl_-", "Na_+", "SO4_2-", "Mg_2+", "Ca_2+", "K_+", "HCO3_-"] + solids_list = { + "Calcite_g": {"Ca_2+": 1, "HCO3_-": 1}, + "Anhydrite_g": {"Ca_2+": 1, "SO4_2-": 1}, + "Glauberite_g": {"Ca_2+": 1, "Na_+": 2, "SO4_2-": 2}, + "Halite_g": {"Na_+": 1, "Cl_-": 1}, + } + m.fs.cryst_prop_feed = MCASParameterBlock( + solute_list=input_ions, + material_flow_basis=MaterialFlowBasis.mass, + ) + + m.fs.water_properties_vapor = WaterParameterBlock() + + # Create crystallizer framework + m.fs.cryst = SurrogateCrystallizer( + property_package=m.fs.cryst_prop_feed, + vapor_property_package=m.fs.water_properties_vapor, + solids_ions_dict=solids_list, + ) + + # Feed composition + feed = { + "ion_composition_g_kg": { + "Na_+": 91.49859876, + "K_+": 3.185931759, + "Cl_-": 159.0941278, + "Ca_2+": 0.788476605, + "Mg_2+": 10.5806906, + "HCO3_-": 1.614773395, + "SO4_2-": 22.20893266, + }, + "water_content_kg": 12.0764972138554, + } + + g_to_kg = 1e-3 + for i in m.fs.cryst_prop_feed.component_list: + if i == "H2O": + m.fs.cryst.inlet.flow_mass_phase_comp[0.0, "Liq", i].fix( + feed["water_content_kg"] + ) + else: + m.fs.cryst.inlet.flow_mass_phase_comp[0.0, "Liq", i].fix( + feed["water_content_kg"] * feed["ion_composition_g_kg"][i] * g_to_kg + ) + + m.fs.cryst.inlet.temperature.fix(298.15) + m.fs.cryst.inlet.pressure.fix(101325) + + assert_units_consistent(m) + + # Create dummy variables for inputs and outputs + surrogate_inputs = { + "Temperature": { + "flowsheet_var": m.fs.cryst.temperature_operating, + "var_bounds": (303.15, 372.15), + }, + "Evaporation percent": { + "flowsheet_var": m.fs.cryst.evaporation_percent, + "var_bounds": (30.0, 98.0), + }, + } + m.fs.o1 = Var(units=pyunits.atm) + m.fs.o2 = Var(units=pyunits.g / pyunits.s) + m.fs.o3 = Var(units=pyunits.g / pyunits.s) + m.fs.o4 = Var(units=pyunits.g / pyunits.s) + m.fs.o5 = Var(units=pyunits.g / pyunits.s) + surrogate_outputs = { + "Vapor Pressure (atm)": {"flowsheet_var": m.fs.o1, "Solid": False}, + "Calcite_g": {"flowsheet_var": m.fs.o2, "Solid": True}, + "Anhydrite_g": {"flowsheet_var": m.fs.o3, "Solid": True}, + "Glauberite_g": {"flowsheet_var": m.fs.o4, "Solid": True}, + "Halite_g": {"flowsheet_var": m.fs.o5, "Solid": True}, + } + + add_crystallizer_rbf_model( + m.fs.cryst, + surrogate_inputs_with_bounds=surrogate_inputs, + surrogate_outputs=surrogate_outputs, + surrogate_to_flowsheet_basis_ratio=1, + ) + + # Costing + m.fs.costing = WaterTAPCosting() + m.fs.cryst.costing = UnitModelCostingBlock( + flowsheet_costing_block=m.fs.costing, + ) + m.fs.costing.cost_process() + + # Edit bound on liquid pressures from property package + m.fs.cryst.properties_out_liq[0].pressure.setlb(1000) + + # 1. Simulate single case + m.fs.cryst.temperature_operating.fix(40 + 273.15) + m.fs.cryst.evaporation_percent.fix(60) + + # Initialize and solve + assert degrees_of_freedom(m) == 0 + m.fs.cryst.initialize() + solver = get_solver() + res = solver.solve(m) + assert_optimal_termination(res) + m.fs.cryst.report()