示例#1
0
    def add_equipment(self, equ_path):
        """Adds equipment informantion like it is stored in CIM DB"""

        if not os.path.exists(equ_path):
            logger.warning(
                'could not find equipment file: {}'.format(equ_path))
            return

        _, equ_file_name = os.path.split(equ_path)
        equ_name, suffix = equ_file_name.split('.')
        if suffix.lower() != 'csv':
            logger.warning(
                'equipment file is not a CSV file: {}'.format(equ_file_name))
            return

        with open(equ_path, newline='') as equ_file:
            csv_reader = csv.reader(equ_file, delimiter=',')
            header = next(csv_reader)
            # Define the database
            csv_data = dict()
            for data in csv_reader:
                csv_data[data[0]] = dict()
                for i in range(1, len(data)):
                    if len(data[i]) == 0:
                        continue
                    csv_data[data[0]][header[i]] = data[i]
            equ_file.close()

        if equ_name not in self._equipment.keys():
            self._equipment[equ_name] = csv_data
        else:
            for key in csv_data.keys():
                if key not in self._equipment[equ_name]:
                    self._equipment[equ_name][key] = csv_data[key]
示例#2
0
    def add_all_coordinates(self, gml_path):
        """Adds coordinates to the corresponding CIM element"""
        if not os.path.exists(gml_path):
            logger.debug('could not find GML file: {}'.format(gml_path))
            return

        xml_data = et.parse(gml_path)
        nmm_root = xml_data.getroot()

        if nmm_root.tag.split('}')[-1] != 'NMMFeatureCollection':
            logger.warning(
                'no NMMFeatureCollection found for file: {}'.format(gml_path))
            return

        for nmm_data in nmm_root:
            if nmm_data.tag.split('}')[-1] != 'DeviceMember':
                logger.debug(
                    'found unidentified NMM element ({}) in: {}'.format(
                        nmm_data, gml_path))
                continue

            nmm_dev = self.get_nmm_device(nmm_data)
            cim_ele = self.get_element_by_id(nmm_dev['id'])
            if cim_ele is None:
                continue
            self._ele.remove(cim_ele)
            cim_ele['coords'] = [float(c) for c in nmm_dev['coords']]
            self._ele.append(cim_ele)
示例#3
0
    def get_cim_element(rdf_data):
        """Analyses and makes a dictionary containing the CIM element data"""
        logger.debug('analysing: {}'.format(rdf_data))

        ele = dict()

        # Get id
        for key in rdf_data.attrib.keys():
            if key.split('}')[-1] == 'ID':
                ele['id'] = rdf_data.attrib[key]
        # Get tag
        ele['tag'] = rdf_data.tag.split('}')[-1]

        # Get data
        for child in rdf_data:
            # Find tag
            tag = child.tag.split('}')[-1]
            if '.' in tag:
                tag = tag.split('.')[-1]
            # tag = tag.lower()

            # Find text for child
            text = child.text
            if text is not None:
                text = text.strip()
            else:
                text = ''
            if len(text) > 0:
                # If text is not empty, add to ele
                if tag in ele.keys():
                    logger.warning(
                        'tag "{}" already exists for element "{}"'.format(
                            tag, ele['id']))
                    continue
                ele[tag] = text
                continue

            # If text is empty, find attributes
            for key in child.attrib.keys():
                if key.split('}')[-1] == 'resource':
                    attrib = child.attrib[key].replace('#', '')
                    if tag in ele.keys():
                        if isinstance(ele[tag], list):
                            ele[tag].append(attrib)
                        else:
                            ele[tag] = [ele[tag], attrib]
                    else:
                        ele[tag] = attrib

        logger.debug('element details : {}'.format(ele))
        return ele
示例#4
0
    def get_element_by_id(self, ele_id, search_eles=None):
        """Returns the element that matches ID"""
        if search_eles is None:
            search_eles = self._ele
        elif not isinstance(search_eles, list):
            search_eles = self.get_elements_by_tag(search_eles)

        ele = [eles for eles in search_eles if eles['id'] == ele_id]
        if len(ele) == 0:
            logger.warning('no element found for ID: {}'.format(ele_id))
            return None
        elif len(ele) > 1:
            logger.warning('{} elements found for ID: {}'.format(
                len(ele), ele_id))

        return ele[0]
