Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Just an idea, the attached code can be run in PSSE to get the shortest path between two buses. If you use your slack bus as one, and the queried bus as the other, the length of the result will be your path length. It is feasible to step through your buses and save the path lengths for each and sort. It is computational expensive however.

# Delivers shortest path (miles) between two buses
# The two buses must be the same voltage level (the script cannot see past transformers)

# Can't default to swing bus for everything if it's a different voltage level than bus1.


from collections import defaultdict

class Graph():
    def __init__(self):
        """
        self.edges is a dict of all possible next nodes e.g. {'X': ['A', 'B', 'C', 'E'], ...}
        self.weights has all the weights between two nodes, with the two nodes as a tuple as the key
        e.g. {('X', 'A'): 7, ('X', 'B'): 2, ...}
        """
        self.edges = defaultdict(list)
        self.weights = {}
    def add_edge(self, from_node, to_node, weight):
        # Note: assumes edges are bi-directional
        self.edges[from_node].append(to_node)
        self.edges[to_node].append(from_node)
        self.weights[(from_node, to_node)] = weight
        self.weights[(to_node, from_node)] = weight

def dijsktra(graph, initial, end):
    # shortest paths is a dict of nodes whose value is a tuple of (previous node, weight)
    shortest_paths = {initial: (None, 0)}
    current_node = initial
    visited = set()
    while current_node != end:
        visited.add(current_node)
        jbus = 1
        ierr = psspy.inibrn(current_node,2)
        # nxtbrn provides neighboring buses until all have been provided. Then it returns jbus = 0
        while jbus > 0:
            ierr, jbus, kbus, ickt = psspy.nxtbrn3(current_node)
            if jbus > 0:
                # Get the length of the line between these two buses (in miles)
                ierr, weight = psspy.brndat(current_node,jbus,ickt,'LENGTH') # gives the literal shortest path
                weight = 1.0 #override weight to minimize the number of bus hops (longer line miles)
                if weight is None:
                    weight=0.0
                edge = (current_node,jbus,weight)
                # Add this edge to the graph if it's not there already
                if edge not in graph.edges:
                    graph.add_edge(*edge)
        # Destinations: all the buses we can go to from the bus we're currently at
        destinations = graph.edges[current_node]
        # weight_to_current_node: how far we've travelled so far to get to this node
        weight_to_current_node = shortest_paths[current_node][1]

        for next_node in destinations:
            weight = graph.weights[(current_node, next_node)] + weight_to_current_node
            if next_node not in shortest_paths:
                shortest_paths[next_node] = (current_node, weight)
            else:
                current_shortest_weight = shortest_paths[next_node][1]
                if current_shortest_weight > weight:
                    shortest_paths[next_node] = (current_node, weight)
        # Don't visit the same node twice. This would lead to an infinite loop
        next_destinations = {node: shortest_paths[node] for node in shortest_paths if node not in visited}

        if not next_destinations:
            return "Route Not Possible"
        # next node is the destination with the lowest weight
        current_node = min(next_destinations, key = lambda k: next_destinations[k][1])

    # Work back through destinations in shortest path
    path = []
    while current_node is not None:
        path.append(current_node)
        next_node = shortest_paths[current_node][0]
        current_node = next_node
    # Reverse path
    path = path[::-1]
    return path

if __name__ == '__main__':                                    ## Main program!!!
    # cases = glob.glob("*.sav")
    # case = cases[0]
    # ierr = psspy.case(case)    
    # if len(cases) > 1:
        # 'More than 1 sav case found. Using: ' + case
    print "Enter first bus number"
    s = raw_input('-->  ')
    try:
        bus1 = int(s)
    except:
        print "Please enter integer"
        s = raw_intput('-->  ')
        bus1 = int(s)
    print "Enter second bus number"
    s = raw_input('-->  ')
    try:
        bus2 = int(s)
    except:
        print "Please enter integer"
        s = raw_intput('-->  ')
        bus2 = int(s)
    graph = Graph()
    shortestPath = dijsktra(graph,bus1,bus2)
    output = []
    for bus in shortestPath:
        # Look up each bus number's corresponding bus name and pair this info into tuples for output
        ierr, name = psspy.notona(bus)
        output.append((bus,name))
    print "The shortest path is: " 
    for i in output:
        print str(i)

