def test_discrete(self): """Test discrete time functionality""" # Create some linear and nonlinear systems to play with linsys = ct.StateSpace( [[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[0]], True) lnios = ios.LinearIOSystem(linsys) # Set up parameters for simulation T, U, X0 = self.T, self.U, self.X0 # Simulate and compare to LTI output ios_t, ios_y = ios.input_output_response(lnios, T, U, X0) lin_t, lin_y, lin_x = ct.forced_response(linsys, T, U, X0) np.testing.assert_array_almost_equal(ios_t, lin_t, decimal=3) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) # Test MIMO system, converted to discrete time linsys = ct.StateSpace(self.mimo_linsys1) linsys.dt = self.T[1] - self.T[0] lnios = ios.LinearIOSystem(linsys) # Set up parameters for simulation T = self.T U = [np.sin(T), np.cos(T)] X0 = 0 # Simulate and compare to LTI output ios_t, ios_y = ios.input_output_response(lnios, T, U, X0) lin_t, lin_y, lin_x = ct.forced_response(linsys, T, U, X0) np.testing.assert_array_almost_equal(ios_t, lin_t, decimal=3) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
def test_connect(self): # Define a couple of (linear) systems to interconnection linsys1 = self.siso_linsys iosys1 = ios.LinearIOSystem(linsys1) linsys2 = self.siso_linsys iosys2 = ios.LinearIOSystem(linsys2) # Connect systems in different ways and compare to StateSpace linsys_series = linsys2 * linsys1 iosys_series = ios.InterconnectedSystem( (iosys1, iosys2), # systems ((1, 0),), # interconnection (series) 0, # input = first system 1 # output = second system ) # Run a simulation and compare to linear response T, U = self.T, self.U X0 = np.concatenate((self.X0, self.X0)) ios_t, ios_y, ios_x = ios.input_output_response( iosys_series, T, U, X0, return_x=True) lti_t, lti_y, lti_x = ct.forced_response(linsys_series, T, U, X0) np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) # Connect systems with different timebases linsys2c = self.siso_linsys linsys2c.dt = 0 # Reset the timebase iosys2c = ios.LinearIOSystem(linsys2c) iosys_series = ios.InterconnectedSystem( (iosys1, iosys2c), # systems ((1, 0),), # interconnection (series) 0, # input = first system 1 # output = second system ) self.assertTrue(ct.isctime(iosys_series, strict=True)) ios_t, ios_y, ios_x = ios.input_output_response( iosys_series, T, U, X0, return_x=True) lti_t, lti_y, lti_x = ct.forced_response(linsys_series, T, U, X0) np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3) # Feedback interconnection linsys_feedback = ct.feedback(linsys1, linsys2) iosys_feedback = ios.InterconnectedSystem( (iosys1, iosys2), # systems ((1, 0), # input of sys2 = output of sys1 (0, (1, 0, -1))), # input of sys1 = -output of sys2 0, # input = first system 0 # output = first system ) ios_t, ios_y, ios_x = ios.input_output_response( iosys_feedback, T, U, X0, return_x=True) lti_t, lti_y, lti_x = ct.forced_response(linsys_feedback, T, U, X0) np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
def test_nonsquare_bdalg(self): # Set up parameters for simulation T = self.T U2 = [np.sin(T), np.cos(T)] U3 = [np.sin(T), np.cos(T), T] X0 = 0 # Set up systems to be composed linsys_2i3o = ct.StateSpace( [[-1, 1, 0], [0, -2, 0], [0, 0, -3]], [[1, 0], [0, 1], [1, 1]], [[1, 0, 0], [0, 1, 0], [0, 0, 1]], np.zeros((3, 2))) iosys_2i3o = ios.LinearIOSystem(linsys_2i3o) linsys_3i2o = ct.StateSpace( [[-1, 1, 0], [0, -2, 0], [0, 0, -3]], [[1, 0, 0], [0, 1, 0], [0, 0, 1]], [[1, 0, 1], [0, 1, -1]], np.zeros((2, 3))) iosys_3i2o = ios.LinearIOSystem(linsys_3i2o) # Multiplication linsys_multiply = linsys_3i2o * linsys_2i3o iosys_multiply = iosys_3i2o * iosys_2i3o lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U2, X0) ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U2, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) linsys_multiply = linsys_2i3o * linsys_3i2o iosys_multiply = iosys_2i3o * iosys_3i2o lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U3, X0) ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) # Right multiplication # TODO: add real tests once conversion from other types is supported iosys_multiply = ios.InputOutputSystem.__rmul__(iosys_3i2o, iosys_2i3o) ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) # Feedback linsys_multiply = ct.feedback(linsys_3i2o, linsys_2i3o) iosys_multiply = iosys_3i2o.feedback(iosys_2i3o) lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U3, X0) ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) # Mismatch should generate exception args = (iosys_3i2o, iosys_3i2o) self.assertRaises(ValueError, ct.series, *args)
def test_linearize(self): # Create a single input/single output linear system linsys = self.siso_linsys iosys = ios.LinearIOSystem(linsys) # Linearize it and make sure we get back what we started with linearized = iosys.linearize([0, 0], 0) np.testing.assert_array_almost_equal(linsys.A, linearized.A) np.testing.assert_array_almost_equal(linsys.B, linearized.B) np.testing.assert_array_almost_equal(linsys.C, linearized.C) np.testing.assert_array_almost_equal(linsys.D, linearized.D) # Create a simple nonlinear system to check (kinematic car) def kincar_update(t, x, u, params): return np.array([np.cos(x[2]) * u[0], np.sin(x[2]) * u[0], u[1]]) def kincar_output(t, x, u, params): return np.array([x[0], x[1]]) iosys = ios.NonlinearIOSystem(kincar_update, kincar_output) linearized = iosys.linearize([0, 0, 0], [0, 0]) np.testing.assert_array_almost_equal(linearized.A, np.zeros((3,3))) np.testing.assert_array_almost_equal( linearized.B, [[1, 0], [0, 0], [0, 1]]) np.testing.assert_array_almost_equal( linearized.C, [[1, 0, 0], [0, 1, 0]]) np.testing.assert_array_almost_equal(linearized.D, np.zeros((2,2)))
def test_bdalg_functions(self): """Test block diagram functions algebra on I/O systems""" # Set up parameters for simulation T = self.T U = [np.sin(T), np.cos(T)] X0 = 0 # Set up systems to be composed linsys1 = self.mimo_linsys1 linio1 = ios.LinearIOSystem(linsys1) linsys2 = self.mimo_linsys2 linio2 = ios.LinearIOSystem(linsys2) # Series interconnection linsys_series = ct.series(linsys1, linsys2) iosys_series = ct.series(linio1, linio2) lin_t, lin_y, lin_x = ct.forced_response(linsys_series, T, U, X0) ios_t, ios_y = ios.input_output_response(iosys_series, T, U, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) # Make sure that systems don't commute linsys_series = ct.series(linsys2, linsys1) lin_t, lin_y, lin_x = ct.forced_response(linsys_series, T, U, X0) self.assertFalse((np.abs(lin_y - ios_y) < 1e-3).all()) # Parallel interconnection linsys_parallel = ct.parallel(linsys1, linsys2) iosys_parallel = ct.parallel(linio1, linio2) lin_t, lin_y, lin_x = ct.forced_response(linsys_parallel, T, U, X0) ios_t, ios_y = ios.input_output_response(iosys_parallel, T, U, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) # Negation linsys_negate = ct.negate(linsys1) iosys_negate = ct.negate(linio1) lin_t, lin_y, lin_x = ct.forced_response(linsys_negate, T, U, X0) ios_t, ios_y = ios.input_output_response(iosys_negate, T, U, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3) # Feedback interconnection linsys_feedback = ct.feedback(linsys1, linsys2) iosys_feedback = ct.feedback(linio1, linio2) lin_t, lin_y, lin_x = ct.forced_response(linsys_feedback, T, U, X0) ios_t, ios_y = ios.input_output_response(iosys_feedback, T, U, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
def test_feedback(self): # Set up parameters for simulation T, U, X0 = self.T, self.U, self.X0 # Linear system with constant feedback (via "nonlinear" mapping) ioslin = ios.LinearIOSystem(self.siso_linsys) nlios = ios.NonlinearIOSystem(None, \ lambda t, x, u, params: u, inputs=1, outputs=1) iosys = ct.feedback(ioslin, nlios) linsys = ct.feedback(self.siso_linsys, 1) ios_t, ios_y = ios.input_output_response(iosys, T, U, X0) lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0) np.testing.assert_array_almost_equal(ios_y, lti_y, decimal=3)
def test_summer(self): # Construct a MIMO system for testing linsys = self.mimo_linsys1 linio = ios.LinearIOSystem(linsys) linsys_parallel = linsys + linsys iosys_parallel = linio + linio # Set up parameters for simulation T = self.T U = [np.sin(T), np.cos(T)] X0 = 0 lin_t, lin_y, lin_x = ct.forced_response(linsys_parallel, T, U, X0) ios_t, ios_y = ios.input_output_response(iosys_parallel, T, U, X0) np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
def test_linear_iosys(self): # Create an input/output system from the linear system linsys = self.siso_linsys iosys = ios.LinearIOSystem(linsys) # 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)): np.testing.assert_array_almost_equal( np.reshape(iosys._rhs(0, x, u), (-1,1)), linsys.A * np.reshape(x, (-1, 1)) + linsys.B * u) # Make sure that simulations also line up T, U, X0 = self.T, self.U, self.X0 lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0) ios_t, ios_y = ios.input_output_response(iosys, T, U, X0) np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
def test_rmul(self): # Test right multiplication # TODO: replace with better tests when conversions are implemented # Set up parameters for simulation T, U, X0 = self.T, self.U, self.X0 # Linear system with input and output nonlinearities # Also creates a nested interconnected system ioslin = ios.LinearIOSystem(self.siso_linsys) nlios = ios.NonlinearIOSystem(None, \ lambda t, x, u, params: u*u, inputs=1, outputs=1) sys1 = nlios * ioslin sys2 = ios.InputOutputSystem.__rmul__(nlios, sys1) # Make sure we got the right thing (via simulation comparison) ios_t, ios_y = ios.input_output_response(sys2, T, U, X0) lti_t, lti_y, lti_x = ct.forced_response(ioslin, T, U*U, X0) np.testing.assert_array_almost_equal(ios_y, lti_y*lti_y, decimal=3)
def test_neg(self): """Test negation of a system""" # Set up parameters for simulation T, U, X0 = self.T, self.U, self.X0 # Static nonlinear system nlios = ios.NonlinearIOSystem(None, \ lambda t, x, u, params: u*u, inputs=1, outputs=1) ios_t, ios_y = ios.input_output_response(-nlios, T, U, X0) np.testing.assert_array_almost_equal(ios_y, -U*U, decimal=3) # Linear system with input nonlinearity # Also creates a nested interconnected system ioslin = ios.LinearIOSystem(self.siso_linsys) sys = (ioslin) * (-nlios) # Make sure we got the right thing (via simulation comparison) ios_t, ios_y = ios.input_output_response(sys, T, U, X0) lti_t, lti_y, lti_x = ct.forced_response(ioslin, T, U*U, X0) np.testing.assert_array_almost_equal(ios_y, -lti_y, decimal=3)
def test_static_nonlinearity(self): # Linear dynamical system linsys = self.siso_linsys ioslin = ios.LinearIOSystem(linsys) # Nonlinear saturation sat = lambda u: u if abs(u) < 1 else np.sign(u) sat_output = lambda t, x, u, params: sat(u) nlsat = ios.NonlinearIOSystem(None, sat_output, inputs=1, outputs=1) # Set up parameters for simulation T, U, X0 = self.T, 2 * self.U, self.X0 Usat = np.vectorize(sat)(U) # Make sure saturation works properly by comparing linear system with # saturated input to nonlinear system with saturation composition lti_t, lti_y, lti_x = ct.forced_response(linsys, T, Usat, X0) ios_t, ios_y, ios_x = ios.input_output_response( ioslin * nlsat, T, U, X0, return_x=True) np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=2)
def test_named_signals(self): sys1 = ios.NonlinearIOSystem( updfcn = lambda t, x, u, params: np.array( np.dot(self.mimo_linsys1.A, np.reshape(x, (-1, 1))) \ + np.dot(self.mimo_linsys1.B, np.reshape(u, (-1, 1))) ).reshape(-1,), outfcn = lambda t, x, u, params: np.array( self.mimo_linsys1.C * np.reshape(x, (-1, 1)) \ + self.mimo_linsys1.D * np.reshape(u, (-1, 1)) ).reshape(-1,), inputs = ('u[0]', 'u[1]'), outputs = ('y[0]', 'y[1]'), states = self.mimo_linsys1.states, name = 'sys1') sys2 = ios.LinearIOSystem(self.mimo_linsys2, inputs = ('u[0]', 'u[1]'), outputs = ('y[0]', 'y[1]'), name = 'sys2') # Series interconnection (sys1 * sys2) using __mul__ ios_mul = sys1 * sys2 ss_series = self.mimo_linsys1 * self.mimo_linsys2 lin_series = ct.linearize(ios_mul, 0, 0) for M, N in ((ss_series.A, lin_series.A), (ss_series.B, lin_series.B), (ss_series.C, lin_series.C), (ss_series.D, lin_series.D)): np.testing.assert_array_almost_equal(M, N) # Series interconnection (sys1 * sys2) using series ios_series = ct.series(sys2, sys1) ss_series = ct.series(self.mimo_linsys2, self.mimo_linsys1) lin_series = ct.linearize(ios_series, 0, 0) for M, N in ((ss_series.A, lin_series.A), (ss_series.B, lin_series.B), (ss_series.C, lin_series.C), (ss_series.D, lin_series.D)): np.testing.assert_array_almost_equal(M, N) # Series interconnection (sys1 * sys2) using named + mixed signals ios_connect = ios.InterconnectedSystem( (sys2, sys1), connections=( (('sys1', 'u[0]'), 'sys2.y[0]'), ('sys1.u[1]', 'sys2.y[1]') ), inplist=('sys2.u[0]', ('sys2', 1)), outlist=((1, 'y[0]'), 'sys1.y[1]') ) lin_series = ct.linearize(ios_connect, 0, 0) for M, N in ((ss_series.A, lin_series.A), (ss_series.B, lin_series.B), (ss_series.C, lin_series.C), (ss_series.D, lin_series.D)): np.testing.assert_array_almost_equal(M, N) # Make sure that we can use input signal names as system outputs ios_connect = ios.InterconnectedSystem( (sys1, sys2), connections=( ('sys2.u[0]', 'sys1.y[0]'), ('sys2.u[1]', 'sys1.y[1]'), ('sys1.u[0]', '-sys2.y[0]'), ('sys1.u[1]', '-sys2.y[1]') ), inplist=('sys1.u[0]', 'sys1.u[1]'), outlist=('sys2.u[0]', 'sys2.u[1]') # = sys1.y[0], sys1.y[1] ) ss_feedback = ct.feedback(self.mimo_linsys1, self.mimo_linsys2) lin_feedback = ct.linearize(ios_connect, 0, 0) np.testing.assert_array_almost_equal(ss_feedback.A, lin_feedback.A) np.testing.assert_array_almost_equal(ss_feedback.B, lin_feedback.B) np.testing.assert_array_almost_equal(ss_feedback.C, lin_feedback.C) np.testing.assert_array_almost_equal(ss_feedback.D, lin_feedback.D)
def test_params(self): # Start with the default set of parameters ios_secord_default = ios.NonlinearIOSystem( secord_update, secord_output, inputs=1, outputs=1, states=2) lin_secord_default = ios.linearize(ios_secord_default, [0, 0], [0]) w_default, v_default = np.linalg.eig(lin_secord_default.A) # New copy, with modified parameters ios_secord_update = ios.NonlinearIOSystem( secord_update, secord_output, inputs=1, outputs=1, states=2, params={'omega0':2, 'zeta':0}) # Make sure the default parameters haven't changed lin_secord_check = ios.linearize(ios_secord_default, [0, 0], [0]) w, v = np.linalg.eig(lin_secord_check.A) np.testing.assert_array_almost_equal(np.sort(w), np.sort(w_default)) # Make sure updated system parameters got set correctly lin_secord_update = ios.linearize(ios_secord_update, [0, 0], [0]) w, v = np.linalg.eig(lin_secord_update.A) np.testing.assert_array_almost_equal(np.sort(w), np.sort([2j, -2j])) # Change the parameters of the default sys just for the linearization lin_secord_local = ios.linearize(ios_secord_default, [0, 0], [0], params={'zeta':0}) w, v = np.linalg.eig(lin_secord_local.A) np.testing.assert_array_almost_equal(np.sort(w), np.sort([1j, -1j])) # Change the parameters of the updated sys just for the linearization lin_secord_local = ios.linearize(ios_secord_update, [0, 0], [0], params={'zeta':0, 'omega0':3}) w, v = np.linalg.eig(lin_secord_local.A) np.testing.assert_array_almost_equal(np.sort(w), np.sort([3j, -3j])) # Make sure that changes propagate through interconnections ios_series_default_local = ios_secord_default * ios_secord_update lin_series_default_local = ios.linearize( ios_series_default_local, [0, 0, 0, 0], [0]) w, v = np.linalg.eig(lin_series_default_local.A) np.testing.assert_array_almost_equal( np.sort(w), np.sort(np.concatenate((w_default, [2j, -2j])))) # Show that we can change the parameters at linearization lin_series_override = ios.linearize( ios_series_default_local, [0, 0, 0, 0], [0], params={'zeta':0, 'omega0':4}) w, v = np.linalg.eig(lin_series_override.A) np.testing.assert_array_almost_equal(w, [4j, -4j, 4j, -4j]) # Check for warning if we try to set params for LinearIOSystem linsys = self.siso_linsys iosys = ios.LinearIOSystem(linsys) T, U, X0 = self.T, self.U, self.X0 lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0) with warnings.catch_warnings(record=True) as warnval: # Turn off deprecation warnings warnings.simplefilter("ignore", category=DeprecationWarning) warnings.simplefilter("ignore", category=PendingDeprecationWarning) # Trigger a warning ios_t, ios_y = ios.input_output_response( iosys, T, U, X0, params={'something':0}) # Verify that we got a warning self.assertEqual(len(warnval), 1) self.assertTrue(issubclass(warnval[-1].category, UserWarning)) self.assertTrue("LinearIOSystem" in str(warnval[-1].message)) self.assertTrue("ignored" in str(warnval[-1].message)) # Check to make sure results are OK np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
def test_find_eqpts(self): """Test find_eqpt function""" # Simple equilibrium point with no inputs nlsys = ios.NonlinearIOSystem(predprey) xeq, ueq, result = ios.find_eqpt( nlsys, [1.6, 1.2], None, return_result=True) self.assertTrue(result.success) np.testing.assert_array_almost_equal(xeq, [1.64705879, 1.17923874]) np.testing.assert_array_almost_equal( nlsys._rhs(0, xeq, ueq), np.zeros((2,))) # Ducted fan dynamics with output = velocity nlsys = ios.NonlinearIOSystem(pvtol, lambda t, x, u, params: x[0:2]) # Make sure the origin is a fixed point xeq, ueq, result = ios.find_eqpt( nlsys, [0, 0, 0, 0], [0, 4*9.8], return_result=True) self.assertTrue(result.success) np.testing.assert_array_almost_equal( nlsys._rhs(0, xeq, ueq), np.zeros((4,))) np.testing.assert_array_almost_equal(xeq, [0, 0, 0, 0]) # Use a small lateral force to cause motion xeq, ueq, result = ios.find_eqpt( nlsys, [0, 0, 0, 0], [0.01, 4*9.8], return_result=True) self.assertTrue(result.success) np.testing.assert_array_almost_equal( nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5) # Equilibrium point with fixed output xeq, ueq, result = ios.find_eqpt( nlsys, [0, 0, 0, 0], [0.01, 4*9.8], y0=[0.1, 0.1], return_result=True) self.assertTrue(result.success) np.testing.assert_array_almost_equal( nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5) np.testing.assert_array_almost_equal( nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5) # Specify outputs to constrain (replicate previous) xeq, ueq, result = ios.find_eqpt( nlsys, [0, 0, 0, 0], [0.01, 4*9.8], y0=[0.1, 0.1], iy = [0, 1], return_result=True) self.assertTrue(result.success) np.testing.assert_array_almost_equal( nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5) np.testing.assert_array_almost_equal( nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5) # Specify inputs to constrain (replicate previous), w/ no result xeq, ueq = ios.find_eqpt( nlsys, [0, 0, 0, 0], [0.01, 4*9.8], y0=[0.1, 0.1], iu = []) np.testing.assert_array_almost_equal( nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5) np.testing.assert_array_almost_equal( nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5) # Now solve the problem with the original PVTOL variables # Constrain the output angle and x velocity nlsys_full = ios.NonlinearIOSystem(pvtol_full, None) xeq, ueq, result = ios.find_eqpt( nlsys_full, [0, 0, 0, 0, 0, 0], [0.01, 4*9.8], y0=[0, 0, 0.1, 0.1, 0, 0], iy = [2, 3], idx=[2, 3, 4, 5], ix=[0, 1], return_result=True) self.assertTrue(result.success) np.testing.assert_array_almost_equal( nlsys_full._out(0, xeq, ueq)[[2, 3]], [0.1, 0.1], decimal=5) np.testing.assert_array_almost_equal( nlsys_full._rhs(0, xeq, ueq)[-4:], np.zeros((4,)), decimal=5) # Fix one input and vary the other nlsys_full = ios.NonlinearIOSystem(pvtol_full, None) xeq, ueq, result = ios.find_eqpt( nlsys_full, [0, 0, 0, 0, 0, 0], [0.01, 4*9.8], y0=[0, 0, 0.1, 0.1, 0, 0], iy=[3], iu=[1], idx=[2, 3, 4, 5], ix=[0, 1], return_result=True) self.assertTrue(result.success) np.testing.assert_almost_equal(ueq[1], 4*9.8, decimal=5) np.testing.assert_array_almost_equal( nlsys_full._out(0, xeq, ueq)[[3]], [0.1], decimal=5) np.testing.assert_array_almost_equal( nlsys_full._rhs(0, xeq, ueq)[-4:], np.zeros((4,)), decimal=5) # PVTOL with output = y velocity xeq, ueq, result = ios.find_eqpt( nlsys_full, [0, 0, 0, 0.1, 0, 0], [0.01, 4*9.8], y0=[0, 0, 0, 0.1, 0, 0], iy=[3], dx0=[0.1, 0, 0, 0, 0, 0], idx=[1, 2, 3, 4, 5], ix=[0, 1], return_result=True) self.assertTrue(result.success) np.testing.assert_array_almost_equal( nlsys_full._out(0, xeq, ueq)[-3:], [0.1, 0, 0], decimal=5) np.testing.assert_array_almost_equal( nlsys_full._rhs(0, xeq, ueq)[-5:], np.zeros((5,)), decimal=5) # Unobservable system linsys = ct.StateSpace( [[-1, 1], [0, -2]], [[0], [1]], [[0, 0]], [[0]]) lnios = ios.LinearIOSystem(linsys) # If result is returned, user has to check xeq, ueq, result = ios.find_eqpt( lnios, [0, 0], [0], y0=[1], return_result=True) self.assertFalse(result.success) # If result is not returned, find_eqpt should return None xeq, ueq = ios.find_eqpt(lnios, [0, 0], [0], y0=[1]) self.assertEqual(xeq, None) self.assertEqual(ueq, None)
def test_algebraic_loop(self): # Create some linear and nonlinear systems to play with linsys = self.siso_linsys lnios = ios.LinearIOSystem(linsys) nlios = ios.NonlinearIOSystem(None, \ lambda t, x, u, params: u*u, inputs=1, outputs=1) nlios1 = nlios.copy() nlios2 = nlios.copy() # Set up parameters for simulation T, U, X0 = self.T, self.U, self.X0 # Single nonlinear system - no states ios_t, ios_y = ios.input_output_response(nlios, T, U, X0) np.testing.assert_array_almost_equal(ios_y, U*U, decimal=3) # Composed nonlinear system (series) ios_t, ios_y = ios.input_output_response(nlios1 * nlios2, T, U, X0) np.testing.assert_array_almost_equal(ios_y, U**4, decimal=3) # Composed nonlinear system (parallel) ios_t, ios_y = ios.input_output_response(nlios1 + nlios2, T, U, X0) np.testing.assert_array_almost_equal(ios_y, 2*U**2, decimal=3) # Nonlinear system composed with LTI system (series) ios_t, ios_y = ios.input_output_response( nlios * lnios * nlios, T, U, X0) lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U*U, X0) np.testing.assert_array_almost_equal(ios_y, lti_y*lti_y, decimal=3) # Nonlinear system in feeback loop with LTI system iosys = ios.InterconnectedSystem( (lnios, nlios), # linear system w/ nonlinear feedback ((1,), # feedback interconnection (sig to 0) (0, (1, 0, -1))), 0, # input to linear system 0 # output from linear system ) ios_t, ios_y = ios.input_output_response(iosys, T, U, X0) # No easy way to test the result # Algebraic loop from static nonlinear system in feedback # (error will be due to no states) iosys = ios.InterconnectedSystem( (nlios1, nlios2), # two copies of a static nonlinear system ((0, 1), # feedback interconnection (1, (0, 0, -1))), 0, 0 ) args = (iosys, T, U, X0) self.assertRaises(RuntimeError, ios.input_output_response, *args) # Algebraic loop due to feedthrough term linsys = ct.StateSpace( [[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[1]]) lnios = ios.LinearIOSystem(linsys) iosys = ios.InterconnectedSystem( (nlios, lnios), # linear system w/ nonlinear feedback ((0, 1), # feedback interconnection (1, (0, 0, -1))), 0, 0 ) args = (iosys, T, U, X0) # ios_t, ios_y = ios.input_output_response(iosys, T, U, X0) self.assertRaises(RuntimeError, ios.input_output_response, *args)