Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion control/sysnorm.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def _h2norm_slycot(sys, print_warning=True):
try:
from slycot import ab13bd
except ImportError:
ct.ControlSlycot("Can't find slycot module ab13bd")
raise ct.ControlSlycot("Can't find slycot module ab13bd")

try:
from slycot.exceptions import SlycotArithmeticError
Expand Down
64 changes: 44 additions & 20 deletions control/tests/sysnorm_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,54 @@
import pytest


def test_norm_1st_order_stable_system():
@pytest.mark.parametrize("method", [pytest.param("slycot", marks=pytest.mark.slycot), "scipy", None])
def test_norm_1st_order_stable_system(method):
"""First-order stable continuous-time system"""
s = ct.tf('s')

G1 = 1/(s+1)
assert np.allclose(ct.norm(G1, p='inf'), 1.0) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(G1, p=2), 0.707106781186547) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(G1, p='inf', method=method), 1.0) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(G1, p=2, method=method), 0.707106781186547) # Comparison to norm computed in MATLAB

Gd1 = ct.sample_system(G1, 0.1)
assert np.allclose(ct.norm(Gd1, p='inf'), 1.0) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(Gd1, p=2), 0.223513699524858) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(Gd1, p='inf', method=method), 1.0) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(Gd1, p=2, method=method), 0.223513699524858) # Comparison to norm computed in MATLAB


def test_norm_1st_order_unstable_system():
@pytest.mark.parametrize("method", [pytest.param("slycot", marks=pytest.mark.slycot), "scipy", None])
def test_norm_1st_order_unstable_system(method):
"""First-order unstable continuous-time system"""
s = ct.tf('s')

G2 = 1/(1-s)
assert np.allclose(ct.norm(G2, p='inf'), 1.0) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(G2, p='inf', method=method), 1.0) # Comparison to norm computed in MATLAB
with pytest.warns(UserWarning, match="System is unstable!"):
assert ct.norm(G2, p=2) == float('inf') # Comparison to norm computed in MATLAB
assert ct.norm(G2, p=2, method=method) == float('inf') # Comparison to norm computed in MATLAB

Gd2 = ct.sample_system(G2, 0.1)
assert np.allclose(ct.norm(Gd2, p='inf'), 1.0) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(Gd2, p='inf', method=method), 1.0) # Comparison to norm computed in MATLAB
with pytest.warns(UserWarning, match="System is unstable!"):
assert ct.norm(Gd2, p=2) == float('inf') # Comparison to norm computed in MATLAB
assert ct.norm(Gd2, p=2, method=method) == float('inf') # Comparison to norm computed in MATLAB

def test_norm_2nd_order_system_imag_poles():
@pytest.mark.parametrize("method", [pytest.param("slycot", marks=pytest.mark.slycot), "scipy", None])
def test_norm_2nd_order_system_imag_poles(method):
"""Second-order continuous-time system with poles on imaginary axis"""
s = ct.tf('s')

G3 = 1/(s**2+1)
with pytest.warns(UserWarning, match="Poles close to, or on, the imaginary axis."):
assert ct.norm(G3, p='inf') == float('inf') # Comparison to norm computed in MATLAB
assert ct.norm(G3, p='inf', method=method) == float('inf') # Comparison to norm computed in MATLAB
with pytest.warns(UserWarning, match="Poles close to, or on, the imaginary axis."):
assert ct.norm(G3, p=2) == float('inf') # Comparison to norm computed in MATLAB
assert ct.norm(G3, p=2, method=method) == float('inf') # Comparison to norm computed in MATLAB

Gd3 = ct.sample_system(G3, 0.1)
with pytest.warns(UserWarning, match="Poles close to, or on, the complex unit circle."):
assert ct.norm(Gd3, p='inf') == float('inf') # Comparison to norm computed in MATLAB
assert ct.norm(Gd3, p='inf', method=method) == float('inf') # Comparison to norm computed in MATLAB
with pytest.warns(UserWarning, match="Poles close to, or on, the complex unit circle."):
assert ct.norm(Gd3, p=2) == float('inf') # Comparison to norm computed in MATLAB
assert ct.norm(Gd3, p=2, method=method) == float('inf') # Comparison to norm computed in MATLAB

def test_norm_3rd_order_mimo_system():
@pytest.mark.parametrize("method", [pytest.param("slycot", marks=pytest.mark.slycot), "scipy", None])
def test_norm_3rd_order_mimo_system(method):
"""Third-order stable MIMO continuous-time system"""
A = np.array([[-1.017041847539126, -0.224182952826418, 0.042538079149249],
[-0.310374015319095, -0.516461581407780, -0.119195790221750],
Expand All @@ -66,9 +70,29 @@ def test_norm_3rd_order_mimo_system():
[-0.863652821988714, -1.214117043615409, -0.006849328103348]])
D = np.zeros((2,2))
G4 = ct.ss(A,B,C,D) # Random system generated in MATLAB
assert np.allclose(ct.norm(G4, p='inf'), 4.276759162964244) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(G4, p=2), 2.237461821810309) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(G4, p='inf', method=method), 4.276759162964244) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(G4, p=2, method=method), 2.237461821810309) # Comparison to norm computed in MATLAB

Gd4 = ct.sample_system(G4, 0.1)
assert np.allclose(ct.norm(Gd4, p='inf'), 4.276759162964228) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(Gd4, p=2), 0.707434962289554) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(Gd4, p='inf', method=method), 4.276759162964228) # Comparison to norm computed in MATLAB
assert np.allclose(ct.norm(Gd4, p=2, method=method), 0.707434962289554) # Comparison to norm computed in MATLAB


@pytest.mark.noslycot
def test_sysnorm_no_slycot():
"""Test that sysnorm raises ControlSlycot when slycot is requested but not available"""
s = ct.tf('s')
G = 1/(s+1)

with pytest.raises(ct.ControlSlycot, match="Can't find slycot module ab13bd"):
ct.norm(G, p=2, method='slycot')


def test_norm_invalid_p():
"""Test that norm raises Error for invalid norm request"""
s = ct.tf('s')
G = 1/(s+1)

with pytest.raises(ct.ControlArgument,
match="Norm computation for p=myownnorm currently not supported."):
ct.norm(G, p='myownnorm', method=None)
Loading