示例#5
0
    def save_opendss_for_equipment(self, equipment):
        logger.info('saving equipment: {}'.format(equipment['id']))

        cn, terminals = self._get_terminals(equipment)

        if equipment['tag'] == 'PowerTransformer':

            # Find transformer asset
            txfrmr_asset = self.get_elements_by_resource(
                equipment['id'], 'Asset')
            if len(txfrmr_asset) == 0:
                logger.error('transformer ({}) has no asset'.format(
                    equipment['id']))
                return cn, terminals
            elif len(txfrmr_asset) > 1:
                logger.warning('{} assets found for transformer ({})'.format(
                    len(txfrmr_asset), equipment['id']))
            txfrmr_asset = txfrmr_asset[0]

            # Find transformer information
            txfrmr_info = self.get_element_by_id(txfrmr_asset['AssetInfo'])
            if txfrmr_info is None:
                logger.error(
                    'cannot find information for transforemer asset {}'.format(
                        txfrmr_asset['AssetInfo']))
                return cn, terminals

            # Populate dss and save
            dss = dict()
            if 'transformer' in self._equipment.keys(
            ) and txfrmr_info['name'] in self._equipment['transformer']:
                txfrmr_info = self._equipment['transformer'][
                    txfrmr_info['name']]
                dss['wdg_kv'] = [
                    float(txfrmr_info['PrimaryVoltageKVLL']),
                    float(txfrmr_info['SecondaryVoltageKVLL'])
                ]
                dss['wdg_kva'] = [
                    float(txfrmr_info['NominalRatingKVA']),
                    float(txfrmr_info['NominalRatingKVA'])
                ]
                dss['wdg_conn'] = ['delta', 'wye']
                dss['wdg_rneut'] = [
                    float(txfrmr_info['PrimGroundingResistanceOhms']),
                    float(txfrmr_info['SecGroundingResistanceOhms'])
                ]
                dss['wdg_xneut'] = [
                    float(txfrmr_info['PrimGroundingReactanceOhms']),
                    float(txfrmr_info['SecGroundingReactanceOhms'])
                ]
                dss['txfrmr_xhl'] = float(txfrmr_info['XR0Ratio'])
            else:
                dss['wdg_kv'] = [11.0, 0.4]
                dss['wdg_kva'] = [500.0, 500.0]
                dss['wdg_conn'] = ['delta', 'wye']
                dss['wdg_rneut'] = [0.0, 0.0]
                dss['wdg_xneut'] = [0.0, 0.0]
                dss['txfrmr_xhl'] = 8.0

            dss['id'] = equipment['id']
            dss['wdg_bus'] = [c['id'] for c in cn]
            dss['txfrmr_windings'] = 2
            dss['txfrmr_basefreq'] = 50.0
            dss['txfrmr_sub'] = 'y'

            self._save_new_dss_transformer(dss)

        elif equipment['tag'] in [
                'BusbarSection', 'Disconnector', 'Fuse', 'LoadBreakSwitch'
        ]:

            # Make a dummy line element
            dss = dict()
            dss['id'] = equipment['id']
            dss['line_bus'] = [c['id'] for c in cn]
            dss['line_length'] = 1e-4
            dss['line_linecode'] = 'DEFAULT'
            dss['line_phases'] = 3
            dss['line_units'] = 'm'

            self._save_new_dss_line(dss)
            if 'coords' in equipment.keys() and len(
                    equipment['coords']) == 2 * len(dss['line_bus']):
                self._save_new_dss_coordinates(dss['line_bus'],
                                               equipment['coords'])

        elif equipment['tag'] == 'ACLineSegment':

            # Get line asset
            line_asset = self.get_elements_by_resource(equipment['id'],
                                                       'Asset')

            dss = dict()
            dss['id'] = equipment['id']
            dss['line_bus'] = [c['id'] for c in cn]
            dss['line_phases'] = 3
            dss['line_units'] = 'm'

            # Check if it's overhead (boken into wires) or underground (single cable)
            if equipment['PSRType'] == 'PSRType_Overhead':
                if len(line_asset) != 2 and len(line_asset) != 4:
                    logger.warning('found {} assets overhead wire {}'.format(
                        len(line_asset), equipment['id']))
                    dss['line_linecode'] = 'DEFAULT'
                else:
                    wire_info = self.get_element_by_id(
                        line_asset[0]['AssetInfo'], 'OverheadWireInfo')
                    dss['line_linecode'] = wire_info['name']
            elif equipment['PSRType'] == 'PSRType_Underground':
                if len(line_asset) != 1:
                    logger.warning(
                        'found {} assets for underground cable {}'.format(
                            len(line_asset), equipment['id']))
                    dss['line_linecode'] = 'DEFAULT'
                else:
                    cable_info = self.get_element_by_id(
                        line_asset[0]['AssetInfo'], 'CableInfo')
                    dss['line_linecode'] = cable_info['name']
            else:
                logger.warning('unknown PSRType {}'.format(
                    equipment['PSRType']))
                dss['line_linecode'] = 'DEFAULT'

            # Populate length correctly
            dss['line_length'] = equipment['length']
            if 'coords' in equipment.keys():
                # Break into indicidual lines
                line_length_orginal = dss['line_length']
                line_bus_original = dss['line_bus'].copy()

                length_scales = list()
                length_scales_sum = 0.0
                c = equipment['coords']
                for i in range(2, len(c), 2):
                    length_scales.append(
                        math.sqrt(
                            math.pow(c[i] - c[i - 2], 2) +
                            math.pow(c[i + 1] - c[i - 1], 2)))
                    length_scales_sum += length_scales[-1]

                for i in range(len(length_scales)):
                    length_scales[i] /= length_scales_sum

                for i in range(len(length_scales)):
                    if i == 0:
                        dss['line_bus'][0] = line_bus_original[0]
                    else:
                        dss['line_bus'][0] = line_bus_original[0] + '_' + str(
                            i)

                    if i == len(length_scales) - 1:
                        dss['line_bus'][1] = line_bus_original[1]
                    else:
                        dss['line_bus'][1] = line_bus_original[0] + '_' + str(
                            i + 1)

                    dss['line_length'] = float(
                        line_length_orginal) * length_scales[i]
                    self._save_new_dss_line(dss)
                    self._save_new_dss_coordinates(dss['line_bus'],
                                                   c[i * 2:i * 2 + 4])
            else:
                self._save_new_dss_line(dss)

        elif equipment['tag'] == 'EnergyConsumer':

            # Get information
            dss = dict()
            dss['id'] = equipment['id']
            dss['load_name'] = equipment['name']
            dss['load_bus'] = cn[0]['id']
            # Apparently some energy consumers do not have phasing info
            if 'phases' in terminals[0]:
                dss['load_phases'] = terminals[0]['phases']
            else:
                dss['load_phases'] = 'DummyPhaseCode.ABCN'
            dss['load_power'] = 1.0
            dss['load_voltage'] = 0.23
            dss['load_pf'] = 0.95
            dss['load_model'] = 1

            self._save_new_dss_load(dss)
            if 'coords' in equipment.keys():
                self._save_new_dss_coordinates(dss['load_bus'],
                                               equipment['coords'])

        elif equipment['tag'] in ['EnergyServicePoint', 'EnergySource']:

            # All things that need not be used in OpenDSS
            pass

        else:

            # If an element has not yet been defined, flag it up and update code
            logger.warning('element {} not yet implemented'.format(
                equipment['tag']))

        return cn, terminals
