From 2e58de6f842921c7aca5ea265d380c5aba884f29 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Wed, 26 Jun 2024 14:08:03 -0400 Subject: [PATCH] Activating IPOPT_V2 with presolver (#1436) * Adding infrastructure to support ipopt_v2 * Moving core/util to ipopt_v2 * Moving MH initializer to ipopt_v2 * Fixing pint version issue * Set TSA to use old IPOPT interface * Trying to resolve Windows failures * Working on platofrm dependent failure * BTInitializer with presolve * Moving last bits of core code to ipopt_v2 * Starting on idaes/models * Removing ma57_automatic_scaling default and updating idaes/models/control * idaes/model/properties part 1 * Remaining parts of idaes/models/proeprties * Fixing typo * Switching idaes/models/unit_models to ipopt_v2 * Attempt to work around HXLC issues for now * Some clean up * Switching modular properties initializer to solver indexed blocks * Addressing comments * Fixing pylint warings * Removing unnecessary test for legacy solver wrapper --- idaes/config.py | 79 +++++++++++++++++ .../initialization/block_triangularization.py | 59 +++++++++++-- idaes/core/initialization/initializer_base.py | 15 +++- .../tests/test_initializer_base.py | 3 +- idaes/core/solvers/config.py | 27 ++++-- idaes/core/solvers/get_solver.py | 27 +++++- idaes/core/solvers/tests/test_solvers.py | 82 +++++++++++++++++- idaes/core/util/initialization.py | 2 +- idaes/core/util/model_diagnostics.py | 2 +- idaes/core/util/phase_equilibria.py | 17 ++-- idaes/core/util/tests/test_initialization.py | 2 +- .../core/util/tests/test_model_diagnostics.py | 9 +- .../util/tests/test_pid_initialization.py | 2 +- .../util/tests/test_utility_minimization.py | 2 +- idaes/models/control/tests/test_antiwindup.py | 5 +- idaes/models/control/tests/test_controller.py | 2 +- idaes/models/control/tests/test_pid_pump.py | 6 +- .../control/tests/test_steam_tank_pressure.py | 3 +- .../models/costing/tests/test_SSLW_costing.py | 2 +- idaes/models/flowsheets/demo_flowsheet.py | 2 +- .../activity_coeff_prop_pack.py | 15 +++- .../tests/test_ideal_ideal_FTPz.py | 2 +- .../tests/test_ideal_ideal_FcTP.py | 2 +- .../properties/cubic_eos/cubic_prop_pack.py | 15 +++- .../cubic_eos/tests/test_BT_example.py | 6 +- .../tests/test_saponification_reaction.py | 2 +- .../tests/test_saponification_thermo.py | 2 +- .../tests/test_with_heater.py | 2 +- .../base/generic_property.py | 86 +++++++++---------- .../test_generic_property_integration.py | 10 +-- .../base/tests/test_noncondense.py | 2 +- .../base/tests/test_noncondense_PR.py | 2 +- .../base/tests/test_nonvap.py | 2 +- .../base/tests/test_nonvap_PR.py | 2 +- .../modular_properties/base/tests/test_vle.py | 2 +- .../coolprop/tests/test_coolprop_wrapper.py | 2 +- .../eos/tests/test_ceos_PR.py | 4 +- .../reactions/tests/test_reaction_example.py | 2 +- .../examples/tests/test_ASU_PR.py | 2 +- .../tests/test_ASU_PR_Dowling_2015.py | 2 +- .../examples/tests/test_BTIdeal.py | 2 +- .../examples/tests/test_BTIdeal_FPhx.py | 2 +- .../examples/tests/test_BTIdeal_FcPh.py | 2 +- .../examples/tests/test_BTIdeal_FcTP.py | 2 +- .../examples/tests/test_BT_PR.py | 3 +- .../tests/test_BT_PR_legacy_SmoothVLE.py | 3 +- .../examples/tests/test_CO2_H2O_Ideal_VLE.py | 2 +- .../examples/tests/test_CO2_bmimPF6_PR.py | 2 +- .../examples/tests/test_HC_PR.py | 2 +- .../examples/tests/test_HC_PR_vap.py | 2 +- .../test_solubility_product_verification.py | 2 +- .../tests/test_FPhx_electrolyte.py | 5 +- .../tests/test_FTPx_electrolyte.py | 7 +- .../tests/test_FcPh_electrolyte.py | 5 +- .../tests/test_FcTP_electrolyte.py | 5 +- .../tests/test_FpcTP_electrolyte.py | 10 +-- .../tests/test_shell_and_tube_1D_transport.py | 2 +- idaes/models/properties/tests/test_harness.py | 2 +- idaes/models/unit_models/heat_exchanger.py | 2 +- idaes/models/unit_models/heat_exchanger_1D.py | 2 +- idaes/models/unit_models/heat_exchanger_lc.py | 1 + .../models/unit_models/heat_exchanger_ntu.py | 2 +- idaes/models/unit_models/mixer.py | 2 +- idaes/models/unit_models/mscontactor.py | 3 +- idaes/models/unit_models/pressure_changer.py | 2 +- idaes/models/unit_models/separator.py | 2 +- idaes/models/unit_models/shell_and_tube_1d.py | 2 +- .../solid_liquid/tests/test_sl_separator.py | 2 +- .../solid_liquid/tests/test_thickener.py | 2 +- idaes/models/unit_models/tests/test_cstr.py | 2 +- .../tests/test_equilibrium_reactor.py | 7 +- idaes/models/unit_models/tests/test_feed.py | 2 +- .../unit_models/tests/test_feed_flash.py | 2 +- idaes/models/unit_models/tests/test_flash.py | 7 +- idaes/models/unit_models/tests/test_gibbs.py | 8 +- .../unit_models/tests/test_heat_exchanger.py | 7 +- .../tests/test_heat_exchanger_1D.py | 7 +- .../tests/test_heat_exchanger_lc.py | 12 ++- idaes/models/unit_models/tests/test_heater.py | 2 +- idaes/models/unit_models/tests/test_hx_ntu.py | 2 +- idaes/models/unit_models/tests/test_mixer.py | 6 +- .../unit_models/tests/test_mscontactor.py | 4 +- idaes/models/unit_models/tests/test_pfr.py | 2 +- .../tests/test_pressure_changer.py | 2 +- .../models/unit_models/tests/test_product.py | 2 +- idaes/models/unit_models/tests/test_rstoic.py | 2 +- .../unit_models/tests/test_separator.py | 10 +-- .../tests/test_shell_and_tube_1D.py | 2 +- .../tests/test_skeleton_unit_model.py | 2 +- .../unit_models/tests/test_statejunction.py | 2 +- .../unit_models/tests/test_translator.py | 2 +- idaes/models/unit_models/tests/test_valve.py | 5 +- .../initializer.py | 30 ++++--- .../tests/test_fixed_bed_tsa0d.py | 11 +-- 94 files changed, 505 insertions(+), 229 deletions(-) diff --git a/idaes/config.py b/idaes/config.py index 584a90cd66..b59856e9ac 100644 --- a/idaes/config.py +++ b/idaes/config.py @@ -19,6 +19,7 @@ import os import pyomo.common.config +from pyomo.common.config import Bool _log = logging.getLogger(__name__) # Default release version if no options provided for get-extensions @@ -322,6 +323,84 @@ def _new_idaes_config_block(): ), ) + cfg.declare( + "ipopt_v2", + pyomo.common.config.ConfigBlock( + implicit=False, + description="Default config for 'ipopt' solver", + ), + ) + cfg["ipopt_v2"].declare( + "options", + pyomo.common.config.ConfigBlock( + implicit=True, + description="Default solver options for 'ipopt'", + ), + ) + + cfg["ipopt_v2"]["options"].declare( + "nlp_scaling_method", + pyomo.common.config.ConfigValue( + domain=str, + default="gradient-based", + description="Ipopt NLP scaling method", + ), + ) + + cfg["ipopt_v2"]["options"].declare( + "tol", + pyomo.common.config.ConfigValue( + domain=float, + default=1e-6, + description="Ipopt tol option", + ), + ) + + cfg["ipopt_v2"]["options"].declare( + "max_iter", + pyomo.common.config.ConfigValue( + domain=int, + default=200, + description="Ipopt max_iter option", + ), + ) + + cfg["ipopt_v2"]["options"].declare( + "linear_solver", + pyomo.common.config.ConfigValue( + domain=str, + default="ma57", + description="Linear solver to be used by IPOPT", + ), + ) + + cfg["ipopt_v2"].declare( + "writer_config", + pyomo.common.config.ConfigBlock( + implicit=True, + description="Default writer configuration for 'ipopt'", + ), + ) + + # TODO: Remember to update BTInitializer to use get_solver once scaling tools are deployed. + 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", + ), + ) + + cfg["ipopt_v2"]["writer_config"].declare( + "linear_presolve", + pyomo.common.config.ConfigValue( + domain=Bool, + default=True, + description="Whether to apply linear presolve in writer", + ), + ) + cfg.declare( "ipopt_l1", pyomo.common.config.ConfigBlock( diff --git a/idaes/core/initialization/block_triangularization.py b/idaes/core/initialization/block_triangularization.py index c0208c2cde..0230324ed9 100644 --- a/idaes/core/initialization/block_triangularization.py +++ b/idaes/core/initialization/block_triangularization.py @@ -14,7 +14,7 @@ Initializer class for implementing Block Triangularization initialization """ from pyomo.environ import SolverFactory -from pyomo.common.config import ConfigDict, ConfigValue +from pyomo.common.config import Bool, ConfigDict, ConfigValue from pyomo.contrib.incidence_analysis import ( IncidenceGraphInterface, solve_strongly_connected_components, @@ -25,7 +25,6 @@ InitializationStatus, ) from idaes.core.util.exceptions import InitializationError -from idaes.core.solvers import get_solver __author__ = "Andrew Lee" @@ -47,7 +46,7 @@ class BlockTriangularizationInitializer(InitializerBase): CONFIG.declare( "block_solver", ConfigValue( - default="ipopt", + default="ipopt_v2", description="Solver to use for NxN blocks", ), ) @@ -59,13 +58,52 @@ class BlockTriangularizationInitializer(InitializerBase): doc="Dict of options to use to set solver.options.", ), ) + CONFIG.block_solver_options.declare( + "tol", + ConfigValue( + default=1e-8, + domain=float, + description="Convergence tolerance for block solver", + ), + ) + CONFIG.block_solver_options.declare( + "max_iter", + ConfigValue( + default=200, + domain=int, + description="Iteration limit for block solver", + ), + ) + CONFIG.declare( + "block_solver_writer_config", + ConfigDict( + implicit=True, + description="Dict of writer_config arguments to pass to block solver", + ), + ) + CONFIG.block_solver_writer_config.declare( + "linear_presolve", + ConfigValue( + default=True, + domain=Bool, + description="Whether to use linear presolver with block solver", + ), + ) + CONFIG.block_solver_writer_config.declare( + "scale_model", + ConfigValue( + default=False, + domain=Bool, + description="Whether to apply model scaling with block solver", + ), + ) CONFIG.declare( "block_solver_call_options", ConfigDict( implicit=True, description="Dict of arguments to pass to solver.solve call", doc="Dict of arguments to be passed as part of the solver.solve " - "call, such as tee=True/", + "call, such as tee=True.", ), ) CONFIG.declare( @@ -111,11 +149,14 @@ def initialization_routine(self, model): """ Call Block Triangularization solver on model. """ - if self.config.block_solver is not None: - solver = SolverFactory(self.config.block_solver) - solver.options.update(self.config.block_solver_options) - else: - solver = get_solver(options=self.config.block_solver_options) + # TODO: For now, go directly through solver factory as default solver + # options cause failures. Most of these appear to be due to scaling, + # so hopefully we can fix these later. + solver = SolverFactory( + self.config.block_solver, + options=self.config.block_solver_options, + writer_config=self.config.block_solver_writer_config, + ) if model.is_indexed(): for d in model.values(): diff --git a/idaes/core/initialization/initializer_base.py b/idaes/core/initialization/initializer_base.py index 27f4de040e..5ec8de7f75 100644 --- a/idaes/core/initialization/initializer_base.py +++ b/idaes/core/initialization/initializer_base.py @@ -551,7 +551,7 @@ class ModularInitializerBase(InitializerBase): CONFIG.declare( "solver", ConfigValue( - default=None, # TODO: Can we add a square problem solver as the default here? + default="ipopt_v2", # TODO: Can we add a square problem solver as the default here? # At the moment there is an issue with the scipy solvers not supporting the tee argument. description="Solver to use for initialization", ), @@ -563,6 +563,13 @@ class ModularInitializerBase(InitializerBase): description="Dict of options to pass to solver", ), ) + CONFIG.declare( + "writer_config", + ConfigDict( + implicit=True, + description="Dict of writer_config arguments to pass to solver", + ), + ) CONFIG.declare( "default_submodel_initializer", ConfigValue( @@ -820,6 +827,10 @@ def cleanup(self, model, plugin_initializer_args, sub_initializers): def _get_solver(self): if self._solver is None: - self._solver = get_solver(self.config.solver, self.config.solver_options) + self._solver = get_solver( + self.config.solver, + solver_options=self.config.solver_options, + writer_config=self.config.writer_config, + ) return self._solver diff --git a/idaes/core/initialization/tests/test_initializer_base.py b/idaes/core/initialization/tests/test_initializer_base.py index 94775ac525..2310c13d91 100644 --- a/idaes/core/initialization/tests/test_initializer_base.py +++ b/idaes/core/initialization/tests/test_initializer_base.py @@ -706,8 +706,9 @@ def test_base_attributed(self): assert initializer.config.default_submodel_initializer is None assert initializer._solver is None - assert initializer.config.solver is None + assert initializer.config.solver == "ipopt_v2" assert initializer.config.solver_options == {} + assert initializer.config.writer_config == {} @pytest.mark.unit def test_get_submodel_initializer_specific_model(self): diff --git a/idaes/core/solvers/config.py b/idaes/core/solvers/config.py index 72e79edea0..a2f68e2caa 100644 --- a/idaes/core/solvers/config.py +++ b/idaes/core/solvers/config.py @@ -10,15 +10,22 @@ # All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md # for full copyright and license information. ################################################################################# -# TODO: Missing doc strings -# pylint: disable=missing-module-docstring -# pylint: disable=missing-class-docstring +""" +Wrapper for Pyomo solvers to allow us to define default solver options +""" + +from copy import deepcopy from pyomo.environ import SolverFactory + import idaes class SolverWrapper(object): + """ + Wrapper for Pyomo solvers to allow us to define default solver options + """ + def __init__(self, name, register=True): if name is None: name = "default" @@ -43,13 +50,11 @@ def __call__(self, *args, **kwargs): 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 @@ -57,6 +62,14 @@ def __call__(self, *args, **kwargs): for opk, opv in v.items(): if opk not in kwargs["options"]: kwargs["options"][opk] = opv + 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 + return solver(*args, **kwargs) diff --git a/idaes/core/solvers/get_solver.py b/idaes/core/solvers/get_solver.py index 115080e6da..9ff06da677 100644 --- a/idaes/core/solvers/get_solver.py +++ b/idaes/core/solvers/get_solver.py @@ -22,25 +22,46 @@ # Author: Andrew Lee -def get_solver(solver=None, options=None): +def get_solver( + solver=None, + solver_options: dict = None, + writer_config: dict = None, + options: dict = None, +): """ General method for getting a solver object which defaults to the standard IDAES solver (defined in the IDAES configuration). Args: solver: string name for desired solver. Default=None, use default solver - options: dict of solver options to use, overwrites any settings + solver_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. + options: DEPRECATED. Alias of solver_options. Returns: A Pyomo solver object """ + if solver_options is not None: + if options is not None: + raise ValueError( + "Cannot provide both the 'options' and 'solver_options' argument. " + "'options' has been deprecated in favor of 'solver_options'." + ) + options = solver_options + 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) + 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 return solver_obj diff --git a/idaes/core/solvers/tests/test_solvers.py b/idaes/core/solvers/tests/test_solvers.py index 51fa45ba2e..abd241cd01 100644 --- a/idaes/core/solvers/tests/test_solvers.py +++ b/idaes/core/solvers/tests/test_solvers.py @@ -10,12 +10,13 @@ # All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md # for full copyright and license information. ################################################################################# -import pyomo.environ as pyo import pytest + +import pyomo.environ as pyo +from pyomo.contrib.solver.base import LegacySolverWrapper + from idaes.core.solvers.features import lp, milp, nlp, minlp, nle, dae -from idaes.core.solvers import ipopt_has_linear_solver -from idaes.core.solvers import petsc -from idaes.core.solvers import ipopt_l1 +from idaes.core.solvers import get_solver, ipopt_has_linear_solver, petsc @pytest.mark.unit @@ -259,3 +260,76 @@ def test_clp_idaes_solve(): solver = pyo.SolverFactory("clp") solver.solve(m) assert pytest.approx(x) == pyo.value(m.x) + + +@pytest.mark.skipif(not pyo.SolverFactory("ipopt").available(False), reason="no Ipopt") +@pytest.mark.unit +def test_get_solver_default(): + solver = get_solver() + + assert not isinstance(solver, LegacySolverWrapper) + + assert solver.options == { + "nlp_scaling_method": "gradient-based", + "tol": 1e-6, + "max_iter": 200, + } + + +@pytest.mark.skipif(not pyo.SolverFactory("ipopt").available(False), reason="no Ipopt") +@pytest.mark.unit +def test_get_solver_default_solver_w_options(): + with pytest.raises( + AttributeError, + match="'IPOPT' object has no attribute 'config'", + ): + get_solver(options={"foo": "bar", "tol": 1e-5}, writer_config={"foo": "bar"}) + + +@pytest.mark.skipif(not pyo.SolverFactory("ipopt").available(False), reason="no Ipopt") +@pytest.mark.unit +def test_get_solver_ipopt_v2(): + solver = get_solver("ipopt_v2") + + assert isinstance(solver, LegacySolverWrapper) + + assert solver.options.nlp_scaling_method == "gradient-based" + assert solver.options.tol == 1e-6 + assert solver.options.max_iter == 200 + + assert solver.config.writer_config.linear_presolve + assert not solver.config.writer_config.scale_model + + +@pytest.mark.skipif(not pyo.SolverFactory("ipopt").available(False), reason="no Ipopt") +@pytest.mark.unit +def test_get_solver_ipopt_v2_w_options(): + solver = get_solver( + "ipopt_v2", + options={"tol": 1e-5, "foo": "bar"}, + writer_config={"linear_presolve": False}, + ) + + assert isinstance(solver, LegacySolverWrapper) + + print(solver.options) + assert solver.options.nlp_scaling_method == "gradient-based" + assert solver.options.tol == 1e-5 + assert solver.options.max_iter == 200 + assert solver.options.foo == "bar" + + assert not solver.config.writer_config.linear_presolve + assert not solver.config.writer_config.scale_model + + +@pytest.mark.unit +def test_get_solver_ipopt_options_and_solver_options(): + with pytest.raises( + ValueError, + match="Cannot provide both the 'options' and 'solver_options' argument. " + "'options' has been deprecated in favor of 'solver_options'.", + ): + get_solver( + options={"tol": 1e-5, "foo": "bar"}, + solver_options={"tol": 1e-5, "foo": "bar"}, + ) diff --git a/idaes/core/util/initialization.py b/idaes/core/util/initialization.py index 51c792e822..6adb195f01 100644 --- a/idaes/core/util/initialization.py +++ b/idaes/core/util/initialization.py @@ -356,7 +356,7 @@ def initialize_by_time_element(fs, time, **kwargs): solver_log = idaeslog.getSolveLogger(__name__, level=outlvl) ignore_dof = kwargs.pop("ignore_dof", False) - solver = kwargs.pop("solver", get_solver()) + solver = kwargs.pop("solver", get_solver("ipopt_v2")) fix_diff_only = kwargs.pop("fix_diff_only", True) # This option makes the assumption that the only variables that # link constraints to previous points in time (which must be fixed) diff --git a/idaes/core/util/model_diagnostics.py b/idaes/core/util/model_diagnostics.py index 6db5767a77..f4488f4680 100644 --- a/idaes/core/util/model_diagnostics.py +++ b/idaes/core/util/model_diagnostics.py @@ -761,7 +761,7 @@ def compute_infeasibility_explanation(self, stream=None, solver=None, tee=False) """ if solver is None: - solver = get_solver() + solver = get_solver("ipopt_v2") if stream is None: stream = sys.stdout diff --git a/idaes/core/util/phase_equilibria.py b/idaes/core/util/phase_equilibria.py index 53f74b1291..42b5d5ec5e 100644 --- a/idaes/core/util/phase_equilibria.py +++ b/idaes/core/util/phase_equilibria.py @@ -97,26 +97,28 @@ def Txy_data( num_points=20, temperature=298.15, print_level=idaeslog.NOTSET, - solver=None, + solver="ipopt_v2", solver_op=None, + solver_writer_config=None, ): """ Function to generate T-x-y data. The function builds a state block and extracts bubble and dew temperatures at P pressure for N number of compositions. - As N is increased increase the time of the calculation will increase and + As N is increased the time of the calculation will increase and create a smoother looking plot. Args: component_1: Component 1 component_2: Component 2 - pressure: Pressure at which the bubble and drew temperatures will be calculates + pressure: Pressure at which the bubble and drew temperatures will be calculated temperature: Temperature at which to initialize state block num_points: Number of data point to be calculated - model: Model wit initialized Property package which contains data to calculate + model: Model with initialized property package which contains data to calculate bubble and dew temperatures for component 1 and component 2 print_level: printing level from initialization solver: solver to use (default=None, use IDAES default solver) solver_op: solver options + solver_writer_config: writer_config arguments for solver Returns: (Class): A class containing the T-x-y data @@ -150,14 +152,17 @@ def Txy_data( # Initialize flash unit model model.props[1].calculate_scaling_factors() + # TODO: This will need to be updated at some point as we deprecate the old initialization API model.props.initialize(solver=solver, optarg=solver_op, outlvl=print_level) - solver = get_solver(solver, solver_op) + solver = get_solver( + solver, solver_options=solver_op, writer_config=solver_writer_config + ) # Create an array of compositions with N number of points x_d = np.linspace(x, 1 - x - xs, num_points) - # Create emprty arrays for concentration, bubble temperature and dew temperature + # Create empty arrays for concentration, bubble temperature and dew temperature X = [] Tbubb = [] Tdew = [] diff --git a/idaes/core/util/tests/test_initialization.py b/idaes/core/util/tests/test_initialization.py index 138370ec1d..7390b8f34e 100644 --- a/idaes/core/util/tests/test_initialization.py +++ b/idaes/core/util/tests/test_initialization.py @@ -65,7 +65,7 @@ # Set up solver -solver = get_solver() +solver = get_solver("ipopt_v2") @declare_process_block_class("AqueousEnzymeParameterBlock") diff --git a/idaes/core/util/tests/test_model_diagnostics.py b/idaes/core/util/tests/test_model_diagnostics.py index d502c4ebf8..0a8bbfe4f3 100644 --- a/idaes/core/util/tests/test_model_diagnostics.py +++ b/idaes/core/util/tests/test_model_diagnostics.py @@ -504,7 +504,8 @@ def model(self): m.b.v5.fix(2) m.b.v6.fix(0) - solver = get_solver() + # Presolver identifies problem as trivially infeasible (correctly). Turn off presolve. + solver = get_solver("ipopt_v2", writer_config={"linear_presolve": False}) solver.solve(m) return m @@ -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() + # Presolver identifies problem as trivially infeasible (correctly). Turn off presolve. + solver = get_solver("ipopt_v2", writer_config={"linear_presolve": False}) solver.solve(m) dt = DiagnosticsToolbox(model=m.b) @@ -1216,7 +1218,8 @@ def test_assert_no_numerical_warnings(self, model): # Fix numerical issues m.b.v3.setlb(-5) - solver = get_solver() + # Presolver identifies problem as trivially infeasible (correctly). Turn off presolve. + solver = get_solver("ipopt_v2", writer_config={"linear_presolve": False}) solver.solve(m) dt = DiagnosticsToolbox(model=m.b) diff --git a/idaes/core/util/tests/test_pid_initialization.py b/idaes/core/util/tests/test_pid_initialization.py index 7efc8bbf61..49ba7c4dd3 100644 --- a/idaes/core/util/tests/test_pid_initialization.py +++ b/idaes/core/util/tests/test_pid_initialization.py @@ -53,7 +53,7 @@ __author__ = "Robert Parker" -solver = get_solver() +solver = get_solver("ipopt_v2") def make_model(horizon=6, ntfe=60, ntcp=2, inlet_E=11.91, inlet_S=12.92): diff --git a/idaes/core/util/tests/test_utility_minimization.py b/idaes/core/util/tests/test_utility_minimization.py index e942f924b0..3404d618f7 100644 --- a/idaes/core/util/tests/test_utility_minimization.py +++ b/idaes/core/util/tests/test_utility_minimization.py @@ -52,7 +52,7 @@ # Chemical Engineering Series - L. T. Biegler, I. E. Grossmann, # A. W. Westerberg, page 529, Example 16.1 # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") @pytest.mark.unit diff --git a/idaes/models/control/tests/test_antiwindup.py b/idaes/models/control/tests/test_antiwindup.py index a43c013b86..f7afc7183a 100644 --- a/idaes/models/control/tests/test_antiwindup.py +++ b/idaes/models/control/tests/test_antiwindup.py @@ -242,11 +242,12 @@ def create_model( # Initialize the model solver = get_solver( - options={ + solver="ipopt_v2", + solver_options={ "max_iter": 300, "nlp_scaling_method": "user-scaling", "linear_solver": "ma57", - } + }, ) for t in m.fs.time: diff --git a/idaes/models/control/tests/test_controller.py b/idaes/models/control/tests/test_controller.py index b17810dd29..4d7d5ac692 100644 --- a/idaes/models/control/tests/test_controller.py +++ b/idaes/models/control/tests/test_controller.py @@ -93,7 +93,7 @@ def add_derivative_components(comp_dict, nt): # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/control/tests/test_pid_pump.py b/idaes/models/control/tests/test_pid_pump.py index 74ae1d885d..1307d7d76d 100644 --- a/idaes/models/control/tests/test_pid_pump.py +++ b/idaes/models/control/tests/test_pid_pump.py @@ -164,14 +164,16 @@ def get_model(dynamic=False): m.fs.valve.valve_opening.unfix() dof = degrees_of_freedom(m) assert dof == 0 - solver = get_solver() + # TODO: MA27 works better than MA57 + solver = get_solver(solver="ipopt_v2", solver_options={"linear_solver": "ma27"}) solver.solve(m, tee=True) return m def run_dynamic(m): - solver = get_solver() + # TODO: MA27 works better than MA57 + solver = get_solver(solver="ipopt_v2", solver_options={"linear_solver": "ma27"}) # add step change for t in m.fs.time: diff --git a/idaes/models/control/tests/test_steam_tank_pressure.py b/idaes/models/control/tests/test_steam_tank_pressure.py index 76ee0f1223..132c02211f 100644 --- a/idaes/models/control/tests/test_steam_tank_pressure.py +++ b/idaes/models/control/tests/test_steam_tank_pressure.py @@ -227,7 +227,8 @@ def vol_frac_vap(b, t): m.fs.ctrl.mv_ub = 1.0 # Initialize the model - solver = get_solver(options={"max_iter": 50}) + # TODO: MA27 works better than MA57 + solver = get_solver(solver="ipopt_v2", solver_options={"linear_solver": "ma27"}) for t in m.fs.time: m.fs.valve_1.inlet.flow_mol[t] = 100 # initial guess on flow diff --git a/idaes/models/costing/tests/test_SSLW_costing.py b/idaes/models/costing/tests/test_SSLW_costing.py index 217a15daaf..102297f71b 100644 --- a/idaes/models/costing/tests/test_SSLW_costing.py +++ b/idaes/models/costing/tests/test_SSLW_costing.py @@ -90,7 +90,7 @@ __author__ = "Andrew Lee" -solver = get_solver() +solver = get_solver("ipopt_v2") @pytest.fixture diff --git a/idaes/models/flowsheets/demo_flowsheet.py b/idaes/models/flowsheets/demo_flowsheet.py index 7708ae442b..b5bbbd2cc6 100644 --- a/idaes/models/flowsheets/demo_flowsheet.py +++ b/idaes/models/flowsheets/demo_flowsheet.py @@ -484,7 +484,7 @@ def initialize_flowsheet(m): def solve_flowsheet(m, stee=False): """Solve demo flowsheet""" - solver = get_solver() + solver = get_solver("ipopt_v2") solver.solve(m, tee=stee) diff --git a/idaes/models/properties/activity_coeff_models/activity_coeff_prop_pack.py b/idaes/models/properties/activity_coeff_models/activity_coeff_prop_pack.py index 6e8aab73ce..449dd62777 100644 --- a/idaes/models/properties/activity_coeff_models/activity_coeff_prop_pack.py +++ b/idaes/models/properties/activity_coeff_models/activity_coeff_prop_pack.py @@ -322,7 +322,7 @@ class ActivityCoeffInitializer(InitializerBase): CONFIG.declare( "solver", ConfigValue( - default=None, + default="ipopt_v2", description="Solver to use for initialization", ), ) @@ -333,6 +333,13 @@ class ActivityCoeffInitializer(InitializerBase): description="Dict of options to pass to solver", ), ) + CONFIG.declare( + "solver_writer_config", + ConfigDict( + implicit=True, + description="Dict of writer_config arguments to pass to solver", + ), + ) CONFIG.declare( "calculate_variable_options", ConfigDict( @@ -379,7 +386,11 @@ def initialization_routine( k.eq_mol_frac_out.deactivate() # Create solver - solver = get_solver(self.config.solver, self.config.solver_options) + solver = get_solver( + solver=self.config.solver, + solver_options=self.config.solver_options, + writer_config=self.config.solver_writer_config, + ) # --------------------------------------------------------------------- # Initialization sequence: Deactivating certain constraints diff --git a/idaes/models/properties/activity_coeff_models/tests/test_ideal_ideal_FTPz.py b/idaes/models/properties/activity_coeff_models/tests/test_ideal_ideal_FTPz.py index f62055b015..7a691719ff 100644 --- a/idaes/models/properties/activity_coeff_models/tests/test_ideal_ideal_FTPz.py +++ b/idaes/models/properties/activity_coeff_models/tests/test_ideal_ideal_FTPz.py @@ -36,7 +36,7 @@ from idaes.core.solvers import get_solver from idaes.core.initialization import InitializationStatus -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/properties/activity_coeff_models/tests/test_ideal_ideal_FcTP.py b/idaes/models/properties/activity_coeff_models/tests/test_ideal_ideal_FcTP.py index 470d229c52..ad81694bb4 100644 --- a/idaes/models/properties/activity_coeff_models/tests/test_ideal_ideal_FcTP.py +++ b/idaes/models/properties/activity_coeff_models/tests/test_ideal_ideal_FcTP.py @@ -37,7 +37,7 @@ import idaes.core.util.scaling as iscale -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/properties/cubic_eos/cubic_prop_pack.py b/idaes/models/properties/cubic_eos/cubic_prop_pack.py index f52a791f10..97e3a89e28 100644 --- a/idaes/models/properties/cubic_eos/cubic_prop_pack.py +++ b/idaes/models/properties/cubic_eos/cubic_prop_pack.py @@ -247,7 +247,7 @@ class CubicEoSInitializer(InitializerBase): CONFIG.declare( "solver", ConfigValue( - default=None, + default="ipopt_v2", description="Solver to use for initialization", ), ) @@ -258,6 +258,13 @@ class CubicEoSInitializer(InitializerBase): description="Dict of options to pass to solver", ), ) + CONFIG.declare( + "solver_writer_config", + ConfigDict( + implicit=True, + description="Dict of writer_config arguments to pass to solver", + ), + ) CONFIG.declare( "calculate_variable_options", ConfigDict( @@ -297,7 +304,11 @@ def initialization_routine( ) # Create solver object - solver_obj = get_solver(self.config.solver, self.config.solver_options) + solver_obj = get_solver( + solver=self.config.solver, + solver_options=self.config.solver_options, + writer_config=self.config.solver_writer_config, + ) init_log.info("Starting initialization routine") diff --git a/idaes/models/properties/cubic_eos/tests/test_BT_example.py b/idaes/models/properties/cubic_eos/tests/test_BT_example.py index 14703e61b5..64af296ef2 100644 --- a/idaes/models/properties/cubic_eos/tests/test_BT_example.py +++ b/idaes/models/properties/cubic_eos/tests/test_BT_example.py @@ -37,7 +37,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- @@ -115,6 +115,10 @@ def test_initialization_failure(self): @pytest.mark.integration def test_T_sweep(self): + # TODO: This test fails if using MA57 with the linear_presolve + # This model is known to have some formulation issues. + solver = get_solver(solver="ipopt_v2", solver_options={"linear_solver": "ma27"}) + m = ConcreteModel() m.fs = FlowsheetBlock(dynamic=False) diff --git a/idaes/models/properties/examples/tests/test_saponification_reaction.py b/idaes/models/properties/examples/tests/test_saponification_reaction.py index 28f5005eae..80cc848911 100644 --- a/idaes/models/properties/examples/tests/test_saponification_reaction.py +++ b/idaes/models/properties/examples/tests/test_saponification_reaction.py @@ -31,7 +31,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") class TestParamBlock(object): diff --git a/idaes/models/properties/examples/tests/test_saponification_thermo.py b/idaes/models/properties/examples/tests/test_saponification_thermo.py index 66ab553489..c26f19d0d0 100644 --- a/idaes/models/properties/examples/tests/test_saponification_thermo.py +++ b/idaes/models/properties/examples/tests/test_saponification_thermo.py @@ -33,7 +33,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") class TestParamBlock(object): diff --git a/idaes/models/properties/general_helmholtz/tests/test_with_heater.py b/idaes/models/properties/general_helmholtz/tests/test_with_heater.py index a899f750b8..600fdc6547 100644 --- a/idaes/models/properties/general_helmholtz/tests/test_with_heater.py +++ b/idaes/models/properties/general_helmholtz/tests/test_with_heater.py @@ -35,7 +35,7 @@ # ----------------------------------------------------------------------------- # set up solver -solver = get_solver() +solver = get_solver("ipopt_v2") @pytest.mark.skipif(not helmholtz_available(), reason="General Helmholtz not available") diff --git a/idaes/models/properties/modular_properties/base/generic_property.py b/idaes/models/properties/modular_properties/base/generic_property.py index f49b88da40..5650432603 100644 --- a/idaes/models/properties/modular_properties/base/generic_property.py +++ b/idaes/models/properties/modular_properties/base/generic_property.py @@ -33,7 +33,6 @@ ) from pyomo.common.config import ConfigBlock, ConfigDict, ConfigValue, In, Bool from pyomo.util.calc_var_value import calculate_variable_from_constraint -from pyomo.contrib.incidence_analysis import solve_strongly_connected_components # Import IDAES cores from idaes.core import ( @@ -1214,9 +1213,6 @@ class ModularPropertiesInitializer(InitializerBase): 3. Solve for phase-equilibrium conditions 4. Initialize all remaining properties - The Pyomo solve_strongly_connected_components method is used at each - step to converge the problem. - Note that for systems without vapor-liquid equilibrium the generic BlockTriangularizationInitializer is probably sufficient for initializing the property package. @@ -1238,6 +1234,13 @@ class ModularPropertiesInitializer(InitializerBase): description="Dict of options to pass to solver", ), ) + CONFIG.declare( + "solver_writer_config", + ConfigDict( + implicit=True, + description="Dict of writer_config arguments to pass to solver", + ), + ) CONFIG.declare( "calculate_variable_options", ConfigDict( @@ -1277,7 +1280,11 @@ def initialization_routine( ) # Create solver object - solver_obj = get_solver(self.config.solver, self.config.solver_options) + solver_obj = get_solver( + solver=self.config.solver, + solver_options=self.config.solver_options, + writer_config=self.config.solver_writer_config, + ) init_log.info("Starting initialization routine") @@ -1365,13 +1372,8 @@ def initialization_routine( f"initialization at bubble, dew, and critical point step: " f"{degrees_of_freedom(b)}." ) - with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - solve_strongly_connected_components( - b, - solver=solver_obj, - solve_kwds={"tee": slc.tee}, - calc_var_kwds=self.config.calculate_variable_options, - ) + with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: + solve_indexed_blocks(solver_obj, model, tee=slc.tee) init_log.info("Bubble, dew, and critical point initialization completed.") # --------------------------------------------------------------------- @@ -1492,23 +1494,18 @@ def initialization_routine( pp ].phase_equil_initialization(b, pp) - if number_activated_constraints(b) > 0: - dof = degrees_of_freedom(b) - if degrees_of_freedom(b) == 0: - with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - solve_strongly_connected_components( - b, - solver=solver_obj, - solve_kwds={"tee": slc.tee}, - calc_var_kwds=self.config.calculate_variable_options, - ) - elif dof > 0: - raise InitializationError( - f"{b.name} Unexpected degrees of freedom during " - f"initialization at phase equilibrium step: {dof}." - ) - # Skip solve if DoF < 0 - this is probably due to a - # phase-component flow state with flash + if number_activated_constraints(model) > 0: + dof = degrees_of_freedom(model) + if dof == 0: + with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: + solve_indexed_blocks(solver_obj, [model], tee=slc.tee) + elif dof > 0: + raise InitializationError( + f"{model.name} Unexpected degrees of freedom during " + f"initialization at phase equilibrium step: {dof}." + ) + # Skip solve if DoF < 0 - this is probably due to a + # phase-component flow state with flash init_log.info("Phase equilibrium initialization completed.") @@ -1558,27 +1555,22 @@ def initialization_routine( lc = log(c) v.set_value(value(lc)) - if number_activated_constraints(b) > 0: - dof = degrees_of_freedom(b) - if degrees_of_freedom(b) == 0: - with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - solve_strongly_connected_components( - b, - solver=solver_obj, - solve_kwds={"tee": slc.tee}, - calc_var_kwds=self.config.calculate_variable_options, - ) - elif dof > 0: - raise InitializationError( - f"{b.name} Unexpected degrees of freedom during " - f"initialization at phase equilibrium step: {dof}." - ) - # Skip solve if DoF < 0 - this is probably due to a - # phase-component flow state with flash + if number_activated_constraints(model) > 0: + dof = degrees_of_freedom(model) + if dof == 0: + with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: + result = solve_indexed_blocks(solver_obj, model, tee=slc.tee) + elif dof > 0: + raise InitializationError( + f"{model.name} Unexpected degrees of freedom during " + f"initialization at phase equilibrium step: {dof}." + ) + # Skip solve if DoF < 0 - this is probably due to a + # phase-component flow state with flash init_log.info("Property initialization routine finished.") - return None + return result class _GenericStateBlock(StateBlock): diff --git a/idaes/models/properties/modular_properties/base/tests/test_generic_property_integration.py b/idaes/models/properties/modular_properties/base/tests/test_generic_property_integration.py index 1968f08176..b5e5df8bea 100644 --- a/idaes/models/properties/modular_properties/base/tests/test_generic_property_integration.py +++ b/idaes/models/properties/modular_properties/base/tests/test_generic_property_integration.py @@ -57,6 +57,8 @@ from idaes.core.solvers import get_solver +solver = get_solver("ipopt_v2") + configuration = { # Specifying components @@ -197,8 +199,6 @@ def test_heater_w_inherent_rxns_comp_phase(self, frame): frame.fs.H101.initialize() - solver = get_solver() - results = solver.solve(frame) assert check_optimal_termination(results) @@ -256,8 +256,6 @@ def test_heater_w_inherent_rxns_comp_total(self, frame): frame.fs.H101.initialize() - solver = get_solver() - results = solver.solve(frame) assert check_optimal_termination(results) @@ -330,8 +328,6 @@ def test_CV1D_w_inherent_rxns_comp_phase(self, frame): frame.fs.cv.initialize() - solver = get_solver() - results = solver.solve(frame) assert check_optimal_termination(results) @@ -404,8 +400,6 @@ def test_CV1D_w_inherent_rxns_comp_total(self, frame): frame.fs.cv.initialize() - solver = get_solver() - results = solver.solve(frame) assert check_optimal_termination(results) diff --git a/idaes/models/properties/modular_properties/base/tests/test_noncondense.py b/idaes/models/properties/modular_properties/base/tests/test_noncondense.py index cf070aca51..cabbb48636 100644 --- a/idaes/models/properties/modular_properties/base/tests/test_noncondense.py +++ b/idaes/models/properties/modular_properties/base/tests/test_noncondense.py @@ -55,7 +55,7 @@ # Set up logger _log = idaeslog.getLogger(__name__) -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/properties/modular_properties/base/tests/test_noncondense_PR.py b/idaes/models/properties/modular_properties/base/tests/test_noncondense_PR.py index a467d7c977..41653ad650 100644 --- a/idaes/models/properties/modular_properties/base/tests/test_noncondense_PR.py +++ b/idaes/models/properties/modular_properties/base/tests/test_noncondense_PR.py @@ -55,7 +55,7 @@ # Set up logger _log = idaeslog.getLogger(__name__) -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/properties/modular_properties/base/tests/test_nonvap.py b/idaes/models/properties/modular_properties/base/tests/test_nonvap.py index 35bea43a07..3c2d8643b7 100644 --- a/idaes/models/properties/modular_properties/base/tests/test_nonvap.py +++ b/idaes/models/properties/modular_properties/base/tests/test_nonvap.py @@ -55,7 +55,7 @@ # Set up logger _log = idaeslog.getLogger(__name__) -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/properties/modular_properties/base/tests/test_nonvap_PR.py b/idaes/models/properties/modular_properties/base/tests/test_nonvap_PR.py index a0331113a6..2c6e54de8e 100644 --- a/idaes/models/properties/modular_properties/base/tests/test_nonvap_PR.py +++ b/idaes/models/properties/modular_properties/base/tests/test_nonvap_PR.py @@ -55,7 +55,7 @@ # Set up logger _log = idaeslog.getLogger(__name__) -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/properties/modular_properties/base/tests/test_vle.py b/idaes/models/properties/modular_properties/base/tests/test_vle.py index 5431a2aea5..2beaf1ce6f 100644 --- a/idaes/models/properties/modular_properties/base/tests/test_vle.py +++ b/idaes/models/properties/modular_properties/base/tests/test_vle.py @@ -56,7 +56,7 @@ # Set up logger _log = idaeslog.getLogger(__name__) -solver = get_solver() +solver = get_solver("ipopt_v2") class TestNoHenryComps(object): diff --git a/idaes/models/properties/modular_properties/coolprop/tests/test_coolprop_wrapper.py b/idaes/models/properties/modular_properties/coolprop/tests/test_coolprop_wrapper.py index 5d2a0cdfbd..0d92cf4227 100644 --- a/idaes/models/properties/modular_properties/coolprop/tests/test_coolprop_wrapper.py +++ b/idaes/models/properties/modular_properties/coolprop/tests/test_coolprop_wrapper.py @@ -49,7 +49,7 @@ CoolPropPropertyError, ) -solver = get_solver() +solver = get_solver("ipopt_v2") class TestWrapper: diff --git a/idaes/models/properties/modular_properties/eos/tests/test_ceos_PR.py b/idaes/models/properties/modular_properties/eos/tests/test_ceos_PR.py index 6509009d44..361fdc1dd4 100644 --- a/idaes/models/properties/modular_properties/eos/tests/test_ceos_PR.py +++ b/idaes/models/properties/modular_properties/eos/tests/test_ceos_PR.py @@ -1165,7 +1165,7 @@ def model(self): }, ) - m.props = m.params.state_block_class( + m.props = m.params.build_state_block( [1], defined_state=True, parameters=m.params ) @@ -1389,7 +1389,7 @@ def test_initialize_with_critical_props(self, model): @pytest.mark.solver def test_solve_critical_props(self, model): # Solve model - solver = get_solver() + solver = get_solver("ipopt_v2") res = solver.solve(model, tee=True) assert_optimal_termination(res) diff --git a/idaes/models/properties/modular_properties/examples/reactions/tests/test_reaction_example.py b/idaes/models/properties/modular_properties/examples/reactions/tests/test_reaction_example.py index 96d30215ad..6a6f160a20 100644 --- a/idaes/models/properties/modular_properties/examples/reactions/tests/test_reaction_example.py +++ b/idaes/models/properties/modular_properties/examples/reactions/tests/test_reaction_example.py @@ -39,7 +39,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") class TestParamBlock(object): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_ASU_PR.py b/idaes/models/properties/modular_properties/examples/tests/test_ASU_PR.py index 2e1411857d..599cd58ea5 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_ASU_PR.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_ASU_PR.py @@ -47,7 +47,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_ASU_PR_Dowling_2015.py b/idaes/models/properties/modular_properties/examples/tests/test_ASU_PR_Dowling_2015.py index 4704d59a0d..9b208e2ac4 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_ASU_PR_Dowling_2015.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_ASU_PR_Dowling_2015.py @@ -49,7 +49,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal.py b/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal.py index 899a5df975..e5431202b5 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal.py @@ -47,7 +47,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FPhx.py b/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FPhx.py index 3d8e04d094..b092f1e43c 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FPhx.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FPhx.py @@ -55,7 +55,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FcPh.py b/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FcPh.py index effa1fc08a..2230ac6f9c 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FcPh.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FcPh.py @@ -56,7 +56,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FcTP.py b/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FcTP.py index e6ad6c55b3..5a72a2319c 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FcTP.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_BTIdeal_FcTP.py @@ -57,7 +57,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_BT_PR.py b/idaes/models/properties/modular_properties/examples/tests/test_BT_PR.py index ad9ad9afa7..e4da6fb0aa 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_BT_PR.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_BT_PR.py @@ -41,9 +41,8 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() # Limit iterations to make sure sweeps aren't getting out of hand -solver.options["max_iter"] = 50 +solver = get_solver(solver="ipopt_v2", solver_options={"max_iter": 100}) @pytest.mark.skipif(not cubic_roots_available(), reason="Cubic functions not available") diff --git a/idaes/models/properties/modular_properties/examples/tests/test_BT_PR_legacy_SmoothVLE.py b/idaes/models/properties/modular_properties/examples/tests/test_BT_PR_legacy_SmoothVLE.py index f25035437a..7406ffb7ba 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_BT_PR_legacy_SmoothVLE.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_BT_PR_legacy_SmoothVLE.py @@ -55,9 +55,8 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() # Limit iterations to make sure sweeps aren't getting out of hand -solver.options["max_iter"] = 50 +solver = get_solver(solver="ipopt_v2", solver_options={"max_iter": 50}) # --------------------------------------------------------------------- # Configuration dictionary for an ideal Benzene-Toluene system diff --git a/idaes/models/properties/modular_properties/examples/tests/test_CO2_H2O_Ideal_VLE.py b/idaes/models/properties/modular_properties/examples/tests/test_CO2_H2O_Ideal_VLE.py index f8700bc211..cf609d0b78 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_CO2_H2O_Ideal_VLE.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_CO2_H2O_Ideal_VLE.py @@ -54,7 +54,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_CO2_bmimPF6_PR.py b/idaes/models/properties/modular_properties/examples/tests/test_CO2_bmimPF6_PR.py index b4fc0154d4..5d9956b27f 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_CO2_bmimPF6_PR.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_CO2_bmimPF6_PR.py @@ -46,7 +46,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_HC_PR.py b/idaes/models/properties/modular_properties/examples/tests/test_HC_PR.py index 19b78556db..ce1c21d4de 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_HC_PR.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_HC_PR.py @@ -47,7 +47,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/examples/tests/test_HC_PR_vap.py b/idaes/models/properties/modular_properties/examples/tests/test_HC_PR_vap.py index 05792ab138..8a24426b15 100644 --- a/idaes/models/properties/modular_properties/examples/tests/test_HC_PR_vap.py +++ b/idaes/models/properties/modular_properties/examples/tests/test_HC_PR_vap.py @@ -54,7 +54,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") def _as_quantity(x): diff --git a/idaes/models/properties/modular_properties/reactions/tests/test_solubility_product_verification.py b/idaes/models/properties/modular_properties/reactions/tests/test_solubility_product_verification.py index cebde493e8..c82c5a69e9 100644 --- a/idaes/models/properties/modular_properties/reactions/tests/test_solubility_product_verification.py +++ b/idaes/models/properties/modular_properties/reactions/tests/test_solubility_product_verification.py @@ -52,7 +52,7 @@ ) -solver = get_solver() +solver = get_solver("ipopt_v2") def dummy_h(b, *args, **kwargs): diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FPhx_electrolyte.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FPhx_electrolyte.py index 6fb16622e3..6ccaa719bc 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FPhx_electrolyte.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FPhx_electrolyte.py @@ -52,6 +52,8 @@ from idaes.core.util.model_statistics import degrees_of_freedom from idaes.core.solvers import get_solver +solver = get_solver("ipopt_v2") + def dummy_method(b, *args, **kwargs): return 42 @@ -235,7 +237,6 @@ def test_solve_for_true_species(self, frame): assert degrees_of_freedom(m.fs) == 0 - solver = get_solver() res = solver.solve(m.fs, tee=True) # Check for optimal solution @@ -522,7 +523,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution @@ -1017,7 +1017,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FTPx_electrolyte.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FTPx_electrolyte.py index e67e37e684..8da0d8a652 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FTPx_electrolyte.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FTPx_electrolyte.py @@ -52,6 +52,8 @@ from idaes.core.util.model_statistics import degrees_of_freedom from idaes.core.solvers import get_solver +solver = get_solver("ipopt_v2") + # ----------------------------------------------------------------------------- class TestApparentSpeciesBasisNoInherent: @@ -226,7 +228,6 @@ def test_solve_for_true_species(self, frame): assert degrees_of_freedom(m.fs) == 0 - solver = get_solver() res = solver.solve(m.fs, tee=True) # Check for optimal solution @@ -511,7 +512,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution @@ -999,7 +999,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution @@ -1325,7 +1324,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution @@ -1660,7 +1658,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FcPh_electrolyte.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FcPh_electrolyte.py index 16a3fe2e0f..761c2f54b5 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FcPh_electrolyte.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FcPh_electrolyte.py @@ -52,6 +52,8 @@ from idaes.core.util.model_statistics import degrees_of_freedom from idaes.core.solvers import get_solver +solver = get_solver("ipopt_v2") + def dummy_method(b, *args, **kwargs): return 42 @@ -234,7 +236,6 @@ def test_solve_for_true_species(self, frame): assert degrees_of_freedom(m.fs) == 0 - solver = get_solver() res = solver.solve(m.fs, tee=True) # Check for optimal solution @@ -520,7 +521,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution @@ -1014,7 +1014,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FcTP_electrolyte.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FcTP_electrolyte.py index bccfbab14a..2bc2e8c263 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FcTP_electrolyte.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FcTP_electrolyte.py @@ -53,6 +53,8 @@ from idaes.core.util.model_statistics import degrees_of_freedom from idaes.core.solvers import get_solver +solver = get_solver("ipopt_v2") + # ----------------------------------------------------------------------------- class TestApparentSpeciesBasisNoInherent: @@ -226,7 +228,6 @@ def test_solve_for_true_species(self, frame): assert degrees_of_freedom(m.fs) == 0 - solver = get_solver() res = solver.solve(m.fs, tee=True) # Check for optimal solution @@ -510,7 +511,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution @@ -997,7 +997,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FpcTP_electrolyte.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FpcTP_electrolyte.py index 63d786c89b..18ab7782b9 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FpcTP_electrolyte.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FpcTP_electrolyte.py @@ -53,6 +53,8 @@ from idaes.core.util.model_statistics import degrees_of_freedom from idaes.core.solvers import get_solver +solver = get_solver("ipopt_v2") + # ----------------------------------------------------------------------------- class TestApparentSpeciesBasisNoInherent: @@ -215,7 +217,6 @@ def test_solve_for_true_species(self, frame): assert degrees_of_freedom(m.fs) == 0 - solver = get_solver() res = solver.solve(m.fs, tee=True) # Check for optimal solution @@ -489,7 +490,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution @@ -958,7 +958,6 @@ def test_solve_for_true_species(self, frame): m.fs.state.initialize() - solver = get_solver() res = solver.solve(m.fs) # Check for optimal solution @@ -1020,6 +1019,7 @@ def test_solve_for_true_species(self, frame): ) # Check apparent species mole fractions + m.fs.state[1].mole_frac_phase_comp_apparent.display() assert value( m.fs.state[1].mole_frac_phase_comp_apparent["Liq", "K2CO3"] ) == pytest.approx(0.2, rel=1e-5) @@ -1028,7 +1028,7 @@ def test_solve_for_true_species(self, frame): ) == pytest.approx(0.8, rel=1e-5) assert value( m.fs.state[1].mole_frac_phase_comp_apparent["Liq", "KHCO3"] - ) == pytest.approx(1.6e-8, abs=1e-9) + ) == pytest.approx(0, abs=1e-6) assert value( m.fs.state[1].mole_frac_phase_comp_apparent["Liq", "KHCO3"] - ) == pytest.approx(1.6e-8, abs=1e-9) + ) == pytest.approx(0, abs=1e-6) diff --git a/idaes/models/properties/modular_properties/transport_properties/tests/test_shell_and_tube_1D_transport.py b/idaes/models/properties/modular_properties/transport_properties/tests/test_shell_and_tube_1D_transport.py index 4855499796..f51f95f38b 100644 --- a/idaes/models/properties/modular_properties/transport_properties/tests/test_shell_and_tube_1D_transport.py +++ b/idaes/models/properties/modular_properties/transport_properties/tests/test_shell_and_tube_1D_transport.py @@ -52,7 +52,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/properties/tests/test_harness.py b/idaes/models/properties/tests/test_harness.py index 481a02a01d..5a8279cdc8 100644 --- a/idaes/models/properties/tests/test_harness.py +++ b/idaes/models/properties/tests/test_harness.py @@ -41,7 +41,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/heat_exchanger.py b/idaes/models/unit_models/heat_exchanger.py index dd0535c3d7..5b5763d519 100644 --- a/idaes/models/unit_models/heat_exchanger.py +++ b/idaes/models/unit_models/heat_exchanger.py @@ -136,7 +136,7 @@ def initialize_main_model( ) # Create solver - solver = get_solver(self.config.solver, self.config.solver_options) + solver = self._get_solver() self.initialize_control_volume(model.hot_side, copy_inlet_state) init_log.info_high("Initialization Step 1a (hot side) Complete.") diff --git a/idaes/models/unit_models/heat_exchanger_1D.py b/idaes/models/unit_models/heat_exchanger_1D.py index 7ec44f315b..1ea52d4ed9 100644 --- a/idaes/models/unit_models/heat_exchanger_1D.py +++ b/idaes/models/unit_models/heat_exchanger_1D.py @@ -115,7 +115,7 @@ def initialize_main_model( ) # Create solver - solver = get_solver(self.config.solver, self.config.solver_options) + solver = self._get_solver() # --------------------------------------------------------------------- # Get length values diff --git a/idaes/models/unit_models/heat_exchanger_lc.py b/idaes/models/unit_models/heat_exchanger_lc.py index 53fd069c47..b69a9d3359 100644 --- a/idaes/models/unit_models/heat_exchanger_lc.py +++ b/idaes/models/unit_models/heat_exchanger_lc.py @@ -246,6 +246,7 @@ def build(self): Args: None + Returns: None """ diff --git a/idaes/models/unit_models/heat_exchanger_ntu.py b/idaes/models/unit_models/heat_exchanger_ntu.py index 956af7b52c..c7a04f1935 100644 --- a/idaes/models/unit_models/heat_exchanger_ntu.py +++ b/idaes/models/unit_models/heat_exchanger_ntu.py @@ -129,7 +129,7 @@ def initialize_main_model( ) # Create solver - solver = get_solver(self.config.solver, self.config.solver_options) + solver = self._get_solver() self.initialize_control_volume(model.hot_side, copy_inlet_state) init_log.info_high("Initialization Step 1a (hot side) Complete.") diff --git a/idaes/models/unit_models/mixer.py b/idaes/models/unit_models/mixer.py index 3935fd963a..05afcbc6ca 100644 --- a/idaes/models/unit_models/mixer.py +++ b/idaes/models/unit_models/mixer.py @@ -110,7 +110,7 @@ def initialization_routine( ) # Create solver - solver = get_solver(self.config.solver, self.config.solver_options) + solver = self._get_solver() # Initialize inlet state blocks inlet_list = model.create_inlet_list() diff --git a/idaes/models/unit_models/mscontactor.py b/idaes/models/unit_models/mscontactor.py index 5ba55666ef..59b64ed961 100644 --- a/idaes/models/unit_models/mscontactor.py +++ b/idaes/models/unit_models/mscontactor.py @@ -51,7 +51,6 @@ ) from idaes.core.initialization import ModularInitializerBase from idaes.core.initialization.initializer_base import StoreState -from idaes.core.solvers import get_solver from idaes.core.util.model_serializer import to_json, from_json import idaes.logger as idaeslog from idaes.core.util.units_of_measurement import report_quantity @@ -158,7 +157,7 @@ def initialization_routine( c.deactivate() # Call css_solver - solver = get_solver(self.config.solver, options=self.config.solver_options) + solver = self._get_solver() solve_strongly_connected_components( model, solver=solver, diff --git a/idaes/models/unit_models/pressure_changer.py b/idaes/models/unit_models/pressure_changer.py index c5d2e123a5..c0b55e1af9 100644 --- a/idaes/models/unit_models/pressure_changer.py +++ b/idaes/models/unit_models/pressure_changer.py @@ -105,7 +105,7 @@ def initialization_routine( ) # Create solver - solver = get_solver(self.config.solver, self.config.solver_options) + solver = self._get_solver() cv = model.control_volume diff --git a/idaes/models/unit_models/separator.py b/idaes/models/unit_models/separator.py index a2a273957a..25d30c927b 100644 --- a/idaes/models/unit_models/separator.py +++ b/idaes/models/unit_models/separator.py @@ -128,7 +128,7 @@ def initialization_routine( ) # Create solver - solver = get_solver(self.config.solver, self.config.solver_options) + solver = self._get_solver() # Initialize mixed state block if model.config.mixed_state_block is not None: diff --git a/idaes/models/unit_models/shell_and_tube_1d.py b/idaes/models/unit_models/shell_and_tube_1d.py index 3ae0a42ee1..d907e05e58 100644 --- a/idaes/models/unit_models/shell_and_tube_1d.py +++ b/idaes/models/unit_models/shell_and_tube_1d.py @@ -100,7 +100,7 @@ def initialize_main_model( ) # Create solver - solver = get_solver(self.config.solver, self.config.solver_options) + solver = self._get_solver() # --------------------------------------------------------------------- # Initialize control volumes diff --git a/idaes/models/unit_models/solid_liquid/tests/test_sl_separator.py b/idaes/models/unit_models/solid_liquid/tests/test_sl_separator.py index 5ff3a5c95e..5648ae54d3 100644 --- a/idaes/models/unit_models/solid_liquid/tests/test_sl_separator.py +++ b/idaes/models/unit_models/solid_liquid/tests/test_sl_separator.py @@ -50,7 +50,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/solid_liquid/tests/test_thickener.py b/idaes/models/unit_models/solid_liquid/tests/test_thickener.py index 6775d6bb6e..b78448cca2 100644 --- a/idaes/models/unit_models/solid_liquid/tests/test_thickener.py +++ b/idaes/models/unit_models/solid_liquid/tests/test_thickener.py @@ -61,7 +61,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") @declare_process_block_class("SolidParameterBlock") diff --git a/idaes/models/unit_models/tests/test_cstr.py b/idaes/models/unit_models/tests/test_cstr.py index 0701c68e32..29fd694cdf 100644 --- a/idaes/models/unit_models/tests/test_cstr.py +++ b/idaes/models/unit_models/tests/test_cstr.py @@ -53,7 +53,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_equilibrium_reactor.py b/idaes/models/unit_models/tests/test_equilibrium_reactor.py index 8dc8c9c199..d87fa717fe 100644 --- a/idaes/models/unit_models/tests/test_equilibrium_reactor.py +++ b/idaes/models/unit_models/tests/test_equilibrium_reactor.py @@ -52,7 +52,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- @@ -330,7 +330,10 @@ def test_general_hierarchical(self, model): @pytest.mark.component def test_block_triangularization(self, model): - initializer = BlockTriangularizationInitializer(constraint_tolerance=2e-5) + initializer = BlockTriangularizationInitializer( + constraint_tolerance=2e-5, + block_solver_writer_config={"linear_presolve": False}, + ) initializer.initialize(model.fs.unit) assert initializer.summary[model.fs.unit]["status"] == InitializationStatus.Ok diff --git a/idaes/models/unit_models/tests/test_feed.py b/idaes/models/unit_models/tests/test_feed.py index f299334bb3..9138858959 100644 --- a/idaes/models/unit_models/tests/test_feed.py +++ b/idaes/models/unit_models/tests/test_feed.py @@ -42,7 +42,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_feed_flash.py b/idaes/models/unit_models/tests/test_feed_flash.py index f521b2002c..61772649a2 100644 --- a/idaes/models/unit_models/tests/test_feed_flash.py +++ b/idaes/models/unit_models/tests/test_feed_flash.py @@ -48,7 +48,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_flash.py b/idaes/models/unit_models/tests/test_flash.py index 7e4b459939..d709328a48 100644 --- a/idaes/models/unit_models/tests/test_flash.py +++ b/idaes/models/unit_models/tests/test_flash.py @@ -53,7 +53,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- @@ -668,7 +668,10 @@ def test_general_hierarchical(self, model): @pytest.mark.component def test_block_triangularization(self, model): - initializer = BlockTriangularizationInitializer(constraint_tolerance=2e-5) + initializer = BlockTriangularizationInitializer( + constraint_tolerance=2e-5, + block_solver_writer_config={"linear_presolve": False}, + ) initializer.initialize(model.fs.unit) assert initializer.summary[model.fs.unit]["status"] == InitializationStatus.Ok diff --git a/idaes/models/unit_models/tests/test_gibbs.py b/idaes/models/unit_models/tests/test_gibbs.py index 5bf9e87272..638aa8e2a1 100644 --- a/idaes/models/unit_models/tests/test_gibbs.py +++ b/idaes/models/unit_models/tests/test_gibbs.py @@ -59,7 +59,8 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +# TODO: Does not solve with MA57 - check again once scaling is deployed +solver = get_solver(solver="ipopt_v2", solver_options={"linear_solver": "ma27"}) # ----------------------------------------------------------------------------- @@ -1759,7 +1760,10 @@ def test_general_hierarchical(self, model): @pytest.mark.component def test_block_triangularization(self, model): - initializer = BlockTriangularizationInitializer(constraint_tolerance=2e-5) + initializer = BlockTriangularizationInitializer( + constraint_tolerance=2e-5, + block_solver_writer_config={"linear_presolve": False}, + ) initializer.initialize( model.fs.unit, initial_guesses={ diff --git a/idaes/models/unit_models/tests/test_heat_exchanger.py b/idaes/models/unit_models/tests/test_heat_exchanger.py index ce671036cd..e7843dd42a 100644 --- a/idaes/models/unit_models/tests/test_heat_exchanger.py +++ b/idaes/models/unit_models/tests/test_heat_exchanger.py @@ -89,7 +89,9 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +# TODO: Using MA57 causes a numerical diagnostics check to fail for some reason +# TODO: Instance of model is poorly scaled, so hopefully can be resolved with scaling tools +solver = get_solver(solver="ipopt_v2", solver_options={"linear_solver": "ma27"}) # ----------------------------------------------------------------------------- @@ -1527,8 +1529,9 @@ def test_conservation(self, sapon): @pytest.mark.skipif(solver is None, reason="Solver not available") @pytest.mark.component def test_numerical_issues(self, sapon): - # Heat transfer constraint has a a residual of ~1e-3 + # TODO: Heat transfer constraint has a residual of ~1e-3 # Model could be better scaled + # TODO: Using MA57 results in extreme Jacobian and aprallel constraints? dt = DiagnosticsToolbox(sapon, constraint_residual_tolerance=1e-2) dt.assert_no_numerical_warnings() diff --git a/idaes/models/unit_models/tests/test_heat_exchanger_1D.py b/idaes/models/unit_models/tests/test_heat_exchanger_1D.py index 7cbd2d1753..5a37d5bb76 100644 --- a/idaes/models/unit_models/tests/test_heat_exchanger_1D.py +++ b/idaes/models/unit_models/tests/test_heat_exchanger_1D.py @@ -85,7 +85,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- @@ -3342,7 +3342,10 @@ def test_general_hx1d_initializer(self, model): @pytest.mark.integration def test_block_triangularization(self, model): - initializer = BlockTriangularizationInitializer(constraint_tolerance=2e-5) + initializer = BlockTriangularizationInitializer( + constraint_tolerance=2e-5, + block_solver_writer_config={"linear_presolve": False}, + ) # Need to ignore unused variables at inlets initializer.initialize(model.fs.unit, exclude_unused_vars=True) diff --git a/idaes/models/unit_models/tests/test_heat_exchanger_lc.py b/idaes/models/unit_models/tests/test_heat_exchanger_lc.py index f1ed349f44..3e97624ff3 100644 --- a/idaes/models/unit_models/tests/test_heat_exchanger_lc.py +++ b/idaes/models/unit_models/tests/test_heat_exchanger_lc.py @@ -15,6 +15,7 @@ Author: Rusty Gentile, John Eslick, Andrew Lee """ +from sys import platform import pytest import re @@ -62,7 +63,7 @@ from idaes.core.util import DiagnosticsToolbox # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # Number of steps for transient simulations TIME_STEPS = 50 @@ -550,7 +551,6 @@ def model(self): m.fs.unit.area.fix(1000) # Modified from the original: - # m.fs.unit.overall_heat_transfer_coefficient.fix(100) m.fs.unit.ua_hot_side.fix(200 * 1000) m.fs.unit.ua_cold_side.fix(200 * 1000) @@ -560,7 +560,13 @@ def model(self): @pytest.mark.component def test_hx_initializer(self, model): - initializer = HX0DInitializer() + # Setting bounds on deltaT seems to help avoid a temperature cross-over + # during initialization. + model.fs.unit.delta_temperature_in.setlb(1e-8) + model.fs.unit.delta_temperature_out.setlb(1e-8) + # TODO: Linear presolve appears to cause issues on Windows + # Hope that these will be fixed with better scaling + initializer = HX0DInitializer(writer_config={"linear_presolve": False}) initializer.initialize(model.fs.unit) assert initializer.summary[model.fs.unit]["status"] == InitializationStatus.Ok diff --git a/idaes/models/unit_models/tests/test_heater.py b/idaes/models/unit_models/tests/test_heater.py index 6f67b13f11..01515fa486 100644 --- a/idaes/models/unit_models/tests/test_heater.py +++ b/idaes/models/unit_models/tests/test_heater.py @@ -61,7 +61,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_hx_ntu.py b/idaes/models/unit_models/tests/test_hx_ntu.py index 86af622fa1..7b9864566a 100644 --- a/idaes/models/unit_models/tests/test_hx_ntu.py +++ b/idaes/models/unit_models/tests/test_hx_ntu.py @@ -54,7 +54,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_mixer.py b/idaes/models/unit_models/tests/test_mixer.py index 6046bf23e8..a07d9f1ac7 100644 --- a/idaes/models/unit_models/tests/test_mixer.py +++ b/idaes/models/unit_models/tests/test_mixer.py @@ -100,7 +100,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- @@ -1842,7 +1842,7 @@ def test_component_phase_w_inherent_rxns(): initializer = BlockTriangularizationInitializer() initializer.initialize(m.fs.unit) - results = get_solver().solve(m) + results = solver.solve(m) assert check_optimal_termination(results) assert initializer.summary[m.fs.unit]["status"] == InitializationStatus.Ok @@ -1900,7 +1900,7 @@ def test_component_total_w_inherent_rxns(): initializer = BlockTriangularizationInitializer() initializer.initialize(m.fs.unit) - results = get_solver().solve(m) + results = solver.solve(m) assert check_optimal_termination(results) assert initializer.summary[m.fs.unit]["status"] == InitializationStatus.Ok diff --git a/idaes/models/unit_models/tests/test_mscontactor.py b/idaes/models/unit_models/tests/test_mscontactor.py index 56f4effd5b..e82606063c 100644 --- a/idaes/models/unit_models/tests/test_mscontactor.py +++ b/idaes/models/unit_models/tests/test_mscontactor.py @@ -76,6 +76,8 @@ SaponificationParameterBlock, ) +solver = get_solver("ipopt_v2") + # ----------------------------------------------------------------------------- # Property packages for testing @@ -2787,7 +2789,6 @@ def test_toy_problem(self, model): assert (degrees_of_freedom(model)) == 0 - solver = get_solver() results = solver.solve(model) assert_optimal_termination(results) @@ -3456,7 +3457,6 @@ def test_initialize_and_solve(self, model): # Solve the full model assert degrees_of_freedom(model) == 0 - solver = get_solver() res = solver.solve(model, tee=True) assert_optimal_termination(res) diff --git a/idaes/models/unit_models/tests/test_pfr.py b/idaes/models/unit_models/tests/test_pfr.py index b639ba0650..dba8462374 100644 --- a/idaes/models/unit_models/tests/test_pfr.py +++ b/idaes/models/unit_models/tests/test_pfr.py @@ -54,7 +54,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_pressure_changer.py b/idaes/models/unit_models/tests/test_pressure_changer.py index 0d2ab161d6..5506ceeac3 100644 --- a/idaes/models/unit_models/tests/test_pressure_changer.py +++ b/idaes/models/unit_models/tests/test_pressure_changer.py @@ -73,7 +73,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_product.py b/idaes/models/unit_models/tests/test_product.py index 72d23aaa2b..3c5908ce1c 100644 --- a/idaes/models/unit_models/tests/test_product.py +++ b/idaes/models/unit_models/tests/test_product.py @@ -52,7 +52,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_rstoic.py b/idaes/models/unit_models/tests/test_rstoic.py index 3663a294de..1cb7957957 100644 --- a/idaes/models/unit_models/tests/test_rstoic.py +++ b/idaes/models/unit_models/tests/test_rstoic.py @@ -56,7 +56,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_separator.py b/idaes/models/unit_models/tests/test_separator.py index 13f9458448..c1390fe0e2 100644 --- a/idaes/models/unit_models/tests/test_separator.py +++ b/idaes/models/unit_models/tests/test_separator.py @@ -91,7 +91,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- @@ -4012,7 +4012,7 @@ def test_total_flow_w_inherent_rxns(): initializer = BlockTriangularizationInitializer() initializer.initialize(m.fs.sep) - results = get_solver().solve(m) + results = solver.solve(m) assert check_optimal_termination(results) assert initializer.summary[m.fs.sep]["status"] == InitializationStatus.Ok @@ -4087,7 +4087,7 @@ def test_component_flow_w_inherent_rxns(): initializer = BlockTriangularizationInitializer() initializer.initialize(m.fs.sep) - results = get_solver().solve(m) + results = solver.solve(m) assert check_optimal_termination(results) assert initializer.summary[m.fs.sep]["status"] == InitializationStatus.Ok @@ -4162,7 +4162,7 @@ def test_phase_flow_w_inherent_rxns(): initializer = BlockTriangularizationInitializer() initializer.initialize(m.fs.sep) - results = get_solver().solve(m) + results = solver.solve(m) assert check_optimal_termination(results) assert initializer.summary[m.fs.sep]["status"] == InitializationStatus.Ok @@ -4237,7 +4237,7 @@ def test_phase_component_flow_w_inherent_rxns(): initializer = BlockTriangularizationInitializer() initializer.initialize(m.fs.sep) - results = get_solver().solve(m) + results = solver.solve(m) assert check_optimal_termination(results) assert initializer.summary[m.fs.sep]["status"] == InitializationStatus.Ok diff --git a/idaes/models/unit_models/tests/test_shell_and_tube_1D.py b/idaes/models/unit_models/tests/test_shell_and_tube_1D.py index bdb087e3cb..c83ddb6d68 100644 --- a/idaes/models/unit_models/tests/test_shell_and_tube_1D.py +++ b/idaes/models/unit_models/tests/test_shell_and_tube_1D.py @@ -78,7 +78,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_skeleton_unit_model.py b/idaes/models/unit_models/tests/test_skeleton_unit_model.py index 7d353f1b0c..a903fc9909 100644 --- a/idaes/models/unit_models/tests/test_skeleton_unit_model.py +++ b/idaes/models/unit_models/tests/test_skeleton_unit_model.py @@ -35,7 +35,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_statejunction.py b/idaes/models/unit_models/tests/test_statejunction.py index 43e5b920fc..36737f1fe8 100644 --- a/idaes/models/unit_models/tests/test_statejunction.py +++ b/idaes/models/unit_models/tests/test_statejunction.py @@ -49,7 +49,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_translator.py b/idaes/models/unit_models/tests/test_translator.py index d906036ff4..5100d34d79 100644 --- a/idaes/models/unit_models/tests/test_translator.py +++ b/idaes/models/unit_models/tests/test_translator.py @@ -45,7 +45,7 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver() +solver = get_solver("ipopt_v2") # ----------------------------------------------------------------------------- diff --git a/idaes/models/unit_models/tests/test_valve.py b/idaes/models/unit_models/tests/test_valve.py index cfa4cf2343..d19a830364 100644 --- a/idaes/models/unit_models/tests/test_valve.py +++ b/idaes/models/unit_models/tests/test_valve.py @@ -38,7 +38,10 @@ # ----------------------------------------------------------------------------- # Get default solver for testing -solver = get_solver(options={"nlp_scaling_method": "user-scaling"}) +# TODO: Will need to switch this to not use user-scaling once scaling tools are ready +solver = get_solver( + solver="ipopt_v2", solver_options={"nlp_scaling_method": "user-scaling"} +) @pytest.mark.skipif(not helmholtz_available(), reason="General Helmholtz not available") diff --git a/idaes/models_extra/temperature_swing_adsorption/initializer.py b/idaes/models_extra/temperature_swing_adsorption/initializer.py index 3c279defd0..96aed60d5d 100644 --- a/idaes/models_extra/temperature_swing_adsorption/initializer.py +++ b/idaes/models_extra/temperature_swing_adsorption/initializer.py @@ -32,7 +32,6 @@ from idaes.core.util.exceptions import InitializationError from idaes.core.util.model_serializer import to_json, from_json from idaes.core.util.model_statistics import degrees_of_freedom -from idaes.core.solvers import get_solver from idaes.models_extra.temperature_swing_adsorption import SteamCalculationType @@ -51,6 +50,10 @@ class FixedBedTSA0DInitializer(ModularInitializerBase): """ + # TODO: Using IPOPT_v2 as the default solver causes lots of test failures + CONFIG = ModularInitializerBase.CONFIG() + CONFIG.solver = "ipopt" + def initialize( self, model: Block, @@ -107,10 +110,7 @@ def initialization_routine( ) # create solver - if self.config.solver is None: - self.opt = get_solver(self.config.solver, self.config.solver_options) - else: - self.opt = self.config.solver + solver = self._get_solver() # initialization of fixed bed TSA model unit init_log.info("Starting fixed bed TSA initialization") @@ -313,7 +313,7 @@ def initialization_routine( if degrees_of_freedom(blk) == 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(blk, tee=slc.tee) + res = solver.solve(blk, tee=slc.tee) if check_optimal_termination(res): init_log.info( @@ -384,7 +384,7 @@ def initialization_routine( # re-solve compressor model with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(blk.compressor, tee=slc.tee) + res = solver.solve(blk.compressor, tee=slc.tee) if check_optimal_termination(res): init_log.info( @@ -461,7 +461,7 @@ def initialization_routine( ) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(blk.steam_heater, tee=slc.tee) + res = solver.solve(blk.steam_heater, tee=slc.tee) if check_optimal_termination(res): init_log.info_high( @@ -501,7 +501,7 @@ def initialization_routine( ) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(blk.steam_heater, tee=slc.tee) + res = solver.solve(blk.steam_heater, tee=slc.tee) if check_optimal_termination(res): init_log.info_high( @@ -570,7 +570,7 @@ def initialization_routine( if degrees_of_freedom(blk) == 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(blk, tee=slc.tee) + res = solver.solve(blk, tee=slc.tee) if ( blk.config.compressor and blk.config.steam_calculation != SteamCalculationType.none @@ -682,8 +682,9 @@ def _step_initialize(self, cycle_step=None): ) # solve cycle step + solver = self._get_solver() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(cycle_step, tee=slc.tee) + res = solver.solve(cycle_step, tee=slc.tee) if check_optimal_termination(res): init_log.info( @@ -735,8 +736,9 @@ def _false_position_method(self, blk, cycle_step=None, t_guess=None): count = 1 # solve model + solver = self._get_solver() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(cycle_step, tee=slc.tee) + res = solver.solve(cycle_step, tee=slc.tee) if check_optimal_termination(res): init_log.info_high( @@ -778,7 +780,7 @@ def _false_position_method(self, blk, cycle_step=None, t_guess=None): # solve model with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(cycle_step, tee=slc.tee) + res = solver.solve(cycle_step, tee=slc.tee) if check_optimal_termination(res): init_log.info_high( @@ -843,7 +845,7 @@ def _false_position_method(self, blk, cycle_step=None, t_guess=None): # solve model with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: - res = self.opt.solve(cycle_step, tee=slc.tee) + res = solver.solve(cycle_step, tee=slc.tee) if check_optimal_termination(res): init_log.info_high( diff --git a/idaes/models_extra/temperature_swing_adsorption/tests/test_fixed_bed_tsa0d.py b/idaes/models_extra/temperature_swing_adsorption/tests/test_fixed_bed_tsa0d.py index 93ab2ed8cd..0ab6e37361 100644 --- a/idaes/models_extra/temperature_swing_adsorption/tests/test_fixed_bed_tsa0d.py +++ b/idaes/models_extra/temperature_swing_adsorption/tests/test_fixed_bed_tsa0d.py @@ -47,10 +47,7 @@ SteamCalculationType, TransformationScheme, ) -from idaes.models_extra.temperature_swing_adsorption.util import ( - plot_tsa_profiles, - tsa_summary, -) +from idaes.models_extra.temperature_swing_adsorption.util import tsa_summary import idaes.core.util.scaling as iscale # ----------------------------------------------------------------------------- @@ -182,10 +179,6 @@ def test_summary(self, model): assert "Cycle time [h]" in summary_df.index assert "Pressure drop [Pa]" in summary_df.index - @pytest.mark.unit - def test_plotting(self, model): - plot_tsa_profiles(model.fs.unit) - class TestTsaMgmof: # also testing calculate beds and simple steam calcs @@ -249,7 +242,7 @@ def test_units(self, model): @pytest.mark.skipif(solver is None, reason="Solver not available") @pytest.mark.component def test_initialize(self, model): - initializer = FixedBedTSA0DInitializer() + initializer = FixedBedTSA0DInitializer(constraint_tolerance=1e-4) initializer.initialize( model.fs.unit, heating_time_guess=2000, cooling_time_guess=900 )