def run(self): """ Run the PTDF and LODF """ self.numerical_circuit = compile_snapshot_circuit(self.grid) islands = self.numerical_circuit.split_into_islands() self.results = LinearAnalysisResults( n_br=self.numerical_circuit.nbr, n_bus=self.numerical_circuit.nbus, br_names=self.numerical_circuit.branch_data.branch_names, bus_names=self.numerical_circuit.bus_data.bus_names, bus_types=self.numerical_circuit.bus_data.bus_types) # compute the PTDF per islands if len(islands) > 0: for island in islands: # no slacks will make it impossible to compute the PTDF analytically if len(island.vd) > 0 and len(island.pqpv) > 0: # compute the PTDF of the island ptdf_island = make_ptdf( Bbus=island.Bbus, Bf=island.Bf, pqpv=island.pqpv, distribute_slack=self.distributed_slack) # assign the PTDF to the matrix self.results.PTDF[np.ix_( island.original_branch_idx, island.original_bus_idx)] = ptdf_island # compute the island LODF lodf_island = make_lodf(Cf=island.Cf, Ct=island.Ct, PTDF=ptdf_island, correct_values=self.correct_values) # assign the LODF to the matrix self.results.LODF[np.ix_( island.original_branch_idx, island.original_branch_idx)] = lodf_island else: # there is only 1 island, compute the PTDF self.results.PTDF = make_ptdf( Bbus=islands[0].Bbus, Bf=islands[0].Bf, pqpv=islands[0].pqpv, distribute_slack=self.distributed_slack) # compute the LODF upon the PTDF self.results.LODF = make_lodf(Cf=islands[0].Cf, Ct=islands[0].Ct, PTDF=self.results.PTDF, correct_values=self.correct_values)
def run(self): """ Run the PTDF and LODF """ self.numerical_circuit = compile_snapshot_circuit(self.grid) islands = self.numerical_circuit.split_into_islands() n_br = self.numerical_circuit.nbr n_bus = self.numerical_circuit.nbus self.PTDF = np.zeros((n_br, n_bus)) self.LODF = np.zeros((n_br, n_br)) # compute the PTDF per islands if len(islands) > 0: for n_island, island in enumerate(islands): # no slacks will make it impossible to compute the PTDF analytically if len(island.vd) == 1: if len(island.pqpv) > 0: # compute the PTDF of the island ptdf_island = make_ptdf(Bbus=island.Bbus, Bf=island.Bf, pqpv=island.pqpv, distribute_slack=self.distributed_slack) # assign the PTDF to the matrix self.PTDF[np.ix_(island.original_branch_idx, island.original_bus_idx)] = ptdf_island # compute the island LODF lodf_island = make_lodf(Cf=island.Cf, Ct=island.Ct, PTDF=ptdf_island, correct_values=self.correct_values) # assign the LODF to the matrix self.LODF[np.ix_(island.original_branch_idx, island.original_branch_idx)] = lodf_island else: self.logger.add_error('No PQ or PV nodes', 'Island {}'.format(n_island)) elif len(island.vd) == 0: self.logger.add_warning('No slack bus', 'Island {}'.format(n_island)) else: self.logger.add_error('More than one slack bus', 'Island {}'.format(n_island)) else: # there is only 1 island, compute the PTDF self.PTDF = make_ptdf(Bbus=islands[0].Bbus, Bf=islands[0].Bf, pqpv=islands[0].pqpv, distribute_slack=self.distributed_slack) # compute the LODF upon the PTDF self.LODF = make_lodf(Cf=islands[0].Cf, Ct=islands[0].Ct, PTDF=self.PTDF, correct_values=self.correct_values)
def run(self): """ Run the PTDF and LODF """ self.numerical_circuit = compile_snapshot_circuit(self.grid) islands = split_into_islands(self.numerical_circuit) self.results = LinearAnalysisResults( n_br=self.numerical_circuit.nbr, n_bus=self.numerical_circuit.nbus, br_names=self.numerical_circuit.branch_names, bus_names=self.numerical_circuit.bus_names, bus_types=self.numerical_circuit.bus_types) # compute the PTDF per islands if len(islands) > 0: for island in islands: # compute the linear-DC matrices Bbus, Bf, reactances = island.get_linear_matrices() if len(island.vd) > 0 and len( island.pqpv ) > 0: # no slacks will make it impossible to compute the PTDF analytically # compute the PTDF of the island ptdf_island = make_ptdf( Bbus=Bbus, Bf=Bf, pqpv=island.pqpv, distribute_slack=self.distributed_slack) # assign the PTDF to the matrix self.results.PTDF[np.ix_( island.original_branch_idx, island.original_bus_idx)] = ptdf_island else: # compute the linear-DC matrices Bbus, Bf, reactances = islands[0].get_linear_matrices() # there is only 1 island, compute the PTDF self.results.PTDF = make_ptdf( Bbus=Bbus, Bf=Bf, pqpv=islands[0].pqpv, distribute_slack=self.distributed_slack) # the LODF algorithm doesn't seem to solve any circuit, hence there is no need of island splitting self.results.LODF = make_lodf(Cf=self.numerical_circuit.C_branch_bus_f, Ct=self.numerical_circuit.C_branch_bus_t, PTDF=self.results.PTDF, correct_values=self.correct_values)
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 run(self): """ run the voltage collapse simulation @return: """ print('Running voltage collapse...') # compile the numerical circuit numerical_circuit = compile_snapshot_circuit(self.circuit) evt = get_reliability_scenario(numerical_circuit) run_events(nc=numerical_circuit, events_list=evt) print('done!') self.progress_text.emit('Done!') self.done_signal.emit()
# Voltage collapse #################################################################################################################### vc_options = ContinuationPowerFlowOptions( step=0.001, approximation_order=CpfParametrization.ArcLength, adapt_step=True, step_min=0.00001, step_max=0.2, error_tol=1e-3, tol=1e-6, max_it=20, stop_at=CpfStopAt.Full, verbose=False) # just for this test numeric_circuit = compile_snapshot_circuit(main_circuit) numeric_inputs = numeric_circuit.split_into_islands( ignore_single_node_islands=pf_options.ignore_single_node_islands) Sbase_ = np.zeros(len(main_circuit.buses), dtype=complex) Vbase_ = np.zeros(len(main_circuit.buses), dtype=complex) for c in numeric_inputs: Sbase_[c.original_bus_idx] = c.Sbus Vbase_[c.original_bus_idx] = c.Vbus np.random.seed(42) unitary_vector = -1 + 2 * np.random.random(len(main_circuit.buses)) # unitary_vector = random.random(len(grid.buses)) vc_inputs = ContinuationPowerFlowInput( Sbase=Sbase_, Vbase=Vbase_,
def run(self): """ Run a power flow for every circuit @return: """ self._is_running = True if len(self.options.branch_index) > 0: # if there are branch indices where to perform short circuits, modify the grid accordingly grid = self.grid.copy() sc_bus_index = list() for k, br_idx in enumerate(self.options.branch_index): # modify the grid by inserting a mid-line short circuit bus br1, br2, middle_bus = self.split_branch(branch=br_idx, fault_position=self.options.branch_fault_locations[k], r_fault=self.options.branch_fault_impedance[k].real, x_fault=self.options.branch_fault_impedance[k].imag) grid.add_branch(br1) grid.add_branch(br2) grid.add_bus(middle_bus) sc_bus_index.append(len(grid.buses) - 1) else: grid = self.grid # Compile the grid numerical_circuit = compile_snapshot_circuit(circuit=grid, apply_temperature=self.pf_options.apply_temperature_correction, branch_tolerance_mode=self.pf_options.branch_impedance_tolerance_mode, opf_results=self.opf_results) calculation_inputs = numerical_circuit.split_into_islands(ignore_single_node_islands=self.pf_options.ignore_single_node_islands) results = ShortCircuitResults(n=numerical_circuit.nbus, m=numerical_circuit.nbr, n_tr=numerical_circuit.ntr, bus_names=numerical_circuit.bus_names, branch_names=numerical_circuit.branch_names, transformer_names=numerical_circuit.tr_names, bus_types=numerical_circuit.bus_types) results.bus_types = numerical_circuit.bus_types Zf = self.compile_zf(grid) if len(calculation_inputs) > 1: # multi-island for i, calculation_input in enumerate(calculation_inputs): bus_original_idx = calculation_input.original_bus_idx branch_original_idx = calculation_input.original_branch_idx res = self.single_short_circuit(calculation_inputs=calculation_input, Vpf=self.pf_results.voltage[bus_original_idx], Zf=Zf[bus_original_idx]) # merge results results.apply_from_island(res, bus_original_idx, branch_original_idx) else: # single island results = self.single_short_circuit(calculation_inputs=calculation_inputs[0], Vpf=self.pf_results.voltage, Zf=Zf) self.results = results self.grid.short_circuit_results = results self._is_running = False self.progress_signal.emit(0.0) self.progress_text.emit('Done!') self.done_signal.emit()
def ptdf(self, circuit: MultiCircuit, options: PowerFlowOptions, group_mode: PtdfGroupMode, power_amount, text_func=None, prog_func=None): """ Power Transfer Distribution Factors analysis :param circuit: MultiCircuit instance :param options: power flow options :param group_mode: group mode :param power_amount: amount o power to vary in MW :param text_func: text function to display progress :param prog_func: progress function to display progress [0~100] :return: """ if text_func is not None: text_func('Compiling...') # compile to arrays numerical_circuit = compile_snapshot_circuit( circuit=circuit, apply_temperature=options.apply_temperature_correction, branch_tolerance_mode=options.branch_impedance_tolerance_mode, opf_results=self.opf_results) calculation_inputs = numerical_circuit.split_into_islands( ignore_single_node_islands=options.ignore_single_node_islands) # compute the variations delta_of_power_variations = get_ptdf_variations( circuit=circuit, numerical_circuit=numerical_circuit, group_mode=group_mode, power_amount=power_amount) # declare the PTDF results results = PTDFResults(n_variations=len(delta_of_power_variations) - 1, n_br=numerical_circuit.nbr, n_bus=numerical_circuit.nbus, br_names=numerical_circuit.branch_names, bus_names=numerical_circuit.bus_names, bus_types=numerical_circuit.bus_types) if text_func is not None: text_func('Running PTDF...') nvar = len(delta_of_power_variations) for v, variation in enumerate(delta_of_power_variations): # this super strange way of calling a function is done to maintain the same # call format as the multi-threading function returns = dict() power_flow_worker(variation=0, nbus=numerical_circuit.nbus, nbr=numerical_circuit.nbr, n_tr=numerical_circuit.ntr, bus_names=numerical_circuit.bus_names, branch_names=numerical_circuit.branch_names, transformer_names=numerical_circuit.tr_names, bus_types=numerical_circuit.bus_types, calculation_inputs=calculation_inputs, options=options, dP=variation.dP, return_dict=returns) pf_results, log = returns[0] results.logger += log # add the power flow results if v == 0: results.default_pf_results = pf_results else: results.add_results_at(v - 1, pf_results, variation) if prog_func is not None: p = (v + 1) / nvar * 100.0 prog_func(p) if self.__cancel__: break return results
def ptdf_multi_treading(self, circuit: MultiCircuit, options: PowerFlowOptions, group_mode: PtdfGroupMode, power_amount, text_func=None, prog_func=None): """ Power Transfer Distribution Factors analysis :param circuit: MultiCircuit instance :param options: power flow options :param group_mode: ptdf grouping mode :param power_amount: amount o power to vary in MW :param text_func: :param prog_func :return: """ if text_func is not None: text_func('Compiling...') # compile to arrays # numerical_circuit = circuit.compile_snapshot() # calculation_inputs = numerical_circuit.compute(apply_temperature=options.apply_temperature_correction, # branch_tolerance_mode=options.branch_impedance_tolerance_mode, # ignore_single_node_islands=options.ignore_single_node_islands) numerical_circuit = compile_snapshot_circuit( circuit=circuit, apply_temperature=options.apply_temperature_correction, branch_tolerance_mode=options.branch_impedance_tolerance_mode, opf_results=self.opf_results) calculation_inputs = numerical_circuit.split_into_islands( ignore_single_node_islands=options.ignore_single_node_islands) # compute the variations delta_of_power_variations = get_ptdf_variations( circuit=circuit, numerical_circuit=numerical_circuit, group_mode=group_mode, power_amount=power_amount) # declare the PTDF results results = PTDFResults(n_variations=len(delta_of_power_variations) - 1, n_br=numerical_circuit.nbr, n_bus=numerical_circuit.nbus, br_names=numerical_circuit.branch_names, bus_names=numerical_circuit.bus_names, bus_types=numerical_circuit.bus_types) if text_func is not None: text_func('Running PTDF...') jobs = list() n_cores = multiprocessing.cpu_count() manager = multiprocessing.Manager() return_dict = manager.dict() # for v, variation in enumerate(delta_of_power_variations): v = 0 nvar = len(delta_of_power_variations) while v < nvar: k = 0 # launch only n_cores jobs at the time while k < n_cores + 2 and (v + k) < nvar: # run power flow at the circuit p = multiprocessing.Process( target=power_flow_worker, args=(v, numerical_circuit.nbus, numerical_circuit.nbr, numerical_circuit.ntr, numerical_circuit.bus_names, numerical_circuit.branch_names, numerical_circuit.tr_names, numerical_circuit.bus_types, calculation_inputs, options, delta_of_power_variations[v].dP, return_dict)) jobs.append(p) p.start() v += 1 k += 1 if self.__cancel__: break # wait for all jobs to complete for process_ in jobs: process_.join() # emit the progress if prog_func is not None: p = (v + 1) / nvar * 100.0 prog_func(p) if self.__cancel__: break if text_func is not None: text_func('Collecting results...') # gather the results if not self.__cancel__: for v in range(nvar): pf_results, log = return_dict[v] results.logger += log if v == 0: results.default_pf_results = pf_results else: results.add_results_at(v - 1, pf_results, delta_of_power_variations[v]) return results
def run(self): """ run the voltage collapse simulation @return: """ print('Running voltage collapse...') nbus = self.circuit.get_bus_number() numerical_circuit = compile_snapshot_circuit( circuit=self.circuit, apply_temperature=self.pf_options.apply_temperature_correction, branch_tolerance_mode=self.pf_options. branch_impedance_tolerance_mode, opf_results=self.opf_results) numerical_input_islands = split_into_islands( numeric_circuit=numerical_circuit, ignore_single_node_islands=self.pf_options. ignore_single_node_islands) self.results = VoltageCollapseResults( nbus=numerical_circuit.nbus, nbr=numerical_circuit.nbr, bus_names=numerical_circuit.bus_names) self.results.bus_types = numerical_circuit.bus_types for nc, numerical_island in enumerate(numerical_input_islands): self.progress_text.emit('Running voltage collapse at circuit ' + str(nc) + '...') if len(numerical_island.vd) > 0: Voltage_series, Lambda_series, \ normF, success = continuation_nr(Ybus=numerical_island.Ybus, Ibus_base=numerical_island.Ibus, Ibus_target=numerical_island.Ibus, Sbus_base=self.inputs.Sbase[numerical_island.original_bus_idx], Sbus_target=self.inputs.Starget[numerical_island.original_bus_idx], V=self.inputs.Vbase[numerical_island.original_bus_idx], pv=numerical_island.pv, pq=numerical_island.pq, step=self.options.step, approximation_order=self.options.approximation_order, adapt_step=self.options.adapt_step, step_min=self.options.step_min, step_max=self.options.step_max, error_tol=self.options.error_tol, tol=self.options.tol, max_it=self.options.max_it, stop_at=self.options.stop_at, verbose=False, call_back_fx=self.progress_callback) # nbus can be zero, because all the arrays are going to be overwritten res = VoltageCollapseResults( nbus=numerical_island.nbus, nbr=numerical_island.nbr, bus_names=numerical_island.bus_names) res.voltages = np.array(Voltage_series) res.lambdas = np.array(Lambda_series) res.error = normF res.converged = bool(success) else: res = VoltageCollapseResults( nbus=numerical_island.nbus, nbr=numerical_island.nbr, bus_names=numerical_island.bus_names) res.voltages = np.array([[0] * numerical_island.nbus]) res.lambdas = np.array([[0] * numerical_island.nbus]) res.error = [0] res.converged = True if len(res.voltages) > 0: # compute the island branch results Sbranch, Ibranch, Vbranch, \ loading, losses, flow_direction, \ Sbus = power_flow_post_process(calculation_inputs=numerical_island, Sbus=self.inputs.Starget[numerical_island.original_bus_idx], V=res.voltages[-1], branch_rates=numerical_island.branch_rates) # update results self.results.apply_from_island( voltage_collapse_res=res, Sbranch=Sbranch, Ibranch=Ibranch, loading=loading, losses=losses, Sbus=Sbus, bus_original_idx=numerical_island.original_bus_idx, branch_original_idx=numerical_island.original_branch_idx, nbus_full=nbus) else: print('No voltage values!') print('done!') self.progress_text.emit('Done!') self.done_signal.emit()
def multi_island_sigma( multi_circuit: MultiCircuit, options: PowerFlowOptions, logger=Logger()) -> "SigmaAnalysisResults": """ Multiple islands power flow (this is the most generic power flow function) :param multi_circuit: MultiCircuit instance :param options: PowerFlowOptions instance :param logger: list of events to add to :return: PowerFlowResults instance """ # print('PowerFlowDriver at ', self.grid.name) n = len(multi_circuit.buses) m = multi_circuit.get_branch_number() results = SigmaAnalysisResults(n) numerical_circuit = compile_snapshot_circuit( circuit=multi_circuit, apply_temperature=options.apply_temperature_correction, branch_tolerance_mode=options.branch_impedance_tolerance_mode, opf_results=None) calculation_inputs = split_into_islands( numeric_circuit=numerical_circuit, ignore_single_node_islands=options.ignore_single_node_islands) if len(calculation_inputs) > 1: # simulate each island and merge the results for i, calculation_input in enumerate(calculation_inputs): if len(calculation_input.vd) > 0: # V, converged, norm_f, Scalc, iter_, elapsed, Sig_re, Sig_im U, X, Q, iter_ = helm_coefficients_josep( Yseries=calculation_input.Yseries, V0=calculation_input.Vbus, S0=calculation_input.Sbus, Ysh0=calculation_input.Yshunt, pq=calculation_input.pq, pv=calculation_input.pv, sl=calculation_input.vd, pqpv=calculation_input.pqpv, tolerance=options.tolerance, max_coeff=options.max_iter, verbose=False, ) # compute the sigma values n = calculation_input.nbus Sig_re = np.zeros(n, dtype=float) Sig_im = np.zeros(n, dtype=float) Sigma = sigma_function( U, X, iter_ - 1, calculation_input.Vbus[calculation_input.vd]) Sig_re[calculation_input.pqpv] = np.real(Sigma) Sig_im[calculation_input.pqpv] = np.imag(Sigma) sigma_distances = np.abs(sigma_distance(Sig_re, Sig_im)) # store the results island_results = SigmaAnalysisResults( n=len(calculation_input.Vbus)) island_results.lambda_value = 1.0 island_results.Sbus = calculation_input.Sbus island_results.sigma_re = Sig_re island_results.sigma_im = Sig_im island_results.distances = sigma_distances bus_original_idx = calculation_input.original_bus_idx # merge the results from this island results.apply_from_island(island_results, bus_original_idx) else: logger.append('There are no slack nodes in the island ' + str(i)) else: if len(calculation_inputs[0].vd) > 0: # only one island calculation_input = calculation_inputs[0] U, X, Q, iter_ = helm_coefficients_josep( Yseries=calculation_input.Yseries, V0=calculation_input.Vbus, S0=calculation_input.Sbus, Ysh0=calculation_input.Yshunt, pq=calculation_input.pq, pv=calculation_input.pv, sl=calculation_input.vd, pqpv=calculation_input.pqpv, tolerance=options.tolerance, max_coeff=options.max_iter, verbose=False, ) # compute the sigma values n = calculation_input.nbus Sig_re = np.zeros(n, dtype=float) Sig_im = np.zeros(n, dtype=float) Sigma = sigma_function( U, X, iter_ - 1, calculation_input.Vbus[calculation_input.vd]) Sig_re[calculation_input.pqpv] = np.real(Sigma) Sig_im[calculation_input.pqpv] = np.imag(Sigma) sigma_distances = np.abs(sigma_distance(Sig_re, Sig_im)) # store the results island_results = SigmaAnalysisResults( n=len(calculation_input.Vbus)) island_results.lambda_value = 1.0 island_results.Sbus = calculation_input.Sbus island_results.sigma_re = Sig_re island_results.sigma_im = Sig_im island_results.distances = sigma_distances results.apply_from_island(island_results, calculation_input.original_bus_idx) else: logger.append('There are no slack nodes') return results
def run(self): """ Run the PTDF and LODF """ self.numerical_circuit = compile_snapshot_circuit(self.grid) islands = self.numerical_circuit.split_into_islands() self.results = LinearAnalysisResults(n_br=self.numerical_circuit.nbr, n_bus=self.numerical_circuit.nbus, br_names=self.numerical_circuit.branch_data.branch_names, bus_names=self.numerical_circuit.bus_data.bus_names, bus_types=self.numerical_circuit.bus_data.bus_types) # compute the PTDF per islands if len(islands) > 0: for island in islands: if len(island.vd) > 0 and len(island.pqpv) > 0: # no slacks will make it impossible to compute the PTDF analytically # compute the PTDF of the island ptdf_island = make_ptdf(Bbus=island.Bbus, Bf=island.Bf, pqpv=island.pqpv, distribute_slack=self.distributed_slack) if self.distributed_slack: # the LODF requires a PTDF that does not have the distributed slack ptdf_island_no_dist = make_ptdf(Bbus=island.Bbus, Bf=island.Bf, pqpv=island.pqpv, distribute_slack=False) else: ptdf_island_no_dist = ptdf_island # assign the PTDF to the matrix self.results.PTDF[np.ix_(island.original_branch_idx, island.original_bus_idx)] = ptdf_island # compute the island LODF lodf_island = make_lodf(Cf=island.Cf, Ct=island.Ct, PTDF=ptdf_island_no_dist, correct_values=self.correct_values) self.results.LODF[np.ix_(island.original_branch_idx, island.original_branch_idx)] = lodf_island else: # there is only 1 island, compute the PTDF self.results.PTDF = make_ptdf(Bbus=islands[0].Bbus, Bf=islands[0].Bf, pqpv=islands[0].pqpv, distribute_slack=self.distributed_slack) if self.distributed_slack: # the LODF requires a PTDF that does not have the distributed slack ptdf_island_no_dist = make_ptdf(Bbus=islands[0].Bbus, Bf=islands[0].Bf, pqpv=islands[0].pqpv, distribute_slack=False) else: ptdf_island_no_dist = self.results.PTDF # the LODF algorithm doesn't seem to solve any circuit, hence there is no need of island splitting self.results.LODF = make_lodf(Cf=islands[0].Cf, Ct=islands[0].Ct, PTDF=ptdf_island_no_dist, correct_values=self.correct_values)
def multi_island_pf(multi_circuit: MultiCircuit, options: PowerFlowOptions, opf_results=None, logger=bs.Logger()) -> "PowerFlowResults": """ Multiple islands power flow (this is the most generic power flow function) :param multi_circuit: MultiCircuit instance :param options: PowerFlowOptions instance :param opf_results: OPF results, to be used if not None :param logger: list of events to add to :return: PowerFlowResults instance """ nc = compile_snapshot_circuit(circuit=multi_circuit, apply_temperature=options.apply_temperature_correction, branch_tolerance_mode=options.branch_impedance_tolerance_mode, opf_results=opf_results) calculation_inputs = nc.split_into_islands(ignore_single_node_islands=options.ignore_single_node_islands) results = PowerFlowResults(n=nc.nbus, m=nc.nbr, n_tr=nc.ntr, n_hvdc=nc.nhvdc, bus_names=nc.bus_data.bus_names, branch_names=nc.branch_data.branch_names, transformer_names=nc.transformer_data.tr_names, hvdc_names=nc.hvdc_data.names, bus_types=nc.bus_data.bus_types) if len(calculation_inputs) > 1: # simulate each island and merge the results for i, calculation_input in enumerate(calculation_inputs): if len(calculation_input.vd) > 0: # run circuit power flow res = single_island_pf(circuit=calculation_input, Vbus=calculation_input.Vbus, Sbus=calculation_input.Sbus, Ibus=calculation_input.Ibus, branch_rates=calculation_input.Rates, options=options, logger=logger) bus_original_idx = calculation_input.original_bus_idx branch_original_idx = calculation_input.original_branch_idx tr_original_idx = calculation_input.original_tr_idx # merge the results from this island results.apply_from_island(res, bus_original_idx, branch_original_idx, tr_original_idx) else: logger.add_info('No slack nodes in the island', str(i)) else: if len(calculation_inputs[0].vd) > 0: # only one island # run circuit power flow res = single_island_pf(circuit=calculation_inputs[0], Vbus=calculation_inputs[0].Vbus, Sbus=calculation_inputs[0].Sbus, Ibus=calculation_inputs[0].Ibus, branch_rates=calculation_inputs[0].Rates, options=options, logger=logger) if calculation_inputs[0].nbus == nc.nbus: # we can confidently say that the island is the only one results = res else: # the island is the only valid subset, but does not contain all the buses bus_original_idx = calculation_inputs[0].original_bus_idx branch_original_idx = calculation_inputs[0].original_branch_idx tr_original_idx = calculation_inputs[0].original_tr_idx # merge the results from this island results.apply_from_island(res, bus_original_idx, branch_original_idx, tr_original_idx) else: logger.add_error('There are no slack nodes') # compile HVDC results (available for the complete grid since HVDC line as formulated are split objects # Pt is the "generation" at the sending point results.hvdc_Pf = -nc.hvdc_Pf results.hvdc_Pt = -nc.hvdc_Pt results.hvdc_loading = nc.hvdc_loading results.hvdc_losses = nc.hvdc_losses return results
def run(self): """ run the voltage collapse simulation @return: """ print('Running voltage collapse...') nc = compile_snapshot_circuit( circuit=self.grid, apply_temperature=self.pf_options.apply_temperature_correction, branch_tolerance_mode=self.pf_options. branch_impedance_tolerance_mode, opf_results=self.opf_results) islands = nc.split_into_islands(ignore_single_node_islands=self. pf_options.ignore_single_node_islands) result_series = list() for island in islands: self.progress_text.emit('Running voltage collapse at circuit ' + str(nc) + '...') if len(island.vd) > 0 and len(island.pqpv) > 0: results = continuation_nr( Ybus=island.Ybus, Cf=island.Cf, Ct=island.Ct, Yf=island.Yf, Yt=island.Yt, branch_rates=island.branch_rates, Sbase=island.Sbase, Ibus_base=island.Ibus, Ibus_target=island.Ibus, Sbus_base=self.inputs.Sbase[island.original_bus_idx], Sbus_target=self.inputs.Starget[island.original_bus_idx], V=self.inputs.Vbase[island.original_bus_idx], distributed_slack=self.pf_options.distributed_slack, bus_installed_power=island.bus_installed_power, vd=island.vd, pv=island.pv, pq=island.pq, step=self.options.step, approximation_order=self.options.approximation_order, adapt_step=self.options.adapt_step, step_min=self.options.step_min, step_max=self.options.step_max, error_tol=self.options.error_tol, tol=self.options.tol, max_it=self.options.max_it, stop_at=self.options.stop_at, control_q=self.pf_options.control_Q, qmax_bus=island.Qmax_bus, qmin_bus=island.Qmin_bus, original_bus_types=island.bus_types, base_overload_number=self.inputs.base_overload_number, verbose=False, call_back_fx=self.progress_callback) # store the result series result_series.append(results) # analyze the result series to compact all the results into one object if len(result_series) > 0: max_len = max([len(r) for r in result_series]) else: max_len = 0 # declare results self.results = ContinuationPowerFlowResults( nval=max_len, nbus=nc.nbus, nbr=nc.nbr, bus_names=nc.bus_names, branch_names=nc.branch_names, bus_types=nc.bus_types) for i in range(len(result_series)): if len(result_series[i]) > 0: self.results.apply_from_island(result_series[i], islands[i].original_bus_idx, islands[i].original_branch_idx) print('done!') self.progress_text.emit('Done!') self.done_signal.emit()
def run(self): """ Run thread """ start = time.time() self.progress_text.emit('Analyzing') self.progress_signal.emit(0) # compile the circuit nc = compile_snapshot_circuit(self.grid) # get the converted bus indices idx1b = self.options.bus_idx_from idx2b = self.options.bus_idx_to # declare the linear analysis linear = LinearAnalysis( grid=self.grid, distributed_slack=self.options.distributed_slack, correct_values=self.options.correct_values) linear.run() # get the branch indices to analyze br_idx = linear.numerical_circuit.branch_data.get_contingency_enabled_indices( ) # declare the results self.results = AvailableTransferCapacityResults( n_bus=linear.numerical_circuit.nbus, br_names=linear.numerical_circuit.branch_names, bus_names=linear.numerical_circuit.bus_names, bus_types=linear.numerical_circuit.bus_types, bus_idx_from=idx1b, bus_idx_to=idx2b, br_idx=br_idx) # compute the branch exchange sensitivity (alpha) alpha = compute_alpha(ptdf=linear.PTDF, P0=nc.Sbus.real, Pinstalled=nc.bus_installed_power, idx1=idx1b, idx2=idx2b, bus_types=nc.bus_types.astype(np.int), dT=self.options.dT, mode=self.options.mode.value) # get flow if self.options.use_provided_flows: flows = self.options.Pf if self.options.Pf is None: msg = 'The option to use the provided flows is enabled, but no flows are available' self.logger.add_error(msg) raise Exception(msg) else: flows = linear.get_flows(nc.Sbus) # base exchange base_exchange = (self.options.inter_area_branch_sense * flows[self.options.inter_area_branch_idx]).sum() # consider the HVDC transfer if self.options.Pf_hvdc is not None: if len(self.options.idx_hvdc_br): base_exchange += ( self.options.inter_area_hvdc_branch_sense * self.options.Pf_hvdc[self.options.idx_hvdc_br]).sum() # compute ATC beta_mat, beta_used, atc_n, atc_mc, atc_final, \ atc_limiting_contingency_branch, \ atc_limiting_contingency_flow = compute_atc(br_idx=br_idx, ptdf=linear.PTDF, lodf=linear.LODF, alpha=alpha, flows=flows, rates=nc.Rates, contingency_rates=nc.ContingencyRates, threshold=self.options.threshold) # post-process and store the results self.results.br_idx = br_idx self.results.alpha = alpha[br_idx] self.results.atc = atc_final self.results.atc_n = atc_n self.results.atc_mc = atc_mc self.results.ntc = atc_final + base_exchange self.results.base_exchange = base_exchange self.results.beta_mat = beta_mat self.results.beta = beta_used self.results.atc_limiting_contingency_branch = atc_limiting_contingency_branch.astype( int) self.results.atc_limiting_contingency_flow = atc_limiting_contingency_flow self.results.base_flow = flows[br_idx] self.results.rates = nc.Rates[br_idx] self.results.contingency_rates = nc.ContingencyRates[br_idx] self.results.make_report(threshold=self.options.threshold) end = time.time() self.elapsed = end - start self.progress_text.emit('Done!') self.done_signal.emit()