コード例 #1
0
def check_loads_connected(model, verbose=True):
    all_sources = []
    all_loads = set()
    load_source_map = {}
    result = True

    for i in model.models:
        if isinstance(i, PowerSource) and i.connecting_element is not None:
            all_sources.append(i)
        elif isinstance(i, PowerSource):
            print(
                'Warning - a PowerSource element has a None connecting element'
            )
        if isinstance(i, Load):
            all_loads.add(i)
            load_source_map[i.name] = []

    if len(all_sources) == 0:
        print('Model does not contain any power source')
        return False

    for source in all_sources:
        ditto_graph = Network()
        ditto_graph.build(model, source.connecting_element)
        ditto_graph.set_attributes(model)
        ditto_graph.remove_open_switches(
            model)  # This deletes the switches inside the networkx graph only
        source_name = source.connecting_element
        all_paths = nx.single_source_shortest_path(ditto_graph.graph,
                                                   source_name)

        for load in all_loads:
            min_dist = float('inf')
            load_connection = load.connecting_element
            if load_connection in all_paths:
                load_source_map[load.name].append(source_name)

    result = True
    sourceless_loads = []
    multi_source_loads = {}
    for load in load_source_map:
        if len(load_source_map[load]) == 0:
            result = False
            sourceless_loads.append(load)
        if len(load_source_map[load]) > 1:
            result = False
            multi_source_loads[load.name] = load_source_map[load]

    if verbose:
        if len(sourceless_loads) > 0:
            print('Loads missing sources:')
            for load in sourceless_loads:
                print(load)

        if len(multi_source_loads) > 0:
            print('Loads with multiple sources:')
            for load in multi_source_loads:
                print(load + ': ' + multi_source_loads[load])

    return result
コード例 #2
0
ファイル: check_loops.py プロジェクト: mmdione/ditto
def check_loops(model, verbose=True):
    all_sources = []
    for i in model.models:
        if isinstance(i,PowerSource) and i.connecting_element is not None:
            all_sources.append(i)
        elif isinstance(i,PowerSource):
            print('Warning - a PowerSource element has a None connecting element')

    # TODO: Address issue in network.py where a single source is used to determine bfs order
    if len(all_sources) == 0:
        raise('Model does not contain any power source. Required to build networkx graph')

    for source in all_sources:
        print('Checking loops for source '+source.name)
        ditto_graph = Network()
        ditto_graph.build(model,source.connecting_element)
        ditto_graph.set_attributes(model)
        ditto_graph.remove_open_switches(model) # This deletes the switches inside the networkx graph only
    
        loops = nx.cycle_basis(ditto_graph.graph)
        number_of_loops = len(loops)
        if number_of_loops > 0:
            if verbose:
                print('Loops found:')
                print(loops)
        if number_of_loops == 0:
            return True
    return False
