Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Applying Scalers to BSM2 Flowsheets #1550

Draft
wants to merge 52 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
7531440
Create initial files for ADM1 scalers
MarcusHolly Nov 22, 2024
059803e
Add new scaler objects
MarcusHolly Nov 22, 2024
a0921cd
Update tests
MarcusHolly Nov 22, 2024
367f18c
Carry over ASM1 scaler updates
MarcusHolly Nov 22, 2024
40ab286
Create first draft of AD Scaler object
MarcusHolly Nov 25, 2024
03e970a
Move iscale scaling to calculate_scaling_factors
MarcusHolly Nov 27, 2024
863e6b3
Move more iscale scaling to calculate_scaling_factors
MarcusHolly Nov 27, 2024
01427e9
Move iscale functionality in AD to calculate_scaling_factors
MarcusHolly Nov 29, 2024
2bcc1b6
Merge branch 'main' into AD_unit_scaling
MarcusHolly Nov 29, 2024
9b624d9
Address pylint issue
MarcusHolly Nov 29, 2024
07aabdd
Undo changes to adm1 vapor property package
MarcusHolly Nov 29, 2024
7b73282
Update tests
MarcusHolly Dec 2, 2024
ff3a127
Trying to resolve BSM2P issues
MarcusHolly Dec 2, 2024
7a6867b
Merge branch 'main' into AD_unit_scaling
MarcusHolly Dec 6, 2024
f6203ea
Carry over latest changes from other scaler PRs
MarcusHolly Dec 6, 2024
de4ca55
Update Jupyter notebook
MarcusHolly Dec 10, 2024
2e9aaaf
Cut back on scaling and update tests
MarcusHolly Dec 10, 2024
8943b4e
Merge branch 'main' into AD_unit_scaling
MarcusHolly Dec 11, 2024
9797a52
Improve scaling for bio_P=False configuration
MarcusHolly Dec 11, 2024
c7f05a6
Test changes to boi_P=False scaling
MarcusHolly Dec 12, 2024
3313ef2
Try adjusting scaling again
MarcusHolly Dec 12, 2024
9eb6984
Add additional scaling for heat and enthalpy
MarcusHolly Dec 12, 2024
1e57393
Use InverseMaximum scaling scheme
MarcusHolly Dec 12, 2024
f75f4b0
Additional scaling improvement
MarcusHolly Dec 12, 2024
0dc4b4f
Add rate expression constraint scaling
MarcusHolly Dec 12, 2024
5dda788
Use inverseMinimum and update test
MarcusHolly Dec 13, 2024
07688ab
Address merge conflicts
MarcusHolly Dec 13, 2024
0fa813e
Address additional merge conflicts
MarcusHolly Dec 13, 2024
01ffa20
Clean up flowsheet
MarcusHolly Dec 14, 2024
1086276
Update BSM2-P test
MarcusHolly Dec 16, 2024
93862e7
Update BSM2-P test
MarcusHolly Dec 16, 2024
5f467b5
Use inverseRSS scaling scheme
MarcusHolly Dec 16, 2024
ac27d2b
Add scaling for hydraulic retention time
MarcusHolly Dec 16, 2024
b83aded
Change scaling factor for conc_mass_comp
MarcusHolly Dec 16, 2024
e7758fc
Clean up flowsheet
MarcusHolly Dec 16, 2024
a05549a
Merge branch 'main' into AD_unit_scaling
MarcusHolly Dec 17, 2024
3a54930
Update AD testing
MarcusHolly Dec 17, 2024
448b18a
Merge branch 'main' into AD_unit_scaling
MarcusHolly Dec 23, 2024
aa789c0
Undo changes to modified ADM1 rxn test
MarcusHolly Dec 23, 2024
10c9583
Add check for condition number in BSM2-P test
MarcusHolly Dec 23, 2024
7c8becb
Update condition number test
MarcusHolly Dec 23, 2024
7681d4b
Address pylint issue
MarcusHolly Dec 23, 2024
9f11bfc
Add CSTR scalers
MarcusHolly Jan 2, 2025
534ccdd
Add clarifier scaling
MarcusHolly Jan 2, 2025
beb4d7d
Add dewaterer/thickener scalers
MarcusHolly Jan 2, 2025
f791e58
Add translator scalers
MarcusHolly Jan 2, 2025
e98c7bb
Add config option for scaling routine to BSM2
MarcusHolly Jan 2, 2025
6b0957e
Add aeration tank scaler
MarcusHolly Jan 2, 2025
93c5410
Preliminary version of BSM2 with scalers
MarcusHolly Jan 6, 2025
0f25608
BSM2-P solving to acceptable level
MarcusHolly Jan 8, 2025
0c4493a
Add To-Do note on TransformationFactory scaling
MarcusHolly Jan 10, 2025
fde1c01
Try refining scaling more
MarcusHolly Jan 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 227 additions & 31 deletions watertap/flowsheets/full_water_resource_recovery_facility/BSM2.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# "https://github.com/watertap-org/watertap/"
#################################################################################
"""
Flowsheet example full Water Resource Recovery Facility
Flowsheet example full Water Resource Recovery Facility
(WRRF; a.k.a., wastewater treatment plant) with ASM1 and ADM1.

The flowsheet follows the same formulation as benchmark simulation model no.2 (BSM2)
Expand All @@ -22,23 +22,37 @@
import pyomo.environ as pyo

from pyomo.network import Arc, SequentialDecomposition
from watertap.unit_models.anaerobic_digester import AD
from watertap.unit_models.thickener import Thickener
from watertap.unit_models.dewatering import DewateringUnit
from watertap.unit_models.cstr import CSTR
from watertap.unit_models.clarifier import Clarifier

from watertap.unit_models.translators.translator_asm1_adm1 import Translator_ASM1_ADM1
from watertap.unit_models.translators.translator_adm1_asm1 import Translator_ADM1_ASM1
from watertap.unit_models.anaerobic_digester import AD, ADScaler
from watertap.unit_models.thickener import Thickener, ThickenerScaler
from watertap.unit_models.dewatering import DewateringUnit, DewatererScaler
from watertap.unit_models.cstr import CSTR, CSTRScaler
from watertap.unit_models.clarifier import Clarifier, ClarifierScaler

from watertap.unit_models.translators.translator_asm1_adm1 import (
Translator_ASM1_ADM1,
ASM1ADM1Scaler,
)
from watertap.unit_models.translators.translator_adm1_asm1 import (
Translator_ADM1_ASM1,
ADM1ASM1Scaler,
)

import idaes.logger as idaeslog
from watertap.core.solvers import get_solver
import idaes.core.util.scaling as iscale
from idaes.core.util import DiagnosticsToolbox
from idaes.core.scaling.scaling_base import ScalerBase
from idaes.core.scaling.custom_scaler_base import (
CustomScalerBase,
ConstraintScalingScheme,
)
from watertap.property_models.unit_specific.anaerobic_digestion.adm1_properties import (
ADM1ParameterBlock,
ADM1PropertiesScaler,
)
from watertap.property_models.unit_specific.anaerobic_digestion.adm1_reactions import (
ADM1ReactionParameterBlock,
ADM1ReactionScaler,
)
from idaes.models.unit_models.mixer import MomentumMixingType
from idaes.models.unit_models.separator import SplittingType
Expand All @@ -55,12 +69,18 @@
Product,
)

from watertap.unit_models.aeration_tank import AerationTank, ElectricityConsumption
from watertap.unit_models.aeration_tank import (
AerationTank,
AerationTankScaler,
ElectricityConsumption,
)
from watertap.property_models.unit_specific.activated_sludge.asm1_properties import (
ASM1ParameterBlock,
ASM1PropertiesScaler,
)
from watertap.property_models.unit_specific.activated_sludge.asm1_reactions import (
ASM1ReactionParameterBlock,
ASM1ReactionScaler,
)
from watertap.core.util.initialization import assert_degrees_of_freedom
from watertap.costing import WaterTAPCosting
Expand All @@ -71,19 +91,29 @@
from pyomo.util.check_units import assert_units_consistent


def main(reactor_volume_equalities=False):
def main(reactor_volume_equalities=False, has_scalers=True):
m = build()
set_operating_conditions(m)
dt = DiagnosticsToolbox(m)
print("---Structural Issues---")
dt.report_structural_issues()

assert_degrees_of_freedom(m, 0)
assert_units_consistent(m)
scale_system(m, has_scalers=has_scalers)
# TODO: Should follow this with TransformationFactory("core.scale_model"), but would need to refactor tear guesses
initialize_system(m)

# badly_scaled_var_list = iscale.badly_scaled_var_generator(m, large=1e2, small=1e-2)
# print("---------------- badly_scaled_var_list ----------------")
# for x in badly_scaled_var_list:
# print(f"{x[0].name}\t{x[0].value}\tsf: {iscale.get_scaling_factor(x[0])}")

assert_degrees_of_freedom(m, 0)

results = solve(m)

add_costing(m)
add_costing(m, has_scalers=has_scalers)
m.fs.costing.initialize()
assert_degrees_of_freedom(m, 0)

Expand All @@ -102,6 +132,17 @@ def main(reactor_volume_equalities=False):
display_costing(m)
display_performance_metrics(m)

badly_scaled_var_list = iscale.badly_scaled_var_generator(m, large=1e2, small=1e-2)
print("---------------- badly_scaled_var_list ----------------")
for x in badly_scaled_var_list:
print(f"{x[0].name}\t{x[0].value}\tsf: {iscale.get_scaling_factor(x[0])}")

print("---Numerical Issues---")
dt.report_numerical_issues()
dt.display_variables_at_or_outside_bounds()
dt.display_variables_with_extreme_jacobians()
dt.display_constraints_with_extreme_jacobians()

return m, results


Expand Down Expand Up @@ -385,17 +426,154 @@ def set_operating_conditions(m):
for mx in m.mixers:
mx.pressure_equality_constraints[0.0, 2].deactivate()

for var in m.fs.component_data_objects(pyo.Var, descend_into=True):
if "flow_vol" in var.name:
iscale.set_scaling_factor(var, 1e1)
if "temperature" in var.name:
iscale.set_scaling_factor(var, 1e-1)
if "pressure" in var.name:
iscale.set_scaling_factor(var, 1e-6)
if "conc_mass_comp" in var.name:
iscale.set_scaling_factor(var, 1e1)

iscale.calculate_scaling_factors(m)
def scale_system(m, has_scalers=True):
if has_scalers:
sb = ScalerBase()
csb = CustomScalerBase()

ad_scaler = ADScaler()
ad_scaler.scale_model(
m.fs.RADM,
submodel_scalers={
m.fs.RADM.liquid_phase.properties_in: ADM1PropertiesScaler,
m.fs.RADM.liquid_phase.properties_out: ADM1PropertiesScaler,
m.fs.RADM.liquid_phase.reactions: ADM1ReactionScaler,
},
)
sb.set_variable_scaling_factor(m.fs.RADM.hydraulic_retention_time[0], 1e-6)
# sb.set_variable_scaling_factor(m.fs.RADM.liquid_phase.properties_in[0].flow_vol, 1e3, overwrite=True)
# sb.set_variable_scaling_factor(m.fs.RADM.liquid_phase.properties_out[0].flow_vol, 1e3, overwrite=True)
# sb.set_variable_scaling_factor(m.fs.RADM.KH_h2[0], 1e4)
# sb.set_variable_scaling_factor(m.fs.RADM.KH_h2[0], 1e3)
# sb.set_variable_scaling_factor(m.fs.RADM.KH_h2[0], 1e2)
# sb.set_variable_scaling_factor(m.fs.RADM.KH_ch4[0], 1e3)
# sb.set_variable_scaling_factor(m.fs.RADM.KH_ch4[0], 1e2)
# sb.set_variable_scaling_factor(m.fs.RADM.volume_AD[0], 1e3)
# sb.set_variable_scaling_factor(m.fs.RADM.vapor_phase[0].pressure, 1e-5, overwrite=True)
# sb.set_variable_scaling_factor(m.fs.RADM.vapor_phase[0].pressure, 1e-6, overwrite=True)
# sb.set_variable_scaling_factor(m.fs.RADM.liquid_phase.heat[0], 1e3)
# sb.set_variable_scaling_factor(m.fs.RADM.liquid_phase.heat[0], 1e2)

cstr_list = [m.fs.R1, m.fs.R2]
cstr_scaler = CSTRScaler()
for unit in cstr_list:
cstr_scaler.scale_model(
unit,
submodel_scalers={
unit.control_volume.properties_in: ASM1PropertiesScaler,
unit.control_volume.properties_out: ASM1PropertiesScaler,
unit.control_volume.reactions: ASM1ReactionScaler,
},
)
sb.set_variable_scaling_factor(unit.hydraulic_retention_time[0], 1e-2)

aeration_list = [m.fs.R3, m.fs.R4, m.fs.R5]
aeration_scaler = AerationTankScaler()
for unit in aeration_list:
aeration_scaler.scale_model(
unit,
submodel_scalers={
unit.control_volume.properties_in: ASM1PropertiesScaler,
unit.control_volume.properties_out: ASM1PropertiesScaler,
unit.control_volume.reactions: ASM1ReactionScaler,
},
)

clarifier_list = [m.fs.CL, m.fs.CL1]
clarifier_scaler = ClarifierScaler()
for unit in clarifier_list:
clarifier_scaler.scale_model(
unit,
submodel_scalers={
unit.mixed_state: ASM1PropertiesScaler,
unit.underflow_state: ASM1PropertiesScaler,
unit.effluent_state: ASM1PropertiesScaler,
},
)

thickener_scaler = ThickenerScaler()
thickener_scaler.scale_model(
m.fs.TU,
submodel_scalers={
m.fs.TU.mixed_state: ASM1PropertiesScaler,
m.fs.TU.underflow_state: ASM1PropertiesScaler,
m.fs.TU.overflow_state: ASM1PropertiesScaler,
},
)

dewaterer_scaler = DewatererScaler()
dewaterer_scaler.scale_model(
m.fs.DU,
submodel_scalers={
m.fs.DU.mixed_state: ASM1PropertiesScaler,
m.fs.DU.underflow_state: ASM1PropertiesScaler,
m.fs.DU.overflow_state: ASM1PropertiesScaler,
},
)
sb.set_variable_scaling_factor(m.fs.DU.volume[0], 1, overwrite=True)

as_ad_scaler = ASM1ADM1Scaler()
as_ad_scaler.scale_model(
m.fs.asm_adm,
submodel_scalers={
m.fs.asm_adm.properties_in: ASM1PropertiesScaler,
m.fs.asm_adm.properties_out: ADM1PropertiesScaler,
},
)

ad_as_scaler = ADM1ASM1Scaler()
ad_as_scaler.scale_model(
m.fs.adm_asm,
submodel_scalers={
m.fs.adm_asm.properties_in: ADM1PropertiesScaler,
m.fs.adm_asm.properties_out: ASM1PropertiesScaler,
},
)

for var in m.fs.component_data_objects(pyo.Var, descend_into=True):
# if "flow_vol" in var.name:
# sb.set_variable_scaling_factor(var, 1e2, overwrite=True)
if "flow_vol" in var.name:
sb.set_variable_scaling_factor(var, 1e2)
if "temperature" in var.name:
sb.set_variable_scaling_factor(var, 1e-2)
if "pressure" in var.name:
sb.set_variable_scaling_factor(var, 1e-5)
if "conc_mass_comp" in var.name:
sb.set_variable_scaling_factor(var, 1e3)
# if "cation" in var.name:
# sb.set_variable_scaling_factor(var, 1e3)
if "conc_mol" in var.name:
sb.set_variable_scaling_factor(var, 1e2)
if "alkalinity" in var.name:
sb.set_variable_scaling_factor(var, 1e3)

# for c in m.fs.component_data_objects(pyo.Constraint, descend_into=True):
# csb.scale_constraint_by_nominal_value(
# c,
# scheme=ConstraintScalingScheme.inverseMaximum,
# overwrite=True,
# )

# csb.scale_constraint_by_nominal_value(
# m.fs.RADM.AD_retention_time[0],
# scheme=ConstraintScalingScheme.inverseMaximum,
# overwrite=True,
# )

else:
for var in m.fs.component_data_objects(pyo.Var, descend_into=True):
if "flow_vol" in var.name:
iscale.set_scaling_factor(var, 1e1)
if "temperature" in var.name:
iscale.set_scaling_factor(var, 1e-1)
if "pressure" in var.name:
iscale.set_scaling_factor(var, 1e-6)
if "conc_mass_comp" in var.name:
iscale.set_scaling_factor(var, 1e1)

iscale.calculate_scaling_factors(m)


def initialize_system(m):
Expand Down Expand Up @@ -470,7 +648,7 @@ def function(unit):
mx.pressure_equality_constraints[0.0, 2].deactivate()


def add_costing(m):
def add_costing(m, has_scalers=True):
m.fs.costing = WaterTAPCosting()
m.fs.costing.base_currency = pyo.units.USD_2020

Expand Down Expand Up @@ -503,15 +681,33 @@ def add_costing(m):
m.fs.costing.add_specific_energy_consumption(m.fs.FeedWater.properties[0].flow_vol)

m.fs.objective = pyo.Objective(expr=m.fs.costing.LCOW)
iscale.set_scaling_factor(m.fs.costing.LCOW, 1e3)
iscale.set_scaling_factor(m.fs.costing.total_capital_cost, 1e-5)

for block in m.fs.component_objects(pyo.Block, descend_into=True):
if isinstance(block, UnitModelBlockData) and hasattr(block, "costing"):
iscale.set_scaling_factor(block.costing.capital_cost, 1e-6)
if has_scalers:
sb = ScalerBase()

sb.set_variable_scaling_factor(m.fs.costing.total_capital_cost, 1e-7)

for block in m.fs.component_objects(pyo.Block, descend_into=True):
if isinstance(block, UnitModelBlockData) and hasattr(block, "costing"):
sb.set_variable_scaling_factor(block.costing.capital_cost, 1e-6)
sb.set_constraint_scaling_factor(m.fs.DU.costing.capital_cost_constraint, 1e-6)
sb.set_constraint_scaling_factor(
m.fs.RADM.costing.capital_cost_constraint, 1e-6
)
else:
iscale.set_scaling_factor(m.fs.costing.LCOW, 1e3)
iscale.set_scaling_factor(m.fs.costing.total_capital_cost, 1e-5)

for block in m.fs.component_objects(pyo.Block, descend_into=True):
if isinstance(block, UnitModelBlockData) and hasattr(block, "costing"):
iscale.set_scaling_factor(block.costing.capital_cost, 1e-6)

iscale.constraint_scaling_transform(m.fs.DU.costing.capital_cost_constraint, 1e-6)
iscale.constraint_scaling_transform(m.fs.RADM.costing.capital_cost_constraint, 1e-6)
iscale.constraint_scaling_transform(
m.fs.DU.costing.capital_cost_constraint, 1e-6
)
iscale.constraint_scaling_transform(
m.fs.RADM.costing.capital_cost_constraint, 1e-6
)


def setup_optimization(m, reactor_volume_equalities=False):
Expand Down Expand Up @@ -754,4 +950,4 @@ def display_performance_metrics(m):


if __name__ == "__main__":
m, results = main()
m, results = main(has_scalers=False)
Loading
Loading