Пример #1
0
def test_power_flow():
    fname = Path(__file__).parent.parent.parent / \
            'Grids_and_profiles' / 'grids' / 'IEEE 30 Bus with storage.xlsx'

    print('Reading...')
    main_circuit = FileOpen(fname).open()
    options = PowerFlowOptions(SolverType.NR,
                               verbose=False,
                               initialize_with_existing_solution=False,
                               multi_core=False,
                               dispatch_storage=True,
                               control_q=ReactivePowerControlMode.NoControl,
                               control_p=True)
    # exit()
    ####################################################################################################################
    # PowerFlowDriver
    ####################################################################################################################
    print('\n\n')
    power_flow = PowerFlowDriver(main_circuit, options)
    power_flow.run()
    print('\n\n', main_circuit.name)
    print('\t|V|:', abs(power_flow.results.voltage))
    print('\t|Sbranch|:', abs(power_flow.results.Sbranch))
    print('\t|loading|:', abs(power_flow.results.loading) * 100)
    print('\tReport')
    print(power_flow.results.get_report_dataframe())

    assert power_flow.results.error() < 1e-3
Пример #2
0
def test_ieee_grids():
    """
    Checks the .RAW files of IEEE grids against the PSS/e results
    This test checks 2 things:
    - PSS/e import fidelity
    - PSS/e vs GridCal results
    :return: Nothing if ok, fails if not
    """

    files = [
        ('IEEE 14 bus.raw', 'IEEE 14 bus.sav.xlsx'),
        ('IEEE 30 bus.raw', 'IEEE 30 bus.sav.xlsx'),
        ('IEEE 118 Bus v2.raw', 'IEEE 118 Bus.sav.xlsx'),
    ]

    for solver_type in [SolverType.NR, SolverType.IWAMOTO, SolverType.LM]:

        print(solver_type)

        options = PowerFlowOptions(
            solver_type,
            verbose=False,
            initialize_with_existing_solution=False,
            multi_core=False,
            dispatch_storage=True,
            control_q=ReactivePowerControlMode.NoControl,
            control_p=True,
            retry_with_other_methods=False)

        for f1, f2 in files:
            print(f1, end=' ')

            fname = os.path.join('data', 'grids', f1)
            main_circuit = FileOpen(fname).open()
            power_flow = PowerFlowDriver(main_circuit, options)
            power_flow.run()

            # load the associated results file
            df_v = pd.read_excel(os.path.join('data', 'results', f2),
                                 sheet_name='Vabs',
                                 index_col=0)
            df_p = pd.read_excel(os.path.join('data', 'results', f2),
                                 sheet_name='Pbranch',
                                 index_col=0)

            v_gc = np.abs(power_flow.results.voltage)
            v_psse = df_v.values[:, 0]
            p_gc = power_flow.results.Sf.real
            p_psse = df_p.values[:, 0]

            v_ok = np.allclose(v_gc, v_psse, atol=1e-3)
            flow_ok = np.allclose(p_gc, p_psse, atol=1e-0)
            assert (v_ok)
            assert (flow_ok)

        print(solver_type, 'ok')
Пример #3
0
def test_power_flow():
    fname = Path(__file__).parent.parent.parent / \
            'Grids_and_profiles' / 'grids' / 'IEEE 5 Bus.xlsx'

    print('Reading...')

    main_circuit = FileOpen(fname).open()

    options = PowerFlowOptions(SolverType.NR,
                               verbose=False,
                               initialize_with_existing_solution=False,
                               multi_core=False,
                               dispatch_storage=True,
                               control_q=ReactivePowerControlMode.Direct,
                               control_p=True)

    # grid.export_profiles('ppppppprrrrroooofiles.xlsx')
    # exit()
    ####################################################################################################################
    # PowerFlowDriver
    ####################################################################################################################
    print('\n\n')
    power_flow = PowerFlowDriver(main_circuit, options)
    power_flow.run()

    main_circuit.build_graph()

    print('\n\n', main_circuit.name)
    print('\t|V|:', abs(power_flow.results.voltage))
    print('\t|Sbranch|:', abs(power_flow.results.Sbranch))
    print('\t|loading|:', abs(power_flow.results.loading) * 100)
    print('\tReport')
    print(power_flow.results.get_report_dataframe())

    vc_options = VoltageCollapseOptions()
    numeric_circuit = main_circuit.compile()
    numeric_inputs = numeric_circuit.compute()
    Sbase = np.zeros(len(main_circuit.buses), dtype=complex)
    Vbase = np.zeros(len(main_circuit.buses), dtype=complex)
    for c in numeric_inputs:
        Sbase[c.original_bus_idx] = c.Sbus
        Vbase[c.original_bus_idx] = c.Vbus
    unitary_vector = -1 + 2 * np.random.random(len(main_circuit.buses))
    vc_inputs = VoltageCollapseInput(Sbase=Sbase,
                                     Vbase=Vbase,
                                     Starget=Sbase * (1 + unitary_vector))
    vc = VoltageCollapse(circuit=main_circuit,
                         options=vc_options,
                         inputs=vc_inputs)
    vc.run()
    mdl = vc.results.mdl()
    mdl.plot()
    plt.show()
Пример #4
0
def test_api_helm():
    np.set_printoptions(precision=4)
    fname = os.path.join('..', '..', 'Grids_and_profiles', 'grids', 'IEEE 30 Bus with storage.xlsx')
    grid = FileOpen(fname).open()

    print('\n\n', grid.name)

    # print('Ybus:\n', grid.circuits[0].power_flow_input.Ybus.todense())
    options = PowerFlowOptions(SolverType.HELM, verbose=False, tolerance=1e-9)
    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    print_power_flow_results(power_flow)
Пример #5
0
def test_demo_5_node(root_path=ROOT_PATH):
    np.core.arrayprint.set_printoptions(precision=4)

    grid = MultiCircuit()

    # Add buses
    bus1 = Bus('Bus 1', vnom=20)
    grid.add_bus(bus1)
    gen1 = Generator('Slack Generator', voltage_module=1.0)
    grid.add_generator(bus1, gen1)

    bus2 = Bus('Bus 2', vnom=20)
    grid.add_bus(bus2)
    grid.add_load(bus2, Load('load 2', P=40, Q=20))

    bus3 = Bus('Bus 3', vnom=20)
    grid.add_bus(bus3)
    grid.add_load(bus3, Load('load 3', P=25, Q=15))

    bus4 = Bus('Bus 4', vnom=20)
    grid.add_bus(bus4)
    grid.add_load(bus4, Load('load 4', P=40, Q=20))

    bus5 = Bus('Bus 5', vnom=20)
    grid.add_bus(bus5)
    grid.add_load(bus5, Load('load 5', P=50, Q=20))

    # add branches (Lines in this case)
    grid.add_line(Line(bus1, bus2, 'line 1-2', r=0.05, x=0.11, b=0.02))
    grid.add_line(Line(bus1, bus3, 'line 1-3', r=0.05, x=0.11, b=0.02))
    grid.add_line(Line(bus1, bus5, 'line 1-5', r=0.03, x=0.08, b=0.02))
    grid.add_line(Line(bus2, bus3, 'line 2-3', r=0.04, x=0.09, b=0.02))
    grid.add_line(Line(bus2, bus5, 'line 2-5', r=0.04, x=0.09, b=0.02))
    grid.add_line(Line(bus3, bus4, 'line 3-4', r=0.06, x=0.13, b=0.03))
    grid.add_line(Line(bus4, bus5, 'line 4-5', r=0.04, x=0.09, b=0.02))
    # grid.plot_graph()
    print('\n\n', grid.name)

    FileSave(grid, 'demo_5_node.json').save()

    options = PowerFlowOptions(SolverType.NR, verbose=False)

    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    print_power_flow_results(power_flow=power_flow)
    v = np.array([1., 0.9553, 0.9548, 0.9334, 0.9534])
    all_ok = np.isclose(np.abs(power_flow.results.voltage), v, atol=1e-3)
    return all_ok
