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))
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))
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
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