コード例 #3
0
def check_transformer_phase_path(model,
                                 needs_transformers=False,
                                 verbose=True):
    all_sources = []
    all_transformers = set()
    all_loads = set()
    load_transformer_map = {
    }  # Not used but provides a mapping from load to transformer
    result = True

    for i in model.models:
        if isinstance(i, PowerSource) and i.connecting_element is not None:
            all_sources.append(i)
        elif isinstance(i, PowerSource):
            print(
                'Warning - a PowerSource element has a None connecting element'
            )
        if isinstance(i, PowerTransformer):
            all_transformers.add(i)
        if isinstance(i, Load):
            all_loads.add(i)

    if len(all_sources) > 1:
        print('Warning - using first source to orient the network')
    if len(all_sources) == 0:
        print('Model does not contain any power source')
        return False

    for source in all_sources:
        ditto_graph = Network()
        ditto_graph.build(model, source.connecting_element)
        ditto_graph.set_attributes(model)
        ditto_graph.remove_open_switches(
            model)  # This deletes the switches inside the networkx graph only
        source_name = source.connecting_element
        all_paths = nx.single_source_shortest_path(ditto_graph.graph,
                                                   source_name)
        break_load = False
        for load in all_loads:
            load_connection = load.connecting_element
            if load_connection in all_paths:

                ### check that each load has a path to the substation
                path = all_paths[load_connection]
                #                print(load_connection,path)
                num_transformers = 0
                transformer_names = []

                ### check that only zero or one transformers are on the path from load to source (exclude regulators)
                transformer_low_side = None
                for i in range(len(path) - 1, 0, -1):
                    element = ditto_graph.graph[path[i]][path[i - 1]]
                    if element['equipment'] == 'PowerTransformer' and not element[
                            'is_substation']:  #TODO: check if the transformer is part of a regulator. Shouldn't be a problem but could be depending on how regulator defined
                        transformer_names.append(element['name'])
                        num_transformers += 1
                    if num_transformers == 0 and not element[
                            'equipment'] == 'PowerTransformer':
                        transformer_low_side = path[i - 1]

                ### Check that the low side of the transformer is connected to a line that leads to a load
                if num_transformers == 1:
                    load_transformer_map[load.name] = element['name']
                    if model[transformer_names[
                            0]].to_element != transformer_low_side:
                        if verbose:
                            print('Load ' + load.name +
                                  ' has connected transformer of ' +
                                  transformer_names[0] +
                                  ' incorrectly connected (likely backwards)')
                        result = False
                elif num_transformers == 0 and needs_transformers:
                    if verbose:
                        print('Load ' + load.name +
                              ' has no transformers connected.')
                    result = False
                elif num_transformers > 2:
                    result = False
                    if verbose:
                        print('Load ' + load.name +
                              ' has the following transformers connected: ')
                        for trans in transformer_names:
                            print(trans)

                if num_transformers == 1 and not needs_transformers:
                    print(
                        'Warning - transformer found for system where no transformers required between load and customer'
                    )

                if num_transformers == 1:
                    low_phases = [
                        phase_winding.phase for phase_winding in model[
                            transformer_names[0]].windings[1].phase_windings
                    ]
                    high_phases = [
                        phase_winding.phase for phase_winding in model[
                            transformer_names[0]].windings[0].phase_windings
                    ]
                    prev_line_phases = ['A', 'B', 'C'
                                        ]  # Assume 3 phase power at substation

                    ### If there is a transformer, check that the phases on the low side are consistent with the transformer settign
                    for i in range(len(path) - 1, 0, -1):
                        element = ditto_graph.graph[path[i]][path[i - 1]]
                        if element[
                                'equipment'] == 'PowerTransformer' and not element[
                                    'is_substation']:
                            break
                        if element['equipment'] == 'Line':
                            line_phases = [
                                wire.phase for wire in element['wires']
                                if wire.phase != 'N'
                            ]  #Neutral phases not included in the transformer
                            if not set(line_phases) == set(
                                    low_phases
                            ):  #Low phase lines must match transformer exactly
                                if verbose:
                                    print(
                                        'Load ' + load.name +
                                        ' has incorrect phases on low side of transformer for line '
                                        + element['name'])
                                result = False
                                break
                        elif element['equipment'] != 'Regulator':
                            print('Warning: element of type ' +
                                  element['equipment'] +
                                  ' found on path to load ' + load.name)

                    ### If there is a transformer, check that there phases on the high side are consistent with the transformer setting, and increase until the substation
                    for i in range(len(path) - 1):
                        element = ditto_graph.graph[path[i]][path[i + 1]]
                        if element[
                                'equipment'] == 'PowerTransformer' and not element[
                                    'is_substation']:
                            break
                        if element['equipment'] == 'Line':
                            line_phases = [
                                wire.phase for wire in element['wires']
                                if wire.phase != 'N'
                            ]  #Neutral phases not included in the transformer
                            if not set(high_phases).issubset(
                                    set(line_phases)
                            ):  #MV phase line phase must be able to support transformer phase
                                if verbose:
                                    print(
                                        'Load ' + load.name +
                                        ' has incorrect phases on high side of transformer for line '
                                        + element['name'])
                                result = False
                                break
                            if len(line_phases) > len(prev_line_phases):
                                if verbose:
                                    print(
                                        'Number of phases increases along line '
                                        + element['name'] + ' from ' +
                                        str(len(prev_line_phases)) + ' to ' +
                                        str(len(line_phases)))
                                result = False
                                break
                            prev_line_phases = line_phases
                        elif element['equipment'] != 'Regulator':
                            print('Warning: element of type ' +
                                  element['equipment'] +
                                  ' found on path to load ' + load.name)

                if num_transformers == 0:
                    prev_line_phases = ['A', 'B', 'C'
                                        ]  # Assume 3 phase power at substation

                    ### If there is no transformer, check that there phases increase until the substation
                    for i in range(len(path) - 1):
                        element = ditto_graph.graph[path[i]][path[i + 1]]
                        if element['equipment'] == 'Line':
                            line_phases = [
                                wire.phase for wire in element['wires']
                                if wire.phase != 'N'
                            ]  #Neutral phases not included in the transformer
                            if len(line_phases) > len(prev_line_phases):
                                if verbose:
                                    print(
                                        'Number of phases increases along line '
                                        + element['name'] + ' from ' +
                                        str(len(prev_line_phases)) + ' to ' +
                                        str(len(line_phases)))
                                result = False
                                break
                            prev_line_phases = line_phases
                        elif element['equipment'] != 'Regulator' and element[
                                'equipment'] != 'PowerTransformer':
                            print('Warning: element of type ' +
                                  element['equipment'] +
                                  ' found on path to load ' + load.name)
    return result
コード例 #4
0
def fix_transformer_phase_path(model, needs_transformers=False, verbose=True):
    all_sources = []
    all_transformers = set()
    all_loads = set()
    load_transformer_map = {
    }  # Not used but provides a mapping from load to transformer
    result = True

    for i in model.models:
        if isinstance(i, PowerSource) and i.connecting_element is not None:
            all_sources.append(i)
        elif isinstance(i, PowerSource):
            print(
                'Warning - a PowerSource element has a None connecting element'
            )
        if isinstance(i, PowerTransformer):
            all_transformers.add(i)
        if isinstance(i, Load):
            all_loads.add(i)

    if len(all_sources) > 1:
        print('Warning - using first source to orient the network')
    if len(all_sources) == 0:
        print('Model does not contain any power source')
        return

    for source in all_sources:
        ditto_graph = Network()
        ditto_graph.build(model, source.connecting_element)
        ditto_graph.set_attributes(model)
        ditto_graph.remove_open_switches(
            model)  # This deletes the switches inside the networkx graph only
        source_name = source.connecting_element
        all_paths = nx.single_source_shortest_path(ditto_graph.graph,
                                                   source_name)
        break_load = False
        for load in all_loads:
            load_connection = load.connecting_element
            if load_connection in all_paths:

                ### check that each load has a path to the substation
                path = all_paths[load_connection]
                num_transformers = 0
                transformer_names = []

                ### Fix the low side of the transformer is connected to a line that leads to a load if possible
                transformer_low_side = None
                for i in range(len(path) - 1, 0, -1):
                    element = ditto_graph.graph[path[i]][path[i - 1]]
                    if element['equipment'] == 'PowerTransformer' and not element[
                            'is_substation']:  #TODO: check if the transformer is part of a regulator. Shouldn't be a problem but could be depending on how regulator defined
                        transformer_names.append(element['name'])
                        num_transformers += 1
                    if num_transformers == 0 and not element[
                            'equipment'] == 'PowerTransformer':
                        transformer_low_side = path[i - 1]

                ### Check that the low side of the transformer is connected to a line that leads to a load
                if num_transformers == 1:
                    load_transformer_map[load.name] = element['name']
                    if model[transformer_names[
                            0]].to_element != transformer_low_side:
                        print(
                            'Load ' + load.name +
                            ' has connected transformer of ' +
                            transformer_names[0] +
                            ' incorrectly connected (likely backwards). Trying to rotate...'
                        )
                        if model[transformer_names[
                                0]].from_element == transformer_low_side:
                            from_element_orig = model[
                                transformer_names[0]].from_element
                            to_element_orig = model[
                                transformer_names[0]].to_element
                            model[transformer_names[
                                0]].from_element = to_element_orig
                            model[transformer_names[
                                0]].to_element = from_element_orig
                            print('Succeeded.')
                        else:
                            print('Failed.')
