@@ -9,24 +9,37 @@ busfile - .csv file that contains geographic locations of all buses
branchfile - optional .csv file that contains transmission line lengths
nddfile - .jld2 file in which the final network data dictionary will be saved
=#
"""
build_network_data(file::String; kwargs...)
Reads a .RAW or .m `file` containing the data of a power grid and calculates the power flow according to the chosen model (see below). Returns a network data dictionary conform with the [PowerModels Network Data Format](https://lanl-ansi.github.io/PowerModels.jl/stable/network-data/) that also contains the calculated power flow solution.
Returns an array of tuples containing branch indices of branches with a minimum loading of min_loading and their actual loading (considering the power flow type pf_type). Choosing min_loading = 1. yields overloaded branches.
=#
# Returns an array of tuples that each contain information about specific branches of interest. The tuples contain the respective branch index, the loading of the branch and a boolean value set to `1`, if the branch is a transformer, or `0`, if the branch is a transmission line.
Returns the indices of branches in `network_data` that are loaded (flow/capacity) by at least `min_loading` (see **keyword arguments**).
# Keyword Arguments
`pf_type` can be set to `"MVA-loading"` (default), `"MW-loading"` or `"Mvar-loading"` and specifies whether the loading due to apparent (MVA), active (MW) or reactive power (Mvar) should be considered. If `network_data["pf_model"] => :dc` meaning that it was last updated using a DC power flow solution, `pf_type` is automatically set to `"MW-loading"`.
`min_loading` is a `Float64` (default `0.0`) that specifies the minimum loading of branches to be returned (according to `pf_type`). Only branches with a loading equal or above `min_loading` will be returned. Hence, `min_loading = 1.0` can be used to obtain overloaded branches.
`br_status` can be set to `1` (default) or `0` and specifies whether active (`1`) or inactive branches (`0`) should be returned.
"""
function get_branches(
network_data::Dict{String,<:Any};
pf_type="MVA-loading",# or "MW-loading", "Mvar-loading"
min_loading=0.0,# minimum loading of branches
pf_type="MVA-loading"# or "MW-loading", "Mvar-loading"
br_status=1# active or inactive branches
)
branches=[
(i,br[pf_type])for(i,br)innetwork_data["branch"]
ifbr["br_status"]==1&&br[pf_type]>=min_loading
]
### Check whether network_data contains a DC-PF solution
ifnetwork_data["pf_model"]==:dc
pf_type="MW-loading"# use branch loadings due to active power
end
# branches = [
# (i, br[pf_type], br["transformer"]) for (i, br) in network_data["branch"] if br["br_status"] == 1 && br[pf_type] >= min_loading
Function that deactivates (overhead) transmission lines identified by their indices (string) in tl_ids that have been destroyed by a hurricane. For any given line index all parallel lines also become deactivated. The status of these lines is set to 0 in network_data. Afterwards PowerModels function simplify_network! is run in order to remove dangling buses etc. that might occur due to the failures.
=#
function destroy_tl!(
network_data::Dict{String,<:Any},
tl_ids::Array# string ids of destroyed transmission lines
tl_ids::Array# string-ID's of destroyed transmission lines
Checks whether active buses in `network_data` have voltage magnitudes outside of their tolerance interval (usually ranging from 0.9 p.u. to 1.1 p.u.) and returns a dictionary with the bus indices as keys and the respective active loads connected to the bus as values (PQ-generators are excluded). ITCPG's logger prints a `warn` message everytime a voltage violation is detected.
# Keyword Arguments
`limit` can be set to `"lower"`, `"upper"` or `"both"` (default) and specifies which voltage bound should be considered. If e.g. `limit = "lower"` is chosen, only buses with under-voltages are returned.
`filter_active_loads` can be set to `true` or `false` (default) and specifies whether only buses with active loads connected to them should be returned (useful for under-voltage load shedding).
See also: [`run_outer_ac_pf!`](@ref), [`run_uvls!`](@ref)
"""
function check_voltage_bounds(
network_data::Dict{String,<:Any};
limit="both",# considered voltage bounds
filter_active_loads=false# whether to filter buses with active loads
)
vv_buses=Dict{String,Array{String,1}}()
### Go through buses and check their voltage magnitudes
for(key,bus)innetwork_data["bus"]
vm=bus["vm"]
iflimit=="both"||limit=="lower"
ifvm<bus["vmin"]&&bus["bus_type"]!=4
warn(LOGGER,
"Bus $key (type $(network_data["bus"][key]["bus_type"])) has a voltage magnitude of |V|=$vm that is below its lower bound of $(bus["vmin"])"
)
### Get string indices of active loads connected to the bus
connected_loads=[
l_idfor(l_id,l)innetwork_data["load"]if
l["load_bus"]==parse(Int64,key)&&l["status"]==1&&
haskey(l,"is_gen")==false# exclude PQ-generators
]
vv_buses[key]=connected_loads
end
end
iflimit=="both"||limit=="upper"
ifvm>bus["vmax"]&&bus["bus_type"]!=4
warn(LOGGER,
"Bus $key (type $(network_data["bus"][key]["bus_type"])) has a voltage magnitude of |V|=$vm that is above its upper bound of $(bus["vmax"])"
)
### Get string indices of active loads connected to the bus
connected_loads=[
l_idfor(l_id,l)innetwork_data["load"]if
l["load_bus"]==parse(Int64,key)&&l["status"]==1&&
haskey(l,"is_gen")==false# exclude PQ-generators
]
vv_buses[key]=connected_loads
end
end
end
### Check whether to exclude buses without active loads
iffilter_active_loads==true
filter!(x->isempty(last(x))==false,vv_buses)
end
returnvv_buses
end
function check_gen_bounds(network_data::Dict{String,<:Any})
for(i,gen)innetwork_data["gen"]
ifgen["gen_status"]==1
ifgen["pg"]>gen["pmax"]||gen["pg"]<gen["pmin"]
warn(LOGGER,
"Active power dispatch of generator $i lies outside of interval [$(gen["pmin"]), $(gen["pmax"])]: $(gen["pg"])"
)
end
ifgen["qg"]>gen["qmax"]||gen["qg"]<gen["qmin"]
warn(LOGGER,
"Reactive power dispatch of generator $i lies outside of interval [$(gen["qmin"]), $(gen["qmax"])]: $(gen["qg"])"
)
end
end
end
returnnothing
end
function check_gen_bounds(
network_data::Dict{String,<:Any},
cc::Set{Int64},# set of bus indices in connected component
cc_i::Int64;# index/number of connected components
power="active"# "active": active power, "both": reactive power too
)
cc_slack=[
b["index"]forbinvalues(network_data["bus"])if
b["bus_type"]==3&&b["index"]∈cc
]
iflength(cc_slack)!=1
warn(LOGGER,"(Sub-)Grid #$cc_i has $(length(cc_slack)) slack buses!")
end
for(i,gen)innetwork_data["gen"]
ifgen["gen_bus"]∈cc&&gen["gen_status"]==1
ifgen["gen_bus"]∈cc_slack
info(LOGGER,
"Active power dispatch of slack bus $i in #$cc_i with interval [$(gen["pmin"]), $(gen["pmax"])]: $(gen["pg"])"
)
else
ifgen["pg"]>gen["pmax"]||gen["pg"]<gen["pmin"]
warn(LOGGER,"Active power dispatch of generator $i in #$cc_i lies outside of interval [$(gen["pmin"]), $(gen["pmax"])]: $(gen["pg"])")
end
ifpower=="both"
ifgen["qg"]>gen["qmax"]||gen["qg"]<gen["qmin"]
warn(LOGGER,
"Reactive power dispatch of generator $i lies outside of interval [$(gen["qmin"]), $(gen["qmax"])]: $(gen["qg"])"