コード例 #1
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))
コード例 #2
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))
コード例 #3
0
def network_FLiER_test(network_filename,
                       pmus,
                       noise,
                       use_filter=True,
                       test_branches=None,
                       FLiER_type="Jacobian",
                       verbose=False):
    """
    Simulate the failure of each line in the network one at a run, and run
    FLiER for each case.
    
    Inputs:
    network_filename - The path to the file containing the network in IEEE CDF
       format.
    pmus - A list of bus indices with PMUs on them.
    
    Outputs:
    
    """
    if network_filename.endswith('.m'):
        powernet = PowerNetwork.powernet_from_matpower(network_filename)
    else:
        powernet = PowerNetwork.powernet_from_IEEECDF(network_filename)
    num_lines = len(powernet.branches)
    solution_ranks = []  # Contains ranks of correct answers
    score_list = []  # Contains tij's
    fraction_ts_computed_list = []  # How many tij's computed per test.
    dict_of_score_dicts = dict()
    num_filtered_out = 0

    V = powernet.simulate()
    pre_event_volts = np.hstack([np.angle(V), np.abs(V)])

    if test_branches is None:
        test_branches = range(len(powernet.branches))
    for i in test_branches:
        branch = powernet.branches[i]
        if verbose:
            print "On line {0}".format([bus.index for bus in branch.buses])
        out = single_FLiER_test(powernet,
                                pmus,
                                noise,
                                branch_to_fail=branch,
                                use_filter=use_filter,
                                pre_event_volts=pre_event_volts,
                                FLiER_type=FLiER_type,
                                verbose=verbose)
        score_dict, initial_score_dict, fraction_ts_computed = out
        if score_dict == False:
            # then power flow equation solver did not converge
            continue

        fraction_ts_computed_list.append(fraction_ts_computed)
        if i in score_dict:
            score_of_solution = score_dict[i]
            rank = np.sum(score_dict.values() <= score_of_solution)
            solution_ranks.append(rank)
        else:
            # Then the correct line was filtered out.
            # Just choose the rank as last, and some large tij
            # filler value.
            if verbose:
                print "Solution filtered out."
            score_of_solution = 100
            rank = num_lines
            solution_ranks.append(rank)
            num_filtered_out += 1
        dict_of_score_dicts[i] = (score_dict, initial_score_dict,
                                  fraction_ts_computed)

        # Produce a list of (key, value) dictionary elements sorted by value.
        # Then split that list to create a sorted list of keys and a sorted list of
        # values. Recall that the keys are indices into the list of edges.
        branch_inds, scores = zip(
            *sorted(score_dict.iteritems(), key=lambda x: x[1]))

        if verbose:
            print score_of_solution, rank, len(score_dict)
        score_list.append((scores, score_of_solution))

    if verbose:
        print "{0} filtered out entirely.".format(num_filtered_out)
    return score_list, dict_of_score_dicts, powernet.branches, solution_ranks, fraction_ts_computed_list
コード例 #4
0
def network_FLiER_test(network_filename, pmus, noise, use_filter=True, 
                       test_branches = None, FLiER_type = "Jacobian", 
                       verbose=False):
    """
    Simulate the failure of each line in the network one at a run, and run
    FLiER for each case.
    
    Inputs:
    network_filename - The path to the file containing the network in IEEE CDF
       format.
    pmus - A list of bus indices with PMUs on them.
    
    Outputs:
    
    """
    if network_filename.endswith('.m'):
        powernet = PowerNetwork.powernet_from_matpower(network_filename)
    else:
        powernet = PowerNetwork.powernet_from_IEEECDF(network_filename)
    num_lines = len(powernet.branches)
    solution_ranks = [] # Contains ranks of correct answers
    score_list = [] # Contains tij's
    fraction_ts_computed_list = [] # How many tij's computed per test.
    dict_of_score_dicts = dict()
    num_filtered_out = 0
    
    V = powernet.simulate()
    pre_event_volts = np.hstack([np.angle(V), np.abs(V)])

    if test_branches is None:
        test_branches = range(len(powernet.branches))
    for i in test_branches:
        branch = powernet.branches[i]
        if verbose:
            print "On line {0}".format([bus.index for bus in branch.buses])
        out = single_FLiER_test(powernet, pmus, noise, branch_to_fail=branch, 
                                use_filter=use_filter, pre_event_volts=pre_event_volts,
                                FLiER_type=FLiER_type, verbose=verbose)
        score_dict, initial_score_dict, fraction_ts_computed = out
        if score_dict == False:
            # then power flow equation solver did not converge
            continue
        
        fraction_ts_computed_list.append(fraction_ts_computed)
        if i in score_dict:
            score_of_solution = score_dict[i]
            rank = np.sum(score_dict.values() <= score_of_solution)
            solution_ranks.append(rank)
        else:
            # Then the correct line was filtered out.
            # Just choose the rank as last, and some large tij
            # filler value.
            if verbose:
                print "Solution filtered out."
            score_of_solution = 100
            rank = num_lines
            solution_ranks.append(rank)
            num_filtered_out += 1
        dict_of_score_dicts[i] = (score_dict, initial_score_dict, fraction_ts_computed)
            
        # Produce a list of (key, value) dictionary elements sorted by value.
        # Then split that list to create a sorted list of keys and a sorted list of
        # values. Recall that the keys are indices into the list of edges.
        branch_inds, scores = zip(*sorted(score_dict.iteritems(), key=lambda x: x[1]))
        
        if verbose:
            print score_of_solution, rank, len(score_dict)
        score_list.append((scores, score_of_solution))
        
    if verbose:
        print "{0} filtered out entirely.".format(num_filtered_out)
    return score_list, dict_of_score_dicts, powernet.branches, solution_ranks, fraction_ts_computed_list