From ca302d65f6efa3f116dc5660099ac7383ea0161e Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 29 Apr 2021 15:09:37 +0200 Subject: [PATCH 1/3] add coverage for rlocus with dtime, grid and sisotool --- control/tests/rlocus_test.py | 18 +++++++++++----- control/tests/sisotool_test.py | 39 ++++++++++------------------------ 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/control/tests/rlocus_test.py b/control/tests/rlocus_test.py index f7aff9ebe..40c84d335 100644 --- a/control/tests/rlocus_test.py +++ b/control/tests/rlocus_test.py @@ -18,11 +18,19 @@ class TestRootLocus: """These are tests for the feedback function in rlocus.py.""" - @pytest.fixture(params=[(TransferFunction, ([1, 2], [1, 2, 3])), - (StateSpace, ([[1., 4.], [3., 2.]], - [[1.], [-4.]], - [[1., 0.]], [[0.]]))], - ids=["tf", "ss"]) + @pytest.fixture(params=[pytest.param((sysclass, sargs + (dt, )), + id=f"{systypename}-{dtstring}") + for sysclass, systypename, sargs in [ + (TransferFunction, 'TF', ([1, 2], + [1, 2, 3])), + (StateSpace, 'SS', ([[1., 4.], [3., 2.]], + [[1.], [-4.]], + [[1., 0.]], + [[0.]])), + ] + for dt, dtstring in [(0, 'ctime'), + (True, 'dtime')] + ]) def sys(self, request): """Return some simple LTI system for testing""" # avoid construction during collection time: prevent unfiltered diff --git a/control/tests/sisotool_test.py b/control/tests/sisotool_test.py index 14e9692c1..ab5d546dd 100644 --- a/control/tests/sisotool_test.py +++ b/control/tests/sisotool_test.py @@ -17,14 +17,10 @@ class TestSisotool: """These are tests for the sisotool in sisotool.py.""" @pytest.fixture - def sys(self): + def tsys(self, request): """Return a generic SISO transfer function""" - return TransferFunction([1000], [1, 25, 100, 0]) - - @pytest.fixture - def sysdt(self): - """Return a generic SISO transfer function""" - return TransferFunction([1000], [1, 25, 100, 0], True) + dt = getattr(request, 'param', 0) + return TransferFunction([1000], [1, 25, 100, 0], dt) @pytest.fixture def sys222(self): @@ -50,8 +46,8 @@ def sys221(self): D221 = [[1., -1.]] return StateSpace(A222, B222, C221, D221) - def test_sisotool(self, sys): - sisotool(sys, Hz=False) + def test_sisotool(self, tsys): + sisotool(tsys, Hz=False) fig = plt.gcf() ax_mag, ax_rlocus, ax_phase, ax_step = fig.axes[:4] @@ -89,7 +85,7 @@ def test_sisotool(self, sys): event = type('test', (object,), {'xdata': 2.31206868287, 'ydata': 15.5983051046, 'inaxes': ax_rlocus.axes})() - _RLClickDispatcher(event=event, sys=sys, fig=fig, + _RLClickDispatcher(event=event, sys=tsys, fig=fig, ax_rlocus=ax_rlocus, sisotool=True, plotstr='-', bode_plot_params=bode_plot_params, tvect=None) @@ -118,10 +114,12 @@ def test_sisotool(self, sys): assert_array_almost_equal( ax_step.lines[0].get_data()[1][:10], step_response_moved, 4) - def test_sisotool_tvect(self, sys): + @pytest.mark.parametrize('tsys', [0, True], + indirect=True, ids=['ctime', 'dtime']) + def test_sisotool_tvect(self, tsys): # test supply tvect tvect = np.linspace(0, 1, 10) - sisotool(sys, tvect=tvect) + sisotool(tsys, tvect=tvect) fig = plt.gcf() ax_rlocus, ax_step = fig.axes[1], fig.axes[3] @@ -129,26 +127,11 @@ def test_sisotool_tvect(self, sys): event = type('test', (object,), {'xdata': 2.31206868287, 'ydata': 15.5983051046, 'inaxes': ax_rlocus.axes})() - _RLClickDispatcher(event=event, sys=sys, fig=fig, + _RLClickDispatcher(event=event, sys=tsys, fig=fig, ax_rlocus=ax_rlocus, sisotool=True, plotstr='-', bode_plot_params=dict(), tvect=tvect) assert_array_almost_equal(tvect, ax_step.lines[0].get_data()[0]) - def test_sisotool_tvect_dt(self, sysdt): - # test supply tvect - tvect = np.linspace(0, 1, 10) - sisotool(sysdt, tvect=tvect) - fig = plt.gcf() - ax_rlocus, ax_step = fig.axes[1], fig.axes[3] - - # Move the rootlocus to another point and confirm same tvect - event = type('test', (object,), {'xdata': 2.31206868287, - 'ydata': 15.5983051046, - 'inaxes': ax_rlocus.axes})() - _RLClickDispatcher(event=event, sys=sysdt, fig=fig, - ax_rlocus=ax_rlocus, sisotool=True, plotstr='-', - bode_plot_params=dict(), tvect=tvect) - assert_array_almost_equal(tvect, ax_step.lines[0].get_data()[0]) def test_sisotool_mimo(self, sys222, sys221): # a 2x2 should not raise an error: From 39dee6f38fe391027783aacc453489eb2d012000 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 29 Apr 2021 15:11:01 +0200 Subject: [PATCH 2/3] simplify grid and sisotool logic --- control/rlocus.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/control/rlocus.py b/control/rlocus.py index 4f83c019b..91a3c9010 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -232,16 +232,11 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, ax.set_ylim(ylim) # Draw the grid - if grid and sisotool: + if grid: if isdtime(sys, strict=True): zgrid(ax=ax) else: - _sgrid_func(fig=fig) - elif grid: - if isdtime(sys, strict=True): - zgrid(ax=ax) - else: - _sgrid_func() + _sgrid_func(fig=fig if sisotool else None) else: ax.axhline(0., linestyle=':', color='k', linewidth=.75, zorder=-20) ax.axvline(0., linestyle=':', color='k', linewidth=.75, zorder=-20) From fae38af3578cde05e660b59cf04def5f63e6d567 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 29 Apr 2021 19:00:47 +0200 Subject: [PATCH 3/3] more coverage --- control/rlocus.py | 2 +- control/setup.py | 5 ----- control/tests/rlocus_test.py | 33 ++++++++++++++++++++++++++++++--- setup.cfg | 1 - 4 files changed, 31 insertions(+), 10 deletions(-) delete mode 100644 control/setup.py diff --git a/control/rlocus.py b/control/rlocus.py index 91a3c9010..ee30fe489 100644 --- a/control/rlocus.py +++ b/control/rlocus.py @@ -137,7 +137,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, print_gain = config._get_param( 'rlocus', 'print_gain', print_gain, _rlocus_defaults) - sys_loop = sys if sys.issiso() else sys[0,0] + sys_loop = sys if sys.issiso() else sys[0, 0] # Convert numerator and denominator to polynomials if they aren't (nump, denp) = _systopoly1d(sys_loop) diff --git a/control/setup.py b/control/setup.py deleted file mode 100644 index 3ed3e3a7e..000000000 --- a/control/setup.py +++ /dev/null @@ -1,5 +0,0 @@ -def configuration(parent_package='', top_path=None): - from numpy.distutils.misc_util import Configuration - config = Configuration('control', parent_package, top_path) - config.add_subpackage('tests') - return config diff --git a/control/tests/rlocus_test.py b/control/tests/rlocus_test.py index 40c84d335..ef9bd7ecb 100644 --- a/control/tests/rlocus_test.py +++ b/control/tests/rlocus_test.py @@ -15,6 +15,7 @@ from control.bdalg import feedback +@pytest.mark.usefixtures("mplcleanup") class TestRootLocus: """These are tests for the feedback function in rlocus.py.""" @@ -32,7 +33,7 @@ class TestRootLocus: (True, 'dtime')] ]) def sys(self, request): - """Return some simple LTI system for testing""" + """Return some simple LTI systems for testing""" # avoid construction during collection time: prevent unfiltered # deprecation warning sysfn, args = request.param @@ -45,7 +46,7 @@ def check_cl_poles(self, sys, pole_list, k_list): np.testing.assert_array_almost_equal(poles, poles_expected) def testRootLocus(self, sys): - """Basic root locus plot""" + """Basic root locus (no plot)""" klist = [-1, 0, 1] roots, k_out = root_locus(sys, klist, plot=False) @@ -57,6 +58,33 @@ def test_without_gains(self, sys): roots, kvect = root_locus(sys, plot=False) self.check_cl_poles(sys, roots, kvect) + @pytest.mark.parametrize('grid', [None, True, False]) + def test_root_locus_plot_grid(self, sys, grid): + rlist, klist = root_locus(sys, grid=grid) + ax = plt.gca() + n_gridlines = sum([int(line.get_linestyle() in [':', 'dotted', + '--', 'dashed']) + for line in ax.lines]) + if grid is False: + assert n_gridlines == 2 + else: + assert n_gridlines > 2 + # TODO check validity of grid + + def test_root_locus_warnings(self): + sys = TransferFunction([1000], [1, 25, 100, 0]) + with pytest.warns(FutureWarning, match="Plot.*deprecated"): + rlist, klist = root_locus(sys, Plot=True) + with pytest.warns(FutureWarning, match="PrintGain.*deprecated"): + rlist, klist = root_locus(sys, PrintGain=True) + + 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"): + root_locus(TransferFunction([-1, 2], [1, 2])) + + # TODO: cover and validate negative false_gain branch in _default_gains() + def test_root_locus_zoom(self): """Check the zooming functionality of the Root locus plot""" system = TransferFunction([1000], [1, 25, 100, 0]) @@ -104,4 +132,3 @@ def test_rlocus_default_wn(self): [-1e-2, 1-1e7j, 1+1e7j], [0, -1e7j, 1e7j], 1)) ct.root_locus(sys) - diff --git a/setup.cfg b/setup.cfg index c72ef19a8..5b1ce28a7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,4 +5,3 @@ universal=1 addopts = -ra filterwarnings = error:.*matrix subclass:PendingDeprecationWarning - pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy