コード例 #1
0
def get_post_event_volts(sub, splitting_nodes, powernet, curr_FLiER):
    """Simulate a contingency and return the resulting network voltages.

    Inputs:
    sub (Ring_Substation) - The substation where the contingency occurs.
    splitting_nodes (List of ints) - The set of nodes splitting from the 
       substation.
    powernet (Power_Network) - The power network.
    curr_FLiER (FLiER_Substation) - The FLiER object that will run the FLiER
       algorithm.

    Output:
    List of 2n doubles. The set of post-contingency voltages. Angles followed
    by magnitudes.
    """
    split_powernet = split_bus(powernet, sub, splitting_nodes)
    V_split = split_powernet.simulate()
    new_bus_volt = V_split[-1]

    eVs = FLiER_Substation.extend_complex_vector(curr_FLiER.substations,
                                                 V_split[:-1], curr_FLiER.en)
    eVs = np.hstack([np.angle(eVs), np.abs(eVs)])
    for sn in splitting_nodes:
        eVs[sub.nodes[sn].eind] = np.angle(new_bus_volt)
        eVs[sub.nodes[sn].eind + curr_FLiER.en] = np.abs(new_bus_volt)
    eVs = eVs[curr_FLiER.reindr2eindr]
    post_event_volts = curr_FLiER.E[:, :2 * curr_FLiER.en].dot(eVs)
    split_powernet.prep_for_delete()
    return post_event_volts
コード例 #2
0
def get_post_event_volts(sub, splitting_nodes, powernet, curr_FLiER):
    """Simulate a contingency and return the resulting network voltages.

    Inputs:
    sub (Ring_Substation) - The substation where the contingency occurs.
    splitting_nodes (List of ints) - The set of nodes splitting from the 
       substation.
    powernet (Power_Network) - The power network.
    curr_FLiER (FLiER_Substation) - The FLiER object that will run the FLiER
       algorithm.

    Output:
    List of 2n doubles. The set of post-contingency voltages. Angles followed
    by magnitudes.
    """
    split_powernet = split_bus(powernet, sub, splitting_nodes)
    V_split = split_powernet.simulate()
    new_bus_volt = V_split[-1]
    
    eVs = FLiER_Substation.extend_complex_vector(curr_FLiER.substations, V_split[:-1],
                                                  curr_FLiER.en)
    eVs = np.hstack([np.angle(eVs), np.abs(eVs)])
    for sn in splitting_nodes:
        eVs[sub.nodes[sn].eind] = np.angle(new_bus_volt)
        eVs[sub.nodes[sn].eind+curr_FLiER.en] = np.abs(new_bus_volt)
    eVs = eVs[curr_FLiER.reindr2eindr]
    post_event_volts = curr_FLiER.E[:,:2*curr_FLiER.en].dot(eVs)
    split_powernet.prep_for_delete()
    return post_event_volts