コード例 #5
0
def check_unique_path(model, needs_transformers=False, show_all=False, verbose =True):
    all_sources = []
    all_transformers = set()
    all_loads = set()
    result = True

    for i in model.models:
        if isinstance(i,PowerSource) and i.connecting_element is not None:
            all_sources.append(i)
        elif isinstance(i,PowerSource):
            print('Warning - a PowerSource element has a None connecting element')
        if isinstance(i,PowerTransformer):
            all_transformers.add(i)
        if isinstance(i,Load):
            all_loads.add(i)

    if len(all_sources) > 1:
        print('Warning - using first source to orient the network')
    if len(all_sources) == 0:
        print('Model does not contain any power source')
        return False

    load_transformer_map = {}
    all_bad_loads = []

    for load in all_loads:
        load_transformer_map[load.name] = []
    for source in all_sources:
        ditto_graph = Network()
        ditto_graph.build(model,source.connecting_element)
        ditto_graph.set_attributes(model)
        ditto_graph.remove_open_switches(model) # This deletes the switches inside the networkx graph only
        source_name = source.connecting_element
        break_load = False
        for load in all_loads:
            
            ### check there is a unique path from each load to the source
            two_paths = nx.all_simple_paths(ditto_graph.graph, source_name, load.name) # Warning - this can take a very long time if the graph is has large numbers of loops (shouldn't hold for distribution systems)
            num_paths = 0
            if break_load:
                break
            for candidate in two_paths:
                num_paths +=1
                if num_paths > 1:
                    print('Multiple paths from load '+load.name+' to '+source_name)
                    result = False
                    if not show_all:
                        break_load = True
                    all_bad_loads.append((">1",load.name))
                    break
            if num_paths == 0:
                print('No path from load '+load.name+' to ' + source_name)
                if not show_all:
                    break_load = True
                all_bad_loads.append(("0",load.name))
                result = False
                break
    if verbose:
        if show_all and len(all_bad_loads) >0:
            print('All bad loads:')
        elif len(all_bad_loads)>0:
            print('First bad load:')
        for load in all_bad_loads:
            print(load[0],'connections for load',load[1])
        all_bad_loads = []

    return result
コード例 #6
0
def fix_transformer_phase_path(model,needs_transformers=False,verbose=True):
    all_sources = []
    all_transformers = set()
    all_loads = set()
    load_transformer_map = {} # Not used but provides a mapping from load to transformer
    result = True

    for i in model.models:
        if isinstance(i,PowerSource) and i.connecting_element is not None:
            all_sources.append(i)
        elif isinstance(i,PowerSource):
            print('Warning - a PowerSource element has a None connecting element')
        if isinstance(i,PowerTransformer):
            all_transformers.add(i)
        if isinstance(i,Load):
            all_loads.add(i)


    if len(all_sources) > 1:
        print('Warning - using first source to orient the network')
    if len(all_sources) == 0:
        print('Model does not contain any power source')
        return

    for source in all_sources:
        ditto_graph = Network()
        ditto_graph.build(model,source.connecting_element)
        ditto_graph.set_attributes(model)
        ditto_graph.remove_open_switches(model) # This deletes the switches inside the networkx graph only
        source_name = source.connecting_element
        all_paths = nx.single_source_shortest_path(ditto_graph.graph,source_name)
        break_load = False
        for load in all_loads:
            load_connection = load.connecting_element
            if load_connection in all_paths:

                ### check that each load has a path to the substation
                path = all_paths[load_connection]
                num_transformers = 0
                transformer_names = []

                ### Fix the low side of the transformer is connected to a line that leads to a load if possible
                transformer_low_side = None
                for i in range(len(path)-1,0,-1):
                    element = ditto_graph.graph[path[i]][path[i-1]]
                    if element['equipment'] == 'PowerTransformer' and not element['is_substation']: #TODO: check if the transformer is part of a regulator. Shouldn't be a problem but could be depending on how regulator defined 
                        transformer_names.append(element['name'])
                        num_transformers+=1
                    if num_transformers == 0 and not element['equipment'] == 'PowerTransformer':
                        transformer_low_side = path[i-1]

                ### Check that the low side of the transformer is connected to a line that leads to a load
                if num_transformers ==1:
                    load_transformer_map[load.name] = element['name']
                    if model[transformer_names[0]].to_element != transformer_low_side:
                        print('Load '+load.name+' has connected transformer of '+transformer_names[0]+' incorrectly connected (likely backwards). Trying to rotate...')
                        if model[transformer_names[0]].from_element == transformer_low_side:
                            from_element_orig = model[transformer_names[0]].from_element
                            to_element_orig = model[transformer_names[0]].to_element
                            model[transformer_names[0]].from_element = to_element_orig
                            model[transformer_names[0]].to_element = from_element_orig
                            print('Succeeded.')
                        else:
                            print('Failed.')



                ### If there is a transformer, check that there phases on the high side are consistent with the transformer setting, and increase until the substation
                if num_transformers == 1:
                    high_phases = [phase_winding.phase for phase_winding in model[transformer_names[0]].windings[0].phase_windings]
                    for i in range(len(path)-1):
                        element = ditto_graph.graph[path[i]][path[i+1]]
                        if element['equipment'] == 'PowerTransformer' and not element['is_substation']:
                            break
                        if element['equipment'] == 'Line':
                            line_phases = [wire.phase for wire in element['wires'] if wire.phase != 'N'] #Neutral phases not included in the transformer
                            if not set(high_phases).issubset(set(line_phases)): #MV phase line phase must be able to support transformer phase
                                if len(set(high_phases)) == 1 and len(set(line_phases)) == 1:
                                    print('Single phase transformer has incorrect phase of '+str(high_phases)+'. Setting to be '+str(line_phases))
                                    high_phases = [phase_winding.phase for phase_winding in model[transformer_names[0]].windings[0].phase_windings]
                                    model[transformer_names[0]].windings[0].phase_windings[0].phase = line_phases[0]
                        elif element['equipment'] != 'Regulator':
                            print('Warning: element of type '+element['equipment'] +' found on path to load '+load.name)
