def assert_step_info_match(self, sys, info, info_ref): """Assert reasonable step_info accuracy.""" if sys.isdtime(strict=True): dt = sys.dt else: _, dt = _ideal_tfinal_and_dt(sys, is_step=True) for k in ['RiseTime', 'SettlingTime', 'PeakTime']: np.testing.assert_allclose(info[k], info_ref[k], atol=dt, err_msg=f"{k} does not match") for k in ['Overshoot', 'Undershoot', 'Peak', 'SteadyStateValue']: np.testing.assert_allclose(info[k], info_ref[k], rtol=5e-3, err_msg=f"{k} does not match") # steep gradient right after RiseTime absrefinf = np.abs(info_ref['SteadyStateValue']) if info_ref['RiseTime'] > 0: y_next_sample_max = 0.8 * absrefinf / info_ref['RiseTime'] * dt else: y_next_sample_max = 0 for k in ['SettlingMin', 'SettlingMax']: if (np.abs(info_ref[k]) - 0.9 * absrefinf) > y_next_sample_max: # local min/max peak well after signal has risen np.testing.assert_allclose(info[k], info_ref[k], rtol=1e-3)
def test_auto_generated_time_vector_dt_cont(self, wn, zeta): """Confirm a TF with a natural frequency of wn rad/s gets a dt of 1/(ratio*wn)""" dtref = 0.25133 / wn tfsys = TransferFunction(1, [1, 2*zeta*wn, wn**2]) np.testing.assert_almost_equal(_ideal_tfinal_and_dt(tfsys)[1], dtref)
def test_auto_generated_time_vector_dt_cont2(self): """A sampled tf keeps its dt""" wn = 100 zeta = .1 tfsys = TransferFunction(1, [1, 2*zeta*wn, wn**2]).sample(.1) tfinal, dt = _ideal_tfinal_and_dt(tfsys) np.testing.assert_almost_equal(dt, .1) T, _ = initial_response(tfsys) np.testing.assert_almost_equal(np.diff(T[:2]), [.1])
def test_default_timevector_functions_c(self, fun): """Test that functions can calculate the time vector automatically""" sys = TransferFunction(1, [1, .5, 0]) _tfinal, _dt = _ideal_tfinal_and_dt(sys) # test impose number of time steps tout, _ = fun(sys, T_num=10) assert len(tout) == 10 # test impose final time tout, _ = fun(sys, T=100.) np.testing.assert_allclose(tout[-1], 100., atol=0.5*_dt)
def test_auto_generated_time_vector_tfinal(self, tfsys, tfinal): """Confirm a TF with a pole at p simulates for tfinal seconds""" ideal_tfinal, ideal_dt = _ideal_tfinal_and_dt(tfsys) np.testing.assert_allclose(ideal_tfinal, tfinal, rtol=1e-4) T = _default_time_vector(tfsys) np.testing.assert_allclose(T[-1], tfinal, atol=0.5*ideal_dt)
def test_auto_generated_time_vector_tfinal(self, tfsys, tfinal): """Confirm a TF with a pole at p simulates for tfinal seconds""" np.testing.assert_almost_equal( _ideal_tfinal_and_dt(tfsys)[0], tfinal, decimal=4)
def test_auto_generated_time_vector(self): # confirm a TF with a pole at p simulates for ratio/p seconds p = 0.5 ratio = 9.21034*p # taken from code ratio2 = 25*p np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, .5]))[0], (ratio/p)) np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, .5]).sample(.1))[0], (ratio2/p)) # confirm a TF with poles at 0 and p simulates for ratio/p seconds np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, .5, 0]))[0], (ratio2/p)) # confirm a TF with a natural frequency of wn rad/s gets a # dt of 1/(ratio*wn) wn = 10 ratio_dt = 1/(0.025133 * ratio * wn) np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, 0, wn**2]))[1], 1/(ratio_dt*ratio*wn)) wn = 100 np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, 0, wn**2]))[1], 1/(ratio_dt*ratio*wn)) zeta = .1 np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, 2*zeta*wn, wn**2]))[1], 1/(ratio_dt*ratio*wn)) # but a smapled one keeps its dt np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, 2*zeta*wn, wn**2]).sample(.1))[1], .1) np.testing.assert_array_almost_equal( np.diff(initial_response(TransferFunction(1, [1, 2*zeta*wn, wn**2]).sample(.1))[0][0:2]), .1) np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, 2*zeta*wn, wn**2]))[1], 1/(ratio_dt*ratio*wn)) # TF with fast oscillations simulates only 5000 time steps even with long tfinal self.assertEqual(5000, len(_default_time_vector(TransferFunction(1, [1, 0, wn**2]),tfinal=100))) sys = TransferFunction(1, [1, .5, 0]) sysdt = TransferFunction(1, [1, .5, 0], .1) # test impose number of time steps self.assertEqual(10, len(step_response(sys, T_num=10)[0])) # test that discrete ignores T_num self.assertNotEqual(15, len(step_response(sysdt, T_num=15)[0])) # test impose final time np.testing.assert_array_almost_equal( 100, np.ceil(step_response(sys, 100)[0][-1])) np.testing.assert_array_almost_equal( 100, np.ceil(step_response(sysdt, 100)[0][-1])) np.testing.assert_array_almost_equal( 100, np.ceil(impulse_response(sys, 100)[0][-1])) np.testing.assert_array_almost_equal( 100, np.ceil(initial_response(sys, 100)[0][-1]))
def test_auto_generated_time_vector(self): # confirm a TF with a pole at p simulates for 7.0/p seconds p = 0.5 np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, .5]))[0], (7 / p)) np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, .5]).sample(.1))[0], (7 / p)) # confirm a TF with poles at 0 and p simulates for 7.0/p seconds np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, .5, 0]))[0], (7 / p)) # confirm a TF with a natural frequency of wn rad/s gets a # dt of 1/(7.0*wn) wn = 10 np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction(1, [1, 0, wn**2]))[1], 1 / (7.0 * wn)) zeta = .1 np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction( 1, [1, 2 * zeta * wn, wn**2]))[1], 1 / (7.0 * wn)) # but a smapled one keeps its dt np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt( TransferFunction(1, [1, 2 * zeta * wn, wn**2]).sample(.1))[1], .1) np.testing.assert_array_almost_equal( np.diff( initial_response( TransferFunction( 1, [1, 2 * zeta * wn, wn**2]).sample(.1))[0][0:2]), .1) np.testing.assert_array_almost_equal( _ideal_tfinal_and_dt(TransferFunction( 1, [1, 2 * zeta * wn, wn**2]))[1], 1 / (7.0 * wn)) # TF with fast oscillations simulates only 5000 time steps even with long tfinal self.assertEqual( 5000, len( _default_time_vector(TransferFunction(1, [1, 0, wn**2]), tfinal=100))) # and simulates for 7.0/dt time steps self.assertEqual( len(_default_time_vector(TransferFunction(1, [1, 0, wn**2]))), int(7.0 / (1 / (7.0 * wn)))) sys = TransferFunction(1, [1, .5, 0]) sysdt = TransferFunction(1, [1, .5, 0], .1) # test impose number of time steps self.assertEqual(10, len(step_response(sys, T_num=10)[0])) self.assertEqual(10, len(step_response(sysdt, T_num=10)[0])) # test impose final time np.testing.assert_array_almost_equal(100, step_response(sys, 100)[0][-1], decimal=.5) np.testing.assert_array_almost_equal(100, step_response(sysdt, 100)[0][-1], decimal=.5) np.testing.assert_array_almost_equal(100, impulse_response(sys, 100)[0][-1], decimal=.5) np.testing.assert_array_almost_equal(100, initial_response(sys, 100)[0][-1], decimal=.5)