21
21
from .bdalg import feedback
22
22
from .ctrlplot import ControlPlot , _add_arrows_to_line2D , _ctrlplot_rcParams , \
23
23
_find_axes_center , _get_line_labels , _make_legend_labels , \
24
- _process_ax_keyword , _process_line_labels , _update_plot_title
24
+ _process_ax_keyword , _process_legend_keywords , _process_line_labels , \
25
+ _update_plot_title
25
26
from .ctrlutil import unwrap
26
27
from .exception import ControlMIMONotImplemented
27
28
from .frdata import FrequencyResponseData
@@ -87,8 +88,7 @@ def bode_plot(
87
88
plot = None , plot_magnitude = True , plot_phase = None ,
88
89
overlay_outputs = None , overlay_inputs = None , phase_label = None ,
89
90
magnitude_label = None , label = None , display_margins = None ,
90
- margins_method = 'best' , legend_map = None , legend_loc = None ,
91
- sharex = None , sharey = None , title = None , ** kwargs ):
91
+ margins_method = 'best' , title = None , sharex = None , sharey = None , ** kwargs ):
92
92
"""Bode plot for a system.
93
93
94
94
Plot the magnitude and phase of the frequency response over a
@@ -142,25 +142,33 @@ def bode_plot(
142
142
143
143
Other Parameters
144
144
----------------
145
- ax : array of Axes
146
- The matplotlib Axes to draw the figure on. If not specified, the
147
- Axes for the current figure are used or, if there is no current
148
- figure with the correct number and shape of Axes , a new figure is
145
+ ax : array of matplotlib.axes. Axes, optional
146
+ The matplotlib axes to draw the figure on. If not specified, the
147
+ axes for the current figure are used or, if there is no current
148
+ figure with the correct number and shape of axes , a new figure is
149
149
created. The shape of the array must match the shape of the
150
150
plotted data.
151
- grid : bool
151
+ grid : bool, optional
152
152
If True, plot grid lines on gain and phase plots. Default is set by
153
153
`config.defaults['freqplot.grid']`.
154
- initial_phase : float
154
+ initial_phase : float, optional
155
155
Set the reference phase to use for the lowest frequency. If set, the
156
156
initial phase of the Bode plot will be set to the value closest to the
157
157
value specified. Units are in either degrees or radians, depending on
158
158
the `deg` parameter. Default is -180 if wrap_phase is False, 0 if
159
159
wrap_phase is True.
160
- label : str or array-like of str
160
+ label : str or array_like of str, optional
161
161
If present, replace automatically generated label(s) with the given
162
162
label(s). If sysdata is a list, strings should be specified for each
163
163
system. If MIMO, strings required for each system, output, and input.
164
+ legend_map : array of str, optional
165
+ Location of the legend for multi-axes plots. Specifies an array
166
+ of legend location strings matching the shape of the subplots, with
167
+ each entry being either None (for no legend) or a legend location
168
+ string (see :func:`~matplotlib.pyplot.legend`).
169
+ legend_loc : int or str, optional
170
+ Include a legend in the given location. Default is 'center right',
171
+ with no legend for a single response. Use False to supress legend.
164
172
margins_method : str, optional
165
173
Method to use in computing margins (see :func:`stability_margins`).
166
174
omega_limits : array_like of two values
@@ -179,6 +187,10 @@ def bode_plot(
179
187
rcParams : dict
180
188
Override the default parameters used for generating plots.
181
189
Default is set by config.default['freqplot.rcParams'].
190
+ show_legend : bool, optional
191
+ Force legend to be shown if ``True`` or hidden if ``False``. If
192
+ ``None``, then show legend when there is more than one line on an
193
+ axis or ``legend_loc`` or ``legend_map`` has been specified.
182
194
title : str, optional
183
195
Set the title of the plot. Defaults to plot type and system name(s).
184
196
wrap_phase : bool or float
@@ -478,8 +490,10 @@ def bode_plot(
478
490
if kw not in kwargs or kwargs [kw ] is None :
479
491
kwargs [kw ] = config .defaults ['freqplot.' + kw ]
480
492
481
- fig , ax_array = _process_ax_keyword (ax , (
482
- nrows , ncols ), squeeze = False , rcParams = rcParams , clear_text = True )
493
+ fig , ax_array = _process_ax_keyword (
494
+ ax , (nrows , ncols ), squeeze = False , rcParams = rcParams , clear_text = True )
495
+ legend_loc , legend_map , show_legend = _process_legend_keywords (
496
+ kwargs , (nrows ,ncols ), 'center right' )
483
497
484
498
# Get the values for sharing axes limits
485
499
share_magnitude = kwargs .pop ('share_magnitude' , None )
@@ -989,21 +1003,15 @@ def gen_zero_centered_series(val_min, val_max, period):
989
1003
# different response (system).
990
1004
#
991
1005
992
- # Figure out where to put legends
993
- if legend_map is None :
994
- legend_map = np .full (ax_array .shape , None , dtype = object )
995
- if legend_loc == None :
996
- legend_loc = 'center right'
997
-
998
- # TODO: add in additional processing later
999
-
1000
- # Put legend in the upper right
1001
- legend_map [0 , - 1 ] = legend_loc
1002
-
1003
1006
# Create axis legends
1004
- legend_array = np .full (ax_array .shape , None , dtype = object )
1005
- for i in range (nrows ):
1006
- for j in range (ncols ):
1007
+ if show_legend != False :
1008
+ # Figure out where to put legends
1009
+ if legend_map is None :
1010
+ legend_map = np .full (ax_array .shape , None , dtype = object )
1011
+ legend_map [0 , - 1 ] = legend_loc
1012
+
1013
+ legend_array = np .full (ax_array .shape , None , dtype = object )
1014
+ for i , j in itertools .product (range (nrows ), range (ncols )):
1007
1015
if legend_map [i , j ] is None :
1008
1016
continue
1009
1017
ax = ax_array [i , j ]
@@ -1016,10 +1024,13 @@ def gen_zero_centered_series(val_min, val_max, period):
1016
1024
ignore_common = line_labels is not None )
1017
1025
1018
1026
# Generate the label, if needed
1019
- if len (labels ) > 1 :
1027
+ if show_legend == True or len (labe
F438
ls ) > 1 :
1020
1028
with plt .rc_context (rcParams ):
1029
+ print (f"{ lines = } , { labels = } " )
1021
1030
legend_array [i , j ] = ax .legend (
1022
1031
lines , labels , loc = legend_map [i , j ])
1032
+ else :
1033
+ legend_array = None
1023
1034
1024
1035
#
1025
1036
# Legacy return pocessing
@@ -1476,7 +1487,7 @@ def nyquist_response(
1476
1487
1477
1488
def nyquist_plot (
1478
1489
data , omega = None , plot = None , label_freq = 0 , color = None , label = None ,
1479
- return_contour = None , title = None , legend_loc = 'upper right' , ax = None ,
1490
+ return_contour = None , title = None , ax = None ,
1480
1491
unit_circle = False , mt_circles = None , ms_circles = None , ** kwargs ):
1481
1492
"""Nyquist plot for a system.
1482
1493
@@ -1550,6 +1561,10 @@ def nyquist_plot(
1550
1561
8 and can be set using config.defaults['nyquist.arrow_size'].
1551
1562
arrow_style : matplotlib.patches.ArrowStyle, optional
1552
1563
Define style used for Nyquist curve arrows (overrides `arrow_size`).
1564
+ ax : matplotlib.axes.Axes, optional
1565
+ The matplotlib axes to draw the figure on. If not specified and
1566
+ the current figure has a single axes, that axes is used.
1567
+ Otherwise, a new figure is created.
1553
1568
encirclement_threshold : float, optional
1554
1569
Define the threshold for generating a warning if the number of net
1555
1570
encirclements is a non-integer value. Default value is 0.05 and can
@@ -1564,13 +1579,16 @@ def nyquist_plot(
1564
1579
Amount to indent the Nyquist contour around poles on or near the
1565
1580
imaginary axis. Portions of the Nyquist plot corresponding to indented
1566
1581
portions of the contour are plotted using a different line style.
1567
- label : str or array-like of str
1582
+ label : str or array_like of str, optional
1568
1583
If present, replace automatically generated label(s) with the given
1569
1584
label(s). If sysdata is a list, strings should be specified for each
1570
1585
system.
1571
1586
label_freq : int, optiona
1572
1587
Label every nth frequency on the plot. If not specified, no labels
1573
1588
are generated.
1589
+ legend_loc : int or str, optional
1590
+ Include a legend in the given location. Default is 'center right',
1591
+ with no legend for a single response. Use False to supress legend.
1574
1592
max_curve_magnitude : float, optional
1575
1593
Restrict the maximum magnitude of the Nyquist plot to this value.
1576
1594
Portions of the Nyquist plot whose magnitude is restricted are
@@ -1609,6 +1627,10 @@ def nyquist_plot(
1609
1627
return_contour : bool, optional
1610
1628
(legacy) If 'True', return the encirclement count and Nyquist
1611
1629
contour used to generate the Nyquist plot.
1630
+ show_legend : bool, optional
1631
+ Force legend to be shown if ``True`` or hidden if ``False``. If
1632
+ ``None``, then show legend when there is more than one line on the
1633
+ plot or ``legend_loc`` has been specified.
1612
1634
start_marker : str, optional
1613
1635
Matplotlib marker to use to mark the starting point of the Nyquist
1614
1636
plot. Defaults value is 'o' and can be set using
@@ -1775,6 +1797,8 @@ def _parse_linestyle(style_name, allow_false=False):
1775
1797
1776
1798
fig , ax = _process_ax_keyword (
1777
1799
ax_user , shape = (1 , 1 ), squeeze = True , rcParams = rcParams )
1800
+ legend_loc , _ , show_legend = _process_legend_keywords (
1801
+ kwargs , None , 'upper right' )
1778
1802
1779
1803
# Create a list of lines for the output
1780
1804
out = np .empty (len (nyquist_responses ), dtype = object )
@@ -1950,11 +1974,11 @@ def _parse_linestyle(style_name, allow_false=False):
1950
1974
lines , labels = _get_line_labels (ax )
1951
1975
1952
1976
# Add legend if there is more than one system plotted
1953
- if len (labels ) > 1 :
1977
+ if show_legend == True or ( show_legend != False and len (labels ) > 1 ) :
1954
1978
with plt .rc_context (rcParams ):
1955
1979
legend = ax .legend (lines , labels , loc = legend_loc )
1956
1980
else :
1957
- legend = None
1981
+ legend = None
1958
1982
1959
1983
# Add the title
1960
1984
if ax_user is None :
@@ -2193,7 +2217,7 @@ def singular_values_response(
2193
2217
2194
2218
def singular_values_plot (
2195
2219
data , omega = None, * fmt , plot = None , omega_limits = None , omega_num = None ,
2196
- ax = None , label = None , title = None , legend_loc = 'center right' , ** kwargs ):
2220
+ ax = None , label = None , title = None , ** kwargs ):
2197
2221
"""Plot the singular values for a system.
2198
2222
2199
2223
Plot the singular values as a function of frequency for a system or
@@ -2237,16 +2261,20 @@ def singular_values_plot(
2237
2261
2238
2262
Other Parameters
2239
2263
----------------
2264
+ ax : matplotlib.axes.Axes, optional
2265
+ The matplotlib axes to draw the figure on. If not specified and
2266
+ the current figure has a single axes, that axes is used.
2267
+ Otherwise, a new figure is created.
2240
2268
grid : bool
2241
2269
If True, plot grid lines on gain and phase plots. Default is set by
2242
2270
`config.defaults['freqplot.grid']`.
2243
- label : str or array-like of str
2271
+ label : str or array_like of str, optional
2244
2272
If present, replace automatically generated label(s) with the given
2245
2273
label(s). If sysdata is a list, strings should be specified for each
2246
2274
system.
2247
- legend_loc : str, optional
2248
- For plots with multiple lines, a legend will be included in the
2249
- given location. Default is 'center right' . Use False to supress.
2275
+ legend_loc : int or str, optional
2276
+ Include a legend in the given location. Default is 'center right',
2277
+ with no legend for a single response . Use False to supress legend .
2250
2278
omega_limits : array_like of two values
2251
2279
Set limits for plotted frequency range. If Hz=True the limits are
2252
2280
in Hz otherwise in rad/s. Specifying ``omega`` as a list of two
@@ -2262,6 +2290,10 @@ def singular_values_plot(
2262
2290
rcParams : dict
2263
2291
Override the default parameters used for generating plots.
2264
2292
Default is set up config.default['freqplot.rcParams'].
2293
+ show_legend : bool, optional
2294
+ Force legend to be shown if ``True`` or hidden if ``False``. If
2295
+ ``None``, then show legend when there is more than one line on an
2296
+ axis or ``legend_loc`` or ``legend_map`` has been specified.
2265
2297
title : str, optional
2266
2298
Set the title of the plot. Defaults to plot type and system name(s).
2267
2299
@@ -2343,6 +2375,8 @@ def singular_values_plot(
2343
2375
fig , ax_sigma = _process_ax_keyword (
2344
2376
ax , shape = (1 , 1 ), squeeze = True , rcParams= rcParams )
2345
2377
ax_sigma .set_label ('control-sigma' ) # TODO: deprecate?
2378
+ legend_loc , _ , show_legend = _process_legend_keywords (
2379
+ kwargs , None , 'center right' )
2346
2380
2347
2381
# Handle color cycle manually as all singular values
2348
2382
# of the same systems are expected to be of the same color
@@ -2413,7 +2447,7 @@ def singular_values_plot(
2413
2447
lines , labels = _get_line_labels (ax_sigma )
2414
2448
2415
2449
# Add legend if there is more than one system plotted
2416
- if len ( labels ) > 1 and legend_loc is not False :
2450
+ if show_legend == True or ( show_legend != False and len ( labels ) > 1 ) :
2417
2451
with plt .rc_context (rcParams ):
2418
2452
legend = ax_sigma .legend (lines , labels , loc = legend_loc )
2419
2453
else :
0 commit comments