def parse_loads(self, cim: CIMCircuit, circuit: MultiCircuit, busbar_dict): """ :param cim: :param circuit: :param busbar_dict: :return: """ cim_loads = ['ConformLoad', 'EnergyConsumer', 'NonConformLoad'] if any_in_dict(cim.elements_by_type, cim_loads): for elm in get_elements(cim.elements_by_type, cim_loads): b1 = elm.get_bus() B1 = try_bus(b1, busbar_dict) if B1 is not None: p, q = elm.get_pq() load = gcdev.Load(idtag=elm.uuid, name=str(elm.name), G=0, B=0, Ir=0, Ii=0, P=p if p is not None else 0, Q=q if q is not None else 0) circuit.add_load(B1, load) else: self.logger.add_error('Bus not found', elm.rfid)
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 load_dpx(file_name, contraction_factor=1000) -> MultiCircuit: """ Read DPX file :param file_name: file name :param contraction_factor: contraction factor :return: MultiCircuit """ circuit = MultiCircuit() Sbase = 100 circuit.Sbase = Sbase SQRT3 = np.sqrt(3) # read the raw data into a structured dictionary print('Reading file...') structures_dict, logger = read_dpx_data(file_name=file_name) # format the read data print('Packing data...') data_structures, logger = repack(data_structures=structures_dict, logger=logger) buses_id_dict = dict() # create nodes for tpe in data_structures['Nodes']: # Airline support post # __headers__['Nodes']['APOIO'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'EXIST'] # __headers__['Nodes']['ARM'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'EXIST', 'YEAR'] # __headers__['Nodes']['CX'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'EXIST'] # __headers__['Nodes']['CXN'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'EXIST'] # __headers__['Nodes']['LOAD'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'EXIST', 'VMIN', 'VMAX', 'NCMPLAN'] # fill to fit... if tpe in ['APOIO', 'ARM', 'CX', 'CXN', 'LOAD']: df = data_structures['Nodes'][tpe] for i in range(df.shape[0]): name = 'B' + str(len(circuit.buses) + 1) + '_' + str( df['NAME'].values[i]) Vnom = float(df['VBASE'].values[i]) x = float(df['GX'].values[i]) / contraction_factor y = float(df['GY'].values[i]) / contraction_factor id_ = df['ID'].values[i] bus = Bus(name=name, vnom=Vnom, xpos=x, ypos=y, height=40, width=60) circuit.add_bus(bus) buses_id_dict[id_] = bus # Network Equivalent # __headers__['Nodes']['EQUIV'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'VMIN', 'VMAX', 'ZONE', # 'SEPNET', 'AUTOUP', 'P', 'Q', 'ELAST', 'SIMUL', 'HTYP', 'HARM5', 'HARM7', # 'HARM11', # 'HARM13', 'NOGRW', 'RS', 'XS', 'R1', 'X1', 'R2', 'X2', 'RH', 'XH', 'COM'] elif tpe == 'EQUIV': df = data_structures['Nodes'][tpe] for i in range(df.shape[0]): name = 'B' + str(len(circuit.buses) + 1) + '_' + str( df['NAME'].values[i]) Vnom = float(df['VBASE'].values[i]) x = float(df['GX'].values[i]) / contraction_factor y = float(df['GY'].values[i]) / contraction_factor id_ = df['ID'].values[i] bus = Bus(name=name, vnom=Vnom, xpos=x, ypos=y, height=40, width=60, is_slack=True) circuit.add_bus(bus) buses_id_dict[id_] = bus name = 'LD' + str(len(circuit.buses)) + '_' + str( df['NAME'].values[i]) p = float(df['P'].values[i]) * Sbase q = float(df['Q'].values[i]) * Sbase load = Load(name=name, P=p, Q=q) circuit.add_load(bus, load) # Generator # __headers__['Nodes']['GEN'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'EXIST', 'MODEL', 'VMIN', # 'VMAX', # 'V', 'ENAB', 'P', 'Q', 'QMIN', 'QMAX', 'ELAST', 'HTYP', 'HARM5', 'HARM7', # 'HARM11', # 'HARM13', 'VNOM', 'RAT', 'TGEN', 'COST', 'YEAR'] elif tpe == 'GEN': df = data_structures['Nodes'][tpe] for i in range(df.shape[0]): name = 'B' + str(len(circuit.buses) + 1) + '_' + str( df['NAME'].values[i]) Vnom = float(df['VBASE'].values[i]) x = float(df['GX'].values[i]) / contraction_factor y = float(df['GY'].values[i]) / contraction_factor id_ = df['ID'].values[i] bus = Bus(name=name, vnom=Vnom, xpos=x, ypos=y, height=40, width=60) circuit.add_bus(bus) buses_id_dict[id_] = bus mode = int(df['MODEL'].values[i]) if mode == 1: name = 'GEN' + str(len(circuit.buses)) + '_' + str( df['NAME'].values[i]) p = float(df['P'].values[i]) * Sbase q = float(df['Q'].values[i]) * Sbase v = float(df['V'].values[i]) # p.u. gen = Generator(name=name, active_power=p, voltage_module=v) circuit.add_generator(bus, gen) else: name = 'GENSTAT' + str(len(circuit.buses)) + '_' + str( df['NAME'].values[i]) p = float(df['P'].values[i]) * Sbase q = float(df['Q'].values[i]) * Sbase gen = StaticGenerator(name=name, P=p, Q=q) circuit.add_static_generator(bus, gen) # Transformation station # __headers__['Nodes']['PT'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'EXIST', 'VMIN', 'VMAX', # 'ZONE', # 'ENAB', 'P', 'Q', 'ELAST', 'SIMUL', 'HTYP', 'HARM5', 'HARM7', 'HARM11', 'HARM13', # 'NOGRW', # 'EQEXIST', 'EQPOSS1', 'MCOST1', 'ICOST1', 'EQPOSS2', 'MCOST2', 'ICOST2', # 'EQPOSS3', 'MCOST3', # 'ICOST3', 'NCLI', 'EQTYPE', 'YEAR', 'COM', 'INFOCOM', 'ID_AUX'] elif tpe in ['PT', 'PTC']: df = data_structures['Nodes'][tpe] for i in range(df.shape[0]): name = 'B' + str(len(circuit.buses) + 1) + '_' + str( df['NAME'].values[i]) Vnom = float(df['VBASE'].values[i]) x = float(df['GX'].values[i]) / contraction_factor y = float(df['GY'].values[i]) / contraction_factor id_ = df['ID'].values[i] bus = Bus(name=name, vnom=Vnom, xpos=x, ypos=y, height=40, width=60) name = 'LD' + str(len(circuit.buses) + 1) + '_' + str( df['NAME'].values[i]) p = float(df['P'].values[i]) * Sbase q = float(df['Q'].values[i]) * Sbase load = Load(name=name, P=p, Q=q) circuit.add_bus(bus) circuit.add_load(bus, load) buses_id_dict[id_] = bus # Reference node # __headers__['Nodes']['REF'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'VREF', 'RAT', # 'COST', 'TGEN', 'YEAR'] elif tpe == 'REF': df = data_structures['Nodes'][tpe] for i in range(df.shape[0]): name = 'B' + str(len(circuit.buses) + 1) + '_' + str( df['NAME'].values[i]) Vnom = float(df['VBASE'].values[i]) x = float(df['GX'].values[i]) / contraction_factor y = float(df['GY'].values[i]) / contraction_factor id_ = df['ID'].values[i] bus = Bus(name=name, vnom=Vnom, xpos=x, ypos=y, height=40, width=60, is_slack=True) circuit.add_bus(bus) buses_id_dict[id_] = bus # Voltage Transformer # __headers__['Nodes']['TT'] = ['CLASS', 'ID', 'NAME', 'VBASE', 'GX', 'GY', 'SX', 'SY', 'EXIST', 'VMIN', 'VMAX', # 'DISABLE', 'HARM5', 'HARM7', 'HARM11', 'HARM13', 'EQEXIST', 'TAP', 'YEAR', # 'ID_AUX'] elif tpe == 'TT': df = data_structures['Nodes'][tpe] for i in range(df.shape[0]): name = 'B' + str(len(circuit.buses) + 1) + '_' + str( df['NAME'].values[i]) Vnom = float(df['VBASE'].values[i]) x = float(df['GX'].values[i]) / contraction_factor y = float(df['GY'].values[i]) / contraction_factor id_ = df['ID'].values[i] bus = Bus(name=name, vnom=Vnom, xpos=x, ypos=y, height=40, width=60) circuit.add_bus(bus) buses_id_dict[id_] = bus else: logger.add_error('Not recognised under Nodes', tpe) # create branches for tpe in data_structures['Branches']: # Condenser series or shunt # __headers__['Branches']['CAP'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'EXIST', 'STAT', 'PERM', 'EQ', 'YEAR'] if tpe in ['CAP', 'IND']: df = data_structures['Branches'][tpe] for i in range(df.shape[0]): name = df['NAME'].values[i] id1 = df['ID1'].values[i] id2 = df['ID2'].values[i] b1 = buses_id_dict[id1] b2 = buses_id_dict[id2] # get equipment reference in the catalogue eq_id = df['EQ'].values[i] df_cat = data_structures['CatalogBranch'][tpe] cat_elm = df_cat[df_cat['EQ'] == eq_id] try: x = float(cat_elm['REAC'].values[0]) * Sbase except: x = 1e-20 br = Branch(bus_from=b1, bus_to=b2, name=name, x=x, branch_type=BranchType.Branch) circuit.add_branch(br) # Estimator # __headers__['Branches']['ESTIM'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'INDEP', 'I', 'SIMULT'] if tpe in ['ESTIM']: df = data_structures['Branches'][tpe] for i in range(df.shape[0]): name = df['NAME'].values[i] id1 = df['ID1'].values[i] id2 = df['ID2'].values[i] b1 = buses_id_dict[id1] b2 = buses_id_dict[id2] br = Branch(bus_from=b1, bus_to=b2, name=name, branch_type=BranchType.Branch) circuit.add_branch(br) # Breaker # __headers__['Branches']['DISJ'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'EXIST', 'STAT', 'PERM', 'FAILRT', # 'TISOL', 'TRECONF', 'TREPAIR', 'EQ', 'YEAR', 'CONTROL'] # Fuse # __headers__['Branches']['FUS'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'EXIST', 'STAT', 'PERM', 'FAILRT', # 'TISOL','TRECONF', 'TREPAIR', 'EQ', 'YEAR'] # Switch # __headers__['Branches']['INTR'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'EXIST', 'STAT', 'PERM', 'FAILRT', # 'TISOL', 'TRECONF', 'TREPAIR', 'EQ', 'YEAR', 'DRIVE', 'CONTROL'] # Disconnector # __headers__['Branches']['SECC'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'EXIST', 'STAT', 'PERM', 'FAILRT', # 'TISOL', 'TRECONF', 'TREPAIR', 'EQ', 'YEAR', 'DRIVE', 'CONTROL'] if tpe in ['DISJ', 'FUS', 'INTR', 'SECC']: df = data_structures['Branches'][tpe] for i in range(df.shape[0]): name = df['NAME'].values[i] id1 = df['ID1'].values[i] id2 = df['ID2'].values[i] state = bool(int(df['STAT'].values[i])) b1 = buses_id_dict[id1] b2 = buses_id_dict[id2] br = Branch(bus_from=b1, bus_to=b2, name=name, active=state, branch_type=BranchType.Switch) circuit.add_branch(br) # Lines, cables and bars # fill until it fits or truncate the data # __headers__['Branches']['LINE'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'EXIST', 'COLOR', 'GEOLEN', 'LEN', # 'STAT', # 'PERM', 'FAILRT', 'TISOL', 'TRECONF', 'TREPAIR', 'RERAT', 'EQEXIST', 'NPOSS', # 'CHOOSEQ', 'INSRTCOST', 'EQPOSS1', 'MATCOST1', 'EQPOSS2', 'MATCOST2', # 'EQPOSS3', # 'MATCOST3', 'NCOOG', 'GX1', 'GY1', 'GX2', 'GY2'] if tpe in ['LINE']: df = data_structures['Branches'][tpe] for i in range(df.shape[0]): name = df['NAME'].values[i] id1 = df['ID1'].values[i] id2 = df['ID2'].values[i] b1 = buses_id_dict[id1] b2 = buses_id_dict[id2] length = float(df['LEN'].values[i]) # get equipment reference in the catalogue eq_id = df['EQEXIST'].values[i] df_cat = data_structures['CatalogBranch'][tpe] cat_elm = df_cat[df_cat['EQ'] == eq_id] try: r = float(cat_elm['R'].values[0]) * length / 1000 except: r = 1e-20 try: x = float(cat_elm['X'].values[0]) * length / 1000 except: x = 1e-20 try: b = float(cat_elm['B'].values[0]) * length / 1000 except: b = 1e-20 Imax = float( cat_elm['RATTYP'].values[0]) / 1000.0 # pass from A to kA Vnom = float(cat_elm['VNOM'].values[0]) # kV Smax = Imax * Vnom * SQRT3 # MVA # correct for zero values which are problematic r = r if r > 0.0 else 1e-20 x = x if x > 0.0 else 1e-20 b = b if b > 0.0 else 1e-20 br = Branch(bus_from=b1, bus_to=b2, name=name, r=r, x=x, b=b, rate=Smax, length=length, branch_type=BranchType.Line) circuit.add_branch(br) # Intensity Transformer # __headers__['Branches']['TI'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'INDEP', 'I', 'SIMULT', 'EXIST', 'STAT', # 'PERM', 'FAILRT', 'TISOL', 'TRECONF', 'TREPAIR', 'EQ', 'TAP1', 'TAP2', 'YEAR'] if tpe in ['TI']: df = data_structures['Branches'][tpe] for i in range(df.shape[0]): name = df['NAME'].values[i] id1 = df['ID1'].values[i] id2 = df['ID2'].values[i] b1 = buses_id_dict[id1] b2 = buses_id_dict[id2] # get equipment reference in the catalogue eq_id = df['EQ'].values[i] df_cat = data_structures['CatalogBranch'][tpe] cat_elm = df_cat[df_cat['EQ'] == eq_id] br = Branch(bus_from=b1, bus_to=b2, name=name, branch_type=BranchType.Transformer) circuit.add_branch(br) # Self-transformer # __headers__['Branches']['XFORM1'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'ID3', 'ID1N', 'ID2N', 'ID3N', # 'EXIST', # 'STAT', 'FAILRT', 'TISOL', 'TRECONF', 'TREPAIR', 'RERAT', 'CON1', 'RE1', # 'XE1', # 'CON2', 'RE2', 'XE2', 'CON3', 'RE3', 'XE3', 'LOSS', 'TPERM', 'SETVSEL', # 'SETV', # 'EQ', 'TAP1', 'TAP2', 'TAP3', 'YEAR', 'NUM'] if tpe in ['XFORM1', 'XFORM2']: df = data_structures['Branches'][tpe] for i in range(df.shape[0]): name = df['NAME'].values[i] id1 = df['ID1'].values[i] id2 = df['ID2'].values[i] b1 = buses_id_dict[id1] b2 = buses_id_dict[id2] # get equipment reference in the catalogue # eq_id = df['EQ'].values[i] eq_id = df['XE3'].values[ i] # to correct the bad data formatting these file has... df_cat = data_structures['CatalogBranch'][tpe] cat_elm = df_cat[df_cat['EQ'] == eq_id] if cat_elm.shape[0] > 0: r1 = float(cat_elm['RD1'].values[0]) r2 = float(cat_elm['RD2'].values[0]) x1 = float(cat_elm['XD1'].values[0]) x2 = float(cat_elm['XD2'].values[0]) s1 = float(cat_elm['SNOMTYP1'].values[0] ) / 1000.0 # from kVA to MVA s2 = float(cat_elm['SNOMTYP2'].values[0] ) / 1000.0 # from kVA to MVA r = r1 + r2 x = x1 + x2 s = s1 + s2 r = r if r > 0.0 else 1e-20 x = x if x > 0.0 else 1e-20 s = s if s > 0.0 else 1e-20 else: r = 1e-20 x = 1e-20 s = 1e-20 logger.add_error('Not found.', tpe + ':' + eq_id) br = Branch(bus_from=b1, bus_to=b2, name=name, r=r, x=x, rate=s, branch_type=BranchType.Transformer) circuit.add_branch(br) # 3-winding transformer # __headers__['Branches']['XFORM3'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'ID3', 'ID1N', 'ID2N', 'ID3N', # 'EXIST', # 'STAT', 'FAILRT', 'TISOL', 'TRECONF', 'TREPAIR', 'RERAT', 'CON1', 'RE1', # 'XE1', # 'CON2', 'RE2', 'XE2', 'CON3', 'RE3', 'XE3', 'LOSS', 'TPERM', 'SETVSEL', # 'SETV', # 'EQ', 'TAP1', 'TAP2', 'TAP3', 'YEAR', 'NUM'] if tpe in ['XFORM3']: df = data_structures['Branches'][tpe] for i in range(df.shape[0]): name = df['NAME'].values[i] id1 = df['ID1'].values[i] id2 = df['ID2'].values[i] id3 = df['ID3'].values[i] b1 = buses_id_dict[id1] b2 = buses_id_dict[id2] b3 = buses_id_dict[id3] # get equipment reference in the catalogue eq_id = df['EQ'].values[i] df_cat = data_structures['CatalogBranch'][tpe] cat_elm = df_cat[df_cat['EQ'] == eq_id] r1 = float(cat_elm['RD1'].values[0]) r2 = float(cat_elm['RD2'].values[0]) r3 = float(cat_elm['RD3'].values[0]) x1 = float(cat_elm['XD1'].values[0]) x2 = float(cat_elm['XD2'].values[0]) x3 = float(cat_elm['XD3'].values[0]) s1 = float( cat_elm['SNOMTYP1'].values[0]) / 1000.0 # from kVA to MVA s2 = float( cat_elm['SNOMTYP2'].values[0]) / 1000.0 # from kVA to MVA s3 = float( cat_elm['SNOMTYP3'].values[0]) / 1000.0 # from kVA to MVA r12 = r1 + r2 x12 = x1 + x2 s12 = s1 + s2 r13 = r1 + r3 x13 = x1 + x3 s13 = s1 + s3 r23 = r2 + r3 x23 = x2 + x3 s23 = s2 + s3 r12 = r12 if r12 > 0.0 else 1e-20 x12 = x12 if x12 > 0.0 else 1e-20 s12 = s12 if s12 > 0.0 else 1e-20 r13 = r13 if r13 > 0.0 else 1e-20 x13 = x13 if x13 > 0.0 else 1e-20 s13 = s13 if s13 > 0.0 else 1e-20 r23 = r23 if r23 > 0.0 else 1e-20 x23 = x23 if x23 > 0.0 else 1e-20 s23 = s23 if s23 > 0.0 else 1e-20 br = Branch(bus_from=b1, bus_to=b2, name=name, r=r12, x=x12, rate=s12, branch_type=BranchType.Transformer) circuit.add_branch(br) br = Branch(bus_from=b1, bus_to=b3, name=name, r=r13, x=x13, rate=s13, branch_type=BranchType.Transformer) circuit.add_branch(br) br = Branch(bus_from=b2, bus_to=b3, name=name, r=r23, x=x23, rate=s23, branch_type=BranchType.Transformer) circuit.add_branch(br) # Neutral impedance # __headers__['Branches']['ZN'] = ['CLASS', 'ID', 'NAME', 'ID1', 'ID2', 'EXIST', 'STAT', 'PERM', 'FAILRT', # 'TISOL','TRECONF', 'TREPAIR', 'EQ', 'YEAR'] if tpe in ['ZN']: df = data_structures['Branches'][tpe] for i in range(df.shape[0]): name = df['NAME'].values[i] id1 = df['ID1'].values[i] id2 = df['ID2'].values[i] b1 = buses_id_dict[id1] b2 = buses_id_dict[id2] br = Branch(bus_from=b1, bus_to=b2, name=name, branch_type=BranchType.Branch) circuit.add_branch(br) # return the circuit and the logs return circuit, logger
def load_iPA(file_name): circuit = MultiCircuit() with open(file_name) as json_file: data = json.load(json_file) # elements dictionaries xfrm_dict = {entry['IdEnRed']: entry for entry in data['Transformadores']} # nodes_dict = {entry['id']: entry for entry in data['Nudos']} nodes_dict = dict() buses_dict = dict() for entry in data['Nudos']: nodes_dict[entry['id']] = entry bus = Bus(name=str(entry['id'])) buses_dict[entry['id']] = bus if entry['id'] > 0: # omit the node 0 because it is the "earth node"... circuit.add_bus(bus) gen_dict = {entry['IdEnRed']: entry for entry in data['Generadores']} load_dict = {entry['IdEnRed']: entry for entry in data['Consumos']} sw_dict = {entry['IdEnRed']: entry for entry in data['Interruptores']} # main grid vector_red = data['Red'] ''' {'id': 0, 'Tipo': 1, 'E': 0, 'EFase': 0, 'Tomas': 0, 'R1': 1e-05, 'X1': 1e-05, 'R0': 1e-05, 'X0': 1e-05, 'RN': 1e-05, 'XN': 1e-05, 'P': 0, 'Q': 0, 'Nudo1': 2410, 'Nudo2': 2403, 'Carga_Max': -1, 'ClassID': 1090, 'ClassMEMBER': 98076366, 'Conf': 'abc', 'LineaMT': '2030:98075347', 'Unom': 15.0} ''' for entry in vector_red: # pick the general attributes identifier = entry['id'] tpe = entry['Tipo'] n1_id = entry['Nudo1'] n2_id = entry['Nudo2'] # get the Bus objects associated to the bus indices if n1_id in buses_dict.keys(): bus1 = buses_dict[n1_id] if n2_id in buses_dict.keys(): bus2 = buses_dict[n2_id] if tpe == 0: # Fuente de Tensión(elemento Ptheta) # pick the bus that is not the earth bus... if n1_id == 0: bus = bus2 else: bus = bus1 bus.is_slack = True elm = Generator(name='Slack') circuit.add_generator(bus, elm) elif tpe == 1: # Elemento impedancia(lineas) V = entry['Unom'] Zbase = V * V / circuit.Sbase if identifier in load_dict.keys(): # load!!! print('Load found in lines: WTF?') else: # line!!! r = entry['R1'] / Zbase x = entry['X1'] / Zbase if r > 1e-5: branch_type = BranchType.Line else: # mark as "generic branch" the branches with very low resistance branch_type = BranchType.Branch elm = Branch(bus_from=bus1, bus_to=bus2, name=str(identifier), r=r, x=x, branch_type=branch_type) circuit.add_branch(elm) elif tpe == 2: # Elemento PQ # pick the bus that is not the earth bus... if n1_id == 0: bus = bus2 else: bus = bus1 p = entry['P'] # power in MW q = entry['Q'] elm = Load(name=str(identifier), P=p * 1e-3, Q=q * 1e-3) circuit.add_load(bus, elm) elif tpe == 3: # Elemento PV pass elif tpe == 4: # Reg de tensión V = entry['Unom'] Zbase = V * V / circuit.Sbase r = entry['R1'] / Zbase x = entry['X1'] / Zbase elm = Branch(bus_from=bus1, bus_to=bus2, name=str(identifier), r=r, x=x, branch_type=BranchType.Transformer) circuit.add_branch(elm) elif tpe == 5: # Transformador V = entry['Unom'] Zbase = V * V / circuit.Sbase r = entry['R1'] / Zbase x = entry['X1'] / Zbase elm = Branch(bus_from=bus1, bus_to=bus2, name=str(identifier), r=r, x=x, branch_type=BranchType.Transformer) circuit.add_branch(elm) # return the circuit return circuit
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 data_to_grid_object(data, pos_dict, codification="utf-8") -> MultiCircuit: """ Turns the read data dictionary into a GridCal MultiCircuit object Args: data: Dictionary of data read from a DGS file pos_dict: Dictionary of objects and their positions read from a DGS file Returns: GridCal MultiCircuit object """ ############################################################################### # Refactor data into classes ############################################################################### # store tables for easy reference ''' ############################################################################### * Line * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypLne,TypTow,TypGeo,TypCabsys * chr_name: Characteristic Name * dline: Parameters: Length of Line in km * fline: Parameters: Derating Factor * outserv: Out of Service * pStoch: Failures: Element model in StoTyplne ''' if "ElmLne" in data.keys(): lines = data["ElmLne"] else: lines = np.zeros((0, 20)) ''' ############################################################################### * Line Type * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * chr_name: Characteristic Name * Ithr: Rated Short-Time (1s) Current (Conductor) in kA * aohl_: Cable / OHL * cline: Parameters per Length 1,2-Sequence: Capacitance C' in uF/km * cline0: Parameters per Length Zero Sequence: Capacitance C0' in uF/km * nlnph: Phases:1:2:3 * nneutral: Number of Neutrals:0:1 * rline: Parameters per Length 1,2-Sequence: AC-Resistance R'(20°C) in Ohm/km * rline0: Parameters per Length Zero Sequence: AC-Resistance R0' in Ohm/km * rtemp: Max. End Temperature in degC * sline: Rated Current in kA * uline: Rated Voltage in kV * xline: Parameters per Length 1,2-Sequence: Reactance X' in Ohm/km * xline0: Parameters per Length Zero Sequence: Reactance X0' in Ohm/km ''' if "TypLne" in data.keys(): lines_types = data["TypLne"] else: lines_types = np.zeros((0, 20)) ''' ############################################################################### * 2-Winding Transformer * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypTr2 * chr_name: Characteristic Name * sernum: Serial Number * constr: Year of Construction * cgnd_h: Internal Grounding Impedance, HV Side: Star Point:Connected:Not connected * cgnd_l: Internal Grounding Impedance, LV Side: Star Point:Connected:Not connected * i_auto: Auto Transformer * nntap: Tap Changer 1: Tap Position * ntrcn: Controller, Tap Changer 1: Automatic Tap Changing * outserv: Out of Service * ratfac: Rating Factor ''' if "ElmTr2" in data.keys(): transformers = data["ElmTr2"] else: transformers = np.zeros((0, 20)) ''' ############################################################################### * 2-Winding Transformer Type * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * chr_name: Characteristic Name * curmg: Magnetising Impedance: No Load Current in % * dutap: Tap Changer 1: Additional Voltage per Tap in % * frnom: Nominal Frequency in Hz * manuf: Manufacturer * nntap0: Tap Changer 1: Neutral Position * nt2ag: Vector Group: Phase Shift in *30deg * ntpmn: Tap Changer 1: Minimum Position * ntpmx: Tap Changer 1: Maximum Position * pcutr: Positive Sequence Impedance: Copper Losses in kW * pfe: Magnetising Impedance: No Load Losses in kW * phitr: Tap Changer 1: Phase of du in deg * strn: Rated Power in MVA * tap_side: Tap Changer 1: at Side:HV:LV * tr2cn_h: Vector Group: HV-Side:Y :YN:Z :ZN:D * tr2cn_l: Vector Group: LV-Side:Y :YN:Z :ZN:D * uk0tr: Zero Sequence Impedance: Short-Circuit Voltage uk0 in % * uktr: Positive Sequence Impedance: Short-Circuit Voltage uk in % * ur0tr: Zero Sequence Impedance: SHC-Voltage (Re(uk0)) uk0r in % * utrn_h: Rated Voltage: HV-Side in kV * utrn_l: Rated Voltage: LV-Side in kV * zx0hl_n: Zero Sequence Magnetising Impedance: Mag. Impedance/uk0 ''' if "TypTr2" in data.keys(): transformers_types = data["TypTr2"] else: transformers_types = np.zeros((0, 20)) ''' ############################################################################### * Terminal * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypBar * chr_name: Characteristic Name * iUsage: Usage:Busbar:Junction Node:Internal Node * outserv: Out of Service * phtech: Phase Technology:ABC:ABC-N:BI:BI-N:2PH:2PH-N:1PH:1PH-N:N * uknom: Nominal Voltage: Line-Line in kV ''' if "ElmTerm" in data.keys(): buses = data["ElmTerm"] else: buses = np.zeros((0, 20)) ''' ############################################################################### * Cubicle * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * chr_name: Characteristic Name * obj_bus: Bus Index * obj_id: Connected with in Elm* ''' if "StaCubic" in data.keys(): cubicles = data["StaCubic"] else: cubicles = np.zeros((0, 20)) ''' ############################################################################### * General Load * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypLod,TypLodind * chr_name: Characteristic Name * outserv: Out of Service * plini: Operating Point: Active Power in MW * qlini: Operating Point: Reactive Power in Mvar * scale0: Operating Point: Scaling Factor ''' if "ElmLod" in data.keys(): loads = data["ElmLod"] else: loads = np.zeros((0, 20)) ''' ############################################################################### * External Grid * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * chr_name: Characteristic Name * bustp: Bus Type:PQ:PV:SL * cgnd: Internal Grounding Impedance: Star Point:Connected:Not connected * iintgnd: Neutral Conductor: N-Connection:None:At terminal (ABC-N):Separate terminal * ikssmin: Min. Values: Short-Circuit Current Ik''min in kA * r0tx0: Max. Values Impedance Ratio: R0/X0 max. * r0tx0min: Min. Values Impedance Ratio: R0/X0 min. * rntxn: Max. Values: R/X Ratio (max.) * rntxnmin: Min. Values: R/X Ratio (min.) * snss: Max. Values: Short-Circuit Power Sk''max in MVA * snssmin: Min. Values: Short-Circuit Power Sk''min in MVA ''' if "ElmXnet" in data.keys(): external = data["ElmXnet"] else: external = np.zeros((0, 20)) ''' ############################################################################### * Grid * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * frnom: Nominal Frequency in Hz ''' if "ElmNet" in data.keys(): grid = data["ElmNet"] else: grid = np.zeros((0, 20)) ''' ############################################################################### ''' if "ElmGenstat" in data.keys(): static_generators = data["ElmGenstat"] else: static_generators = np.zeros((0, 20)) ''' ############################################################################### * Synchronous Machine * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypSym * chr_name: Characteristic Name * i_mot: Generator/Motor * iv_mode: Local Controller * ngnum: Number of: parallel Machines * outserv: Out of Service * pgini: Dispatch: Active Power in MW * q_max: Reactive Power Operational Limits: Max. in p.u. * q_min: Reactive Power Operational Limits: Min. in p.u. * qgini: Dispatch: Reactive Power in Mvar * usetp: Dispatch: Voltage in p.u. ''' if "ElmSym" in data.keys(): synchronous_machine = data["ElmSym"] else: synchronous_machine = np.zeros((0, 20)) ''' ############################################################################### * Synchronous Machine Type * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * cosn: Power Factor * rstr: Stator Resistance: rstr in p.u. * satur: For single fed short-circuit: Machine Type IEC909/IEC60909 * sgn: Nominal Apparent Power in MVA * ugn: Nominal Voltage in kV * xd: Synchronous Reactances: xd in p.u. * xdsat: For single fed short-circuit: Reciprocal of short-circuit ratio (xdsat) in p.u. * xdsss: Subtransient Reactance: saturated value xd''sat in p.u. * xq: Synchronous Reactances: xq in p.u. ''' if "TypSym" in data.keys(): synchronous_machine_type = data["TypSym"] else: synchronous_machine_type = np.zeros((0, 20)) ''' ############################################################################### * Asynchronous Machine * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypAsm*,TypAsmo*,TypAsm1* * chr_name: Characteristic Name * i_mot: Generator/Motor * ngnum: Number of: parallel Machines * outserv: Out of Service * pgini: Dispatch: Active Power in MW ''' if "ElmAsm" in data.keys(): asynchronous_machine = data["ElmAsm"] else: asynchronous_machine = np.zeros((0, 20)) ''' ############################################################################### * Synchronous Machine Type * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * i_mode: Input Mode * aiazn: Consider Transient Parameter: Locked Rotor Current (Ilr/In) in p.u. * amazn: Locked Rotor Torque in p.u. * amkzn: Torque at Stalling Point in p.u. * anend: Nominal Speed in rpm * cosn: Rated Power Factor * effic: Efficiency at nominal Operation in % * frequ: Nominal Frequency in Hz * i_cage: Rotor * nppol: No of Pole Pairs * pgn: Power Rating: Rated Mechanical Power in kW * ugn: Rated Voltage in kV * xmrtr: Rotor Leakage Reac. Xrm in p.u. * xstr: Stator Reactance Xs in p.u. ''' if "TypAsmo" in data.keys(): asynchronous_machine_type = data["TypAsmo"] else: asynchronous_machine_type = np.zeros((0, 20)) ''' ############################################################################### * Shunt/Filter * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * chr_name: Characteristic Name * ctech: Technology * fres: Design Parameter (per Step): Resonance Frequency in Hz * greaf0: Design Parameter (per Step): Quality Factor (at fr) * iswitch: Controller: Switchable * ncapa: Controller: Act.No. of Step * ncapx: Controller: Max. No. of Steps * outserv: Out of Service * qtotn: Design Parameter (per Step): Rated Reactive Power, L-C in Mvar * shtype: Shunt Type * ushnm: Nominal Voltage in kV ''' if "ElmShnt" in data.keys(): shunts = data["ElmShnt"] else: shunts = np.zeros((0, 20)) ''' ############################################################################### * Breaker/Switch * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypSwitch * chr_name: Characteristic Name * aUsage: Switch Type * nneutral: No. of Neutrals:0:1 * nphase: No. of Phases:1:2:3 * on_off: Closed ''' if "ElmCoup" in data.keys(): switches = data["ElmCoup"] else: switches = np.zeros((0, 20)) ############################################################################### # Post process the data ############################################################################### # put the tables that connect to a terminal in a list classes = [lines, transformers, loads, external, static_generators, shunts, synchronous_machine, asynchronous_machine] # construct the terminals dictionary ''' $$StaCubic;ID(a:40);loc_name(a:40);fold_id(p);chr_name(a:20);obj_bus(i);obj_id(p) ******************************************************************************** * Cubicle * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * chr_name: Characteristic Name * obj_bus: Bus Index * obj_id: Connected with in Elm* ******************************************************************************** ''' terminals_dict = dict() # dictionary to store the terminals ID associated with an object ID cub_obj_idx = cubicles['obj_id'].values cub_term_idx = cubicles['fold_id'].values # for i, elm_id in enumerate(cub_obj_idx): # bus_idx = cub_term_idx[i] # terminals_dict[elm_id] = bus_idx ID_idx = 0 for cla in classes: if cla.__len__() > 0: for ID in cla['ID'].values: idx = np.where(cubicles == ID)[0] terminals_dict[ID] = cub_term_idx[idx] ############################################################################### # Generate GridCal data ############################################################################### # general values baseMVA = 100 frequency = grid['frnom'][0] w = 2.0 * math.pi * frequency circuit = MultiCircuit() #################################################################################################################### # Terminals (nodes) #################################################################################################################### ''' ******************************************************************************** * Terminal * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypBar * iUsage: Usage:Busbar:Junction Node:Internal Node * uknom: Nominal Voltage: Line-Line in kV * chr_name: Characteristic Name * outserv: Out of Service ******************************************************************************** ''' # print('Parsing terminals') buses_dict = dict() for i in range(len(buses)): ID = buses['ID'][i] x, y = pos_dict[ID] buses_dict[ID] = i bus_name = buses['loc_name'][i].decode(codification) # BUS_Name vnom = buses['uknom'][i] bus = Bus(name=bus_name, vnom=vnom, vmin=0.9, vmax=1.1, xpos=x, ypos=-y, active=True) circuit.add_bus(bus) #################################################################################################################### # External grids (slacks) #################################################################################################################### ''' ############################################################################### ******************************************************************************** * External Grid * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * outserv: Out of Service * snss: Max. Values: Short-Circuit Power Sk''max in MVA * rntxn: Max. Values: R/X Ratio (max.) * z2tz1: Max. Values Impedance Ratio: Z2/Z1 max. * snssmin: Min. Values: Short-Circuit Power Sk''min in MVA * rntxnmin: Min. Values: R/X Ratio (min.) * z2tz1min: Min. Values Impedance Ratio: Z2/Z1 min. * chr_name: Characteristic Name * bustp: Bus Type:PQ:PV:SL * pgini: Operation Point: Active Power in MW * qgini: Operation Point: Reactive Power in Mvar * phiini: Operation Point: Angle in deg * usetp: Operation Point: Voltage Setpoint in p.u. ******************************************************************************** ''' for i in range(len(external)): ID = external['ID'][i] if 'phiini' in external.columns.values: va = external['phiini'][i] vm = external['usetp'][i] else: va = 0 vm = 1 buses = terminals_dict[ID] # array with the ID of the connection Buses bus1 = buses_dict[buses[0]] # index of the bus bus_obj = circuit.buses[bus1] # apply the slack values to the buses structure if the element is marked as slack if external['bustp'].values[i] == b'SL': # create the slack entry on buses bus_obj.is_slack = True # BUSES[bus1, bd.BUS_TYPE] = 3 # BUSES[bus1, bd.VA] = va # BUSES[bus1, bd.VM] = vm # # # create the slack entry on generators (add the slack generator) # gen_ = gen_line.copy() # gen_[gd.GEN_BUS] = bus1 # gen_[gd.MBASE] = baseMVA # gen_[gd.VG] = vm # gen_[gd.GEN_STATUS] = 1 # gen_[gd.PG] += external['pgini'].values[i] # # GEN.append(gen_) # GEN_NAMES.append(external['loc_name'][i]) elif external['bustp'].values[i] == b'PV': if 'pgini' in external.columns.values: p = external['pgini'].values[i] else: p = 0 # add a generator to the bus gen = Generator(name=external['loc_name'][i].decode(codification), active_power=p, voltage_module=vm, Qmin=-9999, Qmax=9999, Snom=9999, power_prof=None, vset_prof=None) circuit.add_generator(bus_obj, gen) # # mark the bus as pv # BUSES[bus1, bd.BUS_TYPE] = 2 # BUSES[bus1, bd.VA] = 0.0 # BUSES[bus1, bd.VM] = vm # # add the PV entry on generators # gen_ = gen_line.copy() # gen_[gd.GEN_BUS] = bus1 # gen_[gd.MBASE] = baseMVA # gen_[gd.VG] = vm # gen_[gd.GEN_STATUS] = 1 # gen_[gd.PG] += external['pgini'].values[i] # # GEN.append(gen_) # GEN_NAMES.append(external['loc_name'][i]) elif external['bustp'].values[i] == b'PQ': # Add a load to the bus load = Load(name=external['loc_name'][i].decode(codification), P=external['pgini'].values[i], Q=external['qgini'].values[i]) circuit.add_load(bus_obj, load) # BUSES[bus1, bd.BUS_TYPE] = 1 # BUSES[bus1, bd.VA] = va # BUSES[bus1, bd.VM] = vm # BUSES[bus1, bd.PD] += external['pgini'].values[i] # BUSES[bus1, bd.QD] += external['qgini'].values[i] #################################################################################################################### # Lines (branches) #################################################################################################################### # print('Parsing lines') if lines_types.__len__() > 0: lines_ID = lines['ID'].values lines_type_id = lines['typ_id'].values line_types_ID = lines_types['ID'].values lines_lenght = lines['dline'].values if 'outserv' in lines.keys(): lines_enables = lines['outserv'] else: lines_enables = np.ones(len(lines_ID)) lines_R = lines_types['rline'].values lines_L = lines_types['xline'].values lines_C = lines_types['cline'].values lines_rate = lines_types['sline'].values lines_voltage = lines_types['uline'].values for i in range(len(lines)): # line_ = branch_line.copy() ID = lines_ID[i] ID_Type = lines_type_id[i] type_idx = np.where(line_types_ID == ID_Type)[0][0] buses = terminals_dict[ID] # array with the ID of the connection Buses bus1 = buses_dict[buses[0]] bus2 = buses_dict[buses[1]] bus_from = circuit.buses[bus1] bus_to = circuit.buses[bus2] status = lines_enables[i] # impedances lenght = np.double(lines_lenght[i]) R = np.double(lines_R[type_idx]) * lenght # Ohm L = np.double(lines_L[type_idx]) * lenght # Ohm C = np.double(lines_C[type_idx]) * lenght * w * 1e-6 # S (siemens) # pass impedance to per unit vbase = np.double(lines_voltage[type_idx]) # kV zbase = vbase**2 / baseMVA # Ohm ybase = 1.0 / zbase # S r = R / zbase # pu l = L / zbase # pu b = C / ybase # pu # rated power Irated = np.double(lines_rate[type_idx]) # kA Smax = Irated * vbase # MVA line = Branch(bus_from=bus_from, bus_to=bus_to, name=lines['loc_name'][i].decode(codification), r=r, x=l, g=1e-20, b=b, rate=Smax, tap=1, shift_angle=0, active=status, mttf=0, mttr=0) circuit.add_branch(line) # # put all in the correct column # line_[brd.F_BUS] = bus1 # line_[brd.T_BUS] = bus2 # line_[brd.BR_R] = r # line_[brd.BR_X] = l # line_[brd.BR_B] = c # line_[brd.RATE_A] = Smax # line_[brd.BR_STATUS] = status # BRANCHES.append(line_) # # name_ = lines['loc_name'][i] # line_Name # BRANCH_NAMES.append(name_) # # # add edge to graph # g.add_edge(bus1, bus2) else: warn('Line types are empty') #################################################################################################################### # Transformers (Branches) #################################################################################################################### # print('Parsing transformers') ''' ******************************************************************************** * 2-Winding Transformer * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypTr2 * outserv: Out of Service * nntap: Tap Changer 1: Tap Position * sernum: Serial Number * constr: Year of Construction * chr_name: Characteristic Name ******************************************************************************** ''' if len(transformers_types) > 0: ''' ******************************************************************************** * 2-Winding Transformer Type * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * strn: Rated Power in MVA * frnom: Nominal Frequency in Hz * utrn_h: Rated Voltage: HV-Side in kV * utrn_l: Rated Voltage: LV-Side in kV * uktr: Positive Sequence Impedance: Short-Circuit Voltage uk in % * pcutr: Positive Sequence Impedance: Copper Losses in kW * uk0tr: Zero Sequence Impedance: Short-Circuit Voltage uk0 in % * ur0tr: Zero Sequence Impedance: SHC-Voltage (Re(uk0)) uk0r in % * tr2cn_h: Vector Group: HV-Side:Y :YN:Z :ZN:D * tr2cn_l: Vector Group: LV-Side:Y :YN:Z :ZN:D * nt2ag: Vector Group: Phase Shift in *30deg * curmg: Magnetizing Impedance: No Load Current in % * pfe: Magnetizing Impedance: No Load Losses in kW * zx0hl_n: Zero Sequence Magnetizing Impedance: Mag. Impedance/uk0 * tap_side: Tap Changer 1: at Side:HV:LV * dutap: Tap Changer 1: Additional Voltage per Tap in % * phitr: Tap Changer 1: Phase of du in deg * nntap0: Tap Changer 1: Neutral Position * ntpmn: Tap Changer 1: Minimum Position * ntpmx: Tap Changer 1: Maximum Position * manuf: Manufacturer * chr_name: Characteristic Name ******************************************************************************** ''' type_ID = transformers_types['ID'].values HV_nominal_voltage = transformers_types['utrn_h'].values LV_nominal_voltage = transformers_types['utrn_l'].values Nominal_power = transformers_types['strn'].values Copper_losses = transformers_types['pcutr'].values Iron_losses = transformers_types['pfe'].values No_load_current = transformers_types['curmg'].values Short_circuit_voltage = transformers_types['uktr'].values # GR_hv1 = transformers_types['ID'] # GX_hv1 = transformers_types['ID'] for i in range(len(transformers)): # line_ = branch_line.copy() ID = transformers['ID'][i] ID_Type = transformers['typ_id'][i] if ID_Type in type_ID: type_idx = np.where(type_ID == ID_Type)[0][0] buses = terminals_dict[ID] # array with the ID of the connection Buses bus1 = buses_dict[buses[0]] bus2 = buses_dict[buses[1]] bus_from = circuit.buses[bus1] bus_to = circuit.buses[bus2] Smax = Nominal_power[type_idx] # Uhv, Ulv, Sn, Pcu, Pfe, I0, Usc tpe = TransformerType(hv_nominal_voltage=HV_nominal_voltage[type_idx], lv_nominal_voltage=LV_nominal_voltage[type_idx], nominal_power=Smax, copper_losses=Copper_losses[type_idx], iron_losses=Iron_losses[type_idx], no_load_current=No_load_current[type_idx], short_circuit_voltage=Short_circuit_voltage[type_idx], gr_hv1=0.5, gx_hv1=0.5) Zs, Zsh = tpe.get_impedances() if Zsh != 0: Ysh = 1.0 / Zsh else: Ysh = 0j status = 1 - transformers['outserv'][i] trafo = Branch(bus_from=bus_from, bus_to=bus_to, name=transformers['loc_name'][i].decode(codification), r=Zs.real, x=Zs.imag, g=Ysh.real, b=Ysh.imag, rate=Smax, tap=1.0, shift_angle=0.0, active=status, mttf=0, mttr=0, branch_type=BranchType.Transformer) circuit.add_branch(trafo) else: warn('Transformer type not found!') else: warn('Transformer types are empty') #################################################################################################################### # Loads (nodes) #################################################################################################################### ''' ******************************************************************************** * General Load * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypLod,TypLodind * chr_name: Characteristic Name * plini: Operating Point: Active Power in MW * qlini: Operating Point: Reactive Power in Mvar * scale0: Operating Point: Scaling Factor ******************************************************************************** ''' # print('Parsing Loads') if len(loads) > 0: loads_ID = loads['ID'] loads_P = loads['plini'] loads_Q = loads['qlini'] scale = loads['scale0'] for i in range(len(loads)): ID = loads_ID[i] bus_idx = buses_dict[(terminals_dict[ID][0])] bus_obj = circuit.buses[bus_idx] p = loads_P[i] * scale[i] # in MW q = loads_Q[i] * scale[i] # in MVA load = Load(name=loads['loc_name'][i].decode(codification), P=p, Q=q) circuit.add_load(bus_obj, load) # BUSES[bus_idx, 2] += p # BUSES[bus_idx, 3] += q else: warn('There are no loads') #################################################################################################################### # Shunts #################################################################################################################### ''' ******************************************************************************** * Shunt/Filter * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * chr_name: Characteristic Name * shtype: Shunt Type * ushnm: Nominal Voltage in kV * qcapn: Design Parameter (per Step): Rated Reactive Power, C in Mvar * ncapx: Controller: Max. No. of Steps * ncapa: Controller: Act.No. of Step * outserv: Out of Service ******************************************************************************** ''' for i in range(len(shunts)): ID = shunts['ID'][i] buses = terminals_dict[ID] # array with the ID of the connection Buses bus1 = buses_dict[buses[0]] bus_obj = circuit.buses[bus1] name = shunts['loc_name'][i].decode(codification) if 'qcapn' in shunts.columns.values: b = shunts['ushnm'][i] / shunts['qcapn'][i] elif 'qtotn' in shunts.columns.values: b = shunts['ushnm'][i] / shunts['qtotn'][i] else: b = 1e-20 shunt = Shunt(name=name, B=b) circuit.add_shunt(bus_obj, shunt) #################################################################################################################### # Static generators (Gen) #################################################################################################################### ''' ******************************************************************************** * Static Generator * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * bus1: Terminal in StaCubic * outserv: Out of Service * sgn: Ratings: Nominal Apparent Power in MVA * cosn: Ratings: Power Factor * ngnum: Number of: parallel Machines * pgini: Dispatch: Active Power in MW * qgini: Dispatch: Reactive Power in Mvar * av_mode: Local Controller * ip_ctrl: Reference Machine ******************************************************************************** ''' for i in range(len(static_generators)): ID = static_generators['ID'][i] buses = terminals_dict[ID] # array with the ID of the connection Buses bus1 = buses_dict[buses[0]] bus_obj = circuit.buses[bus1] mode = static_generators['av_mode'][i] num_machines = static_generators['ngnum'][i] gen = StaticGenerator(name=static_generators['loc_name'][i].decode(codification), P=static_generators['pgini'][i] * num_machines, Q=static_generators['qgini'][i] * num_machines) circuit.add_static_generator(bus_obj, gen) #################################################################################################################### # Synchronous Machine (Gen) #################################################################################################################### ''' ******************************************************************************** * Synchronous Machine * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * typ_id: Type in TypSym * ngnum: Number of: parallel Machines * i_mot: Generator/Motor * chr_name: Characteristic Name * outserv: Out of Service * pgini: Dispatch: Active Power in MW * qgini: Dispatch: Reactive Power in Mvar * usetp: Dispatch: Voltage in p.u. * iv_mode: Mode of Local Voltage Controller * q_min: Reactive Power Operational Limits: Min. in p.u. * q_max: Reactive Power Operational Limits: Max. in p.u. ******************************************************************************** ''' for i in range(len(synchronous_machine)): ID = synchronous_machine['ID'][i] buses = terminals_dict[ID] # array with the ID of the connection Buses bus1 = buses_dict[buses[0]] bus_obj = circuit.buses[bus1] num_machines = synchronous_machine['ngnum'][i] # Get the type element ''' ******************************************************************************** * Synchronous Machine Type * * ID: Unique identifier for DGS file * loc_name: Name * fold_id: In Folder * sgn: Nominal Apparent Power in MVA * ugn: Nominal Voltage in kV * cosn: Power Factor * xd: Synchronous Reactances: xd in p.u. * xq: Synchronous Reactances: xq in p.u. * xdsss: Subtransient Reactance: saturated value xd''sat in p.u. * rstr: Stator Resistance: rstr in p.u. * xdsat: For single fed short-circuit: Reciprocal of short-circuit ratio (xdsat) in p.u. * satur: For single fed short-circuit: Machine Type IEC909/IEC60909 ******************************************************************************** ''' typ = synchronous_machine_type[synchronous_machine_type.ID == synchronous_machine['typ_id'][i]] snom = typ['sgn'].values[0] vnom = synchronous_machine['usetp'][i] name = synchronous_machine['loc_name'][i].decode(codification) gen = Generator(name=name, active_power=synchronous_machine['pgini'][i] * num_machines, voltage_module=vnom, Qmin=synchronous_machine['q_min'][i] * num_machines * snom, Qmax=synchronous_machine['q_max'][i] * num_machines * snom, Snom=snom, power_prof=None, vset_prof=None) circuit.add_generator(bus_obj, gen) # if synchronous_machine['pgini'][i] != 0: # # gen = StaticGenerator(name=name, power=complex(0, synchronous_machine['pgini'][i])) # gen = Generator(name=name, active_power=synchronous_machine['pgini'][i]) # circuit.add_static_generator(bus_obj, gen) return circuit
def parse_json_data_v2(data: dict, logger: Logger): """ New Json parser :param data: :param logger: :return: """ devices = data['devices'] profiles = data['profiles'] if DeviceType.CircuitDevice.value in devices.keys(): dta = devices[DeviceType.CircuitDevice.value] circuit = MultiCircuit(name=str(dta['name']), Sbase=float(dta['sbase']), fbase=float(dta['fbase']), idtag=str(dta['id'])) jcircuit = devices["Circuit"] circuit.Sbase = jcircuit["sbase"] # Countries country_dict = dict() if 'Country' in devices.keys(): elms = devices["Country"] for jentry in elms: elm = Country(idtag=str(jentry['id']), code=str(jentry['code']), name=str(jentry['name'])) circuit.countries.append(elm) country_dict[elm.idtag] = elm else: elm = Country(idtag=None, code='Default', name='Default') circuit.countries.append(elm) # Areas areas_dict = dict() if 'Area' in devices.keys(): elms = devices["Area"] for jentry in elms: elm = Area(idtag=str(jentry['id']), code=str(jentry['code']), name=str(jentry['name'])) circuit.areas.append(elm) areas_dict[elm.idtag] = elm else: elm = Area(idtag=None, code='Default', name='Default') circuit.areas.append(elm) # Zones zones_dict = dict() if 'Zone' in devices.keys(): elms = devices["Zone"] for jentry in elms: elm = Zone(idtag=str(jentry['id']), code=str(jentry['code']), name=str(jentry['name'])) circuit.zones.append(elm) zones_dict[elm.idtag] = elm else: elm = Zone(idtag=None, code='Default', name='Default') circuit.zones.append(elm) # Substations substations_dict = dict() if 'Substation' in devices.keys(): elms = devices["Substation"] for jentry in elms: elm = Substation(idtag=str(jentry['id']), code=str(jentry['code']), name=str(jentry['name'])) circuit.substations.append(elm) substations_dict[elm.idtag] = elm else: elm = Substation(idtag=None, code='Default', name='Default') circuit.substations.append(elm) # buses bus_dict = dict() if 'Bus' in devices.keys(): buses = devices["Bus"] for jentry in buses: area_id = str(jentry['area']) if 'area' in jentry.keys() else '' zone_id = str(jentry['zone']) if 'zone' in jentry.keys() else '' substation_id = str(jentry['substation']) if 'substation' in jentry.keys() else '' country_id = str(jentry['country']) if 'country' in jentry.keys() else '' if area_id in areas_dict.keys(): area = areas_dict[area_id] else: area = circuit.areas[0] if zone_id in zones_dict.keys(): zone = zones_dict[zone_id] else: zone = circuit.zones[0] if substation_id in substations_dict.keys(): substation = substations_dict[substation_id] else: substation = circuit.substations[0] if country_id in country_dict.keys(): country = country_dict[country_id] else: country = circuit.countries[0] bus = Bus(name=str(jentry['name']), idtag=str(jentry['id']), vnom=float(jentry['vnom']), vmin=float(jentry['vmin']), vmax=float(jentry['vmax']), r_fault=float(jentry['rf']), x_fault=float(jentry['xf']), xpos=float(jentry['x']), ypos=float(jentry['y']), height=float(jentry['h']), width=float(jentry['w']), active=bool(jentry['active']), is_slack=bool(jentry['is_slack']), area=area, zone=zone, substation=substation, country=country, longitude=float(jentry['lon']), latitude=float(jentry['lat'])) bus_dict[jentry['id']] = bus circuit.add_bus(bus) if 'Generator' in devices.keys(): generators = devices["Generator"] for jentry in generators: gen = Generator(name=str(jentry['name']), idtag=str(jentry['id']), active_power=float(jentry['p']), power_factor=float(jentry['pf']), voltage_module=float(jentry['vset']), is_controlled=bool(jentry['is_controlled']), Qmin=float(jentry['qmin']), Qmax=float(jentry['qmax']), Snom=float(jentry['snom']), active=bool(jentry['active']), p_min=float(jentry['pmin']), p_max=float(jentry['pmax']), op_cost=float(jentry['cost']), ) gen.bus = bus_dict[jentry['bus']] circuit.add_generator(gen.bus, gen) if 'Battery' in devices.keys(): batteries = devices["Battery"] for jentry in batteries: gen = Battery(name=str(jentry['name']), idtag=str(jentry['id']), active_power=float(jentry['p']), power_factor=float(jentry['pf']), voltage_module=float(jentry['vset']), is_controlled=bool(jentry['is_controlled']), Qmin=float(jentry['qmin']), Qmax=float(jentry['qmax']), Snom=float(jentry['snom']), active=bool(jentry['active']), p_min=float(jentry['pmin']), p_max=float(jentry['pmax']), op_cost=float(jentry['cost']), ) gen.bus = bus_dict[jentry['bus']] circuit.add_battery(gen.bus, gen) if 'Load' in devices.keys(): loads = devices["Load"] for jentry in loads: elm = Load(name=str(jentry['name']), idtag=str(jentry['id']), P=float(jentry['p']), Q=float(jentry['q']), active=bool(jentry['active'])) elm.bus = bus_dict[jentry['bus']] circuit.add_load(elm.bus, elm) if "Shunt" in devices.keys(): shunts = devices["Shunt"] for jentry in shunts: elm = Shunt(name=str(jentry['name']), idtag=str(jentry['id']), G=float(jentry['g']), B=float(jentry['b']), active=bool(jentry['active'])) elm.bus = bus_dict[jentry['bus']] circuit.add_shunt(elm.bus, elm) if "Line" in devices.keys(): lines = devices["Line"] for entry in lines: elm = Line(bus_from=bus_dict[entry['bus_from']], bus_to=bus_dict[entry['bus_to']], name=str(entry['name']), idtag=str(entry['id']), r=float(entry['r']), x=float(entry['x']), b=float(entry['b']), rate=float(entry['rate']), active=entry['active'], length=float(entry['length']), ) circuit.add_line(elm) if "Transformer" in devices.keys() or "Transformer2w" in devices.keys(): if "Transformer" in devices.keys(): transformers = devices["Transformer"] elif "Transformer2w" in devices.keys(): transformers = devices["Transformer2w"] else: raise Exception('Transformer key not found') for entry in transformers: elm = Transformer2W(bus_from=bus_dict[entry['bus_from']], bus_to=bus_dict[entry['bus_to']], name=str(entry['name']), idtag=str(entry['id']), r=float(entry['r']), x=float(entry['x']), g=float(entry['g']), b=float(entry['b']), rate=float(entry['rate']), active=bool(entry['active']), tap=float(entry['tap_module']), shift_angle=float(entry['tap_angle']), ) circuit.add_transformer2w(elm) if "VSC" in devices.keys(): vsc = devices["VSC"] # TODO: call correct_buses_connection() if "HVDC Line" in devices.keys(): hvdc = devices["HVDC Line"] return circuit else: logger.add('The Json structure does not have a Circuit inside the devices!') return MultiCircuit()
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)
class GridGeneratorGUI(QDialog): def __init__( self, parent=None, ): """ :param parent: """ QDialog.__init__(self, parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setWindowTitle('Grid Generator') self.g = RpgAlgorithm() self.circuit = MultiCircuit() self.applied = False self.ui.applyButton.clicked.connect(self.apply) self.ui.previewButton.clicked.connect(self.preview) def msg(self, text, title="Warning"): """ Message box :param text: Text to display :param title: Name of the window """ msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText(text) # msg.setInformativeText("This is additional information") msg.setWindowTitle(title) # msg.setDetailedText("The details are as follows:") msg.setStandardButtons(QMessageBox.Ok) retval = msg.exec_() def fill_graph(self): """ # set desired parameters and perform algorithm :return: """ self.g = RpgAlgorithm() n = self.ui.nodes_spinBox.value() n0 = 10 r = self.ui.ratio_SpinBox.value() if n0 >= n: n0 = n - 1 self.g.set_params(n=n, n0=n0, r=r) self.g.initialise() self.g.grow() def preview(self): """ :return: """ self.fill_graph() G = nx.Graph(self.g.edges) pos = { i: (self.g.lat[i], self.g.lon[i]) for i in range(self.g.added_nodes) } self.ui.plotwidget.clear() nx.draw(G, ax=self.ui.plotwidget.get_axis(), pos=pos, with_labels=True, node_color='lightblue') self.ui.plotwidget.redraw() def apply(self): """ Create graph, then the circuit and close :return: Nothing """ self.fill_graph() self.circuit = MultiCircuit() explosion_factor = 10000.0 # number of nodes n = self.g.added_nodes # assign the load and generation buses genbus = self.ui.generation_nodes_SpinBox.value() loadbus = self.ui.load_nodes_SpinBox.value() if (genbus + loadbus) > 100: s = genbus + loadbus genbus /= s loadbus /= s gen_buses_num = int(np.floor(n * genbus / 100)) load_buses_num = int(np.floor(n * loadbus / 100)) rng = default_rng() numbers = rng.choice(n, size=gen_buses_num + load_buses_num, replace=False) gen_buses = numbers[:gen_buses_num] load_buses = numbers[gen_buses_num:] pmax = self.ui.power_SpinBox.value() # generate buses bus_dict = dict() for i in range(n): bus = Bus(name='Bus ' + str(i + 1), xpos=self.g.lat[i] * explosion_factor, ypos=-self.g.lon[i] * explosion_factor) bus_dict[i] = bus self.circuit.add_bus(bus) # generate loads factor = np.random.random(load_buses_num) factor /= factor.sum() pf = self.ui.power_factor_SpinBox.value() for k, i in enumerate(load_buses): bus = bus_dict[i] p = pmax * factor[k] q = p * pf load = Load(name='Load@bus' + str(i + 1), P=p, Q=q) self.circuit.add_load(bus, load) # generate generators factor = np.random.random(gen_buses_num) factor /= factor.sum() for k, i in enumerate(gen_buses): bus = bus_dict[i] gen = Generator(name='Generator@bus' + str(i + 1), active_power=pmax * factor[k]) self.circuit.add_generator(bus, gen) # generate lines r = self.ui.r_SpinBox.value() x = self.ui.x_SpinBox.value() b = self.ui.b_SpinBox.value() for f, t in self.g.edges: dx = (self.g.lat[f] - self.g.lat[t]) * explosion_factor dy = (self.g.lon[f] - self.g.lon[t]) * explosion_factor m = np.sqrt( dx * dx + dy * dy) / 10.0 # divided by 10 to have more meaningful values b1 = bus_dict[f] b2 = bus_dict[t] lne = Line(bus_from=b1, bus_to=b2, name='Line ' + str(f) + '-' + str(t), r=r * m, x=x * m, b=b * m, length=m) self.circuit.add_line(lne) # quit self.applied = True self.close()
def parse_json_data_v2(data: dict, logger: Logger): """ New Json parser :param data: :param logger: :return: """ devices = data['devices'] profiles = data['profiles'] if DeviceType.CircuitDevice.value in devices.keys(): dta = devices[DeviceType.CircuitDevice.value] circuit = MultiCircuit(name=str(dta['name']), Sbase=float(dta['sbase']), fbase=float(dta['fbase']), idtag=str(dta['id'])) jcircuit = devices["Circuit"] circuit.Sbase = jcircuit["sbase"] bus_dict = dict() if 'Bus' in devices.keys(): buses = devices["Bus"] for jentry in buses: bus = Bus(name=str(jentry['name']), idtag=str(jentry['id']), vnom=float(jentry['vnom']), vmin=float(jentry['vmin']), vmax=float(jentry['vmax']), r_fault=float(jentry['rf']), x_fault=float(jentry['xf']), xpos=float(jentry['x']), ypos=float(jentry['y']), height=float(jentry['h']), width=float(jentry['w']), active=bool(jentry['active']), is_slack=bool(jentry['is_slack']), # is_dc=jbus['id'], area=jentry['area'], zone=jentry['zone'], substation=jentry['substation'], # country=jbus['id'], longitude=float(jentry['lon']), latitude=float(jentry['lat']) ) bus_dict[jentry['id']] = bus circuit.add_bus(bus) if 'Generator' in devices.keys(): generators = devices["Generator"] for jentry in generators: gen = Generator(name=str(jentry['name']), idtag=str(jentry['id']), active_power=float(jentry['p']), power_factor=float(jentry['pf']), voltage_module=float(jentry['vset']), is_controlled=bool(jentry['is_controlled']), Qmin=float(jentry['qmin']), Qmax=float(jentry['qmax']), Snom=float(jentry['snom']), # power_prof=jgen['name'], # power_factor_prof=jgen['name'], # vset_prof=jgen['name'], # Cost_prof=jgen['name'], active=bool(jentry['active']), p_min=float(jentry['pmin']), p_max=float(jentry['pmax']), op_cost=float(jentry['cost']), # Sbase=jgen['name'], # enabled_dispatch=jgen['name'], # mttf=jgen['name'], # mttr=jgen['name'] ) gen.bus = bus_dict[jentry['bus']] circuit.add_generator(gen.bus, gen) if 'Battery' in devices.keys(): batteries = devices["Battery"] for jentry in batteries: gen = Battery(name=str(jentry['name']), idtag=str(jentry['id']), active_power=float(jentry['p']), power_factor=float(jentry['pf']), voltage_module=float(jentry['vset']), is_controlled=bool(jentry['is_controlled']), Qmin=float(jentry['qmin']), Qmax=float(jentry['qmax']), Snom=float(jentry['snom']), # power_prof=jgen['name'], # power_factor_prof=jgen['name'], # vset_prof=jgen['name'], # Cost_prof=jgen['name'], active=bool(jentry['active']), p_min=float(jentry['pmin']), p_max=float(jentry['pmax']), op_cost=float(jentry['cost']), # Sbase=jgen['name'], # enabled_dispatch=jgen['name'], # mttf=jgen['name'], # mttr=jgen['name'] ) gen.bus = bus_dict[jentry['bus']] circuit.add_battery(gen.bus, gen) if 'Load' in devices.keys(): loads = devices["Load"] for jentry in loads: elm = Load(name=str(jentry['name']), idtag=str(jentry['id']), # G: float = 0.0, # B: float = 0.0, # Ir: float = 0.0, # Ii: float = 0.0, P=float(jentry['p']), Q=float(jentry['q']), # cost=jentry['cost'], # G_prof: Any = None, # B_prof: Any = None, # Ir_prof: Any = None, # Ii_prof: Any = None, # P_prof: Any = None, # Q_prof: Any = None, active=bool(jentry['active'])) elm.bus = bus_dict[jentry['bus']] circuit.add_load(elm.bus, elm) if "Shunt" in devices.keys(): shunts = devices["Shunt"] for jentry in shunts: elm = Shunt(name=str(jentry['name']), idtag=str(jentry['id']), G=float(jentry['g']), B=float(jentry['b']), # G_prof: Any = None, # B_prof: Any = None, active=bool(jentry['active'])) elm.bus = bus_dict[jentry['bus']] circuit.add_shunt(elm.bus, elm) if "Line" in devices.keys(): lines = devices["Line"] for entry in lines: elm = Line(bus_from=bus_dict[entry['bus_from']], bus_to=bus_dict[entry['bus_to']], name=str(entry['name']), idtag=str(entry['id']), r=float(entry['r']), x=float(entry['x']), b=float(entry['b']), rate=float(entry['rate']), active=entry['active'], # tolerance: int = 0, # cost: float = 0.0, # mttf: int = 0, # mttr: int = 0, # r_fault: float = 0.0, # x_fault: float = 0.0, # fault_pos: float = 0.5, length=float(entry['length']), # temp_base: int = 20, # temp_oper: int = 20, # alpha: float = 0.00330, # template: LineTemplate = LineTemplate(), # rate_prof: Any = None, # Cost_prof: Any = None, # active_prof: Any = None, # temp_oper_prof: Any = None ) circuit.add_line(elm) if "Transformer" in devices.keys(): transformers = devices["Transformer"] for entry in transformers: elm = Transformer2W(bus_from=bus_dict[entry['bus_from']], bus_to=bus_dict[entry['bus_to']], name=str(entry['name']), idtag=str(entry['id']), r=float(entry['r']), x=float(entry['x']), g=float(entry['g']), b=float(entry['b']), rate=float(entry['rate']), active=bool(entry['active']), tap=float(entry['tap_module']), shift_angle=float(entry['tap_angle']), # tolerance: int = 0, # cost: float = 0.0, # mttf: int = 0, # mttr: int = 0, # r_fault: float = 0.0, # x_fault: float = 0.0, # fault_pos: float = 0.5, # temp_base: int = 20, # temp_oper: int = 20, # alpha: float = 0.00330, # template: LineTemplate = LineTemplate(), # rate_prof: Any = None, # Cost_prof: Any = None, # active_prof: Any = None, # temp_oper_prof: Any = None ) circuit.add_transformer2w(elm) if "VSC" in devices.keys(): vsc = devices["VSC"] if "HVDC Line" in devices.keys(): hvdc = devices["HVDC Line"] return circuit else: logger.add('The Json structure does not have a Circuit inside the devices!') return MultiCircuit()
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