diff --git a/idaes/core/util/model_diagnostics.py b/idaes/core/util/model_diagnostics.py index 36aac27286..5a562d17e0 100644 --- a/idaes/core/util/model_diagnostics.py +++ b/idaes/core/util/model_diagnostics.py @@ -2962,6 +2962,14 @@ def psweep_runner_validator(val): description="Options to pass to IPOPT.", ), ) +CACONFIG.declare( + "halt_on_error", + ConfigValue( + default=False, + domain=bool, + doc="Whether to halt execution of parameter sweep on encountering a solver error (default=False).", + ), +) class ConvergenceAnalysis: @@ -2986,14 +2994,19 @@ def __init__(self, model, **kwargs): self._model = model + solver = SolverFactory("ipopt") + if self.config.solver_options is not None: + solver.options = self.config.solver_options + self._psweep = self.config.workflow_runner( input_specification=self.config.input_specification, build_model=self._build_model, + rebuild_model=True, run_model=self._run_model, collect_results=self._collect_results, - failure_recourse=self._recourse, - solver="ipopt", - solver_options=self.config.solver_options, + halt_on_error=self.config.halt_on_error, + handle_solver_error=self._recourse, + solver=solver, ) @property diff --git a/idaes/core/util/parameter_sweep.py b/idaes/core/util/parameter_sweep.py index 5cd795a9e0..71d01d5f27 100644 --- a/idaes/core/util/parameter_sweep.py +++ b/idaes/core/util/parameter_sweep.py @@ -31,6 +31,10 @@ # Set up logger _log = idaeslog.getLogger(__name__) +# TODO: Timeouts +# TODO: Progress bar/indicator +# TODO: Re-initialize option/callback + class ParameterSweepSpecification(object): """Defines a set of input variables/parameters and values to be used in diff --git a/idaes/core/util/tests/test_model_diagnostics.py b/idaes/core/util/tests/test_model_diagnostics.py index b034027188..23d44b2974 100644 --- a/idaes/core/util/tests/test_model_diagnostics.py +++ b/idaes/core/util/tests/test_model_diagnostics.py @@ -17,7 +17,6 @@ import math import numpy as np import pytest -from collections import OrderedDict import os from copy import deepcopy @@ -1944,20 +1943,13 @@ def test_report_irreducible_degenerate_sets_none(self, model, scip_solver): ca_dict = { "specification": { - "inputs": OrderedDict( - [ - ( - "v2", - OrderedDict( - [ - ("pyomo_path", "v2"), - ("lower", 2), - ("upper", 6), - ] - ), - ) - ] - ), + "inputs": { + "v2": { + "pyomo_path": "v2", + "lower": 2, + "upper": 6, + }, + }, "sampling_method": "UniformSampling", "sample_size": [2], "samples": { @@ -1968,31 +1960,21 @@ def test_report_irreducible_degenerate_sets_none(self, model, scip_solver): "column_names": [None], }, }, - "results": OrderedDict( - [ - ( - 0, - { - "solved": True, - "results": 2, - }, - ), - ( - 1, - { - "solved": True, - "results": 6, - }, - ), - ] - ), + "results": { + 0: { + "solved": True, + "results": 2, + }, + 1: { + "solved": True, + "results": 6, + }, + }, } ca_res = { "specification": { - "inputs": OrderedDict( - [("v2", OrderedDict([("pyomo_path", "v2"), ("lower", 2), ("upper", 6)]))] - ), + "inputs": {"v2": {"pyomo_path": "v2", "lower": 2, "upper": 6}}, "sampling_method": "UniformSampling", "sample_size": [2], "samples": { @@ -2003,36 +1985,28 @@ def test_report_irreducible_degenerate_sets_none(self, model, scip_solver): "column_names": [None], }, }, - "results": OrderedDict( - [ - ( - 0, - { - "solved": False, - "results": { - "iters": 7, - "iters_in_restoration": 4, - "iters_w_regularization": 0, - "time": 0.0, - "numerical_issues": True, - }, - }, - ), - ( - 1, - { - "solved": False, - "results": { - "iters": 7, - "iters_in_restoration": 4, - "iters_w_regularization": 0, - "time": 0.0, - "numerical_issues": True, - }, - }, - ), - ] - ), + "results": { + 0: { + "solved": False, + "results": { + "iters": 7, + "iters_in_restoration": 4, + "iters_w_regularization": 0, + "time": 0.0, + "numerical_issues": True, + }, + }, + 1: { + "solved": False, + "results": { + "iters": 7, + "iters_in_restoration": 4, + "iters_w_regularization": 0, + "time": 0.0, + "numerical_issues": True, + }, + }, + }, } @@ -2055,7 +2029,7 @@ def test_init(self, model): assert ca._model is model assert isinstance(ca._psweep, SequentialSweepRunner) - assert isinstance(ca.results, OrderedDict) + assert isinstance(ca.results, dict) assert ca.config.input_specification is None assert ca.config.solver_options is None @@ -2184,7 +2158,7 @@ def test_run_convergence_analysis(self, model): ca.run_convergence_analysis() - assert isinstance(ca.results, OrderedDict) + assert isinstance(ca.results, dict) assert len(ca.results) == 4 # Ignore time, as it is too noisy to test @@ -2232,12 +2206,10 @@ def ca_with_results(self): input_specification=spec, ) - ca._psweep._results = OrderedDict( - { - 0: {"solved": True, "results": 2}, - 1: {"solved": True, "results": 6}, - } - ) + ca._psweep._results = { + 0: {"solved": True, "results": 2}, + 1: {"solved": True, "results": 6}, + } return ca