From c425d2c9312af16eb49c13222f81ce0e94ff7b16 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Sat, 12 Nov 2022 22:03:18 -0800 Subject: [PATCH] fix _isstatic() to use nstates==0 --- control/statesp.py | 10 ++-------- control/tests/iosys_test.py | 10 +++++++++- control/tests/statesp_test.py | 33 ++++++++++++++------------------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/control/statesp.py b/control/statesp.py index 374b036ca..ca14a5690 100644 --- a/control/statesp.py +++ b/control/statesp.py @@ -326,7 +326,7 @@ def __init__(self, *args, init_namedio=True, **kwargs): D = np.zeros((C.shape[0], B.shape[1])) D = _ssmatrix(D) - # Matrices definining the linear system + # Matrices defining the linear system self.A = A self.B = B self.C = C @@ -346,9 +346,8 @@ def __init__(self, *args, init_namedio=True, **kwargs): defaults = args[0] if len(args) == 1 else \ {'inputs': D.shape[1], 'outputs': D.shape[0], 'states': A.shape[0]} - static = (A.size == 0) name, inputs, outputs, states, dt = _process_namedio_keywords( - kwargs, defaults, static=static, end=True) + kwargs, defaults, static=(A.size == 0), end=True) # Initialize LTI (NamedIOSystem) object super().__init__( @@ -1478,11 +1477,6 @@ def output(self, t, x, u=None): return (self.C @ x).reshape((-1,)) \ + (self.D @ u).reshape((-1,)) # return as row vector - def _isstatic(self): - """True if and only if the system has no dynamics, that is, - if A and B are zero. """ - return not np.any(self.A) and not np.any(self.B) - # TODO: add discrete time check def _convert_to_statespace(sys): diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py index 693be979e..8a6ed8165 100644 --- a/control/tests/iosys_test.py +++ b/control/tests/iosys_test.py @@ -41,6 +41,9 @@ class TSys: [[-1, 1], [0, -2]], [[0, 1], [1, 0]], [[1, 0], [0, 1]], np.zeros((2, 2))) + # Create a static gain linear system + T.staticgain = ct.StateSpace([], [], [], 1) + # Create simulation parameters T.T = np.linspace(0, 10, 100) T.U = np.sin(T.T) @@ -51,7 +54,7 @@ class TSys: def test_linear_iosys(self, tsys): # Create an input/output system from the linear system linsys = tsys.siso_linsys - iosys = ios.LinearIOSystem(linsys) + iosys = ios.LinearIOSystem(linsys).copy() # Make sure that the right hand side matches linear system for x, u in (([0, 0], 0), ([1, 0], 0), ([0, 1], 0), ([0, 0], 1)): @@ -66,6 +69,11 @@ def test_linear_iosys(self, tsys): np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_allclose(lti_y, ios_y, atol=0.002, rtol=0.) + # Make sure that a static linear system has dt=None + # and otherwise dt is as specified + assert ios.LinearIOSystem(tsys.staticgain).dt is None + assert ios.LinearIOSystem(tsys.staticgain, dt=.1).dt == .1 + def test_tf2io(self, tsys): # Create a transfer function from the state space system linsys = tsys.siso_linsys diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index 97dc84e3c..5fe7d36af 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -399,25 +399,6 @@ def test_freq_resp(self): mag, phase, omega = sys.freqresp(true_omega) np.testing.assert_almost_equal(mag, true_mag) - def test__isstatic(self): - A0 = np.zeros((2,2)) - A1 = A0.copy() - A1[0,1] = 1.1 - B0 = np.zeros((2,1)) - B1 = B0.copy() - B1[0,0] = 1.3 - C0 = A0 - C1 = np.eye(2) - D0 = 0 - D1 = np.ones((2,1)) - assert StateSpace(A0, B0, C1, D1)._isstatic() - assert not StateSpace(A1, B0, C1, D1)._isstatic() - assert not StateSpace(A0, B1, C1, D1)._isstatic() - assert not StateSpace(A1, B1, C1, D1)._isstatic() - assert StateSpace(A0, B0, C0, D0)._isstatic() - assert StateSpace(A0, B0, C0, D1)._isstatic() - assert StateSpace(A0, B0, C1, D0)._isstatic() - @slycotonly def test_minreal(self): """Test a minreal model reduction.""" @@ -1158,3 +1139,17 @@ def test_linfnorm_ct_mimo(self, ct_siso): gpeak, fpeak = linfnorm(sys) np.testing.assert_allclose(gpeak, refgpeak) np.testing.assert_allclose(fpeak, reffpeak) + +@pytest.mark.parametrize("args, static", [ + (([], [], [], 1), True), # ctime, empty state + (([], [], [], 1, 1), True), # dtime, empty state + ((0, 0, 0, 1), False), # ctime, unused state + ((-1, 0, 0, 1), False), # ctime, exponential decay + ((-1, 0, 0, 0), False), # ctime, no input, no output + ((0, 0, 0, 1, 1), False), # dtime, integrator + ((1, 0, 0, 1, 1), False), # dtime, unused state + ((0, 0, 0, 1, None), False), # unspecified, unused state +]) +def test_isstatic(args, static): + sys = ct.StateSpace(*args) + assert sys._isstatic() == static