Пример #6
0
def test_demo_5_node(root_path):
    np.core.arrayprint.set_printoptions(precision=4)

    grid = MultiCircuit()

    # Add buses
    bus1 = Bus('Bus 1', vnom=20)
    # bus1.is_slack = True
    grid.add_bus(bus1)
    gen1 = Generator('Slack Generator', voltage_module=1.0)
    grid.add_generator(bus1, gen1)

    bus2 = Bus('Bus 2', vnom=20)
    grid.add_bus(bus2)
    grid.add_load(bus2, Load('load 2', P=40, Q=20))

    bus3 = Bus('Bus 3', vnom=20)
    grid.add_bus(bus3)
    grid.add_load(bus3, Load('load 3', P=25, Q=15))

    bus4 = Bus('Bus 4', vnom=20)
    grid.add_bus(bus4)
    grid.add_load(bus4, Load('load 4', P=40, Q=20))

    bus5 = Bus('Bus 5', vnom=20)
    grid.add_bus(bus5)
    grid.add_load(bus5, Load('load 5', P=50, Q=20))

    # add branches (Lines in this case)
    grid.add_branch(Branch(bus1, bus2, 'line 1-2', r=0.05, x=0.11, b=0.02))
    grid.add_branch(Branch(bus1, bus3, 'line 1-3', r=0.05, x=0.11, b=0.02))
    grid.add_branch(Branch(bus1, bus5, 'line 1-5', r=0.03, x=0.08, b=0.02))
    grid.add_branch(Branch(bus2, bus3, 'line 2-3', r=0.04, x=0.09, b=0.02))
    grid.add_branch(Branch(bus2, bus5, 'line 2-5', r=0.04, x=0.09, b=0.02))
    grid.add_branch(Branch(bus3, bus4, 'line 3-4', r=0.06, x=0.13, b=0.03))
    grid.add_branch(Branch(bus4, bus5, 'line 4-5', r=0.04, x=0.09, b=0.02))
    # grid.plot_graph()
    print('\n\n', grid.name)

    options = PowerFlowOptions(SolverType.NR, verbose=False)

    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    print_power_flow_results(power_flow=power_flow)
Пример #7
0
def test_api_multi_core_starmap():
    """
    Test the pool.starmap function together with GridCal
    """

    file_name = os.path.join('..', '..', 'Grids_and_profiles', 'grids', 'IEEE 30 Bus with storage.xlsx')
    batch_size = 100
    grid = FileOpen(file_name).open()
    print('\n\n', grid.name)

    options = PowerFlowOptions(SolverType.NR, verbose=False)
    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    # create instances of the of the power flow simulation given the grid
    print('running...')

    pool = Pool()
    results = pool.starmap(multi_island_pf, [(grid, options, 0)] * batch_size)
Пример #8
0
    def __init__(self,
                 circuit: MultiCircuit,
                 options: PowerFlowOptions,
                 max_iter=1000,
                 callback=None):
        self.circuit = circuit

        self.options = options

        self.callback = callback

        # initialize the power flow
        self.power_flow = PowerFlowDriver(self.circuit, self.options)

        # compile circuits
        self.numerical_circuit = self.circuit.compile()
        self.numerical_input_islands = self.numerical_circuit.compute()

        n = len(self.circuit.buses)
        m = len(self.circuit.branches)

        self.max_eval = max_iter

        # the dimension is the number of nodes
        self.dim = self.numerical_circuit.n_ctrl_gen
        self.x0 = np.abs(self.numerical_circuit.generator_voltage) - np.ones(
            self.dim)
        self.min = 0
        self.minimum = np.zeros(self.dim)
        self.lb = -0.1 * np.ones(self.dim)
        self.ub = 0.1 * np.ones(self.dim)
        self.int_var = np.array([])
        self.cont_var = np.arange(0, self.dim)
        self.info = str(
            self.dim) + "Generators voltage set points optimization"

        # results
        self.results = MonteCarloResults(n,
                                         m,
                                         self.max_eval,
                                         name='Set point optimization')

        self.all_f = list()

        self.it = 0
Пример #9
0
    def perform_step_run(self):
        """
        Perform only one step cascading
        Returns:
            Nothing
        """

        # initialize the simulator
        if self.cascade_type is CascadeType.PowerFlow:
            model_simulator = PowerFlowDriver(self.grid, self.options)

        elif self.cascade_type is CascadeType.LatinHypercube:
            model_simulator = StochasticPowerFlowDriver(
                self.grid, self.options, sampling_points=self.n_lhs_samples)

        else:
            model_simulator = PowerFlowDriver(self.grid, self.options)

        # For every circuit, run a power flow
        # for c in self.grid.circuits:
        model_simulator.run()

        if self.current_step == 0:
            # the first iteration try to trigger the selected indices, if any
            idx, criteria = self.remove_elements(
                self.grid,
                idx=self.triggering_idx,
                loading_vector=model_simulator.results.loading)
        else:
            # cascade normally
            idx, criteria = self.remove_elements(
                self.grid, loading_vector=model_simulator.results.loading)

        # store the removed indices and the results
        entry = CascadingReportElement(idx, model_simulator.results, criteria)
        self.results.events.append(entry)

        # increase the step number
        self.current_step += 1

        # print(model_simulator.results.get_convergence_report())

        # send the finnish signal
        self.progress_signal.emit(0.0)
        self.progress_text.emit('Done!')
        self.done_signal.emit()
Пример #10
0
    def __init__(self,
                 circuit: MultiCircuit,
                 options: PowerFlowOptions,
                 max_iter=1000,
                 callback=None):
        self.circuit = circuit

        self.options = options

        self.callback = callback

        # initialize the power flow
        self.power_flow = PowerFlowDriver(self.circuit, self.options)

        n = len(self.circuit.buses)
        m = self.circuit.get_branch_number()

        self.max_eval = max_iter

        # the dimension is the number of nodes
        self.dim = n
        self.min = 0
        self.minimum = np.zeros(self.dim)
        self.lb = -15 * np.ones(self.dim)
        self.ub = 20 * np.ones(self.dim)
        self.int_var = np.array([])
        self.cont_var = np.arange(0, self.dim)
        self.info = str(self.dim) + "Voltage collapse optimization"

        # results
        self.results = StochasticPowerFlowResults(n,
                                                  m,
                                                  self.max_eval,
                                                  name='Voltage optimization')

        # compile circuits
        self.numerical_circuit = self.circuit.compile_snapshot()
        self.numerical_input_islands = self.numerical_circuit.compute(
            ignore_single_node_islands=options.ignore_single_node_islands)

        self.it = 0
