def test_du_bounds(self): """ Test with blocking_factor du_bounds. """ op = transfer_to_casadi_interface( "CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={"state_initial_equations": True}) op.set('_start_c', float(self.c_0_A)) op.set('_start_T', float(self.T_0_A)) # Set options collocation n_e = 50 opt_opts = op.optimize_options() opt_opts['n_e'] = n_e opt_opts['IPOPT_options']['print_level'] = 0 # Define some MPC-options sample_period = 3 horizon = 50 seed = 7 # Define blocking factors bl_list = [1] * horizon factors = {'Tc': bl_list} du_bounds = {'Tc': 5} bf = BlockingFactors(factors=factors, du_bounds=du_bounds) opt_opts['blocking_factors'] = bf # Create MPC-object MPC_object = MPC(op, opt_opts, sample_period, horizon, noise_seed=seed, initial_guess='trajectory') MPC_object.update_state() u_k1 = MPC_object.sample() res = MPC_object.get_results_this_sample() Tc = res['Tc'] prev_value = Tc[0] largest_delta = 0 for value in Tc: delta = value - prev_value if delta > largest_delta: largest_delta = delta prev_value = value assert largest_delta < 5, "Value {} is not less than {}".format( largest_delta, 5)
def test_get_results_this_sample(self): """ Test that get_results_this_sample returns the optimization result for this optimization. """ op = transfer_to_casadi_interface( "CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={"state_initial_equations": True}) op.set('_start_c', float(self.c_0_A)) op.set('_start_T', float(self.T_0_A)) # Set options collocation n_e = 50 opt_opts = op.optimize_options() opt_opts['n_e'] = n_e opt_opts['IPOPT_options']['print_level'] = 0 # Define some MPC-options sample_period = 3 horizon = 50 seed = 7 cvc = {'T': 1e6} # Define blocking factors bl_list = [1] * horizon factors = {'Tc': bl_list} bf = BlockingFactors(factors=factors) opt_opts['blocking_factors'] = bf # Create MPC-object MPC_object = MPC(op, opt_opts, sample_period, horizon, constr_viol_costs=cvc, noise_seed=seed) MPC_object.update_state() u_k1 = MPC_object.sample() result1 = MPC_object.get_results_this_sample() N.testing.assert_equal(0, result1['time'][0]) N.testing.assert_equal(sample_period * horizon, result1['time'][-1]) MPC_object.update_state() u_k2 = MPC_object.sample() result2 = MPC_object.get_results_this_sample() N.testing.assert_equal(sample_period, result2['time'][0]) N.testing.assert_equal(sample_period * (horizon + 1), result2['time'][-1])
def test_warm_start_options(self): """ Test that the warm start options are activated. """ op = transfer_to_casadi_interface( "CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={"state_initial_equations": True}) op.set('_start_c', float(self.c_0_A)) op.set('_start_T', float(self.T_0_A)) # Set options collocation n_e = 50 opt_opts = op.optimize_options() opt_opts['n_e'] = n_e opt_opts['IPOPT_options']['print_level'] = 0 # Define some MPC-options sample_period = 3 horizon = 50 cvc = {'T': 1e6} # Create MPC-object MPC_object = MPC(op, opt_opts, sample_period, horizon, initial_guess='trajectory', constr_viol_costs=cvc) MPC_object.update_state({ '_start_c': 587.47543496, '_start_T': 345.64619542 }) u_k1 = MPC_object.sample() MPC_object.update_state() u_k2 = MPC_object.sample() N.testing.assert_(MPC_object.collocator.warm_start) wsip =\ MPC_object.collocator.solver_object.getOption('warm_start_init_point') mu_init = MPC_object.collocator.solver_object.getOption('mu_init') prl = MPC_object.collocator.solver_object.getOption('print_level') N.testing.assert_(wsip == 'yes') N.testing.assert_equal(mu_init, 1e-3) N.testing.assert_equal(prl, 0)
def run_demo(with_plots=True): """ Demonstrate how to optimize a VDP oscillator using CasADiInterface. """ # Compile and load optimization problem from pyjmi import get_files_path, transfer_to_casadi_interface file_path = os.path.join(get_files_path(), "VDP.mop") op = transfer_to_casadi_interface("VDP_pack.VDP_Opt2", file_path) # Set algorithm options opts = op.optimize_options() opts['n_e'] = 30 # Optimize res = op.optimize(options=opts) # Extract variable profiles x1 = res['x1'] x2 = res['x2'] u = res['u'] time = res['time'] assert N.abs(res.final('x1')) < 1e-3 assert N.abs(res.final('x2')) < 1e-3 # Plot if with_plots: plt.figure(1) plt.clf() plt.subplot(3, 1, 1) plt.plot(time, x1) plt.grid() plt.ylabel('x1') plt.subplot(3, 1, 2) plt.plot(time, x2) plt.grid() plt.ylabel('x2') plt.subplot(3, 1, 3) plt.plot(time, u) plt.grid() plt.ylabel('u') plt.xlabel('time') plt.show()
def test_softening_bounds(self): """ Test the automatic softening of hard variable bounds. """ op = transfer_to_casadi_interface( "CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={"state_initial_equations": True}) # Set options collocation n_e = 50 opt_opts = op.optimize_options() opt_opts['n_e'] = n_e # Define some MPC-options sample_period = 3 horizon = 50 seed = 7 # Define blocking factors bl_list = [1] * horizon factors = {'Tc': bl_list} bf = BlockingFactors(factors=factors) opt_opts['blocking_factors'] = bf opt_opts['IPOPT_options']['print_level'] = 0 cvc = {'T': 1e6} originalPathConstraints = op.getPathConstraints() # Create MPC-object MPC_object = MPC(op, opt_opts, sample_period, horizon, constr_viol_costs=cvc, noise_seed=seed) # Assert that an optimization with an initial value outside of bounds # succeeds MPC_object.update_state({'_start_c': self.c_0_A, '_start_T': 355}) MPC_object.sample() N.testing.assert_( 'Solve_Succeeded', MPC_object.collocator.solver_object.getStat('return_status'))
def test_auto_bl_factors(self): """ Test blocking factors generated in the mpc-class. """ op = transfer_to_casadi_interface( "CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={"state_initial_equations": True}) # Set options collocation n_e = 50 opt_opts = op.optimize_options() opt_opts['n_e'] = n_e opt_opts['IPOPT_options']['print_level'] = 0 # Define some MPC-options sample_period = 3 horizon = 50 # Create MPC-object MPC_object_auto = MPC(op, opt_opts, sample_period, horizon) MPC_object_auto.update_state() MPC_object_auto.sample() res_auto = MPC_object_auto.get_results_this_sample() opt_opts_auto = op.optimize_options() opt_opts_auto['n_e'] = n_e opt_opts_auto['IPOPT_options']['print_level'] = 0 bf_list = [1] * horizon factors = {'Tc': bf_list} bf = BlockingFactors(factors) opt_opts_auto['blocking_factors'] = bf MPC_object = MPC(op, opt_opts_auto, sample_period, horizon) MPC_object.update_state() MPC_object.sample() res = MPC_object.get_results_this_sample() # Assert that res_auto['Tc'] and res['Tc'] are equal N.testing.assert_array_equal(res_auto['Tc'], res['Tc'])
def test_infeasible_start(self): """ Test that the MPC class throws an exception if the first optimization is unsuccessful. """ op = transfer_to_casadi_interface( "CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={"state_initial_equations": True}) # Set options collocation n_e = 50 opt_opts = op.optimize_options() opt_opts['n_e'] = n_e opt_opts['IPOPT_options']['print_level'] = 0 # Define some MPC-options sample_period = 3 horizon = 50 cvc = {'T': 1e6} # Define blocking factors bl_list = [1] * horizon factors = {'Tc': bl_list} bf = BlockingFactors(factors=factors) opt_opts['blocking_factors'] = bf # Create MPC-object MPC_object = MPC(op, opt_opts, sample_period, horizon, constr_viol_costs=cvc) # Test with infeasible problem MPC_object.update_state({'_start_c': 900, '_start_T': 700}) N.testing.assert_raises(Exception, MPC_object.sample)
def run_demo(with_plots=True): """ This example is based on a combined cycle power plant (CCPP). The model has 9 states, 128 algebraic variables and 1 control variable. The task is to minimize the time required to perform a warm start-up of the power plant. This problem has become highly industrially relevant during the last few years, due to an increasing need to improve power generation flexibility. The example consists of the following steps: 1. Simulating the system using a simple input trajectory. 2. Solving the optimal control problem using the result from the first step to initialize the non-linear program. 3. Verifying the result from the second step by simulating the system once more usng the optimized input trajectory. The model was developed by Francesco Casella and is published in @InProceedings{CFA2011, author = "Casella, Francesco and Donida, Filippo and {\AA}kesson, Johan", title = "Object-Oriented Modeling and Optimal Control: A Case Study in Power Plant Start-Up", booktitle = "18th IFAC World Congress", address = "Milano, Italy", year = 2011, month = aug } """ ### 1. Compute initial guess trajectories by means of simulation # Locate the Modelica and Optimica code file_paths = (os.path.join(get_files_path(), "CombinedCycle.mo"), os.path.join(get_files_path(), "CombinedCycleStartup.mop")) # Compile the optimization initialization model init_sim_fmu = compile_fmu("CombinedCycleStartup.Startup6Reference", file_paths, separate_process=True) # Load the model init_sim_model = load_fmu(init_sim_fmu) # Simulate init_res = init_sim_model.simulate(start_time=0., final_time=10000.) # Extract variable profiles init_sim_plant_p = init_res['plant.p'] init_sim_plant_sigma = init_res['plant.sigma'] init_sim_plant_load = init_res['plant.load'] init_sim_time = init_res['time'] # Plot the initial guess trajectories if with_plots: plt.close(1) plt.figure(1) plt.subplot(3, 1, 1) plt.plot(init_sim_time, init_sim_plant_p * 1e-6) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.title('Initial guess obtained by simulation') plt.subplot(3, 1, 2) plt.plot(init_sim_time, init_sim_plant_sigma * 1e-6) plt.grid(True) plt.ylabel('turbine thermal stress [MPa]') plt.subplot(3, 1, 3) plt.plot(init_sim_time, init_sim_plant_load) plt.grid(True) plt.ylabel('input load [1]') plt.xlabel('time [s]') ### 2. Solve the optimal control problem # Compile model from pyjmi import transfer_to_casadi_interface op = transfer_to_casadi_interface("CombinedCycleStartup.Startup6", file_paths) # Set options opt_opts = op.optimize_options() opt_opts['n_e'] = 50 # Number of elements opt_opts['init_traj'] = init_res.result_data # Simulation result opt_opts['nominal_traj'] = init_res.result_data # Solve the optimal control problem opt_res = op.optimize(options=opt_opts) # Extract variable profiles opt_plant_p = opt_res['plant.p'] opt_plant_sigma = opt_res['plant.sigma'] opt_plant_load = opt_res['plant.load'] opt_time = opt_res['time'] opt_input = N.vstack([opt_time, opt_plant_load]).T # Plot the optimized trajectories if with_plots: plt.close(2) plt.figure(2) plt.subplot(3, 1, 1) plt.plot(opt_time, opt_plant_p * 1e-6) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.title('Optimized trajectories') plt.subplot(3, 1, 2) plt.plot(opt_time, opt_plant_sigma * 1e-6) plt.grid(True) plt.ylabel('turbine thermal stress [MPa]') plt.subplot(3, 1, 3) plt.plot(opt_time, opt_plant_load) plt.grid(True) plt.ylabel('input load [1]') plt.xlabel('time [s]') # Verify solution for testing purposes try: import casadi except: pass else: cost = float(opt_res.solver.solver.output(casadi.NLP_SOLVER_F)) N.testing.assert_allclose(cost, 17492.465548193624, rtol=1e-5) ### 3. Simulate to verify the optimal solution # Compile model sim_fmu = compile_fmu("CombinedCycle.Optimization.Plants.CC0D_WarmStartUp", file_paths) # Load model sim_model = load_fmu(sim_fmu) # Simulate using optimized input sim_res = sim_model.simulate(start_time=0., final_time=4000., input=('load', opt_input)) # Extract variable profiles sim_plant_p = sim_res['p'] sim_plant_sigma = sim_res['sigma'] sim_plant_load = sim_res['load'] sim_time = sim_res['time'] # Plot the simulated trajectories if with_plots: plt.close(3) plt.figure(3) plt.subplot(3, 1, 1) plt.plot(opt_time, opt_plant_p * 1e-6, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_p * 1e-6, lw=2) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.legend(('optimized', 'simulated'), loc='lower right') plt.title('Verification') plt.subplot(3, 1, 2) plt.plot(opt_time, opt_plant_sigma * 1e-6, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_sigma * 1e-6, lw=2) plt.ylabel('turbine thermal stress [MPa]') plt.grid(True) plt.subplot(3, 1, 3) plt.plot(opt_time, opt_plant_load, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_load, lw=2) plt.ylabel('input load [1]') plt.xlabel('time [s]') plt.grid(True) plt.show() # Verify solution for testing purposes N.testing.assert_allclose(opt_res.final('plant.p'), sim_res.final('p'), rtol=5e-3)
def run_demo(with_plots=True): """ This example is based on a combined cycle power plant (CCPP). The model has 9 states, 128 algebraic variables and 1 control variable. The task is to minimize the time required to perform a warm start-up of the power plant. This problem has become highly industrially relevant during the last few years, due to an increasing need to improve power generation flexibility. The example consists of the following steps: 1. Simulating the system using a simple input trajectory. 2. Solving the optimal control problem using the result from the first step to initialize the non-linear program. 3. Verifying the result from the second step by simulating the system once more usng the optimized input trajectory. The model was developed by Francesco Casella and is published in @InProceedings{CFA2011, author = "Casella, Francesco and Donida, Filippo and {\AA}kesson, Johan", title = "Object-Oriented Modeling and Optimal Control: A Case Study in Power Plant Start-Up", booktitle = "18th IFAC World Congress", address = "Milano, Italy", year = 2011, month = aug } """ ### 1. Compute initial guess trajectories by means of simulation # Locate the Modelica and Optimica code file_paths = (os.path.join(get_files_path(), "CombinedCycle.mo"), os.path.join(get_files_path(), "CombinedCycleStartup.mop")) # Compile the optimization initialization model init_sim_fmu = compile_fmu("CombinedCycleStartup.Startup6Reference", file_paths, separate_process=True) # Load the model init_sim_model = load_fmu(init_sim_fmu) # Simulate init_res = init_sim_model.simulate(start_time=0., final_time=10000.) # Extract variable profiles init_sim_plant_p = init_res['plant.p'] init_sim_plant_sigma = init_res['plant.sigma'] init_sim_plant_load = init_res['plant.load'] init_sim_time = init_res['time'] # Plot the initial guess trajectories if with_plots: plt.close(1) plt.figure(1) plt.subplot(3, 1, 1) plt.plot(init_sim_time, init_sim_plant_p * 1e-6) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.title('Initial guess obtained by simulation') plt.subplot(3, 1, 2) plt.plot(init_sim_time, init_sim_plant_sigma * 1e-6) plt.grid(True) plt.ylabel('turbine thermal stress [MPa]') plt.subplot(3, 1, 3) plt.plot(init_sim_time, init_sim_plant_load) plt.grid(True) plt.ylabel('input load [1]') plt.xlabel('time [s]') ### 2. Solve the optimal control problem # Compile model from pyjmi import transfer_to_casadi_interface op = transfer_to_casadi_interface("CombinedCycleStartup.Startup6", file_paths) # Set options opt_opts = op.optimize_options() opt_opts['n_e'] = 50 # Number of elements opt_opts['init_traj'] = init_res # Simulation result opt_opts['nominal_traj'] = init_res opt_opts['verbosity'] = 1 # Solve the optimal control problem opt_res = op.optimize(options=opt_opts) # Extract variable profiles opt_plant_p = opt_res['plant.p'] opt_plant_sigma = opt_res['plant.sigma'] opt_plant_load = opt_res['plant.load'] opt_time = opt_res['time'] opt_input = N.vstack([opt_time, opt_plant_load]).T # Plot the optimized trajectories if with_plots: plt.close(2) plt.figure(2) plt.subplot(3, 1, 1) plt.plot(opt_time, opt_plant_p * 1e-6) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.title('Optimized trajectories') plt.subplot(3, 1, 2) plt.plot(opt_time, opt_plant_sigma * 1e-6) plt.grid(True) plt.ylabel('turbine thermal stress [MPa]') plt.subplot(3, 1, 3) plt.plot(opt_time, opt_plant_load) plt.grid(True) plt.ylabel('input load [1]') plt.xlabel('time [s]') # Verify solution for testing purposes try: import casadi except: pass else: cost = float(opt_res.solver.solver_object.output(casadi.NLP_SOLVER_F)) N.testing.assert_allclose(cost, 17492.465548193624, rtol=1e-5) ### 3. Simulate to verify the optimal solution # Compile model sim_fmu = compile_fmu("CombinedCycle.Optimization.Plants.CC0D_WarmStartUp", file_paths) # Load model sim_model = load_fmu(sim_fmu) # Simulate using optimized input sim_res = sim_model.simulate(start_time=0., final_time=4000., input=('load', opt_input)) # Extract variable profiles sim_plant_p = sim_res['p'] sim_plant_sigma = sim_res['sigma'] sim_plant_load = sim_res['load'] sim_time = sim_res['time'] # Plot the simulated trajectories if with_plots: plt.close(3) plt.figure(3) plt.subplot(3, 1, 1) plt.plot(opt_time, opt_plant_p * 1e-6, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_p * 1e-6, lw=2) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.legend(('optimized', 'simulated'), loc='lower right') plt.title('Verification') plt.subplot(3, 1, 2) plt.plot(opt_time, opt_plant_sigma * 1e-6, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_sigma * 1e-6, lw=2) plt.ylabel('turbine thermal stress [MPa]') plt.grid(True) plt.subplot(3, 1, 3) plt.plot(opt_time, opt_plant_load, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_load, lw=2) plt.ylabel('input load [1]') plt.xlabel('time [s]') plt.grid(True) plt.show() # Verify solution for testing purposes N.testing.assert_allclose(opt_res.final('plant.p'), sim_res.final('p'), rtol=5e-3)
def run_demo(with_plots=True): """ This example demonstrates how to solve parameter estimation problmes. The data used in the example was recorded by Kristian Soltesz at the Department of Automatic Control. """ # Load measurement data from file data_path = os.path.join(get_files_path(), "qt_par_est_data.mat") data = loadmat(data_path, appendmat=False) # Extract data series t_meas = data['t'][6000::100, 0] - 60 y1_meas = data['y1_f'][6000::100, 0] / 100 y2_meas = data['y2_f'][6000::100, 0] / 100 y3_meas = data['y3_d'][6000::100, 0] / 100 y4_meas = data['y4_d'][6000::100, 0] / 100 u1 = data['u1_d'][6000::100, 0] u2 = data['u2_d'][6000::100, 0] # Plot measurements and inputs if with_plots: plt.close(1) plt.figure(1) plt.subplot(2, 2, 1) plt.plot(t_meas, y3_meas) plt.title('x3') plt.grid() plt.subplot(2, 2, 2) plt.plot(t_meas, y4_meas) plt.title('x4') plt.grid() plt.subplot(2, 2, 3) plt.plot(t_meas, y1_meas) plt.title('x1') plt.xlabel('t[s]') plt.grid() plt.subplot(2, 2, 4) plt.plot(t_meas, y2_meas) plt.title('x2') plt.xlabel('t[s]') plt.grid() plt.close(2) plt.figure(2) plt.subplot(2, 1, 1) plt.plot(t_meas, u1) plt.hold(True) plt.title('u1') plt.grid() plt.subplot(2, 1, 2) plt.plot(t_meas, u2) plt.title('u2') plt.xlabel('t[s]') plt.hold(True) plt.grid() # Build input trajectory matrix for use in simulation u = N.transpose(N.vstack([t_meas, u1, u2])) # Compile and load FMU model_path = os.path.join(get_files_path(), "QuadTankPack.mop") fmu_name = compile_fmu('QuadTankPack.Sim_QuadTank', model_path) model = load_fmu(fmu_name) # Simulate model response with nominal parameter values res = model.simulate(input=(['u1', 'u2'], u), start_time=0., final_time=60.) # Load simulation result x1_sim = res['qt.x1'] x2_sim = res['qt.x2'] x3_sim = res['qt.x3'] x4_sim = res['qt.x4'] t_sim = res['time'] u1_sim = res['u1'] u2_sim = res['u2'] # Check simulation results for testing purposes assert N.abs(res.final('qt.x1') - 0.05642485) < 1e-3 assert N.abs(res.final('qt.x2') - 0.05510478) < 1e-3 assert N.abs(res.final('qt.x3') - 0.02736532) < 1e-3 assert N.abs(res.final('qt.x4') - 0.02789808) < 1e-3 assert N.abs(res.final('u1') - 6.0) < 1e-3 assert N.abs(res.final('u2') - 5.0) < 1e-3 # Plot simulation result if with_plots: plt.figure(1) plt.subplot(2, 2, 1) plt.plot(t_sim, x3_sim) plt.subplot(2, 2, 2) plt.plot(t_sim, x4_sim) plt.subplot(2, 2, 3) plt.plot(t_sim, x1_sim) plt.subplot(2, 2, 4) plt.plot(t_sim, x2_sim) plt.figure(2) plt.subplot(2, 1, 1) plt.plot(t_sim, u1_sim, 'r') plt.subplot(2, 1, 2) plt.plot(t_sim, u2_sim, 'r') # Load optimization problem op = transfer_to_casadi_interface("QuadTankPack.QuadTank_ParEstCasADi", model_path) # Create measurement data object for optimization Q = N.diag([1., 1., 10., 10.]) data_x1 = N.vstack([t_meas, y1_meas]) data_x2 = N.vstack([t_meas, y2_meas]) data_u1 = N.vstack([t_meas, u1]) data_u2 = N.vstack([t_meas, u2]) unconstrained = OrderedDict() unconstrained['qt.x1'] = data_x1 unconstrained['qt.x2'] = data_x2 unconstrained['u1'] = data_u1 unconstrained['u2'] = data_u2 measurement_data = MeasurementData(Q=Q, unconstrained=unconstrained) # Set optimization options and optimize opts = op.optimize_options() opts['n_e'] = 60 opts['measurement_data'] = measurement_data opts['init_traj'] = res.result_data opts['nominal_traj'] = res.result_data res = op.optimize(options=opts) # Load state profiles x1_opt = res["qt.x1"] x2_opt = res["qt.x2"] x3_opt = res["qt.x3"] x4_opt = res["qt.x4"] u1_opt = res["qt.u1"] u2_opt = res["qt.u2"] t_opt = res["time"] # Extract estimated values of parameters a1_opt = res.final("qt.a1") a2_opt = res.final("qt.a2") # Print and assert estimated parameter values print('a1: ' + str(a1_opt * 1e4) + 'cm^2') print('a2: ' + str(a2_opt * 1e4) + 'cm^2') a_ref = [0.02656702, 0.02713898] N.testing.assert_allclose(1e4 * N.array([a1_opt, a2_opt]), [0.02656702, 0.02713898], rtol=1e-4) # Plot estimated trajectories if with_plots: plt.figure(1) plt.subplot(2, 2, 1) plt.plot(t_opt, x3_opt, 'k') plt.subplot(2, 2, 2) plt.plot(t_opt, x4_opt, 'k') plt.subplot(2, 2, 3) plt.plot(t_opt, x1_opt, 'k') plt.subplot(2, 2, 4) plt.plot(t_opt, x2_opt, 'k') plt.figure(2) plt.subplot(2, 1, 1) plt.plot(t_opt, u1_opt, 'k') plt.subplot(2, 1, 2) plt.plot(t_opt, u2_opt, 'k') plt.show()
def run_demo(with_plots=True): """ This example is based on a combined cycle power plant (CCPP). The model has 9 states, 128 algebraic variables and 1 control variable. The task is to minimize the time required to perform a warm start-up of the power plant. This problem has become highly industrially relevant during the last few years, due to an increasing need to improve power generation flexibility. The example consists of the following steps: 1. Simulating the system using a simple input trajectory. 2. Solving the optimal control problem using the result from the first step to initialize the non-linear program. In addition, a set of algebraic variables is eliminated by means of BLt information 3. Verifying the result from the second step by simulating the system once more usng the optimized input trajectory. The model was developed by Francesco Casella and is published in @InProceedings{CFA2011, author = "Casella, Francesco and Donida, Filippo and {\AA}kesson, Johan", title = "Object-Oriented Modeling and Optimal Control: A Case Study in Power Plant Start-Up", booktitle = "18th IFAC World Congress", address = "Milano, Italy", year = 2011, month = aug } """ ### 1. Compute initial guess trajectories by means of simulation # Locate the Modelica and Optimica code file_paths = (os.path.join(get_files_path(), "CombinedCycle.mo"), os.path.join(get_files_path(), "CombinedCycleStartup.mop")) # Compile the optimization initialization model init_sim_fmu = compile_fmu("CombinedCycleStartup.Startup6Reference", file_paths, separate_process=True) # Load the model init_sim_model = load_fmu(init_sim_fmu) # Simulate init_res = init_sim_model.simulate(start_time=0., final_time=10000.) # Extract variable profiles init_sim_plant_p = init_res['plant.p'] init_sim_plant_sigma = init_res['plant.sigma'] init_sim_plant_load = init_res['plant.load'] init_sim_time = init_res['time'] # Plot the initial guess trajectories if with_plots: plt.close(1) plt.figure(1) plt.subplot(3, 1, 1) plt.plot(init_sim_time, init_sim_plant_p * 1e-6) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.title('Initial guess obtained by simulation') plt.subplot(3, 1, 2) plt.plot(init_sim_time, init_sim_plant_sigma * 1e-6) plt.grid(True) plt.ylabel('turbine thermal stress [MPa]') plt.subplot(3, 1, 3) plt.plot(init_sim_time, init_sim_plant_load) plt.grid(True) plt.ylabel('input load [1]') plt.xlabel('time [s]') ### 2. Solve the optimal control problem # Compile model from pyjmi import transfer_to_casadi_interface compiler_options = {'equation_sorting': True, "common_subexp_elim": False} op = transfer_to_casadi_interface("CombinedCycleStartup.Startup6", file_paths, compiler_options) # Set options opt_opts = op.optimize_options() opt_opts['n_e'] = 50 # Number of elements opt_opts['init_traj'] = init_res # Simulation result opt_opts['nominal_traj'] = init_res # variable elimination eliminables = op.getEliminableVariables() algebraics = op.getVariables(op.REAL_ALGEBRAIC) print "Number of algebraics: ", len(algebraics) print "Eliminating variables!" eliminable_algebraics = [a for a in algebraics if a in eliminables] variables_to_eliminate = list() variables_with_bounds = list() for v in eliminable_algebraics: if not v.hasAttributeSet("min") and not v.hasAttributeSet("max"): variables_to_eliminate.append(v) else: variables_with_bounds.append(v) # Elimination of unbounded variables op.markVariablesForElimination(variables_to_eliminate) # Elimination of bounded variables (skip elimination of plant.sigma) bounded_eliminations = [ v for v in variables_with_bounds if not v.getName() == "plant.sigma" ] op.markVariablesForElimination(bounded_eliminations) op.eliminateVariables() # Alternative way of eliminating variables however this method do not eliminate any bounded variables #op.eliminateAlgebraics() print "Done with elimination" eliminated_vars = op.getEliminatedVariables() print "Number of variables that were eliminated: ", len(eliminated_vars) # Solve the optimal control problem opt_res = op.optimize(options=opt_opts) # get output of one eliminated variable elim_var_name = eliminable_algebraics[5].getName() # Extract variable profiles opt_plant_p = opt_res['plant.p'] opt_plant_sigma = opt_res['plant.sigma'] opt_plant_load = opt_res['plant.load'] opt_eliminated = opt_res[elim_var_name] opt_time = opt_res['time'] opt_input = N.vstack([opt_time, opt_plant_load]).T # Plot the optimized trajectories if with_plots: plt.close(2) plt.figure(2) plt.subplot(4, 1, 1) plt.plot(opt_time, opt_plant_p * 1e-6) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.title('Optimized trajectories') plt.subplot(4, 1, 2) plt.plot(opt_time, opt_plant_sigma * 1e-6) plt.grid(True) plt.ylabel('turbine thermal stress [MPa]') plt.subplot(4, 1, 3) plt.plot(opt_time, opt_plant_load) plt.grid(True) plt.ylabel('input load [1]') plt.xlabel('time [s]') plt.subplot(4, 1, 4) plt.plot(opt_time, opt_eliminated) plt.grid(True) plt.ylabel(elim_var_name) plt.xlabel('time [s]') # Verify solution for testing purposes try: import casadi except: pass else: cost = float(opt_res.solver.solver_object.output(casadi.NLP_SOLVER_F)) N.testing.assert_allclose(cost, 17492.465548193624, rtol=1e-5) ### 3. Simulate to verify the optimal solution # Compile model sim_fmu = compile_fmu("CombinedCycle.Optimization.Plants.CC0D_WarmStartUp", file_paths) # Load model sim_model = load_fmu(sim_fmu) # Simulate using optimized input sim_res = sim_model.simulate(start_time=0., final_time=4000., input=('load', opt_input)) # Extract variable profiles sim_plant_p = sim_res['p'] sim_plant_sigma = sim_res['sigma'] sim_plant_load = sim_res['load'] sim_time = sim_res['time'] # Plot the simulated trajectories if with_plots: plt.close(3) plt.figure(3) plt.subplot(3, 1, 1) plt.plot(opt_time, opt_plant_p * 1e-6, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_p * 1e-6, lw=2) plt.ylabel('evaporator pressure [MPa]') plt.grid(True) plt.legend(('optimized', 'simulated'), loc='lower right') plt.title('Verification') plt.subplot(3, 1, 2) plt.plot(opt_time, opt_plant_sigma * 1e-6, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_sigma * 1e-6, lw=2) plt.ylabel('turbine thermal stress [MPa]') plt.grid(True) plt.subplot(3, 1, 3) plt.plot(opt_time, opt_plant_load, '--', lw=5) plt.hold(True) plt.plot(sim_time, sim_plant_load, lw=2) plt.ylabel('input load [1]') plt.xlabel('time [s]') plt.grid(True) plt.show() # Verify solution for testing purposes N.testing.assert_allclose(opt_res.final('plant.p'), sim_res.final('p'), rtol=5e-3)
def test_eliminated_variables(self): """ Test that the results when using eliminated variables are the same as when not using them. """ # Compile and load the model used for simulation sim_fmu = compile_fmu( "CSTR.CSTR_MPC_Model", self.cstr_file_path, compiler_options={"state_initial_equations": True}) sim_model = load_fmu(sim_fmu) # Compile and load the model with eliminated variables used for simulation sim_fmu_elim = compile_fmu("CSTR.CSTR_elim_vars_MPC_Model", self.cstr_file_path, compiler_options={ "state_initial_equations": True, 'equation_sorting': True, 'automatic_tearing': False }) sim_model_elim = load_fmu(sim_fmu_elim) # Define stationary point A and set initial values and inputs c_0_A = 956.271352 T_0_A = 250.051971 sim_model.set('_start_c', c_0_A) sim_model.set('_start_T', T_0_A) sim_model.set('Tc', 280) init_res = sim_model.simulate(start_time=0., final_time=150) # Compile and load optimization problems op = transfer_to_casadi_interface("CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={ "state_initial_equations": True, "common_subexp_elim": False }) op_elim = transfer_to_casadi_interface("CSTR.CSTR_elim_vars_MPC", self.cstr_file_path, compiler_options={ "state_initial_equations": True, 'equation_sorting': True, 'automatic_tearing': False, "common_subexp_elim": False }) # Define MPC options sample_period = 5 # s horizon = 10 # Samples on the horizon n_e_per_sample = 1 # Collocation elements / sample n_e = n_e_per_sample * horizon # Total collocation elements finalTime = 50 # s number_samp_tot = 5 # Total number of samples to do # Create blocking factors with quadratic penalty and bound on 'Tc' bf_list = [n_e_per_sample] * (horizon / n_e_per_sample) factors = {'Tc': bf_list} du_quad_pen = {'Tc': 50} du_bounds = {'Tc': 30} bf = BlockingFactors(factors, du_bounds, du_quad_pen) # Set collocation options opt_opts = op.optimize_options() opt_opts['n_e'] = n_e opt_opts['n_cp'] = 2 opt_opts['init_traj'] = init_res constr_viol_costs = {'T': 1e6} # Create the MPC object MPC_object = MPC(op, opt_opts, sample_period, horizon, constr_viol_costs=constr_viol_costs, noise_seed=1) # Set initial state x_k = {'_start_c': c_0_A, '_start_T': T_0_A} # Update the state and optimize number_samp_tot times for k in range(number_samp_tot): # Update the state and compute the optimal input for next sample period MPC_object.update_state(x_k) u_k = MPC_object.sample() # Reset the model and set the new initial states before simulating # the next sample period with the optimal input u_k sim_model.reset() sim_model.set(list(x_k.keys()), list(x_k.values())) sim_res = sim_model.simulate(start_time=k * sample_period, final_time=(k + 1) * sample_period, input=u_k) # Extract state at end of sample_period from sim_res and add Gaussian # noise with mean 0 and standard deviation 0.005*(state_current_value) x_k = MPC_object.extract_states(sim_res, mean=0, st_dev=0.005) # Extract variable profiles complete_result = MPC_object.get_complete_results() op_elim.eliminateAlgebraics() assert (len(op_elim.getEliminatedVariables()) == 2) opt_opts_elim = op_elim.optimize_options() opt_opts_elim['n_e'] = n_e opt_opts_elim['n_cp'] = 2 opt_opts_elim['init_traj'] = init_res # Create the MPC object with eliminated variables MPC_object_elim = MPC(op_elim, opt_opts_elim, sample_period, horizon, constr_viol_costs=constr_viol_costs, noise_seed=1) # Set initial state x_k = {'_start_c': c_0_A, '_start_T': T_0_A} # Update the state and optimize number_samp_tot times for k in range(number_samp_tot): # Update the state and compute the optimal input for next sample period MPC_object_elim.update_state(x_k) u_k = MPC_object_elim.sample() # Reset the model and set the new initial states before simulating # the next sample period with the optimal input u_k sim_model_elim.reset() sim_model_elim.set(list(x_k.keys()), list(x_k.values())) sim_res = sim_model_elim.simulate(start_time=k * sample_period, final_time=(k + 1) * sample_period, input=u_k) # Extract state at end of sample_period from sim_res and add Gaussian # noise with mean 0 and standard deviation 0.005*(state_current_value) x_k = MPC_object_elim.extract_states(sim_res, mean=0, st_dev=0.005) # Extract variable profiles complete_result_elim = MPC_object_elim.get_complete_results() N.testing.assert_array_almost_equal(complete_result['c'], complete_result_elim['c']) N.testing.assert_array_almost_equal(complete_result['T'], complete_result_elim['T']) N.testing.assert_array_almost_equal(complete_result['Tc'], complete_result_elim['Tc'])
def test_infeasible_return_input(self): """ Test that the input returned from an unsuccessful optimization is the next input in the last successful optimization. """ op = transfer_to_casadi_interface( "CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={"state_initial_equations": True}) # Set options collocation n_e = 50 opt_opts = op.optimize_options() opt_opts['n_e'] = n_e opt_opts['IPOPT_options']['print_level'] = 0 # Define some MPC-options sample_period = 3 horizon = 50 seed = 7 cvc = {'T': 1e6} # Define blocking factors bl_list = [1] * horizon factors = {'Tc': bl_list} bf = BlockingFactors(factors=factors) opt_opts['blocking_factors'] = bf # Create MPC-object MPC_object = MPC(op, opt_opts, sample_period, horizon, constr_viol_costs=cvc, noise_seed=seed, create_comp_result=False, initial_guess='trajectory') # NOTE: THIS NOT WORKING WITH initial_guess='shift'!! MPC_object.update_state({ '_start_c': 587.47543496, '_start_T': 345.64619542 }) u_k1 = MPC_object.sample() result1 = MPC_object.get_results_this_sample() # Optimize with infeasible problem MPC_object.update_state({'_start_c': 900, '_start_T': 400}) u_k2 = MPC_object.sample() # Assert that problem was infeasible and that the returned input is # the next input from the last succesful optimization N.testing.assert_( 'Infeasible_Problem_Detected', MPC_object.collocator.solver_object.getStat('return_status')) N.testing.assert_almost_equal(u_k2[1](0)[0], result1['Tc'][4], decimal=10) # Assert that the returned resultfile is that of the last succesful # optimization result2 = MPC_object.get_results_this_sample() assert result1 == result2, "UNEQUAL VALUES. result1={}\nresult2={}".format( result1, result2) # Assert that problem was infeasible yet again and that the returned # input is the next (third) input from the last succesful optimization MPC_object.update_state({'_start_c': 900, '_start_T': 400}) u_k3 = MPC_object.sample() N.testing.assert_( 'Infeasible_Problem_Detected', MPC_object.collocator.solver_object.getStat('return_status')) N.testing.assert_almost_equal(u_k3[1](0)[0], result1['Tc'][7], decimal=10)
def test_du_quad_pen(self): """ Test with blocking_factor du_quad_pen. """ op = transfer_to_casadi_interface( "CSTR.CSTR_MPC", self.cstr_file_path, compiler_options={"state_initial_equations": True}) op.set('_start_c', float(self.c_0_A)) op.set('_start_T', float(self.T_0_A)) # Set options collocation n_e = 50 opt_opts = op.optimize_options() opt_opts['n_e'] = n_e opt_opts['IPOPT_options']['print_level'] = 0 # Define some MPC-options sample_period = 3 horizon = 50 seed = 7 # Define blocking factors bl_list = [1] * horizon factors = {'Tc': bl_list} bf = BlockingFactors(factors=factors) opt_opts['blocking_factors'] = bf # Create MPC-object without du_quad_pen MPC_object = MPC(op, opt_opts, sample_period, horizon, noise_seed=seed, initial_guess='trajectory') MPC_object.update_state() MPC_object.sample() res = MPC_object.get_results_this_sample() # Create MPC-object with du_quad_pen opt_opts_quad = op.optimize_options() opt_opts_quad['n_e'] = n_e opt_opts_quad['IPOPT_options']['print_level'] = 0 bf_list = [1] * horizon factors = {'Tc': bf_list} du_quad_pen = {'Tc': 100} bf = BlockingFactors(factors, du_quad_pen=du_quad_pen) opt_opts_quad['blocking_factors'] = bf MPC_object_quad = MPC(op, opt_opts_quad, sample_period, horizon, noise_seed=seed, initial_guess='trajectory') MPC_object_quad.update_state() MPC_object_quad.sample() res_quad = MPC_object_quad.get_results_this_sample() Tc = res['Tc'] prev_value = Tc[0] largest_delta = 0 for value in Tc: delta = value - prev_value if delta > largest_delta: largest_delta = delta prev_value = value Tc = res_quad['Tc'] prev_value = Tc[0] largest_delta_quad = 0 for value in Tc: delta = value - prev_value if delta > largest_delta_quad: largest_delta_quad = delta prev_value = value N.testing.assert_(largest_delta_quad < largest_delta)