Skip to content

Commit

Permalink
Adding spiral-wound vs flat-sheet config options to RO and OARO (wate…
Browse files Browse the repository at this point in the history
…rtap-org#1272)

* Half width for membrane fold

* changed width halving implementation

* added config options for flat_plate vs spiral_wound modules

* added ModuleType congif to RO and OARO tests

* ran black

* adding config error test: not working yet

* ran black

* updated error logic and test values

* Option B: Removed FrictionFactor config option

* ran pylint

* updated OARO

* Corrected Flux Eqn for RO1D

* fixed flux and mass transfer eqn units

* adjusted RO1D test

* ran black

* Added a line to the cod to differentiate the two area calcs

* ran black

* updated RO0D and RO1D tests

* updated and ran black

* updated module_type config option descriptions

* fixed diff area on OARO1D

* added 1D sprial wound P_loss test

* ran black

* renamed test

* added spiral-wound support to docs

* added to OARO docs

* debugging

* debug

* dirty fix

* revert fix

* test

* fixed for flux eqn

* added friction_factor by module_type

* ran black

* removed custom friction factor

* removed friction_factor custom option

* Update watertap/core/membrane_channel_base.py

Co-authored-by: Adam Atia <[email protected]>

* returned FrictionFactor to init

* updated FrictionFactor class description

* removed custom option

* fixing eq_area check

* fixing OARO merge conflicts

* ran black

---------

Co-authored-by: Adam Atia <[email protected]>
  • Loading branch information
zacharybinger and adam-a-a authored Apr 26, 2024
1 parent 41d9e6d commit b30c16a
Show file tree
Hide file tree
Showing 13 changed files with 304 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This osmotically assisted reverse osmosis (OARO) unit model
* is 0-dimensional
* supports a single liquid phase only
* supports steady-state only
* supports flat-sheet and spiral-wound module designs
* is based on the solution-diffusion model and film theory
* assumes isothermal conditions
* assumes the feed-side flows in the forward direction
Expand Down Expand Up @@ -177,7 +178,8 @@ Equations
"Reynolds number",":math:`Re = \frac{\rho v_f d_h}{\mu}`"
"Hydraulic diameter",":math:`d_h = \frac{4\epsilon_{sp}}{2/h_{ch} + (1-\epsilon_{sp})8/h_{ch}}`"
"Cross-sectional area",":math:`A_c = h_{ch}W\epsilon_{sp}`"
"Membrane area",":math:`A_m = LW`"
"Membrane area (flat-plate)",":math:`A_m = LW`"
"Membrane area (spiral-wound)",":math:`A_m = 2LW`"
"Pressure drop",":math:`ΔP = (\frac{ΔP}{Δx})_{avg}L`"
"Feed-channel velocity",":math:`v_f = Q_f/A_c`"
"Friction factor",":math:`f = 0.42+\frac{189.3}{Re}`"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This osmotically assisted reverse osmosis (OARO) unit model
* is 1-dimensional
* supports a single liquid phase only
* supports steady-state only
* supports flat-sheet and spiral-wound module designs
* is based on the solution-diffusion model and film theory
* assumes isothermal conditions
* assumes the feed-side flows in the forward direction
Expand Down
4 changes: 3 additions & 1 deletion docs/technical_reference/unit_models/reverse_osmosis_0D.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This reverse osmosis (RO) unit model
* is 0-dimensional
* supports a single liquid phase only
* supports steady-state only
* supports flat-sheet and spiral-wound module designs
* is based on the solution-diffusion model and film theory
* assumes isothermal conditions

Expand Down Expand Up @@ -159,7 +160,8 @@ Equations
"Reynolds number",":math:`Re = \frac{\rho v_f d_h}{\mu}`"
"Hydraulic diameter",":math:`d_h = \frac{4\epsilon_{sp}}{2/h_{ch} + (1-\epsilon_{sp})8/h_{ch}}`"
"Cross-sectional area",":math:`A_c = h_{ch}W\epsilon_{sp}`"
"Membrane area",":math:`A_m = LW`"
"Membrane area (flat-plate)",":math:`A_m = LW`"
"Membrane area (spiral-wound)",":math:`A_m = 2LW`"
"Pressure drop",":math:`ΔP = (\frac{ΔP}{Δx})_{avg}L`"
"Feed-channel velocity",":math:`v_f = Q_f/A_c`"
"Friction factor",":math:`f = 0.42+\frac{189.3}{Re}`"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This reverse osmosis (RO) unit model
* is 1-dimensional
* supports a single liquid phase only
* supports steady-state only
* supports flat-sheet and spiral-wound module designs
* is based on the solution-diffusion model and film theory
* assumes isothermal conditions

Expand Down
112 changes: 69 additions & 43 deletions watertap/core/membrane_channel_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ class TransportModel(Enum):
SKK = auto()


class ModuleType(Enum):
"""
flat_sheet: flat-sheet module configuration
spiral_wound: spiral-wound module configuration
"""

flat_sheet = auto()
spiral_wound = auto()


class PressureChangeType(Enum):
"""
fixed_per_stage: pressure drop across membrane channel is a user-specified value
Expand All @@ -85,12 +95,11 @@ class PressureChangeType(Enum):

class FrictionFactor(Enum):
"""
flat_sheet: Darcy's friction factor correlation by Guillen & Hoek
spiral_wound: Darcy's friction factor correlation by Schock & Miquel, 1987
default_by_module_type: Will revert FrictionFactor to either the Darcy's friction factor correlation
by Guillen & Hoek (flat-plate) or by Schock & Miquel (spiral-wound)
"""

flat_sheet = auto()
spiral_wound = auto()
default_by_module_type = auto()


CONFIG_Template = ConfigDict()
Expand Down Expand Up @@ -261,6 +270,24 @@ class FrictionFactor(Enum):
),
)

CONFIG_Template.declare(
"module_type",
ConfigValue(
default=ModuleType.flat_sheet,
domain=In(ModuleType),
description="Membrane geometry for flat-sheet or spiral-wound",
doc="""
Options to account for geometry differences between flat sheet and spiral wound membranes.
**default** - ``ModuleType.flat_sheet``
"``ModuleType.flat_sheet``", "Module type option for flat-sheet membrane modules"
"``ModuleType.spiral_wound``", "Module type option for spiral-wound membrane modules, this option accounts for how membranes in spiral-wound modules are folded which reduces the channel width by half"
""",
),
)

CONFIG_Template.declare(
"has_pressure_change",
ConfigValue(
Expand Down Expand Up @@ -303,19 +330,18 @@ class FrictionFactor(Enum):
CONFIG_Template.declare(
"friction_factor",
ConfigValue(
default=FrictionFactor.flat_sheet,
default=FrictionFactor.default_by_module_type,
domain=In(FrictionFactor),
description="Darcy friction factor correlation",
doc="""
Options to account for friction factor correlations.
**default** - ``FrictionFactor.flat_sheet``
**default** - ``FrictionFactor.default_by_module_type``
.. csv-table::
:header: "Configuration Options", "Description"
"``FrictionFactor.flat_sheet``", "Friction factor correlation for flat-sheet membrane modules"
"``FrictionFactor.spiral_wound``", "Friction factor correlation for spiral-wound membranes"
"``FrictionFactor.default_by_module_type``", "Friction factor correlation that is specific to the supported membrane modules type"
""",
),
)
Expand Down Expand Up @@ -358,7 +384,8 @@ def add_total_pressure_balances(
has_pressure_change=True,
pressure_change_type=PressureChangeType.calculated,
custom_term=None,
friction_factor=FrictionFactor.flat_sheet,
module_type=ModuleType.flat_sheet,
friction_factor=FrictionFactor.default_by_module_type,
):
super().add_total_pressure_balances(
has_pressure_change=has_pressure_change, custom_term=custom_term
Expand All @@ -378,7 +405,9 @@ def eq_equal_pressure_interface(b, t, x):
self._add_pressure_change(pressure_change_type=pressure_change_type)

if pressure_change_type == PressureChangeType.calculated:
self._add_calculated_pressure_change(friction_factor=friction_factor)
self._add_calculated_pressure_change(
module_type=module_type, friction_factor=friction_factor
)

def add_interface_isothermal_conditions(self):

Expand Down Expand Up @@ -710,7 +739,9 @@ def _add_interface_stateblock(self, has_phase_equilibrium=None):
)

def _add_calculated_pressure_change(
self, friction_factor=FrictionFactor.flat_sheet
self,
module_type=ModuleType.flat_sheet,
friction_factor=FrictionFactor.default_by_module_type,
):
self._add_calculated_pressure_change_mass_transfer_components()

Expand Down Expand Up @@ -749,32 +780,35 @@ def eq_velocity(b, t, x):
return b.velocity[t, x] * b.area == b.properties[t, x].flow_vol_phase["Liq"]

## ==========================================================================
# Darcy friction factor based on eq. S27 in SI for Cost Optimization of Osmotically Assisted Reverse Osmosis
if friction_factor == FrictionFactor.flat_sheet:

@self.Constraint(
self.flowsheet().config.time,
self.length_domain,
doc="Darcy friction factor constraint for flat sheet membranes",
)
def eq_friction_factor(b, t, x):
return (b.friction_factor_darcy[t, x] - 0.42) * b.N_Re[t, x] == 189.3

# Darcy friction factor based on eq. 24 in Mass transfer and pressure loss in spiral wound modules (Schock & Miquel, 1987)
elif friction_factor == FrictionFactor.spiral_wound:

@self.Constraint(
self.flowsheet().config.time,
self.length_domain,
doc="Darcy friction factor constraint for spiral-wound membranes",
)
def eq_friction_factor(b, t, x):
return b.friction_factor_darcy[t, x] == 6.23 * b.N_Re[t, x] ** -0.3
if friction_factor == FrictionFactor.default_by_module_type:
# Darcy friction factor based on eq. S27 in SI for Cost Optimization of Osmotically Assisted Reverse Osmosis
if module_type == ModuleType.flat_sheet:

@self.Constraint(
self.flowsheet().config.time,
self.length_domain,
doc="Darcy friction factor constraint for flat sheet membranes",
)
def eq_friction_factor(b, t, x):
return (b.friction_factor_darcy[t, x] - 0.42) * b.N_Re[
t, x
] == 189.3

# Darcy friction factor based on eq. 24 in Mass transfer and pressure loss in spiral wound modules (Schock & Miquel, 1987)
elif module_type == ModuleType.spiral_wound:

@self.Constraint(
self.flowsheet().config.time,
self.length_domain,
doc="Darcy friction factor constraint for spiral-wound membranes",
)
def eq_friction_factor(b, t, x):
return b.friction_factor_darcy[t, x] == 6.23 * b.N_Re[t, x] ** -0.3

else:
raise ConfigurationError(f"Unrecognized module_type type {module_type}")
else:
raise ConfigurationError(
f"Unrecognized friction_factor type {friction_factor}"
)
pass

## ==========================================================================
# Pressure change per unit length due to friction
Expand Down Expand Up @@ -1021,11 +1055,3 @@ def validate_membrane_config_args(unit):
unit.config.concentration_polarization_type,
)
)

if (
unit.config.pressure_change_type != PressureChangeType.calculated
and unit.config.friction_factor != unit.config.get("friction_factor")._default
):
raise ConfigurationError(
"\nChanging the 'friction_factor' will have no effect if the 'pressure_change_type' is not `PressureChangeType.calculated`"
)
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ def eq_connect_mass_transfer(b, t, x, p, j):
)
def eq_permeate_production(b, t, x, p, j):
return (
-b.feed_side.mass_transfer_term[t, x, p, j]
== b.width * b.flux_mass_phase_comp[t, x, p, j]
-b.feed_side.mass_transfer_term[t, x, p, j] * b.length
== b.flux_mass_phase_comp[t, x, p, j] * b.area
)

def calculate_scaling_factors(self):
Expand Down
30 changes: 21 additions & 9 deletions watertap/unit_models/osmotically_assisted_reverse_osmosis_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
validate_membrane_config_args,
ConcentrationPolarizationType,
TransportModel,
ModuleType,
)

from watertap.core import InitializationMixin
Expand Down Expand Up @@ -116,6 +117,7 @@ def build(self):
balance_type=self.config.momentum_balance_type,
pressure_change_type=self.config.pressure_change_type,
has_pressure_change=self.config.has_pressure_change,
module_type=self.config.module_type,
friction_factor=self.config.friction_factor,
)

Expand Down Expand Up @@ -153,6 +155,7 @@ def build(self):
balance_type=self.config.momentum_balance_type,
pressure_change_type=self.config.pressure_change_type,
has_pressure_change=self.config.has_pressure_change,
module_type=self.config.module_type,
friction_factor=self.config.friction_factor,
)

Expand Down Expand Up @@ -341,15 +344,24 @@ def _add_area(self, include_constraint=True):

if include_constraint:
if not hasattr(self, "eq_area"):
# Membrane area equation
@self.Constraint(doc="Total Membrane area")
def eq_area(b):
return b.area == b.length * b.width

else:
raise ValueError(
"include_constraint was set to True inside of _add_area(), but area constraint already exists."
)
if self.config.module_type == ModuleType.flat_sheet:
# Membrane area equation for flat plate membranes
@self.Constraint(doc="Total Membrane area")
def eq_area(b):
return b.area == b.length * b.width

elif self.config.module_type == ModuleType.spiral_wound:
# Membrane area equation
@self.Constraint(doc="Total Membrane area")
def eq_area(b):
return b.area == b.length * 2 * b.width

else:
raise ConfigurationError(
"Unsupported membrane module type: {}".format(
self.config.module_type
)
)

def _add_flux_balance(self):

Expand Down
4 changes: 2 additions & 2 deletions watertap/unit_models/reverse_osmosis_1D.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ def eq_connect_mass_transfer(b, t, x, p, j):
)
def eq_mass_flux_equal_mass_transfer(b, t, x, p, j):
return (
b.flux_mass_phase_comp[t, x, p, j] * b.width
== -b.feed_side.mass_transfer_term[t, x, p, j]
b.flux_mass_phase_comp[t, x, p, j] * b.area
== -b.feed_side.mass_transfer_term[t, x, p, j] * b.length
)

# ==========================================================================
Expand Down
24 changes: 19 additions & 5 deletions watertap/unit_models/reverse_osmosis_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
validate_membrane_config_args,
ConcentrationPolarizationType,
TransportModel,
ModuleType,
)
from watertap.costing.unit_models.reverse_osmosis import cost_reverse_osmosis

Expand Down Expand Up @@ -100,7 +101,6 @@ def build(self):
balance_type=self.config.momentum_balance_type,
pressure_change_type=self.config.pressure_change_type,
has_pressure_change=self.config.has_pressure_change,
friction_factor=self.config.friction_factor,
)

self.feed_side.add_control_volume_isothermal_conditions()
Expand Down Expand Up @@ -287,10 +287,24 @@ def _add_area(self, include_constraint=True):
)

if include_constraint:
# Membrane area equation
@self.Constraint(doc="Total Membrane area")
def eq_area(b):
return b.area == b.length * b.width
if self.config.module_type == ModuleType.flat_sheet:
# Membrane area equation for flat plate membranes
@self.Constraint(doc="Total Membrane area")
def eq_area(b):
return b.area == b.length * b.width

elif self.config.module_type == ModuleType.spiral_wound:
# Membrane area equation
@self.Constraint(doc="Total Membrane area")
def eq_area(b):
return b.area == b.length * 2 * b.width

else:
raise ConfigurationError(
"Unsupported membrane module type: {}".format(
self.config.module_type
)
)

def _add_flux_balance(self):

Expand Down
Loading

0 comments on commit b30c16a

Please sign in to comment.