8000 customizable line properties, combine_* -> overlay_*, documentation · python-control/python-control@fc16e67 · GitHub
[go: up one dir, main page]

Skip to content

Commit fc16e67

Browse files
committed
customizable line properties, combine_* -> overlay_*, documentation
1 parent 790e386 commit fc16e67

File tree

8 files changed

+224
-127
lines changed

8 files changed

+224
-127
lines changed

control/tests/kwargs_test.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ def test_kwarg_search(module, prefix):
7575
# @parametrize messes up the check, but we know it is there
7676
pass
7777

78-
elif source and source.find('unrecognized keyword') < 0:
78+
elif source and source.find('unrecognized keyword') < 0 and \
79+
source.find('unexpected keyword') < 0:
7980
warnings.warn(
8081
f"'unrecognized keyword' not found in unit test "
8182
f"for {name}")
@@ -162,7 +163,21 @@ def test_matplotlib_kwargs(function, nsysargs, moreargs, kwargs, mplcleanup):
162163
function(*args, **kwargs, unknown=None)
163164

164165

166+
@pytest.mark.parametrize(
167+
"function", [control.time_response_plot, control.TimeResponseData.plot])
168+
def test_time_response_plot_kwargs(function):
169+
# Create a system for testing
170+
response = control.step_response(control.rss(4, 2, 2))
171+
172+
# Call the plotting function normally and make sure it works
173+
function(response)
165174

175+
# Now add an unrecognized keyword and make sure there is an error
176+
with pytest.raises(AttributeError,
177+
match="(has no property|unexpected keyword)"):
178+
function(response, unknown=None)
179+
180+
166181
#
167182
# List of all unit tests that check for unrecognized keywords
168183
#

control/tests/timeplot_test.py

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
# @pytest.mark.parametrize("transpose", [False, True])
2828
# @pytest.mark.parametrize("plot_inputs", [False, None, True, 'overlay'])
2929
# @pytest.mark.parametrize("plot_outputs", [True, False])
30-
# @pytest.mark.parametrize("combine_signals", [False, True])
31-
# @pytest.mark.parametrize("combine_traces", [False, True])
30+
# @pytest.mark.parametrize("overlay_signals", [False, True])
31+
# @pytest.mark.parametrize("overlay_traces", [False, True])
3232
# @pytest.mark.parametrize("second_system", [False, True])
3333
# @pytest.mark.parametrize("fcn", [
3434
# ct.step_response, ct.impulse_response, ct.initial_response,
@@ -90,7 +90,7 @@ def test_response_plots(
9090
# Save up the keyword arguments
9191
kwargs = dict(
9292
plot_inputs=pltinp, plot_outputs=pltout, transpose=trpose,
93-
combine_signals=cmbsig, combine_traces=cmbtrc)
93+
overlay_signals=cmbsig, overlay_traces=cmbtrc)
9494

9595
# Create the response
9696
if fcn is ct.input_output_response and \
@@ -189,7 +189,7 @@ def test_response_plots(
189189

190190

191191
def test_axes_setup():
192-
get_axes = ct.timeplot.get_axes
192+
get_plot_axes = ct.timeplot.get_plot_axes
193193

194194
sys_2x3 = ct.rss(4, 2, 3)
195195
sys_2x3b = ct.rss(4, 2, 3)
@@ -199,36 +199,36 @@ def test_axes_setup():
199199
# Two plots of the same size leaves axes unchanged
200200
out1 = ct.step_response(sys_2x3).plot()
201201
out2 = ct.step_response(sys_2x3b).plot()
202-
np.testing.assert_equal(get_axes(out1), get_axes(out2))
202+
np.testing.assert_equal(get_plot_axes(out1), get_plot_axes(out2))
203203
plt.close()
204204

205205
# Two plots of same net size leaves axes unchanged (unfortunately)
206206
out1 = ct.step_response(sys_2x3).plot()
207207
out2 = ct.step_response(sys_3x2).plot()
208208
np.testing.assert_equal(
209-
get_axes(out1).reshape(-1), get_axes(out2).reshape(-1))
209+
get_plot_axes(out1).reshape(-1), get_plot_axes(out2).reshape(-1))
210210
plt.close()
211211

212212
# Plots of different shapes generate new plots
213213
out1 = ct.step_response(sys_2x3).plot()
214214
out2 = ct.step_response(sys_3x1).plot()
215-
ax1_list = get_axes(out1).reshape(-1).tolist()
216-
ax2_list = get_axes(out2).reshape(-1).tolist()
215+
ax1_list = get_plot_axes(out1).reshape(-1).tolist()
216+
ax2_list = get_plot_axes(out2).reshape(-1).tolist()
217217
for ax in ax1_list:
218218
assert ax not in ax2_list
219219
plt.close()
220220

221221
# Passing a list of axes preserves those axes
222222
out1 = ct.step_response(sys_2x3).plot()
223223
out2 = ct.step_response(sys_3x1).plot()
224-
out3 = ct.step_response(sys_2x3b).plot(ax=get_axes(out1))
225-
np.testing.assert_equal(get_axes(out1), get_axes(out3))
224+
out3 = ct.step_response(sys_2x3b).plot(ax=get_plot_axes(out1))
225+
np.testing.assert_equal(get_plot_axes(out1), get_plot_axes(out3))
226226
plt.close()
227227

