예제 #1
0
 def init(self):
     if self.pf == 'pf':
         pp.runpp(self.network)
     elif self.pf == 'dcpf':
         pp.rundcpp(self.network)
     elif self.pf == 'opf':
         pp.runopp(self.network)
     else:
         pp.rundcopp(self.network)
예제 #2
0
def test_dcopf_poly(simple_opf_test_net):
    net = simple_opf_test_net
    pp.create_poly_cost(net, 0, "gen", cp1_eur_per_mw=100)
    # run OPF
    pp.rundcopp(net, )

    # check and assert result
    logger.debug("test_simplest_voltage")
    logger.debug("res_gen:\n%s" % net.res_gen)
    logger.debug("res_ext_grid:\n%s" % net.res_ext_grid)
    logger.debug("res_bus.vm_pu: \n%s" % net.res_bus.vm_pu)
    assert abs(100 * net.res_gen.p_mw.values - net.res_cost) < 1e-3
예제 #3
0
def test_dcopf_poly(simple_opf_test_net):
    net = simple_opf_test_net
    pp.create_polynomial_cost(net, 0, "gen", np.array([100, 0]))
    # run OPF
    pp.rundcopp(net, verbose=False)

    # check and assert result
    logger.debug("test_simplest_voltage")
    logger.debug("res_gen:\n%s" % net.res_gen)
    logger.debug("res_ext_grid:\n%s" % net.res_ext_grid)
    logger.debug("res_bus.vm_pu: \n%s" % net.res_bus.vm_pu)
    assert abs(100 * net.res_gen.p_kw.values - net.res_cost) < 1e-3
예제 #4
0
def test_dcopf_pwl(simple_opf_test_net):
    # create net
    net = simple_opf_test_net
    pp.create_pwl_cost(net, 0, "gen"     , [[0, 100, 100], [100, 200, 100]])
    pp.create_pwl_cost(net, 0, "ext_grid", [[0, 100, 0], [100, 200, 0]])
    # run OPF
    pp.rundcopp(net, )
    assert net["OPF_converged"]

    # check and assert result
    logger.debug("test_simplest_voltage")
    logger.debug("res_gen:\n%s" % net.res_gen)
    logger.debug("res_ext_grid:\n%s" % net.res_ext_grid)
    logger.debug("res_bus.vm_pu: \n%s" % net.res_bus.vm_pu)
    assert abs(100 * net.res_gen.p_mw.values - net.res_cost) < 1e-3
예제 #5
0
def test_dcopf_pwl(simple_opf_test_net):
    # create net
    net = simple_opf_test_net
    # pp.create_polynomial_cost(net, 0, "gen", np.array([-100, 0]))
    pp.create_piecewise_linear_cost(
        net, 0, "gen", np.array([[-200, -20000], [-100, -10000], [0, 0]]))
    # run OPF
    pp.rundcopp(net, verbose=False)
    assert net["OPF_converged"]

    # check and assert result
    logger.debug("test_simplest_voltage")
    logger.debug("res_gen:\n%s" % net.res_gen)
    logger.debug("res_ext_grid:\n%s" % net.res_ext_grid)
    logger.debug("res_bus.vm_pu: \n%s" % net.res_bus.vm_pu)
    assert abs(100 * net.res_gen.p_kw.values - net.res_cost) < 1e-3
예제 #6
0
 def step(self, *args, **kwargs):
     if self.pf == 'pf':
         a = pp.runpp(self.network)
     elif self.pf == 'dcpf':
         a = pp.rundcpp(self.network)
     elif self.pf == 'opf':
         a = pp.runopp(self.network)
     else:
         a = pp.rundcopp(self.network)
     return a