コード例 #7
0
    def apply(cls, stack, model, *args, **kwargs):
        path_to_feeder_file = None
        path_to_switching_devices_file = None
        center_tap_postprocess = False
        switch_to_recloser = False
        if 'path_to_feeder_file' in kwargs:
            path_to_feeder_file = kwargs['path_to_feeder_file']

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

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

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

        #Make sure the voltage of st_mat is set
        model['st_mat'].nominal_voltage = 230000.0

        #Create the modifier object
        modifier = system_structure_modifier(model, 'st_mat')

        #Center-tap loads
        if center_tap_postprocess:
            modifier.center_tap_load_preprocessing()

        #Set node nominal voltages
        modifier.set_nominal_voltages_recur()

        #Set line nominal voltages
        modifier.set_nominal_voltages_recur_line()

        #Create Feeder_metadata objects for each feeder

        #Open and read feeder.txt
        with open(path_to_feeder_file, 'r') as f:
            lines = f.readlines()

        all_feeder_data = set()
        for line in lines[1:]:
            #Parse the line
            node, sub, feed, sub_trans = map(lambda x: x.strip().lower(),
                                             line.split(' '))
            sub_trans = sub_trans.replace('ctrafo', 'tr')
            all_feeder_data.add((feed, sub, sub_trans))

        for feeder in all_feeder_data:
            modifier.set_feeder_metadata(feeder[0], feeder[1], feeder[2])

        for i in modifier.model.models:
            if isinstance(i, PowerSource) and hasattr(
                    i, 'is_sourcebus') and i.is_sourcebus == 1:
                i.positive_sequence_impedance = complex(1.1208, 3.5169)
                i.zero_sequence_impedance = complex(1.1208, 3.5169)

        #Set headnodes for each feeder
        modifier.set_feeder_headnodes()

        #Replace all switchin devices ampacity 3000.0 default values with nans
        #such that we can call the modifier method on it
        for obj in modifier.model.models:
            if isinstance(obj, Line):
                if obj.is_switch == 1 or obj.is_breaker == 1 or obj.is_sectionalizer == 1 or obj.is_fuse == 1 or obj.is_recloser == 1:
                    for w in obj.wires:
                        if w.ampacity == 3000.0:
                            w.ampacity = np.nan

        #Do the actual mdifications
        modifier.set_switching_devices_ampacity()

        #Open the switches which need to be opened.
        modifier.open_close_switches(path_to_switching_devices_file)

        tmp_net = Network()
        tmp_net.build(modifier.model, 'st_mat')
        tmp_net.set_attributes(modifier.model)
        tmp_net.remove_open_switches(modifier.model)
        print(len(nx.cycle_basis(tmp_net.graph)))
        print(nx.cycle_basis(tmp_net.graph))

        #Create the sub-transmission Feeder_metadata
        if not 'subtransmission' in modifier.model.model_names.keys():
            api_feeder_metadata = Feeder_metadata(modifier.model)
            api_feeder_metadata.name = 'subtransmission'
            api_feeder_metadata.substation = modifier.source
            api_feeder_metadata.headnode = modifier.source
            api_feeder_metadata.transformer = modifier.source

        #switch to recloser post-processing


#        if switch_to_recloser:
#            modifier.replace_first_switch_with_recloser()

#Return the modified model
        return modifier.model
