From 4e3f03015ecb7de4cc59df087438c6f766a6e2a3 Mon Sep 17 00:00:00 2001 From: jtabarez <53569964+jtabarez@users.noreply.github.com> Date: Thu, 13 Jun 2024 07:36:57 -0600 Subject: [PATCH] inverter test models added and phases added to components --- src/PowerModelsProtection.jl | 2 +- src/core/admittance_matrix.jl | 225 ++++++++++++++++++++++----------- src/core/data.jl | 50 ++++++++ src/core/types.jl | 6 +- src/data_model/components.jl | 21 ++- src/data_model/eng2math.jl | 17 +-- src/data_model/eng2math_pmd.jl | 2 +- src/io/dss/dss2eng.jl | 60 +++++++-- src/io/opendss.jl | 2 +- test/fs_mc.jl | 13 -- test/pf_mc.jl | 15 --- 11 files changed, 288 insertions(+), 125 deletions(-) diff --git a/src/PowerModelsProtection.jl b/src/PowerModelsProtection.jl index 8f13fa8..29c6da7 100644 --- a/src/PowerModelsProtection.jl +++ b/src/PowerModelsProtection.jl @@ -17,6 +17,7 @@ module PowerModelsProtection const _SP = SparseArrays import JSON + include("core/types.jl") include("core/admittance_matrix.jl") include("core/variable.jl") include("core/constraint_template.jl") @@ -26,7 +27,6 @@ module PowerModelsProtection include("core/expression.jl") include("core/ref.jl") include("core/objective.jl") - include("core/types.jl") include("core/solution.jl") include("data_model/units.jl") diff --git a/src/core/admittance_matrix.jl b/src/core/admittance_matrix.jl index d08ac64..efe1854 100644 --- a/src/core/admittance_matrix.jl +++ b/src/core/admittance_matrix.jl @@ -125,9 +125,9 @@ function add_mc_2w_transformer_p_matrix!(transformer::Dict{String,<:Any}, data:: t_bus = transformer["t_bus"] for (_j, j) in enumerate(transformer["t_connections"]) if haskey(data["admittance_map"], (t_bus, j)) - if transformer["dss"]["phases"] == 3 + if transformer["phases"] == 3 haskey(admit_matrix, (data["admittance_map"][(f_bus, i)], data["admittance_map"][(t_bus, j)])) ? admit_matrix[(data["admittance_map"][(f_bus, i)], data["admittance_map"][(t_bus, j)])] += transformer["p_matrix"][_i,_j+4] : admit_matrix[(data["admittance_map"][(f_bus, i)], data["admittance_map"][(t_bus, j)])] = transformer["p_matrix"][_i,_j+4] - elseif transformer["dss"]["phases"] == 1 + elseif transformer["phases"] == 1 haskey(admit_matrix, (data["admittance_map"][(f_bus, i)], data["admittance_map"][(t_bus, j)])) ? admit_matrix[(data["admittance_map"][(f_bus, i)], data["admittance_map"][(t_bus, j)])] += transformer["p_matrix"][_i,_j+2] : admit_matrix[(data["admittance_map"][(f_bus, i)], data["admittance_map"][(t_bus, j)])] = transformer["p_matrix"][_i,_j+2] end end @@ -139,9 +139,9 @@ function add_mc_2w_transformer_p_matrix!(transformer::Dict{String,<:Any}, data:: if haskey(data["admittance_map"], (t_bus, i)) for (_j, j) in enumerate(transformer["t_connections"]) if haskey(data["admittance_map"], (t_bus, j)) - if transformer["dss"]["phases"] == 3 + if transformer["phases"] == 3 haskey(admit_matrix, (data["admittance_map"][(t_bus, i)], data["admittance_map"][(t_bus, j)])) ? admit_matrix[(data["admittance_map"][(t_bus, i)], data["admittance_map"][(t_bus, j)])] += transformer["p_matrix"][_i+4,_j+4] : admit_matrix[(data["admittance_map"][(t_bus, i)], data["admittance_map"][(t_bus, j)])] = transformer["p_matrix"][_i+4,_j+4] - elseif transformer["dss"]["phases"] == 1 + elseif transformer["phases"] == 1 haskey(admit_matrix, (data["admittance_map"][(t_bus, i)], data["admittance_map"][(t_bus, j)])) ? admit_matrix[(data["admittance_map"][(t_bus, i)], data["admittance_map"][(t_bus, j)])] += transformer["p_matrix"][_i+2,_j+2] : admit_matrix[(data["admittance_map"][(t_bus, i)], data["admittance_map"][(t_bus, j)])] = transformer["p_matrix"][_i+2,_j+2] end end @@ -149,9 +149,9 @@ function add_mc_2w_transformer_p_matrix!(transformer::Dict{String,<:Any}, data:: f_bus = transformer["f_bus"] for (_j, j) in enumerate(transformer["f_connections"]) if haskey(data["admittance_map"], (f_bus, j)) - if transformer["dss"]["phases"] == 3 + if transformer["phases"] == 3 haskey(admit_matrix, (data["admittance_map"][(t_bus, i)], data["admittance_map"][(f_bus, j)])) ? admit_matrix[(data["admittance_map"][(t_bus, i)], data["admittance_map"][(f_bus, j)])] += transformer["p_matrix"][_i+4,_j] : admit_matrix[(data["admittance_map"][(t_bus, i)], data["admittance_map"][(f_bus, j)])] = transformer["p_matrix"][_i+4,_j] - elseif transformer["dss"]["phases"] == 1 + elseif transformer["phases"] == 1 haskey(admit_matrix, (data["admittance_map"][(t_bus, i)], data["admittance_map"][(f_bus, j)])) ? admit_matrix[(data["admittance_map"][(t_bus, i)], data["admittance_map"][(f_bus, j)])] += transformer["p_matrix"][_i+2,_j] : admit_matrix[(data["admittance_map"][(t_bus, i)], data["admittance_map"][(f_bus, j)])] = transformer["p_matrix"][_i+2,_j] end end @@ -279,33 +279,56 @@ end """ function build_mc_current_vector(data::Dict{String,<:Any}, v::Matrix{ComplexF64}) i = zeros(Complex{Float64}, length(keys(data["admittance_type"])), 1) -# TODO look at models for gen and how they are defined + # TODO look at models for gen and how they are defined for (_, gen) in data["gen"] -if occursin("voltage_source.", gen["source_id"]) - if gen["gen_status"] == 1 - bus = data["bus"][string(gen["gen_bus"])] - n = 3 #TODO fix when 4 is included - p_matrix = zeros(Complex{Float64}, n, n) - v = zeros(Complex{Float64}, n, 1) - for i in gen["connections"] - if i != 4 - v[i,1] = bus["vm"][i] * data["settings"]["voltage_scale_factor"] * exp(1im * bus["va"][i] * pi/180) - for j in gen["connections"] - if j != 4 - p_matrix[i,j] = gen["p_matrix"][i,j] + if gen["element"] == SolarElement + if gen["gen_status"] == 1 && gen["grid_forming"] + bus = data["bus"][string(gen["gen_bus"])] + n = 3 #TODO fix when 4 is included + p_matrix = zeros(Complex{Float64}, n, n) + va = [0 -2*pi/3 2*pi/3] + for i in gen["connections"] + if i != 4 + v[i,1] = bus["vbase"] * data["settings"]["voltage_scale_factor"] * exp(1im * va[i]) + for j in gen["connections"] + if j != 4 + p_matrix[i,j] = gen["p_matrix"][i,j] + end + end end end - end - end i_update = p_matrix * v - for (_j, j) in enumerate(gen["connections"]) - if (gen["gen_bus"], j) in keys(data["admittance_map"]) - i[data["admittance_map"][(gen["gen_bus"], j)],1] = i_update[_j,1] + for (_j, j) in enumerate(gen["connections"]) + if (gen["gen_bus"], j) in keys(data["admittance_map"]) + i[data["admittance_map"][(gen["gen_bus"], j)],1] = i_update[_j,1] + end + end + end + elseif gen["element"] == VoltageSourceElement + if gen["gen_status"] == 1 + bus = data["bus"][string(gen["gen_bus"])] + n = 3 #TODO fix when 4 is included + p_matrix = zeros(Complex{Float64}, n, n) + _v = zeros(Complex{Float64}, n, 1) + for i in gen["connections"] + if i != 4 + _v[i,1] = bus["vm"][i] * data["settings"]["voltage_scale_factor"] * exp(1im * bus["va"][i] * pi/180) + for j in gen["connections"] + if j != 4 + p_matrix[i,j] = gen["p_matrix"][i,j] + end + end + end + end + i_update = p_matrix * _v + for (_j, j) in enumerate(gen["connections"]) + if (gen["gen_bus"], j) in keys(data["admittance_map"]) + i[data["admittance_map"][(gen["gen_bus"], j)],1] = i_update[_j,1] + end + end end end end - end - end return i end @@ -338,6 +361,7 @@ end function calc_mc_delta_current_control_gfmi!(gen, delta_i, v, data) bus = gen["gen_bus"] v_solar = [gen["vg"][1]; gen["vg"][1]*exp(-2im/3*pi); gen["vg"][1]*exp(2im/3*pi)] + i_vsource = gen["vs_matrix"][1:3, 1:3] * v_solar pg = gen["pg"] haskey(gen, "qg") ? qg = gen["qg"] : qg = gen["pg"].*0.0 s = pg .+ 1im .* qg @@ -375,6 +399,33 @@ function update_mc_delta_current_gfmi_control!(delta_i, v, data) end +function update_mc_delta_current_regulator_control!(delta_i, v, data) + for (_, transformer) in data["transformer"] + if haskey(transformer, "controls") + if transformer["phases"] == 1 + f_bus = transformer["f_bus"] + t_bus = transformer["t_bus"] + v_regulator = zeros(Complex{Float64}, 2, 1) + y = transformer["p_matrix"][3:4,3:4] + n = transformer["f_connections"][1] + if haskey(data["admittance_map"], (f_bus, n)) && haskey(data["admittance_map"], (t_bus, n)) + v_regulator[1, 1] = v[data["admittance_map"][(f_bus, n)], 1] + v_regulator[2, 1] = v[data["admittance_map"][(t_bus, n)], 1] + i_regulator = y * v_regulator + ptratio = transformer["controls"]["ptratio"][2] + ctprim = transformer["controls"]["ctprim"][2] + z_volts = transformer["controls"]["r"][2] + 1im * transformer["controls"]["x"][2] + v_pt = v[data["admittance_map"][(t_bus, n)], 1] ./ ptratio + band = transformer["controls"]["band"][2] + z_load = z_volts ./ 5 + v_reg = v_pt .- z_load * i_regulator[2] ./ (ctprim/5) + end + end + end + end +end + + function update_mc_delta_current_gfmi_control_vbalance!(gen, delta_i, v, data) transformer = data["transformer"][gen["transformer_id"]] f_bus = data["bus"]["$(transformer["f_bus"])"] @@ -577,13 +628,11 @@ end function build_mc_delta_current_inverter!(delta_i, v, data, z_matrix) for (_, gen) in data["gen"] - if occursin("solar.", gen["source_id"]) - if gen["pv_model"] == 1 - if gen["grid_forming"] - calc_mc_delta_current_gfmi!(gen, delta_i, v, data) - else - calc_mc_delta_current_gfli!(gen, delta_i, v, data) - end + if gen["element"] == SolarElement + if gen["grid_forming"] + calc_mc_delta_current_gfmi!(gen, delta_i, v, data) + else + calc_mc_delta_current_gfli!(gen, delta_i, v, data) end end end @@ -591,40 +640,75 @@ end function calc_mc_delta_current_gfli!(gen, delta_i, v, data) - bus = gen["gen_bus"] - pg = gen["pg"] - haskey(gen, "qg") ? qg = gen["qg"] : qg = gen["pg"].*0.0 - s = (pg .+ 1im .* qg) .* data["settings"]["power_scale_factor"] - if gen["configuration"] == _PMD.WYE - if gen["balanced"] - v_solar = zeros(Complex{Float64}, length(s), 1) - for (_j, j) in enumerate(gen["connections"]) - if haskey(data["admittance_map"], (bus, j)) - v_solar[_j, 1] = v[data["admittance_map"][(bus, j)], 1] + if gen["response"] == ConstantPAtPF + bus = data["bus"]["$(gen["gen_bus"])"] + pg = gen["pg"] + haskey(gen, "qg") ? qg = gen["qg"] : qg = gen["pg"].*0.0 + s = (pg .+ 1im .* qg) .* data["settings"]["power_scale_factor"] + if gen["configuration"] == _PMD.WYE + if gen["balanced"] + v_solar = zeros(Complex{Float64}, length(s), 1) + v0 = zeros(Complex{Float64}, length(s), 1) + for (_j, j) in enumerate(gen["connections"]) + if haskey(data["admittance_map"], (bus["bus_i"], j)) + v_solar[_j, 1] = v[data["admittance_map"][(bus["bus_i"], j)], 1] + if haskey(bus, "pre_fault") + v0[_j, 1] = bus["pre_fault"][_j] + else + v0[_j, 1] = bus["vbase"] * data["settings"]["voltage_scale_factor"] + end + end end - end - s_seq = s[1] - v_seq = inv(_A)*v_solar - i_seq = conj(s_seq/v_seq[2]) - if abs(i_seq) <= gen["i_max"][1] - i_inj = _A*[0;i_seq;0] - else - i_inj = _A*[0;gen["i_max"][1]*exp(1im*angle(i_seq));0] - end - for (_j, j) in enumerate(gen["connections"]) - if j != 4 - delta_i[data["admittance_map"][(bus, j)], 1] += i_inj[j] + deadband = .1 * bus["vbase"] * data["settings"]["voltage_scale_factor"] + k = 2 + s_seq = s[1] + v_seq = inv(_A)*v_solar + deadband = .1 * bus["vbase"] * data["settings"]["voltage_scale_factor"] + if haskey(bus, "pre_fault") + v0_seq = inv(_A)*v0exz + else + v0_seq = [0.0;v0[1];0.0] end - end - else - k = findall(x->x==4, gen["connections"])[1] - for (_j, j) in enumerate(gen["connections"]) - if j != 4 - i_inj = conj(s[_j]/v[data["admittance_map"][(bus, j)], 1]) - if abs(i_inj) < gen["i_max"][_j] - delta_i[data["admittance_map"][(bus, j)], 1] += i_inj * exp(-1im*angle(i_inj)) + if abs(abs(v_seq[2]) - abs(v0_seq[2])) > deadband + i_q = k * (abs(v_seq[2]) - abs(v0_seq[2]))/(bus["vbase"] * data["settings"]["voltage_scale_factor"])*abs(gen["pre_fault"][1]) + if abs(i_q) > gen["i_max"][1] + i_q = i_q/abs(i_q)*gen["i_max"][1] + i_p = 0.0 else - delta_i[data["admittance_map"][(bus, j)], 1] += gen["i_max"][_j] * exp(-1im*angle(i_inj)) + i_p = sqrt(gen["i_max"][1]^2 - i_q^2) + end + if abs(v_seq[3]) > .25 * bus["vbase"] * data["settings"]["voltage_scale_factor"] + i_neg = .5*gen["i_max"][1]*exp(1im*(angle(v_seq[3]) + pi/2)) + else + i_neg = .5*abs(v_seq[3])/(bus["vbase"] * data["settings"]["voltage_scale_factor"])*gen["i_max"][1]*exp(1im*(angle(v_seq[3]) + pi/2)) + end + i_pos_q = i_q - abs(i_neg) * sin(angle(i_neg)) + i_pos_r = i_p - abs(i_neg) * cos(angle(i_neg)) + i_seq = [0; i_pos_r + 1im*i_pos_q; i_neg] + i_inj = _A*i_seq + else + i_seq = conj(s_seq/v_seq[2]) + if abs(i_seq) <= gen["i_max"][1] + i_inj = _A*[0;i_seq;0] + else + i_inj = _A*[0;gen["i_max"][1]*exp(1im*angle(i_seq));0] + end + end + for (_j, j) in enumerate(gen["connections"]) + if j != 4 + delta_i[data["admittance_map"][(bus["bus_i"], j)], 1] += i_inj[j] + end + end + else + k = findall(x->x==4, gen["connections"])[1] + for (_j, j) in enumerate(gen["connections"]) + if j != 4 + i_inj = conj(s[_j]/v[data["admittance_map"][(bus, j)], 1]) + if abs(i_inj) < gen["i_max"][_j] + delta_i[data["admittance_map"][(bus, j)], 1] += i_inj * exp(-1im*angle(i_inj)) + else + delta_i[data["admittance_map"][(bus, j)], 1] += gen["i_max"][_j] * exp(-1im*angle(i_inj)) + end end end end @@ -681,6 +765,7 @@ function update_mc_delta_current_vector(model, v) update_mc_delta_current_generator!(delta_i, v, model.data) update_mc_delta_current_load!(delta_i, v, model.data) update_mc_delta_current_inverter!(delta_i, v, model.data) + update_mc_delta_current_regulator_control!(delta_i, v, model.data) return _SP.sparse(delta_i) end @@ -713,13 +798,11 @@ end function update_mc_delta_current_inverter!(delta_i, v, data) for (_, gen) in data["gen"] - if occursin("solar.", gen["source_id"]) - if gen["pv_model"] == 1 - if gen["grid_forming"] - calc_mc_delta_current_gfmi!(gen, delta_i, v, data) - else - calc_mc_delta_current_gfli!(gen, delta_i, v, data) - end + if gen["element"] == SolarElement + if gen["grid_forming"] + calc_mc_delta_current_gfmi!(gen, delta_i, v, data) + else + calc_mc_delta_current_gfli!(gen, delta_i, v, data) end end end diff --git a/src/core/data.jl b/src/core/data.jl index 1c335a7..0da2409 100644 --- a/src/core/data.jl +++ b/src/core/data.jl @@ -473,3 +473,53 @@ function calc_line_paramters!(line::Dict{String,<:Any}, data_dss::Dict{String,<: nothing end end + + +function add_pre_fault!(pre_v, model::AdmittanceModel) + add_pre_fault_bus_voltage!(pre_v, model) + add_pre_fault_gfli_current!(pre_v, model) +end + + +function add_pre_fault_bus_voltage!(pre_v, model::AdmittanceModel) + for (_, bus) in model.data["bus"] + _v = zeros(Complex{Float64}, 1, length(bus["terminals"])) + for (i, j) in enumerate(bus["terminals"]) + if haskey(model.data["admittance_map"], (bus["bus_i"], j)) + _v[1, i] = pre_v[model.data["admittance_map"][(bus["bus_i"], j)], 1] + end + end + bus["pre_fault"] = _v + end +end + +function add_pre_fault_gfli_current!(pre_v, model::AdmittanceModel) + for (_, gen) in model.data["gen"] + if gen["element"] == SolarElement + transformer = model.data["transformer"][gen["transformer_id"]] + if gen["phases"] == 3 + f_bus = transformer["f_bus"] + t_bus = transformer["t_bus"] + y = transformer["p_matrix"] + n = size(y)[1] + _v = zeros(Complex{Float64}, n, 1) + for (i, j) in enumerate(transformer["f_connections"]) + if haskey(model.data["admittance_map"], (f_bus, j)) + _v[i, 1] = pre_v[model.data["admittance_map"][(f_bus, j)], 1] + else + _v[i, 1] = 0.0 + 0.0im + end + end + for (i, j) in enumerate(transformer["t_connections"]) + if haskey(model.data["admittance_map"], (t_bus, j)) + _v[i+length(transformer["t_connections"]), 1] = pre_v[model.data["admittance_map"][(t_bus, j)], 1] + else + _v[i+length(transformer["t_connections"]), 1] = 0.0 + 0.0im + end + end + _i = y * _v + gen["pre_fault"] = _i + end + end + end +end \ No newline at end of file diff --git a/src/core/types.jl b/src/core/types.jl index 89c8461..73caf84 100644 --- a/src/core/types.jl +++ b/src/core/types.jl @@ -8,7 +8,11 @@ struct AdmittanceModel delta_i::Matrix{Complex{Float64}} end -@enum ResponseCharateristic ConstantPQ ConstantZ ConstantI ConstantZIP ConstantPAtPF ConstantPV ConstantPFixedQ ConstantPXFixedQ ConstantPQCurrentLimited +@enum ResponseCharateristic ConstantPQ ConstantZ ConstantI ConstantZIP ConstantPAtPF ConstantPV ConstantPFixedQ ConstantPXFixedQ ConstantPQCurrentLimited ConstantV ConstantVCurrentLimited + +@enum Element VoltageSourceElement CurrentSourceElement LoadElement Transformer2WElement Transformer3WElement TransformerCenterTapElement SolarElement GeneratorElement + +@enum Standard IEEE2800 diff --git a/src/data_model/components.jl b/src/data_model/components.jl index 52c412b..788d90d 100644 --- a/src/data_model/components.jl +++ b/src/data_model/components.jl @@ -382,15 +382,21 @@ end function _map_eng2math_mc_admittance_solar!(data_math::Dict{String,<:Any}, data_eng::Dict{String,<:Any}; pass_props::Vector{String}=String[]) if haskey(data_math, "gen") for (name, gen) in data_math["gen"] - if occursin("solar", gen["source_id"]) + if gen["element"] == SolarElement n = length(gen["connections"]) - y = zeros(n, n) + y = zeros(Complex{Float64}, n, n) if gen["configuration"] == _PMD.WYE for (i, connection) in enumerate(gen["connections"]) j = findall(x->x==4, gen["connections"])[1] if connection != 4 - y[i,i] = 1/1e6 - y[j,j] = y[i,i] + if gen["grid_forming"] + zs = .0001 + .0005im + y[i,i] = 1/zs + y[j,j] = y[i,i] + else + y[i,i] = 1/1e6im + y[j,j] = y[i,i] + end end end end @@ -464,7 +470,7 @@ function _map_eng2math_mc_admittance_2w_transformer!(transformer::Dict{String,<: (2,2) => [7,4], (2,3) => [11,6] ) - if transformer["dss"]["phases"] == 3 + if transformer["phases"] == 3 z = sum(transformer["rw"]) + 1im .* transformer["xsc"][1] z_1volt= z * 3/transformer["sm_nom"][1]/1000 z_b = [z_1volt 0 0;0 z_1volt 0;0 0 z_1volt] @@ -519,6 +525,9 @@ function _map_eng2math_mc_admittance_2w_transformer!(transformer::Dict{String,<: end y_w = n*y1*transpose(n) p_matrix = a*y_w*transpose(a) + if transformer["dss"]["name"] == "xfm1" + Nothing + end ybase = (transformer["sm_nom"][1]/3) / (transformer["tm_nom"][2]*transformer["tm_set"][2][1]/sqrt(3))^2 /1000 if haskey(transformer["dss"], "%noloadloss") @@ -535,7 +544,7 @@ function _map_eng2math_mc_admittance_2w_transformer!(transformer::Dict{String,<: p_matrix[8,8] += 3*shunt end transformer["p_matrix"] = p_matrix - elseif transformer["dss"]["phases"] == 1 + elseif transformer["phases"] == 1 z = sum(transformer["rw"]) + 1im .* transformer["xsc"][1] z_1volt= z * 1/transformer["sm_nom"][1]/1000 b = [1 ;-1] diff --git a/src/data_model/eng2math.jl b/src/data_model/eng2math.jl index 8094056..2746504 100644 --- a/src/data_model/eng2math.jl +++ b/src/data_model/eng2math.jl @@ -157,11 +157,11 @@ end "field/values to passthrough from the ENGINEERING to MATHEMATICAL data models" const _pmp_eng2math_passthrough = Dict{String,Vector{String}}( - "generator" => String["zr", "zx", "gen_model", "xdp", "rp", "xdpp", "vnom_kv"], - "solar" => String["i_max", "solar_max", "kva", "pf", "grid_forming", "balanced", "vminpu", "transformer", "type", "pv_model", "transformer_id"], - "voltage_source" => String["zr", "zx"], - "load" => String["vminpu", "vmaxpu", "response"], - "transformer" => String["leadlag"] + "generator" => String["zr", "zx", "gen_model", "xdp", "rp", "xdpp", "vnom_kv", "phases", "response", "element"], + "solar" => String["i_max", "solar_max", "kva", "pf", "grid_forming", "balanced", "vminpu", "transformer", "type", "pv_model", "phases", "response", "element"], + "voltage_source" => String["zr", "zx", "phases", "response", "element"], + "load" => String["vminpu", "vmaxpu", "response", "phases", "element"], + "transformer" => String["leadlag", "phases", "element"] ) @@ -309,7 +309,7 @@ function populate_bus_voltages!(data::Dict{String,Any}) f_bus = transformer["f_bus"] t_bus = transformer["t_bus"] if haskey(transformer, "tm_nom") - transformer["dss"]["phases"] == 3 ? multi = 1/sqrt(3) : multi = 1 + transformer["phases"] == 3 ? multi = 1/sqrt(3) : multi = 1 if !haskey(data["bus"][string(f_bus)], "vbase") data["bus"][string(f_bus)]["vbase"] = transformer["tm_nom"][1]*multi end @@ -332,7 +332,7 @@ function populate_bus_voltages!(data::Dict{String,Any}) for (i, gen) in data["gen"] if !haskey(data["bus"][string(gen["gen_bus"])], "vbase") - if occursin("voltage_source", gen["source_id"]) + if gen["element"] == VoltageSourceElement data["bus"][string(gen["gen_bus"])]["vbase"] = gen["vg"][1] end end @@ -390,4 +390,5 @@ function correct_grounds!(data::Dict{String,Any}) end end end -end \ No newline at end of file +end + diff --git a/src/data_model/eng2math_pmd.jl b/src/data_model/eng2math_pmd.jl index d3a7178..eb466d9 100644 --- a/src/data_model/eng2math_pmd.jl +++ b/src/data_model/eng2math_pmd.jl @@ -413,7 +413,7 @@ end function _eng2math_link_transformer(data_math::Dict{String,<:Any}, data_eng::Dict{String,<:Any}) for (_, gen) in data_math["gen"] if haskey(gen, "type") - if gen["type"] == "solar" + if gen["element"] == SolarElement # TODO adds transformer need to add for when transformer key set to false for (id, transformer) in data_math["transformer"] if transformer["f_bus"] == gen["gen_bus"] || transformer["t_bus"] == gen["gen_bus"] diff --git a/src/io/dss/dss2eng.jl b/src/io/dss/dss2eng.jl index 8674560..62b17d9 100644 --- a/src/io/dss/dss2eng.jl +++ b/src/io/dss/dss2eng.jl @@ -48,6 +48,11 @@ function _dss2eng_solar_dynamics!(data_eng::Dict{String,<:Any}, data_dss::Dict{S else model = 1 end + if haskey(dss_obj, "phases") + phases = dss_obj["phases"] + else + phases = 3 + end ncnd = length(solar["connections"]) >= 3 ? 3 : 1 solar["i_max"] = fill(1/vminpu * kva / (ncnd/sqrt(3)*dss_obj["kv"]), ncnd) solar["solar_max"] = irradiance*pmpp @@ -57,8 +62,16 @@ function _dss2eng_solar_dynamics!(data_eng::Dict{String,<:Any}, data_dss::Dict{S solar["vminpu"] = vminpu solar["type"] = "solar" solar["pv_model"] = model - solar["transformer"] = false solar["grid_forming"] = false + if model == 1 + solar["response"] = ConstantPAtPF + elseif model == 2 + solar["response"] = ConstantI + elseif model == 3 + solar["response"] = ConstantPQ + end + solar["phases"] = phases + solar["element"] = SolarElement end end end @@ -69,18 +82,25 @@ function _dss2eng_load_dynamics!(data_eng::Dict{String,<:Any}, data_dss::Dict{St if haskey(data_eng, "load") for (id, load) in data_eng["load"] dss_obj = data_dss["load"][id] + defaults = _PMD._apply_ordered_properties(_PMD._create_pvsystem(id; _PMD._to_kwargs(dss_obj)...), dss_obj) if haskey(dss_obj, "vminpu") vminpu = dss_obj["vminpu"] else - vminpu = .95 + vminpu = defaults["vminpu"] end if haskey(dss_obj, "vmaxpu") vmaxpu = dss_obj["vmaxpu"] else - vmaxpu = 1.05 + vmaxpu = defaults["vmaxpu"] + end + if haskey(dss_obj, "phases") + phases = dss_obj["phases"] + else + phases = defaults["phases"] end load["vminpu"] = vminpu load["vmaxpu"] = vmaxpu + load["phases"] = phases if load["model"] == _PMD.IMPEDANCE load["response"] = ConstantZ elseif load["model"] == _PMD.POWER @@ -90,6 +110,7 @@ function _dss2eng_load_dynamics!(data_eng::Dict{String,<:Any}, data_dss::Dict{St elseif load["model"] == _PMD.ZIP load["response"] = ConstantZIP end + load["element"] = LoadElement end end end @@ -99,14 +120,29 @@ end function _dss2eng_transformer_dynamics!(data_eng::Dict{String,<:Any}, data_dss::Dict{String,<:Any}) if haskey(data_eng, "transformer") for (id, transformer) in data_eng["transformer"] - if transformer["configuration"] != _PMD.ConnConfig[_PMD.WYE, _PMD.WYE] # need to fix combining single phase into 3 phase - dss_obj = data_dss["transformer"][id] - if haskey(dss_obj, "leadlag") - transformer["leadlag"] = dss_obj["leadlag"] + dss_obj = data_dss["transformer"][id] + defaults = _PMD._apply_ordered_properties(_PMD._create_pvsystem(id; _PMD._to_kwargs(dss_obj)...), dss_obj) + if haskey(dss_obj, "leadlag") + leadlag = dss_obj["leadlag"] + else + if haskey(defaults, "leadlag") + leadlag = defaults["leadlag"] else - transformer["leadlag"] = "lag" + leadlag = "lag" end end + if haskey(dss_obj, "phases") + phases = dss_obj["phases"] + else + phases = defaults["phases"] + end + transformer["leadlag"] = leadlag + transformer["phases"] = phases + if length(transformer["connections"]) == 2 + transformer["element"] = Transformer2WElement + else + Nothing + end end end end @@ -118,6 +154,13 @@ function _dss2eng_voltage_source_dynamics!(data_eng::Dict{String,<:Any}, data_ds for (id, voltage_source) in data_eng["voltage_source"] dss_obj = data_dss["vsource"][id] defaults = _PMD._apply_ordered_properties(_PMD._create_vsource(id; _PMD._to_kwargs(dss_obj)...), dss_obj) + if haskey(dss_obj, "phases") + phases = dss_obj["phases"] + else + phases = defaults["phases"] + end + voltage_source["phases"] = phases + voltage_source["element"] = VoltageSourceElement if haskey(dss_obj, "r1") && haskey(dss_obj, "x1") r1 = dss_obj["r1"] x1 = dss_obj["x1"] @@ -167,6 +210,7 @@ function _dss2eng_gen_dynamics!(data_eng::Dict{String,<:Any}, data_dss::Dict{Str if haskey(generator["dss"], "kv") generator["vnom_kv"] = generator["dss"]["kv"] / sqrt(3) end + generator["element"] = GeneratorElement # if generator["dss"]["model"] == 3 # dss_obj = data_dss["generator"][id] # _PMD._apply_like!(dss_obj, data_dss, "generator") diff --git a/src/io/opendss.jl b/src/io/opendss.jl index d4fe9ae..c425c3d 100644 --- a/src/io/opendss.jl +++ b/src/io/opendss.jl @@ -9,7 +9,7 @@ data model adding some extra data transformations specific to fault studies. `kwargs` can be any valid keyword arguments from PowerModelsDistribution's `parse_file` function. """ function parse_opendss(file::String; method::Union{String,Missing}=missing, kwargs...) - pm_data = _PMD.parse_file(file; bank_transformers=false, dss2eng_extensions=[_dss2eng_solar_dynamics!, _dss2eng_load_dynamics!, _dss2eng_gen_dynamics!, _dss2eng_transformer_dynamics!, _dss2eng_phases!, _dss2eng_issues!], kwargs...) + pm_data = _PMD.parse_file(file; bank_transformers=false, dss2eng_extensions=[_dss2eng_solar_dynamics!, _dss2eng_load_dynamics!, _dss2eng_voltage_source_dynamics!, _dss2eng_gen_dynamics!, _dss2eng_transformer_dynamics!, _dss2eng_issues!], kwargs...) if ismissing(method) pm_data["method"] = "PMD" else diff --git a/test/fs_mc.jl b/test/fs_mc.jl index 10ceefb..a53ce6b 100644 --- a/test/fs_mc.jl +++ b/test/fs_mc.jl @@ -119,17 +119,4 @@ @test calculate_error_percentage(sol["loadbus"]["3pg"][2], 1531.0) < .02 @test calculate_error_percentage(sol["loadbus"]["3pg"][3], 1532.0) < .02 end - @testset "check 3 phase gfli defined pq" begin - model = PowerModelsProtection.instantiate_mc_admittance_model(gfli_case;loading=false) - sol = PowerModelsProtection.solve_mc_fault_study(model) - @test calculate_error_percentage(sol["loadbus"]["ll"][(1,2)][1], 13799.0) < .001 - @test calculate_error_percentage(sol["loadbus"]["ll"][(2,3)][1], 13799.0) < .001 - @test calculate_error_percentage(sol["loadbus"]["ll"][(1,3)][1], 13799.0) < .001 - @test calculate_error_percentage(sol["loadbus"]["lg"][1][1], 9570.5) < .001 - @test calculate_error_percentage(sol["loadbus"]["lg"][2][1], 9570.5) < .001 - @test calculate_error_percentage(sol["loadbus"]["lg"][3][1], 9570.5) < .001 - @test calculate_error_percentage(sol["loadbus"]["3pg"][1], 15929.0) < .001 - @test calculate_error_percentage(sol["loadbus"]["3pg"][2], 15929.0) < .001 - @test calculate_error_percentage(sol["loadbus"]["3pg"][3], 15929.0) < .001 - end end diff --git a/test/pf_mc.jl b/test/pf_mc.jl index 2466590..ecd041c 100644 --- a/test/pf_mc.jl +++ b/test/pf_mc.jl @@ -68,21 +68,6 @@ @test calculate_error_percentage(sol["bus"]["3"]["vm"][3], 230.59) < .001 @test calculate_error_percentage(sol["bus"]["3"]["va"][3], 121.4) < .001 end - @testset "check 3 phase gfli defined pq" begin - model = PowerModelsProtection.instantiate_mc_admittance_model(gfli_case) - sol = PowerModelsProtection.compute_mc_pf(model) - @test sol["solver"]["it"] < 10 - @test calculate_error_percentage(sol["bus"]["3"]["vm"][1], 2299.3) < .001 - @test calculate_error_percentage(sol["bus"]["3"]["vm"][2], 2299.3) < .001 - @test calculate_error_percentage(sol["bus"]["3"]["va"][2], -119.9) < .001 - @test calculate_error_percentage(sol["bus"]["3"]["vm"][3], 2299.3) < .001 - @test calculate_error_percentage(sol["bus"]["3"]["va"][3], 120.1) < .001 - @test calculate_error_percentage(sol["bus"]["5"]["vm"][1], 235.87) < .001 - @test calculate_error_percentage(sol["bus"]["5"]["vm"][2], 235.87) < .001 - @test calculate_error_percentage(sol["bus"]["5"]["va"][2], -147.8) < .001 - @test calculate_error_percentage(sol["bus"]["5"]["vm"][3], 235.87) < .001 - @test calculate_error_percentage(sol["bus"]["5"]["va"][3], 92.2) < .001 - end end