def run_events(nc: SnapshotData, events_list: list): for t, tpe, i, state in events_list: # Set the state of the event if tpe == DeviceType.BusDevice: pass elif tpe == DeviceType.BranchDevice: nc.branch_data.branch_active[i] = state elif tpe == DeviceType.GeneratorDevice: nc.generator_data.generator_active[i] = state elif tpe == DeviceType.StaticGeneratorDevice: nc.static_generator_data.static_gen_active[i] = state elif tpe == DeviceType.BatteryDevice: nc.battery_data.battery_active[i] = state elif tpe == DeviceType.ShuntDevice: nc.shunt_data.shunt_active[i] = state elif tpe == DeviceType.LoadDevice: nc.load_data.load_active[i] = state else: pass # compile the grid information calculation_islands = split_into_islands(nc)
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 run(self): """ Run a power flow for every circuit @return: """ 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 = split_into_islands( numeric_circuit=numerical_circuit, 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
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 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 = split_into_islands( numeric_circuit=numerical_circuit, 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 = split_into_islands( numeric_circuit=numerical_circuit, 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