def test_static_model(self): """Tests for static_model function.""" # Define constants y_list = [0, 1, -1, 0.1, -2.5, 100, 1000, -10000.1] K_list = [1, 10, -2] offset_list = [0, 1, -2] mean = 0 std_list = [0.01, 0.03, 0.1, 0.3, 1, 3, 10] param_list = set(itertools.chain(*[zip(x, offset_list) for x in itertools.permutations(K_list, len(offset_list))])) # Test the default function implementation for y in y_list: for K, offset in param_list: self.assertEqual(sim_functions.static_model(y, K, offset), K * y + offset, "Default static_model does not return expected value for params y={}, K={}, offset={}".format(y, K, offset)) # Test the function with Gaussian noise for y in y_list: for K, offset in param_list: for std in std_list: self.assertNotEqual(sim_functions.static_model(y, K, offset, mean, std), K * y + offset, "Static model response not showing noise effect.") # Test that list and string arguments raise a TypeError with self.assertRaises(TypeError): sim_functions.static_model([1, 2], 1, 1) sim_functions.static_model(1, [1, 2], 2) sim_functions.static_model(1, 2, [1, 2])
def test_static_sensor_class(self): """Tests for the lab_hardware.StaticSensor class.""" # Initialize constants P_list = [0, 1, 2, 3, 4, 5, 14.0, -1.1, -2] # actual pressures to read # Test initialization self.assertIsInstance( self.test_sensor, lab_hardware.StaticSensor, "Test static sensor not initialized to StaticSensor class.") self.assertEqual(self.test_sensor.get_output(), 0.0, "Sensor does not return expected initial reading.") self.assertEqual( self.test_sensor.get_location(), 1.0, "Sensor object does not return the correct location.") # Test readings with the sensor for P_actual in P_list: expected = sim_functions.static_model(P_actual, self.Ks, self.offset) self.assertNotEqual( self.test_sensor.update(P_actual), expected, "Measured pressure with noise equals value without noise. Noise may not be working." ) self.assertAlmostEqual( self.test_sensor.get_output(), expected, delta=4 * self.std, msg="Stored sensor state >3*std away from expected state.")
def setUp(self): """Run repeated setup code once.""" # Define class and local parameters unittest.TestCase.setUp(self) self.K_list = [3, 10, 99.21] self.tau_list = [0.3, 1] self.delay_list = [0.03, 0.1, 0.3] self.t_step = 0.01 self.stop_time = 20.0 self.input_val = 1.0 self.t_step_ctrl = 0.1 self.zeta = 0.7054 # air MFC damping ratio self.wn = 1.3501 # air MFC natural frequency, rad/s self.td = 1.8 # air MFC time delay, sec self.y0_1 = [0] self.y0_2 = [0] * 3 # for first-order model self.y0_3 = [0] * 4 # for second-order model A, B, self.C = sim_functions.get_state_matrices( self.K_list[1], self.tau_list[-1], self.delay_list[0]) A2, B2, self.C2 = sim_functions.get_second_ord_matrices( self.K_list[-1], self.zeta, self.wn, self.td) self.Kp_list = [4.0, 5.0] self.mass_flow_des = [4.0, 2.0] self.std = 1.0 self.Ks = 2.0 # filter static gain (?) Q = 1e-5 * np.identity(A2.shape[0]) # process noise covariance R = self.std # measurement noise covariance self.P = Q # initial estimate of error covariance self.offset = 0.0 mean = 0.0 # Define objects self.test_mfc1 = lab_hardware.MFC( lambda t, y, u: test_ode(t, y, u, self.K_list[1], self.tau_list[-1] ), lambda x: x[0], self.y0_1) self.test_mfc2 = lab_hardware.MFC( lambda t, y, u: sim_functions.system_state_update(t, y, u, A, B), lambda y: sim_functions.system_output(y, self.C), self.y0_2) self.test_mfc3 = lab_hardware.MFC( lambda t, y, u: sim_functions.system_state_update(t, y, u, A2, B2), lambda y: sim_functions.system_output(y, self.C2), self.y0_3) self.mfc_list = [ lab_hardware.MFC(lambda t, y, u: test_ode(t, y, u, K, tau), lambda x: x[0], self.y0_1) for K, tau in zip(self.K_list, self.tau_list) ] self.test_KF = lab_hardware.KalmanFilter(A2, B2, self.Ks * self.C2, Q, R, self.P) self.test_sensor = lab_hardware.StaticSensor( lambda y: sim_functions.static_model(y, self.Ks, self.offset, mean, self.std), 1.0)
def run_simulation(): """Run a simulation of the controller.""" # Define system parameters p_atm = 14.0 # psi t_step = 0.01 # simulation time step, seconds t_step_ctrl = 0.1 # controller update rate, seconds K = 0.1 # static pressure sensor gain, V/psi (?) offset = 1.0 # static pressure sensor offset, V (?) K_mfcs = [99.21, 7.968, 8.007] # MFC gains: [air, pilot, outer], SLPM/V zeta_mfcs = [0.7054, 0.6, 0.6] # MFC damping ratios: [air, pilot, outer] wn_mfcs = [1.3501, 1, 1] # MFC natural frequencies: [air, pilot, outer], rad/sec td_mfcs = [1.8, 0.139, 0.306] # MFC time delays: [air, pilot, outer], sec K_mid = 7.964 # middle fuel MFC static gain, SLPM/V tau_mid = 0.516 # middle fuel MFC time constant, sec (first-order) td_mid = 0.194 # middle fuel MFC time delay, sec y0 = [0.0, 0.0, 0.0, 0.0] # initial value, 2nd order sys with 3,2 order Pade approximation of time delay mfc_list = [] control_law_list = [] Q = np.ndarray([[100, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0] [0, 0, 0, 1]]) R = np.ndarray([[1]]) sensor_locations = [43.81, 76.2, 236.728, 609.6] # dynamic P sensor locations from base, mm # Initialize sensor list sensor_list = [lab_hardware.StaticSensor(model=lambda y: sim_functions.static_model(y, K, offset), location=0.0)] for loc in sensor_locations: sensor_list.append(lab_hardware.DynamicSensor(model=tf1, location=loc)) # TODO figure out sensor transfer functions or models! # Initialize mass flow controller (MFC) list and control law list for K, zeta, wn, td in zip(K_mfcs, zeta_mfcs, wn_mfcs, td_mfcs): A, B, C = sim_functions.get_second_ord_matrices(K, zeta, wn, td) K_lqr = sim_functions.make_lqr_law(A, B, Q, R) mfc_list.append(lab_hardware.MFC(lambda t, y, u: sim_functions.system_state_update(t, y, u, A, B), lambda y: sim_functions.system_output(y, C), y0)) control_law_list.append(lambda e:-K_lqr.dot(e)) # Insert our first-order-modeled middle fuel MFC A, B, C = sim_functions.get_state_matrices(K_mid, tau_mid, td_mid) K_lqr = sim_functions.make_lqr_law(A, B, Q[:2, :2], R) # smaller Q matrix, only 3 states mfc_list.insert(2, lab_hardware.MFC(lambda t, y, u: sim_functions.system_state_update(t, y, u, A, B), lambda y: sim_functions.system_output(y, C), y0[:2])) # shorter y0, only 3 states control_law_list.insert(2, lambda e:-K_lqr.dot(e)) # mfc and control law list should be in order: [air, pilot, middle, outer] # Initialize the combustor object combustor = lab_hardware.Combustor(p_atm, t_step, t_step_ctrl, sensor_list, mfc_list, control_law_list)
def setUp(self): """Run repeated setup code once.""" # Define class and local parameters unittest.TestCase.setUp(self) self.K_list = [3, 10, 99.21] self.tau_list = [0.3, 1] self.delay_list = [0.03, 0.1, 0.3] self.t_step = 0.01 self.stop_time = 20.0 self.input_val = 1.0 self.t_step_ctrl = 0.1 self.zeta = 0.7054 # air MFC damping ratio self.wn = 1.3501 # air MFC natural frequency, rad/s self.td = 1.8 # air MFC time delay, sec self.y0_1 = [0] self.y0_2 = [0] * 3 # for first-order model self.y0_3 = [0] * 4 # for second-order model A, B, self.C = sim_functions.get_state_matrices(self.K_list[1], self.tau_list[-1], self.delay_list[0]) A2, B2, self.C2 = sim_functions.get_second_ord_matrices(self.K_list[-1], self.zeta, self.wn, self.td) self.Kp_list = [4.0, 5.0] self.mass_flow_des = [4.0, 2.0] self.std = 1.0 self.Ks = 2.0 # filter static gain (?) Q = 1e-5 * np.identity(A2.shape[0]) # process noise covariance R = self.std # measurement noise covariance self.P = Q # initial estimate of error covariance self.offset = 0.0 mean = 0.0 # Define objects self.test_mfc1 = lab_hardware.MFC(lambda t, y, u: test_ode(t, y, u, self.K_list[1], self.tau_list[-1]), lambda x: x[0], self.y0_1) self.test_mfc2 = lab_hardware.MFC(lambda t, y, u: sim_functions.system_state_update(t, y, u, A, B), lambda y: sim_functions.system_output(y, self.C), self.y0_2) self.test_mfc3 = lab_hardware.MFC(lambda t, y, u: sim_functions.system_state_update(t, y, u, A2, B2), lambda y: sim_functions.system_output(y, self.C2), self.y0_3) self.mfc_list = [lab_hardware.MFC(lambda t, y, u: test_ode(t, y, u, K, tau), lambda x: x[0], self.y0_1) for K, tau in zip(self.K_list, self.tau_list)] self.test_KF = lab_hardware.KalmanFilter(A2, B2, self.Ks * self.C2, Q, R, self.P) self.test_sensor = lab_hardware.StaticSensor(lambda y: sim_functions.static_model(y, self.Ks, self.offset, mean, self.std), 1.0)
def test_static_sensor_class(self): """Tests for the lab_hardware.StaticSensor class.""" # Initialize constants P_list = [0, 1, 2, 3, 4, 5, 14.0, -1.1, -2] # actual pressures to read # Test initialization self.assertIsInstance(self.test_sensor, lab_hardware.StaticSensor, "Test static sensor not initialized to StaticSensor class.") self.assertEqual(self.test_sensor.get_output(), 0.0, "Sensor does not return expected initial reading.") self.assertEqual(self.test_sensor.get_location(), 1.0, "Sensor object does not return the correct location.") # Test readings with the sensor for P_actual in P_list: expected = sim_functions.static_model(P_actual, self.Ks, self.offset) self.assertNotEqual(self.test_sensor.update(P_actual), expected, "Measured pressure with noise equals value without noise. Noise may not be working.") self.assertAlmostEqual(self.test_sensor.get_output(), expected, delta=4 * self.std, msg="Stored sensor state >3*std away from expected state.")