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])
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 ])
5858def 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+
214289if __name__ == "__main__" :
215290 #
216291 # Interactive mode: generate plots for manual viewing
0 commit comments