示例#6
0
    def _save_new_dss_line(self, dss):
        # Before saving the line, save the linecode
        if 'linecode' not in self._dss_ele.keys():
            self._dss_ele['linecode'] = list()

        # Fix the linecode so that OpenDSS can use it
        dss['line_linecode_fixed'] = 'id_' + re.sub('[^0-9a-zA-Z]+', '_',
                                                    dss['line_linecode'])
        if dss['line_linecode_fixed'] not in self._dss_ele['linecode']:

            linecode = dict()
            linecode['name'] = dss['line_linecode_fixed']
            if 'cable' in self._equipment.keys(
            ) and dss['line_linecode'] in self._equipment['cable']:
                linecode_info = self._equipment['cable'][dss['line_linecode']]
                linecode['r1'] = linecode_info['PositiveSequenceResistance']
                linecode['x1'] = linecode_info['PositiveSequenceReactance']
                linecode['r0'] = linecode_info['ZeroSequenceResistance']
                linecode['x0'] = linecode_info['ZeroSequenceReactance']
                linecode['normamps'] = linecode_info['NominalRating']
                linecode['emergamps'] = linecode_info['FourthRating']
            elif 'overhead' in self._equipment.keys(
            ) and dss['line_linecode'] in self._equipment['overhead']:
                linecode_info = self._equipment['overhead'][
                    dss['line_linecode']]
                linecode['r1'] = linecode_info['PositiveSequenceResistance']
                linecode['x1'] = linecode_info['PositiveSequenceReactance']
                linecode['r0'] = linecode_info['ZeroSequenceResistance']
                linecode['x0'] = linecode_info['ZeroSequenceReactance']
                linecode['normamps'] = linecode_info['NominalRating']
                linecode['emergamps'] = linecode_info['FourthRating']
            else:
                logger.warning(
                    'linecode {} not found, so I\'m using default instead'.
                    format(dss['line_linecode']))
                linecode['r1'] = 0.4
                linecode['x1'] = 1.4
                linecode['r0'] = 0.4
                linecode['x0'] = 1.4
                linecode['normamps'] = 100
                linecode['emergamps'] = 120

            linecode['units'] = 'km'
            linecode['basefreq'] = 50.0
            linecode['nphases'] = 3

            with open(os.path.join(self._output_dir, 'linecode.dss'),
                      'a') as f:
                f.write(
                    'New Linecode.{} Nphases={} R1={} X1={} R0={} X0={} Units={} BaseFreq={:.1f} Normamps={} Emergamps={}\n'
                    .format(linecode['name'], linecode['nphases'],
                            linecode['r1'], linecode['x1'], linecode['r0'],
                            linecode['x0'], linecode['units'],
                            float(linecode['basefreq']), linecode['normamps'],
                            linecode['emergamps']))
                f.close()
            self._dss_ele['linecode'].append(dss['line_linecode_fixed'])

        # Save the line itself
        if 'line' not in self._dss_ele.keys():
            self._dss_ele['line'] = list()
        self._dss_ele['line'].append(dss['id'])

        dss['line_name'] = 'line_' + str(len(self._dss_ele['line']))
        with open(os.path.join(self._output_dir, 'lines.dss'), 'a') as f:
            f.write(
                'new Line.{} bus1={} bus2={} Linecode={} Length={} Phases={} Units={}\n'
                .format(dss['line_name'], dss['line_bus'][0],
                        dss['line_bus'][1], dss['line_linecode_fixed'],
                        dss['line_length'], dss['line_phases'],
                        dss['line_units']))
            f.close()