コード例 #8
0
    def apply(cls, stack, model, *args, **kwargs):
        if 'path_to_feeder_file' in kwargs:
            path_to_feeder_file = kwargs['path_to_feeder_file']

        path_to_no_feeder_file = None
        if 'path_to_no_feeder_file' in kwargs:
            path_to_no_feeder_file = kwargs['path_to_no_feeder_file']

        if 'compute_metrics' in kwargs:
            compute_metrics = kwargs['compute_metrics']
        else:
            compute_metrics = False

        if 'compute_kva_density_with_transformers' in kwargs:
            compute_kva_density_with_transformers = kwargs[
                'compute_kva_density_with_transformers']
        else:
            compute_kva_density_with_transformers = True

        if compute_metrics:
            if 'excel_output' in kwargs:
                excel_output = kwargs['excel_output']
            else:
                raise ValueError('Missing output file name for excel')

            if 'json_output' in kwargs:
                json_output = kwargs['json_output']
            else:
                raise ValueError('Missing output file name for json')

        #Open and read feeder.txt
        with open(path_to_feeder_file, 'r') as f:
            lines = f.readlines()

        #Parse feeder.txt to have the feeder structure of the network
        feeders = {}
        substations = {}
        substation_transformers = {}

        for line in lines[1:]:

            #Parse the line
            node, sub, feed, sub_trans = map(lambda x: x.strip().lower(),
                                             line.split(' '))

            #If feeder is new, then add it to the feeders dict
            if feed not in feeders:

                #Initialize with a list holding the node
                feeders[feed] = [node.lower().replace('.', '')]

            #Othewise, just append the node
            else:
                feeders[feed].append(node.lower().replace('.', ''))

            #Same thing for the substation
            if feed not in substations:
                substations[feed] = sub.lower().replace('.', '')

            #Same thing for substation_transformers
            if feed not in substation_transformers:
                substation_transformers[feed] = sub.lower().replace('.', '')

        if path_to_no_feeder_file is not None:
            with open(path_to_no_feeder_file, 'r') as f:
                lines = f.readlines()
            for line in lines[1:]:
                node, feed = map(lambda x: x.strip().lower(), line.split(' '))
                if feed != 'mv-mesh':
                    if 'subtransmission' not in feeders:
                        feeders['subtransmission'] = [
                            node.lower().replace('.', '')
                        ]
                    else:
                        feeders['subtransmission'].append(node.lower().replace(
                            '.', ''))
                    if 'subtransmission' not in substations:
                        substations['subtransmission'] = ''

        #Create a network analyzer object
        network_analyst = NetworkAnalyzer(model)

        #Add the feeder information to the network analyzer
        network_analyst.add_feeder_information(
            list(feeders.keys()), list(feeders.values()), substations,
            '')  #TODO find a way to get the feeder type

        #Split the network into feeders
        network_analyst.split_network_into_feeders()

        #Tag the objects
        network_analyst.tag_objects()

        #Set the names
        network_analyst.model.set_names()

        # Set reclosers. This algorithm finds to closest 1/3 of goabs to the feeder head (in topological order)
        # without common ancestry. i.e. no recloser should be upstream of another recloser. If this is not possible,
        # the number of reclosers is decreased

        recloser_proportion = 0.33
        all_goabs = {}
        np.random.seed(0)
        tmp_network = Network()
        tmp_network.build(network_analyst.model, 'st_mat')
        tmp_network.set_attributes(network_analyst.model)
        tmp_network.remove_open_switches(network_analyst.model)
        tmp_network.rebuild_digraph(network_analyst.model, 'st_mat')
        sorted_elements = []
        for element in nx.topological_sort(tmp_network.digraph):
            sorted_elements.append(element)
        for i in network_analyst.model.models:
            if isinstance(
                    i, Line
            ) and i.is_switch is not None and i.is_switch and i.name is not None and 'goab' in i.name.lower(
            ):
                is_open = False
                for wire in i.wires:
                    if wire.is_open:
                        is_open = True
                if is_open:
                    continue
                if hasattr(
                        i, 'feeder_name'
                ) and i.feeder_name is not None and i.feeder_name != 'subtransmission':
                    if i.feeder_name in all_goabs:
                        all_goabs[i.feeder_name].append(i.name)
                    else:
                        all_goabs[i.feeder_name] = [i.name]

        for key in list(all_goabs.keys()):
            feeder_goabs_dic = {
            }  # Topological sorting done by node. This matches goabs to their end-node
            for goab in all_goabs[key]:
                feeder_goabs_dic[
                    model[goab].
                    to_element] = goab  # shouldn't have multiple switches ending at the same node
            feeder_goabs = []
            feeder_goab_ends = []
            for element in sorted_elements:
                if element in feeder_goabs_dic:
                    feeder_goabs.append(feeder_goabs_dic[element])
                    feeder_goab_ends.append(element)
            connectivity_matrix = [[False for i in range(len(feeder_goabs))]
                                   for j in range(len(feeder_goabs))]
            for i in range(len(feeder_goabs)):
                recloser1 = feeder_goab_ends[i]
                for j in range(i + 1, len(feeder_goabs)):
                    recloser2 = feeder_goab_ends[j]
                    if recloser2 == recloser1:
                        continue
                    connected = nx.has_path(tmp_network.digraph, recloser1,
                                            recloser2)
                    connectivity_matrix[i][j] = connected
                    if connected:
                        connectivity_matrix[j][i] = connected

            selected_goabs = []
            num_goabs = int(len(feeder_goabs) * float(recloser_proportion))
            finished = False
            if num_goabs == 0:
                finished = True
            while not finished:
                for i in range(len(feeder_goabs)):
                    current_set = set([i])
                    for j in range(i + 1, len(feeder_goabs)):
                        skip_this_one = False
                        for k in current_set:
                            if connectivity_matrix[j][
                                    k]:  #i.e. see if the candidate node has common anything upstream or downstream
                                skip_this_one = True
                                break
                        if skip_this_one:
                            continue
                        current_set.add(j)
                        if len(current_set) == num_goabs:
                            break
                    if len(current_set) == num_goabs:
                        finished = True
                        for k in current_set:
                            selected_goabs.append(feeder_goabs[k])
                        break
                if not finished:
                    num_goabs -= 1

            #selected_goabs = np.random.choice(feeder_goabs,int(len(feeder_goabs)*float(recloser_proportion)))

            for recloser in selected_goabs:
                network_analyst.model[recloser].is_switch = False
                network_analyst.model[recloser].is_recloser = True
                if network_analyst.model[recloser].wires is not None:
                    for wire in network_analyst.model[recloser].wires:
                        wire.is_switch = False
                        wire.is_recloser = True
                network_analyst.model[recloser].name = network_analyst.model[
                    recloser].name.replace('goab', 'recloser')
                network_analyst.model[recloser].nameclass = 'recloser'
        network_analyst.model.set_names()

        #Compute the metrics if needed
        if compute_metrics:

            #Compute metrics
            network_analyst.compute_all_metrics_per_feeder(
                compute_kva_density_with_transformers=
                compute_kva_density_with_transformers)

            #Export metrics to Excel
            network_analyst.export(excel_output)

            #Export metrics to JSON
            network_analyst.export_json(json_output)

        #Return the model
        return network_analyst.model
