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

[WIP] Activating Pyomo linear presolve for IDAES models #1415

Closed
wants to merge 63 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
a36deb1
Adding initial support for v2 solvers
andrewlee94 Mar 22, 2024
f515d80
Cleaning up and using isinstance ot detect legacy solver interface
andrewlee94 Mar 25, 2024
38a9a94
Correcting solve config
andrewlee94 Mar 29, 2024
d9287c2
Fixing leak of options
andrewlee94 Mar 29, 2024
eb20e86
Fixing or skipping failing tests
andrewlee94 Mar 31, 2024
7f5fe40
Fixing use of pytest skip
andrewlee94 Apr 1, 2024
258e2c0
Fixing presolve issues in apps and core
andrewlee94 Apr 1, 2024
532d23b
Switch Block to ConcreteModel in solve_indexed_blocks
mrmundt Apr 2, 2024
8c37ddf
Merge branch 'main' into scaling_v2
andrewlee94 Apr 10, 2024
61c938e
Merge branch 'scaling_v2' of https://github.com/andrewlee94/idaes-pse…
andrewlee94 Apr 10, 2024
88553c1
Push latest main to branch (#1394)
andrewlee94 Apr 10, 2024
c8f2f7c
Merge branch 'scaling_v2' of https://github.com/IDAES/idaes-pse into …
andrewlee94 Apr 10, 2024
637b7fb
Cleaning up
andrewlee94 Apr 10, 2024
075edf7
Updating Pyomo tag
andrewlee94 Apr 10, 2024
6c54cbd
Startign to test with presolve
andrewlee94 Apr 10, 2024
4d73ced
Trying @jsiirola's branch fix
andrewlee94 Apr 11, 2024
f312c00
Trying @jsiirola's branch fix again
andrewlee94 Apr 11, 2024
c3a7010
Trying @jsiirola's branch fix again
andrewlee94 Apr 11, 2024
b5ddcf8
Bumping Pyomo tag
andrewlee94 Apr 11, 2024
2409b92
Merge branch 'scaling_v2' into scaling_v2_2
andrewlee94 Apr 11, 2024
2e89daf
Renaming Block classes
andrewlee94 May 2, 2024
75b71e8
Fixing instances of _General
andrewlee94 May 2, 2024
4424c53
Fixing other _Data classes
andrewlee94 May 3, 2024
7eed1cc
Some clean up
andrewlee94 May 3, 2024
3e0a27c
bumping Pyomo tag
andrewlee94 May 7, 2024
5f685b2
Merge branch 'main' into renaming_components
andrewlee94 May 7, 2024
2c43633
Merging main
andrewlee94 May 7, 2024
8a7cee3
merging main
andrewlee94 May 7, 2024
50956cd
Fixing conflict
andrewlee94 May 7, 2024
a9189be
Merge branch 'scaling_v2' into scaling_v2_2
andrewlee94 May 7, 2024
b971874
Merging main
andrewlee94 May 9, 2024
c6c8a01
Fixng some presolve issues
andrewlee94 May 10, 2024
67c787f
Merge branch 'main' of https://github.com/IDAES/idaes-pse into scalin…
andrewlee94 May 10, 2024
fcfad19
Fixing some tests
andrewlee94 May 10, 2024
d5b87ca
Fixing typo
andrewlee94 May 10, 2024
38127fa
Merge branch 'main' into scaling_v2_2
andrewlee94 May 22, 2024
712b570
Fixing tolerance in test
andrewlee94 May 23, 2024
1a5ec58
Patching presolve failure in TSA
andrewlee94 May 23, 2024
00c85ea
Turning off presolve in column models
andrewlee94 May 23, 2024
c6fcd42
Disabling presolve in gas-solid contactors
andrewlee94 May 23, 2024
d7a28a6
Fixing some issues
andrewlee94 May 23, 2024
bcada30
Some clean up
andrewlee94 May 23, 2024
c1927b2
Merge branch 'main' into scaling_v2_2
andrewlee94 May 28, 2024
2db998f
Fixing some 6.7.3 issues
andrewlee94 May 30, 2024
dbe6f45
Fixing SOEC model to avoid 0 O2 concentration
andrewlee94 May 31, 2024
6ae5f94
Merge branch 'main' of https://github.com/IDAES/idaes-pse into scalin…
andrewlee94 May 31, 2024
90702de
Merge branch 'main' into scaling_v2_2
andrewlee94 Jun 6, 2024
008a6d1
Fixing typo
andrewlee94 Jun 6, 2024
bd765f1
patching issue with solvent condenser
andrewlee94 Jun 6, 2024
ceec179
Trying to resolve HX bounds issues
andrewlee94 Jun 7, 2024
9ee06f2
trying to patch last remaining issues
andrewlee94 Jun 10, 2024
ea4a49c
Adding a deprecation TODO
andrewlee94 Jun 10, 2024
bb65bee
Fixing typo
andrewlee94 Jun 10, 2024
13a4a10
Fixing pylint issues
andrewlee94 Jun 10, 2024
005c976
Fixing failures due to Pint version
andrewlee94 Jun 10, 2024
bdeb9ff
ADding dsiplay methods for platform specific debugging
andrewlee94 Jun 10, 2024
3ca5ea8
REverting pint change and setting max version
andrewlee94 Jun 10, 2024
5c06fe1
Adding expplnation for commented code
andrewlee94 Jun 10, 2024
db3bf36
Re-enabling scaling of tray column
andrewlee94 Jun 11, 2024
a94ee56
clean up
andrewlee94 Jun 11, 2024
68febd9
Working on tray column failure
andrewlee94 Jun 11, 2024
6c6e897
still working on tray column
andrewlee94 Jun 11, 2024
f39ccdf
Trying skipif on failing column test
andrewlee94 Jun 11, 2024
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
3 changes: 2 additions & 1 deletion idaes/apps/grid_integration/multiperiod/multiperiod.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ def __init__(
self._stochastic_model = False

if solver is None:
solver = get_solver()
# TODO: fails using linear presolve (non-descript RuntimeError)
solver = get_solver(writer_config={"linear_presolve": False})

_logger = logging.getLogger(__name__)
_logger.setLevel(outlvl)
Expand Down
102 changes: 101 additions & 1 deletion idaes/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,106 @@ def _new_idaes_config_block():
),
)

