Repository navigation
Update find_eqpt to find_operating_point, adding root_method + better docs #1054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
7479353
fa162ec
223acc6
5eaee66
aed5706
1a2770c
47e1c09
e6fdc17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1694,14 +1694,14 @@ class OperatingPoint(object): | |||||
| """ | ||||||
| def __init__( | ||||||
| self, states, inputs=None, outputs=None, result=None, | ||||||
| return_y=False, return_result=False): | ||||||
| return_outputs=False, return_result=False): | ||||||
| self.states = states | ||||||
| self.inputs = inputs | ||||||
|
|
||||||
| if outputs is None and return_y and not return_result: | ||||||
| raise SystemError("return_y specified by no y0 value") | ||||||
| if outputs is None and return_outputs and not return_result: | ||||||
| raise SystemError("return_outputs specified by no y0 value") | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here and below are both non-standard uses of SystemError, which normally is for "when the [Python] interpreter finds an internal error". I recommend ValueError instead, which is also used elsewhere in this module for inconsistent arguments. |
||||||
| self.outputs = outputs | ||||||
| self.return_y = return_y | ||||||
| self.return_outputs = return_outputs | ||||||
|
|
||||||
| if result is None and return_result: | ||||||
| raise SystemError("return_result specified by no result value") | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
@@ -1710,9 +1710,9 @@ def __init__( | |||||
|
|
||||||
| # Implement iter to allow assigning to a tuple | ||||||
| def __iter__(self): | ||||||
| if self.return_y and self.return_result: | ||||||
| if self.return_outputs and self.return_result: | ||||||
| return iter((self.states, self.inputs, self.outputs, self.result)) | ||||||
| elif self.return_y: | ||||||
| elif self.return_outputs: | ||||||
| return iter((self.states, self.inputs, self.outputs)) | ||||||
| elif self.return_result: | ||||||
| return iter((self.states, self.inputs, self.result)) | ||||||
|
|
@@ -1729,9 +1729,9 @@ def __len__(self): | |||||
|
|
||||||
|
|
||||||
| def find_operating_point( | ||||||
| sys, x0, u0=None, y0=None, t=0, params=None, | ||||||
| iu=None, iy=None, ix=None, idx=None, dx0=None, root_method=None, | ||||||
| root_kwargs=None, return_y=False, return_result=False): | ||||||
| sys, x0, u0=None, y0=None, t=0, params=None, iu=None, iy=None, | ||||||
| ix=None, idx=None, dx0=None, root_method=None, root_kwargs=None, | ||||||
| return_outputs=None, return_result=None, **kwargs): | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be |
||||||
| """Find an operating point for an input/output system. | ||||||
|
|
||||||
| An operating point for a nonlinear system is a state and input around | ||||||
|
|
@@ -1804,8 +1804,8 @@ def find_operating_point( | |||||
| is passed to the :func:`scipy.optimize.root` function. | ||||||
| root_kwargs : dict, optional | ||||||
| Additional keyword arguments to pass :func:`scipy.optimize.root`. | ||||||
| return_y : bool, optional | ||||||
| If True, return the value of output at the operating point. | ||||||
| return_outputs : bool, optional | ||||||
| If True, return the value of outputs at the operating point. | ||||||
| return_result : bool, optional | ||||||
| If True, return the `result` option from the | ||||||
| :func:`scipy.optimize.root` function used to compute the | ||||||
|
|
@@ -1820,8 +1820,8 @@ def find_operating_point( | |||||
| :class:`OperatingPoint` for a description of other attributes. | ||||||
|
|
||||||
| If accessed as a tuple, returns `states`, `inputs`, and optionally | ||||||
| `outputs` and `result` based on the `return_y` and `return_result` | ||||||
| parameters. | ||||||
| `outputs` and `result` based on the `return_outputs` and | ||||||
| `return_result` parameters. | ||||||
|
|
||||||
| Notes | ||||||
| ----- | ||||||
|
|
@@ -1844,6 +1844,12 @@ def find_operating_point( | |||||
| """ | ||||||
| from scipy.optimize import root | ||||||
|
|
||||||
| # Process keyword arguments | ||||||
| return_outputs = config._process_legacy_keyword( | ||||||
| kwargs, 'return_y', 'return_outputs', return_outputs) | ||||||
| if kwargs: | ||||||
| raise TypeError("unrecognized keyword(s): " + str(kwargs)) | ||||||
|
|
||||||
| # Process arguments for the root function | ||||||
| root_kwargs = dict() if root_kwargs is None else root_kwargs | ||||||
| if root_method: | ||||||
|
|
@@ -2019,14 +2025,14 @@ def rootfun(z): | |||||
| # Return the result based on what the user wants and what we found | ||||||
| if return_result or result.success: | ||||||
| return OperatingPoint( | ||||||
| z[0], z[1], z[2], result, return_y, return_result) | ||||||
| z[0], z[1], z[2], result, return_outputs, return_result) | ||||||
| else: | ||||||
| # Something went wrong, don't return anything | ||||||
| return OperatingPoint( | ||||||
| None, None, None, result, return_y, return_result) | ||||||
| None, None, None, result, return_outputs, return_result) | ||||||
|
|
||||||
| # TODO: remove code when ready | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The above if/else block returns always, so the code from |
||||||
| if not return_y: | ||||||
| if not return_outputs: | ||||||
| z = z[0:2] # Strip y from result if not desired | ||||||
| if return_result: | ||||||
| # Return whatever we got, along with the result dictionary | ||||||
|
|
@@ -2036,7 +2042,7 @@ def rootfun(z): | |||||
| return z | ||||||
| else: | ||||||
| # Something went wrong, don't return anything | ||||||
| return (None, None, None) if return_y else (None, None) | ||||||
| return (None, None, None) if return_outputs else (None, None) | ||||||
|
|
||||||
|
|
||||||
| # Linearize an input/output system | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,10 +10,11 @@ | |
|
|
||
| import re | ||
| import warnings | ||
| import pytest | ||
| from math import sqrt | ||
|
|
||
| import numpy as np | ||
| from math import sqrt | ||
| import pytest | ||
| import scipy | ||
|
|
||
| import control as ct | ||
|
|
||
|
|
@@ -2088,6 +2089,7 @@ def test_find_eqpt(x0, ix, u0, iu, y0, iy, dx0, idx, dt, x_expect, u_expect): | |
|
|
||
|
|
||
| # Test out new operating point version of find_eqpt | ||
| # TODO: add return_)y tests | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a test below (function |
||
| def test_find_operating_point(): | ||
| dt = 1 | ||
| sys = ct.NonlinearIOSystem( | ||
|
|
@@ -2122,6 +2124,10 @@ def test_find_operating_point(): | |
| assert op_point.inputs is not None | ||
| assert op_point.result.success is False | ||
|
|
||
| # Check to make sure unknown keywords are caught | ||
| with pytest.raises(TypeError, match="unrecognized keyword"): | ||
| ct.find_operating_point(sys, x0, u0, unknown=None) | ||
|
|
||
|
|
||
| def test_operating_point(): | ||
| dt = 1 | ||
|
|
@@ -2146,6 +2152,36 @@ def test_operating_point(): | |
| op_point = ct.find_operating_point( | ||
| sys, 0, 0, root_method='lm', root_kwargs={'tol': 1e-6}) | ||
|
|
||
| # Make sure we can get back the right arguments in a tuple | ||
| op_point = ct.find_operating_point(sys, 0, 0, return_outputs=True) | ||
| assert len(op_point) == 3 | ||
| assert isinstance(op_point[0], np.ndarray) | ||
| assert isinstance(op_point[1], np.ndarray) | ||
| assert isinstance(op_point[2], np.ndarray) | ||
|
|
||
| with pytest.warns(FutureWarning, match="return_outputs"): | ||
| op_point = ct.find_operating_point(sys, 0, 0, return_y=True) | ||
| assert len(op_point) == 3 | ||
| assert isinstance(op_point[0], np.ndarray) | ||
| assert isinstance(op_point[1], np.ndarray) | ||
| assert isinstance(op_point[2], np.ndarray) | ||
|
|
||
| # Make sure we can get back the right arguments in a tuple | ||
| op_point = ct.find_operating_point(sys, 0, 0, return_result=True) | ||
| assert len(op_point) == 3 | ||
| assert isinstance(op_point[0], np.ndarray) | ||
| assert isinstance(op_point[1], np.ndarray) | ||
| assert isinstance(op_point[2], scipy.optimize.OptimizeResult) | ||
|
|
||
| # Make sure we can get back the right arguments in a tuple | ||
| op_point = ct.find_operating_point( | ||
| sys, 0, 0, return_result=True, return_outputs=True) | ||
| assert len(op_point) == 4 | ||
| assert isinstance(op_point[0], np.ndarray) | ||
| assert isinstance(op_point[1], np.ndarray) | ||
| assert isinstance(op_point[2], np.ndarray) | ||
| assert isinstance(op_point[3], scipy.optimize.OptimizeResult) | ||
|
|
||
|
|
||
| def test_iosys_sample(): | ||
| csys = ct.rss(2, 1, 1) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inputsparameter is not optional.