Beispiel #1
0
    def test_case3_by_value(self):
        """
        Test for power flow computation.
        """
        case = load_case("case3")
        grid = GridDCOPF(case,
                         base_unit_v=case.base_unit_v,
                         base_unit_p=case.base_unit_p)

        params = StandardParameters(tol=1e-9)
        model = StandardDCOPF(
            f"{case.name} Standard DC OPF",
            grid=grid,
            grid_backend=case.grid_backend,
            base_unit_p=case.base_unit_p,
            base_unit_v=case.base_unit_v,
            params=params,
        )

        model.build_model()
        result = model.solve(verbose=False)
        model.print_results()

        time.sleep(0.1)
        # Test DC Power Flow
        self.assertTrue(
            np.equal(
                result["res_bus"]["delta_pu"].values,
                np.array([0.0, -0.250, -0.375, 0.0, 0.0, 0.0]),
            ).all())
Beispiel #2
0
def load_experience(case_name, agent_name, experience_dir, env_dc=True):
    case_experience_dir = make_dir(
        os.path.join(experience_dir, f"{case_name}-{env_pf(env_dc)}"))

    parameters = CaseParameters(case_name=case_name, env_dc=env_dc)
    case = load_case(case_name, env_parameters=parameters)
    env = case.env

    collector = load_agent_experience(env, agent_name, case_experience_dir)

    return case, collector
        # "l2rpn_wcci_2020",
]:
    if "l2rpn_wcci_2020" in case_name:
        n_steps = 100
    else:
        n_steps = 500
        # n_steps = 10

    case_save_dir = make_dir(
        os.path.join(save_dir, f"{case_name}-{env_pf(env_dc)}"))
    create_logger(logger_name=f"logger", save_dir=case_save_dir)
    """
        Initialize environment.
    """
    parameters = CaseParameters(case_name=case_name, env_dc=env_dc)
    case = load_case(case_name, env_parameters=parameters)

    for agent_name in [
            # "agent-mip",
            # "agent-mip-l2rpn",
            # "agent-mip-q",
            "agent-multistep-mip",
    ]:
        """
            Initialize agent.
        """
        agent = make_test_agent(agent_name, case, **kwargs)
        """
            Experiments.
        """
        experiment_switching.analyse(
Beispiel #4
0
env_dc = True
verbose = False

# Agent parameters
kwargs = {}

for case_name in [
        "rte_case5_example",
        "l2rpn_2019",
        "l2rpn_wcci_2020",
]:
    """
    Initialize environment.
    """
    parameters = CaseParameters(case_name=case_name, env_dc=env_dc)
    case = load_case(case_name, env_parameters=parameters, verbose=verbose)
    env = case.env

    for agent_name in [
            "do-nothing-agent",
            "agent-mip",
            "agent-multistep-mip",
    ]:
        np.random.seed(0)
        """
            Initialize agent.
        """
        # Switching penalty
        if "rte_case5" in case_name:
            kwargs["obj_lambda_action"] = 0.006
        elif "l2rpn_2019" in case_name:
Beispiel #5
0
from lib.action_space import ActionSpaceGenerator
from lib.dc_opf import load_case
from lib.visualizer import pprint

if __name__ == "__main__":
    case_name = "l2rpn_2019"
    # case_name = "rte_case5_example"
    # case_name = "l2rpn_wcci_2020"
    verbose = True

    case = load_case(case_name, verbose=verbose)
    env = case.env
    action_space = env.action_space

    # Generator
    action_generator = ActionSpaceGenerator(env)

    # Grid2Op Generator
    grid2op_actions_topology_set = (
        action_generator.grid2op_get_all_unitary_topologies_set())
    grid2op_actions_line_set = (
        action_generator.grid2op_get_all_unitary_line_status_set())

    # Custom Generator with Analysis and Action Information
    actions_do_nothing = action_space({})
    (
        actions_topology_set,
        actions_topology_set_info,
    ) = action_generator.get_all_unitary_topologies_set(
        verbose=False, filter_one_line_disconnections=False)
    (
Beispiel #6
0
class TestTopologyOptimizationDCOPF(unittest.TestCase):
    """
    Test DC-OPF with line status switching implementation.
    """
    @classmethod
    def setUpClass(cls):
        print("\nDC-OPF with topology optimization tests.\n")

    @staticmethod
    def topology_to_parts(x_topology, n_gen, n_load, n_line):
        x_gen = x_topology[:n_gen]
        x_load = x_topology[n_gen:(n_gen + n_load)]
        x_line_or_1 = x_topology[(n_gen + n_load):(n_gen + n_load + n_line)]
        x_line_or_2 = x_topology[(n_gen + n_load + n_line):(n_gen + n_load +
                                                            2 * n_line)]
        x_line_ex_1 = x_topology[(n_gen + n_load +
                                  2 * n_line):(n_gen + n_load + 3 * n_line)]
        x_line_ex_2 = x_topology[(n_gen + n_load + 3 * n_line):]

        return x_gen, x_load, x_line_or_1, x_line_or_2, x_line_ex_1, x_line_ex_2

    def is_valid_topology(self, x_topology, n_gen, n_load, n_line):
        (
            x_gen,
            x_load,
            x_line_or_1,
            x_line_or_2,
            x_line_ex_1,
            x_line_ex_2,
        ) = self.topology_to_parts(x_topology, n_gen, n_load, n_line)

        cond_line_or = np.less_equal(x_line_or_1 + x_line_or_2, 1).all()
        cond_line_ex = np.less_equal(x_line_ex_1 + x_line_ex_2, 1).all()
        cond_line_disconnected = np.equal(x_line_or_1 + x_line_or_2,
                                          x_line_ex_1 + x_line_ex_2).all()

        return cond_line_or and cond_line_ex and cond_line_disconnected

    def runner_opf_topology_optimization(
        self,
        model,
        verbose=False,
    ):
        np.random.seed(0)
        model.gen["cost_pu"] = np.random.uniform(1.0, 5.0,
                                                 (model.grid.gen.shape[0], ))
        model.build_model()

        if verbose:
            model.print_model()

        if model.params.solver_name == "glpk":
            print("Solver does not support bilinear or quadratic terms.")
            self.assertTrue(True)
            return

        result = model.solve(verbose=verbose)
        result_x = result["res_x"]
        result_objective = result["res_cost"]

        n_gen = model.grid.gen.shape[0]
        n_load = model.grid.load.shape[0]
        n_line = model.grid.line.shape[0]
        """
            BACKEND BRUTE FORCE.
        """

        results_backend = []
        for idx, x_topology in enumerate(
                itertools.product([0, 1], repeat=n_gen + n_load + 4 * n_line)):
            # x_topology = [x_gen, x_load, x_line_or_1, x_line_or_2, x_line_ex_1, x_line_ex_2]
            x_topology = np.array(x_topology, dtype=np.int)

            # Initialization of variables
            (
                x_gen,
                x_load,
                x_line_or_1,
                x_line_or_2,
                x_line_ex_1,
                x_line_ex_2,
            ) = self.topology_to_parts(x_topology, n_gen, n_load, n_line)

            # Check valid topology
            if self.is_valid_topology(x_topology, n_gen, n_load, n_line):
                # Generator bus
                gen_sub_bus = np.ones_like(x_gen, dtype=np.int)
                gen_sub_bus[x_gen.astype(np.bool)] = 2
                gen_bus = [
                    model.grid.sub["bus"][sub_id][sub_bus - 1] for sub_bus,
                    sub_id in zip(gen_sub_bus, model.grid.gen["sub"])
                ]

                # Load bus
                load_sub_bus = np.ones_like(x_load, dtype=np.int)
                load_sub_bus[x_load.astype(np.bool)] = 2
                load_bus = [
                    model.grid.sub["bus"][sub_id][sub_bus - 1] for sub_bus,
                    sub_id in zip(load_sub_bus, model.grid.load["sub"])
                ]

                # Power line status
                line_status = np.logical_and(
                    np.logical_or(x_line_or_1, x_line_or_2),
                    np.logical_or(x_line_ex_1, x_line_ex_2),
                )

                # Power line - Origin bus
                line_or_sub_bus = -np.ones_like(x_line_or_1, dtype=np.int)
                line_or_sub_bus[x_line_or_1.astype(np.bool)] = 1
                line_or_sub_bus[x_line_or_2.astype(np.bool)] = 2
                line_or_bus = np.array([
                    model.grid.sub["bus"][sub_id][sub_bus - 1]
                    if sub_bus != -1 else model.grid.sub["bus"][sub_id][0]
                    for sub_bus, sub_id in zip(line_or_sub_bus,
                                               model.grid.line["sub_or"])
                ])

                # Power line - Extremity bus
                line_ex_sub_bus = -np.ones_like(x_line_ex_1, dtype=np.int)
                line_ex_sub_bus[x_line_ex_1.astype(np.bool)] = 1
                line_ex_sub_bus[x_line_ex_2.astype(np.bool)] = 2
                line_ex_bus = np.array([
                    model.grid.sub["bus"][sub_id][sub_bus - 1]
                    if sub_bus != -1 else model.grid.sub["bus"][sub_id][0]
                    for sub_bus, sub_id in zip(line_ex_sub_bus,
                                               model.grid.line["sub_ex"])
                ])

                # Construct grid for backend
                grid_tmp = model.grid_backend.deepcopy()
                grid_tmp.gen["bus"] = gen_bus
                grid_tmp.load["bus"] = load_bus

                grid_tmp.line["in_service"] = line_status[~model.grid.line.
                                                          trafo]
                grid_tmp.line["from_bus"] = line_or_bus[~model.grid.line.trafo]
                grid_tmp.line["to_bus"] = line_ex_bus[~model.grid.line.trafo]

                grid_tmp.trafo["in_service"] = line_status[
                    model.grid.line.trafo]
                grid_tmp.trafo["hv_bus"] = line_or_bus[model.grid.line.trafo]
                grid_tmp.trafo["lv_bus"] = line_ex_bus[model.grid.line.trafo]

                for gen_id in grid_tmp.gen.index.values:
                    pp.create_poly_cost(
                        grid_tmp,
                        gen_id,
                        "gen",
                        cp1_eur_per_mw=model.convert_per_unit_to_mw(
                            model.grid.gen["cost_pu"][gen_id]),
                    )

                print(f"{len(results_backend) + 1}/{idx}: Running DC-OPF ...")
                try:
                    pp.rundcopp(grid_tmp)
                    valid = True
                except (pp.optimal_powerflow.OPFNotConverged, IndexError) as e:
                    grid_tmp.res_cost = 0.0
                    print(e)
                    continue

                load_p = model.convert_mw_to_per_unit(
                    grid_tmp.load["p_mw"].sum())
                gen_p = model.convert_mw_to_per_unit(
                    grid_tmp.res_gen["p_mw"].sum())
                valid = valid and np.abs(gen_p - load_p) < 1e-6

                if (model.params.obj_gen_cost and model.params.obj_reward_quad
                        and model.params.solver_name != "glpk"):
                    objective = (grid_tmp.res_cost + np.square(
                        model.convert_mw_to_per_unit(
                            grid_tmp.res_line["p_from_mw"]) /
                        model.grid.line["max_p_pu"]).sum())
                elif (model.params.obj_reward_quad
                      and model.params.solver_name != "glpk"):
                    objective = +np.square(
                        model.convert_mw_to_per_unit(
                            grid_tmp.res_line["p_from_mw"]) /
                        model.grid.line["max_p_pu"]).sum()
                else:
                    objective = grid_tmp.res_cost

                results_backend.append({
                    "x":
                    np.concatenate((
                        x_gen,
                        x_load,
                        x_line_or_1,
                        x_line_or_2,
                        x_line_ex_1,
                        x_line_ex_2,
                    )),
                    "gen_bus":
                    gen_bus,
                    "load_bus":
                    load_bus,
                    "line_or_bus":
                    line_or_bus,
                    "line_ex_bus":
                    line_ex_bus,
                    "line_status":
                    line_status.astype(int),
                    "valid":
                    valid,
                    "objective":
                    np.round(objective, 3),
                    "load_p":
                    load_p,
                    "gen_p":
                    np.round(gen_p, 3),
                })

        results_backend = pd.DataFrame(results_backend)

        # Check with brute force solution
        objective_brute = results_backend["objective"][
            results_backend["valid"]].min()
        hot_brute = np.abs(results_backend["objective"].values -
                           objective_brute) < 0.05
        indices_brute = hot_to_indices(hot_brute)
        status_brute = results_backend["x"][indices_brute]

        match_idx = [
            idx for idx, line_status in zip(indices_brute, status_brute)
            if np.equal(line_status, result_x).all()
        ]

        # Compare
        results_backend["candidates"] = hot_brute
        results_backend["result_objective"] = np.nan
        results_backend["result_objective"][match_idx] = np.round(
            result_objective, 3)

        print(f"\n{model.name}\n")
        print(f"Solver: {result_objective}")
        print(results_backend[[
            "gen_bus",
            "load_bus",
            "line_or_bus",
            "line_ex_bus",
            "line_status",
            "load_p",
            "gen_p",
            "valid",
            "candidates",
            "objective",
            "result_objective",
        ]][results_backend["candidates"]
           & results_backend["valid"]].to_string())

        time.sleep(0.1)
        self.assertTrue(bool(match_idx))

    def test_case3_topology(self):
        case = load_case("case3")
        grid = GridDCOPF(case,
                         base_unit_v=case.base_unit_v,
                         base_unit_p=case.base_unit_p)

        params = SinglestepTopologyParameters(
            obj_gen_cost=True,
            obj_reward_lin=False,
            obj_reward_quad=True,
            obj_reward_max=False,
            obj_lin_gen_penalty=False,
            obj_quad_gen_penalty=False,
        )
        model = TopologyOptimizationDCOPF(
            f"{case.name} DC OPF Topology Optimization",
            grid=grid,
            grid_backend=case.grid_backend,
            base_unit_p=case.base_unit_p,
            base_unit_v=case.base_unit_v,
            params=params,
        )

        self.runner_opf_topology_optimization(model, verbose=False)
Beispiel #7
0
class TestStandardDCOPF(unittest.TestCase):
    """
    Test standard DC-OPF implementation.
    """
    @classmethod
    def setUpClass(cls):
        print("\nStandard DC-OPF tests.\n")

    def runner_opf(self, model, n_tests=20, eps=1e-4, verbose=False):
        conditions = list()
        for i in range(n_tests):
            np.random.seed(i)
            model.gen["cost_pu"] = np.random.uniform(
                1.0, 5.0, (model.grid.gen.shape[0], ))

            model.build_model()
            result = model.solve_and_compare(verbose=verbose)

            conditions.append({
                "cost":
                np.less_equal(result["res_cost"]["diff"], eps).all(),
                "bus":
                np.less_equal(result["res_bus"]["diff"], eps).all(),
                "line":
                np.less_equal(result["res_line"]["diff"], eps).all(),
                "gen":
                np.less_equal(result["res_gen"]["diff"], eps).all(),
                "load":
                np.less_equal(result["res_load"]["diff"], eps).all(),
                "ext_grid":
                np.less_equal(result["res_ext_grid"]["diff"], eps).all(),
                "trafo":
                np.less_equal(result["res_trafo"]["diff"], eps).all(),
                "line_loading":
                np.less_equal(result["res_line"]["diff_loading"],
                              100 * eps).all(),
                "trafo_loading":
                np.less_equal(result["res_trafo"]["diff_loading"],
                              100 * eps).all(),
            })

        conditions = pd.DataFrame(conditions)
        conditions["passed"] = np.all(conditions.values, axis=-1)

        print(f"\n\n{model.name}\n")
        print(conditions.to_string())

        time.sleep(0.1)
        # Test DC Power Flow
        self.assertTrue(conditions["passed"].values.all())

    def test_case3(self):
        case = load_case("case3")
        grid = GridDCOPF(case,
                         base_unit_v=case.base_unit_v,
                         base_unit_p=case.base_unit_p)

        params = StandardParameters(tol=1e-9)
        model = StandardDCOPF(
            f"{case.name} Standard DC OPF",
            grid=grid,
            grid_backend=case.grid_backend,
            base_unit_p=case.base_unit_p,
            base_unit_v=case.base_unit_v,
            params=params,
        )

        self.runner_opf(model, verbose=False)
Beispiel #8
0
class TestLineSwitchingDCOPF(unittest.TestCase):
    """
    Test DC-OPF with line status switching implementation.
    """
    @classmethod
    def setUpClass(cls):
        print("\nDC-OPF with line switching tests.\n")

    def runner_opf_line_switching(
        self,
        model,
        grid,
        verbose=False,
    ):
        np.random.seed(0)
        model.gen["cost_pu"] = np.random.uniform(1.0, 5.0,
                                                 (model.grid.gen.shape[0], ))
        model.build_model()

        if verbose:
            model.print_model()
        """
            BACKEND BRUTE FORCE.
        """

        # Construct all possible configurations
        line_statuses = list()
        for i in range(model.params.n_max_line_status_changed +
                       1):  # Number of line disconnection 0, 1, ..., n
            line_statuses.extend([
                ~indices_to_hot(
                    list(line_status),
                    length=grid.line.shape[0],
                    dtype=np.bool,
                )
                for line_status in itertools.combinations(grid.line.index, i)
            ])

        results_backend = pd.DataFrame(columns=[
            "status", "objective", "loads_p", "generators_p", "valid"
        ])

        for idx, status in enumerate(line_statuses):
            model.grid_backend.line["in_service"] = status[~grid.line.trafo]
            model.grid_backend.trafo["in_service"] = status[grid.line.trafo]
            result_backend = model.solve_backend()

            if (model.params.gen_cost and model.params.line_margin
                    and model.params.solver_name != "glpk"):
                objective = (result_backend["res_cost"] +
                             np.square(result_backend["res_line"]["p_pu"] /
                                       model.line["max_p_pu"]).sum())
            elif model.params.line_margin and model.params.solver_name != "glpk":
                objective = np.square(result_backend["res_line"]["p_pu"] /
                                      model.line["max_p_pu"]).sum()
            else:
                objective = result_backend["res_cost"]

            loads_p = grid.load["p_pu"].sum()
            generators_p = result_backend["res_gen"]["p_pu"].sum()
            valid = result_backend["valid"]

            results_backend = results_backend.append(
                {
                    "status": tuple(status),
                    "objective": objective,
                    "loads_p": loads_p,
                    "generators_p": generators_p,
                    "valid": valid,
                },
                ignore_index=True,
            )

        # Solve for optimal line status configuration
        result = model.solve(verbose=verbose)
        result_status = result["res_x"]
        result_objective = result["res_cost"]
        result_gap = result[
            "res_gap"]  # Gap for finding the optimal configuration

        if verbose:
            model.print_results()

        # Check with brute force solution
        objective_brute = results_backend["objective"][
            results_backend["valid"]].min()

        hot_brute = (results_backend["objective"].values <
                     (1 + result_gap) * objective_brute + result_gap)
        hot_brute = np.logical_and(hot_brute, results_backend["valid"])
        indices_brute = hot_to_indices(hot_brute)
        status_brute = results_backend["status"][indices_brute]

        match_idx = [
            idx for idx, line_status in zip(indices_brute, status_brute)
            if np.equal(line_status, result_status).all()
        ]

        # Compare
        results_backend["candidates"] = hot_brute
        results_backend["result_objective"] = np.nan
        results_backend["result_objective"][match_idx] = result_objective

        solution_idx = [
            idx for idx, line_status in zip(results_backend.index,
                                            results_backend["status"])
            if np.equal(line_status, result_status).all()
        ]
        results_backend["solution"] = 0
        results_backend["solution"][solution_idx] = 1

        results_backend["status"] = [
            " ".join(np.array(line_status).astype(int).astype(str))
            for line_status in results_backend["status"]
        ]

        results_backend = results_backend[results_backend["valid"]
                                          & results_backend["candidates"]]
        print(f"\n{model.name}\n")
        print(f"Solver: {result_objective}")
        print(results_backend.to_string())

        time.sleep(0.1)
        self.assertTrue(bool(match_idx))

    def test_case3_line_switching(self):
        case = load_case("case3")
        grid = GridDCOPF(case,
                         base_unit_v=case.base_unit_v,
                         base_unit_p=case.base_unit_p)

        params = LineSwitchingParameters(n_max_line_status_changed=2, tol=1e-9)
        model = LineSwitchingDCOPF(
            f"{case.name} DC OPF Line Switching",
            grid=grid,
            grid_backend=case.grid_backend,
            base_unit_p=case.base_unit_p,
            base_unit_v=case.base_unit_v,
            params=params,
        )

        self.runner_opf_line_switching(model, grid, verbose=False)
Beispiel #9
0
                         base_unit_p=case.base_unit_p)

        params = StandardParameters(tol=1e-9)
        model = StandardDCOPF(
            f"{case.name} Standard DC OPF",
            grid=grid,
            grid_backend=case.grid_backend,
            base_unit_p=case.base_unit_p,
            base_unit_v=case.base_unit_v,
            params=params,
        )

        self.runner_opf(model, verbose=False)

    def test_case4(self):
        case = load_case("case4")
        grid = GridDCOPF(case,
                         base_unit_v=case.base_unit_v,
                         base_unit_p=case.base_unit_p)

        params = StandardParameters(tol=1e-9)
        model = StandardDCOPF(
            f"{case.name} Standard DC OPF",
            grid=grid,
            grid_backend=case.grid_backend,
            base_unit_p=case.base_unit_p,
            base_unit_v=case.base_unit_v,
            params=params,
        )

        self.runner_opf(model, verbose=False)