cfg.declare(
"ipopt_v2",
pyomo.common.config.ConfigBlock(
implicit=False,
description="Default config for 'ipopt' solver",
doc="Default config for 'ipopt' solver",
),
)
cfg["ipopt_v2"].declare(
"options",
pyomo.common.config.ConfigBlock(
implicit=True,
description="Default solver options for 'ipopt'",
doc="Default solver options for 'ipopt' solver",
),
)

cfg["ipopt_v2"]["options"].declare(
"nlp_scaling_method",
pyomo.common.config.ConfigValue(
domain=str,
default="gradient-based",
description="Ipopt NLP scaling method",
doc="Ipopt NLP scaling method",
),
)

cfg["ipopt_v2"]["options"].declare(
"tol",
pyomo.common.config.ConfigValue(
domain=float,
default=1e-6,
description="Ipopt tol option",
doc="Ipopt tol option",
),
)

cfg["ipopt_v2"]["options"].declare(
"max_iter",
pyomo.common.config.ConfigValue(
domain=int,
default=200,
description="Ipopt max_iter option",
doc="Ipopt max_iter option",
),
)

# TODO: Would be nice to make these the defaults, but too many things fail
# right now.
# TODO: Leaving these as a reminder for the future
# cfg["ipopt_v2"]["options"].declare(
# "linear_solver",
# pyomo.common.config.ConfigValue(
# domain=str,
# default="ma57",
# description="Linear solver to be used by IPOPT",
# doc="Linear solver to be used by IPOPT",
# ),
# )
#
# cfg["ipopt_v2"]["options"].declare(
# "ma57_automatic_scaling",
# pyomo.common.config.ConfigValue(
# domain=str,
# default="yes",
# description="Whether to use automatic scaling in MA57",
# doc="Whether to use automatic scaling in MA57. "
# "Valid values: 'yes' (default), 'no'.",
# ),
# )

cfg["ipopt_v2"].declare(
"writer_config",
pyomo.common.config.ConfigBlock(
implicit=True,
description="Default writer configuration for 'ipopt'",
doc="Default writer configuration for 'ipopt' solver",
),
)

cfg["ipopt_v2"]["writer_config"].declare(
"scale_model",
pyomo.common.config.ConfigValue(
domain=bool,
default=False, # TODO: Change to true once transition complete
description="Whether to apply model scaling in writer",
doc="Whether to apply model scaling in writer",
),
)

cfg["ipopt_v2"]["writer_config"].declare(
"linear_presolve",
pyomo.common.config.ConfigValue(
domain=bool,
default=True,
description="Whether to apply linear presolve in writer",
doc="Whether to apply linear presolve in writer",
),
)