コード例 #9
0
    def parse_dg(self, model, **kwargs):
        """PV parser.
        :param model: DiTTo model
        :type model: DiTTo model
        :returns: 1 for success, -1 for failure
        :rtype: int
        """
        if not self.use_reopt:
            return 1
        model.set_names()
        network = Network()
        network.build(model,source="source")
        building_map = {}
        for element in self.geojson_content["features"]:
            if 'properties' in element and 'type' in element['properties'] and 'buildingId' in element['properties'] and element['properties']['type'] == 'ElectricalJunction':
                building_map[element['properties']['buildingId']] = element['properties']['id']
        for element in self.geojson_content["features"]:
            if 'properties' in element and 'type' in element['properties'] and element['properties']['type'] == 'Building':
                id_value = element['properties']['id']
                connecting_element = building_map[id_value]
                feature_data = self.get_feature_data(os.path.join(self.load_folder,id_value,'feature_reports','feature_report_reopt.json'))
                pv_kw = feature_data['distributed_generation']['total_solar_pv_kw']
                upstream_transformer = model[network.get_upstream_transformer(model,connecting_element)]
                is_center_tap = upstream_transformer.is_center_tap
                if pv_kw >0:
                    pv = Photovoltaic(model)
                    pv.name = 'solar_'+id_value
                    pv.connecting_element = connecting_element
                    pv.nominal_voltage = upstream_transformer.windings[1].nominal_voltage
                    phases = []
                    for ph_wdg in upstream_transformer.windings[1].phase_windings:
                        phases.append(Unicode(ph_wdg.phase))
                    if is_center_tap:
                        phases = [Unicode('A'),Unicode('B')]
                    pv.phases = phases
                    pv.rated_power = pv_kw
                    pv.active_rating = 1.1*pv_kw # Should make this a parameter instead
                    pv.connection_type = upstream_transformer.windings[1].connection_type
                    if self.is_timeseries:
                        load_data = pd.read_csv(os.path.join(self.load_folder,id_value,'feature_reports','feature_report_reopt.csv'),header=0)
                        data = load_data['REopt:ElectricityProduced:PV:Total(kw)']
                        timestamps = load_data['Datetime']
                        delta = datetime.datetime.strptime(timestamps.loc[1],'%Y/%m/%d %H:%M:%S') - datetime.datetime.strptime(timestamps.loc[0],'%Y/%m/%d %H:%M:%S') 
                        data_pu = data/pv_kw
                        if not self.timeseries_location is None:
                            if not os.path.exists(self.timeseries_location): #Should have already been created for the loads
                                os.makedirs(self.timeseries_location)
                            data.to_csv(os.path.join(self.timeseries_location,'pv_'+id_value+'.csv'),header=False, index=False)
                            data_pu.to_csv(os.path.join(self.timeseries_location,'pv_'+id_value+'_pu.csv'),header=False, index=False)
                            timeseries = Timeseries(model)
                            timeseries.feeder_name = pv.feeder_name
                            timeseries.substation_name = pv.substation_name
                            timeseries.interval = delta.seconds/3600.0 
                            timeseries.data_type = 'float'
                            timeseries.data_location = os.path.join(self.relative_timeseries_location,'pv_'+id_value+'_pu.csv')
                            timeseries.data_label = 'feature_'+id_value
                            timeseries.scale_factor = 1
                            pv.timeseries = [timeseries]
                        


        return 1