コード例 #3
0
def main(args):
    """Run a test of FLiER.

    Parameters are passed to the function in pairs, where args[i] is a flag
    and args[i+1] is a parameter value. Valid flag, value combinations are the 
    following:
    -network [filename]: A .cdf or .m file containing the network to be read in.
       A .cdf file is in the IEEE Common Data Format (see 
       https://www.ee.washington.edu/research/pstca/). A .m file is in MATPOWER
       format.
    -use_filter ["True", "False"]: Whether or not to use the FLiER filtering
       procedure. Default is True.
    -pmus [int,...,int]: A list of the indices of buses on which to place PMUs.
    -test_type ["Full", "Single_Lines"]: If "Full", perform a test of a 
       substation splitting into two nodes. If "Single_Lines", perform a line 
       failure test. Default is "Full".
    -test_scenarios [**]: A list of the scenarios to test.
    -noise [double]: Add noise to pre- and post-event voltage readings. Value 
       specifies standard deviation of noise. Default is 0.0.
    -write_file [filename]: Where to write the output data. Default is "out.txt".
    -verbose ["True", "False"]: Turn verbose output to the command-line on or 
       off. Default is "False".

    **test_scenarios format: A semicolon-separated list in which each item is a 
       scenario. Each item is a substation index, a comma, and then a 
       space-separated list of substation nodes that split from the substation.
       For example, "86,1 2 3;176,1 2;539,7;702,4 5".
    """
    print "Running FLiER test with the following options:"
    print "\n".join([
        "{0}: {1}".format(args[i], args[i + 1])
        for i in range(1, len(args), 2)
    ])
    out = read_inputs(args)
    (filename, pmus, write_filename, test_type, noise, test_scenarios,
     use_filter, verbose) = out

    if filename.endswith('.m'):
        powernet = PowerNetwork.powernet_from_matpower(filename)
    else:
        powernet = PowerNetwork.powernet_from_IEEECDF(filename)
    # Simulate the pre-event network voltages.
    V = powernet.simulate()
    pre_event_volts = np.hstack([np.angle(V), np.abs(V)])
    curr_FLiER = FLiER_Substation(powernet,
                                  pre_event_volts,
                                  pmus,
                                  verbose=verbose)

    solution_ranks = []
    filter_ranks = []
    num_tests = 0
    num_missed = 0
    file = open(write_filename, 'w')
    # Iterate over test cases. For each case, make the specified change to the
    # network and simulate the resulting voltages. Then run FLiER to attempt to
    # identify the change that occurred.
    for sub in curr_FLiER.substations:
        # For each substation, iterate over possible tests. The set of tests
        # returned by the iterator depends on test_type.
        for splitting_nodes in sub.node_iterator(test_type):
            key = (sub.index, tuple(splitting_nodes))
            if test_scenarios is not None and key not in test_scenarios:
                continue
            if verbose:
                if test_type == 'Full':
                    print "On bus {0}, splitting nbrs {1}".format(
                        sub.index, splitting_nodes)
                else:
                    bab = sub.bus_across_branch(splitting_nodes[0])
                    print "On line {0}".format((sub.index, bab.index))
            try:
                # Make change according to test scenario, simulate results.
                # Some changes result in simulation nonconvergence. In this
                # case, skip the scenario.
                post_event_volts = get_post_event_volts(
                    sub, splitting_nodes, powernet, curr_FLiER)
            except RuntimeError as e:
                print str(e)
                continue

            if noise > 0:
                # Add noise if called for.
                noisy_prev = pre_event_volts.copy()
                noisy_prev += np.random.normal(0, noise, len(noisy_prev))
                noisy_postv = post_event_volts.copy()
                noisy_postv += np.random.normal(0, noise, len(noisy_postv))
                noisy_FLiER = FLiER_Substation(powernet, noisy_prev, pmus)
                out = noisy_FLiER.find_topology_error(noisy_postv, test_type,
                                                      use_filter)
            else:
                # Run FLiER
                out = curr_FLiER.find_topology_error(post_event_volts,
                                                     test_type, use_filter)
            scores, filter_scores, fraction_ts_computed = out
            solkey = (sub.index, tuple(splitting_nodes))  # Solution key

            num_tests += 1
            if scores is False:
                continue
            if solkey not in scores:
                if verbose:
                    print "Solution not present"
                num_missed += 1
                score_of_solution = np.Inf
            else:
                score_of_solution = scores[solkey]
                rank = np.sum(scores.values() <= score_of_solution)
                buses_ordered = [
                    y[0][0] if y[0] != -1 else -1
                    for y in sorted(scores.items(), key=lambda x: x[1])
                ]
                # bus_rank is the rank of the correct answer if we only care about
                # getting the substation index correct and not the specific nodes
                # that split.
                bus_rank = buses_ordered.index(sub.index) + 1
            if score_of_solution == np.Inf:
                # Bit of a hack here. Occasionally the filter removes the
                # correct answer entirely from the list of possibilities. In
                # this case we just set the rank of the solution to something
                # large.
                rank = 300
                bus_rank = 300

            if verbose:
                print "Solution score: {0}".format(score_of_solution)
                print "Solution rank: {0}".format(rank)
                print "Correct bus rank: {0}".format(bus_rank)
                print "Number scores not filtered: {0}".format(len(scores))

            # Update scoring data structures. Write the results of this test to
            # the output file.
            solution_ranks.append(rank)
            sol_filter_score = filter_scores[solkey]
            filter_ranks.append(
                np.sum(filter_scores.values() <= sol_filter_score))
            write_to_file(file, solkey, out, curr_FLiER.substations)

    file.close()

    print num_tests
    print "{0} missed entirely".format(num_missed)
    sorted_solranklist, sorted_filtersolranklist = zip(
        *sorted(zip(solution_ranks, filter_ranks), key=lambda x: x[0]))
    print "Mean solution rank: {0}, Mean filtered solution rank: {1}".format(
        np.mean(sorted_solranklist), np.mean(sorted_filtersolranklist))
    print "Median sol rank: {0}, median filtered sol rank: {1}".format(
        np.median(sorted_solranklist), np.median(sorted_filtersolranklist))
    print "Frac correct: {0}, frac filtered correct: {1}".format(
        np.sum(np.array(sorted_solranklist) == 1) / float(num_tests),
        np.sum(np.array(sorted_filtersolranklist) == 1) / float(num_tests))
    print "Frac in top 3: {0}, frac filtered in top 3: {1}".format(
        np.sum(np.array(sorted_solranklist) <= 3) / float(num_tests),
        np.sum(np.array(sorted_filtersolranklist) <= 3) / float(num_tests))
