diff --git a/src/descriptors/power_system_structs.json b/src/descriptors/power_system_structs.json index bcc7ca38f0..cf7d394fdc 100644 --- a/src/descriptors/power_system_structs.json +++ b/src/descriptors/power_system_structs.json @@ -1469,16 +1469,22 @@ "name": "number_of_steps", "comment": "Number of steps for adjustable shunt", "null_value": "0", - "data_type": "Int", + "data_type": "Union{Int, Vector{Int}}", "default": "0" }, { "name": "Y_increase", "comment": "Admittance increment for each of step increase", "null_value": "0", - "data_type": "Complex{Float64}", + "data_type": "Union{Complex{Float64}, Vector{Complex{Float64}}}", "default": "0" }, + { "name": "admittance_limits", + "comment": "Shunt admittance limits for switched shunt model", + "null_value": "(min=0.0, max=0.0)", + "data_type": "MinMax", + "default": "(min=1.0, max=1.0)" + }, { "name": "dynamic_injector", "data_type": "Union{Nothing, DynamicInjection}", diff --git a/src/models/generated/SwitchedAdmittance.jl b/src/models/generated/SwitchedAdmittance.jl index 905e14f9c8..6043c5959d 100644 --- a/src/models/generated/SwitchedAdmittance.jl +++ b/src/models/generated/SwitchedAdmittance.jl @@ -10,8 +10,9 @@ This file is auto-generated. Do not edit. available::Bool bus::ACBus Y::Complex{Float64} - number_of_steps::Int - Y_increase::Complex{Float64} + number_of_steps::Union{Int, Vector{Int}} + Y_increase::Union{Complex{Float64}, Vector{Complex{Float64}}} + admittance_limits::MinMax dynamic_injector::Union{Nothing, DynamicInjection} services::Vector{Service} ext::Dict{String, Any} @@ -27,8 +28,9 @@ Most often used in power flow studies, iterating over the steps to see impacts o - `available::Bool`: Indicator of whether the component is connected and online (`true`) or disconnected, offline, or down (`false`). Unavailable components are excluded during simulations - `bus::ACBus`: Bus that this component is connected to - `Y::Complex{Float64}`: Initial admittance at N = 0 -- `number_of_steps::Int`: (default: `0`) Number of steps for adjustable shunt -- `Y_increase::Complex{Float64}`: (default: `0`) Admittance increment for each of step increase +- `number_of_steps::Union{Int, Vector{Int}}`: (default: `0`) Number of steps for adjustable shunt +- `Y_increase::Union{Complex{Float64}, Vector{Complex{Float64}}}`: (default: `0`) Admittance increment for each of step increase +- `admittance_limits::MinMax`: (default: `(min=1.0, max=1.0)`) Shunt admittance limits for switched shunt model - `dynamic_injector::Union{Nothing, DynamicInjection}`: (default: `nothing`) corresponding dynamic injection model for admittance - `services::Vector{Service}`: (default: `Device[]`) Services that this device contributes to - `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation, such as latitude and longitude. @@ -44,9 +46,11 @@ mutable struct SwitchedAdmittance <: ElectricLoad "Initial admittance at N = 0" Y::Complex{Float64} "Number of steps for adjustable shunt" - number_of_steps::Int + number_of_steps::Union{Int, Vector{Int}} "Admittance increment for each of step increase" - Y_increase::Complex{Float64} + Y_increase::Union{Complex{Float64}, Vector{Complex{Float64}}} + "Shunt admittance limits for switched shunt model" + admittance_limits::MinMax "corresponding dynamic injection model for admittance" dynamic_injector::Union{Nothing, DynamicInjection} "Services that this device contributes to" @@ -57,12 +61,12 @@ mutable struct SwitchedAdmittance <: ElectricLoad internal::InfrastructureSystemsInternal end -function SwitchedAdmittance(name, available, bus, Y, number_of_steps=0, Y_increase=0, dynamic_injector=nothing, services=Device[], ext=Dict{String, Any}(), ) - SwitchedAdmittance(name, available, bus, Y, number_of_steps, Y_increase, dynamic_injector, services, ext, InfrastructureSystemsInternal(), ) +function SwitchedAdmittance(name, available, bus, Y, number_of_steps=0, Y_increase=0, admittance_limits=(min=1.0, max=1.0), dynamic_injector=nothing, services=Device[], ext=Dict{String, Any}(), ) + SwitchedAdmittance(name, available, bus, Y, number_of_steps, Y_increase, admittance_limits, dynamic_injector, services, ext, InfrastructureSystemsInternal(), ) end -function SwitchedAdmittance(; name, available, bus, Y, number_of_steps=0, Y_increase=0, dynamic_injector=nothing, services=Device[], ext=Dict{String, Any}(), internal=InfrastructureSystemsInternal(), ) - SwitchedAdmittance(name, available, bus, Y, number_of_steps, Y_increase, dynamic_injector, services, ext, internal, ) +function SwitchedAdmittance(; name, available, bus, Y, number_of_steps=0, Y_increase=0, admittance_limits=(min=1.0, max=1.0), dynamic_injector=nothing, services=Device[], ext=Dict{String, Any}(), internal=InfrastructureSystemsInternal(), ) + SwitchedAdmittance(name, available, bus, Y, number_of_steps, Y_increase, admittance_limits, dynamic_injector, services, ext, internal, ) end # Constructor for demo purposes; non-functional. @@ -74,6 +78,7 @@ function SwitchedAdmittance(::Nothing) Y=0.0, number_of_steps=0, Y_increase=0, + admittance_limits=(min=0.0, max=0.0), dynamic_injector=nothing, services=Device[], ext=Dict{String, Any}(), @@ -92,6 +97,8 @@ get_Y(value::SwitchedAdmittance) = value.Y get_number_of_steps(value::SwitchedAdmittance) = value.number_of_steps """Get [`SwitchedAdmittance`](@ref) `Y_increase`.""" get_Y_increase(value::SwitchedAdmittance) = value.Y_increase +"""Get [`SwitchedAdmittance`](@ref) `admittance_limits`.""" +get_admittance_limits(value::SwitchedAdmittance) = value.admittance_limits """Get [`SwitchedAdmittance`](@ref) `dynamic_injector`.""" get_dynamic_injector(value::SwitchedAdmittance) = value.dynamic_injector """Get [`SwitchedAdmittance`](@ref) `services`.""" @@ -111,6 +118,8 @@ set_Y!(value::SwitchedAdmittance, val) = value.Y = val set_number_of_steps!(value::SwitchedAdmittance, val) = value.number_of_steps = val """Set [`SwitchedAdmittance`](@ref) `Y_increase`.""" set_Y_increase!(value::SwitchedAdmittance, val) = value.Y_increase = val +"""Set [`SwitchedAdmittance`](@ref) `admittance_limits`.""" +set_admittance_limits!(value::SwitchedAdmittance, val) = value.admittance_limits = val """Set [`SwitchedAdmittance`](@ref) `services`.""" set_services!(value::SwitchedAdmittance, val) = value.services = val """Set [`SwitchedAdmittance`](@ref) `ext`.""" diff --git a/src/models/generated/includes.jl b/src/models/generated/includes.jl index 298ae42649..d2cb893bcb 100644 --- a/src/models/generated/includes.jl +++ b/src/models/generated/includes.jl @@ -475,6 +475,7 @@ export get_active_power_limits export get_active_power_limits_from export get_active_power_limits_pump export get_active_power_limits_to +export get_admittance_limits export get_angle export get_angle_limits export get_arc @@ -1045,6 +1046,7 @@ export set_active_power_limits! export set_active_power_limits_from! export set_active_power_limits_pump! export set_active_power_limits_to! +export set_admittance_limits! export set_angle! export set_angle_limits! export set_arc! diff --git a/src/parsers/pm_io/psse.jl b/src/parsers/pm_io/psse.jl index 4b66643e5f..85be3c5c9f 100644 --- a/src/parsers/pm_io/psse.jl +++ b/src/parsers/pm_io/psse.jl @@ -95,6 +95,7 @@ function _create_starbus_from_transformer( starbus["bus_type"] = bus_type starbus["area"] = _get_bus_value(transformer["I"], "area", pm_data) starbus["zone"] = _get_bus_value(transformer["I"], "zone", pm_data) + starbus["hidden"] = true starbus["source_id"] = push!( ["transformer", starbus["bus_i"], starbus["name"]], transformer["I"], @@ -288,6 +289,7 @@ function _psse2pm_bus!(pm_data::Dict, pti_data::Dict, import_all::Bool) sub_data["name"] = pop!(bus, "NAME") sub_data["vmax"] = pop!(bus, "NVHI") sub_data["vmin"] = pop!(bus, "NVLO") + sub_data["hidden"] = false sub_data["source_id"] = ["bus", "$(bus["I"])"] sub_data["index"] = pop!(bus, "I") @@ -347,26 +349,24 @@ function _psse2pm_shunt!(pm_data::Dict, pti_data::Dict, import_all::Bool) @info "Parsing PSS(R)E Shunt data into a PowerModels Dict..." pm_data["shunt"] = [] - - pm_data["fixed_shunt"] = [] if haskey(pti_data, "FIXED SHUNT") - for fixed_shunt in pti_data["FIXED SHUNT"] + for shunt in pti_data["FIXED SHUNT"] sub_data = Dict{String, Any}() - sub_data["shunt_bus"] = pop!(fixed_shunt, "I") - sub_data["gs"] = pop!(fixed_shunt, "GL") - sub_data["bs"] = pop!(fixed_shunt, "BL") - sub_data["status"] = pop!(fixed_shunt, "STATUS") + sub_data["shunt_bus"] = pop!(shunt, "I") + sub_data["gs"] = pop!(shunt, "GL") + sub_data["bs"] = pop!(shunt, "BL") + sub_data["status"] = pop!(shunt, "STATUS") sub_data["source_id"] = - ["fixed shunt", sub_data["shunt_bus"], pop!(fixed_shunt, "ID")] - sub_data["index"] = length(pm_data["fixed_shunt"]) + 1 + ["fixed shunt", sub_data["shunt_bus"], pop!(shunt, "ID")] + sub_data["index"] = length(pm_data["shunt"]) + 1 if import_all - _import_remaining_keys!(sub_data, fixed_shunt) + _import_remaining_keys!(sub_data, shunt) end - push!(pm_data["fixed_shunt"], sub_data) + push!(pm_data["shunt"], sub_data) end end @@ -381,13 +381,15 @@ function _psse2pm_shunt!(pm_data::Dict, pti_data::Dict, import_all::Bool) sub_data["gs"] = 0.0 sub_data["bs"] = pop!(switched_shunt, "BINIT") sub_data["status"] = pop!(switched_shunt, "STAT") + sub_data["admittance_limits"] = (pop!(switched_shunt, "VSWLO"), pop!(switched_shunt, "VSWHI")) + + step_numbers = Dict(k => v for (k, v) in switched_shunt if startswith(k, "N") && isdigit(last(k))) + step_numbers_sorted = sort(collect(keys(step_numbers)), by = x -> parse(Int, x[2:end])) + sub_data["step_number"] = [step_numbers[k] for k in step_numbers_sorted] - # Add remaining data - sub_data["upper_limit"] = pop!(switched_shunt, "VSWHI") - sub_data["lower_limit"] = pop!(switched_shunt, "VSWLO") - sub_data["step_number"] = Dict(k => v for (k, v) in switched_shunt if startswith(k, "N") && isdigit(last(k))) - sub_data["b_increment"] = Dict(k => v for (k, v) in switched_shunt if startswith(k, "B") && isdigit(last(k))) - # ================== + y_increment = Dict(k => v for (k, v) in switched_shunt if startswith(k, "B") && isdigit(last(k))) + y_increment_sorted = sort(collect(keys(y_increment)), by = x -> parse(Int, x[2:end])) + sub_data["y_increment"] = [y_increment[k] for k in y_increment_sorted] sub_data["source_id"] = ["switched shunt", sub_data["shunt_bus"], pop!(switched_shunt, "SWREM")] @@ -622,21 +624,21 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) push!(pm_data["branch"], sub_data) else # Three-winding Transformers + # Create 3w-transformer key + if !haskey(pm_data, "3w_transformer") + pm_data["3w_transformer"] = [] + end + bus_id1, bus_id2, bus_id3 = transformer["I"], transformer["J"], transformer["K"] # Creates a starbus (or "dummy" bus) to which each winding of the transformer will connect starbus = _create_starbus_from_transformer(pm_data, transformer, starbus_id) pm_data["bus"][starbus_id] = starbus - starbus_id += 1 - - ## Warn on adding the dummy bus when exporting to a PSSE file / test it on the function / # Create 3 branches from a three winding transformer (one for each winding, which will each connect to the starbus) - br_r12, br_r23, br_r31 = - transformer["R1-2"], transformer["R2-3"], transformer["R3-1"] - br_x12, br_x23, br_x31 = - transformer["X1-2"], transformer["X2-3"], transformer["X3-1"] + br_r12, br_r23, br_r31 = transformer["R1-2"], transformer["R2-3"], transformer["R3-1"] + br_x12, br_x23, br_x31 = transformer["X1-2"], transformer["X2-3"], transformer["X3-1"] # Unit Transformations if transformer["CZ"] == 3 # "for transformer load loss in watts and impedance magnitude in pu on a specified MVA base and winding voltage base." @@ -651,37 +653,13 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) # Unit Transformations if transformer["CZ"] != 1 # NOT "for resistance and reactance in pu on system MVA base and winding voltage base" - br_r12 *= - ( - transformer["NOMV1"] / - _get_bus_value(bus_id1, "base_kv", pm_data) - )^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"]) - br_r23 *= - ( - transformer["NOMV2"] / - _get_bus_value(bus_id2, "base_kv", pm_data) - )^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"]) - br_r31 *= - ( - transformer["NOMV3"] / - _get_bus_value(bus_id3, "base_kv", pm_data) - )^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"]) + br_r12 *= (transformer["NOMV1"]/_get_bus_value(bus_id1, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"]) + br_r23 *= (transformer["NOMV2"]/_get_bus_value(bus_id2, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"]) + br_r31 *= (transformer["NOMV3"]/_get_bus_value(bus_id3, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"]) - br_x12 *= - ( - transformer["NOMV1"] / - _get_bus_value(bus_id1, "base_kv", pm_data) - )^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"]) - br_x23 *= - ( - transformer["NOMV2"] / - _get_bus_value(bus_id2, "base_kv", pm_data) - )^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"]) - br_x31 *= - ( - transformer["NOMV3"] / - _get_bus_value(bus_id3, "base_kv", pm_data) - )^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"]) + br_x12 *= (transformer["NOMV1"]/_get_bus_value(bus_id1, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"]) + br_x23 *= (transformer["NOMV2"]/_get_bus_value(bus_id2, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"]) + br_x31 *= (transformer["NOMV3"]/_get_bus_value(bus_id3, "base_kv", pm_data))^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"]) end # See "Power System Stability and Control", ISBN: 0-07-035958-X, Eq. 6.72 @@ -692,6 +670,79 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) Zx_s = 1 / 2 * (br_x23 - br_x31 + br_x12) Zx_t = 1 / 2 * (br_x31 - br_x12 + br_x23) + # Add parameters to the 3w-transformer key + sub_data = Dict{String, Any}() + sub_data["name"] = transformer["NAME"] + if transformer["STAT"] != 0 + sub_data["available"] = true + end + # sub_data["primary_secondary_arc"] = 0.0 + # sub_data["secondary_tertiary_arc"] = 0.0 + # sub_data["primary_tertiary_arc"] = 0.0 + sub_data["star_bus"] = starbus_id + + # sub_data["active_power_flow_primary"] = 0.0 + # sub_data["reactive_power_flow_primary"] = 0.0 + # sub_data["active_power_flow_secondary"] = 0.0 + # sub_data["reactive_power_flow_secondary"] = 0.0 + # sub_data["active_power_flow_tertiary"] = 0.0 + # sub_data["reactive_power_flow_tertiary"] = 0.0 + + sub_data["r_primary"] = Zr_p + sub_data["x_primary"] = Zx_p + sub_data["r_secondary"] = Zr_s + sub_data["x_secondary"] = Zx_s + sub_data["r_tertiary"] = Zr_t + sub_data["x_tertiary"] = Zx_t + + sub_data["rating_primary"] = min(transformer["RATA1"], transformer["RATB1"], transformer["RATC1"]) + sub_data["rating_secondary"] = min(transformer["RATA2"], transformer["RATB2"], transformer["RATC2"]) + sub_data["rating_tertiary"] = min(transformer["RATA3"], transformer["RATB3"], transformer["RATC3"]) + sub_data["rating"] = min(sub_data["rating_primary"], sub_data["rating_secondary"], sub_data["rating_tertiary"]) + + sub_data["r_12"] = br_r12 + sub_data["x_12"] = br_x12 + sub_data["r_23"] = br_r23 + sub_data["x_23"] = br_x23 + sub_data["r_13"] = br_r31 + sub_data["x_13"] = br_x31 + sub_data["g"] = transformer["MAG1"] + sub_data["b"] = transformer["MAG2"] + + if transformer["CW"] == 1 + sub_data["primary_turns_ratio"] = transformer["WINDV1"] + sub_data["secondary_turns_ratio"] = transformer["WINDV2"] + sub_data["tertiary_turns_ratio"] = transformer["WINDV3"] + else + sub_data["primary_turns_ratio"] = 1.0 + sub_data["secondary_turns_ratio"] = 1.0 + sub_data["tertiary_turns_ratio"] = 1.0 + end + + sub_data["circuit"] = transformer["CKT"] + + sub_data["index"] = length(pm_data["3w_transformer"]) + 1 + + if import_all + _import_remaining_keys!( + sub_data, + transformer; + exclude = [ + "NAME", + "STAT", + "MAG1", + "MAG2", + "WINDV1", + "WINDV2", + "WINDV3" + ], + ) + end + + push!(pm_data["3w_transformer"], sub_data) + + starbus_id += 1 # after adding the 1st 3WT, increase the counter + # Build each of the three transformer branches for (m, (bus_id, br_r, br_x)) in enumerate( zip( @@ -818,6 +869,7 @@ function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool) ) end + # TODO: Delete this line (changes to 3W Transformer) push!(pm_data["branch"], sub_data) end end @@ -1054,7 +1106,7 @@ function _pti_to_powermodels!( _psse2pm_dcline!(pm_data, pti_data, import_all) _psse2pm_storage!(pm_data, pti_data, import_all) _psse2pm_switch!(pm_data, pti_data, import_all) - + if import_all _import_remaining_comps!( pm_data, diff --git a/src/parsers/power_models_data.jl b/src/parsers/power_models_data.jl index f95094ebf1..1a6d5d9a54 100644 --- a/src/parsers/power_models_data.jl +++ b/src/parsers/power_models_data.jl @@ -847,7 +847,7 @@ function read_dcline!( end function make_shunt(name::String, d::Dict, bus::ACBus) - return FixedAdmittance(; + return (; name = name, available = Bool(d["status"]), bus = bus,