Just an idea, the attached code can be run in PSSE to get the shortest path between two buses. If you use your slack bus as one, and the queried bus as the other, the length of the result will be your path length. It is feasible to step through your buses and save the path lengths for each and sort. It is computational computationally expensive however.

# Delivers shortest path (miles) between two buses
# The two buses must be the same voltage level (the script cannot see past transformers)

# Can't default to swing bus for everything if it's a different voltage level than bus1.


from collections import defaultdict

class Graph():
    def __init__(self):
        """
        self.edges is a dict of all possible next nodes e.g. {'X': ['A', 'B', 'C', 'E'], ...}
        self.weights has all the weights between two nodes, with the two nodes as a tuple as the key
        e.g. {('X', 'A'): 7, ('X', 'B'): 2, ...}
        """
        self.edges = defaultdict(list)
        self.weights = {}
    def add_edge(self, from_node, to_node, weight):
        # Note: assumes edges are bi-directional
        self.edges[from_node].append(to_node)
        self.edges[to_node].append(from_node)
        self.weights[(from_node, to_node)] = weight
        self.weights[(to_node, from_node)] = weight

def dijsktra(graph, initial, end):
    # shortest paths is a dict of nodes whose value is a tuple of (previous node, weight)
    shortest_paths = {initial: (None, 0)}
    current_node = initial
    visited = set()
    while current_node != end:
        visited.add(current_node)
        jbus = 1
        ierr = psspy.inibrn(current_node,2)
        # nxtbrn provides neighboring buses until all have been provided. Then it returns jbus = 0
        while jbus > 0:
            ierr, jbus, kbus, ickt = psspy.nxtbrn3(current_node)
            if jbus > 0:
                # Get the length of the line between these two buses (in miles)
                ierr, weight = psspy.brndat(current_node,jbus,ickt,'LENGTH') # gives the literal shortest path
                weight = 1.0 #override weight to minimize the number of bus hops (longer line miles)
                if weight is None:
                    weight=0.0
                edge = (current_node,jbus,weight)
                # Add this edge to the graph if it's not there already
                if edge not in graph.edges:
                    graph.add_edge(*edge)
        # Destinations: all the buses we can go to from the bus we're currently at
        destinations = graph.edges[current_node]
        # weight_to_current_node: how far we've travelled so far to get to this node
        weight_to_current_node = shortest_paths[current_node][1]

        for next_node in destinations:
            weight = graph.weights[(current_node, next_node)] + weight_to_current_node
            if next_node not in shortest_paths:
                shortest_paths[next_node] = (current_node, weight)
            else:
                current_shortest_weight = shortest_paths[next_node][1]
                if current_shortest_weight > weight:
                    shortest_paths[next_node] = (current_node, weight)
        # Don't visit the same node twice. This would lead to an infinite loop
        next_destinations = {node: shortest_paths[node] for node in shortest_paths if node not in visited}

        if not next_destinations:
            return "Route Not Possible"
        # next node is the destination with the lowest weight
        current_node = min(next_destinations, key = lambda k: next_destinations[k][1])

    # Work back through destinations in shortest path
    path = []
    while current_node is not None:
        path.append(current_node)
        next_node = shortest_paths[current_node][0]
        current_node = next_node
    # Reverse path
    path = path[::-1]
    return path

if __name__ == '__main__':                                    ## Main program!!!
    # cases = glob.glob("*.sav")
    # case = cases[0]
    # ierr = psspy.case(case)    
    # if len(cases) > 1:
        # 'More than 1 sav case found. Using: ' + case
    print "Enter first bus number"
    s = raw_input('-->  ')
    try:
        bus1 = int(s)
    except:
        print "Please enter integer"
        s = raw_intput('-->  ')
        bus1 = int(s)
    print "Enter second bus number"
    s = raw_input('-->  ')
    try:
        bus2 = int(s)
    except:
        print "Please enter integer"
        s = raw_intput('-->  ')
        bus2 = int(s)
    graph = Graph()
    shortestPath = dijsktra(graph,bus1,bus2)
    output = []
    for bus in shortestPath:
        # Look up each bus number's corresponding bus name and pair this info into tuples for output
        ierr, name = psspy.notona(bus)
        output.append((bus,name))
    print "The shortest path is: " 
    for i in output:
        print str(i)