1
0
Files
python-control/688.patch

264 lines
11 KiB
Diff

From 58d8cd6e4fe77fd5a18f91c791532e3e259b1949 Mon Sep 17 00:00:00 2001
From: Ben Greiner <code@bnavigator.de>
Date: Tue, 4 Jan 2022 20:09:23 +0100
Subject: [PATCH 1/4] round to nearest integer for default omega
---
control/freqplot.py | 16 +++++++---------
control/tests/sisotool_test.py | 4 ++--
2 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/control/freqplot.py b/control/freqplot.py
index 881ec93d..7225afe9 100644
--- a/control/freqplot.py
+++ b/control/freqplot.py
@@ -1326,7 +1326,7 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
features_ = np.concatenate((np.abs(sys.pole()),
np.abs(sys.zero())))
# Get rid of poles and zeros at the origin
- toreplace = features_ == 0.0
+ toreplace = np.isclose(features_, 0.0)
if np.any(toreplace):
features_ = features_[~toreplace]
elif sys.isdtime(strict=True):
@@ -1339,7 +1339,7 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
# Get rid of poles and zeros on the real axis (imag==0)
# * origin and real < 0
# * at 1.: would result in omega=0. (logaritmic plot!)
- toreplace = (features_.imag == 0.0) & (
+ toreplace = np.isclose(features_.imag, 0.0) & (
(features_.real <= 0.) |
(np.abs(features_.real - 1.0) < 1.e-10))
if np.any(toreplace):
@@ -1360,15 +1360,13 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
if Hz:
features /= 2. * math.pi
- features = np.log10(features)
- lsp_min = np.floor(np.min(features) - feature_periphery_decades)
- lsp_max = np.ceil(np.max(features) + feature_periphery_decades)
+ features = np.log10(features)
+ lsp_min = np.rint(np.min(features) - feature_periphery_decades)
+ lsp_max = np.rint(np.max(features) + feature_periphery_decades)
+ if Hz:
lsp_min += np.log10(2. * math.pi)
lsp_max += np.log10(2. * math.pi)
- else:
- features = np.log10(features)
- lsp_min = np.floor(np.min(features) - feature_periphery_decades)
- lsp_max = np.ceil(np.max(features) + feature_periphery_decades)
+
if freq_interesting:
lsp_min = min(lsp_min, np.log10(min(freq_interesting)))
lsp_max = max(lsp_max, np.log10(max(freq_interesting)))
diff --git a/control/tests/sisotool_test.py b/control/tests/sisotool_test.py
index 6b8c6d14..d5e9dd01 100644
--- a/control/tests/sisotool_test.py
+++ b/control/tests/sisotool_test.py
@@ -102,8 +102,8 @@ def test_sisotool(self, tsys):
# Check if the bode_mag line has moved
bode_mag_moved = np.array(
- [674.0242, 667.8354, 661.7033, 655.6275, 649.6074, 643.6426,
- 637.7324, 631.8765, 626.0742, 620.3252])
+ [69.0065, 68.6749, 68.3448, 68.0161, 67.6889, 67.3631, 67.0388,
+ 66.7159, 66.3944, 66.0743])
assert_array_almost_equal(ax_mag.lines[0].get_data()[1][10:20],
bode_mag_moved, 4)
From 19801e6deac560e72ee9593f81be2d24e8df14e6 Mon Sep 17 00:00:00 2001
From: Ben Greiner <code@bnavigator.de>
Date: Tue, 4 Jan 2022 21:07:35 +0100
Subject: [PATCH 2/4] split up nyquist indent tests
---
control/tests/nyquist_test.py | 36 ++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/control/tests/nyquist_test.py b/control/tests/nyquist_test.py
index 4667c621..c77d94c8 100644
--- a/control/tests/nyquist_test.py
+++ b/control/tests/nyquist_test.py
@@ -182,42 +182,56 @@ def test_nyquist_encirclements():
assert _Z(sys) == count + _P(sys)
-def test_nyquist_indent():
+@pytest.fixture
+def indentsys():
# FBS Figure 10.10
- s = ct.tf('s')
- sys = 3 * (s+6)**2 / (s * (s+1)**2)
# poles: [-1, -1, 0]
+ s = ct.tf('s')
+ return 3 * (s+6)**2 / (s * (s+1)**2)
+
+def test_nyquist_indent_default(indentsys):
plt.figure();
- count = ct.nyquist_plot(sys)
+ count = ct.nyquist_plot(indentsys)
plt.title("Pole at origin; indent_radius=default")
- assert _Z(sys) == count + _P(sys)
+ assert _Z(indentsys) == count + _P(indentsys)
+
+def test_nyquist_indent_dont(indentsys):
# first value of default omega vector was 0.1, replaced by 0. for contour
# indent_radius is larger than 0.1 -> no extra quater circle around origin
- count, contour = ct.nyquist_plot(sys, plot=False, indent_radius=.1007,
+ count, contour = ct.nyquist_plot(indentsys,
+ plot=False,
+ indent_radius=.1007,
return_contour=True)
np.testing.assert_allclose(contour[0], .1007+0.j)
# second value of omega_vector is larger than indent_radius: not indented
assert np.all(contour.real[2:] == 0.)
+
+def test_nyquist_indent_do(indentsys):
plt.figure();
- count, contour = ct.nyquist_plot(sys, indent_radius=0.01,
+ count, contour = ct.nyquist_plot(indentsys,
+ indent_radius=0.01,
return_contour=True)
plt.title("Pole at origin; indent_radius=0.01; encirclements = %d" % count)
- assert _Z(sys) == count + _P(sys)
+ assert _Z(indentsys) == count + _P(indentsys)
# indent radius is smaller than the start of the default omega vector
# check that a quarter circle around the pole at origin has been added.
np.testing.assert_allclose(contour[:50].real**2 + contour[:50].imag**2,
0.01**2)
+
+def test_nyquist_indent_left(indentsys):
plt.figure();
- count = ct.nyquist_plot(sys, indent_direction='left')
+ count = ct.nyquist_plot(indentsys, indent_direction='left')
plt.title(
"Pole at origin; indent_direction='left'; encirclements = %d" % count)
- assert _Z(sys) == count + _P(sys, indent='left')
+ assert _Z(indentsys) == count + _P(indentsys, indent='left')
+
- # System with poles on the imaginary axis
+def test_nyquist_indent_im():
+ """Test system with poles on the imaginary axis."""
sys = ct.tf([1, 1], [1, 0, 1])
# Imaginary poles with standard indentation
From c44b901d3af030187d4c2c75dbee75f2bedcf29a Mon Sep 17 00:00:00 2001
From: Ben Greiner <code@bnavigator.de>
Date: Tue, 4 Jan 2022 21:08:06 +0100
Subject: [PATCH 3/4] passthrough Hz parameter for omega vector
---
control/freqplot.py | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/control/freqplot.py b/control/freqplot.py
index 7225afe9..18b9a448 100644
--- a/control/freqplot.py
+++ b/control/freqplot.py
@@ -209,7 +209,7 @@ def bode_plot(syslist, omega=None,
syslist = (syslist,)
omega, omega_range_given = _determine_omega_vector(
- syslist, omega, omega_limits, omega_num)
+ syslist, omega, omega_limits, omega_num, Hz=Hz)
if plot:
# Set up the axes with labels so that multiple calls to
@@ -965,7 +965,7 @@ def gangof4_plot(P, C, omega=None, **kwargs):
# Select a default range if none is provided
# TODO: This needs to be made more intelligent
if omega is None:
- omega = _default_frequency_range((P, C, S))
+ omega = _default_frequency_range((P, C, S), Hz=Hz)
# Set up the axes with labels so that multiple calls to
# gangof4_plot will superimpose the data. See details in bode_plot.
@@ -1115,7 +1115,7 @@ def singular_values_plot(syslist, omega=None,
syslist = (syslist,)
omega, omega_range_given = _determine_omega_vector(
- syslist, omega, omega_limits, omega_num)
+ syslist, omega, omega_limits, omega_num, Hz=Hz)
omega = np.atleast_1d(omega)
@@ -1210,7 +1210,8 @@ def singular_values_plot(syslist, omega=None,
# Determine the frequency range to be used
-def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
+def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num,
+ Hz=None):
"""Determine the frequency range for a frequency-domain plot
according to a standard logic.
@@ -1236,6 +1237,10 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
omega_num : int
Number of points to be used for the frequency
range (if the frequency range is not user-specified)
+ Hz : bool. optional
+ If True, the limits (first and last value) of the frequencies
+ are set to full decades in Hz so it fits plotting with logarithmic
+ scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.
Returns
-------
@@ -1253,7 +1258,8 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
omega_range_given = False
# Select a default range if none is provided
omega_out = _default_frequency_range(syslist,
- number_of_samples=omega_num)
+ number_of_samples=omega_num,
+ Hz=Hz)
else:
omega_limits = np.asarray(omega_limits)
if len(omega_limits) != 2:
@@ -1280,7 +1286,7 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
----------
syslist : list of LTI
List of linear input/output systems (single system is OK)
- Hz : bool
+ Hz : bool. optional
If True, the limits (first and last value) of the frequencies
are set to full decades in Hz so it fits plotting with logarithmic
scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.
From 021372f4cd821907fee913ceef6d46af899a9bcd Mon Sep 17 00:00:00 2001
From: Ben Greiner <code@bnavigator.de>
Date: Tue, 4 Jan 2022 21:37:10 +0100
Subject: [PATCH 4/4] docstring punctuation
---
control/freqplot.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/control/freqplot.py b/control/freqplot.py
index 18b9a448..a8324e06 100644
--- a/control/freqplot.py
+++ b/control/freqplot.py
@@ -1237,7 +1237,7 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num,
omega_num : int
Number of points to be used for the frequency
range (if the frequency range is not user-specified)
- Hz : bool. optional
+ Hz : bool, optional
If True, the limits (first and last value) of the frequencies
are set to full decades in Hz so it fits plotting with logarithmic
scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.
@@ -1286,7 +1286,7 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
----------
syslist : list of LTI
List of linear input/output systems (single system is OK)
- Hz : bool. optional
+ Hz : bool, optional
If True, the limits (first and last value) of the frequencies
are set to full decades in Hz so it fits plotting with logarithmic
scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.