コード例 #4
0
def main(args):
    """Run a test of FLiER.

    Parameters are passed to the function in pairs, where args[i] is a flag
    and args[i+1] is a parameter value. Valid flag, value combinations are the 
    following:
    -network [filename]: A .cdf or .m file containing the network to be read in.
       A .cdf file is in the IEEE Common Data Format (see 
       https://www.ee.washington.edu/research/pstca/). A .m file is in MATPOWER
       format.
    -use_filter ["True", "False"]: Whether or not to use the FLiER filtering
       procedure. Default is True.
    -pmus [int,...,int]: A list of the indices of buses on which to place PMUs.
    -test_type ["Full", "Single_Lines"]: If "Full", perform a test of a 
       substation splitting into two nodes. If "Single_Lines", perform a line 
       failure test. Default is "Full".
    -test_scenarios [**]: A list of the scenarios to test.
    -noise [double]: Add noise to pre- and post-event voltage readings. Value 
       specifies standard deviation of noise. Default is 0.0.
    -write_file [filename]: Where to write the output data. Default is "out.txt".
    -verbose ["True", "False"]: Turn verbose output to the command-line on or 
       off. Default is "False".

    **test_scenarios format: A semicolon-separated list in which each item is a 
       scenario. Each item is a substation index, a comma, and then a 
       space-separated list of substation nodes that split from the substation.
       For example, "86,1 2 3;176,1 2;539,7;702,4 5".
    """
    print "Running FLiER test with the following options:"
    print "\n".join(["{0}: {1}".format(args[i], args[i+1]) for i in range(1, len(args), 2)])
    out = read_inputs(args)
    (filename, pmus, write_filename, test_type, 
             noise, test_scenarios, use_filter, verbose) = out

    if filename.endswith('.m'):
        powernet = PowerNetwork.powernet_from_matpower(filename)
    else:
        powernet = PowerNetwork.powernet_from_IEEECDF(filename)
    # Simulate the pre-event network voltages.
    V = powernet.simulate()
    pre_event_volts = np.hstack([np.angle(V), np.abs(V)])
    curr_FLiER = FLiER_Substation(powernet, pre_event_volts, pmus, verbose=verbose)
    
    solution_ranks = []
    filter_ranks = []
    num_tests = 0
    num_missed = 0
    file = open(write_filename, 'w')
    # Iterate over test cases. For each case, make the specified change to the
    # network and simulate the resulting voltages. Then run FLiER to attempt to
    # identify the change that occurred.
    for sub in curr_FLiER.substations:
        # For each substation, iterate over possible tests. The set of tests 
        # returned by the iterator depends on test_type.
        for splitting_nodes in sub.node_iterator(test_type):
            key = (sub.index, tuple(splitting_nodes))
            if test_scenarios is not None and key not in test_scenarios:
                continue
            if verbose:
                if test_type == 'Full':
                    print "On bus {0}, splitting nbrs {1}".format(sub.index, 
                                                                  splitting_nodes)
                else:
                    bab = sub.bus_across_branch(splitting_nodes[0])
                    print "On line {0}".format((sub.index, bab.index))
            try:
                # Make change according to test scenario, simulate results.
                # Some changes result in simulation nonconvergence. In this 
                # case, skip the scenario.
                post_event_volts = get_post_event_volts(sub, splitting_nodes, 
                                                        powernet, curr_FLiER)
            except RuntimeError as e:
                print str(e)
                continue
                
            if noise > 0:
                # Add noise if called for.
                noisy_prev = pre_event_volts.copy()
                noisy_prev += np.random.normal(0, noise, len(noisy_prev))
                noisy_postv = post_event_volts.copy()
                noisy_postv += np.random.normal(0, noise, len(noisy_postv))
                noisy_FLiER = FLiER_Substation(powernet, noisy_prev, pmus)
                out = noisy_FLiER.find_topology_error(noisy_postv, test_type,
                                                      use_filter)
            else:               
                # Run FLiER
                out = curr_FLiER.find_topology_error(post_event_volts, test_type,
                                                     use_filter)
            scores, filter_scores, fraction_ts_computed = out
            solkey = (sub.index, tuple(splitting_nodes)) # Solution key
            
            num_tests += 1
            if scores is False:
                continue
            if solkey not in scores:
                if verbose:
                    print "Solution not present"
                num_missed += 1
                score_of_solution = np.Inf
            else:
                score_of_solution = scores[solkey]
                rank = np.sum(scores.values() <= score_of_solution)
                buses_ordered = [y[0][0] if y[0] != -1 else -1 for y in 
                                 sorted(scores.items(), key = lambda x : x[1])]
                # bus_rank is the rank of the correct answer if we only care about
                # getting the substation index correct and not the specific nodes
                # that split.
                bus_rank = buses_ordered.index(sub.index) + 1
            if score_of_solution == np.Inf:
                # Bit of a hack here. Occasionally the filter removes the 
                # correct answer entirely from the list of possibilities. In 
                # this case we just set the rank of the solution to something 
                # large.
                rank = 300
                bus_rank = 300
                
            if verbose:
                print "Solution score: {0}".format(score_of_solution)
                print "Solution rank: {0}".format(rank)
                print "Correct bus rank: {0}".format(bus_rank)
                print "Number scores not filtered: {0}".format(len(scores))
            
            # Update scoring data structures. Write the results of this test to
            # the output file.
            solution_ranks.append(rank)
            sol_filter_score = filter_scores[solkey]
            filter_ranks.append(np.sum(filter_scores.values() <= sol_filter_score))
            write_to_file(file, solkey, out, curr_FLiER.substations)
            
    file.close()
    
    print num_tests
    print "{0} missed entirely".format(num_missed)
    sorted_solranklist, sorted_filtersolranklist = zip(*sorted(zip(solution_ranks, filter_ranks), key = lambda x : x[0]))
    print "Mean solution rank: {0}, Mean filtered solution rank: {1}".format(np.mean(sorted_solranklist), np.mean(sorted_filtersolranklist))
    print "Median sol rank: {0}, median filtered sol rank: {1}".format(np.median(sorted_solranklist), np.median(sorted_filtersolranklist))
    print "Frac correct: {0}, frac filtered correct: {1}".format(np.sum(np.array(sorted_solranklist) == 1) / float(num_tests), np.sum(np.array(sorted_filtersolranklist) == 1) / float(num_tests))
    print "Frac in top 3: {0}, frac filtered in top 3: {1}".format(np.sum(np.array(sorted_solranklist) <= 3) / float(num_tests), np.sum(np.array(sorted_filtersolranklist) <= 3) / float(num_tests))