예제 #7
0
    def solve_backend(self, verbose=False):
        for gen_id in self.gen.index.values:
            pp.create_poly_cost(
                self.grid_backend,
                gen_id,
                "gen",
                cp1_eur_per_mw=self.convert_per_unit_to_mw(
                    self.gen["cost_pu"][gen_id]),
            )

        try:
            pp.rundcopp(
                self.grid_backend,
                verbose=verbose,
                suppress_warnings=True,
                delta=self.params.tol,
            )
            valid = True
        except pp.optimal_powerflow.OPFNotConverged as e:
            valid = False
            print(e)

        self._solve_save_backend()

        generators_p = self.grid_backend.res_gen["p_mw"].sum()
        ext_grids_p = self.grid_backend.res_ext_grid["p_mw"].sum()
        loads_p = self.grid_backend.load["p_mw"].sum()
        res_loads_p = self.grid_backend.res_load["p_mw"].sum()
        valid = (valid and np.abs(generators_p + ext_grids_p - loads_p) < 1e-2
                 and np.abs(res_loads_p - loads_p) < 1e-2)

        result = {
            "res_cost": self.grid_backend.res_cost,
            "res_bus": self.grid_backend.res_bus,
            "res_line": self.grid_backend.res_line,
            "res_gen": self.grid_backend.res_gen,
            "res_load": self.grid_backend.res_load,
            "res_ext_grid": self.grid_backend.res_ext_grid,
            "res_trafo": self.grid_backend.res_trafo,
            "valid": valid,
        }
        return result
예제 #8
0
def test_dcopf_pwl():
    # create net
    net = pp.create_empty_network()
    pp.create_bus(net, vn_kv=10.)
    pp.create_bus(net, vn_kv=.4)
    pp.create_gen(net,
                  1,
                  p_kw=-100,
                  controllable=True,
                  max_p_kw=-5,
                  min_p_kw=-150,
                  max_q_kvar=50,
                  min_q_kvar=-50)
    pp.create_ext_grid(net, 0)
    pp.create_load(net, 1, p_kw=20, controllable=False)
    pp.create_line_from_parameters(net,
                                   0,
                                   1,
                                   50,
                                   name="line2",
                                   r_ohm_per_km=0.876,
                                   c_nf_per_km=260.0,
                                   max_i_ka=0.123,
                                   x_ohm_per_km=0.1159876,
                                   max_loading_percent=100)
    # pp.create_polynomial_cost(net, 0, "gen", array([-100, 0]))
    pp.create_piecewise_linear_cost(
        net, 0, "gen", array([[-200, 20000], [-100, 10000], [0, 0]]))
    # run OPF
    pp.rundcopp(net, verbose=False)
    assert net["OPF_converged"]

    # check and assert result
    logger.debug("test_simplest_voltage")
    logger.debug("res_gen:\n%s" % net.res_gen)
    logger.debug("res_ext_grid:\n%s" % net.res_ext_grid)
    logger.debug("res_bus.vm_pu: \n%s" % net.res_bus.vm_pu)
    assert abs(100 * net.res_gen.p_kw.values - net.res_cost) < 1e-3
