def get_data(): """ Returns all possible simulations """ conf = parse_config() datafile = conf["datafile"] data = pd.read_csv(datafile) if "P1" in data.columns: PL = (data.P1 + data.P2).to_numpy() else: PL = data.PL.to_numpy() PV = data.PV.to_numpy() if "Spot_pris" in data.columns: grid_buy = grid_sell = data.Spot_pris.to_numpy() else: grid_buy = grid_sell = 1.5 if conf["perfect_predictions"] or "PV_pred" not in data.columns: PV_pred = PV.copy() PL_pred = PL.copy() else: PV_pred = data.PV_pred.to_numpy() PL_pred = data.PL_pred.to_numpy() return PV, PV_pred, PL, PL_pred, grid_buy, grid_sell
def __init__(self, T, N): self.T = T self.N = N conf = parse_config() conf_system = conf["system"] # Get system constants self.C_MAX = conf_system["C_MAX"] self.nb_c = conf_system["nb_c"] self.nb_d = conf_system["nb_d"] self.x_min = conf_system["x_min"] self.x_max = conf_system["x_max"] self.x_ref = conf_system["x_ref"] self.Pb_max = conf_system["Pb_max"] self.Pg_max = conf_system["Pg_max"] self.battery_cost = conf_system["battery_cost"] self.grid_cost = conf_system["grid_cost"] self.ref_cost = conf_system["ref_cost"] self.verbose = conf_system["verbose"] self.states = struct_symSX([entry("SOC")]) self.inputs = struct_symSX( [ entry("Pbc"), entry("Pbd"), entry("Pgb"), entry("Pgs"), ] ) self.w = struct_symSX( [ entry("states", struct=self.states, repeat=self.N), entry("inputs", struct=self.inputs, repeat=self.N - 1), ] ) self.data = struct_symSX([entry("pv"), entry("l"), entry("E")]) self.all_data = struct_symSX([entry("data", struct=self.data, repeat=self.N)]) self.E = SX.sym("E", self.N) # Initialize system properties self.ode = self.build_ode() self.L = None self.F = None self.solver = None # Keep optimal solutions self.SOC = np.asarray([]) self.Pbc = np.asarray([]) self.Pbd = np.asarray([]) self.Pgs = np.asarray([]) self.Pgb = np.asarray([])
def __init__(self, actions_per_hour=6): self.logger = logging.getLogger("Metrics") f_handler = logging.FileHandler("./logs/metrics.log") s_handler = logging.StreamHandler() f_handler.setLevel(logging.DEBUG) s_handler.setLevel(logging.INFO) s_format = logging.Formatter("%(message)s") f_format = logging.Formatter("%(message)s") s_handler.setFormatter(s_format) f_handler.setFormatter(f_format) self.logger.addHandler(s_handler) self.logger.addHandler(f_handler) self.conf = parse_config() self.actions_per_hour = actions_per_hour self.C_max = self.conf["battery"]["C_MAX"] self.battery_deg = self.conf["system"]["battery_cost"] self.peak_cost = self.conf["system"]["peak_cost"] self.old_grid_fee = self.conf["system"]["old_grid_fee"] assert ( self.peak_cost * self.old_grid_fee == 0), "Either peak or old cost has to be zero" self.logger.info("\n \n") self.logger.info("Run started at {}".format(datetime.now())) self.logger.info( "Sim-time: {}, Pred-horizon: {}, N Scenarios: {}, Perfect Predictions: {}" .format( str(self.conf["simulation_horizon"]), str(self.conf["prediction_horizon"]), str(self.conf["N_scenarios"]), str(self.conf["perfect_predictions"]), )) self.logger.info("-" * 100) self.grid_cost = 0 self.battery_cost = 0 self.grid_max = 0 self.old_system_cost = 0 self.primary_cost = 0 self.pv_rmse = 0 self.accumulated_error = 0 self.computational_time = 0 self.worst_case = 0 self.steps = 0
def scenario_mpc(): """ Main function for mpc-scheme with receding horizion. """ np.random.seed(1) conf = utils.parse_config() testfile = conf["testfile"] trainfile = conf["trainfile"] logpath = None loggerpath = "./logs/all_logs.log" foldername = conf["foldername"] if foldername: logpath = utils.create_logs_folder(conf["logpath"], foldername) loggerpath = logpath + "logs.log" logging.basicConfig( filename=loggerpath, filemode="a", format="%(name)s - %(levelname)s - %(message)s", level=logging.INFO, ) perfect_predictions = conf["perfect_predictions"] actions_per_hour = conf["actions_per_hour"] horizon = conf["simulation_horizon"] T = conf["prediction_horizon"] N = conf["prediction_horizon"] * actions_per_hour N_scenarios = conf["N_scenarios"] simulation_horizon = horizon * actions_per_hour assert N_scenarios in [1, 3, 7, 9], "Only 1, 3, 7 or 9 branches allowed" # Get data observations = pd.read_csv(testfile, parse_dates=["date"]).fillna(0) observations = observations[observations["date"] >= datetime( conf["start_year"], conf["start_month"], conf["start_day"])] assert (simulation_horizon + N < observations.shape[0]), "Not enough data for simulation" # Read forecast file solcast_forecasts = pd.read_csv(conf["solcast_file"], parse_dates=["time", "collected"]).fillna(0) # Read weighting files load_weights = pd.read_csv("./data/load_weights.csv") pv_weights = pd.read_csv("./data/pv_weights.csv") current_time = observations.date.iloc[0] forecast = solcast_forecasts[solcast_forecasts["collected"] == current_time - timedelta(minutes=60)] obs = observations[ (observations["date"] >= current_time) & (observations["date"] <= current_time + timedelta(minutes=10 * N))] # Initialize components E = pd.read_csv(conf["price_file"], parse_dates=["time"]) L = Load(N, testfile, "L", current_time) B = Battery(T, N, **conf["battery"]) PV = LinearPhotovoltaic(trainfile) sys_metrics = metrics.SystemMetrics() # Define variables Pbc = [] Pbd = [] Pgs = [] Pgb = [] Pgb_p = 0 Pgb_p_all = [Pgb_p] primary_Pgb = 0 primary_Pgs = 0 prob = [1] pv_measured = [] l_measured = [] errors = [] E_measured = [] time_stamps = [current_time] prediction_time = 0 solver_time = 0 filter_ = [str(i) for i in range(N)] # Initialize and build optimal control problem ocp = ScenarioOCP(T, N, N_scenarios) s_data = ocp.s_data(0) s0, lbs, ubs, lbg, ubg = ocp.build_scenario_ocp() # Initialize plots and progressbar fig1, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 7)) bar = progressbar.ProgressBar( maxval=simulation_horizon, widgets=[ progressbar.Bar("=", "[", "]"), " Finished with", progressbar.Percentage(), ], ) bar.start() # MPC loop for step in range(simulation_horizon): step_start = time.time() # Get measurements pv_true = obs["PV"].values[0] l_true = obs["L"].values[0] # Get new forecasts every hour if current_time.minute == 30: new_forecast = solcast_forecasts[solcast_forecasts["collected"] == current_time - timedelta(minutes=30)] if new_forecast.empty: print("Could not find forecast, using old forecast") else: forecast = new_forecast ref = forecast[ (forecast["time"] > current_time) & (forecast["time"] <= current_time + timedelta(minutes=10 * (N)))] E_prediction = E[(E.time > current_time) & (E.time <= current_time + timedelta(minutes=10 * (N)))].price.values # Get predictions if perfect_predictions: pv_prediction = obs["PV"].values[1:] l_prediction = obs["L"].values[1:] else: pred_time = time.time() pv_prediction = PV.predict(ref.temp.values, ref.GHI.values, obs["PV"].iloc[0]) l_prediction, l_lower, l_upper = L.get_statistic_scenarios( current_time, step) l_prediction = L.linear_mixture(l_prediction, l_true) prediction_time += time.time() - pred_time if N_scenarios == 1: pv_scenarios = [pv_prediction] l_scenarios = [l_prediction] else: # Construct scenarios and weights l_lower, l_upper = L.get_minmax_day(current_time, step) l_probs = np.asarray([ utils.get_probability_from_file(load_weights, step, i, filter_) for i in range(3) ]) pv_probs = np.asarray([ utils.get_probability_from_file(pv_weights, step, i, filter_) for i in range(3) ]) pv_upper = PV.predict(ref.temp.values, ref.GHI90.values) pv_lower = PV.predict(ref.temp.values, ref.GHI10.values) pv_scenarios = np.asarray([pv_upper, pv_prediction, pv_lower]) l_scenarios = np.asarray([l_lower, l_prediction, l_upper]) prob = np.multiply(pv_probs, l_probs) prob /= np.sum(prob) # Scale to one if N_scenarios == 7: pv_scenarios = [ pv_upper, pv_upper, pv_prediction, pv_prediction, pv_prediction, pv_lower, pv_lower, ] l_scenarios = [ l_lower, l_prediction, l_lower, l_prediction, l_upper, l_prediction, l_upper, ] prob = np.multiply( np.asarray([ pv_probs[0], pv_probs[0], pv_probs[1], pv_probs[1], pv_probs[1], pv_probs[2], pv_probs[2], ]), np.asarray([ l_probs[0], l_probs[1], l_probs[0], l_probs[1], l_probs[2], l_probs[1], l_probs[2], ]), ) elif N_scenarios == 9: pv_scenarios = np.repeat(pv_scenarios, 3, axis=0) l_scenarios = np.tile(l_scenarios, (3, 1)) prob = np.multiply(np.repeat(pv_probs, 3), np.tile(l_probs, 3)) prob /= np.sum(prob) # Plot scenarios and predictions if N_scenarios <= 3 and step % 18 == 0: colors = [i for i in get_cmap("tab10").colors] t_1 = [current_time + timedelta(minutes=10 * i) for i in range(N)] if step == 0: label_s = ["Upper", "Prediction", "Lower"] label_obs = "Observation" else: label_s = [None] * 3 label_obs = None for i in range(len(pv_scenarios)): ax1.plot( t_1, pv_scenarios[i], label=label_s[1], color=colors[1], ) ax2.plot( t_1, l_scenarios[i], label=label_s[1], color=colors[1], ) ax1.plot(t_1, obs.PV[1:], label=label_obs, color=colors[0]) ax2.plot(t_1, obs.L[1:], label=label_obs, color=colors[0]) # Update parameters for i in range(N_scenarios): s0["scenario" + str(i), "states", 0, "SOC"] = B.get_SOC() lbs["scenario" + str(i), "states", 0, "SOC"] = B.get_SOC() ubs["scenario" + str(i), "states", 0, "SOC"] = B.get_SOC() s0["scenario" + str(i), "states", 0, "Pgb_p"] = Pgb_p lbs["scenario" + str(i), "states", 0, "Pgb_p"] = Pgb_p ubs["scenario" + str(i), "states", 0, "Pgb_p"] = Pgb_p for k in range(N): s_data["scenario" + str(i), "data", k, "pv"] = pv_scenarios[i][k] s_data["scenario" + str(i), "data", k, "l"] = l_scenarios[i][k] s_data["scenario" + str(i), "data", k, "E"] = (E_prediction[k] / actions_per_hour) s_data["scenario" + str(i), "data", k, "prob"] = prob[i] # Solve OCP sol_time = time.time() xk_opt, Uk_opt = ocp.solve_nlp([s0, lbs, ubs, lbg, ubg], s_data, np.argmax(prob)) solver_time += time.time() - sol_time # Simulate the system after disturbances current_time += timedelta(minutes=10) obs = observations[(observations["date"] >= current_time) & (observations["date"] <= current_time + timedelta(minutes=10 * N))] pv_measured.append(pv_true) l_measured.append(l_true) E_measured.append(E_prediction[0]) time_stamps.append(current_time) Uk_temp = np.copy(Uk_opt) e, uk = utils.primary_controller(B.get_SOC(), Uk_opt, obs["PV"].values[0], obs["L"].values[0]) Pgb_p = np.max([Pgb_p_all[-1], uk[2]]) Pgb_p_all.append(Pgb_p) errors.append(e) Pbc.append(uk[0]) Pbd.append(uk[1]) Pgb.append(uk[2]) Pgs.append(uk[3]) # Calculate price from primary controller temp_sale_diff = uk[3] - Uk_temp[3] temp_buy_diff = uk[2] - Uk_temp[2] if temp_buy_diff >= 0: primary_Pgb = np.abs( ((uk[2] - Uk_temp[2]) * E[E.time == current_time].price.values[0] / actions_per_hour)) primary_Pgs = 0 if temp_sale_diff >= 0: primary_Pgs = np.abs( ((uk[3] - Uk_temp[3]) * E[E.time == current_time].price.values[0] / actions_per_hour)) primary_Pgb = 0 B.simulate_SOC(xk_opt, [uk[0], uk[1]]) step_time = time.time() - step_start sys_metrics.update_metrics( [Pbc[-1], Pbd[-1], Uk_temp[2], Uk_temp[3]], E[E.time == current_time].price.values[0] / actions_per_hour, e, Pgb_p_all[-1], primary_Pgb, primary_Pgs, step_time, ) bar.update(step) sys_metrics.print_metrics(B.x_sim, E_measured) df = utils.create_datafile( [ time_stamps[1:], 100 * np.asarray(B.x_sim[1:]), 100 * np.asarray(B.x_opt[1:]), np.asarray(Pbc) - np.asarray(Pbd), np.asarray(Pgb) - np.asarray(Pgs), ocp.Pbc - ocp.Pbd, ocp.Pgb - ocp.Pgs, np.asarray(errors), pv_measured, l_measured, E_measured, Pgb_p_all[1:], ], [ "date", "SOC_sim", "SOC_opt", "Pb_sim", "Pg_sim", "Pb_opt", "Pg_opt", "Errors", "PV", "Load", "Spot_prices", "P_peak", ], logpath=logpath, ).set_index("date") # Plotting u = np.asarray( [np.asarray(Pbc) - np.asarray(Pbd), np.asarray(Pgb) - np.asarray(Pgs)]) p.plot_from_df( df, ["Pb_sim"], title="Battery Action", upsample=True, legends=["Battery"], logpath=logpath, ) p.plot_from_df( df, ["Pg_sim", "Pg_opt", "P_peak"], title="Grid Control Actions", upsample=True, legends=["Primary Control", "Optimal", "Peak Power"], logpath=logpath, ) p.plot_from_df( df, ["SOC_sim"], ylabel="SOC [%]", title="State of Charge", logpath=logpath, ) p.plot_from_df( df, ["PV", "Load"], title="Measured PV and Load", upsample=True, legends=["PV", "Load"], logpath=logpath, ) p.plot_from_df( df, ["Spot_prices"], title="Spot Prices", ylabel="Price [NOK]", upsample=True, logpath=logpath, ) p.format_figure( fig1, ax1, df.index, title="PV Scenarios", logpath=logpath, ) p.format_figure( fig1, ax2, df.index, title="Load Scenarios", logpath=logpath, ) if conf["plot"]: fig1.tight_layout() if logpath: fig1.savefig(logpath + "scenarios" + ".pdf", format="pdf") plt.show(block=True)
def __init__(self, T, N, N_scenarios): self.T = T self.N = N self.N_scenarios = N_scenarios conf = parse_config() conf_system = conf["system"] # Get system constants self.C_MAX = conf["battery"]["C_MAX"] self.nb_c = conf["battery"]["nb_c"] self.nb_d = conf["battery"]["nb_d"] self.x_min = conf_system["x_min"] self.x_max = conf_system["x_max"] self.x_ref = conf_system["x_ref"] self.Pb_max = conf_system["Pb_max"] self.Pg_max = conf_system["Pg_max"] self.battery_cost = conf_system["battery_cost"] self.peak_cost = conf_system["peak_cost"] self.old_grid_fee = conf_system["old_grid_fee"] self.terminal_cost = conf_system["terminal_cost"] self.verbose = conf_system["verbose"] self.states = struct_symSX([entry("SOC"), entry("Pgb_p")]) self.inputs = struct_symSX([ entry("Pbc"), entry("Pbd"), entry("Pgb"), entry("Pgs"), ]) self.slacks = struct_symSX([ entry("us"), entry("ls"), entry("s1"), entry("s2"), ]) self.w = struct_symSX([ entry("states", struct=self.states, repeat=self.N), entry("inputs", struct=self.inputs, repeat=self.N - 1), ]) self.data = struct_symSX([ entry("pv"), entry("l"), entry("E"), entry("prob"), ]) self.all_data = struct_symSX( [entry("data", struct=self.data, repeat=self.N)]) scenarios = [] s_data = [] for k in range(self.N_scenarios): scenarios.append(entry("scenario" + str(k), struct=self.w)) s_data.append(entry("scenario" + str(k), struct=self.all_data)) self.s = struct_symSX(scenarios) self.s_data = struct_symSX(s_data) self.E = SX.sym("E", self.N) # Initialize system properties self.ode = self.build_ode() self.L = None self.F = None self.solver = None # Keep optimal solutions self.SOC = np.asarray([]) self.Pbc = np.asarray([]) self.Pbd = np.asarray([]) self.Pgs = np.asarray([]) self.Pgb = np.asarray([])
def main(): """ Main function for mpc-scheme with receding horizion. """ conf = utils.parse_config() logpath = None log = input("Do you wish to log this run? ") if log in ["y", "yes", "Yes"]: foldername = input("Do you wish to name logfolder? (enter to skip)") logpath = utils.create_logs_folder(conf["logpath"], foldername) openloop = False predictions = conf["predictions"] print("Using {} predictions.".format(predictions)) actions_per_hour = conf["actions_per_hour"] horizon = conf["simulation_horizon"] simulation_horizon = horizon * actions_per_hour start_time = time.time() step_time = start_time PV, PV_pred, PL, PL_pred, grid_buy, grid_sell = utils.load_data() T = conf["prediction_horizon"] N = conf["prediction_horizon"] * actions_per_hour xk = conf["x_inital"] xk_sim = conf["x_inital"] x_opt = np.asarray([xk]) x_sim = np.asarray([xk]) u0 = np.asarray([]) u1 = np.asarray([]) u2 = np.asarray([]) u3 = np.asarray([]) solver = OptiSolver(N) x, lbx, ubx, lbg, ubg = solver.build_nlp( T, N, ) net_cost_grid = 0 net_cost_bat = 0 J = 0 pv_preds = [PV[0]] pl_preds = [PL[0]] pv_error = [] pl_error = [] plt.figure() if predictions in ["arima", "best"]: pv_model = Arima("PV", order=(3, 1, 2)) pl_model = Arima("PL", order=(1, 1, 4), seasonal_order=(0, 0, 0, 0)) for step in range(simulation_horizon - N): # Update NLP parameters x[0] = xk lbx[0] = xk ubx[0] = xk PV_true = PV[step:step + N] PL_true = PL[step:step + N] if predictions == "constant": # Predicted values equal to measurement pv_ref = np.ones(N) * PV[step] pl_ref = np.ones(N) * PL[step] elif predictions == "arima": # Estimate using ARIMA pv_model.update(PV[step]) pl_model.update(PL[step]) pv_ref = pv_model.predict(T) pl_ref = pl_model.predict(T) elif predictions == "data": pv_ref = PV_pred[step:step + N] pl_ref = PL_pred[step:step + N] elif predictions == "scaled_mean": pv_ref = (PV[step] / PV_pred[step]) * PV_pred[step:step + N] pl_ref = (PL[step] / PL_pred[step]) * PL_pred[step:step + N] elif predictions == "best": pv_model.update(PV[step]) pv_ref = pv_model.predict(T) pl_ref = (PL[step] / PL_pred[step]) * PL_pred[step:step + N] else: # Use true predictions pv_ref = PV_true pl_ref = PL_true pv_preds.append(pv_ref[1]) pl_preds.append(pl_ref[1]) pv_error.append(metrics.rmse(PV_true[0:4], pv_ref[0:4])) pl_error.append(metrics.rmse(PL_true[0:4], pl_ref[0:4])) plt.plot(range(step, step + N), pv_ref, c="b") plt.plot(range(step, step + N), PV_true, c="r") xk_opt, Uk_opt, J_opt = solver.solve_nlp([x, lbx, ubx, lbg, ubg], vertcat(pv_ref, pl_ref)) J += J_opt x_opt = np.append(x_opt, xk_opt[1]) xk_sim, Uk_sim = simulate_SOC( xk_sim, Uk_opt, PV[step], PL[step], solver.F, ) x_sim = np.append(x_sim, xk_sim) if openloop: xk = xk_opt[1] # xk is optimal else: xk = xk_sim uk = [u[0] for u in Uk_opt] u0 = np.append(u0, uk[0]) u1 = np.append(u1, uk[1]) u2 = np.append(u2, uk[2]) u3 = np.append(u3, uk[3]) net_cost_grid += metrics.net_spending_grid(uk, 1.5, actions_per_hour) net_cost_bat += metrics.net_cost_battery( uk, conf["system"]["battery_cost"], actions_per_hour) if step % 50 == 0: print("\nFinshed iteration step {}. Current step took {}s".format( step, np.around(time.time() - step_time, 2))) print("xsim {}%, x_opt {}%".format(np.around(xk_sim, 2), np.around(xk_opt[1], 2))) step_time = time.time() peak_power = np.around(np.max(u2), 2) * 70 E_start = conf["x_inital"] * conf["system"]["C_MAX"] E_end = xk * conf["system"]["C_MAX"] battery_change = np.around(grid_buy * (E_end - E_start), 2) print() print("Error PV prediction:", np.mean(pv_error)) print("Error PL prediction:", np.mean(pl_error)) print("Net spending grid: {} kr".format(np.around(net_cost_grid, 2))) print("Peak power cost: {} kr".format(peak_power)) print("Net spending battery: {} kr".format(np.around(net_cost_bat, 2))) print("Grid + battery spending: {} kr".format( np.around(net_cost_grid + net_cost_bat, 2), )) print("Change in battery energy {} kr".format(battery_change)) print("Total spending:", net_cost_grid + net_cost_bat - battery_change + peak_power) # Plotting u = np.asarray([-u0, u1, u2, -u3]) u_bat = np.asarray([-u0, u1]) u_grid = np.asarray([u2, -u3]) p.plot_control_actions(u, horizon - T, actions_per_hour, logpath) p.plot_control_actions( u_bat, horizon - T, actions_per_hour, logpath, title="Battery Controls", legends=["Battery Charge", "Battery Discharge"], ) p.plot_control_actions( u_grid, horizon - T, actions_per_hour, logpath, title="Grid Controls", legends=["Grid Buy", "Grid Sell"], ) p.plot_SOC(x_sim, horizon - T, logpath) p.plot_data( [x_opt, x_sim], logpath=logpath, legends=["SOC optimal", "SOC simulated"], title="Simulated vs optimal SOC", ) p.plot_data( [PV[:simulation_horizon - N], PL[:simulation_horizon - N]], logpath=logpath, legends=["PV Production", "Load Demands"], title="PV Production & Load Demands", ) p.plot_SOC_control_subplots(x_sim, u, horizon - T, logpath=logpath) stop = time.time() print("\nFinished optimation in {}s".format(np.around( stop - start_time, 2))) utils.save_datafile( [x_opt, x_sim, u0, u1, u2, u3, PV, PV_pred, PL, PL_pred], names=[ "x_opt", "x_sim", "u0", "u1", "u2", "u3", "PV", "PV_pred", "PL", "PL_pred", ], logpath=logpath, ) print("One-step PV RMSE:", metrics.rmse_predictions(PV, pv_preds)) print("One-step Load RMSE:", metrics.rmse_predictions(PL, pl_preds)) if conf["plot_predictions"]: p.plot_predictions_subplots(PV, pv_preds, PL, pl_preds, logpath) plt.show(block=True) plt.ion() plt.close("all")
def nominel_mpc(): """ Main function for mpc-scheme with receding horizion. """ conf = utils.parse_config() datafile = conf["datafile"] loads_trainfile = conf["loads_trainfile"] logpath = None log = input("Log this run? ") if log in ["y", "yes", "Yes"]: foldername = input("Enter logfolder name? (enter to skip) ") logpath = utils.create_logs_folder(conf["logpath"], foldername) openloop = conf["openloop"] perfect_predictions = conf["perfect_predictions"] actions_per_hour = conf["actions_per_hour"] horizon = conf["simulation_horizon"] simulation_horizon = horizon * actions_per_hour T = conf["prediction_horizon"] N = conf["prediction_horizon"] * actions_per_hour start_time = time.time() step_time = start_time # Get data observations = pd.read_csv(datafile, parse_dates=["date"]) # observations = observations[observations["date"] >= datetime(2021, 3, 11)] solcast_forecasts = pd.read_csv( conf["solcast_file"], parse_dates=["time", "collected"] ) current_time = observations.date.iloc[0] forecast = solcast_forecasts[ solcast_forecasts["collected"] == current_time - timedelta(minutes=60) ] obs = observations[observations["date"] == current_time] l = Load(N, loads_trainfile, "L", groundtruth=observations["L"]) E = np.ones(2000) # get_spot_price() B = Battery(T, N, **conf["battery"]) PV = Photovoltaic() Pbc = [] Pbd = [] Pgs = [] Pgb = [] pv_measured = [] l_measured = [] errors = [] ocp = NominelMPC(T, N) sys_metrics = metrics.SystemMetrics() x, lbx, ubx, lbg, ubg = ocp.build_nlp() for step in range(simulation_horizon - N): # Get measurements x["states", 0, "SOC"] = B.get_SOC(openloop) lbx["states", 0, "SOC"] = B.get_SOC(openloop) ubx["states", 0, "SOC"] = B.get_SOC(openloop) pv_true = obs["PV"].values[0] l_true = obs["L"].values[0] pv_measured.append(pv_true) l_measured.append(l_true) # Get new forecasts every hour if current_time.minute == 30: new_forecast = solcast_forecasts[ solcast_forecasts["collected"] == current_time - timedelta(minutes=30) ] if new_forecast.empty: print("Could not find forecast, using old forecast") else: forecast = new_forecast ref = forecast[ (forecast["time"] >= current_time) & (forecast["time"] < current_time + timedelta(minutes=10 * (N + 1))) ] # Create predictions for next period if perfect_predictions: pv_ref = pv[step + 1 : step + N + 1] l1_ref = l1.perfect_pred(step) E_ref = E[step : step + N] else: pv_ref = PV.predict(ref.temp.values, ref.GHI.values) l_ref = l.scaled_mean_pred(l_true, step % 126) E_ref = E[step : step + N] forecasts = ocp.update_forecasts(pv_ref, l_ref, E_ref) xk_opt, Uk_opt = ocp.solve_nlp([x, lbx, ubx, lbg, ubg], forecasts) # Simulate the system after disturbances current_time += timedelta(minutes=10) obs = observations[observations["date"] == current_time] e, uk = utils.calculate_real_u( xk_opt, Uk_opt, obs["PV"].values[0], obs["L"].values[0] ) errors.append(e) Pbc.append(uk[0]) Pbd.append(uk[1]) Pgb.append(uk[2]) Pgs.append(uk[3]) B.simulate_SOC(xk_opt, [uk[0], uk[1]]) sys_metrics.update_metrics( [Pbc[step], Pbd[step], Pgb[step], Pgs[step]], E[step], e ) utils.print_status(step, [B.get_SOC(openloop)], step_time, every=50) step_time = time.time() sys_metrics.calculate_consumption_rate(Pgs, pv_measured) sys_metrics.calculate_dependency_rate(Pgb, l_measured) sys_metrics.print_metrics() # Plotting u = np.asarray( [np.asarray(Pbc) - np.asarray(Pbd), np.asarray(Pgb) - np.asarray(Pgs)] ) if openloop: p.plot_control_actions( np.asarray([ocp.Pbc - ocp.Pbd, ocp.Pgb - ocp.Pgb]), horizon - T, actions_per_hour, logpath, legends=["Battery", "Grid"], title="Optimal Control Actions", ) else: p.plot_control_actions( u, horizon - T, actions_per_hour, logpath, legends=["Battery", "Grid"], title="Simulated Control Actions", ) p.plot_data( np.asarray([B.x_sim, B.x_opt]), title="State of charge", legends=["SOC", "SOC_opt"], ) p.plot_data([np.asarray(errors)], title="Errors") p.plot_data( np.asarray([pv_measured, l_measured]), title="PV & Load", legends=["PV", "L"] ) # p.plot_data(np.asarray([E]), title="Spot Prices", legends=["Spotprice"]) stop = time.time() print("\nFinished optimation in {}s".format(np.around(stop - start_time, 2))) plt.ion() if True: plt.show(block=True)
def scenario_mpc(): """ Main function for mpc-scheme with receding horizion. """ conf = utils.parse_config() datafile = conf["datafile"] loads_trainfile = conf["loads_trainfile"] data = pd.read_csv("./data/data_oct20.csv", parse_dates=["date"]).iloc[::10] logpath = None log = input("Log this run? ") if log in ["y", "yes", "Yes"]: foldername = input("Enter logfolder name? (enter to skip) ") logpath = utils.create_logs_folder(conf["logpath"], foldername) openloop = conf["openloop"] perfect_predictions = conf["perfect_predictions"] actions_per_hour = conf["actions_per_hour"] horizon = conf["simulation_horizon"] simulation_horizon = horizon * actions_per_hour T = conf["prediction_horizon"] N = conf["prediction_horizon"] * actions_per_hour Nr = conf["robust_horizon"] branch_factor = conf["branch_factor"] N_scenarios = branch_factor**Nr start_time = time.time() step_time = start_time # Get data observations = pd.read_csv(datafile, parse_dates=["date"]) # observations = observations[observations["date"] >= datetime(2021, 3, 11)] solcast_forecasts = pd.read_csv(conf["solcast_file"], parse_dates=["time", "collected"]) current_time = observations.date.iloc[0] forecast = solcast_forecasts[solcast_forecasts["collected"] == current_time - timedelta(minutes=60)] obs = observations[observations["date"] == current_time] l = Load(N, loads_trainfile, "L", groundtruth=observations["L"]) E = np.ones(2000) # get_spot_price() B = Battery(T, N, **conf["battery"]) PV = Photovoltaic() Pbc = [] Pbd = [] Pgs = [] Pgb = [] pv_measured = [] l_measured = [] errors = [] # Build reference tree tree, leaf_nodes = build_scenario_tree(N, Nr, branch_factor, np.ones(N + 1), 0, np.ones(N + 1), 0) ocp = ScenarioOCP(T, N, N_scenarios) s_data = ocp.s_data(0) s0, lbs, ubs, lbg, ubg = ocp.build_scenario_ocp() sys_metrics = metrics.SystemMetrics() for step in range(simulation_horizon - N): # Get measurements pv_true = obs["PV"].values[0] l_true = obs["L"].values[0] pv_measured.append(pv_true) l_measured.append(l_true) # Get new forecasts every hour if current_time.minute == 30: new_forecast = solcast_forecasts[solcast_forecasts["collected"] == current_time - timedelta(minutes=30)] if new_forecast.empty: print("Could not find forecast, using old forecast") else: forecast = new_forecast ref = forecast[(forecast["time"] >= current_time) & (forecast["time"] < current_time + timedelta(minutes=10 * (N + 1)))] # Get predictions pv_ref = PV.predict(ref.temp.values, ref.GHI.values) l_ref = l.scaled_mean_pred(l_true, step % 126) root, leaf_nodes = build_scenario_tree(N, Nr, branch_factor, pv_ref, 0.5, l_ref, 0.5) pv_scenarios = get_scenarios(leaf_nodes, "pv") l_scenarios = get_scenarios(leaf_nodes, "l") # Update parameters for i in range(N_scenarios): s0["scenario" + str(i), "states", 0, "SOC"] = B.get_SOC(openloop) lbs["scenario" + str(i), "states", 0, "SOC"] = B.get_SOC(openloop) ubs["scenario" + str(i), "states", 0, "SOC"] = B.get_SOC(openloop) for k in range(N): s_data["scenario" + str(i), "data", k, "pv"] = pv_scenarios[i][k] s_data["scenario" + str(i), "data", k, "l"] = l_scenarios[i][k] s_data["scenario" + str(i), "data", k, "E"] = 1 s_data["scenario" + str(i), "data", k, "prob"] = 1 xk_opt, Uk_opt = ocp.solve_nlp([s0, lbs, ubs, lbg, ubg], s_data) # Simulate the system after disturbances current_time += timedelta(minutes=10) obs = observations[observations["date"] == current_time] e, uk = utils.calculate_real_u(xk_opt, Uk_opt, obs["PV"].values[0], obs["L"].values[0]) errors.append(e) Pbc.append(uk[0]) Pbd.append(uk[1]) Pgb.append(uk[2]) Pgs.append(uk[3]) B.simulate_SOC(xk_opt, [uk[0], uk[1]]) sys_metrics.update_metrics([Pbc[-1], Pbd[-1], Pgb[-1], Pgs[-1]], E[-1], e) utils.print_status(step, [B.get_SOC(openloop)], step_time, every=50) step_time = time.time() sys_metrics.calculate_consumption_rate(Pgs, pv_measured) sys_metrics.calculate_dependency_rate(Pgb, l_measured) sys_metrics.print_metrics() # Plotting u = np.asarray( [np.asarray(Pbc) - np.asarray(Pbd), np.asarray(Pgb) - np.asarray(Pgs)]) p.plot_control_actions( np.asarray([ocp.Pbc - ocp.Pbd, ocp.Pgb - ocp.Pgb]), horizon - T, actions_per_hour, logpath, legends=["Battery", "Grid"], title="Optimal Control Actions", ) p.plot_control_actions( u, horizon - T, actions_per_hour, logpath, legends=["Battery", "Grid"], title="Simulated Control Actions", ) p.plot_data( np.asarray([B.x_sim, B.x_opt]), title="State of charge", legends=["SOC", "SOC_opt"], ) p.plot_data([np.asarray(errors)], title="Errors") p.plot_data( np.asarray([pv_measured, l_measured]), title="PV and Load", legends=["PV", "Load"], ) # p.plot_data(np.asarray([E]), title="Spot Prices", legends=["Spotprice"]) stop = time.time() print("\nFinished optimation in {}s".format(np.around( stop - start_time, 2))) plt.ion() if True: plt.show(block=True)