Example #1
0
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
Example #2
0
    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([])
Example #3
0
    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
Example #4
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([])
Example #6
0
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")
Example #7
0
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)
Example #8
0
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)