def OPA_model(net, f_bus, f_gen, f_load, f_line, f_trafo):

    # This variable allows to stop the function when a total blackout situation is reached
    total_blackout = 0

    # Set failed elements as out of service.
    # In the case of failed buses, all the elements connected to the bus are also removed
    f_bus = list(f_bus)
    pp.set_element_status(net, f_bus, in_service=False)
    f_gen = list(f_gen)
    net.gen.loc[f_gen, 'in_service'] = False
    f_load = list(f_load)
    net.load.loc[f_load, 'in_service'] = False
    f_line = list(f_line)
    net.line.loc[f_line, 'in_service'] = False
    f_trafo = list(f_trafo)
    net.trafo.loc[f_trafo, 'in_service'] = False

    # Variable for entering in the while loop
    cascade = True

    # Store the initial power level of each generator

    intermediate_gen_power = dict(net.gen.loc[:, 'p_mw'])
    order_dict_gen = collections.OrderedDict(
        sorted(intermediate_gen_power.items()))
    intermediate_gen_power = list(order_dict_gen.values())

    intermediate_load_power = dict(net.load.loc[:, 'p_mw'])
    order_dict_load = collections.OrderedDict(
        sorted(intermediate_load_power.items()))
    intermediate_load_power = list(order_dict_load.values())

    while cascade == True:

        # Create a networkx graph of the power network
        G = pp.topology.create_nxgraph(net)
        # Create a list of sets containing the islands of the network
        list_islands = list(nx.connected_components(G))

        # Create a list of buses with generators in service
        list_bus_with_gen_in_service = list(
            net.gen.bus[net.gen.in_service == True])
        list_bus_with_gen_in_service = list(set(list_bus_with_gen_in_service))
        list_bus_with_gen_in_service.sort()
        list_bus_with_slack_gen_in_service = list(
            net.gen.bus[net.gen.in_service == True][net.gen.slack == True])

        # Check the configuration of each island in the power network
        for island in list_islands:
            #print(island)
            island = list(island)

            # Check if the island has an external grid. If yes, it is ok and you can proceed to the next island.
            # If no, check if there is an availabl generator in the island. If yes, turn it into an external grid.
            # If no, go to the next island.
            if set(list_bus_with_slack_gen_in_service).isdisjoint(island):
                if set(list_bus_with_gen_in_service).isdisjoint(island):
                    pass
                else:
                    set_island = set(island)
                    bus_with_gen_in_island = list(
                        set_island.intersection(list_bus_with_gen_in_service))
                    slack_gen = list(net.gen.index[net.gen.bus ==
                                                   bus_with_gen_in_island[0]])
                    net.gen.loc[slack_gen[0], 'slack'] = True
            else:
                pass

        # Set the isolated islands (without ext. grid/slack gen.) out of service
        pp.set_isolated_areas_out_of_service(net)

        # Try to run a DC OPF
        try:
            pp.rundcopp(net, verbose=False)
            # If it converges, save a the power level at each generators and ext grids
            intermediate_gen_power = dict(net.res_gen.loc[:, 'p_mw'])
            order_dict_gen = collections.OrderedDict(
                sorted(intermediate_gen_power.items()))
            intermediate_gen_power = list(order_dict_gen.values())

            intermediate_load_power = dict(net.res_load.loc[:, 'p_mw'])
            order_dict_load = collections.OrderedDict(
                sorted(intermediate_load_power.items()))
            intermediate_load_power = list(order_dict_load.values())

        # If the DC OPF does not converge, increase the loads costs
        # A while loop which decreases the loads costs until the DC OPF converges or the costs reach a value of 0
        except pp.OPFNotConverged:
            #print(f_line, f_bus, f_trafo)
            print('CONVERGENCE PROBLEMS')
            cost = 1
            load_cost = -100
            while cost == 1:
                load_cost += 2
                #print(load_cost)
                net.poly_cost.cp1_eur_per_mw[net.poly_cost.et ==
                                             'load'] = load_cost
                try:
                    pp.rundcopp(net)
                    #pp.rundcopp(net, SCPDIPM_RED_IT=100, PDIPM_COSTTOL = 1e-3, PDIPM_GRADTOL = 1e-3)
                    intermediate_gen_power = dict(net.res_gen.loc[:, 'p_mw'])
                    order_dict_gen = collections.OrderedDict(
                        sorted(intermediate_gen_power.items()))
                    intermediate_gen_power = list(order_dict_gen.values())

                    intermediate_load_power = dict(net.res_load.loc[:, 'p_mw'])
                    order_dict_load = collections.OrderedDict(
                        sorted(intermediate_load_power.items()))
                    intermediate_load_power = list(order_dict_load.values())

                    cost = 0
                except pp.OPFNotConverged:
                    pass
                # If the loads costs are set to 0, run a DC power flow with the last available generators power levels
                if load_cost == 0:
                    net.gen.loc[:, 'p_mw'] = intermediate_gen_power
                    net.load.loc[:, 'p_mw'] = intermediate_load_power
                    pp.rundcpp(net)
                    break

        # This is in case to generators is available (everything is failed basically)
        except UserWarning:
            total_blackout = 1
            break
        # If there is a total blackout situation, break the loop
        if total_blackout == 1:
            break

        # Create a list of lines still in service
        list_line_in_service = list(
            net.line.loc[net.line.in_service == True].index)
        list_line_in_service.sort()
        # This variable is to check if there are new failures due to overloads
        new_failure = 0
        for line in list_line_in_service:
            level_loading = net.res_line.loc[line, 'loading_percent']
            # If a line a loading >= the 99% of its maximum capacity, it is considered overloaded
            if level_loading >= 99:
                print('OVERLOADS')
                new_failure = 1
                net.line.loc[line, 'in_service'] = False

        # Same for transformer lines
        list_trafo_in_service = list(
            net.trafo.loc[net.trafo.in_service == True].index)
        list_trafo_in_service.sort()
        for trafo in list_trafo_in_service:
            level_loading = net.res_trafo.loc[trafo, 'loading_percent']
            if level_loading >= 99:
                print('OVERLOADS')
                new_failure = 1
                net.trafo.loc[trafo, 'in_service'] = False

        # If there are no overloads, break the while loop
        if new_failure == 0:
            break

    # Save the final power levels of each generator and load
    if total_blackout == 0:
        final_state_load = dict(net.res_load.loc[:, 'p_mw'])
        order_dict_load = collections.OrderedDict(
            sorted(final_state_load.items()))
        list_load_power = list(order_dict_load.values())

        final_state_gen = dict(net.res_gen.loc[:, 'p_mw'])
        order_dict_gen = collections.OrderedDict(
            sorted(final_state_gen.items()))
        list_gen_power = list(order_dict_gen.values())

        list_gen_power = np.array(list_gen_power)
        list_load_power = np.array(list_load_power)

        for i, j in enumerate(list_gen_power):
            if math.isnan(j):
                list_gen_power[i] = 0
        for i, j in enumerate(list_load_power):
            if math.isnan(j):
                list_load_power[i] = 0
    elif total_blackout == 1:
        list_gen_power = np.zeros(len(net.gen.index))
        list_load_power = np.zeros(len(net.load.index))

    return (list_gen_power, list_load_power)
