Skip to content

Commit

Permalink
Adding basic parameter sweep tool (#1284)
Browse files Browse the repository at this point in the history
* Initial work on parameter sweep

* Fixing config tests

* Working on testing parameter sweep base class

* Finishing parameter sweep base file

* Adding basic sequential runner

* Deprecate old convergence tester tools

* Working on ConvergenceAnalysis

* Adding methods to compare to baseline

* Adding doc strings

* Fixing spelling

* Adding parameterization to parameter sweep callbacks

* Renaming recourse and switching to take solver object instead of name

* Addressing comments on parameter sweep

* Updating convergence tester

* Adding progress bar

* Fixing pyling issues

* Changing how solver is handled

* Rename collect_results to build_results for consistency with WaterTAP

* Addressing most PR comments

* Fixing doc string formatting

* Update idaes/core/util/parameter_sweep.py

Co-authored-by: MarcusHolly <[email protected]>

* Fixing typo in test regex

* Renaming ConvergenceAnalysis to IpoptConvergenceAnalysis

---------

Co-authored-by: Keith Beattie <[email protected]>
Co-authored-by: MarcusHolly <[email protected]>
  • Loading branch information
3 people authored Feb 22, 2024
1 parent 94b98f7 commit 777b813
Show file tree
Hide file tree
Showing 9 changed files with 3,672 additions and 101 deletions.
230 changes: 141 additions & 89 deletions idaes/core/util/convergence/convergence_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
- a Pyomo solver with appropriate options
The module executes convergence evaluation in two steps. In the first step, a
json file is created that containsa set of points sampled from the provided
json file is created that contains a set of points sampled from the provided
inputs. This step only needs to be done once - up front. The second step, which
should be executed any time there is a major code change that could impact the
model, takes that set of sampled points and solves the model at each of the
Expand Down Expand Up @@ -79,6 +79,7 @@ class from ConvergenceEvaluation, and implement three methods:
from pyomo.core import Param, Var
from pyomo.common.log import LoggingIntercept
from pyomo.environ import check_optimal_termination
from pyomo.common.deprecation import deprecated

# idaes
import idaes.core.util.convergence.mpi_utils as mpiu
Expand All @@ -92,6 +93,11 @@ class from ConvergenceEvaluation, and implement three methods:
convergence_classes = {}


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def register_convergence_class(name):
def _register_convergence_class(cls):
if name in convergence_classes:
Expand All @@ -102,6 +108,11 @@ def _register_convergence_class(cls):
return _register_convergence_class


@deprecated(
msg="This class has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
class ConvergenceEvaluationSpecification(object):
"""
Object for defining sample points for convergence evaluations.
Expand All @@ -121,28 +132,27 @@ def add_sampled_input(
(with given mean and standard deviation)
truncated to the values given by lower and upper bounds
Parameters
----------
name : str
The name of the input.
pyomo_path : str
A string representation of the path to the variable or parameter to
be sampled. This string will be executed to retrieve the Pyomo
component.
lower : float
A lower bound on the input variable or parameter.
upper : float
An upper bound on the input variable or parameter
mean : float
The mean value to use when generating normal distribution samples
std : float
The standard deviation to use when generating normal distribution samples
distribution : str
The Distribution type {"normal", "uniform"}
Args:
name - str
The name of the input.
pyomo_path - str
A string representation of the path to the variable or parameter to
be sampled. This string will be executed to retrieve the Pyomo
component.
lower - float
A lower bound on the input variable or parameter.
upper - float
An upper bound on the input variable or parameter
mean - float
The mean value to use when generating normal distribution samples
std - float
The standard deviation to use when generating normal distribution samples
distribution - str
The Distribution type {"normal", "uniform"}
Returns:
None
Returns
-------
N/A
"""
# ToDo: put some error checking here ... Maybe we should have the model
# ToDo: already? Can use to check if the pyomo_path is valid? check if
Expand All @@ -161,6 +171,11 @@ def inputs(self):
return self._inputs


@deprecated(
msg="This class has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
class ConvergenceEvaluation:
"""
Object for running convergence evaluations.
Expand Down Expand Up @@ -196,11 +211,10 @@ def get_initialized_model(self):
values of parameters or variables according to the sampling
specifications.
Returns
-------
Pyomo model : return a Pyomo model object that is initialized and
ready to solve. This is the model object that will be
used in the evaluation.
Returns:
Pyomo model - return a Pyomo model object that is initialized and
ready to solve. This is the model object that will be
used in the evaluation.
"""
raise NotImplementedError(
"Not implemented in the base class. This"
Expand All @@ -214,9 +228,8 @@ def get_solver(self):
Users may overload this to use a custom solver or options if required.
Returns
-------
Pyomo solver
Returns:
Pyomo solver
"""
return get_solver()
Expand Down Expand Up @@ -397,26 +410,22 @@ def _run_ipopt_with_stats(model, solver, max_iter=500, max_cpu_time=120):
"""
Run the solver (must be ipopt) and return the convergence statistics
Parameters
----------
model : Pyomo model
The pyomo model to be solved
solver : Pyomo solver
The pyomo solver to use - it must be ipopt, but with whichever options
are preferred
max_iter : int
The maximum number of iterations to allow for ipopt
Args:
model - Pyomo model
The pyomo model to be solved
solver - Pyomo solver
The pyomo solver to use - it must be ipopt, but with whichever options
are preferred
max_iter - int
The maximum number of iterations to allow for ipopt
max_cpu_time - int
The maximum cpu time to allow for ipopt (in seconds)
max_cpu_time : int
The maximum cpu time to allow for ipopt (in seconds)
Returns:
Returns a tuple with (solve status object, bool (solve successful or
not), number of iters, number of iters in restoration, number of iters with regularization,
solve time)
Returns
-------
Returns a tuple with (solve status object, bool (solve successful or
not), number of iters, number of iters in restoration, number of iters with regularization,
solve time)
"""
# ToDo: Check that the "solver" is, in fact, IPOPT

Expand Down Expand Up @@ -510,25 +519,30 @@ def _set_model_parameters_from_sample(model, inputs, sample_point):
)


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def generate_samples(eval_spec, n_points, seed=None):
"""
Samples the space of the inputs defined in the eval_spec, and creates an
OrderedDict with all the points to be used in executing a convergence
evaluation
Parameters
----------
eval_spec : ConvergenceEvaluationSpecification
The convergence evaluation specification object that we would like to
sample
n_points : int
The total number of points that should be created
seed : int or None
The seed to be used when generating samples. If set to None, then the
seed is not set
Returns
-------
Args:
eval_spec - ConvergenceEvaluationSpecification
The convergence evaluation specification object that we would like to
sample
n_points - int
The total number of points that should be created
seed - int or None
The seed to be used when generating samples. If set to None, then the
seed is not set
Returns:
OrderedDict of samples
"""
if seed is not None:
np.random.seed(seed)
Expand All @@ -549,6 +563,11 @@ def generate_samples(eval_spec, n_points, seed=None):
return samples


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def write_sample_file(
eval_spec, filename, convergence_evaluation_class_str, n_points, seed=None
):
Expand All @@ -557,25 +576,25 @@ def write_sample_file(
json file with all the points to be used in executing a convergence
evaluation
Parameters
----------
filename : str
The filename for the json file that will be created containing all the
points to be run
eval_spec : ConvergenceEvaluationSpecification
The convergence evaluation specification object that we would like to
sample
convergence_evaluation_class_str : str
Python string that identifies the convergence evaluation class for this
specific evaluation. This is usually in the form of module.class_name.
n_points : int
The total number of points that should be created
seed : int or None
The seed to be used when generating samples. If set to None, then the
seed is not set
Returns
-------
N/A
Args:
filename - str
The filename for the json file that will be created containing all the
points to be run
eval_spec - ConvergenceEvaluationSpecification
The convergence evaluation specification object that we would like to
sample
convergence_evaluation_class_str - str
Python string that identifies the convergence evaluation class for this
specific evaluation. This is usually in the form of module.class_name.
n_points - int
The total number of points that should be created
seed - int or None
The seed to be used when generating samples. If set to None, then the
seed is not set
Returns:
None
"""
# build the samples
samples = generate_samples(eval_spec, n_points, seed)
Expand All @@ -592,6 +611,11 @@ def write_sample_file(
json.dump(jsondict, fd, indent=3)


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def run_convergence_evaluation_from_sample_file(sample_file):
"""
Run convergence evaluation using specified sample file.
Expand Down Expand Up @@ -624,6 +648,11 @@ def run_convergence_evaluation_from_sample_file(sample_file):
return run_convergence_evaluation(jsondict, conv_eval)


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def run_single_sample_from_sample_file(sample_file, name):
"""
Run single convergence evaluation from sample in provided file.
Expand Down Expand Up @@ -657,6 +686,11 @@ def run_single_sample_from_sample_file(sample_file, name):
return run_single_sample(jsondict, conv_eval, name)


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def run_single_sample(sample_file_dict, conv_eval, name):
"""
Run single sample from dict and return IPOPT stats.
Expand All @@ -676,23 +710,26 @@ def run_single_sample(sample_file_dict, conv_eval, name):
return _run_ipopt_with_stats(model, solver)


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def run_convergence_evaluation(sample_file_dict, conv_eval):
"""
Run convergence evaluation and generate the statistics based on information
in the sample_file.
Parameters
----------
sample_file_dict : dict
Dictionary created by ConvergenceEvaluationSpecification that contains
the input and sample point information
Args:
sample_file_dict - dict
Dictionary created by ConvergenceEvaluationSpecification that contains
the input and sample point information
conv_eval - ConvergenceEvaluation
The ConvergenceEvaluation object that should be used
conv_eval : ConvergenceEvaluation
The ConvergenceEvaluation object that should be used
Returns:
None
Returns
-------
N/A
"""
inputs = sample_file_dict["inputs"]
samples = sample_file_dict["samples"]
Expand Down Expand Up @@ -754,6 +791,11 @@ def run_convergence_evaluation(sample_file_dict, conv_eval):
return inputs, samples, global_results


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def generate_baseline_statistics(
conv_eval, n_points: int, seed: int = None, display: bool = True
):
Expand Down Expand Up @@ -818,6 +860,11 @@ def generate_baseline_statistics(
return jsondict


@deprecated(
msg="This function has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
def save_convergence_statistics(
inputs, results, dmf=None, display=True, json_path=None, report_path=None
):
Expand All @@ -838,6 +885,11 @@ def save_convergence_statistics(
return s


@deprecated(
msg="This class has been deprecated in favor of the new Parameter Sweep "
"tools and may be removed in a future release.",
version="2.3.0",
)
class Stats(object):
def __init__(self, inputs=None, results=None, from_dict=None, from_json=None):
"""A convergence stats and results object. This class stores the
Expand Down
Loading

0 comments on commit 777b813

Please sign in to comment.