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
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)
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
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_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
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 get_grid_lynn_5_bus_wiki(): grid = MultiCircuit(name='lynn 5 bus') bus_1 = Bus( name='bus_1', 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='Default', # Area (for grouping purposes only) zone='Default', # Zone (for grouping purposes only) substation='Default' # Substation (for grouping purposes only) ) bus_2 = Bus(name='bus_2') bus_3 = Bus(name='bus_3') bus_4 = Bus(name='bus_4') bus_5 = Bus(name='bus_5') grid.add_bus(bus_1) grid.add_bus(bus_2) grid.add_bus(bus_3) grid.add_bus(bus_4) grid.add_bus(bus_5) load_2 = Load( name='Load', # impedance=complex(0, 0), # Impedance of the ZIP model in MVA at the nominal voltage # current=complex(0, 0), # Current of the ZIP model in MVA at the nominal voltage # power=complex(40, 20), # Power of the ZIP model in MVA # impedance_prof=None, # Impedance profile # current_prof=None, # Current profile # power_prof=None, # Power profile active=True, # Is active? mttf=0.0, # Mean time to failure mttr=0.0 # Mean time to recovery ) grid.add_load(bus_2, load_2) grid.add_load( bus_3, Load( # power=complex(25, 15) )) grid.add_load( bus_4, Load( # power=complex(40, 20) )) grid.add_load( bus_5, Load( # power=complex(50, 20) )) generator_1 = 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(bus_1, generator_1) branch_1 = Branch( bus_from=bus_1, bus_to=bus_2, 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) # type_obj=BranchTemplate() # Branch template (The default one is void) ) grid.add_branch(branch_1) grid.add_branch( Branch(bus_1, bus_3, name='Line 1-3', r=0.05, x=0.11, b=0.02, rate=50)) grid.add_branch( Branch(bus_1, bus_5, name='Line 1-5', r=0.03, x=0.08, b=0.02, rate=80)) grid.add_branch( Branch(bus_2, bus_3, name='Line 2-3', r=0.04, x=0.09, b=0.02, rate=3)) grid.add_branch( Branch(bus_2, bus_5, name='Line 2-5', r=0.04, x=0.09, b=0.02, rate=10)) grid.add_branch( Branch(bus_3, bus_4, name='Line 3-4', r=0.06, x=0.13, b=0.03, rate=30)) grid.add_branch( Branch(bus_4, bus_5, name='Line 4-5', r=0.04, x=0.09, b=0.02, rate=30)) grid.compile() return grid
def test_tolerance_lf_higher(): test_name = "test_tolerance_lf_higher" grid = MultiCircuit(name=test_name) 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) 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 (r and x should be in pu) grid.add_branch( Branch(bus_from=Bus0, bus_to=bus_1, name="Cable1", r=0.01, x=0.05, tolerance=10)) # Run non-linear power flow options = PowerFlowOptions( verbose=True, branch_impedance_tolerance_mode=BranchImpedanceMode.Upper) power_flow = PowerFlow(grid, options) power_flow.run() # Check solution approx_losses = round(1000 * power_flow.results.losses[0], 3) solution = complex(0.128, 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