def __init__(self, grid: MultiCircuit, options: NMinusKOptions, pf_options: PowerFlowOptions): """ N - k class constructor @param grid: MultiCircuit Object @param options: N-k options @:param pf_options: power flow options """ QThread.__init__(self) # Grid to run self.grid = grid # Options to use self.options = options # power flow options self.pf_options = pf_options # OPF results self.results = NMinusKResults(0, 0, 0) # set cancel state self.__cancel__ = False self.all_solved = True self.logger = Logger() self.elapsed = 0.0 self.branch_names = list()
def __init__(self, grid: MultiCircuit, options: NMinusKOptions): """ N - k class constructor @param grid: MultiCircuit Object @param options: N-k options @:param pf_options: power flow options """ QThread.__init__(self) # Grid to run self.grid = grid # Options to use self.options = options # N-K results self.results = NMinusKResults(n=0, m=0, bus_names=(), branch_names=(), bus_types=()) self.numerical_circuit = None # set cancel state self.__cancel__ = False self.logger = Logger() self.elapsed = 0.0 self.branch_names = list()
def n_minus_k(self): """ Run N-1 simulation in series :return: returns the results """ self.progress_text.emit("Filtering elements by voltage") self.numerical_circuit = compile_snapshot_circuit(self.grid) results = NMinusKResults( m=self.numerical_circuit.nbr, n=self.numerical_circuit.nbus, branch_names=self.numerical_circuit.branch_names, bus_names=self.numerical_circuit.bus_names, bus_types=self.numerical_circuit.bus_types) self.progress_text.emit('Analyzing outage distribution factors...') linear_analysis = LinearAnalysis( grid=self.grid, distributed_slack=self.options.distributed_slack, correct_values=self.options.correct_values) linear_analysis.run() Pbus = self.numerical_circuit.get_injections(False).real[:, 0] PTDF = linear_analysis.results.PTDF LODF = linear_analysis.results.LODF # compute the branch flows in "n" flows_n = np.dot(PTDF, Pbus) self.progress_text.emit('Computing flows...') nl = self.numerical_circuit.nbr for c in range(nl): # branch that fails (contingency) # for m in range(nl): # branch to monitor # results.Sf[m, c] = flows_n[m] + LODF[m, c] * flows_n[c] # results.loading[m, c] = results.Sf[m, c] / (self.numerical_circuit.branch_rates[m] + 1e-9) results.Sbranch[:, c] = flows_n[:] + LODF[:, c] * flows_n[c] results.loading[:, c] = results.Sbranch[:, c] / ( self.numerical_circuit.branch_rates + 1e-9) results.S[c, :] = Pbus self.progress_signal.emit((c + 1) / nl * 100) results.otdf = LODF return results
def __init__(self, grid: MultiCircuit, options: NMinusKOptions, pf_options: PowerFlowOptions): """ N - k class constructor @param grid: MultiCircuit Object @param options: N-k options @:param pf_options: power flow options """ QThread.__init__(self) # Grid to run self.grid = grid # Options to use self.options = options # power flow options self.pf_options = pf_options # N-K results self.results = NMinusKResults(n=0, m=0, nt=0, n_tr=0, bus_names=(), branch_names=(), transformer_names=(), bus_types=(), time_array=None, states=None) # set cancel state self.__cancel__ = False self.all_solved = True self.logger = Logger() self.elapsed = 0.0 self.branch_names = list()
def n_minus_k_mt(self, k=1, indices=None, vmin=200, states_number_limit=None): """ Run N-K simulation in series :param k: Parameter level (1 for n-1, 2 for n-2, etc...) :param indices: time indices {np.array([0])} :param vmin: minimum nominal voltage to allow (filters out branches and buses below) :param states_number_limit: limit the amount of states :return: Nothing, saves a report """ self.progress_text.emit("Filtering elements by voltage") # filter branches branch_names = list() branch_index = list() branches = list() # list of filtered branches for i, branch in enumerate(self.grid.branches): if branch.bus_from.Vnom > vmin or branch.bus_to.Vnom > vmin: branch_names.append(branch.name) branch_index.append(i) branches.append(branch) branch_index = np.array(branch_index) # filter buses bus_names = list() bus_index = list() for i, bus in enumerate(self.grid.buses): if bus.Vnom > vmin: bus_names.append(bus.name) bus_index.append(i) bus_index = np.array(bus_index) # get N-k states self.progress_text.emit("Enumerating states") states, failed_indices = enumerate_states_n_k(m=len(branch_names), k=k) # limit states for memory reasons if states_number_limit is not None: states = states[:states_number_limit, :] failed_indices = failed_indices[:states_number_limit] # compile the multi-circuit self.progress_text.emit("Compiling assets...") self.progress_signal.emit(0) numerical_circuit = self.grid.compile_time_series() # if no base profile time is given, pick the base values if indices is None: time_indices = np.array([0]) numerical_circuit.set_base_profile() else: time_indices = indices # re-index the profile (this is essential for time-compatibility) self.progress_signal.emit(100) # construct the profile indices profile_indices = np.tile(time_indices, len(states)) numerical_circuit.re_index_time(t_idx=profile_indices) # set the branch states numerical_circuit.branch_active_prof[:, branch_index] = np.tile(states, (len(time_indices), 1)) # initialize the power flow pf_options = PowerFlowOptions(solver_type=SolverType.LACPF) # initialize the grid time series results we will append the island results with another function n = len(self.grid.buses) m = self.circuit.get_branch_number() nt = len(profile_indices) n_k_results = NMinusKResults(n, m, nt, time_array=numerical_circuit.time_array, states=states) # do the topological computation self.progress_text.emit("Compiling topology...") self.progress_signal.emit(0.0) calc_inputs_dict = numerical_circuit.compute(ignore_single_node_islands=pf_options.ignore_single_node_islands) n_k_results.bus_types = numerical_circuit.bus_types # for each partition of the profiles... self.progress_text.emit("Simulating states...") for t_key, calc_inputs in calc_inputs_dict.items(): # For every island, run the time series for island_index, calculation_input in enumerate(calc_inputs): # find the original indices bus_original_idx = calculation_input.original_bus_idx branch_original_idx = calculation_input.original_branch_idx # if there are valid profiles... if self.grid.time_profile is not None: # declare a results object for the partition # nt = calculation_input.ntime nt = len(calculation_input.original_time_idx) n = calculation_input.nbus m = calculation_input.nbr partial_results = NMinusKResults(n, m, nt) last_voltage = calculation_input.Vbus # traverse the time profiles of the partition and simulate each time step for it, t in enumerate(calculation_input.original_time_idx): self.progress_signal.emit(it / nt * 100.0) # set the power values # if the storage dispatch option is active, the batteries power is not included # therefore, it shall be included after processing Ysh = calculation_input.Ysh_prof[:, it] I = calculation_input.Ibus_prof[:, it] S = calculation_input.Sbus_prof[:, it] branch_rates = calculation_input.branch_rates_prof[it, :] # run power flow at the circuit res = single_island_pf(circuit=calculation_input, Vbus=last_voltage, Sbus=S, Ibus=I, branch_rates=branch_rates, options=pf_options, logger=self.logger) # Recycle voltage solution last_voltage = res.voltage # store circuit results at the time index 't' partial_results.set_at(it, res) # merge the circuit's results n_k_results.apply_from_island(partial_results, bus_original_idx, branch_original_idx, calculation_input.original_time_idx, 'TS') else: self.progress_text.emit('There are no profiles') self.logger.append('There are no profiles') return n_k_results