コード例 #10
0
    def parse_loads(self, model, **kwargs):
        """Load parser.
        :param model: DiTTo model
        :type model: DiTTo model
        :returns: 1 for success, -1 for failure
        :rtype: int
        """
        model.set_names()
        network = Network()
        network.build(model,source="source")
        building_map = {}
        for element in self.geojson_content["features"]:
            if 'properties' in element and 'type' in element['properties'] and 'buildingId' in element['properties'] and element['properties']['type'] == 'ElectricalJunction':
                building_map[element['properties']['buildingId']] = element['properties']['id']
        for element in self.geojson_content["features"]:
            if 'properties' in element and 'type' in element['properties'] and element['properties']['type'] == 'Building':
                id_value = element['properties']['id']
                connecting_element = building_map[id_value]
                load = Load(model)
                load.name = id_value
                load.connecting_element = connecting_element
                upstream_transformer = model[network.get_upstream_transformer(model,connecting_element)]
                is_center_tap = upstream_transformer.is_center_tap
                load.nominal_voltage = upstream_transformer.windings[1].nominal_voltage

                load_path = os.path.join(self.load_folder,id_value,'feature_reports')
                if os.path.exists(load_path): #We've found the load data

                    load_data = None
                    load_column = None
                    if self.use_reopt:
                        load_data = pd.read_csv(os.path.join(load_path,'feature_report_reopt.csv'),header=0)
                        load_column = 'REopt:Electricity:Load:Total(kw)'
                    else:
                        load_data = pd.read_csv(os.path.join(load_path,'default_feature_report.csv'),header=0)
                        load_column = 'Net Power(kW)'
                    max_load = max(load_data[load_column])

                    phases = []
                    for ph_wdg in upstream_transformer.windings[1].phase_windings:
                        phases.append(ph_wdg.phase)
                    if is_center_tap:
                        phases = ['A','B']

                    phase_loads = []
                    for phase in phases:
                        phase_load = PhaseLoad(model)
                        phase_load.phase = phase
                        power_factor = 0.95
                        phase_load.p = max_load/len(phases)
                        phase_load.q = phase_load.p * ((1/power_factor-1)**0.5)
                        phase_loads.append(phase_load)
                    load.phase_loads = phase_loads
                    if self.is_timeseries:
                        data = load_data[load_column]
                        timestamps = load_data['Datetime']
                        delta = datetime.datetime.strptime(timestamps.loc[1],'%Y/%m/%d %H:%M:%S') - datetime.datetime.strptime(timestamps.loc[0],'%Y/%m/%d %H:%M:%S') 
                        data_pu = data/max_load
                        if not self.timeseries_location is None:
                            if not os.path.exists(self.timeseries_location):
                                os.makedirs(self.timeseries_location)
                            data.to_csv(os.path.join(self.timeseries_location,'load_'+id_value+'.csv'),header=False, index=False)
                            data_pu.to_csv(os.path.join(self.timeseries_location,'load_'+id_value+'_pu.csv'),header=False, index=False)
                            timestamps.to_csv(os.path.join(self.timeseries_location,'timestamps.csv'),header=True,index=False)
                            timeseries = Timeseries(model)
                            timeseries.feeder_name = load.feeder_name
                            timeseries.substation_name = load.substation_name
                            timeseries.interval = delta.seconds/3600.0 #assume 15 minute loads
                            timeseries.data_type = 'float'
                            timeseries.data_location = os.path.join(self.relative_timeseries_location,'load_'+id_value+'_pu.csv')
                            timeseries.data_label = 'feature_'+id_value
                            timeseries.scale_factor = 1
                            load.timeseries = [timeseries]





                else:
                    print('Load information missing for '+id_value)


        return 1
コード例 #11
0
def fix_undersized_transformers(model,verbose=True):
    all_sources = []
    all_transformers = set()
    all_loads = set()
    transformer_load_map = {} # provides a mapping of all the loads connected to a transformer if there's a path from the load to a source
    result = True
    transformer_sizes = [15,25,50,75,100,300,500,1000,2000,3000,5000] # upgrade sizes in kva
    model.set_names()

    for i in model.models:
        if isinstance(i,PowerSource) and i.connecting_element is not None:
            all_sources.append(i)
        elif isinstance(i,PowerSource):
            print('Warning - a PowerSource element has a None connecting element')
        if isinstance(i,PowerTransformer):
            all_transformers.add(i)
        if isinstance(i,Load):
            all_loads.add(i)

    if len(all_sources) > 1:
        print('Warning - using first source to orient the network')
    if len(all_sources) == 0:
        print('Model does not contain any power source')
        return

    for source in all_sources:
        ditto_graph = Network()
        ditto_graph.build(model,source.connecting_element)
        ditto_graph.set_attributes(model)
        ditto_graph.remove_open_switches(model) # This deletes the switches inside the networkx graph only
        source_name = source.connecting_element
        all_paths = nx.single_source_shortest_path(ditto_graph.graph,source_name)
        break_load = False
        for load in all_loads:
            load_connection = load.connecting_element
            if load_connection in all_paths:

                ### check that each load has a path to the substation
                path = all_paths[load_connection]
                num_transformers = 0
                transformer_names = []

                for i in range(len(path)-1,0,-1):
                    element = ditto_graph.graph[path[i]][path[i-1]]
                    if element['equipment'] == 'PowerTransformer' and not element['is_substation']: #TODO: check if the transformer is part of a regulator. Shouldn't be a problem but could be depending on how regulator defined 
                        transformer_names.append(element['name'])
                        num_transformers+=1

                ### If the transformer is connected correctly with the load underneath it, update the transformer-load mapping dictionary
                if num_transformers ==1:
                    if not transformer_names[0] in transformer_load_map:
                        transformer_load_map[transformer_names[0]] = []
                    transformer_load_map[transformer_names[0]].append(load.name)

        for transformer in transformer_load_map:
            transformer_size = model[transformer].windings[0].rated_power/1000
            total_load_kw = 0 
            for load in transformer_load_map[transformer]:
                load_kw = 0
                for phase_load in model[load].phase_loads:
                    total_load_kw += phase_load.p/1000
                    load_kw += phase_load.p/1000
            if transformer_size <total_load_kw:
                new_transformer_size = None
                for sz in transformer_sizes:
                    if sz > total_load_kw:
                        new_transformer_size = sz
                        break
                if new_transformer_size is not None:
                    print(f'Tranformer {transformer} with size {transformer_size} kVA serves {total_load_kw} kW. Upgrading to {new_transformer_size} kVA')
                    for winding in model[transformer].windings:
                        winding.rated_power = new_transformer_size*1000
                else:
                    print(f'Tranformer {transformer} with size {transformer_size} kVA serves {total_load_kw} kW. No upgrade size found (max is 5000 kVA)')
