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)
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