def apply(cls, stack, model, *args, **kwargs):

        if 'filename' in kwargs:
            filename = kwargs['filename']

        #If the file does not exist, do nothing and return the input model
        if not os.path.exists(filename):
            return model

        # If the file is empty also do nothing and return the input model
        try:
            df = pd.read_csv(filename)
        except pd.errors.EmptyDataError:
            logging.warning("Empty Dataframe loaded for {}".format(filename))
            return model

        #Create a CSV reader
        csv_reader = CSVReader()
        m2 = Store()
        csv_reader.parse(m2, filename)

        #Create a Modifier object
        modifier = Modifier()

        #Merge the two models
        new_model = modifier.merge(model, m2)

        #Return the new model
        return new_model
    def apply(cls, stack, model, load_path):
        loads = Store()
        reader = CsvReader()
        reader.parse(loads, load_path)

        modifier = Modifier()
        intermediate_model = modifier.merge(model, loads)
        return intermediate_model
    def apply(cls, stack, model, *args, **kwargs):

        rnm_name = None
        setpoint = None
        if 'rnm_name' in kwargs:
            rnm_name = kwargs['rnm_name']
        if 'setpoint' in kwargs:
            setpoint = kwargs['setpoint']
        for m in model.models:
            if isinstance(m, PowerTransformer) and hasattr(
                    m, 'name') and rnm_name.lower() in m.name:
                api_regulator = Regulator(model)
                api_regulator.name = 'reg_' + m.name
                api_regulator.from_element = m.from_element
                api_regulator.to_element = m.to_element
                api_regulator.positions = m.positions
                windings = []
                if m.windings is not None and len(m.windings) > 0:
                    for w in m.windings:
                        winding = Winding(model)
                        winding.nominal_voltage = w.nominal_voltage
                        winding.voltage_type = w.voltage_type
                        winding.resistance = w.resistance
                        winding.rated_power = w.rated_power
                        phase_windings = []
                        if w.phase_windings is not None and len(
                                w.phase_windings) > 0:
                            for p in w.phase_windings:
                                phase_winding = PhaseWinding(model)
                                phase_winding.phase = p.phase
                                phase_windings.append(phase_winding)
                        winding.phase_windings = phase_windings
                        windings.append(winding)
                api_regulator.windings = windings

                api_regulator.ltc = 0
                api_regulator.is_subtation = 0
                api_regulator.pt_ratio = float(
                    m.windings[0].nominal_voltage) / 120.0 / (3**0.5)
                api_regulator.ct_ratio = 100.0
                api_regulator.bandwidth = 2.0
                if setpoint is not None:
                    api_regulator.setpoint = setpoint
                modifier = Modifier()
                modifier.delete_element(model, m)

        return model
    def apply(cls, stack, model, load_path, base_dir=None):
        if base_dir and (not os.path.exists(load_path)):
            load_path = os.path.join(base_dir,load_path)

        dtypes = {'Load.positions[0].elevation': float,
                  'Load.rooftop_area': float,
                  'Load.num_levels': float,
                  'Load.num_users': float,
                  'Load.timeseries[0].interval': float,
                  'Load.timeseries[0].loaded': int}

        loads = Store()
        reader = CsvReader()
        reader.parse(loads, load_path, dtypes=dtypes)
        modifier = Modifier()
        intermediate_model = modifier.merge(model, loads)
        return intermediate_model
Example #5
0
    def apply(cls, stack, model, sub_internals):
        reduced_model = model
        modifier = Modifier()
        csv_reader = CsvReader()
        for sub in os.listdir(sub_internals):
            to_delete = Store()
            to_delete2 = Store()
            to_delete3 = Store()
            sub_lines = os.path.join(sub_internals, sub, 'internals_lines.csv')
            csv_reader.parse(to_delete, sub_lines)
            sub_nodes = os.path.join(sub_internals, sub, 'internals_nodes.csv')
            csv_reader.parse(to_delete2, sub_nodes)
            to_delete = modifier.add(to_delete, to_delete2)
            sub_transformers = os.path.join(sub_internals, sub,
                                            'internals_transformers.csv')
            csv_reader.parse(to_delete3, sub_transformers)
            to_delete = modifier.add(to_delete, to_delete3)

            reduced_model = modifier.delete(reduced_model, to_delete)

        substation = Store()
        final_model = reduced_model
        for sub in os.listdir(sub_internals):
            odss_reader = OpenDSSReader()
            sub_master = os.path.join(sub_internals, sub, 'master.dss')
            odss_reader.build_opendssdirect(sub_master)
            sub_bus_coords = os.path.join(sub_internals, sub, 'Buscoords.dss')
            odss_reader.set_dss_file_names({'Nodes': sub_bus_coords})
            odss_reader.parse(substation, verbose=True)

            final_model = modifier.add(final_model, substation)

        return final_model
