def run(self): """ Run state estimation :return: """ n = len(self.grid.buses) m = len(self.grid.branches) self.se_results = StateEstimationResults() self.se_results.initialize(n, m) numerical_circuit = self.grid.compile() islands = numerical_circuit.compute() self.se_results.bus_types = numerical_circuit.bus_types for island in islands: # collect inputs of the island se_input = self.collect_measurements( circuit=self.grid, bus_idx=island.original_bus_idx, branch_idx=island.original_branch_idx) # run solver v_sol, err, converged = solve_se_lm(Ybus=island.Ybus, Yf=island.Yf, Yt=island.Yt, f=island.F, t=island.T, se_input=se_input, ref=island.ref, pq=island.pq, pv=island.pv) # Compute the branches power and the slack buses power Sbranch, Ibranch, Vbrnach, loading, \ losses, flow_direction, Sbus = PowerFlowMP.power_flow_post_process(calculation_inputs=island, V=v_sol) # pack results into a SE results object results = StateEstimationResults(Sbus=Sbus, voltage=v_sol, Sbranch=Sbranch, Ibranch=Ibranch, loading=loading, losses=losses, error=[err], converged=[converged], Qpv=None) self.se_results.apply_from_island(results, island.original_bus_idx, island.original_branch_idx)
def run_single_thread(self) -> TimeSeriesResults: """ Run single thread time series :return: TimeSeriesResults instance """ # initialize the power flow power_flow = PowerFlowMP(self.grid, self.options) # initialize the grid time series results we will append the island results with another function n = len(self.grid.buses) m = len(self.grid.branches) nt = len(self.grid.time_profile) time_series_results = TimeSeriesResults(n, m, nt, self.start_, self.end_, time=self.grid.time_profile) if self.end_ is None: self.end_ = nt # compile the multi-circuit numerical_circuit = self.grid.compile(use_opf_vals=self.use_opf_vals, opf_time_series_results=self.opf_time_series_results) # do the topological computation calc_inputs_dict = numerical_circuit.compute_ts(branch_tolerance_mode= self.options.branch_impedance_tolerance_mode) time_series_results.bus_types = numerical_circuit.bus_types # for each partition of the profiles... 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): # Are we dispatching storage? if so, generate a dictionary of battery -> bus index # to be able to set the batteries values into the vector S batteries = list() batteries_bus_idx = list() if self.options.dispatch_storage: for k, bus in enumerate(self.grid.buses): for battery in bus.batteries: battery.reset() # reset the calculation values batteries.append(battery) batteries_bus_idx.append(k) self.progress_text.emit('Time series at circuit ' + str(island_index) + '...') # 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 n = calculation_input.nbus m = calculation_input.nbr results = TimeSeriesResults(n, m, nt, self.start_, self.end_) last_voltage = calculation_input.Vbus self.progress_signal.emit(0.0) # default value in case of single-valued profile dt = 1.0 # traverse the time profiles of the partition and simulate each time step for it, t in enumerate(calculation_input.original_time_idx): if (t >= self.start_) and (t < self.end_): # 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] # add the controlled storage power if we are controlling the storage devices if self.options.dispatch_storage: if (it+1) < len(calculation_input.original_time_idx): # compute the time delta: the time values come in nanoseconds dt = (calculation_input.time_array[it + 1] - calculation_input.time_array[it]).value * 1e-9 / 3600.0 for k, battery in enumerate(batteries): power = battery.get_processed_at(it, dt=dt, store_values=True) bus_idx = batteries_bus_idx[k] S[bus_idx] += power / calculation_input.Sbase else: pass # run power flow at the circuit res = power_flow.run_pf(circuit=calculation_input, Vbus=last_voltage, Sbus=S, Ibus=I) # Recycle voltage solution last_voltage = res.voltage # store circuit results at the time index 't' results.set_at(t, res) progress = ((t - self.start_ + 1) / (self.end_ - self.start_)) * 100 self.progress_signal.emit(progress) self.progress_text.emit('Simulating island ' + str(island_index) + ' at ' + str(self.grid.time_profile[t])) else: pass if self.__cancel__: # merge the circuit's results time_series_results.apply_from_island(results, bus_original_idx, branch_original_idx, calculation_input.time_array, 'TS') # abort by returning at this point return time_series_results # merge the circuit's results time_series_results.apply_from_island(results, bus_original_idx, branch_original_idx, calculation_input.time_array, 'TS') else: print('There are no profiles') self.progress_text.emit('There are no profiles') return time_series_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( branch_tolerance_mode=self.options.branch_impedance_tolerance_mode) 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 lhs_results.bus_types = numerical_circuit.bus_types # 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 __init__(self, multi_circuit: MultiCircuit, options: PowerFlowOptions, verbose=False, break_at_value=True, good_enough_value=0): self.break_at_value = break_at_value self.good_enough_value = good_enough_value self.multi_circuit = multi_circuit self.numerical_circuit = self.multi_circuit.compile() self.calculation_inputs = self.numerical_circuit.compute(add_storage=False, add_generation=True) self.pf = PowerFlowMP(self.multi_circuit, options) # indices of generators that contribute to the static power vector 'S' self.gen_s_idx = np.where((np.logical_not(self.numerical_circuit.generator_dispatchable) * self.numerical_circuit.generator_active) == True)[0] self.bat_s_idx = np.where((np.logical_not(self.numerical_circuit.battery_dispatchable) * self.numerical_circuit.battery_active) == True)[0] # indices of generators that are to be optimized via the solution vector 'x' self.gen_x_idx = np.where((self.numerical_circuit.generator_dispatchable * self.numerical_circuit.generator_active) == True)[0] self.bat_x_idx = np.where((self.numerical_circuit.battery_dispatchable * self.numerical_circuit.battery_active) == True)[0] self.n_batteries = len(self.numerical_circuit.battery_power) self.n_controlled_gen = len(self.numerical_circuit.generator_power) # compute the problem dimension self.dim = len(self.gen_x_idx) + len(self.bat_x_idx) # get the limits of the devices to control gens = np.array(multi_circuit.get_generators()) bats = np.array(multi_circuit.get_batteries()) gen_x_up = np.array([elm.Pmax for elm in gens[self.gen_x_idx]]) gen_x_low = np.array([elm.Pmin for elm in gens[self.gen_x_idx]]) bat_x_up = np.array([elm.Pmax for elm in bats[self.bat_x_idx]]) bat_x_low = np.array([elm.Pmin for elm in bats[self.bat_x_idx]]) self.ngen = len(self.gen_x_idx) self.xlow = np.r_[gen_x_low, bat_x_low] / self.multi_circuit.Sbase self.xup = np.r_[gen_x_up, bat_x_up] / self.multi_circuit.Sbase self.range = self.xup - self.xlow # form S static ################################################################################################ # all the loads apply self.Sfix = None self.set_default_state() # build Sfix self.Vbus = np.ones(self.numerical_circuit.nbus, dtype=complex) self.Ibus = np.zeros(self.numerical_circuit.nbus, dtype=complex) # other vars needed ############################################################################################ self.converged = False self.result = None self.force_batteries_to_charge = False self.x = np.zeros(self.dim) self.fx = 0 self.t = 0 self.Emin = None self.Emax = None self.E = None self.bat_idx = None self.battery_loading_pu = 0.01 self.dt = 0
class AcOpfNelderMead: def __init__(self, multi_circuit: MultiCircuit, options: PowerFlowOptions, verbose=False, break_at_value=True, good_enough_value=0): self.break_at_value = break_at_value self.good_enough_value = good_enough_value self.multi_circuit = multi_circuit self.numerical_circuit = self.multi_circuit.compile() self.calculation_inputs = self.numerical_circuit.compute(add_storage=False, add_generation=True) self.pf = PowerFlowMP(self.multi_circuit, options) # indices of generators that contribute to the static power vector 'S' self.gen_s_idx = np.where((np.logical_not(self.numerical_circuit.generator_dispatchable) * self.numerical_circuit.generator_active) == True)[0] self.bat_s_idx = np.where((np.logical_not(self.numerical_circuit.battery_dispatchable) * self.numerical_circuit.battery_active) == True)[0] # indices of generators that are to be optimized via the solution vector 'x' self.gen_x_idx = np.where((self.numerical_circuit.generator_dispatchable * self.numerical_circuit.generator_active) == True)[0] self.bat_x_idx = np.where((self.numerical_circuit.battery_dispatchable * self.numerical_circuit.battery_active) == True)[0] self.n_batteries = len(self.numerical_circuit.battery_power) self.n_controlled_gen = len(self.numerical_circuit.generator_power) # compute the problem dimension self.dim = len(self.gen_x_idx) + len(self.bat_x_idx) # get the limits of the devices to control gens = np.array(multi_circuit.get_generators()) bats = np.array(multi_circuit.get_batteries()) gen_x_up = np.array([elm.Pmax for elm in gens[self.gen_x_idx]]) gen_x_low = np.array([elm.Pmin for elm in gens[self.gen_x_idx]]) bat_x_up = np.array([elm.Pmax for elm in bats[self.bat_x_idx]]) bat_x_low = np.array([elm.Pmin for elm in bats[self.bat_x_idx]]) self.ngen = len(self.gen_x_idx) self.xlow = np.r_[gen_x_low, bat_x_low] / self.multi_circuit.Sbase self.xup = np.r_[gen_x_up, bat_x_up] / self.multi_circuit.Sbase self.range = self.xup - self.xlow # form S static ################################################################################################ # all the loads apply self.Sfix = None self.set_default_state() # build Sfix self.Vbus = np.ones(self.numerical_circuit.nbus, dtype=complex) self.Ibus = np.zeros(self.numerical_circuit.nbus, dtype=complex) # other vars needed ############################################################################################ self.converged = False self.result = None self.force_batteries_to_charge = False self.x = np.zeros(self.dim) self.fx = 0 self.t = 0 self.Emin = None self.Emax = None self.E = None self.bat_idx = None self.battery_loading_pu = 0.01 self.dt = 0 def set_state(self, load_power, static_gen_power, controlled_gen_power, storage_power, Emin=None, Emax=None, E=None, dt=0, force_batteries_to_charge=False, bat_idx=None, battery_loading_pu=0.01): # all the loads apply self.Sfix = self.numerical_circuit.C_load_bus.T * ( - load_power / self.numerical_circuit.Sbase * self.numerical_circuit.load_active) # static generators (all apply) self.Sfix += self.numerical_circuit.C_sta_gen_bus.T * ( static_gen_power / self.numerical_circuit.Sbase * self.numerical_circuit.static_gen_active) # controlled generators self.Sfix += (self.numerical_circuit.C_gen_bus[self.gen_s_idx, :]).T * ( controlled_gen_power / self.numerical_circuit.Sbase) # batteries self.Sfix += (self.numerical_circuit.C_batt_bus[self.bat_s_idx, :]).T * ( storage_power / self.numerical_circuit.Sbase) # batteries variables to control the energy self.force_batteries_to_charge = force_batteries_to_charge self.Emin = Emin self.Emax = Emax self.E = E self.dt = dt self.bat_idx = bat_idx self.battery_loading_pu = battery_loading_pu def set_default_state(self): """ Set the default loading state """ self.set_state(load_power=self.numerical_circuit.load_power, static_gen_power=self.numerical_circuit.static_gen_power, controlled_gen_power=self.numerical_circuit.generator_power[self.gen_s_idx], storage_power=self.numerical_circuit.battery_power[self.bat_s_idx], Emin=self.numerical_circuit.battery_Enom * self.numerical_circuit.battery_min_soc, Emax=self.numerical_circuit.battery_Enom * self.numerical_circuit.battery_max_soc, E=self.numerical_circuit.battery_Enom * self.numerical_circuit.battery_soc_0, dt=1) def set_state_at(self, t, force_batteries_to_charge=False, bat_idx=None, battery_loading_pu=0.01, Emin=None, Emax=None, E=None, dt=0): """ Set the problem state at at time index Args: t: force_batteries_to_charge: bat_idx: battery_loading_pu: Emin: Emax: E: dt: Returns: """ self.set_state(load_power=self.numerical_circuit.load_power_profile[t, :], static_gen_power=self.numerical_circuit.static_gen_power_profile[t, :], controlled_gen_power=self.numerical_circuit.generator_power_profile[t, self.gen_s_idx], storage_power=self.numerical_circuit.battery_power_profile[t, self.bat_s_idx], Emin=Emin, Emax=Emax, E=E, dt=dt, force_batteries_to_charge=force_batteries_to_charge, bat_idx=bat_idx, battery_loading_pu=battery_loading_pu) self.t = t def build_solvers(self): pass def f_obj(self, x): # if self.force_batteries_to_charge: # # assign a negative upper limit to force to charge # x_up = self.xup # x_up[self.ngen:] = self.xlow[self.ngen:] * self.battery_loading_pu # else: # # use the normal limits # x_up = self.xup # compute the penalty for x outside boundaries penalty_x = 0.0 idx_up = np.where(x > self.xup)[0] penalty_x += (x[idx_up] - self.xup[idx_up]).sum() idx_low = np.where(x < self.xlow)[0] penalty_x += (self.xlow[idx_low] - x[idx_low]).sum() penalty_x *= 10 # add a lot of weight to the boundary penalty # modify the power injections (apply x) S = self.Sfix.copy() controlled_gen_power = x[0:self.ngen] storage_power = x[self.ngen:] S += (self.numerical_circuit.C_gen_bus[self.gen_x_idx, :]).T * controlled_gen_power S += (self.numerical_circuit.C_batt_bus[self.bat_x_idx, :]).T * storage_power # compute the penalty for trespassing the energy boundaries E = self.E - storage_power * self.dt idx = np.where(E > self.Emax)[0] penalty_x += (E[idx] - self.Emax[idx]).sum() idx = np.where(E < self.Emin)[0] penalty_x += (self.Emin[idx] - E[idx]).sum() # run a power flow results = self.pf.run_multi_island(self.numerical_circuit, self.calculation_inputs, self.Vbus, S, self.Ibus) loading = np.abs(results.loading) # get the indices of the branches with overload idx = np.where(loading > 1)[0] # return the summation of the overload if len(idx) > 0: f = (loading[idx] - 1).sum() # print('objective', f, 'x', x) return f + penalty_x else: return penalty_x def solve(self, verbose=False): if verbose: callback = print else: callback = None x0 = np.zeros_like(self.x) # Run the optimization self.x, self.fx = nelder_mead(self.f_obj, x_start=x0, callback=callback, break_at_value=self.break_at_value, good_enough_value=self.good_enough_value, step=0.01) print('objective', self.fx, 'x', self.x) # modify the power injections S = self.Sfix.copy() controlled_gen_power = self.x[0:self.ngen] storage_power = self.x[self.ngen:] S += (self.numerical_circuit.C_gen_bus[self.gen_x_idx, :]).T * controlled_gen_power S += (self.numerical_circuit.C_batt_bus[self.bat_x_idx, :]).T * storage_power # run a power flow pf_res = self.pf.run_multi_island(self.numerical_circuit, self.calculation_inputs, self.Vbus, S, self.Ibus) # declare the results self.result = OptimalPowerFlowResults(Sbus=pf_res.Sbus, voltage=pf_res.voltage, load_shedding=None, generation_shedding=None, battery_power=None, controlled_generation_power=None, Sbranch=pf_res.Sbranch, overloads=None, loading=pf_res.loading, converged=True) self.result.battery_power = np.zeros(self.n_batteries) self.result.battery_power[self.bat_x_idx] = self.x[self.ngen:] self.result.controlled_generation_power = np.zeros(self.n_controlled_gen) self.result.controlled_generation_power[self.gen_x_idx] = self.x[0:self.ngen] self.result.load_shedding = np.zeros_like(self.numerical_circuit.load_power) self.result.generation_shedding = np.zeros_like(self.numerical_circuit.generator_power) # overloads self.result.overloads = np.zeros_like(self.result.loading) loading = np.abs(self.result.loading) idx = np.where(loading > 1)[0] self.result.overloads[idx] = loading[idx] - 1 self.converged = True return self.result def get_branch_flows(self): return self.result.Sbranch def get_load_shedding(self): return self.result.load_shedding def get_batteries_power(self): return self.result.battery_power def get_controlled_generation(self): return self.result.controlled_generation_power def get_generation_shedding(self): return self.result.generation_shedding def get_voltage(self): return self.result.voltage def get_overloads(self): return self.result.overloads def get_loading(self): return self.result.loading
def ptdf_multi_treading(circuit: MultiCircuit, options: PowerFlowOptions, group_by_technology, power_amount, text_func=None, prog_func=None): """ Power Transfer Distribution Factors analysis :param circuit: MultiCircuit instance :param options: power flow options :param group_by_technology:group by technology of generation? :param power_amount: amount o power to vary in MW :return: """ # initialize the power flow power_flow = PowerFlowMP(circuit, options) if text_func is not None: text_func('Compiling...') # compile to arrays numerical_circuit = circuit.compile() calculation_inputs = numerical_circuit.compute( apply_temperature=options.apply_temperature_correction, branch_tolerance_mode=options.branch_impedance_tolerance_mode) # compute the variations delta_of_power_variations = get_ptdf_variations( circuit=circuit, numerical_circuit=numerical_circuit, group_by_technology=group_by_technology, power_amount=power_amount) # declare the PTDF results results = PTDFResults(n_variations=len(delta_of_power_variations) - 1, n_br=numerical_circuit.nbr, br_names=numerical_circuit.branch_names) 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, calculation_inputs, power_flow, delta_of_power_variations[v].dP, return_dict)) jobs.append(p) p.start() v += 1 k += 1 # 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 text_func is not None: text_func('Collecting results...') # gather the results 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 ptdf(circuit: MultiCircuit, options: PowerFlowOptions, group_by_technology, power_amount, text_func=None, prog_func=None): """ Power Transfer Distribution Factors analysis :param circuit: MultiCircuit instance :param options: power flow options :param group_by_technology:group by technology of generation? :param power_amount: amount o power to vary in MW :return: """ if text_func is not None: text_func('Compiling...') # initialize the power flow power_flow = PowerFlowMP(circuit, options) # compile to arrays numerical_circuit = circuit.compile() calculation_inputs = numerical_circuit.compute( apply_temperature=options.apply_temperature_correction, branch_tolerance_mode=options.branch_impedance_tolerance_mode) # compute the variations delta_of_power_variations = get_ptdf_variations( circuit=circuit, numerical_circuit=numerical_circuit, group_by_technology=group_by_technology, power_amount=power_amount) # declare the PTDF results results = PTDFResults(n_variations=len(delta_of_power_variations) - 1, n_br=numerical_circuit.nbr, br_names=numerical_circuit.branch_names) 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, calculation_inputs=calculation_inputs, power_flow=power_flow, 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) return results
def run(self): """ Run the monte carlo simulation @return: """ self.__cancel__ = False # compile # print('Compiling...', end='') numerical_circuit = self.grid.compile() calculation_inputs = numerical_circuit.compute( branch_tolerance_mode=self.options.branch_impedance_tolerance_mode) self.results = CascadingResults(self.cascade_type) # initialize the simulator if self.cascade_type is CascadeType.PowerFlow: model_simulator = PowerFlowMP(self.grid, self.options) elif self.cascade_type is CascadeType.LatinHypercube: model_simulator = LatinHypercubeSampling( self.grid, self.options, sampling_points=self.n_lhs_samples) else: model_simulator = PowerFlowMP(self.grid, self.options) self.progress_signal.emit(0.0) self.progress_text.emit('Running cascading failure...') n_grids = len(calculation_inputs) + self.max_additional_islands if n_grids > len(self.grid.buses): # safety check n_grids = len(self.grid.buses) - 1 # print('n grids: ', n_grids) it = 0 while len(calculation_inputs) <= n_grids and it <= n_grids: # For every circuit, run a power flow # for c in self.grid.circuits: model_simulator.run() # print(model_simulator.results.get_convergence_report()) # remove grid elements (branches) idx, criteria = self.remove_probability_based( numerical_circuit, model_simulator.results, max_val=1.0, min_prob=0.1) # store the removed indices and the results entry = CascadingReportElement(idx, model_simulator.results, criteria) self.results.events.append(entry) # recompile grid calculation_inputs = numerical_circuit.compute() it += 1 prog = max( len(calculation_inputs) / (n_grids + 1), it / (n_grids + 1)) self.progress_signal.emit(prog * 100.0) if self.__cancel__: break print('Grid split into ', len(calculation_inputs), ' islands after', it, ' steps') # send the finnish signal self.progress_signal.emit(0.0) self.progress_text.emit('Done!') self.done_signal.emit()
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( branch_tolerance_mode=self.options.branch_impedance_tolerance_mode) 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 mc_results.bus_types = numerical_circuit.bus_types # send the finnish signal self.progress_signal.emit(0.0) self.progress_text.emit('Done!') self.done_signal.emit() return mc_results