Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add backward compatibility for matlab.{rlocus,pzmap} + suppress warnings
  • Loading branch information
murrayrm committed Dec 28, 2023
commit 08e55b133dbe9a15f23108c80caaa4a5a50a0932
103 changes: 101 additions & 2 deletions control/matlab/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
from ..lti import LTI
from ..exception import ControlArgument

__all__ = ['bode', 'nyquist', 'ngrid', 'dcgain', 'connect']
__all__ = ['bode', 'nyquist', 'ngrid', 'rlocus', 'pzmap', 'dcgain', 'connect']

def bode(*args, **kwargs):
"""bode(syslist[, omega, dB, Hz, deg, ...])

Bode plot of the frequency response.

Plots a bode gain and phase diagram
Plots a bode gain and phase diagram.

Parameters
----------
Expand Down Expand Up @@ -195,6 +195,104 @@ def _parse_freqplot_args(*args):
return syslist, omega, plotstyle, other


def rlocus(*args, **kwargs):
"""rlocus(sys[, klist, xlim, ylim, ...])

Root locus diagram.

Calculate the root locus by finding the roots of 1 + k * G(s) where G
is a linear system with transfer function num(s)/den(s) and each k is
an element of kvect.

Parameters
----------
sys : LTI object
Linear input/output systems (SISO only, for now).
kvect : array_like, optional
Gains to use in computing plot of closed-loop poles.
xlim : tuple or list, optional
Set limits of x axis, normally with tuple
(see :doc:`matplotlib:api/axes_api`).
ylim : tuple or list, optional
Set limits of y axis, normally with tuple
(see :doc:`matplotlib:api/axes_api`).

Returns
-------
roots : ndarray
Closed-loop root locations, arranged in which each row corresponds
to a gain in gains.
gains : ndarray
Gains used. Same as kvect keyword argument if provided.

Notes
-----
This function is a wrapper for :func:`~control.root_locus_plot`,
with legacy return arguments.

"""
from ..rlocus import root_locus_plot

# Use the plot keyword to get legacy behavior
kwargs = dict(kwargs) # make a copy since we modify this
if 'plot' not in kwargs:
kwargs['plot'] = True

# Turn off deprecation warning
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', message='.* return values of .* is deprecated',
category=DeprecationWarning)
retval = root_locus_plot(*args, **kwargs)

return retval


def pzmap(*args, **kwargs):
"""pzmap(sys[, grid, plot])

Plot a pole/zero map for a linear system.

Parameters
----------
sys: LTI (StateSpace or TransferFunction)
Linear system for which poles and zeros are computed.
plot: bool, optional
If ``True`` a graph is generated with Matplotlib,
otherwise the poles and zeros are only computed and returned.
grid: boolean (default = False)
If True plot omega-damping grid.

Returns
-------
poles: array
The system's poles.
zeros: array
The system's zeros.

Notes
-----
This function is a wrapper for :func:`~control.pole_zero_plot`,
with legacy return arguments.

"""
from ..pzmap import pole_zero_plot

# Use the plot keyword to get legacy behavior
kwargs = dict(kwargs) # make a copy since we modify this
if 'plot' not in kwargs:
kwargs['plot'] = True

# Turn off deprecation warning
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', message='.* return values of .* is deprecated',
category=DeprecationWarning)
retval = pole_zero_plot(*args, **kwargs)

return retval


from ..nichols import nichols_grid
def ngrid():
return nichols_grid()
Expand Down Expand Up @@ -254,6 +352,7 @@ def dcgain(*args):

from ..bdalg import connect as ct_connect
def connect(*args):

"""Index-based interconnection of an LTI system.

The system `sys` is a system typically constructed with `append`, with
Expand Down
4 changes: 2 additions & 2 deletions control/rlocus.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def root_locus_plot(
-------
roots : ndarray
Closed-loop root locations, arranged in which each row corresponds
to a gain in gains
to a gain in gains.
gains : ndarray
Gains used. Same as kvect keyword argument if provided.

Expand Down Expand Up @@ -140,7 +140,7 @@ def root_locus_plot(
#
if plot is not None:
warnings.warn(
"`root_locus` return values of loci, gains is deprecated; "
"`root_locus` return values of roots, gains is deprecated; "
"use root_locus_map()", DeprecationWarning)

if plot is False:
Expand Down
3 changes: 2 additions & 1 deletion control/tests/matlab_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ def testBode(self, siso, mplcleanup):
@pytest.mark.parametrize("subsys", ["ss1", "tf1", "tf2"])
def testRlocus(self, siso, subsys, mplcleanup):
"""Call rlocus()"""
rlocus(getattr(siso, subsys))
rlist, klist = rlocus(getattr(siso, subsys))
np.testing.assert_equal(len(rlist), len(klist))

def testRlocus_list(self, siso, mplcleanup):
"""Test rlocus() with list"""
Expand Down
1 change: 1 addition & 0 deletions control/tests/pzmap_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from control import TransferFunction, config, pzmap


@pytest.mark.filterwarnings("ignore:.*return values.*:DeprecationWarning")
@pytest.mark.parametrize("kwargs",
[pytest.param(dict(), id="default"),
pytest.param(dict(plot=False), id="plot=False"),
Expand Down
4 changes: 4 additions & 0 deletions control/tests/rlocus_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def check_cl_poles(self, sys, pole_list, k_list):
poles = np.sort(poles)
np.testing.assert_array_almost_equal(poles, poles_expected)

@pytest.mark.filterwarnings("ignore:.*return values.*:DeprecationWarning")
def testRootLocus(self, sys):
"""Basic root locus (no plot)"""
klist = [-1, 0, 1]
Expand All @@ -60,6 +61,7 @@ def testRootLocus(self, sys):
np.testing.assert_allclose(klist, k_out)
self.check_cl_poles(sys, roots, klist)

@pytest.mark.filterwarnings("ignore:.*return values.*:DeprecationWarning")
def test_without_gains(self, sys):
roots, kvect = root_locus(sys, plot=False)
self.check_cl_poles(sys, roots, kvect)
Expand All @@ -79,6 +81,7 @@ def test_root_locus_plot_grid(self, sys, grid):
assert n_gridlines > 2
# TODO check validity of grid

@pytest.mark.filterwarnings("ignore:.*return values.*:DeprecationWarning")
def test_root_locus_neg_false_gain_nonproper(self):
""" Non proper TranferFunction with negative gain: Not implemented"""
with pytest.raises(ValueError, match="with equal order"):
Expand Down Expand Up @@ -116,6 +119,7 @@ def test_root_locus_zoom(self):
assert_array_almost_equal(zoom_x, zoom_x_valid)
assert_array_almost_equal(zoom_y, zoom_y_valid)

@pytest.mark.filterwarnings("ignore:.*return values.*:DeprecationWarning")
@pytest.mark.timeout(2)
def test_rlocus_default_wn(self):
"""Check that default wn calculation works properly"""
Expand Down