cfg.declare(
"ipopt_l1",
pyomo.common.config.ConfigBlock(
Expand Down Expand Up @@ -451,7 +551,7 @@ def _new_idaes_config_block():
cfg.declare(
"default_solver",
pyomo.common.config.ConfigValue(
default="ipopt",
default="ipopt_v2",
domain=str,
description="Default solver. See Pyomo's SolverFactory for details.",
doc="Default solver. See Pyomo's SolverFactory for details.",
Expand Down
22 changes: 17 additions & 5 deletions idaes/core/solvers/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
# pylint: disable=missing-module-docstring
# pylint: disable=missing-class-docstring

from copy import deepcopy

from pyomo.environ import SolverFactory

import idaes


Expand Down Expand Up @@ -42,22 +45,31 @@ def __call__(self, *args, **kwargs):
else:
name = self.name
solver = self.solver

if name in idaes.cfg and (
idaes.cfg.use_idaes_solver_config
or name == "default"
or not self.registered
idaes.cfg.use_idaes_solver_config or not self.registered
):
for k, v in idaes.cfg[name].items():
if k not in kwargs:
kwargs[k] = v
kwargs[k] = deepcopy(v)
elif k == "options":
# options is in ConfigBlock and in kwargs, treat "options"
# special so individual options can have defaults not just
# the whole options block
for opk, opv in v.items():
if opk not in kwargs["options"]:
kwargs["options"][opk] = opv
return solver(*args, **kwargs)
elif k == "writer_config":
# writer_config is in ConfigBlock and in kwargs, treat "writer_config"
# special so individual options can have defaults not just
# the whole options block
for opk, opv in v.items():
if opk not in kwargs["writer_config"]:
kwargs["writer_config"][opk] = opv

solver_obj = solver(*args, **kwargs)

return solver_obj


def use_idaes_solver_configuration_defaults(b=True):
Expand Down
21 changes: 18 additions & 3 deletions idaes/core/solvers/get_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
This module contains the IDAES get_solver method.
"""

from pyomo.contrib.solver.base import LegacySolverWrapper

import idaes.logger as idaeslog
import idaes.core.solvers

_log = idaeslog.getLogger(__name__)


# Author: Andrew Lee
def get_solver(solver=None, options=None):
def get_solver(solver=None, options=None, writer_config=None):
"""
General method for getting a solver object which defaults to the standard
IDAES solver (defined in the IDAES configuration).
Expand All @@ -32,15 +34,28 @@ def get_solver(solver=None, options=None):
options: dict of solver options to use, overwrites any settings
provided by IDAES configuration. Default = None, use default
solver options.
writer_config: dict of configuration options for solver writer, overwrites
ny settings provided by IDAES configuration. Default = None, use
default solver options.

Returns:
A Pyomo solver object
"""
# TODO: Deprecate options argument in favour of solver_options to be more consistent
# with new Pyomo interfaces?
if solver is None:
solver = "default"
solver_obj = idaes.core.solvers.SolverWrapper(solver, register=False)()

if options is not None:
solver_obj.options.update(options)
if isinstance(solver_obj, LegacySolverWrapper):
if options is not None:
for k, v in options.items():
solver_obj.options[k] = v
if writer_config is not None:
for k, v in writer_config.items():
solver_obj.config.writer_config[k] = v
else:
if options is not None:
solver_obj.options.update(options)

return solver_obj
3 changes: 2 additions & 1 deletion idaes/core/util/initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from pyomo.environ import (
Block,
check_optimal_termination,
ConcreteModel,
Constraint,
value,
)
Expand Down Expand Up @@ -248,7 +249,7 @@ def solve_indexed_blocks(solver, blocks, **kwds):

try:
# Create a temporary Block
tmp = Block(concrete=True)
tmp = ConcreteModel(concrete=True)

nBlocks = len(blocks)

Expand Down
9 changes: 6 additions & 3 deletions idaes/core/util/tests/test_model_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,8 @@ def model(self):
m.b.v5.fix(2)
m.b.v6.fix(0)

solver = get_solver()
# Model is trivially pre-solvable - turn off
solver = get_solver(writer_config={"linear_presolve": False})
solver.solve(m)

return m
Expand Down Expand Up @@ -1101,7 +1102,8 @@ def test_collect_numerical_warnings_corrected(self, model):
m.b.v3.setlb(-5)
m.b.v5.setub(10)

solver = get_solver()
# Model is trivially pre-solvable - turn off
solver = get_solver(writer_config={"linear_presolve": False})
solver.solve(m)

dt = DiagnosticsToolbox(model=m.b)
Expand Down Expand Up @@ -1216,7 +1218,8 @@ def test_assert_no_numerical_warnings(self, model):
# Fix numerical issues
m.b.v3.setlb(-5)

solver = get_solver()
# Model is trivially pre-solvable - turn off
solver = get_solver(writer_config={"linear_presolve": False})
solver.solve(m)

dt = DiagnosticsToolbox(model=m.b)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,10 +758,11 @@ def _make_state_vars(self):
domain=NonNegativeReals,
doc="Total molar flowrate [mol/s]",
units=pyunits.mol / pyunits.s,
bounds=(1e-8, None),
)
self.mole_frac_comp = Var(
self.params.component_list,
bounds=(0, 1),
bounds=(1e-10, 1.1),
initialize=1 / len(self.params.component_list),
doc="Mixture mole fraction",
)
Expand All @@ -770,12 +771,14 @@ def _make_state_vars(self):
domain=NonNegativeReals,
doc="State pressure [Pa]",
units=pyunits.Pa,
bounds=(1e4, None),
)
self.temperature = Var(
initialize=298.15,
domain=NonNegativeReals,
doc="State temperature [K]",
units=pyunits.K,
bounds=(200, None),
)
else:
self.flow_mol_comp = Var(
Expand All @@ -784,18 +787,21 @@ def _make_state_vars(self):
domain=NonNegativeReals,
doc="Component molar flowrate [mol/s]",
units=pyunits.mol / pyunits.s,
bounds=(1e-8, None),
)
self.pressure = Var(
initialize=101325,
domain=NonNegativeReals,
doc="State pressure [Pa]",
units=pyunits.Pa,
bounds=(1e4, None),
)
self.temperature = Var(
initialize=298.15,
domain=NonNegativeReals,
doc="State temperature [K]",
units=pyunits.K,
bounds=(200, None),
)

def _make_vars(self):
Expand Down
8 changes: 6 additions & 2 deletions idaes/models/properties/cubic_eos/cubic_prop_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -1100,10 +1100,11 @@ def build(self):
domain=NonNegativeReals,
doc="Component molar flowrate [mol/s]",
units=pyunits.mol / pyunits.s,
bounds=(1e-8, None),
)
self.mole_frac_comp = Var(
self.params.component_list,
bounds=(0, None),
bounds=(1e-10, None),
initialize=1 / len(self.params.component_list),
doc="Mixture mole fractions [-]",
)
Expand All @@ -1112,12 +1113,14 @@ def build(self):
domain=NonNegativeReals,
doc="State pressure [Pa]",
units=pyunits.Pa,
bounds=(1e4, None),
)
self.temperature = Var(
initialize=298.15,
domain=NonNegativeReals,
doc="State temperature [K]",
units=pyunits.K,
bounds=(200, None),
)

# Add supporting variables
Expand All @@ -1127,13 +1130,14 @@ def build(self):
domain=NonNegativeReals,
doc="Phase molar flow rates [mol/s]",
units=pyunits.mol / pyunits.s,
bounds=(1e-8, None),
)

self.mole_frac_phase_comp = Var(
self.params.phase_list,
self.params.component_list,
initialize=1 / len(self.params.component_list),
bounds=(0, None),
bounds=(1e-10, None),
doc="Phase mole fractions [-]",
)

Expand Down
10 changes: 9 additions & 1 deletion idaes/models/properties/cubic_eos/tests/test_BT_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def configure(self):
self.param_args = {"valid_phase": "Liq"}
self.prop_args = {}
self.has_density_terms = True
self.skip_initialization_raises_exception_test = True


@pytest.mark.skipif(not cubic_roots_available(), reason="Cubic functions not available")
Expand All @@ -67,6 +68,7 @@ def configure(self):
self.param_args = {"valid_phase": "Vap"}
self.prop_args = {}
self.has_density_terms = True
self.skip_initialization_raises_exception_test = True


# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -103,7 +105,13 @@ def test_initialization_failure(self):
m.fs.state[0].temperature.fix(300)
m.fs.state[0].pressure.fix(1e5)

m.fs.state[0].p_bound = Constraint(expr=m.fs.state[0].pressure <= 1e4)
m.fs.state[0].infeas = Constraint(
expr=sum(
m.fs.state[0].get_material_flow_terms(p, j) ** 2
for p, j in m.fs.state[0].phase_component_set
)
<= 0
)

with pytest.raises(
InitializationError,
Expand Down
Loading
Loading