GeezerVison is showing github.com — fix ax processing bug in {nyquist,nichols,describing_function}_plot · python-control/python-control@2764889
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: control/descfcn.py +3-2Lines changed: 3 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -521,16 +521,17 @@ def describing_function_plot( | 521 | 521 |
| 522 | 522 | # Plot the Nyquist response | 523 | 523 | cplt = dfresp.response.plot(**kwargs) | | 524 | + ax = cplt.axes[0, 0] # Get the axes where the plot was made | 524 | 525 | lines[0] = cplt.lines[0] # Return Nyquist lines for first system | 525 | 526 |
| 526 | 527 | # Add the describing function curve to the plot | 527 | | - lines[1] = plt.plot(dfresp.N_vals.real, dfresp.N_vals.imag) | | 528 | + lines[1] = ax.plot(dfresp.N_vals.real, dfresp.N_vals.imag) | 528 | 529 |
| 529 | 530 | # Label the intersection points | 530 | 531 | if point_label: | 531 | 532 | for pos, (a, omega) in zip(dfresp.positions, dfresp.intersections): | 532 | 533 | # Add labels to the intersection points | 533 | | - plt.text(pos.real, pos.imag, point_label % (a, omega)) | | 534 | + ax.text(pos.real, pos.imag, point_label % (a, omega)) | 534 | 535 |
| 535 | 536 | return ControlPlot(lines, cplt.axes, cplt.figure) | 536 | 537 |
| |
Collapse file Copy file name to clipboardExpand all lines: control/freqplot.py +17-18Lines changed: 17 additions & 18 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1913,7 +1913,7 @@ def _parse_linestyle(style_name, allow_false=False): | 1913 | 1913 | # Plot the regular portions of the curve (and grab the color) | 1914 | 1914 | x_reg = np.ma.masked_where(reg_mask, resp.real) | 1915 | 1915 | y_reg = np.ma.masked_where(reg_mask, resp.imag) | 1916 | | - p = plt.plot( | | 1916 | + p = ax.plot( | 1917 | 1917 | x_reg, y_reg, primary_style[0], color=color, label=label, **kwargs) | 1918 | 1918 | c = p[0].get_color() | 1919 | 1919 | out[idx] += p | @@ -1928,7 +1928,7 @@ def _parse_linestyle(style_name, allow_false=False): | 1928 | 1928 | x_scl = np.ma.masked_where(scale_mask, resp.real) | 1929 | 1929 | y_scl = np.ma.masked_where(scale_mask, resp.imag) | 1930 | 1930 | if x_scl.count() >= 1 and y_scl.count() >= 1: | 1931 | | - out[idx] += plt.plot( | | 1931 | + out[idx] += ax.plot( | 1932 | 1932 | x_scl * (1 + curve_offset), | 1933 | 1933 | y_scl * (1 + curve_offset), | 1934 | 1934 | primary_style[1], color=c, **kwargs) | @@ -1939,20 +1939,19 @@ def _parse_linestyle(style_name, allow_false=False): | 1939 | 1939 | x, y = resp.real.copy(), resp.imag.copy() | 1940 | 1940 | x[reg_mask] *= (1 + curve_offset[reg_mask]) | 1941 | 1941 | y[reg_mask] *= (1 + curve_offset[reg_mask]) | 1942 | | - p = plt.plot(x, y, linestyle='None', color=c) | | 1942 | + p = ax.plot(x, y, linestyle='None', color=c) | 1943 | 1943 |
| 1944 | 1944 | # Add arrows | 1945 | | - ax = plt.gca() | 1946 | 1945 | _add_arrows_to_line2D( | 1947 | 1946 | ax, p[0], arrow_pos, arrowstyle=arrow_style, dir=1) | 1948 | 1947 |
| 1949 | 1948 | # Plot the mirror image | 1950 | 1949 | if mirror_style is not False: | 1951 | 1950 | # Plot the regular and scaled segments | 1952 | | - out[idx] += plt.plot( | | 1951 | + out[idx] += ax.plot( | 1953 | 1952 | x_reg, -y_reg, mirror_style[0], color=c, **kwargs) | 1954 | 1953 | if x_scl.count() >= 1 and y_scl.count() >= 1: | 1955 | | - out[idx] += plt.plot( | | 1954 | + out[idx] += ax.plot( | 1956 | 1955 | x_scl * (1 - curve_offset), | 1957 | 1956 | -y_scl * (1 - curve_offset), | 1958 | 1957 | mirror_style[1], color=c, **kwargs) | @@ -1963,19 +1962,19 @@ def _parse_linestyle(style_name, allow_false=False): | 1963 | 1962 | x, y = resp.real.copy(), resp.imag.copy() | 1964 | 1963 | x[reg_mask] *= (1 - curve_offset[reg_mask]) | 1965 | 1964 | y[reg_mask] *= (1 - curve_offset[reg_mask]) | 1966 | | - p = plt.plot(x, -y, linestyle='None', color=c, **kwargs) | | 1965 | + p = ax.plot(x, -y, linestyle='None', color=c, **kwargs) | 1967 | 1966 | _add_arrows_to_line2D( | 1968 | 1967 | ax, p[0], arrow_pos, arrowstyle=arrow_style, dir=-1) | 1969 | 1968 | else: | 1970 | 1969 | out[idx] += [None, None] | 1971 | 1970 |
| 1972 | 1971 | # Mark the start of the curve | 1973 | 1972 | if start_marker: | 1974 | | - plt.plot(resp[0].real, resp[0].imag, start_marker, | | 1973 | + ax.plot(resp[0].real, resp[0].imag, start_marker, | 1975 | 1974 | color=c, markersize=start_marker_size) | 1976 | 1975 |
| 1977 | 1976 | # Mark the -1 point | 1978 | | - plt.plot([-1], [0], 'r+') | | 1977 | + ax.plot([-1], [0], 'r+') | 1979 | 1978 |
| 1980 | 1979 | # | 1981 | 1980 | # Draw circles for gain crossover and sensitivity functions | @@ -1987,16 +1986,16 @@ def _parse_linestyle(style_name, allow_false=False): | 1987 | 1986 |
| 1988 | 1987 | # Display the unit circle, to read gain crossover frequency | 1989 | 1988 | if unit_circle: | 1990 | | - plt.plot(cos, sin, **config.defaults['nyquist.circle_style']) | | 1989 | + ax.plot(cos, sin, **config.defaults['nyquist.circle_style']) | 1991 | 1990 |
| 1992 | 1991 | # Draw circles for given magnitudes of sensitivity | 1993 | 1992 | if ms_circles is not None: | 1994 | 1993 | for ms in ms_circles: | 1995 | 1994 | pos_x = -1 + (1/ms)*cos | 1996 | 1995 | pos_y = (1/ms)*sin | 1997 | | - plt.plot( | | 1996 | + ax.plot( | 1998 | 1997 | pos_x, pos_y, **config.defaults['nyquist.circle_style']) | 1999 | | - plt.text(pos_x[label_pos], pos_y[label_pos], ms) | | 1998 | + ax.text(pos_x[label_pos], pos_y[label_pos], ms) | 2000 | 1999 |
| 2001 | 2000 | # Draw circles for given magnitudes of complementary sensitivity | 2002 | 2001 | if mt_circles is not None: | @@ -2006,17 +2005,17 @@ def _parse_linestyle(style_name, allow_false=False): | 2006 | 2005 | rt = mt/(mt**2-1) # Mt radius | 2007 | 2006 | pos_x = ct+rt*cos | 2008 | 2007 | pos_y = rt*sin | 2009 | | - plt.plot( | | 2008 | + ax.plot( | 2010 | 2009 | pos_x, pos_y, | 2011 | 2010 | **config.defaults['nyquist.circle_style']) | 2012 | | - plt.text(pos_x[label_pos], pos_y[label_pos], mt) | | 2011 | + ax.text(pos_x[label_pos], pos_y[label_pos], mt) | 2013 | 2012 | else: | 2014 | | - _, _, ymin, ymax = plt.axis() | | 2013 | + _, _, ymin, ymax = ax.axis() | 2015 | 2014 | pos_y = np.linspace(ymin, ymax, 100) | 2016 | | - plt.vlines( | | 2015 | + ax.vlines( | 2017 | 2016 | -0.5, ymin=ymin, ymax=ymax, | 2018 | 2017 | **config.defaults['nyquist.circle_style']) | 2019 | | - plt.text(-0.5, pos_y[label_pos], 1) | | 2018 | + ax.text(-0.5, pos_y[label_pos], 1) | 2020 | 2019 |
| 2021 | 2020 | # Label the frequencies of the points on the Nyquist curve | 2022 | 2021 | if label_freq: | @@ -2039,7 +2038,7 @@ def _parse_linestyle(style_name, allow_false=False): | 2039 | 2038 | # np.round() is used because 0.99... appears | 2040 | 2039 | # instead of 1.0, and this would otherwise be | 2041 | 2040 | # truncated to 0. | 2042 | | - plt.text(xpt, ypt, ' ' + | | 2041 | + ax.text(xpt, ypt, ' ' + | 2043 | 2042 | str(int(np.round(f / 1000 ** pow1000, 0))) + ' ' + | 2044 | 2043 | prefix + 'Hz') | 2045 | 2044 |
| |
Collapse file Copy file name to clipboardExpand all lines: control/nichols.py +4-4Lines changed: 4 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -132,15 +132,15 @@ def nichols_plot( | 132 | 132 | out[idx] = ax_nichols.plot(x, y, *fmt, label=label_, **kwargs) | 133 | 133 |
| 134 | 134 | # Label the plot axes | 135 | | - plt.xlabel('Phase [deg]') | 136 | | - plt.ylabel('Magnitude [dB]') | | 135 | + ax_nichols.set_xlabel('Phase [deg]') | | 136 | + ax_nichols.set_ylabel('Magnitude [dB]') | 137 | 137 |
| 138 | 138 | # Mark the -180 point | 139 | | - plt.plot([-180], [0], 'r+') | | 139 | + ax_nichols.plot([-180], [0], 'r+') | 140 | 140 |
| 141 | 141 | # Add grid | 142 | 142 | if grid: | 143 | | - nichols_grid() | | 143 | + nichols_grid(ax=ax_nichols) | 144 | 144 |
| 145 | 145 | # List of systems that are included in this plot | 146 | 146 | lines, labels = _get_line_labels(ax_nichols) | |
Collapse file Copy file name to clipboardExpand all lines: control/tests/ctrlplot_test.py +9Lines changed: 9 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -243,6 +243,15 @@ def test_plot_ax_processing(resp_fcn, plot_fcn): | 243 | 243 | # No response function available; just plot the data | 244 | 244 | plot_fcn(*args, **kwargs, **plot_kwargs, ax=ax) | 245 | 245 |
| | 246 | + # Make sure the plot ended up in the right place | | 247 | + assert len(axs[0, 0].get_lines()) == 0 # upper left | | 248 | + assert len(axs[0, 1].get_lines()) != 0 # top middle | | 249 | + assert len(axs[1, 0].get_lines()) == 0 # lower left | | 250 | + if resp_fcn != ct.gangof4_response: | | 251 | + assert len(axs[1, 2].get_lines()) == 0 # lower right (normally empty) | | 252 | + else: | | 253 | + assert len(axs[1, 2].get_lines()) != 0 # gangof4 uses this axes | | 254 | + | 246 | 255 | # Check to make sure original settings did not change | 247 | 256 | assert fig._suptitle.get_text() == title | 248 | 257 | assert fig._suptitle.get_fontsize() == titlesize | |
|