Esempio n. 1
0
    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)
Esempio n. 2
0
    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
Esempio n. 3
0
    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
Esempio n. 4
0
    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
Esempio n. 5
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
Esempio n. 6
0
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
Esempio n. 7
0
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
Esempio n. 8
0
    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()
Esempio n. 9
0
    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