Skip to content

Commit 608093e

Browse files
authored
Merge pull request #617 from bnavigator/cover-rlocus
Cover more of root_locus: dtime and sisotool
2 parents 9f27293 + fae38af commit 608093e

File tree

5 files changed

+57
-50
lines changed

5 files changed

+57
-50
lines changed

control/rlocus.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
137137
print_gain = config._get_param(
138138
'rlocus', 'print_gain', print_gain, _rlocus_defaults)
139139

140-
sys_loop = sys if sys.issiso() else sys[0,0]
140+
sys_loop = sys if sys.issiso() else sys[0, 0]
141141

142142
# Convert numerator and denominator to polynomials if they aren't
143143
(nump, denp) = _systopoly1d(sys_loop)
@@ -232,16 +232,11 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
232232
ax.set_ylim(ylim)
233233

234234
# Draw the grid
235-
if grid and sisotool:
236-
if isdtime(sys, strict=True):
237-
zgrid(ax=ax)
238-
else:
239-
_sgrid_func(fig=fig)
240-
elif grid:
235+
if grid:
241236
if isdtime(sys, strict=True):
242237
zgrid(ax=ax)
243238
else:
244-
_sgrid_func()
239+
_sgrid_func(fig=fig if sisotool else None)
245240
else:
246241
ax.axhline(0., linestyle=':', color='k', linewidth=.75, zorder=-20)
247242
ax.axvline(0., linestyle=':', color='k', linewidth=.75, zorder=-20)

control/setup.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

control/tests/rlocus_test.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,25 @@
1515
from control.bdalg import feedback
1616

1717

18+
@pytest.mark.usefixtures("mplcleanup")
1819
class TestRootLocus:
1920
"""These are tests for the feedback function in rlocus.py."""
2021

21-
@pytest.fixture(params=[(TransferFunction, ([1, 2], [1, 2, 3])),
22-
(StateSpace, ([[1., 4.], [3., 2.]],
23-
[[1.], [-4.]],
24-
[[1., 0.]], [[0.]]))],
25-
ids=["tf", "ss"])
22+
@pytest.fixture(params=[pytest.param((sysclass, sargs + (dt, )),
23+
id=f"{systypename}-{dtstring}")
24+
for sysclass, systypename, sargs in [
25+
(TransferFunction, 'TF', ([1, 2],
26+
[1, 2, 3])),
27+
(StateSpace, 'SS', ([[1., 4.], [3., 2.]],
28+
[[1.], [-4.]],
29+
[[1., 0.]],
30+
[[0.]])),
31+
]
32+
for dt, dtstring in [(0, 'ctime'),
33+
(True, 'dtime')]
34+
])
2635
def sys(self, request):
27-
"""Return some simple LTI system for testing"""
36+
"""Return some simple LTI systems for testing"""
2837
# avoid construction during collection time: prevent unfiltered
2938
# deprecation warning
3039
sysfn, args = request.param
@@ -37,7 +46,7 @@ def check_cl_poles(self, sys, pole_list, k_list):
3746
np.testing.assert_array_almost_equal(poles, poles_expected)
3847

3948
def testRootLocus(self, sys):
40-
"""Basic root locus plot"""
49+
"""Basic root locus (no plot)"""
4150
klist = [-1, 0, 1]
4251

4352
roots, k_out = root_locus(sys, klist, plot=False)
@@ -49,6 +58,33 @@ def test_without_gains(self, sys):
4958
roots, kvect = root_locus(sys, plot=False)
5059
self.check_cl_poles(sys, roots, kvect)
5160

61+
@pytest.mark.parametrize('grid', [None, True, False])
62+
def test_root_locus_plot_grid(self, sys, grid):
63+
rlist, klist = root_locus(sys, grid=grid)
64+
ax = plt.gca()
65+
n_gridlines = sum([int(line.get_linestyle() in [':', 'dotted',
66+
'--', 'dashed'])
67+
for line in ax.lines])
68+
if grid is False:
69+
assert n_gridlines == 2
70+
else:
71+
assert n_gridlines > 2
72+
# TODO check validity of grid
73+
74+
def test_root_locus_warnings(self):
75+
sys = TransferFunction([1000], [1, 25, 100, 0])
76+
with pytest.warns(FutureWarning, match="Plot.*deprecated"):
77+
rlist, klist = root_locus(sys, Plot=True)
78+
with pytest.warns(FutureWarning, match="PrintGain.*deprecated"):
79+
rlist, klist = root_locus(sys, PrintGain=True)
80+
81+
def test_root_locus_neg_false_gain_nonproper(self):
82+
""" Non proper TranferFunction with negative gain: Not implemented"""
83+
with pytest.raises(ValueError, match="with equal order"):
84+
root_locus(TransferFunction([-1, 2], [1, 2]))
85+
86+
# TODO: cover and validate negative false_gain branch in _default_gains()
87+
5288
def test_root_locus_zoom(self):
5389
"""Check the zooming functionality of the Root locus plot"""
5490
system = TransferFunction([1000], [1, 25, 100, 0])
@@ -96,4 +132,3 @@ def test_rlocus_default_wn(self):
96132
[-1e-2, 1-1e7j, 1+1e7j], [0, -1e7j, 1e7j], 1))
97133

