Source code for VeraGridEngine.Simulations.ContingencyAnalysis.contingency_plan

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
from __future__ import annotations

from itertools import combinations
import numpy as np
from typing import List, Tuple

from VeraGridEngine.Devices.multi_circuit import MultiCircuit
from VeraGridEngine.Devices.Events.contingency import Contingency, ContingencyGroup
from VeraGridEngine.Devices.Parents.editable_device import DeviceType
from VeraGridEngine.Devices.types import BRANCH_TYPES
from VeraGridEngine.enumerations import ContingencyOperationTypes
import VeraGridEngine.Devices as dev


[docs] def enumerate_states_n_k(m: int, k: int = 1): """ Enumerates the states to produce the so called N-k failures :param m: number of Branches :param k: failure level :return: binary array (number of states, m) """ # num = int(math.factorial(k) / math.factorial(m-k)) states = list() indices = list() arr = np.ones(m, dtype=int).tolist() idx = list(range(m)) for k1 in range(k + 1): for failed in combinations(idx, k1): indices.append(failed) arr2 = arr.copy() for j in failed: arr2[j] = 0 states.append(arr2) return np.array(states), indices
[docs] def add_n1_contingencies(branches: List[BRANCH_TYPES], vmin: float, vmax: float, filter_branches_by_voltage: bool, branch_types: List[DeviceType]): """ generate N-1 contingencies on branches :param branches: :param vmin: :param vmax: :param filter_branches_by_voltage: :param branch_types: :return: """ contingencies = list() groups = list() for i, b in enumerate(branches): vi = b.get_max_bus_nominal_voltage() filter_ok_i = (vmin <= vi <= vmax) if filter_branches_by_voltage else True if filter_ok_i and b.device_type in branch_types: group = ContingencyGroup( name=b.name, category='single', ) contingency = Contingency( device=b, name=b.name, code=b.code, prop=ContingencyOperationTypes.Active, value=0, group=group ) contingencies.append(contingency) groups.append(group) return contingencies, groups
[docs] def add_n2_contingencies(branches, vmin, vmax, filter_branches_by_voltage, branch_types): """ Generate N-2 contingencies for branches :param branches: :param vmin: :param vmax: :param filter_branches_by_voltage: :param branch_types: :return: """ contingencies = list() groups = list() for i, branch_i in enumerate(branches): vi = branch_i.get_max_bus_nominal_voltage() filter_ok_i = (vmin <= vi <= vmax) if filter_branches_by_voltage else True if filter_ok_i and branch_i.device_type in branch_types: for j, branch_j in enumerate(branches): if j != i: vj = branch_j.get_max_bus_nominal_voltage() filter_ok_j = (vmin <= vj <= vmax) if filter_branches_by_voltage else True if filter_ok_j and branch_j.device_type in branch_types: group = ContingencyGroup( name=branch_i.name + " " + branch_j.name, category='double', ) contingency1 = Contingency( device=branch_i, name=branch_i.name, code=branch_i.code, prop=ContingencyOperationTypes.Active, value=0, group=group ) contingency2 = Contingency( device=branch_j, name=branch_j.name, code=branch_j.code, prop=ContingencyOperationTypes.Active, value=0, group=group ) contingencies.append(contingency1) contingencies.append(contingency2) groups.append(group) return contingencies, groups
[docs] def add_generator_contingencies( generators: List[dev.Generator], pmin: float, pmax: float, contingency_perc: float, filter_injections_by_power: bool): """ Create generator contingencies :param generators: Generator list :param pmin: Min power to filter :param pmax: Max power to filter :param contingency_perc: Percentage of power to trigger :param filter_injections_by_power: boolean :return: """ contingencies = list() groups = list() for i, gen in enumerate(generators): if (pmin <= gen.Snom <= pmax) or not filter_injections_by_power: group = ContingencyGroup( name=gen.name, category='generator', ) contingency = Contingency( device=gen, name=gen.name, code=gen.code, prop=ContingencyOperationTypes.PowerPercentage, value=contingency_perc, group=group ) contingencies.append(contingency) groups.append(group) return contingencies, groups
[docs] def generate_automatic_contingency_plan( grid: MultiCircuit, k: int, consider_branches: bool, filter_branches_by_voltage: bool = False, vmin: float = 0, vmax: float = 1000, branch_types: List[DeviceType] | None = None, consider_injections: bool = False, filter_injections_by_power: bool = False, contingency_perc=100.0, pmin=0, pmax=10000, injection_types: List[DeviceType] | None = None ) -> Tuple[List[Contingency], List[ContingencyGroup]]: """ :param grid: MultiCircuit instance :param k: index (1 for N-1, 2 for N-2, other values of k will fail) :param consider_branches: consider branches? :param filter_branches_by_voltage: :param vmin: :param vmax: :param branch_types: List of allowed branch types :param consider_injections: Consider injections? :param filter_injections_by_power: :param contingency_perc: :param pmin: :param pmax: :param injection_types: List of allowed injection types :return: """ if branch_types is None: branch_types = list() if injection_types is None: injection_types = list() assert (k in [1, 2]) contingencies = list() groups = list() # add branch contingencies if consider_branches: branches = grid.get_branches(add_hvdc=False, add_vsc=False, add_switch=True) if k == 1: contingencies1, groups1 = add_n1_contingencies(branches=branches, vmin=vmin, vmax=vmax, filter_branches_by_voltage=filter_branches_by_voltage, branch_types=branch_types) contingencies += contingencies1 groups += groups1 elif k == 2: contingencies, groups = add_n1_contingencies(branches=branches, vmin=vmin, vmax=vmax, filter_branches_by_voltage=filter_branches_by_voltage, branch_types=branch_types) contingencies2, groups2 = add_n2_contingencies(branches=branches, vmin=vmin, vmax=vmax, filter_branches_by_voltage=filter_branches_by_voltage, branch_types=branch_types) contingencies += contingencies2 groups += groups2 # add injection contingencies if consider_injections: contingencies_gen, groups_gen = add_generator_contingencies( generators=grid.get_generators(), pmin=pmin, pmax=pmax, contingency_perc=contingency_perc, filter_injections_by_power=filter_injections_by_power, ) contingencies += contingencies_gen groups += groups_gen return contingencies, groups