示例#7
0
    def save_opendss(self):
        # Clear all saved DSS elements
        self._dss_ele = dict()

        # Remove all OpenDSS files in output
        for file_name in os.listdir(self._output_dir):
            if file_name.split('.')[-1].lower() == 'dss':
                os.remove(os.path.join(self._output_dir, file_name))

        # To begin converting, find the substation
        ss = self.get_elements_by_tag('Substation')
        if len(ss) == 0:
            logger.error('no substation found')
            return
        elif len(ss) > 1:
            logger.warning('{} substations found'.format(len(ss)))
            for s in ss:
                ss_cn = self.get_elements_by_resource(s['id'],
                                                      'ConnectivityNode')
                if len(ss_cn) > 0:
                    ss = s
                    break
        else:
            ss = ss[0]
            # Find connectivity node that belongs to substation (i.e. root node)
            ss_cn = self.get_elements_by_resource(ss['id'], 'ConnectivityNode')

        if len(ss_cn) != 1:
            logger.error('{} connectivity nodes found for substation'.format(
                len(ss_cn)))
            return
        ss_cn = ss_cn[0]

        # Write the beginning of the master file
        with open(os.path.join(self._output_dir, 'master.dss'), 'w') as f:
            f.write('Clear\n\n')
            f.write('Set DefaultBaseFrequency=50.0\n\n')
            f.write('New Circuit.{}\n\n'.format(ss['name'].replace(' ', '_')))
            f.write(
                'Edit Vsource.Source Bus1={} BasekV=11.0 Frequency=50.0\n\n'.
                format(ss_cn['name']))
            f.close()

        # Start the network parsing process
        cn_list = [ss_cn]
        cn_old = list()

        terminal_list = list()
        terminal_old = list()

        saved_equipment = list()

        logger.debug(10 * '-' + ' PARSING ' + 10 * '-')

        while len(cn_list) > 0:
            cn_next = cn_list.pop()
            cn_old.append(cn_next)

            terminal_new = self.get_elements_by_resource(
                cn_next['id'], 'Terminal')
            terminal_new = self.remove_elements_from_set(
                terminal_old, terminal_new)
            terminal_list += terminal_new

            while len(terminal_list) > 0:
                terminal_next = terminal_list.pop()
                terminal_old.append(terminal_next)

                # Find the conducting equipment
                equipment = self.get_element_by_id(
                    terminal_next['ConductingEquipment'])
                cn_new, terminal_equipment = self.save_opendss_for_equipment(
                    equipment)

                # Make sure we ignore the terminals from the passed equipment
                terminal_equipment = self.remove_elements_from_set(
                    terminal_old, terminal_equipment)
                terminal_old += terminal_equipment

                # Make sure we only carry on with connectivity nodes that we have not already seen
                cn_new = self.remove_elements_from_set(cn_old, cn_new)
                cn_list += cn_new

        logger.debug(10 * '-' + ' PARSING DONE ' + 10 * '-')

        # Find all redirect files
        dss_redirect_files = [
            f for f in os.listdir(self._output_dir)
            if f.split('.')[-1] == 'dss'
        ]
        if 'master.dss' in dss_redirect_files:
            dss_redirect_files.remove('master.dss')
        if 'buscoords.dss' in dss_redirect_files:
            dss_redirect_files.remove('buscoords.dss')

        # Finish by adding all files to master
        with open(os.path.join(self._output_dir, 'master.dss'), 'a') as f:
            for dss_redirect_file in dss_redirect_files:
                f.write('Redirect {}\n'.format(dss_redirect_file))
            f.write('\nSet voltagebases=[0.24, 0.4, 11.0]\nCalcvoltagebases\n')
            f.write('\nBuscoords {}\n'.format('buscoords.dss'))
            f.write('\nSolve\n')
            f.close()

        logger.info('finished converting to DSS')