Example #6
0
    def apply(cls,
              stack,
              model,
              random_percent=None,
              scale_factor=None,
              timeseries_path=None):
        if timeseries_path is not None:
            scale_factor_model = Store()
            reader = CsvReader()
            reader.parse(sc_factor_model, scaling_path)
            modifier = Modifier()
            intermediate_model = modifier.merge(model, scale_factor_model)
            return intermediate_model

        if random_percent == None:
            random_percent = 100

        if scale_factor is not None:
            scale_factor_model = Store()
            modifier = Modifier()
            total_loads = 0
            for obj_name in model.model_names:
                if isinstance(model.model_names[obj_name], Load):
                    total_loads = total_loads + 1

            num_selected = int(random_percent / 100.0 * total_loads)
            to_select = [1 for i in range(num_selected)
                         ] + [0 for i in range(total_loads - num_selected)]
            shuffle(to_select)
            load_cnt = 0
            for obj_name in model.model_names:
                if isinstance(model.model_names[obj_name],
                              Load) and to_select[load_cnt] == 1:
                    tmp_load = Load(scale_factor_model)
                    tmp_timeseries = Timeseries(scale_factor_model)
                    tmp_timeseries.scale_factor = scale_factor
                    tmp_load.name = obj_name
                    tmp_load.timeseries = [tmp_timeseries]
                    modifier.set_attributes(model, model.model_names[obj_name],
                                            tmp_load)

            model.set_names()
            return model
        else:
            logger.warn('no scaling factor set')
            return model
    def apply(cls, stack, model, *args, **kwargs):
        cutoff_length = None
        if 'cutoff_length' in kwargs:
            cutoff_length = kwargs['cutoff_length']
        if cutoff_length is None:
            return model

        from_element_cnt = {}
        for i in model.models:
            if isinstance(i,Line) and i.length is not None and i.length >cutoff_length and i.feeder_name is not None and i.feeder_name != 'subtransmission':
                # Option 1: put a switch at one of the intermediate nodes 1/10 of the way down the line
                if i.positions is not None and len(i.positions) >10:
                    node1 = Node(model)
                    node2 = Node(model)
                    pos = int(len(i.positions)/10)

                    node1.is_substation_connection = 0
                    node1.feeder_name = i.feeder_name
                    node1.substation_name = i.substation_name
                    node1.nominal_voltage = i.nominal_voltage
                    if i.from_element in from_element_cnt:
                        from_element_cnt[i.from_element]=from_element_cnt[i.from_element]+1
                    else:
                        from_element_cnt[i.from_element] = 1
                    node1.name = i.from_element+'_b1_'+str(from_element_cnt[i.from_element])
                    node1.positions = [i.positions[pos]]
                    node1_phases = []
                    for p in i.wires:
                        if p.phase is not None and p.phase in ['A','B','C','N']:
                            node1_phases.append(Unicode(p.phase))
                    node1.phases = node1_phases


                    node2.is_substation_connection = 0
                    node2.feeder_name = i.feeder_name
                    node2.substation_name = i.substation_name
                    node2.nominal_voltage = i.nominal_voltage
                    node2.name = i.from_element+'_b2_'+str(from_element_cnt[i.from_element])
                    node2.positions = [i.positions[pos+1]]
                    node2_phases = []
                    for p in i.wires:
                        if p.phase is not None and p.phase in ['A','B','C','N']:
                            node2_phases.append(Unicode(p.phase))
                    node2.phases = node2_phases

                    modifier = Modifier()
                    switch_break = modifier.copy(model,i)
                    switch_break.name = i.name+'_disconnect'
                    switch_break.length = 1 #1 meter
                    switch_break.positions = []
                    switch_break.from_element = node1.name
                    switch_break.to_element = node2.name
                    switch_break.is_switch = True
                    for wire in switch_break.wires:
                        wire.is_open=False

                    second_stretch = modifier.copy(model,i)
                    second_stretch.name = i.name+'_cont'
                    second_stretch.positions = i.positions[pos+2:]
                    second_stretch.from_element = node2.name
                    second_stretch.length = 9/10.0*i.length # rough estimate based on number of positions

                    i.to_element = node1.name
                    i.length = 1/10.0*i.length-1
                    if pos>0:
                        i.positions = i.positions[:pos-1]
                    else:
                        i.positions = []







                    


                    

        return model
    def apply(cls,
              stack,
              model,
              feeder_file,
              output_substation_folder,
              base_dir=None,
              readme_file=None,
              substation_folder=None):
        # Format is max number of feeders, list of substation numbers
        subs_4kv = [(4, [5]), (8, [13]), (12, [14])]
        subs_25kv = [(6, [11]), (12, [15])]
        subs_1247kv = [(1, [1]), (2, [8]), (3, [2]), (4, [4, 10]), (6, [9]),
                       (8, [3, 7]), (12, [6]), (16, [12])]
        sub_list = None
        logger.debug("Starting add_cyme_substations")
        if base_dir and (not os.path.exists(feeder_file)):
            feeder_file = os.path.join(base_dir, feeder_file)

        if substation_folder == None:
            substation_folder = os.path.join(os.path.dirname(__file__),
                                             'resources')
        elif base_dir and (not os.path.exists(output_substation_folder)):
            output_substation_folder = os.path.join(base_dir,
                                                    output_substation_folder)

        transformer_config = 'Y'  #Default is Delta-Wye. If specified we set it to be wye-wye
        if readme_file is not None and os.path.exists(readme_file):
            f = open(readme_file, 'r')
            info = f.readline().split()
            suffix = info[1][:-2]
            config = info[5]
            if config == 'delta-delta':
                transformer_config = 'D'
            kv = int(float(suffix) * 1000)
            if suffix == '12.47':
                suffix = '1247'
            suffix = '_' + suffix
            if kv == 4000:
                sub_list = subs_4kv
            elif kv == 25000:
                sub_list = subs_25kv
            else:
                sub_list = subs_1247kv
        else:
            kv = 12470
            suffix = '_1247'
            sub_list = subs_1247kv

        from_cyme_layer_dir = None
        # first look in stack
        for layer in stack:
            if layer.name == 'From CYME':
                from_cyme_layer_dir = layer.layer_dir
                break
        # then try this layer's library directory
        if from_cyme_layer_dir is None:
            from_cyme_layer_dir = os.path.join(
                os.path.dirname(os.path.dirname(__file__)), 'from_cyme')
        if not os.path.exists(from_cyme_layer_dir):
            msg = "Cannot find the 'From CYME' layer."
            logger.error(msg)
            raise Exception(msg)

        logger.debug("Building the model network")

        srcs = []
        for obj in model.models:
            if isinstance(obj, PowerSource) and obj.is_sourcebus == 1:
                srcs.append(obj.name)
        srcs = np.unique(srcs)
        if len(srcs) == 0:
            raise ValueError('No PowerSource object found in the model.')
        elif len(srcs) > 1:
            raise ValueError(
                'Mupltiple sourcebus found: {srcs}'.format(srcs=srcs))
        else:
            source = srcs[0]
        logger.debug("Identifying the Source Bus as {src}".format(src=source))
        ''' 
           Substation nodes have the form ***_1247. (or _4 or _25 for 4kv and 25kv systems) 
           The boundary of the substation is ***_69 on the high side.
           The boundary of the substation in a node with no x on the low side
           A feeder defines these boundaries.
           e.g. IHS0_1247->IDT706 is a substation of IHS0_1247 with a boundary of IDT706
           We remove everything between the high and low boundaries when updating a substation
        '''

        model.build_networkx(
            source)  # Used to remove internal edges in substation
        df = pd.read_csv(
            feeder_file, ' '
        )  #The RNM file specifying which feeders belong to which substation
        substation_boundaries_low = {}
        substation_boundaries_high = {}
        substation_transformers = {}
        substation_names = {}
        for index, row in df.iterrows():
            substation = row.iloc[1].lower()
            feeder = row.iloc[2].lower()
            transformer = row.iloc[3].lower()
            transformer = 'tr' + transformer[
                6:]  #The prefix is slightly different with the feeder.txt file
            buses = feeder.split('->')
            bus2 = buses[1]
            bus1 = buses[0]
            if substation in substation_boundaries_low:
                substation_boundaries_low[substation].add(
                    bus2 + '-' + bus1 + 'x'
                )  #Set the first node to be the location with one x sign as sometimes there isn't one with xx
                substation_names[bus2 + '-' + bus1 + 'x'] = substation
            else:
                substation_boundaries_low[substation] = set(
                    [bus2 + '-' + bus1 + 'x'])
                substation_names[bus2 + '-' + bus1 + 'x'] = substation

            if substation not in substation_boundaries_high:
                substation_boundaries_high[substation] = set([
                    substation.replace(suffix, '_69')
                ])  # All substations are from 69kV

            if substation not in substation_transformers:
                substation_transformers[substation] = set([transformer])

        print(substation_names)
        for sub in substation_boundaries_low.keys(
        ):  #sub is the name of the substation and substation_boundaries_low[sub] is a set of all the connected feeders
            logger.debug("Building to_delete and modifier")
            to_delete = Store()
            modifier = Modifier()
            logger.info(
                "Processing substation {}. There are {} in total.".format(
                    sub, len(substation_boundaries_low)))

            all_boundaries = substation_boundaries_low[sub].union(
                substation_boundaries_high[sub])

            # TODO: do a search from the high boundary to the low boundary and include everything inside
            #get_internal_nodes(substation_boundaries_high[sub],substation_boundaries_low[sub])

            feeder_names = list(
                substation_boundaries_low[sub]
            )  # Feeder point of connection to the substation
            internal_nodes = [
                i for i in model.model_names
                if sub in i and isinstance(model[i], Node)
            ]
            internal_edges = [
                i for i in model.model_names
                if sub in i and (isinstance(model[i], Line)
                                 or isinstance(model[i], PowerTransformer))
            ]
            tmp_include_edges = []
            for i in internal_edges:
                if model[i].from_element in feeder_names:
                    internal_nodes.append(model[i].from_element)
                if model[i].to_element in feeder_names:
                    internal_nodes.append(model[i].to_element)
                if 'mv' in model[i].to_element and 'mv' in model[
                        i].from_element:
                    tmp_include_edges.append(
                        i
                    )  #Used to address MV loads attached directly to the substation
            for i in tmp_include_edges:
                internal_edges.remove(i)

            high_boundary = list(substation_boundaries_high[sub])[
                0]  #Should only be one high side boundary point in the set
            internal_nodes.append(high_boundary)
            """
            #import pdb;pdb.set_trace()
            all_nodes.append(sub+'-'+high_boundary+'xxx')
            all_nodes.append(sub+'-'+high_boundary+'xx')
            all_nodes.append(sub+'-'+high_boundary+'x')
            all_nodes.append(sub)
            for low_val in substation_boundaries_low[sub]:
                all_nodes.append(low_val)  # End at these nodes. No reclosers after these.
                last_node = low_val+'-'+sub+'x'
                last_node_rev = sub+'-'+low_val+'x'
                if last_node in model.model_names:
                    all_nodes.append(last_node) #The connection point
                if last_node_rev in model.model_names:
                    all_nodes.append(last_node_rev) #The connection point if the name is backwards. Dypically with UMV nodes
                if last_node+'x' in model.model_names:
                    all_nodes.append(last_node+'x') #This extra node often exists 
                if last_node+'_p' in model.model_names:
                    all_nodes.append(last_node+'_p') #This extra node sometimes exists 
                if last_node+'xx' in model.model_names:
                    all_nodes.append(last_node+'xx') #This extra node sometimes exists too
            """
            all_nodes_set = set(internal_nodes)
            #internal_edges = model.get_internal_edges(all_nodes_set)
            #import pdb;pdb.set_trace()

            # These attribuetes will be used to inform which substation is selected
            rated_power = None
            emergency_power = None
            loadloss = None
            noloadloss = None
            reactance = None
            lat = None
            long = None
            transformer = list(substation_transformers[sub])[
                0]  # Assume only one transformer per substation in RNM

            # Currently using CYME reader which has different names for the transformers to the feeders.txt file
            #            reactance = model[transformer].reactances[0] # Assume the same for all windings in a substation
            #            loadloss = model[transformer].loadloss
            #            noloadloss = model[transformer].noload_loss
            #            rated_power = model[transformer].windings[0].rated_power # Assume the same for all windings in a substation
            #            emergency_power = model[transformer].windings[0].emergency_power # Assume the same for all windings in a substation
            try:
                lat = model[sub].positions[0].lat
                long = model[sub].positions[0].long
            except:
                raise ValueError('{} missing position elements'.format(
                    model[sub].name))

        # import pdb;pdb.set_trace()
        # Mark the internals of the substation for deletion
            for n in all_nodes_set:
                if not n in model.model_names:
                    continue