コード例 #12
0
    def apply(cls,
              stack,
              model,
              feeders=100,
              equipment_type=None,
              selection=('Random', 15),
              seed=0,
              placement_folder=''):
        subset = []
        if selection is None:
            return model

        # TODO: Apply placements to feeders selectively. Currently applying to all equipment in the distribution system

        # Currently just including random selections. Need to also include and document other selection options
        if selection[0] == 'Reclosers':
            # Get a subset of nodes at the end of Reclosers. This algorithm finds to closest goabs to the feeder head (in topological order)
            # without common ancestry. i.e. no goab should be upstream of another goab. If this is not possible,
            # the number of reclosers is decreased

            # Percentage of feeders that each Reclosers number is used for e.g. if selection[1] = 4 and feeders = [50,40,30,20], 50% of feedes have 1st goab, 40% of feeders have second etc.
            feeders_str = str(feeders)
            if isinstance(feeders, list):
                feeders_str = ''
                for f in feeders:
                    feeders_str = feeders_str + str(f) + '-'
                feeders_str = feeders_str.strip('-')

            file_name = str(feeders_str) + '_Node_' + selection[0] + '-' + str(
                selection[1]) + '-' + str(selection[2]) + '.json'
            all_goabs = {}
            random.seed(seed)

            tmp_network = Network()
            tmp_network.build(model, 'st_mat')
            tmp_network.set_attributes(model)
            tmp_network.remove_open_switches(model)
            tmp_network.rebuild_digraph(model, 'st_mat')
            sorted_elements = []
            for element in nx.topological_sort(tmp_network.digraph):
                sorted_elements.append(element)
            for i in model.models:
                if isinstance(
                        i,
                        Line) and i.is_recloser is not None and i.is_recloser:
                    is_open = False
                    for wire in i.wires:
                        if wire.is_open:
                            is_open = True
                    if is_open:
                        continue
                    if hasattr(
                            i, 'feeder_name'
                    ) and i.feeder_name is not None and i.feeder_name != 'subtransmission':
                        if i.feeder_name in all_goabs:
                            all_goabs[i.feeder_name].append(i.name)
                        else:
                            all_goabs[i.feeder_name] = [i.name]

            goab_key_list = list(all_goabs.keys())
            random.seed(seed)
            random.shuffle(goab_key_list)
            goab_counter = 0
            for key in goab_key_list:
                goab_counter += 1
                feeder_goabs_dic = {
                }  # Topological sorting done by node. This matches goabs to their end-node
                for goab in all_goabs[key]:
                    feeder_goabs_dic[
                        model[goab].
                        to_element] = goab  # shouldn't have multiple switches ending at the same node
                feeder_goabs = []
                feeder_goab_ends = []
                for element in sorted_elements:
                    if element in feeder_goabs_dic:
                        feeder_goabs.append(feeder_goabs_dic[element])
                        feeder_goab_ends.append(element)
                connectivity_matrix = [[
                    False for i in range(len(feeder_goabs))
                ] for j in range(len(feeder_goabs))]
                for i in range(len(feeder_goabs)):
                    goab1 = feeder_goab_ends[i]
                    for j in range(i + 1, len(feeder_goabs)):
                        goab2 = feeder_goab_ends[j]
                        if goab1 == goab2:
                            continue
                        connected = nx.has_path(tmp_network.digraph, goab1,
                                                goab2)
                        connectivity_matrix[i][j] = connected
                        if connected:
                            connectivity_matrix[j][i] = connected

                selected_goabs = []
                num_goabs = selection[2]
                finished = False
                if num_goabs == 0:
                    finished = True
                while not finished:
                    for i in range(len(feeder_goabs)):
                        current_set = set([i])
                        for j in range(i + 1, len(feeder_goabs)):
                            skip_this_one = False
                            for k in current_set:
                                if connectivity_matrix[j][
                                        k]:  #i.e. see if the candidate node has common anything upstream or downstream
                                    skip_this_one = True
                                    break
                            if skip_this_one:
                                continue
                            current_set.add(j)
                            if len(current_set) == num_goabs:
                                break
                        if len(current_set) == num_goabs:
                            finished = True
                            for k in current_set:
                                selected_goabs.append(feeder_goabs[k])
                            break
                    if not finished:
                        num_goabs -= 1

                for i in range(min(len(selected_goabs), selection[1])):
                    if goab_counter / float(
                            len(goab_key_list)) * 100 <= feeders[i]:
                        subset.append(model[selected_goabs[i]].to_element)

        if selection[0] == 'Random':
            class_equipment_type = locate(equipment_type)
            all_equipment = []
            for i in model.models:
                if isinstance(i, class_equipment_type):
                    all_equipment.append(i.name)

            if len(selection) == 3:
                random.seed(seed)
                random.shuffle(all_equipment)
                start_pos = math.floor(
                    len(all_equipment) * float(selection[1]) / 100.0)
                end_pos = math.floor(
                    len(all_equipment) * float(selection[2]) / 100.0)
                subset = all_equipment[start_pos:end_pos]
                file_name = str(feeders) + '_' + equipment_type.split(
                    '.')[-1] + '_' + selection[0] + '-' + str(
                        selection[1]) + '-' + str(
                            selection[2]) + '_' + str(seed) + '.json'
            if len(selection) == 2:
                random.seed(seed)
                subset = random.sample(
                    all_equipment,
                    math.floor(
                        len(all_equipment) * float(selection[1]) / 100.0))
                file_name = str(feeders) + '_' + equipment_type.split(
                    '.')[-1] + '_' + selection[0] + '-' + str(
                        selection[1]) + '_' + str(seed) + '.json'

        if not os.path.exists(placement_folder):
            os.makedirs(placement_folder)

        with open(os.path.join(placement_folder, file_name), "w") as f:
            f.write(json_tricks.dumps(subset, sort_keys=True, indent=4))

        return model