def run_multi_island(self, numerical_circuit, calculation_inputs, Vbus, Sbus, Ibus): """ Power flow execution for optimization purposes Args: numerical_circuit: calculation_inputs: Vbus: Sbus: Ibus: Returns: PowerFlowResults instance """ n = len(self.grid.buses) m = len(self.grid.branches) results = PowerFlowResults() results.initialize(n, m) if len(numerical_circuit.islands) > 1: # simulate each island and merge the results for i, calculation_input in enumerate(calculation_inputs): if len(calculation_input.ref) > 0: bus_original_idx = numerical_circuit.islands[i] branch_original_idx = numerical_circuit.island_branches[i] # run circuit power flow res = self.run_pf(calculation_input, Vbus[bus_original_idx], Sbus[bus_original_idx], Ibus[bus_original_idx]) # merge the results from this island results.apply_from_island(res, bus_original_idx, branch_original_idx) else: warn('There are no slack nodes in the island ' + str(i)) self.logger.append('There are no slack nodes in the island ' + str(i)) else: # run circuit power flow results = self.run_pf(calculation_inputs[0], Vbus, Sbus, Ibus) return results
def run_multi_thread(self): """ Run the monte carlo simulation @return: """ self.__cancel__ = False # initialize the grid time series results # we will append the island results with another function self.circuit.time_series_results = TimeSeriesResults(0, 0, 0, 0, 0) Sbase = self.circuit.Sbase n_cores = multiprocessing.cpu_count() it = 0 variance_sum = 0.0 std_dev_progress = 0 v_variance = 0 n = len(self.circuit.buses) m = len(self.circuit.branches) mc_results = MonteCarloResults(n, m) avg_res = PowerFlowResults() avg_res.initialize(n, m) # compile circuits numerical_circuit = self.circuit.compile() numerical_input_islands = numerical_circuit.compute() v_sum = zeros(n, dtype=complex) self.progress_signal.emit(0.0) while (std_dev_progress < 100.0) and (it < self.max_mc_iter) and not self.__cancel__: self.progress_text.emit('Running Monte Carlo: Variance: ' + str(v_variance)) mc_results = MonteCarloResults(n, m, self.batch_size) # For every circuit, run the time series for numerical_island in numerical_input_islands: # set the time series as sampled monte_carlo_input = make_monte_carlo_input(numerical_island) mc_time_series = monte_carlo_input(self.batch_size, use_latin_hypercube=False) Vbus = numerical_island.Vbus manager = multiprocessing.Manager() return_dict = manager.dict() # short cut the indices b_idx = numerical_island.original_bus_idx br_idx = numerical_island.original_branch_idx t = 0 while t < self.batch_size and not self.__cancel__: k = 0 jobs = list() # launch only n_cores jobs at the time while k < n_cores + 2 and (t + k) < self.batch_size: # set the power values Y, I, S = mc_time_series.get_at(t) # run power flow at the circuit p = multiprocessing.Process( target=power_flow_worker, args=(t, self.options, numerical_island, Vbus, S / Sbase, I / Sbase, return_dict)) jobs.append(p) p.start() k += 1 t += 1 # wait for all jobs to complete for proc in jobs: proc.join() # progress = ((t + 1) / self.batch_size) * 100 # self.progress_signal.emit(progress) # collect results self.progress_text.emit('Collecting batch results...') for t in return_dict.keys(): # store circuit results at the time index 't' res = return_dict[t] mc_results.S_points[ t, numerical_island.original_bus_idx] = res.Sbus mc_results.V_points[ t, numerical_island.original_bus_idx] = res.voltage mc_results.I_points[ t, numerical_island.original_branch_idx] = res.Ibranch mc_results.loading_points[ t, numerical_island.original_branch_idx] = res.loading mc_results.losses_points[ t, numerical_island.original_branch_idx] = res.losses # compile MC results self.progress_text.emit('Compiling results...') mc_results.compile() # compute the island branch results island_avg_res = numerical_island.compute_branch_results( mc_results.voltage[b_idx]) # apply the island averaged results avg_res.apply_from_island(island_avg_res, b_idx=b_idx, br_idx=br_idx) # Compute the Monte Carlo values it += self.batch_size mc_results.append_batch(mc_results) v_sum += mc_results.get_voltage_sum() v_avg = v_sum / it v_variance = abs( (power(mc_results.V_points - v_avg, 2.0) / (it - 1)).min()) # progress variance_sum += v_variance err = variance_sum / it if err == 0: err = 1e-200 # to avoid division by zeros mc_results.error_series.append(err) # emmit the progress signal std_dev_progress = 100 * self.mc_tol / err if std_dev_progress > 100: std_dev_progress = 100 self.progress_signal.emit( max((std_dev_progress, it / self.max_mc_iter * 100))) # print(iter, '/', max_mc_iter) # print('Vmc:', Vavg) # print('Vstd:', Vvariance, ' -> ', std_dev_progress, ' %') # compute the averaged branch magnitudes mc_results.sbranch = avg_res.Sbranch mc_results.losses = avg_res.losses # print('V mc: ', mc_results.voltage) # send the finnish signal self.progress_signal.emit(0.0) self.progress_text.emit('Done!') self.done_signal.emit() return mc_results
def run_single_thread(self): """ Run the monte carlo simulation @return: """ # print('LHS run') self.__cancel__ = False # initialize the power flow power_flow = PowerFlowMP(self.circuit, self.options) # initialize the grid time series results # we will append the island results with another function self.circuit.time_series_results = TimeSeriesResults(0, 0, 0, 0, 0) batch_size = self.sampling_points n = len(self.circuit.buses) m = len(self.circuit.branches) self.progress_signal.emit(0.0) self.progress_text.emit('Running Latin Hypercube Sampling...') lhs_results = MonteCarloResults(n, m, batch_size) avg_res = PowerFlowResults() avg_res.initialize(n, m) # compile the numerical circuit numerical_circuit = self.circuit.compile() numerical_input_islands = numerical_circuit.compute() max_iter = batch_size * len(numerical_input_islands) Sbase = numerical_circuit.Sbase it = 0 # For every circuit, run the time series for numerical_island in numerical_input_islands: # try: # set the time series as sampled in the circuit # build the inputs monte_carlo_input = make_monte_carlo_input(numerical_island) mc_time_series = monte_carlo_input(batch_size, use_latin_hypercube=True) Vbus = numerical_island.Vbus # short cut the indices b_idx = numerical_island.original_bus_idx br_idx = numerical_island.original_branch_idx # run the time series for t in range(batch_size): # set the power values from a Monte carlo point at 't' Y, I, S = mc_time_series.get_at(t) # Run the set monte carlo point at 't' res = power_flow.run_pf(circuit=numerical_island, Vbus=Vbus, Sbus=S / Sbase, Ibus=I / Sbase) # Gather the results lhs_results.S_points[ t, numerical_island.original_bus_idx] = res.Sbus lhs_results.V_points[ t, numerical_island.original_bus_idx] = res.voltage lhs_results.I_points[ t, numerical_island.original_branch_idx] = res.Ibranch lhs_results.loading_points[ t, numerical_island.original_branch_idx] = res.loading lhs_results.losses_points[ t, numerical_island.original_branch_idx] = res.losses it += 1 self.progress_signal.emit(it / max_iter * 100) if self.__cancel__: break if self.__cancel__: break # compile MC results self.progress_text.emit('Compiling results...') lhs_results.compile() # compute the island branch results island_avg_res = numerical_island.compute_branch_results( lhs_results.voltage[b_idx]) # apply the island averaged results avg_res.apply_from_island(island_avg_res, b_idx=b_idx, br_idx=br_idx) # lhs_results the averaged branch magnitudes lhs_results.sbranch = avg_res.Sbranch # Ibranch = avg_res.Ibranch # loading = avg_res.loading # lhs_results.losses = avg_res.losses # flow_direction = avg_res.flow_direction # Sbus = avg_res.Sbus self.results = lhs_results # send the finnish signal self.progress_signal.emit(0.0) self.progress_text.emit('Done!') self.done_signal.emit() return lhs_results
def run_multi_thread(self): """ Run the monte carlo simulation @return: """ # print('LHS run') self.__cancel__ = False # initialize vars batch_size = self.sampling_points n = len(self.circuit.buses) m = len(self.circuit.branches) n_cores = multiprocessing.cpu_count() self.progress_signal.emit(0.0) self.progress_text.emit( 'Running Latin Hypercube Sampling in parallel using ' + str(n_cores) + ' cores ...') lhs_results = MonteCarloResults(n, m, batch_size) avg_res = PowerFlowResults() avg_res.initialize(n, m) # compile # print('Compiling...', end='') numerical_circuit = self.circuit.compile() numerical_islands = numerical_circuit.compute() max_iter = batch_size * len(numerical_islands) Sbase = self.circuit.Sbase it = 0 # For every circuit, run the time series for numerical_island in numerical_islands: # try: # set the time series as sampled in the circuit monte_carlo_input = make_monte_carlo_input(numerical_island) mc_time_series = monte_carlo_input(batch_size, use_latin_hypercube=True) Vbus = numerical_island.Vbus # short cut the indices b_idx = numerical_island.original_bus_idx br_idx = numerical_island.original_branch_idx manager = multiprocessing.Manager() return_dict = manager.dict() t = 0 while t < batch_size and not self.__cancel__: k = 0 jobs = list() # launch only n_cores jobs at the time while k < n_cores + 2 and (t + k) < batch_size: # set the power values Y, I, S = mc_time_series.get_at(t) # run power flow at the circuit p = multiprocessing.Process( target=power_flow_worker, args=(t, self.options, numerical_island, Vbus, S / Sbase, I / Sbase, return_dict)) jobs.append(p) p.start() k += 1 t += 1 # wait for all jobs to complete for proc in jobs: proc.join() progress = ((t + 1) / batch_size) * 100 self.progress_signal.emit(progress) # collect results self.progress_text.emit('Collecting results...') for t in return_dict.keys(): # store circuit results at the time index 't' res = return_dict[t] lhs_results.S_points[ t, numerical_island.original_bus_idx] = res.Sbus lhs_results.V_points[ t, numerical_island.original_bus_idx] = res.voltage lhs_results.I_points[ t, numerical_island.original_branch_idx] = res.Ibranch lhs_results.loading_points[ t, numerical_island.original_branch_idx] = res.loading lhs_results.losses_points[ t, numerical_island.original_branch_idx] = res.losses # except Exception as ex: # print(c.name, ex) if self.__cancel__: break # compile MC results self.progress_text.emit('Compiling results...') lhs_results.compile() # compute the island branch results island_avg_res = numerical_island.compute_branch_results( lhs_results.voltage[b_idx]) # apply the island averaged results avg_res.apply_from_island(island_avg_res, b_idx=b_idx, br_idx=br_idx) # lhs_results the averaged branch magnitudes lhs_results.sbranch = avg_res.Sbranch lhs_results.losses = avg_res.losses self.results = lhs_results # send the finnish signal self.progress_signal.emit(0.0) self.progress_text.emit('Done!') self.done_signal.emit() return lhs_results
def run_single_thread(self): """ Run the monte carlo simulation @return: """ self.__cancel__ = False # initialize the power flow power_flow = PowerFlowMP(self.circuit, self.options) # initialize the grid time series results # we will append the island results with another function self.circuit.time_series_results = TimeSeriesResults(0, 0, 0, 0, 0) Sbase = self.circuit.Sbase it = 0 variance_sum = 0.0 std_dev_progress = 0 v_variance = 0 n = len(self.circuit.buses) m = len(self.circuit.branches) # compile circuits numerical_circuit = self.circuit.compile() numerical_input_islands = numerical_circuit.compute() mc_results = MonteCarloResults(n, m) avg_res = PowerFlowResults() avg_res.initialize(n, m) v_sum = zeros(n, dtype=complex) self.progress_signal.emit(0.0) while (std_dev_progress < 100.0) and (it < self.max_mc_iter) and not self.__cancel__: self.progress_text.emit('Running Monte Carlo: Variance: ' + str(v_variance)) mc_results = MonteCarloResults(n, m, self.batch_size) # For every circuit, run the time series for numerical_island in numerical_input_islands: # set the time series as sampled monte_carlo_input = make_monte_carlo_input(numerical_island) mc_time_series = monte_carlo_input(self.batch_size, use_latin_hypercube=False) Vbus = numerical_island.Vbus # run the time series for t in range(self.batch_size): # set the power values Y, I, S = mc_time_series.get_at(t) # res = powerflow.run_at(t, mc=True) res = power_flow.run_pf(circuit=numerical_island, Vbus=Vbus, Sbus=S / Sbase, Ibus=I / Sbase) mc_results.S_points[ t, numerical_island.original_bus_idx] = res.Sbus mc_results.V_points[ t, numerical_island.original_bus_idx] = res.voltage mc_results.I_points[ t, numerical_island.original_branch_idx] = res.Ibranch mc_results.loading_points[ t, numerical_island.original_branch_idx] = res.loading mc_results.losses_points[ t, numerical_island.original_branch_idx] = res.losses # short cut the indices b_idx = numerical_island.original_bus_idx br_idx = numerical_island.original_branch_idx self.progress_text.emit('Compiling results...') mc_results.compile() # compute the island branch results island_avg_res = numerical_island.compute_branch_results( mc_results.voltage[b_idx]) # apply the island averaged results avg_res.apply_from_island(island_avg_res, b_idx=b_idx, br_idx=br_idx) # Compute the Monte Carlo values it += self.batch_size mc_results.append_batch(mc_results) v_sum += mc_results.get_voltage_sum() v_avg = v_sum / it v_variance = abs( (power(mc_results.V_points - v_avg, 2.0) / (it - 1)).min()) # progress variance_sum += v_variance err = variance_sum / it if err == 0: err = 1e-200 # to avoid division by zeros mc_results.error_series.append(err) # emmit the progress signal std_dev_progress = 100 * self.mc_tol / err if std_dev_progress > 100: std_dev_progress = 100 self.progress_signal.emit( max((std_dev_progress, it / self.max_mc_iter * 100))) # print(iter, '/', max_mc_iter) # print('Vmc:', Vavg) # print('Vstd:', Vvariance, ' -> ', std_dev_progress, ' %') # compile results mc_results.sbranch = avg_res.Sbranch # mc_results.losses = avg_res.losses # send the finnish signal self.progress_signal.emit(0.0) self.progress_text.emit('Done!') self.done_signal.emit() return mc_results
def __init__(self, n, m, nt, start, end, time=None): """ TimeSeriesResults constructor @param n: number of buses @param m: number of branches @param nt: number of time steps """ PowerFlowResults.__init__(self) self.nt = nt self.m = m self.n = n self.start = start self.end = end self.time = time if nt > 0: self.voltage = zeros((nt, n), dtype=complex) self.Sbranch = zeros((nt, m), dtype=complex) self.Ibranch = zeros((nt, m), dtype=complex) self.loading = zeros((nt, m), dtype=complex) self.losses = zeros((nt, m), dtype=complex) self.flow_direction = zeros((nt, m), dtype=float) self.error = zeros(nt) self.converged = ones(nt, dtype=bool) # guilty assumption # self.Qpv = Qpv self.overloads = [None] * nt self.overvoltage = [None] * nt self.undervoltage = [None] * nt self.overloads_idx = [None] * nt self.overvoltage_idx = [None] * nt self.undervoltage_idx = [None] * nt self.buses_useful_for_storage = [None] * nt else: self.voltage = None self.Sbranch = None self.Ibranch = None self.loading = None self.losses = None self.flow_direction = None self.error = None self.converged = None # self.Qpv = Qpv self.overloads = None self.overvoltage = None self.undervoltage = None self.overloads_idx = None self.overvoltage_idx = None self.undervoltage_idx = None self.buses_useful_for_storage = None self.available_results = [ 'Bus voltage', 'Branch power', 'Branch current', 'Branch_loading', 'Branch losses' ]
def run(self, store_in_island=False): """ Pack run_pf for the QThread :return: """ # print('PowerFlow at ', self.grid.name) n = len(self.grid.buses) m = len(self.grid.branches) results = PowerFlowResults() results.initialize(n, m) # self.progress_signal.emit(0.0) Sbase = self.grid.Sbase # columns of the report col = ['Method', 'Converged?', 'Error', 'Elapsed (s)', 'Iterations'] self.convergence_reports.clear() print('Compiling...', end='') numerical_circuit = self.grid.compile() calculation_inputs = numerical_circuit.compute() if len(numerical_circuit.islands) > 1: # simulate each island and merge the results for i, calculation_input in enumerate(calculation_inputs): if len(calculation_input.ref) > 0: Vbus = calculation_input.Vbus Sbus = calculation_input.Sbus Ibus = calculation_input.Ibus # run circuit power flow res = self.run_pf(calculation_input, Vbus, Sbus, Ibus) bus_original_idx = numerical_circuit.islands[i] branch_original_idx = numerical_circuit.island_branches[i] # merge the results from this island results.apply_from_island(res, bus_original_idx, branch_original_idx) # build the report data = np.c_[results.methods[i], results.converged[i], results.error[i], results.elapsed[i], results.inner_iterations[i]] df = pd.DataFrame(data, columns=col) self.convergence_reports.append(df) else: warn('There are no slack nodes in the island ' + str(i)) self.logger.append('There are no slack nodes in the island ' + str(i)) else: # only one island Vbus = calculation_inputs[0].Vbus Sbus = calculation_inputs[0].Sbus Ibus = calculation_inputs[0].Ibus # run circuit power flow results = self.run_pf(calculation_inputs[0], Vbus, Sbus, Ibus) # build the report data = np.c_[results.methods[0], results.converged[0], results.error[0], results.elapsed[0], results.inner_iterations[0]] df = pd.DataFrame(data, columns=col) self.convergence_reports.append(df) self.last_V = results.voltage # done inside single_power_flow # check the limits sum_dev = results.check_limits(F=numerical_circuit.F, T=numerical_circuit.T, Vmax=numerical_circuit.Vmax, Vmin=numerical_circuit.Vmin, wo=1, wv1=1, wv2=1) self.results = results return self.results
def single_power_flow(self, circuit: CalculationInputs, solver_type: SolverType, voltage_solution, Sbus, Ibus): """ Run a power flow simulation for a single circuit @param circuit: @param solver_type @param voltage_solution @return: """ original_types = circuit.types.copy() ref, pq, pv, pqpv = self.compile_types(Sbus, original_types) any_control_issue = True # guilty assumption... control_max_iter = 10 inner_it = list() outer_it = 0 elapsed = list() methods = list() converged_lst = list() errors = list() it = list() el = list() while any_control_issue and outer_it < control_max_iter: if len(circuit.ref) == 0: voltage_solution = zeros(len(Sbus), dtype=complex) normF = 0 Scalc = Sbus.copy() any_control_issue = False converged = True warn('Not solving power flow because there is no slack bus') self.logger.append('Not solving power flow because there is no slack bus') else: # type HELM if solver_type == SolverType.HELM: methods.append(SolverType.HELM) voltage_solution, converged, normF, Scalc, it, el = helm(Vbus=voltage_solution, Sbus=Sbus, Ybus=circuit.Ybus, pq=pq, pv=pv, ref=ref, pqpv=pqpv, tol=self.options.tolerance, max_coefficient_count=self.options.max_iter) # type DC elif solver_type == SolverType.DC: methods.append(SolverType.DC) voltage_solution, converged, normF, Scalc, it, el = dcpf(Ybus=circuit.Ybus, Sbus=Sbus, Ibus=Ibus, V0=voltage_solution, ref=ref, pvpq=pqpv, pq=pq, pv=pv) # LAC PF elif solver_type == SolverType.LACPF: methods.append(SolverType.LACPF) voltage_solution, converged, normF, Scalc, it, el = lacpf(Y=circuit.Ybus, Ys=circuit.Yseries, S=Sbus, I=Ibus, Vset=voltage_solution, pq=pq, pv=pv) # Levenberg-Marquardt elif solver_type == SolverType.LM: methods.append(SolverType.LM) voltage_solution, converged, normF, Scalc, it, el = LevenbergMarquardtPF( Ybus=circuit.Ybus, Sbus=Sbus, V0=voltage_solution, Ibus=Ibus, pv=pv, pq=pq, tol=self.options.tolerance, max_it=self.options.max_iter) # Fast decoupled elif solver_type == SolverType.FASTDECOUPLED: methods.append(SolverType.FASTDECOUPLED) voltage_solution, converged, normF, Scalc, it, el = FDPF(Vbus=voltage_solution, Sbus=Sbus, Ibus=Ibus, Ybus=circuit.Ybus, B1=circuit.B1, B2=circuit.B2, pq=pq, pv=pv, pqpv=pqpv, tol=self.options.tolerance, max_it=self.options.max_iter) # Newton-Raphson elif solver_type == SolverType.NR: methods.append(SolverType.NR) # Solve NR with the linear AC solution voltage_solution, converged, normF, Scalc, it, el = NR_LS(Ybus=circuit.Ybus, Sbus=Sbus, V0=voltage_solution, Ibus=Ibus, pv=pv, pq=pq, tol=self.options.tolerance, max_it=self.options.max_iter) # Newton-Raphson-Iwamoto elif solver_type == SolverType.IWAMOTO: methods.append(SolverType.IWAMOTO) voltage_solution, converged, normF, Scalc, it, el = IwamotoNR(Ybus=circuit.Ybus, Sbus=Sbus, V0=voltage_solution, Ibus=Ibus, pv=pv, pq=pq, tol=self.options.tolerance, max_it=self.options.max_iter, robust=True) # Newton-Raphson in current equations elif solver_type == SolverType.NRI: methods.append(SolverType.NRI) # NR_I_LS(Ybus, Sbus_sp, V0, Ibus_sp, pv, pq, tol, max_it voltage_solution, converged, normF, Scalc, it, el = NR_I_LS(Ybus=circuit.Ybus, Sbus_sp=Sbus, V0=voltage_solution, Ibus_sp=Ibus, pv=pv, pq=pq, tol=self.options.tolerance, max_it=self.options.max_iter) # for any other method, for now, do a LM else: methods.append(SolverType.LM) voltage_solution, converged, \ normF, Scalc, it, el = LevenbergMarquardtPF(Ybus=circuit.Ybus, Sbus=Sbus, V0=voltage_solution, Ibus=Ibus, pv=pv, pq=pq, tol=self.options.tolerance, max_it=self.options.max_iter) if converged: # Check controls if self.options.control_Q: voltage_solution, \ Qnew, \ types_new, \ any_control_issue = self.switch_logic(V=voltage_solution, Vset=abs(voltage_solution), Q=Scalc.imag, Qmax=circuit.Qmax, Qmin=circuit.Qmin, types=circuit.types, original_types=original_types, verbose=self.options.verbose) if any_control_issue: # voltage_solution = Vnew Sbus = Sbus.real + 1j * Qnew ref, pq, pv, pqpv = self.compile_types(Sbus, types_new) else: if self.options.verbose: print('Controls Ok') else: # did not check Q limits any_control_issue = False else: any_control_issue = False # increment the inner iterations counter inner_it.append(it) # increment the outer control iterations counter outer_it += 1 # add the time taken by the solver in this iteration elapsed.append(el) # append loop error errors.append(normF) # append converged converged_lst.append(bool(converged)) # revert the types to the original # ref, pq, pv, pqpv = self.compile_types(original_types) # Compute the branches power and the slack buses power Sbranch, Ibranch, loading, losses, \ flow_direction, Sbus = self.power_flow_post_process(calculation_inputs=circuit, V=voltage_solution) # voltage, Sbranch, loading, losses, error, converged, Qpv results = PowerFlowResults(Sbus=Sbus, voltage=voltage_solution, Sbranch=Sbranch, Ibranch=Ibranch, loading=loading, losses=losses, flow_direction=flow_direction, error=errors, converged=converged_lst, Qpv=None, inner_it=inner_it, outer_it=outer_it, elapsed=elapsed, methods=methods) return results
def run_pf(self, circuit: CalculationInputs, Vbus, Sbus, Ibus): """ Run a power flow for a circuit Args: circuit: Vbus: Sbus: Ibus: battery_energy: Returns: """ # Retry with another solver if self.options.auxiliary_solver_type is not None: solvers = [self.options.solver_type, SolverType.IWAMOTO, SolverType.FASTDECOUPLED, SolverType.LM, SolverType.LACPF] else: # No retry selected solvers = [self.options.solver_type] # set worked to false to enter in the loop worked = False k = 0 methods = list() inner_it = list() elapsed = list() errors = list() converged_lst = list() outer_it = 0 if not worked: while k < len(solvers) and not worked: # get the solver solver = solvers[k] # print('Trying', solver) # set the initial voltage V0 = Vbus.copy() results = self.single_power_flow(circuit=circuit, solver_type=solver, voltage_solution=V0, Sbus=Sbus, Ibus=Ibus) # did it worked? worked = np.all(results.converged) # record the solver steps methods += results.methods inner_it += results.inner_iterations outer_it += results.outer_iterations elapsed += results.elapsed errors += results.error converged_lst += results.converged k += 1 if not worked: print('Did not converge, even after retry!, Error:', results.error) return None else: # set the total process variables: results.methods = methods results.inner_iterations = inner_it results.outer_iterations = outer_it results.elapsed = elapsed results.error = errors results.converged = converged_lst # check the power flow limits results.check_limits(F=circuit.F, T=circuit.T, Vmax=circuit.Vmax, Vmin=circuit.Vmin, wo=1, wv1=1, wv2=1) return results else: # the original solver worked pass return PowerFlowResults()
def single_power_flow(self, circuit: CalculationInputs, solver_type: SolverType, voltage_solution, Sbus, Ibus): """ Run a power flow simulation for a single circuit using the selected outer loop controls :param circuit: CalculationInputs instance :param solver_type: type of power flow to use first :param voltage_solution: vector of initial voltages :param Sbus: vector of power injections :param Ibus: vector of current injections :return: PowerFlowResults instance """ # get the original types and compile this class' own lists of node types for thread independence original_types = circuit.types.copy() ref, pq, pv, pqpv = self.compile_types(Sbus, original_types) # copy the tap positions tap_positions = circuit.tap_position.copy() tap_module = circuit.tap_mod.copy() any_q_control_issue = True # guilty assumption... any_tap_control_issue = True # The control iterations are either the number of tap_regulated transformers or 10, the larger of the two control_max_iter = 10 for k in circuit.bus_to_regulated_idx: # indices of the branches that are regulated at the bus "to" control_max_iter = max(control_max_iter, circuit.max_tap[k] + circuit.min_tap[k]) # control_max_iter = max(len(circuit.bus_to_regulated_idx), 10) inner_it = list() outer_it = 0 elapsed = list() methods = list() converged_lst = list() errors = list() it = list() el = list() # this the "outer-loop" while (any_q_control_issue or any_tap_control_issue) and outer_it < control_max_iter: if len(circuit.ref) == 0: voltage_solution = zeros(len(Sbus), dtype=complex) normF = 0 Scalc = Sbus.copy() any_q_control_issue = False converged = True warn('Not solving power flow because there is no slack bus') self.logger.append('Not solving power flow because there is no slack bus') else: # run the power flow method that shall be run voltage_solution, converged, normF, Scalc, it, el = self.solve(solver_type=solver_type, V0=voltage_solution, Sbus=Sbus, Ibus=Ibus, Ybus=circuit.Ybus, Yseries=circuit.Yseries, B1=circuit.B1, B2=circuit.B2, pq=pq, pv=pv, ref=ref, pqpv=pqpv, tolerance=self.options.tolerance, max_iter=self.options.max_iter) # record the method used methods.append(solver_type) if converged: # Check controls if self.options.control_Q: voltage_solution, \ Qnew, \ types_new, \ any_q_control_issue = self.switch_logic(V=voltage_solution, Vset=np.abs(voltage_solution), Q=Scalc.imag, Qmax=circuit.Qmax, Qmin=circuit.Qmin, types=circuit.types, original_types=original_types, verbose=self.options.verbose) if any_q_control_issue: Sbus = Sbus.real + 1j * Qnew ref, pq, pv, pqpv = self.compile_types(Sbus, types_new) else: if self.options.verbose: print('Controls Ok') else: # did not check Q limits any_q_control_issue = False # control the transformer taps if self.options.control_taps and any_tap_control_issue: stable, tap_module, \ tap_positions = self.adjust_tap_changers(voltage=voltage_solution, T=circuit.T, bus_to_regulated_idx=circuit.bus_to_regulated_idx, tap_position=tap_positions, tap_module=tap_module, min_tap=circuit.min_tap, max_tap=circuit.max_tap, tap_inc_reg_up=circuit.tap_inc_reg_up, tap_inc_reg_down=circuit.tap_inc_reg_down, vset=circuit.vset, verbose=self.options.verbose) # print('Recompiling Ybus due to tap changes') # recompute the admittance matrices based on the tap changes circuit.re_calc_admittance_matrices(tap_module) any_tap_control_issue = not stable else: any_tap_control_issue = False else: any_q_control_issue = False any_tap_control_issue = False # increment the inner iterations counter inner_it.append(it) # increment the outer control iterations counter outer_it += 1 # add the time taken by the solver in this iteration elapsed.append(el) # append loop error errors.append(normF) # append converged converged_lst.append(bool(converged)) # Compute the branches power and the slack buses power Sbranch, Ibranch, loading, losses, \ flow_direction, Sbus = self.power_flow_post_process(calculation_inputs=circuit, V=voltage_solution) # voltage, Sbranch, loading, losses, error, converged, Qpv results = PowerFlowResults(Sbus=Sbus, voltage=voltage_solution, Sbranch=Sbranch, Ibranch=Ibranch, loading=loading, losses=losses, flow_direction=flow_direction, tap_module=tap_module, error=errors, converged=converged_lst, Qpv=Sbus.imag[pv], inner_it=inner_it, outer_it=outer_it, elapsed=elapsed, methods=methods) return results