#                is_endpoint = False
#                for key in substation_boundaries_low:
#                    if n in substation_boundaries_low[key]: #Don't delete the boundaries of the substation
#                        is_endpoint = True
#                        break
#                if is_endpoint:
#                    continue
                obj_name = type(model[n]).__name__
                base_obj = globals()[obj_name](to_delete)
                base_obj.name = n
            for e in internal_edges:
                if not e in model.model_names:
                    continue
                if model[
                        e].from_element in feeder_names:  #Don't remove edge from bus2-bus1-x to the first distribution transformer
                    continue
                obj_name = type(model[e]).__name__
                base_obj = globals()[obj_name](to_delete)
                base_obj.name = e

            num_model_feeders = len(feeder_names)
            not_allocated = True

            low_voltage = kv
            high_voltage = 69000

            #import pdb;pdb.set_trace()
            # Read the CYME models
            random.seed(0)
            for element in sub_list:  # Insert extra logic here to determine which one to use
                if num_model_feeders > element[0]:
                    continue

                sub_file = 'sb' + str(element[1][random.randint(
                    0,
                    len(element[1]) - 1)])
                print(sub_file)
                sub_model = Store()
                reader = CymeReader(data_folder_path=os.path.join(
                    os.path.dirname(__file__), 'resources', sub_file))
                reader.parse(sub_model)
                sub_model.set_names()

                # Set the nominal voltages within the substation using the cyme model transformer and source voltage
                modifier = system_structure_modifier(sub_model)
                modifier.set_nominal_voltages_recur()

                # Determine which nodes in the CYME model connect feeders/HV
                all_substation_connections = []
                available_feeders = 0
                for i in sub_model.models:
                    if isinstance(i, Node) and hasattr(
                            i, 'is_substation_connection'
                    ) and i.is_substation_connection == 1:
                        all_substation_connections.append(i)
                        i.setpoint = 1.03
                        try:
                            if i.nominal_voltage == low_voltage:
                                available_feeders += 1
                        except:
                            raise ValueError(
                                "Nominal Voltages not set correctly in substation"
                            )

                logger.info(
                    "Processing substation {}. There are {} feeders available and {} feeders in RNM"
                    .format(sub, available_feeders, num_model_feeders))
                if available_feeders >= num_model_feeders:
                    boundry_map = {}
                    feeder_cnt = 0
                    ref_lat = 0
                    ref_long = 0
                    for i in sub_model.models:
                        # Remove the source node. This is no longer needed
                        if isinstance(i, PowerSource):
                            sub_model.remove_element(i)

                        # Set a random node as the reference node
                        if isinstance(i, Node) and hasattr(
                                i, 'positions'
                        ) and len(i.positions) > 0 and hasattr(
                                i.positions[0], 'lat'
                        ) and i.positions[0].lat is not None and hasattr(
                                i.positions[0],
                                'long') and i.positions[0].long is not None:
                            ref_lat = i.positions[0].lat
                            ref_long = i.positions[0].long

                    substation_name = substation_names[feeder_names[0].lower()]
                    for i in sub_model.models:

                        # Remove feeder name from substation elements. This is normally set automatically when reading from CYME
                        if hasattr(i, 'feeder_name'):
                            i.feeder_name = ''
                        if hasattr(i, 'substation_name'):
                            i.substation_name = substation_name
                        if hasattr(i, 'is_substation'):
                            i.is_substation = True

                        if isinstance(
                                i, PowerTransformer
                        ) and transformer_config == 'D' and i.windings is not None and len(
                                i.windings) == 2 and i.windings[1] is not None:
                            i.windings[1].connection_type = 'D'

                        # Assign feeder names to the endpoints of the substation
                        if isinstance(i, Node) and hasattr(
                                i, 'is_substation_connection'
                        ) and i.is_substation_connection == 1:
                            if hasattr(
                                    i, 'nominal_voltage'
                            ) and i.nominal_voltage is not None and i.nominal_voltage >= high_voltage:

                                #########  USE no_feeders.txt to inform how many connection points there should be. Skip this substation if the number isn't correct-->>>>
                                #### Need to discuss with Carlos
                                boundry_map[i.name] = high_boundary
                                i.setpoint = 1.0
                                i.name = high_boundary  #TODO: issue of multiple high voltage inputs needs to be addressed
                                #i.feeder_name = 'subtransmission'
                                i.substation_name = substation_name
                                i.is_substation = False
                            elif hasattr(
                                    i, 'nominal_voltage'
                            ) and i.nominal_voltage is not None and i.nominal_voltage < high_voltage:
                                feeder_cnt += 1
                                if feeder_cnt <= num_model_feeders:
                                    endpoint = feeder_names[feeder_cnt -
                                                            1].split('-')[0]
                                    if 'mv' in endpoint:  #The node names for these are reversed for some reason
                                        boundry_map[
                                            i.
                                            name] = substation_name + '-' + endpoint + 'x'
                                        i.name = substation_name + '-' + endpoint + 'x'
                                        if i.name in all_nodes_set:
                                            all_nodes_set.remove(i.name)
                                    else:
                                        boundry_map[i.name] = feeder_names[
                                            feeder_cnt - 1]
                                        i.name = feeder_names[feeder_cnt -
                                                              1].lower()
                                    i.substation_name = substation_name
                                    i.is_substation = False
                                    i.feeder_name = i.substation_name + '->' + endpoint
                                    #import pdb;pdb.set_trace()
                                    if i.substation_name + '->' + endpoint in model.model_names:  # Set the Feedermetadata headnode to be the correct name.
                                        model[i.substation_name + '->' +
                                              endpoint].headnode = i.name
                                else:
                                    i.name = str(
                                        sub_file + '_' + sub + '_' + i.name
                                    ).lower(
                                    )  #ie. No feeders assigned to this berth so using the substation identifiers
                            else:
                                raise ValueError(
                                    "Nominal Voltages not set correctly in substation"
                                )

                        elif hasattr(i, 'name') and (not isinstance(
                                i, Feeder_metadata)):
                            i.name = str(sub_file + '_' + sub + '_' +
                                         i.name).lower()
                        if isinstance(i, Regulator) and hasattr(
                                i, 'connected_transformer'
                        ) and i.connected_transformer is not None:
                            i.connected_transformer = str(
                                sub_file + '_' + sub + '_' +
                                i.connected_transformer).lower()

                        if hasattr(
                                i,
                                'from_element') and i.from_element is not None:
                            if i.from_element in boundry_map:
                                i.from_element = boundry_map[i.from_element]
                            else:
                                i.from_element = str(sub_file + '_' + sub +
                                                     '_' +
                                                     i.from_element).lower()
                        if hasattr(i,
                                   'to_element') and i.to_element is not None:
                            if i.to_element in boundry_map:
                                i.to_element = boundry_map[i.to_element]
                            else:
                                i.to_element = str(sub_file + '_' + sub + '_' +
                                                   i.to_element).lower()

                        if hasattr(
                                i, 'positions'
                        ) and i.positions is not None and len(i.positions) > 0:
                            # import pdb;pdb.set_trace()
                            if ref_long == 0 and ref_lat == 0:
                                logger.warning(
                                    "Warning: Reference co-ords are (0,0)")
                            scale_factor = 1
                            if element[
                                    0] >= 12:  # The larger substations were created with a strange scale factor
                                scale_factor = 1 / 50.0
                            i.positions[0].lat = scale_factor * 7 * (
                                i.positions[0].lat - ref_lat) + lat
                            i.positions[0].long = scale_factor * 10 * (
                                i.positions[0].long - ref_long) + long
                            if len(i.positions) > 1:
                                for k in range(1, len(i.positions)):
                                    i.positions[k].lat = scale_factor * 7 * (
                                        i.positions[k].lat - ref_lat) + lat
                                    i.positions[k].long = scale_factor * 10 * (
                                        i.positions[k].long - ref_long) + long