98134
ct.root_locus(sys)
99-

control/tests/sisotool_test.py

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,10 @@ class TestSisotool:
1717
"""These are tests for the sisotool in sisotool.py."""
1818

1919
@pytest.fixture
20-
def sys(self):
20+
def tsys(self, request):
2121
"""Return a generic SISO transfer function"""
22-
return TransferFunction([1000], [1, 25, 100, 0])
23-
24-
@pytest.fixture
25-
def sysdt(self):
26-
"""Return a generic SISO transfer function"""
27-
return TransferFunction([1000], [1, 25, 100, 0], True)
22+
dt = getattr(request, 'param', 0)
23+
return TransferFunction([1000], [1, 25, 100, 0], dt)
2824

2925
@pytest.fixture
3026
def sys222(self):
@@ -50,8 +46,8 @@ def sys221(self):
5046
D221 = [[1., -1.]]
5147
return StateSpace(A222, B222, C221, D221)
5248

53-
def test_sisotool(self, sys):
54-
sisotool(sys, Hz=False)
49+
def test_sisotool(self, tsys):
50+
sisotool(tsys, Hz=False)
5551
fig = plt.gcf()
5652
ax_mag, ax_rlocus, ax_phase, ax_step = fig.axes[:4]
5753

@@ -89,7 +85,7 @@ def test_sisotool(self, sys):
8985
event = type('test', (object,), {'xdata': 2.31206868287,
9086
'ydata': 15.5983051046,
9187
'inaxes': ax_rlocus.axes})()
92-
_RLClickDispatcher(event=event, sys=sys, fig=fig,
88+
_RLClickDispatcher(event=event, sys=tsys, fig=fig,
9389
ax_rlocus=ax_rlocus, sisotool=True, plotstr='-',
9490
bode_plot_params=bode_plot_params, tvect=None)
9591

@@ -118,37 +114,24 @@ def test_sisotool(self, sys):
118114
assert_array_almost_equal(
119115
ax_step.lines[0].get_data()[1][:10], step_response_moved, 4)
120116

121-
def test_sisotool_tvect(self, sys):
117+
@pytest.mark.parametrize('tsys', [0, True],
118+
indirect=True, ids=['ctime', 'dtime'])
119+
def test_sisotool_tvect(self, tsys):
122120
# test supply tvect
123121
tvect = np.linspace(0, 1, 10)
124-
sisotool(sys, tvect=tvect)
122+
sisotool(tsys, tvect=tvect)
125123
fig = plt.gcf()
126124
ax_rlocus, ax_step = fig.axes[1], fig.axes[3]
127125

128126
# Move the rootlocus to another point and confirm same tvect
129127
event = type('test', (object,), {'xdata': 2.31206868287,
130128
'ydata': 15.5983051046,
131129
'inaxes': ax_rlocus.axes})()
132-
_RLClickDispatcher(event=event, sys=sys, fig=fig,
130+
_RLClickDispatcher(event=event, sys=tsys, fig=fig,
133131
ax_rlocus=ax_rlocus, sisotool=True, plotstr='-',
134132
bode_plot_params=dict(), tvect=tvect)
135133
assert_array_almost_equal(tvect, ax_step.lines[0].get_data()[0])
136134

137-
def test_sisotool_tvect_dt(self, sysdt):
138-
# test supply tvect
139-
tvect = np.linspace(0, 1, 10)
140-
sisotool(sysdt, tvect=tvect)
141-
fig = plt.gcf()
142-
ax_rlocus, ax_step = fig.axes[1], fig.axes[3]
143-
144-
# Move the rootlocus to another point and confirm same tvect
145-
event = type('test', (object,), {'xdata': 2.31206868287,
146-
'ydata': 15.5983051046,
147-
'inaxes': ax_rlocus.axes})()
148-
_RLClickDispatcher(event=event, sys=sysdt, fig=fig,
149-
ax_rlocus=ax_rlocus, sisotool=True, plotstr='-',
150-
bode_plot_params=dict(), tvect=tvect)
151-
assert_array_almost_equal(tvect, ax_step.lines[0].get_data()[0])
152135

153136
def test_sisotool_mimo(self, sys222, sys221):
154137
# a 2x2 should not raise an error:

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ universal=1
55
addopts = -ra
66
filterwarnings =
77
error:.*matrix subclass:PendingDeprecationWarning
8-

0 commit comments

Comments
 (0)