예제 #10
0
파일: power.py 프로젝트: apalom/OPF
 def optimal_dc(self):
     pp.rundcopp(self.net)
     if not self.opf_converged():
         raise pp.OPFNotConverged
     return self._results()
예제 #11
0
 def optimal_dc(self):
     try:
         pp.rundcopp(self.net)
     except pp.OPFNotConverged:
         return None
     return self._results()
예제 #12
0
def validate_from_ppc(
        ppc_net,
        net,
        pf_type="runpp",
        max_diff_values={
            "bus_vm_pu": 1e-6,
            "bus_va_degree": 1e-5,
            "branch_p_mw": 1e-6,
            "branch_q_mvar": 1e-6,
            "gen_p_mw": 1e-6,
            "gen_q_mvar": 1e-6
        },
        run=True):
    """
    This function validates the pypower case files to pandapower net structure conversion via a \
    comparison of loadflow calculation results. (Hence the opf cost conversion is not validated.)

    INPUT:

        **ppc_net** - The pypower case file, which must already contain the pypower powerflow
            results or pypower must be importable.

        **net** - The pandapower network.

    OPTIONAL:

        **pf_type** ("runpp", string) - Type of validated power flow. Possible are ("runpp",
            "rundcpp", "runopp", "rundcopp")

        **max_diff_values** - Dict of maximal allowed difference values. The keys must be
        'vm_pu', 'va_degree', 'p_branch_mw', 'q_branch_mvar', 'p_gen_mw' and 'q_gen_mvar' and
        the values floats.

        **run** (True, bool or list of two bools) - changing the value to False avoids trying to run
            (optimal) loadflows. Giving a list of two bools addresses first pypower and second
            pandapower.

    OUTPUT:

        **conversion_success** - conversion_success is returned as False if pypower or pandapower
        cannot calculate a powerflow or if the maximum difference values (max_diff_values )
        cannot be hold.

    EXAMPLE:

        import pandapower.converter as pc

        net = cv.from_ppc(ppc_net, f_hz=50)

        conversion_success = cv.validate_from_ppc(ppc_net, net)

    NOTE:

        The user has to take care that the loadflow results already are included in the provided \
        ppc_net or pypower is importable.
    """
    # check in case of optimal powerflow comparison whether cost information exist
    if "opp" in pf_type:
        if not (len(net.polynomial_cost) | len(net.piecewise_linear_cost)):
            if "gencost" in ppc_net:
                if not len(ppc_net["gencost"]):
                    logger.debug(
                        'ppc and pandapower net do not include cost information.'
                    )
                    return True
                else:
                    logger.error(
                        'The pandapower net does not include cost information.'
                    )
                    return False
            else:
                logger.debug(
                    'ppc and pandapower net do not include cost information.')
                return True

    # guarantee run parameter as list, for pypower and pandapower (optimal) powerflow run
    run = [run, run] if isinstance(run, bool) else run

    # --- check pypower powerflow success, if possible
    if pypower_import and run[0]:
        try:
            if pf_type == "runpp":
                ppc_net = runpf.runpf(ppc_net, ppopt)[0]
            elif pf_type == "rundcpp":
                ppc_net = rundcpf.rundcpf(ppc_net, ppopt)[0]
            elif pf_type == "runopp":
                ppc_net = runopf.runopf(ppc_net, ppopt)
            elif pf_type == "rundcopp":
                ppc_net = rundcopf.rundcopf(ppc_net, ppopt)
            else:
                raise ValueError("The pf_type %s is unknown" % pf_type)
        except:
            logger.debug("The pypower run did not work.")
    ppc_success = True
    if 'success' in ppc_net.keys():
        if ppc_net['success'] != 1:
            ppc_success = False
            logger.error(
                "The given ppc data indicates an unsuccessful pypower powerflow: "
                + "'ppc_net['success'] != 1'")
    if (ppc_net['branch'].shape[1] < 17):
        ppc_success = False
        logger.error(
            "The shape of given ppc data indicates missing pypower powerflow results."
        )

    # --- try to run a pandapower powerflow
    if run[1]:
        if pf_type == "runpp":
            try:
                pp.runpp(net,
                         init="dc",
                         calculate_voltage_angles=True,
                         trafo_model="pi")
            except pp.LoadflowNotConverged:
                try:
                    pp.runpp(net,
                             calculate_voltage_angles=True,
                             init="flat",
                             trafo_model="pi")
                except pp.LoadflowNotConverged:
                    try:
                        pp.runpp(net,
                                 trafo_model="pi",
                                 calculate_voltage_angles=False)
                        if "bus_va_degree" in max_diff_values.keys():
                            max_diff_values[
                                "bus_va_degree"] = 1e2 if max_diff_values[
                                    "bus_va_degree"] < 1e2 else max_diff_values[
                                        "bus_va_degree"]
                        logger.info("voltage_angles could be calculated.")
                    except pp.LoadflowNotConverged:
                        logger.error(
                            'The pandapower powerflow does not converge.')
        elif pf_type == "rundcpp":
            try:
                pp.rundcpp(net, trafo_model="pi")
            except pp.LoadflowNotConverged:
                logger.error('The pandapower dc powerflow does not converge.')
        elif pf_type == "runopp":
            try:
                pp.runopp(net, init="flat", calculate_voltage_angles=True)
            except pp.OPFNotConverged:
                try:
                    pp.runopp(net, init="pf", calculate_voltage_angles=True)
                except (pp.OPFNotConverged, pp.LoadflowNotConverged, KeyError):
                    try:
                        pp.runopp(net,
                                  init="flat",
                                  calculate_voltage_angles=False)
                        logger.info("voltage_angles could be calculated.")
                        if "bus_va_degree" in max_diff_values.keys():
                            max_diff_values[
                                "bus_va_degree"] = 1e2 if max_diff_values[
                                    "bus_va_degree"] < 1e2 else max_diff_values[
                                        "bus_va_degree"]
                    except pp.OPFNotConverged:
                        try:
                            pp.runopp(net,
                                      init="pf",
                                      calculate_voltage_angles=False)
                            if "bus_va_degree" in max_diff_values.keys():
                                max_diff_values[
                                    "bus_va_degree"] = 1e2 if max_diff_values[
                                        "bus_va_degree"] < 1e2 else max_diff_values[
                                            "bus_va_degree"]
                            logger.info("voltage_angles could be calculated.")
                        except (pp.OPFNotConverged, pp.LoadflowNotConverged,
                                KeyError):
                            logger.error(
                                'The pandapower optimal powerflow does not converge.'
                            )
        elif pf_type == "rundcopp":
            try:
                pp.rundcopp(net)
            except pp.LoadflowNotConverged:
                logger.error(
                    'The pandapower dc optimal powerflow does not converge.')
        else:
            raise ValueError("The pf_type %s is unknown" % pf_type)

    # --- prepare powerflow result comparison by reordering pp results as they are in ppc results
    if not ppc_success:
        return False
    if "opp" in pf_type:
        if not net.OPF_converged:
            return
    elif not net.converged:
        return False

    # --- store pypower powerflow results
    ppc_res = dict.fromkeys(ppc_elms)
    ppc_res["branch"] = ppc_net['branch'][:, 13:17]
    ppc_res["bus"] = ppc_net['bus'][:, 7:9]
    ppc_res["gen"] = ppc_net['gen'][:, 1:3]

    # --- pandapower bus result table
    pp_res = dict.fromkeys(ppc_elms)
    pp_res["bus"] = array(net.res_bus.sort_index()[['vm_pu', 'va_degree']])

    # --- pandapower gen result table
    pp_res["gen"] = zeros([1, 2])
    # consideration of parallel generators via storing how much generators have been considered
    # each node
    # if in ppc is only one gen -> numpy initially uses one dim array -> change to two dim array
    if len(ppc_net["gen"].shape) == 1:
        ppc_net["gen"] = array(ppc_net["gen"], ndmin=2)
    GENS = DataFrame(ppc_net['gen'][:, [0]].astype(int))
    GEN_uniq = GENS.drop_duplicates()
    already_used_gen = Series(zeros(GEN_uniq.shape[0]).astype(int),
                              index=[int(v) for v in GEN_uniq.values])
    change_q_compare = []
    for i, j in GENS.iterrows():
        current_bus_type, current_bus_idx, same_bus_gen_idx, first_same_bus_in_service_gen_idx, \
            last_same_bus_in_service_gen_idx = _gen_bus_info(ppc_net, i)
        if current_bus_type == 3 and i == first_same_bus_in_service_gen_idx:
            pp_res["gen"] = append(
                pp_res["gen"],
                array(net.res_ext_grid[net.ext_grid.bus == current_bus_idx][[
                    'p_mw', 'q_mvar'
                ]]).reshape((1, 2)), 0)
        elif current_bus_type == 2 and i == first_same_bus_in_service_gen_idx:
            pp_res["gen"] = append(
                pp_res["gen"],
                array(net.res_gen[net.gen.bus == current_bus_idx][[
                    'p_mw', 'q_mvar'
                ]]).reshape((1, 2)), 0)
        else:
            pp_res["gen"] = append(
                pp_res["gen"],
                array(net.res_sgen[net.sgen.bus == current_bus_idx][[
                    'p_mw', 'q_mvar'
                ]])[already_used_gen.at[int(j)]].reshape((1, 2)), 0)
            already_used_gen.at[int(j)] += 1
            change_q_compare += [int(j)]
    pp_res["gen"] = pp_res["gen"][1:, :]  # delete initial zero row

    # --- pandapower branch result table
    pp_res["branch"] = zeros([1, 4])
    # consideration of parallel branches via storing how often branches were considered
    # each node-to-node-connection
    try:
        init1 = concat([net.line.from_bus, net.line.to_bus], axis=1,
                       sort=True).drop_duplicates()
        init2 = concat([net.trafo.hv_bus, net.trafo.lv_bus], axis=1,
                       sort=True).drop_duplicates()
    except TypeError:
        # legacy pandas < 0.21
        init1 = concat([net.line.from_bus, net.line.to_bus],
                       axis=1).drop_duplicates()
        init2 = concat([net.trafo.hv_bus, net.trafo.lv_bus],
                       axis=1).drop_duplicates()
    init1['hv_bus'] = nan
    init1['lv_bus'] = nan
    init2['from_bus'] = nan
    init2['to_bus'] = nan
    try:
        already_used_branches = concat([init1, init2], axis=0, sort=True)
    except TypeError:
        # pandas < 0.21 legacy
        already_used_branches = concat([init1, init2], axis=0)
    already_used_branches['number'] = zeros(
        [already_used_branches.shape[0], 1]).astype(int)
    BRANCHES = DataFrame(ppc_net['branch'][:, [0, 1, 8, 9]])
    for i in BRANCHES.index:
        from_bus = pp.get_element_index(net,
                                        'bus',
                                        name=int(ppc_net['branch'][i, 0]))
        to_bus = pp.get_element_index(net,
                                      'bus',
                                      name=int(ppc_net['branch'][i, 1]))
        from_vn_kv = ppc_net['bus'][from_bus, 9]
        to_vn_kv = ppc_net['bus'][to_bus, 9]
        ratio = BRANCHES[2].at[i]
        angle = BRANCHES[3].at[i]
        # from line results
        if (from_vn_kv == to_vn_kv) & ((ratio == 0) |
                                       (ratio == 1)) & (angle == 0):
            pp_res["branch"] = append(
                pp_res["branch"],
                array(net.res_line[(net.line.from_bus == from_bus)
                                   & (net.line.to_bus == to_bus)][[
                                       'p_from_mw', 'q_from_mvar', 'p_to_mw',
                                       'q_to_mvar'
                                   ]])
                [int(already_used_branches.number.loc[
                    (already_used_branches.from_bus == from_bus) &
                    (already_used_branches.to_bus == to_bus)].values)].reshape(
                        1, 4), 0)
            already_used_branches.number.loc[
                (already_used_branches.from_bus == from_bus)
                & (already_used_branches.to_bus == to_bus)] += 1
        # from trafo results
        else:
            if from_vn_kv >= to_vn_kv:
                pp_res["branch"] = append(
                    pp_res["branch"],
                    array(net.res_trafo[(net.trafo.hv_bus == from_bus)
                                        & (net.trafo.lv_bus == to_bus)]
                          [['p_hv_mw', 'q_hv_mvar', 'p_lv_mw', 'q_lv_mvar'
                            ]])[int(already_used_branches.number.loc[
                                (already_used_branches.hv_bus == from_bus)
                                & (already_used_branches.lv_bus == to_bus)].
                                    values)].reshape(1, 4), 0)
                already_used_branches.number.loc[
                    (already_used_branches.hv_bus == from_bus)
                    & (already_used_branches.lv_bus == to_bus)] += 1
            else:  # switch hv-lv-connection of pypower connection buses
                pp_res["branch"] = append(
                    pp_res["branch"],
                    array(net.res_trafo[(net.trafo.hv_bus == to_bus)
                                        & (net.trafo.lv_bus == from_bus)]
                          [['p_lv_mw', 'q_lv_mvar', 'p_hv_mw', 'q_hv_mvar'
                            ]])[int(already_used_branches.number.loc[
                                (already_used_branches.hv_bus == to_bus)
                                & (already_used_branches.lv_bus == from_bus)].
                                    values)].reshape(1, 4), 0)
                already_used_branches.number.loc[
                    (already_used_branches.hv_bus == to_bus)
                    & (already_used_branches.lv_bus == from_bus)] += 1
    pp_res["branch"] = pp_res["branch"][1:, :]  # delete initial zero row

    # --- do the powerflow result comparison
    diff_res = dict.fromkeys(ppc_elms)
    diff_res["bus"] = ppc_res["bus"] - pp_res["bus"]
    diff_res["bus"][:, 1] -= diff_res["bus"][0, 1]  # remove va_degree offset
    diff_res["branch"] = ppc_res["branch"] - pp_res["branch"]
    diff_res["gen"] = ppc_res["gen"] - pp_res["gen"]
    # comparison of buses with several generator units only as q sum
    for i in GEN_uniq.loc[GEN_uniq[0].isin(change_q_compare)].index:
        next_is = GEN_uniq.index[GEN_uniq.index > i]
        if len(next_is) > 0:
            next_i = next_is[0]
        else:
            next_i = GENS.index[-1] + 1
        if (next_i - i) > 1:
            diff_res["gen"][i:next_i, 1] = sum(diff_res["gen"][i:next_i, 1])
    # logger info
    logger.debug(
        "Maximum voltage magnitude difference between pypower and pandapower: "
        "%.2e pu" % max_(abs(diff_res["bus"][:, 0])))
    logger.debug(
        "Maximum voltage angle difference between pypower and pandapower: "
        "%.2e degree" % max_(abs(diff_res["bus"][:, 1])))
    logger.debug(
        "Maximum branch flow active power difference between pypower and pandapower: "
        "%.2e MW" % max_(abs(diff_res["branch"][:, [0, 2]])))
    logger.debug(
        "Maximum branch flow reactive power difference between pypower and "
        "pandapower: %.2e MVAr" % max_(abs(diff_res["branch"][:, [1, 3]])))
    logger.debug(
        "Maximum active power generation difference between pypower and pandapower: "
        "%.2e MW" % max_(abs(diff_res["gen"][:, 0])))
    logger.debug(
        "Maximum reactive power generation difference between pypower and pandapower: "
        "%.2e MVAr" % max_(abs(diff_res["gen"][:, 1])))
    if _validate_diff_res(diff_res, {"bus_vm_pu": 1e-3, "bus_va_degree": 1e-3, "branch_p_mw": 1e-6,
                                     "branch_q_mvar": 1e-6}) and \
            (max_(abs(diff_res["gen"])) > 1e-1).any():
        logger.debug(
            "The active/reactive power generation difference possibly results "
            "because of a pypower error. Please validate "
            "the results via pypower loadflow."
        )  # this occurs e.g. at ppc case9
    # give a return
    if isinstance(max_diff_values, dict):
        return _validate_diff_res(diff_res, max_diff_values)
    else:
        logger.debug("'max_diff_values' must be a dict.")
예제 #13
0
    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))