#import pdb;pdb.set_trace()
                    not_allocated = False
                    sub_model.set_names()
                    to_delete.set_names()
                    reduced_model = modifier.delete(model, to_delete)
                    logger.info("Adding model from {} to model".format(
                        substation_folder))
                    #import pdb;pdb.set_trace()
                    model = modifier.add(
                        reduced_model, sub_model
                    )  #Is it a problem to be modifying the model directly?
                    break
            if not_allocated:
                raise ValueError(
                    'Substation too small. {num} feeders needed.  Exiting...'.
                    format(num=num_model_feeders))

        model.set_names()
        logger.debug("Returning {!r}".format(model))
        return model
Example #9
0
    def apply(cls, stack, model, feeder_file, output_substation_folder, base_dir=None, substation_folder=None):
        logger.debug("Starting add_substations")
        if base_dir and (not os.path.exists(feeder_file)):
            feeder_file = os.path.join(base_dir,feeder_file)
        if base_dir and (not os.path.exists(output_substation_folder)):
            output_substation_folder = os.path.join(base_dir,output_substation_folder)

        if substation_folder == None:
            substation_folder = os.path.join(os.path.dirname(__file__),'resources')

        # Need to load OpenDSS files later. Make sure we can find the required layer.
        from_opendss_layer_dir = None
        # first look in stack
        for layer in stack:
            if layer.name == 'From OpenDSS':
                from_opendss_layer_dir = layer.layer_dir
                break
        # then try this layer's library directory
        if from_opendss_layer_dir is None:
            from_opendss_layer_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)),'from_opendss')        
        if not os.path.exists(from_opendss_layer_dir):
            msg = "Cannot find the 'From OpenDSS' layer."
            logger.error(msg)
            raise Exception(msg)

        logger.debug("Building the model network")

        model.build_networkx(source=None) # Used to remove internal edges in substation
        df = pd.read_csv(feeder_file,' ') #The RNM file specifying which feeders belong to which substation
        substations = {}
        for index,row in df.iterrows():
            substation = row.iloc[1]
            feeder = row.iloc[2]
            buses = feeder.split('->')
            bus1 = buses[1]
            bus2 = buses[0]
            if bus1[0:4].lower() == 'ucmv': # Not swapped if MV load connected to it
                bus1 = buses[0]
                bus2 = buses[1]
            adjusted_feeder = bus1+'->'+bus2 #In the feeder file bus1 and bus2 are swapped
            if substation in substations:
                substations[substation].add(adjusted_feeder)
            else:
                substations[substation]=set([adjusted_feeder])

        logger.debug("Building to_delete and modifier")

        to_delete = Store()
        modifier = Modifier()
        for sub in substations: #sub is the name of the substation and substations[sub] is a set of all the connected feeders
            logger.debug("Processing substation {}. There are {} in total.".format(sub,len(substations)))

            all_nodes = []
            subname = sub.replace('.','')
            subname = subname.lower()
            all_nodes.append(subname)
            hv_subname = subname+'->'+subname.replace('1247','69')+'_s'
            all_nodes.append(hv_subname)
            sourcenode = hv_subname+'_s' #Source point of connection to the substation
            all_nodes.append(sourcenode)
            feeder_names = [] # Feeder point of connection to the substation
            rated_power = None
            emergency_power = None
            loadloss = None
            noloadloss = None
            reactance = None
            for feeder in substations[sub]:
                feeder_name = feeder.replace('.','')+'_s'
                feeder_name = feeder_name.lower()
                feeder_names.append(feeder_name)
                all_nodes.append(feeder_name)

            all_nodes_set = set(all_nodes)
            internal_edges = model.get_internal_edges(all_nodes_set)
            for n in all_nodes_set:
                obj_name = type(model[n]).__name__
                base_obj = globals()[obj_name](to_delete)
                base_obj.name = n
            for e in internal_edges:
                obj_name = type(model[e]).__name__
                if obj_name == 'PowerTransformer':
                    reactance = model[e].reactances[0] # Assume the same for all windings in a substation
                    loadloss = model[e].loadloss
                    noloadloss = model[e].noload_loss
                    rated_power = model[e].windings[0].rated_power # Assume the same for all windings in a substation
                    emergency_power = model[e].windings[0].emergency_power # Assume the same for all windings in a substationr

                base_obj = globals()[obj_name](to_delete)
                base_obj.name = e

            num_model_feeders = len(substations[sub])
            not_allocated = True
            # Write the substation files to disk. These are then read and added            
            for sub_file in os.listdir(substation_folder): # Important these must be listed in increasing order
                if len(pd.read_csv(substation_folder+'/%s/feeders.csv'%sub_file))>= num_model_feeders:
                    generic_source = list(pd.read_csv(substation_folder+'/%s/source.csv'%sub_file)['source'])
                    generic_feeders = list(pd.read_csv(substation_folder+'/%s/feeders.csv'%sub_file)['feeders'])[:num_model_feeders] #Select the first n feeder bays of the substation as required
                    generic_nodes = list(pd.read_csv(substation_folder+'/%s/all_nodes.csv'%sub_file)['node'])
                    generic_substation_fp = open(substation_folder+'/%s/%s.dss'%(sub_file,sub_file),'r')
                    generic_substation_dss = generic_substation_fp.read()
                    generic_substation_fp.close()
                    substation_dss = generic_substation_dss.replace(generic_source[0],'%s'%sourcenode) # Replace source node
                    for i in range(len(feeder_names)):
                        substation_dss = substation_dss.replace(generic_feeders[i],'%s'%feeder_names[i]) # Replace feeder nodes

                    # TODO: do this in a better way.
                    for i in range(len(generic_nodes)): #Replace any remaining nodes that haven't been changed yet. Unallocated feeder heads are managed here
                        substation_dss = substation_dss.replace(generic_nodes[i]+ ' ','%s_%s_%s '%(sub_file,subname,generic_nodes[i]))
                        substation_dss = substation_dss.replace(generic_nodes[i]+ '.','%s_%s_%s.'%(sub_file,subname,generic_nodes[i]))
                    substation_dss = substation_dss.replace('Line.','Line.%s_%s_'%(sub_file,subname))
                    substation_dss = substation_dss.replace('LineCode.','LineCode.%s_%s_'%(sub_file,subname))
                    substation_dss = substation_dss.replace('Capacitor.','Capacitor.%s_%s_'%(sub_file,subname))
                    substation_dss = substation_dss.replace('CapControl.','CapControl.%s_%s_'%(sub_file,subname))
                    substation_dss = substation_dss.replace('Monitor.','Monitor.%s_%s_'%(sub_file,subname))
                    substation_dss = substation_dss.replace('Relay.','Relay.%s_%s_'%(sub_file,subname))
                    substation_dss = substation_dss.replace('Transformer.','Transformer.%s_%s_'%(sub_file,subname))
                    substation_dss = substation_dss.replace('transformer=','transformer=%s_%s_'%(sub_file,subname))
                    substation_dss = substation_dss.replace('Regcontrol.','Regcontrol.%s_%s_'%(sub_file,subname))

                    # TODO: WARNING: This is a total hack to replace the substation attributes and should not be used long-term.
                    # This is very specific to the substations used in dataset3 and is very case sensative.
                    substation_dss = substation_dss.replace('kvas=(30000, 30000)','kvas=(%f, %f)'%(rated_power,rated_power))
                    substation_dss = substation_dss.replace('kvas=(25000, 25000)','kvas=(%f, %f)'%(rated_power/3.0,rated_power/3.0))
                    substation_dss = substation_dss.replace('%noloadloss=0.12','%noloadloss={noll}'.format(noll=noloadloss))
                    substation_dss = substation_dss.replace('%loadloss=0.1','%loadloss={ll}'.format(ll=loadloss))
                    substation_dss = substation_dss.replace('XHL=0.1','XHL=%f'%(reactance))

                    if not os.path.isdir(output_substation_folder+'/%s'%subname):
                        os.makedirs(output_substation_folder+'/%s'%subname)
                    substation_output = open(output_substation_folder+'/%s/substation.dss'%(subname),'w')
                    substation_output.write(substation_dss)
                    substation_output.close()
                    buscoords = open(output_substation_folder+'/%s/Buscoords.dss'%subname,'w')
                    buscoords.close()
                    masterfile_fp = open(substation_folder+'/%s/master.dss'%sub_file,'r')
                    masterfile_dss = masterfile_fp.read()
                    masterfile_fp.close()
                    masterfile_dss = masterfile_dss.replace('SourceBus',sourcenode)
                    master_output = open(output_substation_folder+'/%s/master.dss'%subname,'w')
                    master_output.write(masterfile_dss)
                    master_output.close()
                    #shutil.copyfile(substation_folder+'/%s/master.dss'%sub_file,output_substation_folder+'/%s/master.dss'%subname)
                    not_allocated = False
                    break
            if not_allocated:
                raise('Substation too small. %d feeders needed.  Exiting...'%(num_model_feeders))

        logger.debug("Creating reduced and final models")

        reduced_model = modifier.delete(model, to_delete) 
        final_model = reduced_model
        from_opendss_layer = Layer(from_opendss_layer_dir)
        from_opendss_layer.args.mode = ArgMode.USE
        from_opendss_layer.kwargs.mode = ArgMode.USE
        for p, dirnames, filenames in os.walk(output_substation_folder):
            for sub_folder in dirnames:
                logger.debug("Processing output_substation_folder/{}".format(sub_folder))
                from_opendss_layer.args[0] = os.path.join(output_substation_folder,sub_folder,'master.dss')
                from_opendss_layer.args[1] = os.path.join(output_substation_folder, sub_folder, 'Buscoords.dss')
                from_opendss_layer.kwargs['read_power_source'] = False
                s = Stack()
                from_opendss_layer.run_layer(s)
                substation_model = s.model
                logger.debug("Adding model from {} to final_model".format(sub_folder))
                final_model = modifier.add(final_model, substation_model)
            break
        logger.debug("Returning {!r}".format(final_model))
        return final_model