228228
# Sending an axes array of the wrong size raises exception
229229
with pytest.raises(ValueError, match="not the right shape"):
230230
out = ct.step_response(sys_2x3).plot()
231-
ct.step_response(sys_3x1).plot(ax=get_axes(out))
231+
ct.step_response(sys_3x1).plot(ax=get_plot_axes(out))
232232
sys_2x3 = ct.rss(4, 2, 3)
233233
sys_2x3b = ct.rss(4, 2, 3)
234234
sys_3x2 = ct.rss(4, 3, 2)
@@ -244,7 +244,7 @@ def test_legend_map():
244244
response.plot(
245245
legend_map=np.array([['center', 'upper right'],
246246
[None, 'center right']]),
247-
plot_inputs=True, combine_signals=True, transpose=True,
247+
plot_inputs=True, overlay_signals=True, transpose=True,
248248
title='MIMO step response with custom legend placement')
249249

250250

@@ -310,18 +310,55 @@ def test_combine_traces():
310310
combresp6 = ct.combine_traces([resp1, resp])
311311

312312

313+
def test_linestyles():
314+
# Check to make sure we can change line styles
315+
sys_mimo = ct.tf2ss(
316+
[[[1], [0.1]], [[0.2], [1]]],
317+
[[[1, 0.6, 1], [1, 1, 1]], [[1, 0.4, 1], [1, 2, 1]]], name="MIMO")
318+
out = ct.step_response(sys_mimo).plot('k--', plot_inputs=True)
319+
for ax in np.nditer(out, flags=["refs_ok"]):
320+
for line in ax.item():
321+
assert line.get_color() == 'k'
322+
assert line.get_linestyle() == '--'
323+
324+
325+
def test_relabel():
326+
sys1 = ct.rss(2, inputs='u', outputs='y')
327+
sys2 = ct.rss(1, 1, 1) # uses default i/o labels
328+
329+
# Generate a plot with specific labels
330+
ct.step_response(sys1).plot()
331+
332+
# Generate a new plot, which overwrites labels
333+
out = ct.step_response(sys2).plot()
334+
ax = ct.get_plot_axes(out)
335+
assert ax[0, 0].get_ylabel() == 'y[0]'
336+
337+
# Regenerate the first plot
338+
plt.figure()
339+
ct.step_response(sys1).plot()
340+
341+
# Generate a new plt, without relabeling
342+
out = ct.step_response(sys2).plot(relabel=False)
343+
ax = ct.get_plot_axes(out)
344+
assert ax[0, 0].get_ylabel() == 'y'
345+
346+
313347
def test_errors():
314348
sys = ct.rss(2, 1, 1)
315349
stepresp = ct.step_response(sys)
316-
with pytest.raises(TypeError, match="unrecognized keyword"):
350+
with pytest.raises(AttributeError,
351+
match="(has no property|unexpected keyword)"):
317352
stepresp.plot(unknown=None)
318353

319-
with pytest.raises(TypeError, match="unrecognized keyword"):
354+
with pytest.raises(AttributeError,
355+
match="(has no property|unexpected keyword)"):
320356
ct.time_response_plot(stepresp, unknown=None)
321357

322358
with pytest.raises(ValueError, match="unrecognized value"):
323359
stepresp.plot(plot_inputs='unknown')
324360

361+
325362
if __name__ == "__main__":
326363
#
327364
# Interactive mode: generate plots for manual viewing
@@ -344,8 +381,8 @@ def test_errors():
344381

345382
# Define and run a selected set of interesting tests
346383
# def test_response_plots(
347-
# fcn, sys, plot_inputs, plot_outputs, combine_signals,
348-
# combine_traces, transpose, second_system, clear=True):
384+
# fcn, sys, plot_inputs, plot_outputs, overlay_signals,
385+
# overlay_traces, transpose, second_system, clear=True):
349386
N, T, F = None, True, False
350387
test_cases = [
351388
# response fcn system in out cs ct tr ss
@@ -379,12 +416,12 @@ def test_errors():
379416
ct.step_response(sys_mimo).plot()
380417
plt.savefig('timeplot-mimo_step-default.png')
381418

382-
# Step response with plot_inputs, combine_signals
419+
# Step response with plot_inputs, overlay_signals
383420
plt.figure()
384421
ct.step_response(sys_mimo).plot(
385-
plot_inputs=True, combine_signals=True,
422+
plot_inputs=True, overlay_signals=True,
386423
title="Step response for 2x2 MIMO system " +
387-
"[plot_inputs, combine_signals]")
424+
"[plot_inputs, overlay_signals]")
388425
plt.savefig('timeplot-mimo_step-pi_cs.png')
389426

390427
# Input/output response with overlaid inputs, legend_map
@@ -412,3 +449,9 @@ def test_errors():
412449
title="I/O responses for 2x2 MIMO system, multiple traces "
413450
"[transpose]")
414451
plt.savefig('timeplot-mimo_ioresp-mt_tr.png')
452+
453+
# Reset line styles
454+
plt.figure()
455+
resp1.plot('g-')
456+
resp2.plot('r--')
457+
# plt.savefig('timeplot-mimo_step-linestyle.png')

0 commit comments

Comments
 (0)
0