def test_helm_vect_asu(): grid = get_grid_lynn_5_bus_wiki() power_flow_options = PowerFlowOptions( solver_type=SolverType.HELM_VECT_ASU, # 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 ) power_flow = PowerFlow(grid, power_flow_options) power_flow.run() headers = ['Vm (p.u.)', 'Va (Deg)', 'Vre', 'Vim'] Vm = np.abs(power_flow.results.voltage) Va = np.angle(power_flow.results.voltage, deg=True) Vre = power_flow.results.voltage.real Vim = power_flow.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) headers = ['Loading (%)', 'Current(p.u.)', 'Power (MVA)'] loading = np.abs(power_flow.results.loading) * 100 current = np.abs(power_flow.results.Ibranch) power = np.abs(power_flow.results.Sbranch) data = np.c_[loading, current, power] br_df = pd.DataFrame(data=data, columns=headers, index=grid.branch_names) print('\n', br_df) print('\nError:', power_flow.results.error) print('Elapsed time (s):', power_flow.results.elapsed)
def test_helm_stable(): grid = get_grid_lynn_5_bus_wiki() power_flow_options = PowerFlowOptions( solver_type=SolverType.HELM_STABLE, # 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 ) power_flow = PowerFlow(grid, power_flow_options) power_flow.run() headers = ['voltage_per_unit (p.u.)', 'voltage_angle (Deg)', 'voltage_real', 'voltage_imaginary'] voltage_per_unit = np.abs(power_flow.results.voltage) voltage_angle = np.angle(power_flow.results.voltage, deg=True) voltage_real = power_flow.results.voltage.real voltage_imaginary = power_flow.results.voltage.imag voltage_data = np.c_[voltage_per_unit, voltage_angle, voltage_real, voltage_imaginary] v_data_frame = pd.DataFrame(data=voltage_data, columns=headers, index=grid.bus_names) print('\n', v_data_frame) headers = ['Loading (%)', 'Current(p.u.)', 'Power (MVA)'] branch_loading_per_cent = np.abs(power_flow.results.loading) * 100 branch_current = np.abs(power_flow.results.Ibranch) branch_power_complex = np.abs(power_flow.results.Sbranch) branch_data = np.c_[branch_loading_per_cent, branch_current, branch_power_complex] branch_data_frame = pd.DataFrame(data=branch_data, columns=headers, index=grid.branch_names) print('\n', branch_data_frame) print('\nError:', power_flow.results.error) print('Elapsed time (s):', power_flow.results.elapsed)
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) # grid.export_profiles('ppppppprrrrroooofiles.xlsx') # exit() #################################################################################################################### # PowerFlow #################################################################################################################### print('\n\n') power_flow = PowerFlow(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())
def instance_executor(instance: PowerFlow): """ function to run the instance :param instance: :return: """ instance.run() return instance.grid
def test_demo_5_node(root_path): np.core.arrayprint.set_printoptions(precision=4) grid = MultiCircuit() # Add buses bus_1 = Bus('Bus 1', vnom=20) # bus_1.is_slack = True grid.add_bus(bus_1) gen1 = Generator('Slack Generator', voltage_module=1.0) grid.add_generator(bus_1, gen1) bus_2 = Bus('Bus 2', vnom=20) grid.add_bus(bus_2) grid.add_load(bus_2, Load('load 2', P=40, Q=20)) bus_3 = Bus('Bus 3', vnom=20) grid.add_bus(bus_3) grid.add_load(bus_3, Load('load 3', P=25, Q=15)) bus_4 = Bus('Bus 4', vnom=20) grid.add_bus(bus_4) grid.add_load(bus_4, Load('load 4', P=40, Q=20)) bus_5 = Bus('Bus 5', vnom=20) grid.add_bus(bus_5) grid.add_load(bus_5, Load('load 5', P=50, Q=20)) # add branches (Lines in this case) grid.add_branch(Branch(bus_1, bus_2, 'line 1-2', r=0.05, x=0.11, b=0.02)) grid.add_branch(Branch(bus_1, bus_3, 'line 1-3', r=0.05, x=0.11, b=0.02)) grid.add_branch(Branch(bus_1, bus_5, 'line 1-5', r=0.03, x=0.08, b=0.02)) grid.add_branch(Branch(bus_2, bus_3, 'line 2-3', r=0.04, x=0.09, b=0.02)) grid.add_branch(Branch(bus_2, bus_5, 'line 2-5', r=0.04, x=0.09, b=0.02)) grid.add_branch(Branch(bus_3, bus_4, 'line 3-4', r=0.06, x=0.13, b=0.03)) grid.add_branch(Branch(bus_4, bus_5, '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 = PowerFlow(grid, options) power_flow.run() print_power_flow_results(power_flow=power_flow)
def test_api_helm(): np.set_printoptions(precision=4) # fname = 'Muthu4Bus.xls' # fname = 'IEEE_30BUS.xls' fname = 'IEEE_39Bus.xls' # fname = 'case9target.xls' grid = FileOpen(fname).open() grid.compile() print('\n\n', grid.name) # print('Ybus:\n', grid.circuits[0].power_flow_input.Ybus.todense()) options = PowerFlowOptions(SolverType.HELM_STABLE, verbose=False, tolerance=1e-9) power_flow = PowerFlow(grid, options) power_flow.run() print_power_flow_results(power_flow)
def test_api_multi_core(): batch_size = 10000 # fname = '/Data/Doctorado/spv_phd/GridCal_project/GridCal/IEEE_300BUS.xls' # fname = '/Data/Doctorado/spv_phd/GridCal_project/GridCal/IEEE_118.xls' # fname = '/Data/Doctorado/spv_phd/GridCal_project/GridCal/IEEE_57BUS.xls' fname = '/home/santi/Documentos/GitHub/GridCal/Grids_and_profiles/grids/IEEE_30_new.xlsx' # fname = 'D:\GitHub\GridCal\Grids_and_profiles\grids\IEEE_30_new.xlsx' # fname = '/Data/Doctorado/spv_phd/GridCal_project/GridCal/IEEE_14.xls' # fname = '/Data/Doctorado/spv_phd/GridCal_project/GridCal/IEEE_39Bus(Islands).xls' grid = FileOpen(fname).open() grid.compile() print('\n\n', grid.name) options = PowerFlowOptions(SolverType.NR, verbose=False) power_flow = PowerFlow(grid, options) power_flow.run() # create instances of the of the power flow simulation given the grid print('cloning...') pool = Pool() instances = pool.map(simulation_constructor, [[grid, options]] * batch_size) # run asynchronous power flows on the created instances print('running...') instances = pool.map_async(instance_executor, instances) # monitor progress while True: if instances.ready(): break remaining = instances._number_left progress = ((batch_size - remaining + 1) / batch_size) * 100 print("Waiting for", remaining, "tasks to complete...", progress, '%') time.sleep(0.5) # display the collected results for instance in instances.get(): print('\n\n' + instance.name)
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 = PowerFlow(self.circuit, self.options) n = len(self.circuit.buses) m = len(self.circuit.branches) 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 = MonteCarloResults(n, m, self.max_eval) # compile circuits self.numerical_circuit = self.circuit.compile() self.numerical_input_islands = self.numerical_circuit.compute() self.it = 0
def perform_step_run(self): """ Perform only one step cascading Returns: Nothing """ # recompile the grid self.grid.compile() # initialize the simulator if self.cascade_type is CascadeType.PowerFlow: model_simulator = PowerFlow(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 = PowerFlow(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()
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 time_array = pd.DatetimeIndex(start='1/1/2018', end='1/2/2018', freq='H') x = np.linspace(-np.pi, np.pi, len(time_array)) y = np.abs(np.sin(x)) df_0 = pd.DataFrame(data=y.astype(complex), index=time_array) # complex values # df_0r = pd.DataFrame(data=y, index=time_array) # only real values # df_vset = pd.DataFrame(data=np.ones(len(time_array)), index=time_array) # only real 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)) ######################################################################################################################## # Overwrite the default profiles with the custom ones ######################################################################################################################## for load in grid.get_loads(): load.P_prof = load.P * df_0 load.Q_prof = load.Q * df_0 for gen in grid.get_static_generators(): gen.P_prof = gen.Q * df_0 gen.Q_prof = gen.Q * df_0 for gen in grid.get_generators(): gen.P_prof = gen.P * df_0 ######################################################################################################################## # Run a power flow simulation ######################################################################################################################## # We need to specify power flow options power_flow_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 power_flow = PowerFlow(grid, power_flow_options) power_flow.run() # now, let's compose a nice DataFrame with the voltage results headers = ['Vm (p.u.)', 'Va (Deg)', 'Vre', 'Vim'] Vm = np.abs(power_flow.results.voltage) Va = np.angle(power_flow.results.voltage, deg=True) Vre = power_flow.results.voltage.real Vim = power_flow.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(power_flow.results.loading) * 100 current = np.abs(power_flow.results.Ibranch) power = np.abs(power_flow.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:', power_flow.results.error) print('Elapsed time (s):', power_flow.results.elapsed, '\n') from tabulate import tabulate 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=power_flow_options, use_opf_vals=False, 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) print(df_voltage) df_voltage.plot() plt.show()
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 = list() # Create buses Bus0 = Bus(name="Bus0", vnom=10, is_slack=True) bus_1 = Bus(name="bus_1", vnom=10) grid.add_bus(Bus0) grid.add_bus(bus_1) # Create load grid.add_load(bus_1, 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=bus_1, 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 = PowerFlow(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
# 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) #################################################################################################################### # PowerFlow #################################################################################################################### print('\n\n') power_flow = PowerFlow(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_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 = list() # 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() grid.compile() 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 = PowerFlow(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
def test_line_losses_3(): """ Basic line losses test, with the impedance split into 2 parallel branches. """ test_name = "test_line_losses_3" grid = MultiCircuit(name=test_name) Sbase = 100 # MVA grid.Sbase = Sbase grid.time_profile = None grid.logger = list() # Create buses Bus0 = Bus(name="Bus0", vnom=25, is_slack=True) bus_1 = Bus(name="bus_1", vnom=25) for b in Bus0, bus_1: grid.add_bus(b) # Create load grid.add_load(bus_1, 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( Branch(bus_from=Bus0, bus_to=bus_1, name="Cable0", r=0.02, x=0.1)) grid.add_branch( Branch(bus_from=Bus0, bus_to=bus_1, name="Cable1", r=0.02, x=0.1)) # Run non-linear load flow options = PowerFlowOptions(verbose=True) power_flow = PowerFlow(grid, options) power_flow.run() # Check solution approx_losses = round(1000 * sum(power_flow.results.losses), 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:") 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
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 = list() # 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 = PowerFlow(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
def _test_api(): fname = os.path.join('..', '..', '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) #################################################################################################################### # PowerFlow #################################################################################################################### print('\n\n') power_flow = PowerFlow(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()) #################################################################################################################### # Short circuit #################################################################################################################### print('\n\n') print('Short Circuit') sc_options = ShortCircuitOptions(bus_index=[16]) sc = ShortCircuit(main_circuit, sc_options, 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=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 = zeros(len(main_circuit.buses), dtype=complex) Vbase = 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, 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, options, sampling_points=100) lhs_sim.run() #################################################################################################################### # Cascading #################################################################################################################### print('Running Cascading...') cascade = Cascading(main_circuit.copy(), 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() #################################################################################################################### # F**k up the voltage #################################################################################################################### print('Run optimization to f**k up the voltage') options = PowerFlowOptions(SolverType.LM, verbose=False, initialize_with_existing_solution=False) opt = Optimize(main_circuit, options, max_iter=100) opt.run() # opt.plot() # plt.show() print('\nDone!')
class VoltageOptimizationProblem(OptimizationProblem): """ :ivar dim: Number of dimensions :ivar lb: Lower variable bounds :ivar ub: Upper variable bounds :ivar int_var: Integer variables :ivar cont_var: Continuous variables :ivar min: Global minimum value :ivar minimum: Global minimizer :ivar info: String with problem info """ 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 = PowerFlow(self.circuit, self.options) n = len(self.circuit.buses) m = len(self.circuit.branches) 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 = MonteCarloResults(n, m, self.max_eval) # compile circuits self.numerical_circuit = self.circuit.compile() self.numerical_input_islands = self.numerical_circuit.compute() self.it = 0 def eval(self, x): """ Evaluate the Ackley function at x :param x: Data point :type x: numpy.array :return: Value at x :rtype: float """ # For every circuit, run the time series for numerical_island in self.numerical_input_islands: # sample from the CDF give the vector x of values in [0, 1] # c.sample_at(x) monte_carlo_input = make_monte_carlo_input(numerical_island) mc_time_series = monte_carlo_input.get_at(x) Y, I, S = mc_time_series.get_at(t=0) # run the sampled values # res = self.power_flow.run_at(0, mc=True) res = self.power_flow.run_pf(circuit=numerical_island, Vbus=numerical_island.Vbus, Sbus=S, Ibus=I) # Y, I, S = circuit.mc_time_series.get_at(0) self.results.S_points[self.it, numerical_island.original_bus_idx] = S self.results.V_points[ self.it, numerical_island.original_bus_idx] = res.voltage[ numerical_island.original_bus_idx] self.results.I_points[ self.it, numerical_island.original_branch_idx] = res.Ibranch[ numerical_island.original_branch_idx] self.results.loading_points[ self.it, numerical_island.original_branch_idx] = res.loading[ numerical_island.original_branch_idx] self.it += 1 if self.callback is not None: prog = self.it / self.max_eval * 100 self.callback(prog) f = abs(self.results.V_points[self.it - 1, :].sum()) / self.dim # print(prog, ' % \t', f) return f
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 = list() # 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 = PowerFlow(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
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 = list() # 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.LM, verbose=True, initialize_with_existing_solution=True, multi_core=True, control_q=ReactivePowerControlMode.Direct, tolerance=1e-6, max_iter=99) power_flow = PowerFlow(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