Source code for watertap.examples.flowsheets.gac.gac_ui

#################################################################################
# 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/"
#################################################################################
"""
GUI configuration for the GAC model.
"""

from pyomo.environ import units as pyunits
from idaes.core import MaterialFlowBasis
from idaes.core.solvers import get_solver
from watertap.property_models.multicomp_aq_sol_prop_pack import DiffusivityCalculation
from watertap.unit_models.gac import (
    FilmTransferCoefficientType,
    SurfaceDiffusionCoefficientType,
)
from watertap.costing.unit_models.gac import ContactorType
from watertap.examples.flowsheets.gac import (
    gac as gac_fs,
)
from watertap.ui.fsapi import FlowsheetInterface, FlowsheetCategory

__author__ = "Hunter Barber"


[docs]def export_to_ui(): """ Exports the variables, flowsheet build, and solver results to the GUI. """ return FlowsheetInterface( name="Granular Activated Carbon (GAC)", do_export=export_variables, do_build=build_flowsheet, do_solve=solve_flowsheet, requires_idaes_solver=True, category=FlowsheetCategory.wastewater, build_options={ "MaterialFlowBasis": { "name": "MaterialFlowBasis", "display_name": "Material Flow Basis", "values_allowed": [ x.name for x in [MaterialFlowBasis.molar, MaterialFlowBasis.mass] ], "value": MaterialFlowBasis.molar.name, }, "FilmTransferCoefficientType": { "name": "FilmTransferCoefficientType", "display_name": "Film Transfer Coefficient Type", "values_allowed": [x.name for x in FilmTransferCoefficientType], "value": FilmTransferCoefficientType.fixed.name, }, "SurfaceDiffusionCoefficientType": { "name": "SurfaceDiffusionCoefficientType", "display_name": "Surface Diffusion Coefficient Type", "values_allowed": [x.name for x in SurfaceDiffusionCoefficientType], "value": SurfaceDiffusionCoefficientType.fixed.name, }, "DiffusivityCalculation": { "name": "DiffusivityCalculation", "display_name": "Diffusivity Calculation", "values_allowed": [x.name for x in DiffusivityCalculation], "value": DiffusivityCalculation.none.name, }, "ContactorType": { "name": "ContactorType", "display_name": "Cost Contactor Type", "values_allowed": [x.name for x in ContactorType], "value": ContactorType.pressure.name, }, }, )
[docs]def export_variables(flowsheet=None, exports=None, build_options=None, **kwargs): """ Exports the variables to the GUI. """ fs = flowsheet rounding = 3 sci_not_rounding = 20 solute_name = "solute" fs.costing.display() # input data # --------------------------------------------------------------------- # solute properties category = "Solute properties" exports.add( obj=fs.properties.mw_comp["solute"], name="MW", ui_units=pyunits.gram / pyunits.mol, display_units="g/mol", rounding=rounding, description="Molecular weight", is_input=True, input_category=category, is_output=False, ) if ( build_options["FilmTransferCoefficientType"].value == "calculated" or build_options["SurfaceDiffusionCoefficientType"].value == "calculated" ): if build_options["DiffusivityCalculation"].value == "none": exports.add( obj=fs.properties.diffus_phase_comp["Liq", "solute"], name="Diffusivity", ui_units=pyunits.m**2 / pyunits.s, display_units="m2/s", rounding=sci_not_rounding, description="Diffusivity", is_input=True, input_category=category, is_output=False, ) else: exports.add( obj=fs.properties.molar_volume_phase_comp["Liq", "solute"], name="Molar volume", ui_units=pyunits.m**3 / pyunits.mol, display_units="m3/mol", rounding=sci_not_rounding, description="Molar volume", is_input=True, input_category=category, is_output=False, ) # --------------------------------------------------------------------- # feed conditions category = "Feed" exports.add( obj=fs.feed.properties[0].temperature, name="Temperature", ui_units=pyunits.kelvin, display_units="K", rounding=rounding, description="Feed temperature", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.feed.properties[0].pressure, name="Pressure", ui_units=pyunits.bar, display_units="bar", rounding=rounding, description="Feed pressure", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.feed.properties[0].flow_vol_phase["Liq"], name="Volumetric flow rate", ui_units=pyunits.Mgallon / pyunits.day, display_units="MGD", rounding=rounding, description="Feed volumetric flow rate", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.feed.properties[0].conc_mass_phase_comp["Liq", solute_name], name="Solute concentration", ui_units=pyunits.g / pyunits.L, display_units="g/L", rounding=rounding, description="Feed solute concentration", is_input=False, is_output=True, output_category=category, ) if build_options["MaterialFlowBasis"].value == "molar": fix_molar = True fix_mass = False else: fix_molar = False fix_mass = True exports.add( obj=fs.feed.properties[0].flow_mol_phase_comp["Liq", "H2O"], name="Molar flow rate water", ui_units=pyunits.mol / pyunits.s, display_units="mol/s", rounding=rounding, description="Feed molar flow rate of water", is_input=fix_molar, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.feed.properties[0].flow_mol_phase_comp["Liq", solute_name], name="Molar flow rate solute", ui_units=pyunits.mol / pyunits.s, display_units="mol/s", rounding=rounding, description="Feed molar flow rate of solute", is_input=fix_molar, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.feed.properties[0].flow_mass_phase_comp["Liq", "H2O"], name="Mass flow rate water", ui_units=pyunits.kg / pyunits.s, display_units="kg/s", rounding=rounding, description="Feed mass flow rate of water", is_input=fix_mass, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.feed.properties[0].flow_mass_phase_comp["Liq", solute_name], name="Mass flow rate solute", ui_units=pyunits.kg / pyunits.s, display_units="kg/s", rounding=rounding, description="Feed mass flow rate of solute", is_input=fix_mass, input_category=category, is_output=True, output_category=category, ) # --------------------------------------------------------------------- # product conditions category = "Product" exports.add( obj=fs.product.properties[0].temperature, name="Temperature", ui_units=pyunits.kelvin, display_units="K", rounding=rounding, description="Product temperature", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.product.properties[0].pressure, name="Pressure", ui_units=pyunits.bar, display_units="bar", rounding=rounding, description="Product pressure", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.product.properties[0].flow_vol_phase["Liq"], name="Volumetric flow rate", ui_units=pyunits.Mgallon / pyunits.day, display_units="MGD", rounding=rounding, description="Product volumetric flow rate", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.product.properties[0].conc_mass_phase_comp["Liq", solute_name], name="Solute concentration", ui_units=pyunits.g / pyunits.L, display_units="g/L", rounding=rounding, description="Product solute concentration", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.product.properties[0].flow_mol_phase_comp["Liq", "H2O"], name="Molar flow rate water", ui_units=pyunits.mol / pyunits.s, display_units="mol/s", rounding=rounding, description="Product molar flow rate of water", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.product.properties[0].flow_mol_phase_comp["Liq", solute_name], name="Molar flow rate solute", ui_units=pyunits.mol / pyunits.s, display_units="mol/s", rounding=rounding, description="Product molar flow rate of solute", is_input=False, is_output=True, output_category=category, ) # --------------------------------------------------------------------- # gac media category = "GAC media properties" exports.add( obj=fs.gac.particle_dens_app, name="Apparent density", ui_units=pyunits.kg * pyunits.m**-3, display_units="kg/m3", rounding=rounding, description="Apparent density of the GAC media", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.particle_dens_bulk, name="Bulk density", ui_units=pyunits.kg * pyunits.m**-3, display_units="kg/m3", rounding=rounding, description="Bulk density of the GAC media in the bed", is_input=False, is_output=False, ) exports.add( obj=fs.gac.particle_dia, name="Diameter", ui_units=pyunits.mm, display_units="mm", rounding=rounding, description="Particle diameter of the GAC media", is_input=True, input_category=category, is_output=False, ) if build_options["SurfaceDiffusionCoefficientType"].value == "calculated": exports.add( obj=fs.gac.particle_porosity, name="Porosity", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Particle porosity of the GAC media", is_input=True, input_category=category, is_output=False, ) # --------------------------------------------------------------------- # adsorption parameters category = "Adsorption parameters" exports.add( obj=fs.gac.freund_k, name="Freundlich k", ui_units=pyunits.dimensionless, display_units="(m3/kg)(1/n)", rounding=rounding, description="Freundlich isotherm parameter k", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.freund_ninv, name="Freundlich 1/n", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Freundlich isotherm parameter 1/n", is_input=True, input_category=category, is_output=False, ) if build_options["SurfaceDiffusionCoefficientType"].value == "fixed": ds_is_input = True else: ds_is_input = False exports.add( obj=fs.gac.tort, name="Tortuosity", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Tortuosity of the path that the adsorbate must take as compared to the radius", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.spdfr, name="SPDFR", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Surface-to-pore diffusion flux ratio", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.ds, name="Surface diffusion coefficient", ui_units=pyunits.m**2 * pyunits.s**-1, display_units="m2/s", rounding=sci_not_rounding, description="Surface diffusion coefficient", is_input=ds_is_input, input_category=category, is_output=False, ) if build_options["FilmTransferCoefficientType"].value == "fixed": kf_is_input = True else: kf_is_input = False exports.add( obj=fs.gac.shape_correction_factor, name="SCF", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Film transfer coefficient shape correction factor", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.kf, name="Film transfer coefficient", ui_units=pyunits.m * pyunits.s**-1, display_units="m/s", rounding=sci_not_rounding, description="Liquid phase film transfer coefficient", is_input=kf_is_input, input_category=category, is_output=False, ) # --------------------------------------------------------------------- # bed design category = "Bed design" exports.add( obj=fs.gac.velocity_sup, name="Superficial velocity", ui_units=pyunits.m * pyunits.hour**-1, display_units="m/h", rounding=rounding, description="Superficial velocity", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.velocity_int, name="Interstitial velocity", ui_units=pyunits.m * pyunits.hour**-1, display_units="m/h", rounding=rounding, description="Interstitial velocity", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.bed_length, name="Bed length", ui_units=pyunits.m, display_units="m", rounding=rounding, description="Length of the GAC bed", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.bed_diameter, name="Bed diameter", ui_units=pyunits.m, display_units="m", rounding=rounding, description="Effective bed diameter as if only once bed was present", is_input=True, input_category=category, is_output=True, output_category=category, is_readonly=True, ) exports.add( obj=fs.gac.bed_area, name="Bed area", ui_units=pyunits.m**2, display_units="m2", rounding=rounding, description="Effective total bed area for flow", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.bed_volume, name="Bed volume", ui_units=pyunits.m**3, display_units="m3", rounding=rounding, description="Effective total bed volume", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.ebct, name="EBCT", ui_units=pyunits.min, display_units="min", rounding=rounding, description="Empty bed contact time", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.residence_time, name="Residence time", ui_units=pyunits.min, display_units="min", rounding=rounding, description="Residence time", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.bed_voidage, name="Bed voidage", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Voidage of the GAC bed", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.bed_mass_gac, name="Media mass", ui_units=pyunits.kg, display_units="kg", rounding=rounding, description="Mass of media in the bed", is_input=True, input_category=category, is_output=True, output_category=category, ) # --------------------------------------------------------------------- # system performance category = "System performance" exports.add( obj=fs.gac.conc_ratio_replace, name="Breakthrough concentration ratio", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Concentration ratio at breakthrough, corresponding to the time of replacing the media", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.conc_ratio_avg, name="Average concentration ratio", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Average concentration ratio during operational time", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.operational_time, name="Breakthrough time", ui_units=pyunits.day, display_units="days", rounding=rounding, description="Time operating until breakthrough", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.bed_volumes_treated, name="BVT", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Bed volumes treated", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.mass_adsorbed, name="Solute adsorption rate", ui_units=pyunits.kg, display_units="kg", rounding=rounding, description="Mass adsorption rate of solute during operational time", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=fs.gac.gac_usage_rate, name="Media usage rate", ui_units=pyunits.kg * pyunits.day**-1, display_units="kg/day", rounding=rounding, description="Mass usage rate of media during operational time", is_input=True, input_category=category, is_output=True, output_category=category, ) # --------------------------------------------------------------------- # empirical parameters category = "Empirical parameters" exports.add( obj=fs.gac.a0, name="Stanton parameter 0", ui_units=pyunits.dimensionless, display_units="-", rounding=6, description="Stanton parameter 0", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.a1, name="Stanton parameter 1", ui_units=pyunits.dimensionless, display_units="-", rounding=6, description="Stanton parameter 1", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.b0, name="Throughput parameter 0", ui_units=pyunits.dimensionless, display_units="-", rounding=6, description="Throughput parameter 0", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.b1, name="Throughput parameter 1", ui_units=pyunits.dimensionless, display_units="-", rounding=6, description="Throughput parameter 1", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.b2, name="Throughput parameter 2", ui_units=pyunits.dimensionless, display_units="-", rounding=6, description="Throughput parameter 2", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.b3, name="Throughput parameter 3", ui_units=pyunits.dimensionless, display_units="-", rounding=6, description="Throughput parameter 3", is_input=True, input_category=category, is_output=False, ) exports.add( obj=fs.gac.b4, name="Throughput parameter 4", ui_units=pyunits.dimensionless, display_units="-", rounding=6, description="Throughput parameter 4", is_input=True, input_category=category, is_output=False, ) # --------------------------------------------------------------------- # costing options category = "Costing options" exports.add( obj=fs.costing.electricity_cost, name="Electricity cost", ui_units=fs.costing.base_currency / pyunits.kWh, display_units="$/kW", rounding=rounding, description="Electricity cost", is_input=True, input_category=category, is_output=True, output_category=category, ) if build_options["ContactorType"].value == "pressure": unit_model_costing = fs.costing.gac_pressure else: unit_model_costing = fs.costing.gac_gravity exports.add( obj=unit_model_costing.regen_frac, name="Regeneration fraction", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Fraction of media that can be regenerated after breakthrough (remainder is replaced)", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=unit_model_costing.num_contactors_op, name="Number of operating beds", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Number of beds in operation at any steady state time", is_input=True, input_category=category, is_output=True, output_category=category, ) exports.add( obj=unit_model_costing.num_contactors_redundant, name="Number of redundant beds", ui_units=pyunits.dimensionless, display_units="-", rounding=rounding, description="Number of beds offline (redundant) at any steady state time", is_input=True, input_category=category, is_output=True, output_category=category, ) # --------------------------------------------------------------------- # cost results category = "Cost Results" exports.add( obj=fs.costing.aggregate_capital_cost, name="Capital costs", ui_units=fs.costing.base_currency, display_units="$", rounding=0, description="Capital costs", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.costing.aggregate_fixed_operating_cost, name="Fixed operating costs", ui_units=fs.costing.base_currency / pyunits.year, display_units="$/yr", rounding=0, description="Fixed operating costs", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.costing.aggregate_flow_costs["electricity"], name="Electricity costs", ui_units=fs.costing.base_currency / pyunits.year, display_units="$/yr", rounding=0, description="Electricity costs", is_input=False, is_output=True, output_category=category, ) exports.add( obj=fs.costing.LCOW, name="LCOW", ui_units=fs.costing.base_currency / pyunits.meter**3, display_units="$/yr", rounding=rounding, description="Levelized cost of water", is_input=False, is_output=True, output_category=category, )
[docs]def build_flowsheet(build_options=None, **kwargs): """ Build and solve the initial flowsheet. """ if build_options is not None: m = gac_fs.build( material_flow_basis=build_options["MaterialFlowBasis"].value, film_transfer_coefficient_type=build_options[ "FilmTransferCoefficientType" ].value, surface_diffusion_coefficient_type=build_options[ "SurfaceDiffusionCoefficientType" ].value, diffusivity_calculation=build_options["DiffusivityCalculation"].value, cost_contactor_type=build_options["ContactorType"].value, ) else: m = gac_fs.build() gac_fs.initialize(m) return m
[docs]def solve_flowsheet(flowsheet=None): """ Solves the flowsheet. """ fs = flowsheet solver = get_solver() res = gac_fs.optimize(fs, solver) return res