Пример #11
0
    # fname = os.path.join('..', '..', '..', '..', 'Grids_and_profiles', 'grids', 'IEEE 30 Bus with storage.xlsx')
    fname = os.path.join('..', '..', '..', '..', 'Grids_and_profiles', 'grids', 'lynn5buspv.xlsx')

    print('Reading...')
    main_circuit = FileOpen(fname).open()
    options = PowerFlowOptions(SolverType.NR, verbose=False,
                               initialize_with_existing_solution=False,
                               multi_core=False, dispatch_storage=True,
                               control_q=ReactivePowerControlMode.NoControl,
                               control_p=True)

    ####################################################################################################################
    # PowerFlowDriver
    ####################################################################################################################
    print('\n\n')
    power_flow = PowerFlowDriver(main_circuit, options)
    power_flow.run()

    print('\n\n', main_circuit.name)
    print('\t|V|:', abs(power_flow.results.voltage))
    print('\t|Sbranch|:', abs(power_flow.results.Sbranch))
    print('\t|loading|:', abs(power_flow.results.loading) * 100)
    print('\tReport')
    print(power_flow.results.get_report_dataframe())

    ####################################################################################################################
    # Voltage collapse
    ####################################################################################################################
    vc_options = VoltageCollapseOptions(step=0.001,
                                        approximation_order=VCParametrization.ArcLength,
                                        adapt_step=True,
def test_xfo_static_tap_1():
    """
    Basic test with the main transformer's  HV tap (X_C3) set at +5% (1.05 pu),
    which lowers the LV by the same amount (-5%).
    """
    test_name = "test_xfo_static_tap_1"
    grid = MultiCircuit(name=test_name)
    grid.Sbase = Sbase
    grid.time_profile = None
    grid.logger = Logger()

    # Create buses
    POI = Bus(
        name="POI",
        vnom=100,  #kV
        is_slack=True)
    grid.add_bus(POI)

    B_C3 = Bus(name="B_C3", vnom=10)  #kV
    grid.add_bus(B_C3)

    B_MV_M32 = Bus(name="B_MV_M32", vnom=10)  #kV
    grid.add_bus(B_MV_M32)

    B_LV_M32 = Bus(name="B_LV_M32", vnom=0.6)  #kV
    grid.add_bus(B_LV_M32)

    # Create voltage controlled generators (or slack, a.k.a. swing)
    UT = Generator(name="Utility")
    UT.bus = POI
    grid.add_generator(POI, UT)

    # Create static generators (with fixed power factor)
    M32 = StaticGenerator(name="M32", P=4.2, Q=0.0)  # MVA (complex)
    M32.bus = B_LV_M32
    grid.add_static_generator(B_LV_M32, M32)

    # Create transformer types
    s = 5  # MVA
    z = 8  # %
    xr = 40
    SS = TransformerType(
        name="SS",
        hv_nominal_voltage=100,  # kV
        lv_nominal_voltage=10,  # kV
        nominal_power=s,
        copper_losses=complex_impedance(z, xr).real * s * 1000 / Sbase,
        iron_losses=6.25,  # kW
        no_load_current=0.5,  # %
        short_circuit_voltage=z)
    grid.add_transformer_type(SS)

    s = 5  # MVA
    z = 6  # %
    xr = 20
    PM = TransformerType(
        name="PM",
        hv_nominal_voltage=10,  # kV
        lv_nominal_voltage=0.6,  # kV
        nominal_power=s,
        copper_losses=complex_impedance(z, xr).real * s * 1000 / Sbase,
        iron_losses=6.25,  # kW
        no_load_current=0.5,  # %
        short_circuit_voltage=z)
    grid.add_transformer_type(PM)

    # Create branches
    X_C3 = Branch(bus_from=POI,
                  bus_to=B_C3,
                  name="X_C3",
                  branch_type=BranchType.Transformer,
                  template=SS,
                  tap=1.05)
    grid.add_branch(X_C3)

    C_M32 = Branch(bus_from=B_C3,
                   bus_to=B_MV_M32,
                   name="C_M32",
                   r=0.784,
                   x=0.174)
    grid.add_branch(C_M32)

    X_M32 = Branch(bus_from=B_MV_M32,
                   bus_to=B_LV_M32,
                   name="X_M32",
                   branch_type=BranchType.Transformer,
                   template=PM)
    grid.add_branch(X_M32)

    # Apply templates (device types)
    grid.apply_all_branch_types()

    print("Buses:")
    for i, b in enumerate(grid.buses):
        print(f" - bus[{i}]: {b}")
    print()

    options = PowerFlowOptions(SolverType.NR,
                               verbose=True,
                               initialize_with_existing_solution=True,
                               multi_core=True,
                               control_q=ReactivePowerControlMode.Direct,
                               tolerance=1e-6,
                               max_iter=99)

    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    approx_volt = [round(100 * abs(v), 1) for v in power_flow.results.voltage]
    solution = [100.0, 94.7, 98.0, 98.1]  # Expected solution from GridCal

    print()
    print(f"Test: {test_name}")
    print(f"Results:  {approx_volt}")
    print(f"Solution: {solution}")
    print()

    print("Generators:")
    for g in grid.get_generators():
        print(f" - Generator {g}: q_min={g.Qmin} MVAR, q_max={g.Qmax} MVAR")
    print()

    print("Branches:")
    for b in grid.branches:
        print(f" - {b}:")
        print(f"   R = {round(b.R, 4)} pu")
        print(f"   X = {round(b.X, 4)} pu")
        print(f"   X/R = {round(b.X/b.R, 1)}")
        print(f"   G = {round(b.G, 4)} pu")
        print(f"   B = {round(b.B, 4)} pu")
    print()

    print("Transformer types:")
    for t in grid.transformer_types:
        print(
            f" - {t}: Copper losses={int(t.Pcu)}kW, Iron losses={int(t.Pfe)}kW, SC voltage={t.Vsc}%"
        )
    print()

    print("Losses:")
    for i in range(len(grid.branches)):
        print(
            f" - {grid.branches[i]}: losses={1000*round(power_flow.results.losses[i], 3)} kVA"
        )
    print()

    equal = True
    for i in range(len(approx_volt)):
        if approx_volt[i] != solution[i]:
            equal = False

    assert equal
def test_xfo_static_tap_3():
    """
    Basic test with the main transformer's  HV tap (X_C3) set at -2.5%
    (0.975 pu), which raises the LV by the same amount (+2.5%).
    """
    test_name = "test_xfo_static_tap_3"
    grid = MultiCircuit(name=test_name)
    grid.Sbase = Sbase
    grid.time_profile = None
    grid.logger = Logger()

    # Create buses
    POI = Bus(
        name="POI",
        vnom=100,  # kV
        is_slack=True)
    grid.add_bus(POI)

    B_C3 = Bus(name="B_C3", vnom=10)  # kV
    grid.add_bus(B_C3)

    B_MV_M32 = Bus(name="B_MV_M32", vnom=10)  # kV
    grid.add_bus(B_MV_M32)

    B_LV_M32 = Bus(name="B_LV_M32", vnom=0.6)  # kV
    grid.add_bus(B_LV_M32)

    # Create voltage controlled generators (or slack, a.k.a. swing)
    UT = Generator(name="Utility")
    UT.bus = POI
    grid.add_generator(POI, UT)

    # Create static generators (with fixed power factor)
    M32 = StaticGenerator(name="M32", P=4.2, Q=0.0)  # MVA (complex)
    M32.bus = B_LV_M32
    grid.add_static_generator(B_LV_M32, M32)

    # Create transformer types
    s = 5  # MVA
    z = 8  # %
    xr = 40
    SS = TransformerType(
        name="SS",
        hv_nominal_voltage=100,  # kV
        lv_nominal_voltage=10,  # kV
        nominal_power=s,
        copper_losses=complex_impedance(z, xr).real * s * 1000 / Sbase,
        iron_losses=6.25,  # kW
        no_load_current=0.5,  # %
        short_circuit_voltage=z)
    grid.add_transformer_type(SS)

    s = 5  # MVA
    z = 6  # %
    xr = 20
    PM = TransformerType(
        name="PM",
        hv_nominal_voltage=10,  # kV
        lv_nominal_voltage=0.6,  # kV
        nominal_power=s,
        copper_losses=complex_impedance(z, xr).real * s * 1000 / Sbase,
        iron_losses=6.25,  # kW
        no_load_current=0.5,  # %
        short_circuit_voltage=z)
    grid.add_transformer_type(PM)

    # Create branches
    X_C3 = Branch(bus_from=POI,
                  bus_to=B_C3,
                  name="X_C3",
                  branch_type=BranchType.Transformer,
                  template=SS,
                  tap=0.975)
    # update to a more precise tap changer
    X_C3.apply_tap_changer(
        TapChanger(taps_up=20, taps_down=20, max_reg=1.1, min_reg=0.9))
    grid.add_branch(X_C3)

    C_M32 = Branch(bus_from=B_C3,
                   bus_to=B_MV_M32,
                   name="C_M32",
                   r=0.784,
                   x=0.174)
    grid.add_branch(C_M32)

    X_M32 = Branch(bus_from=B_MV_M32,
                   bus_to=B_LV_M32,
                   name="X_M32",
                   branch_type=BranchType.Transformer,
                   template=PM)
    grid.add_branch(X_M32)

    # Apply templates (device types)
    grid.apply_all_branch_types()

    print("Buses:")
    for i, b in enumerate(grid.buses):
        print(f" - bus[{i}]: {b}")
    print()

    options = PowerFlowOptions(SolverType.NR,
                               verbose=True,
                               initialize_with_existing_solution=True,
                               multi_core=True,
                               control_q=ReactivePowerControlMode.Direct,
                               tolerance=1e-6,
                               max_iter=15)

    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    print()
    print(f"Test: {test_name}")
    print()

    print("Generators:")
    for g in grid.get_generators():
        print(f" - Generator {g}: q_min={g.Qmin} MVAR, q_max={g.Qmax} MVAR")
    print()

    print("Branches:")
    for b in grid.branches:
        print(f" - {b}:")
        print(f"   R = {round(b.R, 4)} pu")
        print(f"   X = {round(b.X, 4)} pu")
        print(f"   X/R = {round(b.X/b.R, 1)}")
        print(f"   G = {round(b.G, 4)} pu")
        print(f"   B = {round(b.B, 4)} pu")
    print()

    print("Transformer types:")
    for t in grid.transformer_types:
        print(f" - {t}: Copper losses={int(t.Pcu)}kW, "
              f"Iron losses={int(t.Pfe)}kW, SC voltage={t.Vsc}%")
    print()

    print("Losses:")
    for i in range(len(grid.branches)):
        print(
            f" - {grid.branches[i]}: losses={1000*round(power_flow.results.losses[i], 3)} kVA"
        )
    print()

    equal = False
    for i, branch in enumerate(grid.branches):
        if branch.name == "X_C3":
            equal = power_flow.results.tap_module[i] == branch.tap_module

    if not equal:
        grid.export_pf(f"{test_name}_results.xlsx", power_flow.results)
        grid.save_excel(f"{test_name}_grid.xlsx")

    assert equal
Пример #14
0
def test_corr_line_losses():
    test_name = "test_corr_line_losses"

    grid = MultiCircuit(name=test_name)
    grid.Sbase = Sbase
    grid.time_profile = None
    grid.logger = Logger()

    # Create buses
    Bus0 = Bus(name="Bus0", vnom=10, is_slack=True)
    Bus1 = Bus(name="Bus1", vnom=10)

    grid.add_bus(Bus0)
    grid.add_bus(Bus1)

    # Create load
    grid.add_load(Bus1, Load(name="Load0", P=1.0, Q=0.4))

    # Create slack bus
    grid.add_generator(Bus0, Generator(name="Utility"))

    # Create cable
    cable = Branch(
        bus_from=Bus0,
        bus_to=Bus1,
        name="Cable0",
        r=0.784,
        x=0.174,
        temp_base=20,  # °C
        temp_oper=90,  # °C
        alpha=0.00323)  # Copper

    grid.add_branch(cable)

    options = PowerFlowOptions(verbose=True, apply_temperature_correction=True)

    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    # Check solution
    approx_losses = round(power_flow.results.losses[0], 3)
    solution = complex(0.011, 0.002)  # Expected solution from GridCal
    # Tested on ETAP 16.1.0

    print(
        "\n=================================================================")
    print(f"Test: {test_name}")
    print(
        "=================================================================\n")
    print(f"Results:  {approx_losses}")
    print(f"Solution: {solution}")
    print()

    print("Buses:")
    for i, b in enumerate(grid.buses):
        print(f" - bus[{i}]: {b}")
    print()

    print("Branches:")
    for b in grid.branches:
        print(f" - {b}:")
        print(f"   R = {round(b.R, 4)} pu")
        print(f"   X = {round(b.X, 4)} pu")
        print(f"   X/R = {round(b.X/b.R, 2)}")
    print()

    print("Voltages:")
    for i in range(len(grid.buses)):
        print(
            f" - {grid.buses[i]}: voltage={round(power_flow.results.voltage[i], 3)} pu"
        )
    print()

    print("Losses:")
    for i in range(len(grid.branches)):
        print(
            f" - {grid.branches[i]}: losses={round(power_flow.results.losses[i], 3)} MVA"
        )
    print()

    print("Loadings (power):")
    for i in range(len(grid.branches)):
        print(
            f" - {grid.branches[i]}: loading={round(power_flow.results.Sbranch[i], 3)} MVA"
        )
    print()

    print("Loadings (current):")
    for i in range(len(grid.branches)):
        print(
            f" - {grid.branches[i]}: loading={round(power_flow.results.Ibranch[i], 3)} pu"
        )
    print()

    assert approx_losses == solution
Пример #15
0
def main():
    ####################################################################################################################
    # Define the circuit
    #
    # A circuit contains all the grid information regardless of the islands formed or the amount of devices
    ####################################################################################################################


    grid = MultiCircuit(name='lynn 5 bus')

    ####################################################################################################################
    # Define the buses
    ####################################################################################################################
    # I will define this bus with all the properties so you see
    bus1 = Bus(name='Bus1',
               vnom=10,   # Nominal voltage in kV
               vmin=0.9,  # Bus minimum voltage in per unit
               vmax=1.1,  # Bus maximum voltage in per unit
               xpos=0,    # Bus x position in pixels
               ypos=0,    # Bus y position in pixels
               height=0,  # Bus height in pixels
               width=0,   # Bus width in pixels
               active=True,   # Is the bus active?
               is_slack=False,  # Is this bus a slack bus?
               area='Defualt',  # Area (for grouping purposes only)
               zone='Default',  # Zone (for grouping purposes only)
               substation='Default'  # Substation (for grouping purposes only)
               )

    # the rest of the buses are defined with the default parameters
    bus2 = Bus(name='Bus2')
    bus3 = Bus(name='Bus3')
    bus4 = Bus(name='Bus4')
    bus5 = Bus(name='Bus5')

    # add the bus objects to the circuit
    grid.add_bus(bus1)
    grid.add_bus(bus2)
    grid.add_bus(bus3)
    grid.add_bus(bus4)
    grid.add_bus(bus5)

    ####################################################################################################################
    # Add the loads
    ####################################################################################################################
    # In GridCal, the loads, generators ect are stored within each bus object:

    # we'll define the first load completely
    l2 = Load(name='Load',
              G=0,  # Impedance of the ZIP model in MVA at the nominal voltage
              B=0,
              Ir=0,
              Ii=0,  # Current of the ZIP model in MVA at the nominal voltage
              P=40,
              Q=20,  # Power of the ZIP model in MVA
              P_prof=None,  # Impedance profile
              Q_prof=None,  # Current profile
              Ir_prof=None,  # Power profile
              Ii_prof=None,
              G_prof=None,
              B_prof=None,
              active=True,  # Is active?
              mttf=0.0,  # Mean time to failure
              mttr=0.0  # Mean time to recovery
              )
    grid.add_load(bus2, l2)

    # Define the others with the default parameters
    grid.add_load(bus3, Load(P=25, Q=15))
    grid.add_load(bus4, Load(P=40, Q=20))
    grid.add_load(bus5, Load(P=50, Q=20))

    ####################################################################################################################
    # Add the generators
    ####################################################################################################################

    g1 = Generator(name='gen',
                   active_power=0.0,  # Active power in MW, since this generator is used to set the slack , is 0
                   voltage_module=1.0,  # Voltage set point to control
                   Qmin=-9999,  # minimum reactive power in MVAr
                   Qmax=9999,  # Maximum reactive power in MVAr
                   Snom=9999,  # Nominal power in MVA
                   power_prof=None,  # power profile
                   vset_prof=None,  # voltage set point profile
                   active=True  # Is active?
                   )
    grid.add_generator(bus1, g1)

    ####################################################################################################################
    # Add the lines
    ####################################################################################################################

    br1 = Branch(bus_from=bus1,
                 bus_to=bus2,
                 name='Line 1-2',
                 r=0.05,  # resistance of the pi model in per unit
                 x=0.11,  # reactance of the pi model in per unit
                 g=1e-20,  # conductance of the pi model in per unit
                 b=0.02,  # susceptance of the pi model in per unit
                 rate=50,  # Rate in MVA
                 tap=1.0,  # Tap value (value close to 1)
                 shift_angle=0,  # Tap angle in radians
                 active=True,  # is the branch active?
                 mttf=0,  # Mean time to failure
                 mttr=0,  # Mean time to recovery
                 branch_type=BranchType.Line,  # Branch type tag
                 length=1,  # Length in km (to be used with templates)
                 template=BranchTemplate()  # Branch template (The default one is void)
                 )
    grid.add_branch(br1)

    grid.add_branch(Branch(bus1, bus3, name='Line 1-3', r=0.05, x=0.11, b=0.02, rate=50))
    grid.add_branch(Branch(bus1, bus5, name='Line 1-5', r=0.03, x=0.08, b=0.02, rate=80))
    grid.add_branch(Branch(bus2, bus3, name='Line 2-3', r=0.04, x=0.09, b=0.02, rate=3))
    grid.add_branch(Branch(bus2, bus5, name='Line 2-5', r=0.04, x=0.09, b=0.02, rate=10))
    grid.add_branch(Branch(bus3, bus4, name='Line 3-4', r=0.06, x=0.13, b=0.03, rate=30))
    grid.add_branch(Branch(bus4, bus5, name='Line 4-5', r=0.04, x=0.09, b=0.02, rate=30))

    ####################################################################################################################
    # Run a power flow simulation
    ####################################################################################################################

    # We need to specify power flow options
    pf_options = PowerFlowOptions(solver_type=SolverType.NR,  # Base method to use
                                  verbose=False,  # Verbose option where available
                                  tolerance=1e-6,  # power error in p.u.
                                  max_iter=25,  # maximum iteration number
                                  control_q=True  # if to control the reactive power
                                  )

    # Declare and execute the power flow simulation
    pf = PowerFlowDriver(grid, pf_options)
    pf.run()

    # now, let's compose a nice DataFrame with the voltage results
    headers = ['Vm (p.u.)', 'Va (Deg)', 'Vre', 'Vim']
    Vm = np.abs(pf.results.voltage)
    Va = np.angle(pf.results.voltage, deg=True)
    Vre = pf.results.voltage.real
    Vim = pf.results.voltage.imag
    data = np.c_[Vm, Va, Vre, Vim]
    v_df = pd.DataFrame(data=data, columns=headers, index=grid.bus_names)
    print('\n', v_df)


    # Let's do the same for the branch results
    headers = ['Loading (%)', 'Current(p.u.)', 'Power (MVA)']
    loading = np.abs(pf.results.loading) * 100
    current = np.abs(pf.results.Ibranch)
    power = np.abs(pf.results.Sbranch)
    data = np.c_[loading, current, power]
    br_df = pd.DataFrame(data=data, columns=headers, index=grid.branch_names)
    print('\n', br_df)

    # Finally the execution metrics
    print('\nError:', pf.results.error)
    print('Elapsed time (s):', pf.results.elapsed, '\n')

    print(v_df)
    print()
    print(br_df)
def main():
    ####################################################################################################################
    # Define the circuit
    #
    # A circuit contains all the grid information regardless of the islands formed or the amount of devices
    ####################################################################################################################

    # create a circuit

    grid = MultiCircuit(name='lynn 5 bus')

    # let's create a master profile

    st = datetime.datetime(2020, 1, 1)
    dates = [st + datetime.timedelta(hours=i) for i in range(24)]
    time_array = pd.to_datetime(dates)
    x = np.linspace(-np.pi, np.pi, len(time_array))
    y = np.abs(np.sin(x))
    df_0 = pd.DataFrame(data=y, index=time_array)  # complex values

    # set the grid master time profile
    grid.time_profile = df_0.index

    ####################################################################################################################
    # Define the buses
    ####################################################################################################################
    # I will define this bus with all the properties so you see
    bus1 = Bus(name='Bus1',
               vnom=10,   # Nominal voltage in kV
               vmin=0.9,  # Bus minimum voltage in per unit
               vmax=1.1,  # Bus maximum voltage in per unit
               xpos=0,    # Bus x position in pixels
               ypos=0,    # Bus y position in pixels
               height=0,  # Bus height in pixels
               width=0,   # Bus width in pixels
               active=True,   # Is the bus active?
               is_slack=False,  # Is this bus a slack bus?
               area='Defualt',  # Area (for grouping purposes only)
               zone='Default',  # Zone (for grouping purposes only)
               substation='Default'  # Substation (for grouping purposes only)
               )

    # the rest of the buses are defined with the default parameters
    bus2 = Bus(name='Bus2')
    bus3 = Bus(name='Bus3')
    bus4 = Bus(name='Bus4')
    bus5 = Bus(name='Bus5')

    # add the bus objects to the circuit
    grid.add_bus(bus1)
    grid.add_bus(bus2)
    grid.add_bus(bus3)
    grid.add_bus(bus4)
    grid.add_bus(bus5)

    ####################################################################################################################
    # Add the loads
    ####################################################################################################################

    # In GridCal, the loads, generators ect are stored within each bus object:

    # we'll define the first load completely
    l2 = Load(name='Load',
              G=0, B=0,  # admittance of the ZIP model in MVA at the nominal voltage
              Ir=0, Ii=0,  # Current of the ZIP model in MVA at the nominal voltage
              P=40, Q=20,  # Power of the ZIP model in MVA
              active=True,  # Is active?
              mttf=0.0,  # Mean time to failure
              mttr=0.0  # Mean time to recovery
              )
    grid.add_load(bus2, l2)

    # Define the others with the default parameters
    grid.add_load(bus3, Load(P=25, Q=15))
    grid.add_load(bus4, Load(P=40, Q=20))
    grid.add_load(bus5, Load(P=50, Q=20))

    ####################################################################################################################
    # Add the generators
    ####################################################################################################################

    g1 = Generator(name='gen',
                   active_power=0.0,  # Active power in MW, since this generator is used to set the slack , is 0
                   voltage_module=1.0,  # Voltage set point to control
                   Qmin=-9999,  # minimum reactive power in MVAr
                   Qmax=9999,  # Maximum reactive power in MVAr
                   Snom=9999,  # Nominal power in MVA
                   power_prof=None,  # power profile
                   vset_prof=None,  # voltage set point profile
                   active=True  # Is active?
                   )
    grid.add_generator(bus1, g1)

    ####################################################################################################################
    # Add the lines
    ####################################################################################################################

    br1 = Branch(bus_from=bus1,
                 bus_to=bus2,
                 name='Line 1-2',
                 r=0.05,  # resistance of the pi model in per unit
                 x=0.11,  # reactance of the pi model in per unit
                 g=1e-20,  # conductance of the pi model in per unit
                 b=0.02,  # susceptance of the pi model in per unit
                 rate=50,  # Rate in MVA
                 tap=1.0,  # Tap value (value close to 1)
                 shift_angle=0,  # Tap angle in radians
                 active=True,  # is the branch active?
                 mttf=0,  # Mean time to failure
                 mttr=0,  # Mean time to recovery
                 branch_type=BranchType.Line,  # Branch type tag
                 length=1,  # Length in km (to be used with templates)
                 template=BranchTemplate()  # Branch template (The default one is void)
                 )
    grid.add_branch(br1)

    grid.add_branch(Branch(bus1, bus3, name='Line 1-3', r=0.05, x=0.11, b=0.02, rate=50))
    grid.add_branch(Branch(bus1, bus5, name='Line 1-5', r=0.03, x=0.08, b=0.02, rate=80))
    grid.add_branch(Branch(bus2, bus3, name='Line 2-3', r=0.04, x=0.09, b=0.02, rate=3))
    grid.add_branch(Branch(bus2, bus5, name='Line 2-5', r=0.04, x=0.09, b=0.02, rate=10))
    grid.add_branch(Branch(bus3, bus4, name='Line 3-4', r=0.06, x=0.13, b=0.03, rate=30))
    grid.add_branch(Branch(bus4, bus5, name='Line 4-5', r=0.04, x=0.09, b=0.02, rate=30))

    FileSave(grid, 'lynn5node.gridcal').save()

    ####################################################################################################################
    # Overwrite the default profiles with the custom ones
    ####################################################################################################################

    for load in grid.get_loads():
        load.P_prof = load.P * df_0.values[:, 0]
        load.Q_prof = load.Q * df_0.values[:, 0]

    for gen in grid.get_static_generators():
        gen.P_prof = gen.Q * df_0.values[:, 0]
        gen.Q_prof = gen.Q * df_0.values[:, 0]

    for gen in grid.get_generators():
        gen.P_prof = gen.P * df_0.values[:, 0]

    ####################################################################################################################
    # Run a power flow simulation
    ####################################################################################################################

    # We need to specify power flow options
    pf_options = PowerFlowOptions(solver_type=SolverType.NR,  # Base method to use
                                  verbose=False,  # Verbose option where available
                                  tolerance=1e-6,  # power error in p.u.
                                  max_iter=25,  # maximum iteration number
                                  control_q=True  # if to control the reactive power
                                  )

    # Declare and execute the power flow simulation
    pf = PowerFlowDriver(grid, pf_options)
    pf.run()

    writer = pd.ExcelWriter('Results.xlsx')
    # now, let's compose a nice DataFrame with the voltage results
    headers = ['Vm (p.u.)', 'Va (Deg)', 'Vre', 'Vim']
    Vm = np.abs(pf.results.voltage)
    Va = np.angle(pf.results.voltage, deg=True)
    Vre = pf.results.voltage.real
    Vim = pf.results.voltage.imag
    data = np.c_[Vm, Va, Vre, Vim]
    v_df = pd.DataFrame(data=data, columns=headers, index=grid.bus_names)
    # print('\n', v_df)
    v_df.to_excel(writer, sheet_name='V')

    # Let's do the same for the branch results
    headers = ['Loading (%)', 'Current(p.u.)', 'Power (MVA)']
    loading = np.abs(pf.results.loading) * 100
    current = np.abs(pf.results.If)
    power = np.abs(pf.results.Sf)
    data = np.c_[loading, current, power]
    br_df = pd.DataFrame(data=data, columns=headers, index=grid.branch_names)
    br_df.to_excel(writer, sheet_name='Br')

    # Finally the execution metrics
    print('\nError:', pf.results.error)
    print('Elapsed time (s):', pf.results.elapsed, '\n')

    # print(tabulate(v_df, tablefmt="pipe", headers=v_df.columns.values))
    # print()
    # print(tabulate(br_df, tablefmt="pipe", headers=br_df.columns.values))



    ####################################################################################################################
    # Run a time series power flow simulation
    ####################################################################################################################

    ts = TimeSeries(grid=grid,
                    options=pf_options,
                    opf_time_series_results=None,
                    start_=0,
                    end_=None)

    ts.run()

    print()
    print('-' * 200)
    print('Time series')
    print('-' * 200)
    print('Voltage time series')
    df_voltage = pd.DataFrame(data=np.abs(ts.results.voltage), columns=grid.bus_names, index=grid.time_profile)
    df_voltage.to_excel(writer, sheet_name='Vts')
    writer.close()
def test_pv_3():
    """
    Voltage controlled generator test, also useful for a basic tutorial. In this
    case the generator M32 regulates the voltage at a setpoint of 1.025 pu, and
    the slack bus (POI) regulates it at 1.0 pu.

    The transformers' magnetizing branch losses are considered, as well as the
    main power transformer's voltage regulator (X_C3) which regulates bus
    B_MV_M32 at 1.005 pu.

    In addition, the iterative PV control method is used instead of the usual
    (faster) method.
    """
    test_name = "test_pv_3"
    grid = MultiCircuit(name=test_name)
    Sbase = 100  # MVA
    grid.Sbase = Sbase
    grid.time_profile = None
    grid.logger = Logger()

    # Create buses
    POI = Bus(
        name="POI",
        vnom=100,  # kV
        is_slack=True)
    grid.add_bus(POI)

    B_MV_M32 = Bus(name="B_MV_M32", vnom=10)  # kV
    grid.add_bus(B_MV_M32)

    B_LV_M32 = Bus(name="B_LV_M32", vnom=0.6)  # kV
    grid.add_bus(B_LV_M32)

    # Create voltage controlled generators (or slack, a.k.a. swing)
    UT = Generator(name="Utility")
    UT.bus = POI
    grid.add_generator(POI, UT)

    M32 = Generator(name="M32",
                    active_power=4.2,
                    voltage_module=1.025,
                    Qmin=-2.5,
                    Qmax=2.5)
    M32.bus = B_LV_M32
    grid.add_generator(B_LV_M32, M32)

    # Create transformer types
    s = 100  # MVA
    z = 8  # %
    xr = 40
    SS = TransformerType(
        name="SS",
        hv_nominal_voltage=100,  # kV
        lv_nominal_voltage=10,  # kV
        nominal_power=s,
        copper_losses=complex_impedance(z, xr).real * s * 1000 / Sbase,
        iron_losses=125,  # kW
        no_load_current=0.5,  # %
        short_circuit_voltage=z)
    grid.add_transformer_type(SS)

    s = 5  # MVA
    z = 6  # %
    xr = 20
    PM = TransformerType(
        name="PM",
        hv_nominal_voltage=10,  # kV
        lv_nominal_voltage=0.6,  # kV
        nominal_power=s,
        copper_losses=complex_impedance(z, xr).real * s * 1000 / Sbase,
        iron_losses=6.25,  # kW
        no_load_current=0.5,  # %
        short_circuit_voltage=z)
    grid.add_transformer_type(PM)

    # Create branches
    X_C3 = Branch(bus_from=POI,
                  bus_to=B_MV_M32,
                  name="X_C3",
                  branch_type=BranchType.Transformer,
                  template=SS,
                  bus_to_regulated=True,
                  vset=1.005)
    X_C3.tap_changer = TapChanger(taps_up=16,
                                  taps_down=16,
                                  max_reg=1.1,
                                  min_reg=0.9)
    X_C3.tap_changer.set_tap(X_C3.tap_module)
    grid.add_branch(X_C3)

    X_M32 = Branch(bus_from=B_MV_M32,
                   bus_to=B_LV_M32,
                   name="X_M32",
                   branch_type=BranchType.Transformer,
                   template=PM)
    grid.add_branch(X_M32)

    # Apply templates (device types)
    grid.apply_all_branch_types()

    print("Buses:")
    for i, b in enumerate(grid.buses):
        print(f" - bus[{i}]: {b}")
    print()

    options = PowerFlowOptions(SolverType.LM,
                               verbose=True,
                               initialize_with_existing_solution=True,
                               multi_core=True,
                               control_q=ReactivePowerControlMode.Iterative,
                               control_taps=TapsControlMode.Direct,
                               tolerance=1e-6,
                               max_iter=99)

    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    approx_volt = [round(100 * abs(v), 1) for v in power_flow.results.voltage]
    solution = [100.0, 100.7, 102.5]  # Expected solution from GridCal

    print()
    print(f"Test: {test_name}")
    print(f"Results:  {approx_volt}")
    print(f"Solution: {solution}")
    print()

    print("Generators:")
    for g in grid.get_generators():
        print(f" - Generator {g}: q_min={g.Qmin} MVAR, q_max={g.Qmax} MVAR")
    print()

    print("Branches:")
    for b in grid.branches:
        print(f" - {b}:")
        print(f"   R = {round(b.R, 4)} pu")
        print(f"   X = {round(b.X, 4)} pu")
        print(f"   X/R = {round(b.X / b.R, 1)}")
        print(f"   G = {round(b.G, 4)} pu")
        print(f"   B = {round(b.B, 4)} pu")
    print()

    print("Transformer types:")
    for t in grid.transformer_types:
        print(
            f" - {t}: Copper losses={int(t.Pcu)}kW, Iron losses={int(t.Pfe)}kW, SC voltage={t.Vsc}%"
        )
    print()

    print("Losses:")
    for i in range(len(grid.branches)):
        print(
            f" - {grid.branches[i]}: losses={1000 * round(power_flow.results.losses[i], 3)} kVA"
        )
    print()

    equal = True
    for i in range(len(approx_volt)):
        if approx_volt[i] != solution[i]:
            equal = False

    assert equal
Пример #18
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 = PowerFlowDriver(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 = PowerFlowDriver(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()
Пример #19
0
def _test_api():
    fname = os.path.join('..', '..', 'Grids_and_profiles', 'grids',
                         'IEEE 30 Bus with storage.xlsx')
    print('Reading...')
    main_circuit = FileOpen(fname).open()
    pf_options = PowerFlowOptions(SolverType.NR,
                                  verbose=False,
                                  initialize_with_existing_solution=False,
                                  multi_core=False,
                                  dispatch_storage=True,
                                  control_q=ReactivePowerControlMode.NoControl,
                                  control_p=True)
    ####################################################################################################################
    # PowerFlowDriver
    ####################################################################################################################
    print('\n\n')
    power_flow = PowerFlowDriver(main_circuit, pf_options)
    power_flow.run()
    print('\n\n', main_circuit.name)
    print('\t|V|:', abs(power_flow.results.voltage))
    print('\t|Sbranch|:', abs(power_flow.results.Sbranch))
    print('\t|loading|:', abs(power_flow.results.loading) * 100)
    print('\tReport')
    print(power_flow.results.get_report_dataframe())
    ####################################################################################################################
    # Short circuit
    ####################################################################################################################
    print('\n\n')
    print('Short Circuit')
    sc_options = ShortCircuitOptions(bus_index=[16])
    # grid, options, pf_options:, pf_results:
    sc = ShortCircuit(grid=main_circuit,
                      options=sc_options,
                      pf_options=pf_options,
                      pf_results=power_flow.results)
    sc.run()
    print('\n\n', main_circuit.name)
    print('\t|V|:', abs(main_circuit.short_circuit_results.voltage))
    print('\t|Sbranch|:', abs(main_circuit.short_circuit_results.Sbranch))
    print('\t|loading|:',
          abs(main_circuit.short_circuit_results.loading) * 100)
    ####################################################################################################################
    # Time Series
    ####################################################################################################################
    print('Running TS...', '')
    ts = TimeSeries(grid=main_circuit, options=pf_options, start_=0, end_=96)
    ts.run()
    numeric_circuit = main_circuit.compile()
    ts_analysis = TimeSeriesResultsAnalysis(numeric_circuit, ts.results)
    ####################################################################################################################
    # OPF
    ####################################################################################################################
    print('Running OPF...', '')
    opf_options = OptimalPowerFlowOptions(verbose=False,
                                          solver=SolverType.DC_OPF,
                                          mip_solver=False)
    opf = OptimalPowerFlow(grid=main_circuit, options=opf_options)
    opf.run()
    ####################################################################################################################
    # OPF Time Series
    ####################################################################################################################
    print('Running OPF-TS...', '')
    opf_options = OptimalPowerFlowOptions(verbose=False,
                                          solver=SolverType.NELDER_MEAD_OPF,
                                          mip_solver=False)
    opf_ts = OptimalPowerFlowTimeSeries(grid=main_circuit,
                                        options=opf_options,
                                        start_=0,
                                        end_=96)
    opf_ts.run()
    ####################################################################################################################
    # Voltage collapse
    ####################################################################################################################
    vc_options = VoltageCollapseOptions()
    # just for this test
    numeric_circuit = main_circuit.compile()
    numeric_inputs = numeric_circuit.compute()
    Sbase = np.zeros(len(main_circuit.buses), dtype=complex)
    Vbase = np.zeros(len(main_circuit.buses), dtype=complex)
    for c in numeric_inputs:
        Sbase[c.original_bus_idx] = c.Sbus
        Vbase[c.original_bus_idx] = c.Vbus
    unitary_vector = -1 + 2 * np.random.random(len(main_circuit.buses))
    vc_inputs = VoltageCollapseInput(Sbase=Sbase,
                                     Vbase=Vbase,
                                     Starget=Sbase * (1 + unitary_vector))
    vc = VoltageCollapse(circuit=main_circuit,
                         options=vc_options,
                         inputs=vc_inputs)
    vc.run()
    # vc.results.plot()
    ####################################################################################################################
    # Monte Carlo
    ####################################################################################################################
    print('Running MC...')
    mc_sim = MonteCarlo(main_circuit,
                        pf_options,
                        mc_tol=1e-5,
                        max_mc_iter=1000000)
    mc_sim.run()
    lst = np.array(list(range(mc_sim.results.n)), dtype=int)
    # mc_sim.results.plot(ResultTypes.BusVoltageAverage, indices=lst, names=lst)
    ####################################################################################################################
    # Latin Hypercube
    ####################################################################################################################
    print('Running LHC...')
    lhs_sim = LatinHypercubeSampling(main_circuit,
                                     pf_options,
                                     sampling_points=100)
    lhs_sim.run()
    ####################################################################################################################
    # Cascading
    ####################################################################################################################
    print('Running Cascading...')
    cascade = Cascading(main_circuit.copy(),
                        pf_options,
                        max_additional_islands=5,
                        cascade_type_=CascadeType.LatinHypercube,
                        n_lhs_samples_=10)
    cascade.run()
    cascade.perform_step_run()
    cascade.perform_step_run()
    cascade.perform_step_run()
    cascade.perform_step_run()
Пример #20
0
def test_basic():
    """
    Basic GridCal test, also useful for a basic tutorial. In this case the
    magnetizing branch of the transformers is neglected by inputting 1e-20
    excitation current and iron core losses.
    The results are identical to ETAP's, which always uses this assumption in
    balanced load flow calculations.
    """
    test_name = "test_basic"
    grid = MultiCircuit(name=test_name)
    S_base = 100  # MVA
    grid.Sbase = S_base
    grid.time_profile = None
    grid.logger = Logger()

    # Create buses
    POI = Bus(
        name="POI",
        vnom=100,  #kV
        is_slack=True)
    grid.add_bus(POI)

    B_C3 = Bus(name="B_C3", vnom=10)  #kV
    grid.add_bus(B_C3)

    B_MV_M32 = Bus(name="B_MV_M32", vnom=10)  #kV
    grid.add_bus(B_MV_M32)

    B_LV_M32 = Bus(name="B_LV_M32", vnom=0.6)  #kV
    grid.add_bus(B_LV_M32)

    # Create voltage controlled generators (or slack, a.k.a. swing)
    UT = Generator(name="Utility")
    UT.bus = POI
    grid.add_generator(POI, UT)

    # Create static generators (with fixed power factor)
    M32 = StaticGenerator(
        name="M32",
        P=4.2,  # MW
        Q=0.0j)  # MVAR
    M32.bus = B_LV_M32
    grid.add_static_generator(B_LV_M32, M32)

    # Create transformer types
    s = 5  # MVA
    z = 8  # %
    xr = 40
    SS = TransformerType(
        name="SS",
        hv_nominal_voltage=100,  # kV
        lv_nominal_voltage=10,  # kV
        nominal_power=s,
        copper_losses=complex_impedance(z, xr).real * s * 1000 / S_base,
        iron_losses=1e-20,
        no_load_current=1e-20,
        short_circuit_voltage=z)
    grid.add_transformer_type(SS)

    s = 5  # MVA
    z = 6  # %
    xr = 20
    PM = TransformerType(
        name="PM",
        hv_nominal_voltage=10,  # kV
        lv_nominal_voltage=0.6,  # kV
        nominal_power=s,
        copper_losses=complex_impedance(z, xr).real * s * 1000 / S_base,
        iron_losses=1e-20,
        no_load_current=1e-20,
        short_circuit_voltage=z)
    grid.add_transformer_type(PM)

    # Create branches
    X_C3 = Branch(bus_from=POI,
                  bus_to=B_C3,
                  name="X_C3",
                  branch_type=BranchType.Transformer,
                  template=SS)
    grid.add_branch(X_C3)

    C_M32 = Branch(bus_from=B_C3,
                   bus_to=B_MV_M32,
                   name="C_M32",
                   r=0.784,
                   x=0.174)
    grid.add_branch(C_M32)

    X_M32 = Branch(bus_from=B_MV_M32,
                   bus_to=B_LV_M32,
                   name="X_M32",
                   branch_type=BranchType.Transformer,
                   template=PM)
    grid.add_branch(X_M32)

    # Apply templates (device types)
    grid.apply_all_branch_types()

    print("Buses:")
    for i, b in enumerate(grid.buses):
        print(f" - bus[{i}]: {b}")
    print()

    options = PowerFlowOptions(SolverType.LM,
                               verbose=True,
                               initialize_with_existing_solution=True,
                               multi_core=True,
                               control_q=ReactivePowerControlMode.Direct,
                               tolerance=1e-6,
                               max_iter=99)

    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    approx_volt = [round(100 * abs(v), 1) for v in power_flow.results.voltage]
    solution = [
        100.0, 99.6, 102.7, 102.9
    ]  # Expected solution from GridCal and ETAP 16.1.0, for reference

    print()
    print(f"Test: {test_name}")
    print(f"Results:  {approx_volt}")
    print(f"Solution: {solution}")
    print()

    print("Generators:")
    for g in grid.get_generators():
        print(f" - Generator {g}: q_min={g.Qmin}pu, q_max={g.Qmax}pu")
    print()

    print("Branches:")
    for b in grid.branches:
        print(f" - {b}:")
        print(f"   R = {round(b.R, 4)} pu")
        print(f"   X = {round(b.X, 4)} pu")
        print(f"   X/R = {round(b.X/b.R, 1)}")
        print(f"   G = {round(b.G, 4)} pu")
        print(f"   B = {round(b.B, 4)} pu")
    print()

    print("Transformer types:")
    for t in grid.transformer_types:
        print(
            f" - {t}: Copper losses={int(t.Pcu)}kW, Iron losses={int(t.Pfe)}kW, SC voltage={t.Vsc}%"
        )
    print()

    print("Losses:")
    for i in range(len(grid.branches)):
        print(
            f" - {grid.branches[i]}: losses={1000*round(power_flow.results.losses[i], 3)} kVA"
        )
    print()

    equal = True
    for i in range(len(approx_volt)):
        if approx_volt[i] != solution[i]:
            equal = False

    assert equal
Пример #21
0
def test_line_losses_1():
    """
    Basic line losses test.
    """
    test_name = "test_line_losses_1"
    grid = MultiCircuit(name=test_name)
    Sbase = 100  # MVA
    grid.Sbase = Sbase
    grid.time_profile = None
    grid.logger = Logger()

    # Create buses
    Bus0 = Bus(name="Bus0", vnom=25, is_slack=True)
    Bus1 = Bus(name="Bus1", vnom=25)

    grid.add_bus(Bus0)
    grid.add_bus(Bus1)

    # Create load
    grid.add_load(Bus1, Load(name="Load0", P=1.0, Q=0.4))

    # Create slack bus
    grid.add_generator(Bus0, Generator(name="Utility"))

    # Create cable (r and x should be in pu)
    grid.add_branch(
        Line(bus_from=Bus0, bus_to=Bus1, name="Cable1", r=0.01, x=0.05))

    # Run non-linear load flow
    options = PowerFlowOptions(verbose=True)

    power_flow = PowerFlowDriver(grid, options)
    power_flow.run()

    # Check solution
    approx_losses = round(1000 * power_flow.results.losses[0], 3)
    solution = complex(0.116, 0.58)  # Expected solution from GridCal
    # Tested on ETAP 16.1.0 and pandapower

    print(
        "\n=================================================================")
    print(f"Test: {test_name}")
    print(
        "=================================================================\n")
    print(f"Results:  {approx_losses}")
    print(f"Solution: {solution}")
    print()

    print("Buses:")
    for i, b in enumerate(grid.buses):
        print(f" - bus[{i}]: {b}")
    print()

    print("Branches:")
    branches = grid.get_branches()
    for b in branches:
        print(f" - {b}:")
        print(f"   R = {round(b.R, 4)} pu")
        print(f"   X = {round(b.X, 4)} pu")
        print(f"   X/R = {round(b.X/b.R, 2)}")
    print()

    print("Voltages:")
    for i in range(len(grid.buses)):
        print(
            f" - {grid.buses[i]}: voltage={round(power_flow.results.voltage[i], 3)} pu"
        )
    print()

    print("Losses:")
    for i in range(len(branches)):
        print(
            f" - {branches[i]}: losses={round(power_flow.results.losses[i], 3)} MVA"
        )
    print()

    print("Loadings (power):")
    for i in range(len(branches)):
        print(
            f" - {branches[i]}: loading={round(power_flow.results.Sf[i], 3)} MVA"
        )
    print()

    print("Loadings (current):")
    for i in range(len(branches)):
        print(
            f" - {branches[i]}: loading={round(power_flow.results.If[i], 3)} pu"
        )
    print()

    assert approx_losses == solution
Пример #22
0
    def run_illinois_mode(self, time_indices) -> PtdfTimeSeriesResults:
        """
        Run the PTDF with the illinois formulation
        :return: TimeSeriesResults instance
        """

        # initialize the grid time series results, we will append the island results with another function
        nc = compile_opf_time_circuit(
            circuit=self.grid,
            apply_temperature=self.pf_options.apply_temperature_correction,
            branch_tolerance_mode=self.pf_options.
            branch_impedance_tolerance_mode)

        results = PtdfTimeSeriesResults(
            n=nc.nbus,
            m=nc.nbr,
            time_array=self.grid.time_profile[time_indices],
            bus_names=nc.bus_names,
            bus_types=nc.bus_types,
            branch_names=nc.branch_names)

        # if there are valid profiles...
        if self.grid.time_profile is not None:

            # run a power flow to get the initial branch power and compose the second branch power with the increment
            driver = PowerFlowDriver(grid=self.grid, options=self.pf_options)
            driver.run()

            # compile the islands
            islands = split_opf_time_circuit_into_islands(nc)

            # compose the power injections
            Pbus_0 = driver.results.Sbus.real
            V_0 = np.abs(driver.results.voltage)
            Pbr_0 = driver.results.Sbranch.real

            for island in islands:

                PTDF = compute_ptdf(Ybus=island.Ybus,
                                    Yf=island.Yf,
                                    Yt=island.Yt,
                                    Cf=island.C_branch_bus_f,
                                    Ct=island.C_branch_bus_t,
                                    V=island.Vbus[0, :],
                                    Ibus=island.Ibus[:, 0],
                                    Sbus=island.Sbus[:, 0],
                                    pq=island.pq,
                                    pv=island.pv)

                # run the PTDF time series
                for k, t_idx in enumerate(time_indices):
                    dP = Pbus_0[
                        island.original_bus_idx] - island.Sbus[:, t_idx].real
                    dP1 = island.Sbus[:, t_idx].real

                    # results.voltage[k, island.original_bus_idx] = V_0[island.original_bus_idx] + np.dot(dP, vtdf)

                    results.Sbranch[k, island.original_branch_idx] = Pbr_0[
                        island.original_branch_idx] - (PTDF * dP
                                                       )  #* island.Sbase

                    results.loading[
                        k, island.original_branch_idx] = results.Sbranch[
                            k, island.original_branch_idx] / (nc.branch_rates[
                                t_idx, island.original_branch_idx] + 1e-9)

                    results.S[
                        k, island.original_bus_idx] = island.Sbus[:,
                                                                  t_idx].real

                    progress = ((t_idx - self.start_ + 1) /
                                (self.end_ - self.start_)) * 100
                    self.progress_signal.emit(progress)
                    self.progress_text.emit('Simulating PTDF at ' +
                                            str(self.grid.time_profile[t_idx]))

        else:
            print('There are no profiles')
            self.progress_text.emit('There are no profiles')

        return results
Пример #23
0
    def run(self):
        """
        Run the monte carlo simulation
        @return:
        """

        self.__cancel__ = False

        # compile
        # print('Compiling...', end='')
        nc = compile_time_circuit(self.grid)
        calculation_inputs = nc.split_into_islands(
            ignore_single_node_islands=self.options.ignore_single_node_islands)

        self.results = CascadingResults(self.cascade_type)

        # initialize the simulator
        if self.cascade_type is CascadeType.PowerFlow:
            model_simulator = PowerFlowDriver(self.grid, self.options)

        elif self.cascade_type is CascadeType.LatinHypercube:
            model_simulator = StochasticPowerFlowDriver(
                self.grid, self.options, sampling_points=self.n_lhs_samples)

        else:
            model_simulator = PowerFlowDriver(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 the model (time series, lhs, or whatever)
            model_simulator.run()

            # remove grid elements (branches)
            idx, criteria = self.remove_probability_based(
                nc, 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 = nc.split_into_islands(
                ignore_single_node_islands=self.options.
                ignore_single_node_islands)

            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()
Пример #24
0
    def run_jacobian_mode(self, time_indices) -> PtdfTimeSeriesResults:
        """
        Run the PTDF with the illinois formulation
        :return: TimeSeriesResults instance
        """

        # initialize the grid time series results, we will append the island results with another function
        nc = compile_opf_time_circuit(
            circuit=self.grid,
            apply_temperature=self.pf_options.apply_temperature_correction,
            branch_tolerance_mode=self.pf_options.
            branch_impedance_tolerance_mode)

        results = PtdfTimeSeriesResults(
            n=nc.nbus,
            m=nc.nbr,
            time_array=self.grid.time_profile[time_indices],
            bus_names=nc.bus_names,
            bus_types=nc.bus_types,
            branch_names=nc.branch_names)

        # if there are valid profiles...
        if self.grid.time_profile is not None:

            # run a power flow to get the initial branch power and compose the second branch power with the increment
            driver = PowerFlowDriver(grid=self.grid, options=self.pf_options)
            driver.run()

            # compile the islands
            islands = split_opf_time_circuit_into_islands(nc)

            # compose the power injections
            Sbus_0 = driver.results.Sbus
            Pbus_0 = Sbus_0.real
            V_0 = driver.results.voltage
            Pbr_0 = driver.results.Sbranch.real

            for island in islands:

                V = island.Vbus[0, :]
                Ibus = island.Ibus[:, 0]

                n = len(V)
                # set up indexing for updating V
                pvpq = np.r_[island.pv, island.pq]
                npv = len(island.pv)
                npq = len(island.pq)
                # j1:j2 - V angle of pv and pq buses
                j1 = 0
                j2 = npv + npq
                # j2:j3 - V mag of pq buses
                j3 = j2 + npq

                # compute the Jacobian
                J = Jacobian(island.Ybus, V, Ibus, island.pq, pvpq)
                Jfact = factorized(J)
                dVa = np.zeros(n)
                dVm = np.zeros(n)

                # run the PTDF time series
                for k, t_idx in enumerate(time_indices):
                    # dP = Pbus_0[island.original_bus_idx] - island.Sbus[:, t_idx].real

                    # compute the power increment (f)
                    dS = Sbus_0 - island.Sbus[:, t_idx]
                    # dS = island.Sbus[:, t_idx]
                    f = np.r_[dS[pvpq].real, dS[island.pq].imag]

                    # solve the voltage increment
                    dx = Jfact(f)

                    # reassign the solution vector
                    dVa[pvpq] = dx[j1:j2]
                    dVm[island.pq] = dx[j2:j3]
                    dV = dVm * np.exp(1j * dVa)
                    V = V_0 - dV

                    Vf = island.C_branch_bus_f * V
                    If = np.conj(island.Yf * V)
                    Sf = (Vf * If) * island.Sbase

                    results.voltage[k, island.original_bus_idx] = np.abs(V)

                    results.Sbranch[k, island.original_branch_idx] = Sf.real

                    results.loading[
                        k, island.original_branch_idx] = Sf.real / (
                            nc.branch_rates[t_idx,
                                            island.original_branch_idx] + 1e-9)

                    results.S[
                        k, island.original_bus_idx] = island.Sbus[:,
                                                                  t_idx].real

                    progress = ((t_idx - self.start_ + 1) /
                                (self.end_ - self.start_)) * 100
                    self.progress_signal.emit(progress)
                    self.progress_text.emit('Simulating PTDF at ' +
                                            str(self.grid.time_profile[t_idx]))

        else:
            print('There are no profiles')
            self.progress_text.emit('There are no profiles')

        return results