8000 updated unit tests · controlPh/python-control@c3cc504 · GitHub
[go: up one dir, main page]

Skip to content

Commit c3cc504

Browse files
committed
updated unit tests
1 parent f865c8c commit c3cc504

File tree

3 files changed

+99
-18
lines changed

3 files changed

+99
-18
lines changed

control/freqplot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,7 @@ def plot(self, *args, **kwargs):
11851185

11861186
class NyquistResponseList(list):
11871187
def plot(self, *args, **kwargs):
1188-
nyquist_plot(self, *args, **kwargs)
1188+
return nyquist_plot(self, *args, **kwargs)
11891189

11901190

11911191
def nyquist_response(

control/tests/freqplot_test.py

Lines changed: 92 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
@pytest.mark.parametrize(
3232
"sys", [
3333
ct.tf([1], [1, 2, 1], name='System 1'), # SISO
34-
manual_response, # simple MIMO
34+
manual_response, # simple MIMO
3535
])
3636
# @pytest.mark.parametrize("pltmag", [True, False])
3737
# @pytest.mark.parametrize("pltphs", [True, False])
@@ -40,29 +40,30 @@
4040
# @pytest.mark.parametrize("shrfrq", ['col', 'all', False, None])
4141
# @pytest.mark.parametrize("secsys", [False, True])
4242
@pytest.mark.parametrize( # combinatorial-style test (faster)
43-
"pltmag, pltphs, shrmag, shrphs, shrfrq, secsys",
44-
[(True, True, None, None, None, False),
45-
(True, False, None, None, None, False),
46-
(False, True, None, None, None, False),
47-
(True, True, None, None, None, True),
48-
(True, True, 'row', 'row', 'col', False),
49-
(True, True, 'row', 'row', 'all', True),
50-
(True, True, 'all', 'row', None, False),
51-
(True, True, 'row', 'all', None, True),
52-
(True, True, 'none', 'none', None, True),
53-
(True, False, 'all', 'row', None, False),
54-
(True, True, True, 'row', None, True),
55-
(True, True, None, 'row', True, False),
56-
(True, True, 'row', None, None, True),
43+
"pltmag, pltphs, shrmag, shrphs, shrfrq, ovlout, ovlinp, secsys",
44+
[(True, True, None, None, None, False, False, False),
45+
(True, False, None, None, None, True, False, False),
46+
(False, True, None, None, None, False, True, False),
47+
(True, True, None, None, None, False, False, True),
48+
(True, True, 'row', 'row', 'col', False, False, False),
49+
(True, True, 'row', 'row', 'all', False, False, True),
50+
(True, True, 'all', 'row', None, False, False, False),
51+
(True, True, 'row', 'all', None, False, False, True),
52+
(True, True, 'none', 'none', None, False, False, True),
53+
(True, False, 'all', 'row', None, False, False, False),
54+
(True, True, True, 'row', None, False, False, True),
55+
(True, True, None, 'row', True, False, False, False),
56+
(True, True, 'row', None, None, False, False, True),
5757
])
5858
def test_response_plots(
59-
sys, pltmag, pltphs, shrmag, shrphs, shrfrq, secsys, clear=True):
59+
sys, pltmag, pltphs, shrmag, shrphs, shrfrq, ovlout, ovlinp,
60+
secsys, clear=True):
6061

6162
# Save up the keyword arguments
6263
kwargs = dict(
6364
plot_magnitude=pltmag, plot_phase=pltphs,
6465
share_magnitude=shrmag, share_phase=shrphs, share_frequency=shrfrq,
65-
# overlay_outputs=ovlout, overlay_inputs=ovlinp
66+
overlay_outputs=ovlout, overlay_inputs=ovlinp
6667
)
6768

6869
# Create the response
@@ -79,6 +80,16 @@ def test_response_plots(
7980
plt.figure()
8081
out = response.plot(**kwargs)
8182

83+
# Check the shape
84+
if ovlout and ovlinp:
85+
assert out.shape == (pltmag + pltphs, 1)
86+
elif ovlout:
87+
assert out.shape == (pltmag + pltphs, sys.ninputs)
88+
elif ovlinp:
89+
assert out.shape == (sys.noutputs * (pltmag + pltphs), 1)
90+
else:
91+
assert out.shape == (sys.noutputs * (pltmag + pltphs), sys.ninputs)
92+
8293
# Make sure all of the outputs are of the right type
8394
nlines_plotted = 0
8495
for ax_lines in np.nditer(out, flags=["refs_ok"]):
@@ -198,19 +209,83 @@ def test_first_arg_listable(response_cmd, return_type):
198209
result = response_cmd(sys)
199210
assert isinstance(result, return_type)
200211

212+
# Save the results from a single plot
213+
lines_single = result.plot()
214+
201215
# If we pass a list of systems, we should get back a list
202216
result = response_cmd([sys, sys, sys])
203217
assert isinstance(result, list)
204218
assert len(result) == 3
205219
assert all([isinstance(item, return_type) for item in result])
206220

221+
# Make sure that plot works
222+
lines_list = result.plot()
223+
if response_cmd == ct.frequency_response:
224+
assert lines_list.shape == lines_single.shape
225+
assert len(lines_list.reshape(-1)[0]) == \
226+
3 * len(lines_single.reshape(-1)[0])
227+
else:
228+
assert lines_list.shape[0] == 3 * lines_single.shape[0]
229+
207230
# If we pass a singleton list, we should get back a list
208231
result = response_cmd([sys])
209232
assert isinstance(result, list)
210233
assert len(result) == 1
211234
assert isinstance(result[0], return_type)
212235

213236

237+
def test_bode_share_options():
238+
# Default sharing should share along rows and cols for mag and phase
239+
lines = ct.bode_plot(manual_response)
240+
axs = ct.get_plot_axes(lines)
241+
for i in range(axs.shape[0]):
242+
for j in range(axs.shape[1]):
243+
# Share y limits along rows
244+
assert axs[i, j].get_ylim() == axs[i, 0].get_ylim()
245+
246+
# Share x limits along columns
247+
assert axs[i, j].get_xlim() == axs[-1, j].get_xlim()
248+
249+
# Sharing along y axis for mag but not phase
250+
plt.figure()
251+
lines = ct.bode_plot(manual_response, share_phase='none')
252+
axs = ct.get_plot_axes(lines)
253+
for i in range(int(axs.shape[0] / 2)):
254+
for j in range(axs.shape[1]):
255+
if i != 0:
256+
# Different rows are different
257+
assert axs[i*2 + 1, 0].get_ylim() != axs[1, 0].get_ylim()
258+
elif j != 0:
259+
# Different columns are different
260+
assert axs[i*2 + 1, j].get_ylim() != axs[i*2 + 1, 0].get_ylim()
261+
262+
# Turn off sharing for magnitude and phase
263+
plt.figure()
264+
lines = ct.bode_plot(manual_response, sharey='none')
265+
axs = ct.get_plot_axes(lines)
266+
for i in range(int(axs.shape[0] / 2)):
267+
for j in range(axs.shape[1]):
268+
if i != 0:
269+
# Different rows are different
270+
assert axs[i*2, 0].get_ylim() != axs[0, 0].get_ylim()
271+
assert axs[i*2 + 1, 0].get_ylim() != axs[1, 0].get_ylim()
272+
elif j != 0:
273+
# Different columns are different
274+
assert axs[i*2, j].get_ylim() != axs[i*2, 0].get_ylim()
275+
assert axs[i*2 + 1, j].get_ylim() != axs[i*2 + 1, 0].get_ylim()
276+
277+
# Turn off sharing in x axes
278+
plt.figure()
279+
lines = ct.bode_plot(manual_response, sharex='none')
280+
# TODO: figure out what to check
281+
282+
283+
def test_bode_errors():
284+
# Turning off both magnitude and phase
285+
with pytest.raises(ValueError, match="no data to plot"):
286+
ct.bode_plot(manual_response, plot_magnitude=False, plot_phase=False)
287+
288+
214289
if __name__ == "__main__":
215290
#
216291
# Interactive mode: generate plots for manual viewing

control/tests/matlab_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,12 @@ def testBode(self, siso, mplcleanup):
415415
# Not yet implemented
416416
# bode(siso.ss1, '-', siso.tf1, 'b--', siso.tf2, 'k.')
417417

418+
# Pass frequency range as a tuple
419+
mag, phase, freq = bode(siso.ss1, (0.2e-2, 0.2e2))
420+
assert np.isclose(min(freq), 0.2e-2)
421+
assert np.isclose(max(freq), 0.2e2)
422+
assert len(freq) > 2
423+
418424
@pytest.mark.parametrize("subsys", ["ss1", "tf1", "tf2"])
419425
def testRlocus(self, siso, subsys, mplcleanup):
420426
"""Call rlocus()"""

0 commit comments

Comments
 (0)
0