def parse_areas_data(circuit: MultiCircuit, data, logger: Logger): """ Parse Matpower / FUBM Matpower area data into GridCal :param circuit: MultiCircuit instance :param data: data dictionary :return: area index -> object dictionary """ area_idx_dict = dict() if 'areas' in data.keys(): table = data['areas'] if table.shape[0] > 0: # if there are areas declared, clean the default areas circuit.areas = list() for i in range(table.shape[0]): area_idx = int(table[i, 0]) area_ref_bus_idx = table[i, 1] a = Area(name='Area ' + str(area_idx), code=str(area_idx)) area_idx_dict[area_idx] = (a, area_ref_bus_idx) circuit.add_area(a) if i == 0: # set the default area circuit.default_area = circuit.areas[0] return area_idx_dict
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 interpret_data_v1(circuit: MultiCircuit, data, logger: Logger) -> MultiCircuit: """ Pass the loaded table-like data to the structures :param circuit: :param data: Data dictionary :return: """ circuit.clear() # time profile if 'master_time' in data.keys(): master_time_array = data['master_time'] else: master_time_array = None # areas area_idx_dict = parse_areas_data(circuit, data, logger) # parse buses bus_idx_dict = parse_buses_data(circuit, data, area_idx_dict, logger) # parse generators parse_generators(circuit, data, bus_idx_dict, logger) # parse branches parse_branches_data(circuit, data, bus_idx_dict, logger) # add the profiles if master_time_array is not None: circuit.format_profiles(master_time_array) return circuit
def parse_switches(self, cim: CIMCircuit, circuit: MultiCircuit, busbar_dict): """ :param cim: :param circuit: :param busbar_dict: :return: """ EPS = 1e-20 cim_switches = ['Switch', 'Disconnector', 'Breaker', 'LoadBreakSwitch'] if any_in_dict(cim.elements_by_type, cim_switches): for elm in get_elements(cim.elements_by_type, cim_switches): b1, b2 = elm.get_buses() B1, B2 = try_buses(b1, b2, busbar_dict) if B1 is not None and B2 is not None: state = True line = gcdev.Switch(idtag=elm.uuid, bus_from=B1, bus_to=B2, name=str(elm.name), r=EPS, x=EPS, rate=EPS, active=state) circuit.add_switch(line) else: self.logger.add_error('Bus not found', elm.rfid)
def parse_matpower_file(filename, export=False) -> MultiCircuit: """ Args: filename: export: Returns: """ # open the file as text with open(filename, 'r') as myfile: text = myfile.read().replace('\n', '') # split the file into its case variables (the case variables always start with 'mpc.') chunks = text.split('mpc.') # declare circuit circuit = MultiCircuit() data = dict() # further process the loaded text for chunk in chunks: vals = chunk.split('=') key = vals[0].strip() if key == "baseMVA": v = find_between(chunk, '=', ';') circuit.Sbase = float(v) elif key == "bus": if chunk.startswith("bus_name"): v = txt2mat(find_between(chunk, '{', '}'), line_splitter=';', to_float=False) v = np.ndarray.flatten(v) data['bus_names'] = v else: data['bus'] = txt2mat(find_between(chunk, '[', ']'), line_splitter=';') elif key == "gencost": data['gen_cost'] = txt2mat(find_between(chunk, '[', ']'), line_splitter=';') elif key == "gen": data['gen'] = txt2mat(find_between(chunk, '[', ']'), line_splitter=';') elif key == "branch": data['branch'] = txt2mat(find_between(chunk, '[', ']'), line_splitter=';') circuit = interpret_data_v1(circuit, data) return circuit
def __init__(self, file_name): """ File open handler :param file_name: name of the file """ self.file_name = file_name self.circuit = MultiCircuit() self.logger = list()
def parse_model(self, cim: CIMCircuit, circuit: MultiCircuit): """ :param cim: :param circuit: :return: """ if 'Model' in cim.elements_by_type.keys(): for elm in cim.elements_by_type['Model']: if 'description' in elm.properties.keys(): circuit.comments = elm.properties['description'] if 'name' in elm.properties.keys(): circuit.name = elm.properties['name']
def parse_power_transformer(self, cim: CIMCircuit, circuit: MultiCircuit, busbar_dict): """ :param cim: :param circuit: :param busbar_dict: :return: """ if 'PowerTransformer' in cim.elements_by_type.keys(): for elm in cim.elements_by_type['PowerTransformer']: b1, b2 = elm.get_buses() B1, B2 = try_buses(b1, b2, busbar_dict) if B1 is not None and B2 is not None: R, X, G, B = elm.get_pu_values() rate = elm.get_rate() voltages = elm.get_voltages() voltages.sort() if len(voltages) == 2: lv, hv = voltages else: lv = 1 hv = 1 self.logger.add_error( 'Could not parse transformer nominal voltages', self.name) line = gcdev.Transformer2W(idtag=cimdev.rfid2uuid( elm.rfid), bus_from=B1, bus_to=B2, name=str(elm.name), r=R, x=X, g=G, b=B, rate=rate, tap=1.0, shift_angle=0, active=True, HV=hv, LV=lv) circuit.add_branch(line) else: self.logger.add_error('Bus not found', elm.rfid)
def get_static_generator_data(circuit: MultiCircuit, bus_dict, time_series=False, ntime=1): """ :param circuit: :param bus_dict: :param time_series: :return: """ devices = circuit.get_static_generators() data = StaticGeneratorData(nstagen=len(devices), nbus=len(circuit.buses), ntime=ntime) for k, elm in enumerate(devices): i = bus_dict[elm.bus] data.static_generator_names[k] = elm.name if time_series: data.static_generator_active[k, :] = elm.active_prof data.static_generator_s[k, :] = elm.P_prof + 1j * elm.Q_prof else: data.static_generator_active[k] = elm.active data.static_generator_s[k] = complex(elm.P, elm.Q) data.C_bus_static_generator[i, k] = 1 return data
def colour_the_schematic(circuit: MultiCircuit, Sbus, Sf, voltages, loadings, types=None, losses=None, St=None, hvdc_sending_power=None, hvdc_losses=None, hvdc_loading=None, failed_br_idx=None, loading_label='loading', ma=None, theta=None, Beq=None, use_flow_based_width=False, min_branch_width=1, max_branch_width=1, min_bus_width=20, max_bus_width=20, file_name=None): """ Color the grid based on the results passed :param circuit: :param Sbus: Buses power :param Sf: Branches power seen from the "from" bus :param voltages: Buses voltage :param loadings: Branches load :param types: Buses type :param losses: Branches losses :param St: power seen from the "to" bus :param hvdc_sending_power: :param hvdc_losses: :param hvdc_loading: :param failed_br_idx: failed branches :param loading_label: :param ma :param theta :param Beq :param file_name: Completely ignore this. It exist for interface compatibility :return: """ colour_sub_schematic(Sbase=circuit.Sbase, buses=circuit.buses, branches=circuit.get_branches_wo_hvdc(), hvdc_lines=circuit.hvdc_lines, Sbus=Sbus, Sf=Sf, St=St if St is not None else Sf, voltages=voltages, loadings=loadings, types=types, losses=losses, hvdc_sending_power=hvdc_sending_power, hvdc_losses=hvdc_losses, hvdc_loading=hvdc_loading, failed_br_idx=failed_br_idx, loading_label=loading_label, ma=ma, theta=theta, Beq=Beq, use_flow_based_width=use_flow_based_width, min_branch_width=min_branch_width, max_branch_width=max_branch_width, min_bus_width=min_bus_width, max_bus_width=max_bus_width, )
def run(self): """ run the file open procedure """ self.circuit = MultiCircuit() path, fname = os.path.split(self.file_name) self.progress_text.emit('Loading ' + fname + '...') self.logger = list() file_handler = FileOpen(file_name=self.file_name) self.circuit = file_handler.open( text_func=self.progress_text.emit, progress_func=self.progress_signal.emit) self.logger += file_handler.logger self.valid = True # post events self.progress_text.emit('Done!') self.done_signal.emit()
def get_shunt_data(circuit: MultiCircuit, bus_dict, time_series=False, ntime=1): """ :param circuit: :param bus_dict: :param time_series: :return: """ devices = circuit.get_shunts() data = ShuntData(nshunt=len(devices), nbus=len(circuit.buses), ntime=ntime) for k, elm in enumerate(devices): i = bus_dict[elm.bus] data.shunt_names[k] = elm.name if time_series: data.shunt_active[k, :] = elm.active_prof data.shunt_admittance[k, :] = elm.G_prof + 1j * elm.B_prof else: data.shunt_active[k] = elm.active data.shunt_admittance[k] = complex(elm.G, elm.B) data.C_bus_shunt[i, k] = 1 return data
def select_branches_to_reduce(circuit: MultiCircuit, rx_criteria=True, rx_threshold=1e-5, selected_types=BranchType.Branch): """ Find branches to remove Args: circuit: Circuit to modify in-place rx_criteria: use the r+x threshold to select branches? rx_threshold: r+x threshold selected_types: branch types to select """ branches_to_remove_idx = list() branches = circuit.get_branches() for i in range(len(branches)): # is this branch of the selected type? if branches[i].branch_type in selected_types: # Am I filtering by r+x threshold? if rx_criteria: # compute the r+x ratio rx = branches[i].R + branches[i].X # if the r+x criteria is met, add it if rx < rx_threshold: print(i, '->', rx, '<', rx_threshold) branches_to_remove_idx.append(i) else: # Add the branch because it was selected and there is no further criteria branches_to_remove_idx.append(i) return branches_to_remove_idx
def get_allowed_sheets(circuit=MultiCircuit()): """ :param circuit: :return: """ ######################################################################################################## # declare objects to iterate name: [sample object, list of objects, headers] ######################################################################################################## object_types = get_objects_dictionary(circuit) ######################################################################################################## # generic object iteration ######################################################################################################## allowed_data_sheets = { 'Conf': None, 'config': None, 'wires': None, 'overhead_line_types': None, 'underground_cable_types': None, 'sequence_line_types': None, 'transformer_types': None, 'time': None, 'load_Sprof': complex, 'load_Iprof': complex, 'load_Zprof': complex, 'static_generator': None, 'static_generator_Sprof': complex, 'static_generator_P_prof': complex, 'static_generator_Q_prof': complex, 'battery': None, 'battery_Vset_profiles': float, 'battery_P_profiles': float, 'controlled_generator': None, 'CtrlGen_Vset_profiles': float, 'CtrlGen_P_profiles': float, 'shunt_Y_profiles': complex, 'tower_wires': None } for object_type_name in object_types.keys(): object_sample, lists_of_objects = object_types[object_type_name] for main_property, profile_property in object_sample.properties_with_profile.items( ): if profile_property not in allowed_data_sheets.keys(): # create the profile allowed_data_sheets[ object_type_name + '_' + profile_property] = object_sample.editable_headers[ main_property].tpe # declare the DataFrames for the normal data allowed_data_sheets[object_type_name] = None return allowed_data_sheets
def get_load_data(circuit: MultiCircuit, bus_dict, opf_results=None, time_series=False, opf=False, ntime=1): """ :param circuit: :param bus_dict: :param opf_results: :param time_series: :param opf: :param ntime: :return: """ devices = circuit.get_loads() if opf: data = LoadOpfData(nload=len(devices), nbus=len(circuit.buses), ntime=ntime) else: data = LoadData(nload=len(devices), nbus=len(circuit.buses), ntime=ntime) for k, elm in enumerate(devices): i = bus_dict[elm.bus] data.load_names[k] = elm.name data.load_active[k] = elm.active if time_series: data.load_s[k, :] = elm.P_prof + 1j * elm.Q_prof if opf: data.load_cost[k, :] = elm.Cost_prof if opf_results is not None: data.load_s[k, :] -= opf_results.load_shedding[:, k] else: data.load_s[k] = complex(elm.P, elm.Q) if opf: data.load_cost[k] = elm.Cost if opf_results is not None: data.load_s[k] -= opf_results.load_shedding[k] data.C_bus_load[i, k] = 1 return data
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 collect_measurements(circuit: MultiCircuit, bus_idx, branch_idx): """ Form the input from the circuit measurements :return: nothing, the input object is stored in this class """ se_input = StateEstimationInput() # collect the bus measurements for i in bus_idx: for m in circuit.buses[i].measurements: if m.measurement_type == MeasurementType.Pinj: se_input.p_inj_idx.append(i) se_input.p_inj.append(m) elif m.measurement_type == MeasurementType.Qinj: se_input.q_inj_idx.append(i) se_input.q_inj.append(m) elif m.measurement_type == MeasurementType.Vmag: se_input.vm_m_idx.append(i) se_input.vm_m.append(m) else: raise Exception('The bus ' + str(circuit.buses[i]) + ' contains a measurement of type ' + str(m.measurement_type)) # collect the branch measurements branches = circuit.get_branches() for i in branch_idx: # branch = circuit.branches[i] for m in branches[i].measurements: if m.measurement_type == MeasurementType.Pflow: se_input.p_flow_idx.append(i) se_input.p_flow.append(m) elif m.measurement_type == MeasurementType.Qflow: se_input.q_flow_idx.append(i) se_input.q_flow.append(m) elif m.measurement_type == MeasurementType.Iflow: se_input.i_flow_idx.append(i) se_input.i_flow.append(m) else: raise Exception('The branch ' + str(branches[i]) + ' contains a measurement of type ' + str(m.measurement_type)) return se_input
def open_h5(file_path): circuit = MultiCircuit() store = pd.HDFStore(file_path) dfs = dict() for group in store.root: dfs[group._v_name] = pd.read_hdf(store, group._v_pathname) return dfs
def __init__(self, file_name, cim_tp_file_name='', cim_eq_file_name=''): """ File open handler :param file_name: name of the file """ self.file_name = file_name self.cim_tp_file_name = cim_tp_file_name self.cim_eq_file_name = cim_eq_file_name self.circuit = MultiCircuit() self.logger = Logger()
def parse_bus_bars(self, cim: CIMCircuit, circuit: MultiCircuit): """ :param cim: :param circuit: :return: """ busbar_dict = dict() if 'BusbarSection' in cim.elements_by_type.keys(): for elm in cim.elements_by_type['BusbarSection']: obj = gcdev.Bus(name=str(elm.name), idtag=elm.uuid) circuit.add_bus(obj) busbar_dict[elm] = obj else: self.logger.add_error( "No BusbarSections: There is no chance to reduce the grid") return busbar_dict
def parse_ac_line_segment(self, cim: CIMCircuit, circuit: MultiCircuit, busbar_dict): """ :param cim: :param circuit: :param busbar_dict: :return: """ if 'ACLineSegment' in cim.elements_by_type.keys(): for elm in cim.elements_by_type['ACLineSegment']: b1, b2 = elm.get_buses() B1, B2 = try_buses(b1, b2, busbar_dict) if B1 is not None and B2 is not None: R, X, G, B = elm.get_pu_values() rate = elm.get_rate() # create AcLineSegment (Line) line = gcdev.Line(idtag=elm.uuid, bus_from=B1, bus_to=B2, name=str(elm.name), r=R, x=X, b=B, rate=rate, active=True, mttf=0, mttr=0) circuit.add_line(line) else: self.logger.add_error('Bus not found', elm.rfid)
def save_json_file(file_path, circuit: MultiCircuit): """ Save JSON file :param file_path: file path :param circuit: GridCal MultiCircuit element """ elements = list() # list of key = 0 bus_key_dict = dict() logger = list() # add the circuit circuit_dict = circuit.get_json_dict(key) elements.append(circuit_dict) key += 1 # add the buses for bus in circuit.buses: # pack the bus data into a dictionary dictionary = bus.get_json_dict(key) dictionary['circuit'] = circuit_dict[ 'id'] # add the circuit id on each bus elements.append(dictionary) bus_key_dict[bus] = key key += 1 # pack all the elements within the bus for device in bus.loads + bus.controlled_generators + bus.static_generators + bus.batteries + bus.shunts: dictionary = device.get_json_dict(key, bus_key_dict) elements.append(dictionary) key += 1 # branches for branch in circuit.branches: # pack the branch data into a dictionary dictionary = branch.get_json_dict(key, bus_key_dict) elements.append(dictionary) key += 1 # convert the list of dictionaries to json json_str = json.dumps(elements, indent=True) # Save json to a text file file text_file = open(file_path, "w") text_file.write(json_str) text_file.close() return logger
def parse_shunts(self, cim: CIMCircuit, circuit: MultiCircuit, busbar_dict): """ :param cim: :param circuit: :param busbar_dict: :return: """ if 'ShuntCompensator' in cim.elements_by_type.keys(): for elm in cim.elements_by_type['ShuntCompensator']: b1 = elm.get_bus() B1 = try_bus(b1, busbar_dict) if B1 is not None: g = 0 b = 0 sh = gcdev.Shunt(idtag=elm.uuid, name=str(elm.name), G=g, B=b) circuit.add_shunt(B1, sh) else: self.logger.add_error('Bus not found', elm.rfid)
def parse_generators(self, cim: CIMCircuit, circuit: MultiCircuit, busbar_dict): """ :param cim: :param circuit: :param busbar_dict: :return: """ if 'SynchronousMachine' in cim.elements_by_type.keys(): for elm in cim.elements_by_type['SynchronousMachine']: b1 = elm.get_bus() B1 = try_bus(b1, busbar_dict) if B1 is not None: gen = gcdev.Generator(idtag=elm.uuid, name=str(elm.name), active_power=elm.p, voltage_module=1.0) circuit.add_generator(B1, gen) else: self.logger.add_error('Bus not found', elm.rfid)
def open_sqlite(file_path): circuit = MultiCircuit() # make connection conn = sqlite3.connect(file_path) dfs = dict() # get the table names tables = conn.execute("SELECT name FROM sqlite_master WHERE type='table';") names = [t[0] for t in tables] check_names(names) for key in names: dfs[key] = pd.read_sql('select * from ' + key, conn) df = dfs['config'] idx = df['Property'][df['Property'] == 'BaseMVA'].index if len(idx) > 0: dfs["baseMVA"] = np.double(df.values[idx, 1]) else: dfs["baseMVA"] = 100 idx = df['Property'][df['Property'] == 'Version'].index if len(idx) > 0: dfs["version"] = np.double(df.values[idx, 1]) idx = df['Property'][df['Property'] == 'Name'].index if len(idx) > 0: dfs["name"] = df.values[idx[0], 1] else: dfs["name"] = 'Grid' idx = df['Property'][df['Property'] == 'Comments'].index if len(idx) > 0: dfs["Comments"] = df.values[idx[0], 1] else: dfs["Comments"] = '' # fill circuit data interpret_excel_v3(circuit, dfs) return circuit
def get_objects_dictionary(circuit=MultiCircuit()): """ :param circuit: :return: """ object_types = { 'bus': [Bus(), circuit.buses], 'branch': [Branch(None, None), circuit.branches], 'load': [Load(), circuit.get_loads()], 'static_generator': [StaticGenerator(), circuit.get_static_generators()], 'battery': [Battery(), circuit.get_batteries()], 'generator': [Generator(), circuit.get_generators()], 'shunt': [Shunt(), circuit.get_shunts()] } return object_types
def load_cim_file(self, cim_files): """ Load CIM file :param cim_files: list of CIM files (.xml) """ # declare GridCal circuit circuit = MultiCircuit() EPS = 1e-16 # declare CIM circuit to process the file(s) self.cim = CIMCircuit(text_func=self.text_func, progress_func=self.progress_func, logger=self.logger) # import the cim files' content into a dictionary data = read_cim_files(cim_files) lst2 = sort_cim_files(list(data.keys())) # Parse the files for f in lst2: name, file_extension = os.path.splitext(f) self.emit_text('Parsing xml structure of ' + name) self.cim.parse_xml_text(text_lines=data[f]) # replace CIM references in the CIM objects self.emit_text('Looking for CIM references...') self.cim.find_references() # Parse devices into GridCal self.emit_text('Converting CIM to GridCal...') self.parse_model(self.cim, circuit) busbar_dict = self.parse_bus_bars(self.cim, circuit) self.parse_ac_line_segment(self.cim, circuit, busbar_dict) self.parse_ac_line_segment(self.cim, circuit, busbar_dict) self.parse_power_transformer(self.cim, circuit, busbar_dict) self.parse_switches(self.cim, circuit, busbar_dict) self.parse_loads(self.cim, circuit, busbar_dict) self.parse_shunts(self.cim, circuit, busbar_dict) self.parse_generators(self.cim, circuit, busbar_dict) self.emit_text('Done!') return circuit
def group_generators_by_technology(circuit: MultiCircuit): """ Compose a dictionary of generator groups :param circuit: MultiCircuit :return: dictionary [Technology] : [generator indices] """ gens = circuit.get_generators() groups = dict() for i, gen in enumerate(gens): if gen.technology in groups.keys(): arr = np.r_[groups[gen.technology], i] groups[gen.technology] = arr else: groups[gen.technology] = np.array([i]) return groups
def remove_elements(circuit: MultiCircuit, loading_vector, idx=None): """ Remove branches based on loading Returns: Nothing """ criteria = 'None' if idx is None: load = abs(loading_vector) idx = np.where(load > 1.0)[0] if len(idx) == 0: criteria = 'Loading' idx = np.where(load >= load.max())[0] # disable the selected branches # print('Removing:', idx, load[idx]) branches = circuit.get_branches() for i in idx: branches[i].active = False return idx, criteria
def get_shunt_data(circuit: MultiCircuit, bus_dict, Vbus, logger: Logger, time_series=False, ntime=1): """ :param circuit: :param bus_dict: :param time_series: :return: """ devices = circuit.get_shunts() data = ShuntData(nshunt=len(devices), nbus=len(circuit.buses), ntime=ntime) for k, elm in enumerate(devices): i = bus_dict[elm.bus] data.shunt_names[k] = elm.name data.shunt_controlled[k] = elm.is_controlled data.shunt_b_min[k] = elm.Bmin data.shunt_b_max[k] = elm.Bmax if time_series: data.shunt_active[k, :] = elm.active_prof data.shunt_admittance[k, :] = elm.G_prof + 1j * elm.B_prof else: data.shunt_active[k] = elm.active data.shunt_admittance[k] = complex(elm.G, elm.B) if Vbus[i, 0].real == 1.0: Vbus[i, :] = complex(elm.Vset, 0) elif elm.Vset != Vbus[i, 0]: logger.add_error('Different set points', elm.bus.name, elm.Vset, Vbus[i, 0]) data.C_bus_shunt[i, k] = 1 return data