diff --git a/src/base/simulation_results.jl b/src/base/simulation_results.jl index 38bf4b5f9..3c9efe157 100644 --- a/src/base/simulation_results.jl +++ b/src/base/simulation_results.jl @@ -36,20 +36,24 @@ Internal function to obtain as a Vector of Float64 of a specific state. It recei global index for a state. """ -function _post_proc_state_series(solution, ix::Int, dt::Nothing) - ix_t = unique(i -> solution.t[i], eachindex(solution.t)) +function _post_proc_state_series(solution, ix::Int, dt::Nothing, unique_timestamps::Bool) + if unique_timestamps + ix_t = unique(i -> solution.t[i], eachindex(solution.t)) + else + ix_t = eachindex(solution.t) + end ts = solution.t[ix_t] state = solution[ix, ix_t] return ts, state end -function _post_proc_state_series(solution, ix::Int, dt::Float64) +function _post_proc_state_series(solution, ix::Int, dt::Float64, ::Bool) ts = collect(range(0; stop = solution.t[end], step = dt)) state = solution(ts; idxs = ix) return ts, state.u end -function _post_proc_state_series(solution, ix::Int, dt::Vector{Float64}) +function _post_proc_state_series(solution, ix::Int, dt::Vector{Float64}, ::Bool) state = solution(dt; idxs = ix) return dt, state.u end @@ -62,6 +66,7 @@ function post_proc_state_series( res::SimulationResults, ref::Tuple{String, Symbol}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) global_state_index = get_global_index(res) if !haskey(global_state_index, ref[1]) @@ -69,7 +74,7 @@ function post_proc_state_series( error("State $(ref[2]) device $(ref[1]) not found in the system. ") end ix = get(global_state_index[ref[1]], ref[2], 0) - return _post_proc_state_series(get_solution(res), ix, dt) + return _post_proc_state_series(get_solution(res), ix, dt, unique_timestamps) end """ @@ -80,6 +85,7 @@ function post_proc_voltage_current_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, )::NTuple{5, Vector{Float64}} #Note: Type annotation since get_dynamic_injector is type unstable and solution is Union{Nothing, DAESol} system = get_system(res) @@ -91,12 +97,14 @@ function post_proc_voltage_current_series( error("Device $(name) not found in the system") end bus_ix = get(bus_lookup, PSY.get_number(PSY.get_bus(device)), -1) - ts, V_R, V_I = post_proc_voltage_series(solution, bus_ix, n_buses, dt) + ts, V_R, V_I = + post_proc_voltage_series(solution, bus_ix, n_buses, dt, unique_timestamps) dyn_device = PSY.get_dynamic_injector(device) if isnothing(dyn_device) - _, I_R, I_I = compute_output_current(res, device, V_R, V_I, dt) + _, I_R, I_I = compute_output_current(res, device, V_R, V_I, dt, unique_timestamps) else - _, I_R, I_I = compute_output_current(res, dyn_device, V_R, V_I, dt) + _, I_R, I_I = + compute_output_current(res, dyn_device, V_R, V_I, dt, unique_timestamps) end return ts, V_R, V_I, I_R, I_I end @@ -111,10 +119,11 @@ function post_proc_voltage_series( bus_ix::Int, n_buses::Int, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) bus_ix < 0 && error("Bus number $(bus_number) not found.") - ts, V_R = _post_proc_state_series(solution, bus_ix, dt) - _, V_I = _post_proc_state_series(solution, bus_ix + n_buses, dt) + ts, V_R = _post_proc_state_series(solution, bus_ix, dt, unique_timestamps) + _, V_I = _post_proc_state_series(solution, bus_ix + n_buses, dt, unique_timestamps) return collect(ts), collect(V_R), collect(V_I) end @@ -127,8 +136,9 @@ function post_proc_real_current_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, _, _, I_R, _ = post_proc_voltage_current_series(res, name, dt) + ts, _, _, I_R, _ = post_proc_voltage_current_series(res, name, dt, unique_timestamps) return ts, I_R end """ @@ -140,8 +150,9 @@ function post_proc_imaginary_current_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, _, _, _, I_I = post_proc_voltage_current_series(res, name, dt) + ts, _, _, _, I_I = post_proc_voltage_current_series(res, name, dt, unique_timestamps) return ts, I_I end @@ -154,8 +165,10 @@ function post_proc_activepower_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, V_R, V_I, I_R, I_I = post_proc_voltage_current_series(res, name, dt) + ts, V_R, V_I, I_R, I_I = + post_proc_voltage_current_series(res, name, dt, unique_timestamps) return ts, V_R .* I_R + V_I .* I_I end @@ -168,8 +181,10 @@ function post_proc_reactivepower_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, V_R, V_I, I_R, I_I = post_proc_voltage_current_series(res, name, dt) + ts, V_R, V_I, I_R, I_I = + post_proc_voltage_current_series(res, name, dt, unique_timestamps) return ts, V_I .* I_R - V_R .* I_I end @@ -182,6 +197,7 @@ function post_proc_field_current_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) system = get_system(res) bus_lookup = get_bus_lookup(res) @@ -189,9 +205,10 @@ function post_proc_field_current_series( solution = res.solution device = PSY.get_component(PSY.StaticInjection, system, name) bus_ix = get(bus_lookup, PSY.get_number(PSY.get_bus(device)), -1) - ts, V_R, V_I = post_proc_voltage_series(solution, bus_ix, n_buses, dt) + ts, V_R, V_I = + post_proc_voltage_series(solution, bus_ix, n_buses, dt, unique_timestamps) dyn_device = PSY.get_dynamic_injector(device) - _, I_fd = compute_field_current(res, dyn_device, V_R, V_I, dt) + _, I_fd = compute_field_current(res, dyn_device, V_R, V_I, dt, unique_timestamps) return ts, I_fd end @@ -204,11 +221,12 @@ function post_proc_field_voltage_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) system = get_system(res) device = PSY.get_component(PSY.StaticInjection, system, name) dyn_device = PSY.get_dynamic_injector(device) - ts, Vf = compute_field_voltage(res, dyn_device, dt) + ts, Vf = compute_field_voltage(res, dyn_device, dt, unique_timestamps) return ts, Vf end @@ -221,11 +239,12 @@ function post_proc_pss_output_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) system = get_system(res) device = PSY.get_component(PSY.StaticInjection, system, name) dyn_device = PSY.get_dynamic_injector(device) - ts, Vs = compute_pss_output(res, dyn_device, dt) + ts, Vs = compute_pss_output(res, dyn_device, dt, unique_timestamps) return ts, Vs end @@ -238,11 +257,12 @@ function post_proc_mechanical_torque_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) system = get_system(res) device = PSY.get_component(PSY.StaticInjection, system, name) dyn_device = PSY.get_dynamic_injector(device) - ts, τm = compute_mechanical_torque(res, dyn_device, dt) + ts, τm = compute_mechanical_torque(res, dyn_device, dt, unique_timestamps) return ts, τm end @@ -254,6 +274,7 @@ function post_proc_branch_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) system = get_system(res) bus_lookup = get_bus_lookup(res) @@ -267,8 +288,10 @@ function post_proc_branch_series( bus_to_number = PSY.get_number(PSY.get_to(PSY.get_arc(branch))) bus_from_ix = get(bus_lookup, bus_from_number, -1) bus_to_ix = get(bus_lookup, bus_to_number, -1) - ts, V_R_from, V_I_from = post_proc_voltage_series(solution, bus_from_ix, n_buses, dt) - _, V_R_to, V_I_to = post_proc_voltage_series(solution, bus_to_ix, n_buses, dt) + ts, V_R_from, V_I_from = + post_proc_voltage_series(solution, bus_from_ix, n_buses, dt, unique_timestamps) + _, V_R_to, V_I_to = + post_proc_voltage_series(solution, bus_to_ix, n_buses, dt, unique_timestamps) r = PSY.get_r(branch) x = PSY.get_x(branch) I_flow = ((V_R_from + V_I_from * 1im) - (V_R_to + V_I_to * 1im)) ./ (r + x * 1im) @@ -282,6 +305,7 @@ function post_proc_frequency_series( res::SimulationResults, name::String, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) system = get_system(res) device = PSY.get_component(PSY.StaticInjection, system, name) @@ -289,14 +313,15 @@ function post_proc_frequency_series( if isnothing(dyn_device) error("Dynamic Injection $(name) not found in the system") end - ts, ω = compute_frequency(res, dyn_device, dt) + ts, ω = compute_frequency(res, dyn_device, dt, unique_timestamps) end """ get_state_series( res::SimulationResults, ref::Tuple{String, Symbol}; - dt::Union{Nothing, Float64, Vector{Float64}} = nothing + dt::Union{Nothing, Float64, Vector{Float64}} = nothing, + unique_timestamps::Bool = true, ) end @@ -307,8 +332,13 @@ Function to obtain series of states out of DAE Solution. - `res::SimulationResults` : Simulation Results object that contains the solution - `ref:Tuple{String, Symbol}` : Tuple used to identify the dynamic device, via its name, as a `String`, and the associated state as a `Symbol`. """ -function get_state_series(res::SimulationResults, ref::Tuple{String, Symbol}; dt = nothing) - return post_proc_state_series(res, ref, dt) +function get_state_series( + res::SimulationResults, + ref::Tuple{String, Symbol}; + dt = nothing, + unique_timestamps = true, +) + return post_proc_state_series(res, ref, dt, unique_timestamps) end """ @@ -324,10 +354,16 @@ Function to obtain the voltage magnitude series out of the DAE Solution. - `res::SimulationResults` : Simulation Results object that contains the solution - `bus_number::Int` : Bus number identifier """ -function get_voltage_magnitude_series(res::SimulationResults, bus_number::Int; dt = nothing) +function get_voltage_magnitude_series( + res::SimulationResults, + bus_number::Int; + dt = nothing, + unique_timestamps = true, +) n_buses = get_bus_count(res) bus_ix = get(get_bus_lookup(res), bus_number, 0) - ts, V_R, V_I = post_proc_voltage_series(res.solution, bus_ix, n_buses, dt) + ts, V_R, V_I = + post_proc_voltage_series(res.solution, bus_ix, n_buses, dt, unique_timestamps) return ts, sqrt.(V_R .^ 2 .+ V_I .^ 2) end @@ -344,10 +380,16 @@ Function to obtain the voltage angle series out of the DAE Solution. - `res::SimulationResults` : Simulation Results object that contains the solution - `bus_number::Int` : Bus number identifier """ -function get_voltage_angle_series(res::SimulationResults, bus_number::Int; dt = nothing) +function get_voltage_angle_series( + res::SimulationResults, + bus_number::Int; + dt = nothing, + unique_timestamps = true, +) n_buses = get_bus_count(res) bus_ix = get(get_bus_lookup(res), bus_number, 0) - ts, V_R, V_I = post_proc_voltage_series(res.solution, bus_ix, n_buses, dt) + ts, V_R, V_I = + post_proc_voltage_series(res.solution, bus_ix, n_buses, dt, unique_timestamps) return ts, atan.(V_I, V_R) end @@ -364,8 +406,13 @@ Function to obtain the real current time series of a Dynamic Injection series ou - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_real_current_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_real_current_series(res, name, dt) +function get_real_current_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_real_current_series(res, name, dt, unique_timestamps) end """ @@ -381,8 +428,13 @@ Function to obtain the imaginary current time series of a Dynamic Injection seri - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_imaginary_current_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_imaginary_current_series(res, name, dt) +function get_imaginary_current_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_imaginary_current_series(res, name, dt, unique_timestamps) end """ @@ -398,8 +450,13 @@ Function to obtain the active power output time series of a Dynamic Injection se - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_activepower_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_activepower_series(res, name, dt) +function get_activepower_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_activepower_series(res, name, dt, unique_timestamps) end """ @@ -415,8 +472,13 @@ Function to obtain the reactive power output time series of a Dynamic Injection - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_reactivepower_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_reactivepower_series(res, name, dt) +function get_reactivepower_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_reactivepower_series(res, name, dt, unique_timestamps) end """ @@ -432,8 +494,13 @@ Function to obtain the field current time series of a Dynamic Generator out of t - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_field_current_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_field_current_series(res, name, dt) +function get_field_current_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_field_current_series(res, name, dt, unique_timestamps) end """ @@ -449,8 +516,13 @@ Function to obtain the field voltage time series of a Dynamic Generator out of t - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_field_voltage_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_field_voltage_series(res, name, dt) +function get_field_voltage_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_field_voltage_series(res, name, dt, unique_timestamps) end """ @@ -466,8 +538,13 @@ Function to obtain the pss output time series of a Dynamic Generator out of the - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_pss_output_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_pss_output_series(res, name, dt) +function get_pss_output_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_pss_output_series(res, name, dt, unique_timestamps) end """ @@ -483,8 +560,13 @@ Function to obtain the mechanical torque time series of the mechanical torque ou - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_mechanical_torque_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_mechanical_torque_series(res, name, dt) +function get_mechanical_torque_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_mechanical_torque_series(res, name, dt, unique_timestamps) end """ @@ -499,8 +581,13 @@ Function to obtain the real current flowing through the series element of a Bran - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified line """ -function get_real_current_branch_flow(res::SimulationResults, name::String; dt = nothing) - ts, _, _, _, _, Ir, _ = post_proc_branch_series(res, name, dt) +function get_real_current_branch_flow( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + ts, _, _, _, _, Ir, _ = post_proc_branch_series(res, name, dt, unique_timestamps) return ts, Ir end @@ -520,8 +607,9 @@ function get_imaginary_current_branch_flow( res::SimulationResults, name::String; dt = nothing, + unique_timestamps = true, ) - ts, _, _, _, _, _, Ii = post_proc_branch_series(res, name, dt) + ts, _, _, _, _, _, Ii = post_proc_branch_series(res, name, dt, unique_timestamps) return ts, Ii end @@ -549,8 +637,10 @@ function get_activepower_branch_flow( name::String, location::Symbol; dt = nothing, + unique_timestamps = true, ) - ts, V_R_from, V_I_from, V_R_to, V_I_to, Ir, Ii = post_proc_branch_series(res, name, dt) + ts, V_R_from, V_I_from, V_R_to, V_I_to, Ir, Ii = + post_proc_branch_series(res, name, dt, unique_timestamps) if location == :from return ts, V_R_from .* Ir + V_I_from .* Ii elseif location == :to @@ -585,8 +675,10 @@ function get_reactivepower_branch_flow( name::String, location::Symbol; dt = nothing, + unique_timestamps = true, ) - ts, V_R_from, V_I_from, V_R_to, V_I_to, Ir, Ii = post_proc_branch_series(res, name, dt) + ts, V_R_from, V_I_from, V_R_to, V_I_to, Ir, Ii = + post_proc_branch_series(res, name, dt, unique_timestamps) if location == :from return ts, V_I_from .* Ir - V_R_from .* Ii elseif location == :to @@ -610,8 +702,13 @@ Function to obtain the frequency time series of a Dynamic Injection out of the D - `res::SimulationResults` : Simulation Results object that contains the solution - `name::String` : Name to identify the specified device """ -function get_frequency_series(res::SimulationResults, name::String; dt = nothing) - return post_proc_frequency_series(res, name, dt) +function get_frequency_series( + res::SimulationResults, + name::String; + dt = nothing, + unique_timestamps = true, +) + return post_proc_frequency_series(res, name, dt, unique_timestamps) end """ diff --git a/src/post_processing/post_proc_generator.jl b/src/post_processing/post_proc_generator.jl index d3da8ca96..8884e084b 100644 --- a/src/post_processing/post_proc_generator.jl +++ b/src/post_processing/post_proc_generator.jl @@ -28,6 +28,7 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicGenerator} #Obtain Data @@ -46,6 +47,7 @@ function compute_output_current( base_power_ratio, res, dt, + unique_timestamps, ) end @@ -60,6 +62,7 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) #Obtain Data @@ -70,18 +73,18 @@ function compute_output_current( Freq_Flag = PSY.get_Freq_Flag(dynamic_device) name = PSY.get_name(dynamic_device) if Freq_Flag == 1 - _, Pord = post_proc_state_series(res, (name, :Pord), dt) - _, dPord = post_proc_state_series(res, (name, :dPord), dt) + _, Pord = post_proc_state_series(res, (name, :Pord), dt, unique_timestamps) + _, dPord = post_proc_state_series(res, (name, :dPord), dt, unique_timestamps) Tpord = PSY.get_Tpord(dynamic_device) P_lim = PSY.get_P_lim(dynamic_device) end # Get states θ = atan.(V_I ./ V_R) - ts, Ip = post_proc_state_series(res, (name, :Ip), dt) - ts, Iq = post_proc_state_series(res, (name, :Iq), dt) - _, Mult = post_proc_state_series(res, (name, :Mult), dt) - _, Vmeas = post_proc_state_series(res, (name, :Vmeas), dt) + ts, Ip = post_proc_state_series(res, (name, :Ip), dt, unique_timestamps) + ts, Iq = post_proc_state_series(res, (name, :Iq), dt, unique_timestamps) + _, Mult = post_proc_state_series(res, (name, :Mult), dt, unique_timestamps) + _, Vmeas = post_proc_state_series(res, (name, :Vmeas), dt, unique_timestamps) Iq_neg = -Iq Ip_cmd = Ip Iq_cmd = Iq @@ -153,6 +156,7 @@ function compute_field_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicGenerator} #Obtain Data @@ -160,7 +164,15 @@ function compute_field_current( #Get machine machine = PSY.get_machine(dynamic_device) - return _field_current(machine, PSY.get_name(dynamic_device), V_R, V_I, res, dt) + return _field_current( + machine, + PSY.get_name(dynamic_device), + V_R, + V_I, + res, + dt, + unique_timestamps, + ) end """ @@ -172,11 +184,12 @@ function compute_field_voltage( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicGenerator} #Get AVR avr = PSY.get_avr(dynamic_device) - return _field_voltage(avr, PSY.get_name(dynamic_device), res, dt) + return _field_voltage(avr, PSY.get_name(dynamic_device), res, dt, unique_timestamps) end """ @@ -188,11 +201,12 @@ function compute_pss_output( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicGenerator} #Get PSS pss = PSY.get_pss(dynamic_device) - return _pss_output(pss, PSY.get_name(dynamic_device), res, dt) + return _pss_output(pss, PSY.get_name(dynamic_device), res, dt, unique_timestamps) end """ @@ -204,11 +218,12 @@ function compute_mechanical_torque( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicGenerator} #Get TG tg = PSY.get_prime_mover(dynamic_device) - return _mechanical_torque(tg, PSY.get_name(dynamic_device), res, dt) + return _mechanical_torque(tg, PSY.get_name(dynamic_device), res, dt, unique_timestamps) end """ @@ -219,9 +234,10 @@ function compute_frequency( res::SimulationResults, dyn_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicGenerator} name = PSY.get_name(dyn_device) - ts, ω = post_proc_state_series(res, (name, :ω), dt) + ts, ω = post_proc_state_series(res, (name, :ω), dt, unique_timestamps) return ts, ω end @@ -237,8 +253,9 @@ function _machine_current( base_power_ratio::Float64, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) R = PSY.get_R(machine) Xd_p = PSY.get_Xd_p(machine) @@ -271,10 +288,11 @@ function _machine_current( base_power_ratio::Float64, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_p = post_proc_state_series(res, (name, :eq_p), dt) - _, ed_p = post_proc_state_series(res, (name, :ed_p), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_p = post_proc_state_series(res, (name, :eq_p), dt, unique_timestamps) + _, ed_p = post_proc_state_series(res, (name, :ed_p), dt, unique_timestamps) R = PSY.get_R(machine) Xd_p = PSY.get_Xd_p(machine) @@ -309,10 +327,11 @@ function _machine_current( base_power_ratio::Float64, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_pp = post_proc_state_series(res, (name, :eq_pp), dt) - _, ed_pp = post_proc_state_series(res, (name, :ed_pp), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_pp = post_proc_state_series(res, (name, :eq_pp), dt, unique_timestamps) + _, ed_pp = post_proc_state_series(res, (name, :ed_pp), dt, unique_timestamps) #Get parameters R = PSY.get_R(machine) @@ -350,12 +369,13 @@ function _machine_current( base_power_ratio::Float64, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_pp = post_proc_state_series(res, (name, :eq_pp), dt) - _, ed_pp = post_proc_state_series(res, (name, :ed_pp), dt) - _, ψd = post_proc_state_series(res, (name, :ψd), dt) - _, ψq = post_proc_state_series(res, (name, :ψq), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_pp = post_proc_state_series(res, (name, :eq_pp), dt, unique_timestamps) + _, ed_pp = post_proc_state_series(res, (name, :ed_pp), dt, unique_timestamps) + _, ψd = post_proc_state_series(res, (name, :ψd), dt, unique_timestamps) + _, ψq = post_proc_state_series(res, (name, :ψq), dt, unique_timestamps) #Get parameters Xd_pp = PSY.get_Xd_pp(machine) @@ -388,14 +408,15 @@ function _machine_current( base_power_ratio::Float64, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_p = post_proc_state_series(res, (name, :eq_p), dt) - _, ed_p = post_proc_state_series(res, (name, :ed_p), dt) - _, ψd = post_proc_state_series(res, (name, :ψd), dt) - _, ψq = post_proc_state_series(res, (name, :ψq), dt) - _, ψd_pp = post_proc_state_series(res, (name, :ψd_pp), dt) - _, ψq_pp = post_proc_state_series(res, (name, :ψq_pp), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_p = post_proc_state_series(res, (name, :eq_p), dt, unique_timestamps) + _, ed_p = post_proc_state_series(res, (name, :ed_p), dt, unique_timestamps) + _, ψd = post_proc_state_series(res, (name, :ψd), dt, unique_timestamps) + _, ψq = post_proc_state_series(res, (name, :ψq), dt, unique_timestamps) + _, ψd_pp = post_proc_state_series(res, (name, :ψd_pp), dt, unique_timestamps) + _, ψq_pp = post_proc_state_series(res, (name, :ψq_pp), dt, unique_timestamps) #Get parameters Xd_pp = PSY.get_Xd_pp(machine) @@ -430,12 +451,13 @@ function _machine_current( base_power_ratio::Float64, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_p = post_proc_state_series(res, (name, :eq_p), dt) - _, ed_p = post_proc_state_series(res, (name, :ed_p), dt) - _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt) - _, ψ_kq = post_proc_state_series(res, (name, :ψ_kq), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_p = post_proc_state_series(res, (name, :eq_p), dt, unique_timestamps) + _, ed_p = post_proc_state_series(res, (name, :ed_p), dt, unique_timestamps) + _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt, unique_timestamps) + _, ψ_kq = post_proc_state_series(res, (name, :ψ_kq), dt, unique_timestamps) #Get parameters R = PSY.get_R(machine) @@ -482,11 +504,12 @@ function _machine_current( base_power_ratio::Float64, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_p = post_proc_state_series(res, (name, :eq_p), dt) - _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt) - _, ψq_pp = post_proc_state_series(res, (name, :ψq_pp), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_p = post_proc_state_series(res, (name, :eq_p), dt, unique_timestamps) + _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt, unique_timestamps) + _, ψq_pp = post_proc_state_series(res, (name, :ψq_pp), dt, unique_timestamps) #Get parameters R = PSY.get_R(machine) @@ -527,9 +550,10 @@ function _field_current( V_I::Vector{Float64}, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {M <: PSY.Machine} @warn("Field current is not supported in the machine type $(M). Returning zeros.") - ts, _ = post_proc_state_series(res, (name, :δ), dt) + ts, _ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) I_fd = zeros(length(ts)) return ts, I_fd end @@ -545,12 +569,13 @@ function _field_current( V_I::Vector{Float64}, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {M <: Union{PSY.RoundRotorQuadratic, PSY.RoundRotorExponential}} - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_p = post_proc_state_series(res, (name, :eq_p), dt) - _, ed_p = post_proc_state_series(res, (name, :ed_p), dt) - _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt) - _, ψ_kq = post_proc_state_series(res, (name, :ψ_kd), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_p = post_proc_state_series(res, (name, :eq_p), dt, unique_timestamps) + _, ed_p = post_proc_state_series(res, (name, :ed_p), dt, unique_timestamps) + _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt, unique_timestamps) + _, ψ_kq = post_proc_state_series(res, (name, :ψ_kd), dt, unique_timestamps) #Get parameters R = PSY.get_R(machine) @@ -599,11 +624,12 @@ function _field_current( V_I::Vector{Float64}, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_p = post_proc_state_series(res, (name, :eq_p), dt) - _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt) - _, ψq_pp = post_proc_state_series(res, (name, :ψq_pp), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_p = post_proc_state_series(res, (name, :eq_p), dt, unique_timestamps) + _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt, unique_timestamps) + _, ψq_pp = post_proc_state_series(res, (name, :ψq_pp), dt, unique_timestamps) #Get parameters R = PSY.get_R(machine) @@ -646,11 +672,12 @@ function _field_current( V_I::Vector{Float64}, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, δ = post_proc_state_series(res, (name, :δ), dt) - _, eq_p = post_proc_state_series(res, (name, :eq_p), dt) - _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt) - _, ψq_pp = post_proc_state_series(res, (name, :ψq_pp), dt) + ts, δ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) + _, eq_p = post_proc_state_series(res, (name, :eq_p), dt, unique_timestamps) + _, ψ_kd = post_proc_state_series(res, (name, :ψ_kd), dt, unique_timestamps) + _, ψq_pp = post_proc_state_series(res, (name, :ψq_pp), dt, unique_timestamps) #Get parameters R = PSY.get_R(machine) @@ -695,8 +722,9 @@ function _field_voltage( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {A <: PSY.AVR} - return post_proc_state_series(res, (name, :Vf), dt) + return post_proc_state_series(res, (name, :Vf), dt, unique_timestamps) end """ @@ -708,9 +736,10 @@ function _field_voltage( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) Vf0 = PSY.get_Vf(avr) - ts, _ = post_proc_state_series(res, (name, :δ), dt) + ts, _ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) Vf = Vf0 * ones(length(ts)) return ts, Vf end @@ -724,9 +753,10 @@ function _field_voltage( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, Ve = post_proc_state_series(res, (name, :Ve), dt) - _, Xad_Ifd = post_proc_field_current_series(res, name, dt) + ts, Ve = post_proc_state_series(res, (name, :Ve), dt, unique_timestamps) + _, Xad_Ifd = post_proc_field_current_series(res, name, dt, unique_timestamps) Kc = PSY.get_Kc(avr) I_N = Kc * Xad_Ifd ./ Ve Vf = Ve .* rectifier_function.(I_N) @@ -742,9 +772,10 @@ function _field_voltage( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, Vr2 = post_proc_state_series(res, (name, :Vr2), dt) - _, Ifd = post_proc_field_current_series(res, name, dt) + ts, Vr2 = post_proc_state_series(res, (name, :Vr2), dt, unique_timestamps) + _, Ifd = post_proc_field_current_series(res, name, dt, unique_timestamps) V_min, V_max = PSY.get_Efd_lim(avr) bus_str = split(name, "-")[2] bus_num = parse(Int, bus_str) @@ -778,15 +809,16 @@ function _field_voltage( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) # Obtain state Va - ts, Va = post_proc_state_series(res, (name, :Va), dt) + ts, Va = post_proc_state_series(res, (name, :Va), dt, unique_timestamps) # Obtain field current - _, Ifd = post_proc_field_current_series(res, name, dt) + _, Ifd = post_proc_field_current_series(res, name, dt, unique_timestamps) # Obtain PSS output - _, Vs = post_proc_pss_output_series(res, name, dt) + _, Vs = post_proc_pss_output_series(res, name, dt, unique_timestamps) # Get parameters VOS = PSY.get_PSS_flags(avr) @@ -903,8 +935,9 @@ function _pss_output( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) - ts, _ = post_proc_state_series(res, (name, :δ), dt) + ts, _ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) return ts, zeros(length(ts)) end @@ -917,14 +950,15 @@ function _pss_output( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) # Obtain states - ts, x_p2 = post_proc_state_series(res, (name, :x_p2), dt) - _, x_p3 = post_proc_state_series(res, (name, :x_p3), dt) - _, x_p4 = post_proc_state_series(res, (name, :x_p4), dt) - _, x_p5 = post_proc_state_series(res, (name, :x_p5), dt) - _, x_p6 = post_proc_state_series(res, (name, :x_p6), dt) - _, x_p7 = post_proc_state_series(res, (name, :x_p7), dt) + ts, x_p2 = post_proc_state_series(res, (name, :x_p2), dt, unique_timestamps) + _, x_p3 = post_proc_state_series(res, (name, :x_p3), dt, unique_timestamps) + _, x_p4 = post_proc_state_series(res, (name, :x_p4), dt, unique_timestamps) + _, x_p5 = post_proc_state_series(res, (name, :x_p5), dt, unique_timestamps) + _, x_p6 = post_proc_state_series(res, (name, :x_p6), dt, unique_timestamps) + _, x_p7 = post_proc_state_series(res, (name, :x_p7), dt, unique_timestamps) # Get Parameters A1 = PSY.get_A1(pss) @@ -984,9 +1018,10 @@ function _mechanical_torque( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) # TODO: This will not plot correctly when changing P_ref in a callback - ts, _ = _post_proc_state_series(res.solution, 1, dt) + ts, _ = _post_proc_state_series(res.solution, 1, dt, unique_timestamps) setpoints = get_setpoints(res) P_ref = setpoints[name]["P_ref"] efficiency = PSY.get_efficiency(tg) @@ -1003,6 +1038,7 @@ function _mechanical_torque( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) # Get params Tc = PSY.get_Tc(tg) @@ -1010,9 +1046,9 @@ function _mechanical_torque( T4 = PSY.get_T4(tg) T5 = PSY.get_T5(tg) # Get state results - ts, x_g1 = post_proc_state_series(res, (name, :x_g1), dt) - _, x_g2 = post_proc_state_series(res, (name, :x_g2), dt) - _, x_g3 = post_proc_state_series(res, (name, :x_g3), dt) + ts, x_g1 = post_proc_state_series(res, (name, :x_g1), dt, unique_timestamps) + _, x_g2 = post_proc_state_series(res, (name, :x_g2), dt, unique_timestamps) + _, x_g3 = post_proc_state_series(res, (name, :x_g3), dt, unique_timestamps) τm = zeros(length(ts)) for ix in 1:length(ts) y_ll, _ = lead_lag(x_g1[ix], x_g2[ix], 1.0, T3, Tc) @@ -1031,6 +1067,7 @@ function _mechanical_torque( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) # TODO: This will not plot correctly when changing P_ref in a callback # Get params @@ -1041,8 +1078,8 @@ function _mechanical_torque( T1 = PSY.get_T1(tg) T2 = PSY.get_T2(tg) # Get state results - ts, xg = post_proc_state_series(res, (name, :xg), dt) - _, ω = post_proc_state_series(res, (name, :ω), dt) + ts, xg = post_proc_state_series(res, (name, :xg), dt, unique_timestamps) + _, ω = post_proc_state_series(res, (name, :ω), dt, unique_timestamps) τm = zeros(length(ts)) for ix in 1:length(ts) y_ll, _ = lead_lag(inv_R * (ω_ref - ω[ix]), xg[ix], 1.0, T1, T2) @@ -1060,6 +1097,7 @@ function _mechanical_torque( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) # TODO: This will not plot correctly when changing P_ref in a callback # Get params @@ -1072,9 +1110,9 @@ function _mechanical_torque( T3 = PSY.get_T3(tg) D_T = PSY.get_D_T(tg) # Get state results - ts, x_g1 = post_proc_state_series(res, (name, :x_g1), dt) - _, x_g2 = post_proc_state_series(res, (name, :x_g2), dt) - _, ω = post_proc_state_series(res, (name, :ω), dt) + ts, x_g1 = post_proc_state_series(res, (name, :x_g1), dt, unique_timestamps) + _, x_g2 = post_proc_state_series(res, (name, :x_g2), dt, unique_timestamps) + _, ω = post_proc_state_series(res, (name, :ω), dt, unique_timestamps) ref_in = inv_R * (P_ref .- (ω .- 1.0)) τm = zeros(length(ts)) for ix in 1:length(ts) @@ -1095,12 +1133,13 @@ function _mechanical_torque( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) # Get params D_turb = PSY.get_D_turb(tg) # Get state results - ts, x_g2 = post_proc_state_series(res, (name, :x_g2), dt) - _, ω = post_proc_state_series(res, (name, :ω), dt) + ts, x_g2 = post_proc_state_series(res, (name, :x_g2), dt, unique_timestamps) + _, ω = post_proc_state_series(res, (name, :ω), dt, unique_timestamps) P_m = x_g2 - D_turb * (ω .- 1.0) τm = P_m ./ ω return ts, τm @@ -1116,7 +1155,7 @@ function _mechanical_torque( res::SimulationResults, dt::Union{Nothing, Float64}, ) - ts, x_a3 = post_proc_state_series(res, (name, :x_a3), dt) + ts, x_a3 = post_proc_state_series(res, (name, :x_a3), dt, unique_timestamps) return ts, x_a3 end """ @@ -1128,6 +1167,7 @@ function _mechanical_torque( name::String, res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) # Get params q_nl = PSY.get_q_nl(tg) @@ -1136,9 +1176,9 @@ function _mechanical_torque( setpoints = get_setpoints(res) ω_ref = setpoints[name]["ω_ref"] # Get state results - ts, x_g3 = post_proc_state_series(res, (name, :x_g3), dt) - _, x_g4 = post_proc_state_series(res, (name, :x_g4), dt) - _, ω = post_proc_state_series(res, (name, :ω), dt) + ts, x_g3 = post_proc_state_series(res, (name, :x_g3), dt, unique_timestamps) + _, x_g4 = post_proc_state_series(res, (name, :x_g4), dt, unique_timestamps) + _, ω = post_proc_state_series(res, (name, :ω), dt, unique_timestamps) Δω = ω .- ω_ref h = (x_g4 ./ x_g3) .^ 2 τm = ((x_g4 .- q_nl) .* h * At - D_T * Δω .* x_g3) ./ ω diff --git a/src/post_processing/post_proc_inverter.jl b/src/post_processing/post_proc_inverter.jl index 6c53b37ae..4aa367419 100644 --- a/src/post_processing/post_proc_inverter.jl +++ b/src/post_processing/post_proc_inverter.jl @@ -9,6 +9,7 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} #Obtain Data @@ -30,6 +31,7 @@ function compute_output_current( res, dynamic_device, dt, + unique_timestamps, ) end @@ -44,6 +46,7 @@ function compute_field_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} @warn("Field current does not exist in inverters. Returning zeros.") return (nothing, zeros(length(V_R))) @@ -58,9 +61,10 @@ function compute_field_voltage( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} @warn("Field voltage does not exist in inverters. Returning zeros.") - _, state = _post_proc_state_series(res.solution, 1, dt) + _, state = _post_proc_state_series(res.solution, 1, dt, unique_timestamps) return (nothing, zeros(length(state))) end @@ -73,9 +77,10 @@ function compute_mechanical_torque( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} @warn("Mechanical torque is not used in inverters. Returning zeros.") - _, state = _post_proc_state_series(res.solution, 1, dt) + _, state = _post_proc_state_series(res.solution, 1, dt, unique_timestamps) return (nothing, zeros(length(state))) end @@ -83,6 +88,7 @@ function compute_frequency( res::SimulationResults, dyn_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} outer_control = PSY.get_outer_control(dyn_device) frequency_estimator = PSY.get_freq_estimator(dyn_device) @@ -93,6 +99,7 @@ function compute_frequency( res, dyn_device, dt, + unique_timestamps, ) end @@ -107,8 +114,9 @@ function _frequency( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {F <: PSY.FrequencyEstimator, G <: PSY.DynamicInverter} - ts, ω = post_proc_state_series(res, (name, :ω_oc), dt) + ts, ω = post_proc_state_series(res, (name, :ω_oc), dt, unique_timestamps) return ts, ω end @@ -123,10 +131,11 @@ function _frequency( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {F <: PSY.FrequencyEstimator, G <: PSY.DynamicInverter} P_ref = PSY.get_P_ref(PSY.get_active_power_control(outer_control)) ω_ref = PSY.get_ω_ref(dynamic_device) - ts, p_oc = post_proc_state_series(res, (name, :p_oc), dt) + ts, p_oc = post_proc_state_series(res, (name, :p_oc), dt, unique_timestamps) Rp = PSY.get_Rp(outer_control.active_power_control) ω_oc = ω_ref .+ Rp .* (P_ref .- p_oc) return ts, ω_oc @@ -146,6 +155,7 @@ function _frequency( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {F <: PSY.FrequencyEstimator, G <: PSY.DynamicInverter} p_ref = PSY.get_P_ref(PSY.get_active_power_control(outer_control)) q_ref = PSY.get_Q_ref(PSY.get_reactive_power_control(outer_control)) @@ -153,10 +163,10 @@ function _frequency( k1 = PSY.get_k1(active_power_control) ψ = PSY.get_ψ(active_power_control) γ = ψ - pi / 2 - ts, E_oc = post_proc_state_series(res, (name, :E_oc), dt) - _, p_elec_out = post_proc_activepower_series(res, name, dt) - _, q_elec_out = post_proc_reactivepower_series(res, name, dt) - ω_sys = _system_frequency_series(res, dt) + ts, E_oc = post_proc_state_series(res, (name, :E_oc), dt, unique_timestamps) + _, p_elec_out = post_proc_activepower_series(res, name, dt, unique_timestamps) + _, q_elec_out = post_proc_reactivepower_series(res, name, dt, unique_timestamps) + ω_sys = _system_frequency_series(res, dt, unique_timestamps) ω_oc = ω_sys .+ (k1 ./ E_oc .^ 2) .* @@ -175,11 +185,12 @@ function _frequency( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} kp_pll = PSY.get_kp_pll(freq_estimator) ki_pll = PSY.get_ki_pll(freq_estimator) - ts, vpll_q = post_proc_state_series(res, (name, :vq_pll), dt) - _, ε_pll = post_proc_state_series(res, (name, :ε_pll), dt) + ts, vpll_q = post_proc_state_series(res, (name, :vq_pll), dt, unique_timestamps) + _, ε_pll = post_proc_state_series(res, (name, :ε_pll), dt, unique_timestamps) pi_output = [pi_block(x, y, kp_pll, ki_pll)[1] for (x, y) in zip(vpll_q, ε_pll)] ω_pll = pi_output .+ 1.0 #See Hug ISGT-EUROPE2018 eqn. 9 return ts, ω_pll @@ -196,12 +207,13 @@ function _frequency( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} kp_pll = PSY.get_kp_pll(freq_estimator) ki_pll = PSY.get_ki_pll(freq_estimator) - ts, vpll_q = post_proc_state_series(res, (name, :vq_pll), dt) - _, vpll_d = post_proc_state_series(res, (name, :vd_pll), dt) - _, ε_pll = post_proc_state_series(res, (name, :ε_pll), dt) + ts, vpll_q = post_proc_state_series(res, (name, :vq_pll), dt, unique_timestamps) + _, vpll_d = post_proc_state_series(res, (name, :vd_pll), dt, unique_timestamps) + _, ε_pll = post_proc_state_series(res, (name, :ε_pll), dt, unique_timestamps) pi_output = [pi_block(x, y, kp_pll, ki_pll)[1] for (x, y) in zip(atan.(vpll_q, vpll_d), ε_pll)] ω_pll = pi_output .+ 1.0 #See Hug ISGT-EUROPE2018 eqn. 9 @@ -211,6 +223,7 @@ end function _system_frequency_series( res::SimulationResults, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) if get_global_vars_update_pointers(res)[GLOBAL_VAR_SYS_FREQ_INDEX] == 0 ω_sys = 1.0 @@ -219,7 +232,7 @@ function _system_frequency_series( get_global_index(res), get_global_vars_update_pointers(res)[GLOBAL_VAR_SYS_FREQ_INDEX], ) - ω_sys = post_proc_state_series(res, ω_sys_state, dt)[2] + ω_sys = post_proc_state_series(res, ω_sys_state, dt, unique_timestamps)[2] end return ω_sys end @@ -238,9 +251,10 @@ function _output_current( res::SimulationResults, dynamic_device::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {C <: PSY.Converter, G <: PSY.DynamicInverter} - ts, ir_filter = post_proc_state_series(res, (name, :ir_filter), dt) - ts, ii_filter = post_proc_state_series(res, (name, :ii_filter), dt) + ts, ir_filter = post_proc_state_series(res, (name, :ir_filter), dt, unique_timestamps) + ts, ii_filter = post_proc_state_series(res, (name, :ii_filter), dt, unique_timestamps) return ts, base_power_ratio * ir_filter, base_power_ratio * ii_filter end @@ -259,6 +273,7 @@ function _output_current( res::SimulationResults, ::G, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} #Get Converter parameters @@ -281,9 +296,9 @@ function _output_current( Iq_extra = max.(K_hv * (V_t .- Vo_lim), 0.0) #Compute current - ts, Ip = post_proc_state_series(res, (name, :Ip), dt) - _, Iq = post_proc_state_series(res, (name, :Iq), dt) - _, Vmeas = post_proc_state_series(res, (name, :Vmeas), dt) + ts, Ip = post_proc_state_series(res, (name, :Ip), dt, unique_timestamps) + _, Iq = post_proc_state_series(res, (name, :Iq), dt, unique_timestamps) + _, Vmeas = post_proc_state_series(res, (name, :Vmeas), dt, unique_timestamps) Ip_sat = Ip if Lvpl_sw == 1 LVPL = get_LVPL_gain.(Vmeas, Zerox, Brkpt, Lvpl1) diff --git a/src/post_processing/post_proc_loads.jl b/src/post_processing/post_proc_loads.jl index f6d14cfeb..90cea9329 100644 --- a/src/post_processing/post_proc_loads.jl +++ b/src/post_processing/post_proc_loads.jl @@ -9,6 +9,7 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) #Obtain Data sys = get_system(res) @@ -29,10 +30,10 @@ function compute_output_current( v_ds = V_R # Read states - ts, ψ_qs = post_proc_state_series(res, (name, :ψ_qs), dt) - _, ψ_ds = post_proc_state_series(res, (name, :ψ_ds), dt) - _, ψ_qr = post_proc_state_series(res, (name, :ψ_qr), dt) - _, ψ_dr = post_proc_state_series(res, (name, :ψ_dr), dt) + ts, ψ_qs = post_proc_state_series(res, (name, :ψ_qs), dt, unique_timestamps) + _, ψ_ds = post_proc_state_series(res, (name, :ψ_ds), dt, unique_timestamps) + _, ψ_qr = post_proc_state_series(res, (name, :ψ_qr), dt, unique_timestamps) + _, ψ_dr = post_proc_state_series(res, (name, :ψ_dr), dt, unique_timestamps) #Additional Fluxes ψ_mq = X_aq * (ψ_qs / X_ls + ψ_qr / X_lr) # (4.14-15) in Krause @@ -59,6 +60,7 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) #Obtain Data sys = get_system(res) @@ -75,8 +77,8 @@ function compute_output_current( X_p = PSY.get_X_p(dynamic_device) # Read states - ts, ψ_qr = post_proc_state_series(res, (name, :ψ_qr), dt) - _, ψ_dr = post_proc_state_series(res, (name, :ψ_dr), dt) + ts, ψ_qr = post_proc_state_series(res, (name, :ψ_qr), dt, unique_timestamps) + _, ψ_dr = post_proc_state_series(res, (name, :ψ_dr), dt, unique_timestamps) # voltages in QD v_qs = V_I @@ -113,6 +115,7 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) #TODO: We should dispatch this using the ZipLoad model that we have, but that would # require to properly have access to it in the SimResults. @@ -153,6 +156,7 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) #TODO: We should dispatch this using the ZipLoad model that we have, but that would # require to properly have access to it in the SimResults. @@ -194,6 +198,7 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) #TODO: We should dispatch this using the ZipLoad model that we have, but that would # require to properly have access to it in the SimResults. diff --git a/src/post_processing/post_proc_source.jl b/src/post_processing/post_proc_source.jl index 7ee34d2ce..f0b570a89 100644 --- a/src/post_processing/post_proc_source.jl +++ b/src/post_processing/post_proc_source.jl @@ -6,6 +6,7 @@ function post_proc_source_voltage_current_series( res::SimulationResults, name::String, dt = nothing, + unique_timestamps = true, ) system = get_system(res) bus_lookup = get_bus_lookup(res) @@ -31,7 +32,8 @@ function post_proc_source_voltage_current_series( bus_ix = get(bus_lookup, PSY.get_number(PSY.get_bus(device)), -1) - ts, Vb_R, Vb_I = post_proc_voltage_series(solution, bus_ix, n_buses, dt) + ts, Vb_R, Vb_I = + post_proc_voltage_series(solution, bus_ix, n_buses, dt, unique_timestamps) I_R = R_th * (Vs_R .- Vb_R) / Z_sq + X_th * (Vs_I .- Vb_I) / Z_sq I_I = R_th * (Vs_I .- Vb_I) / Z_sq - X_th * (Vs_R .- Vb_R) / Z_sq @@ -44,8 +46,14 @@ Function to obtain output real current for a source. It receives the simulation the Source name and an optional argument of the time step of the results. """ -function get_source_real_current_series(res::SimulationResults, name::String, dt = nothing) - ts, _, _, I_R, _ = post_proc_source_voltage_current_series(res, name, dt) +function get_source_real_current_series( + res::SimulationResults, + name::String, + dt = nothing, + unique_timestamps = true, +) + ts, _, _, I_R, _ = + post_proc_source_voltage_current_series(res, name, dt, unique_timestamps) return ts, I_R end @@ -58,8 +66,10 @@ function get_source_imaginary_current_series( res::SimulationResults, name::String, dt = nothing, + unique_timestamps = true, ) - ts, _, _, _, I_I = post_proc_source_voltage_current_series(res, name, dt) + ts, _, _, _, I_I = + post_proc_source_voltage_current_series(res, name, dt, unique_timestamps) return ts, I_I end @@ -74,10 +84,11 @@ function compute_output_current( V_R::Vector{Float64}, V_I::Vector{Float64}, dt::Union{Nothing, Float64, Vector{Float64}}, + unique_timestamps::Bool, ) name = PSY.get_name(dynamic_device) - ts, Vt_internal = post_proc_state_series(res, (name, :Vt), dt) - _, θt_internal = post_proc_state_series(res, (name, :θt), dt) + ts, Vt_internal = post_proc_state_series(res, (name, :Vt), dt, unique_timestamps) + _, θt_internal = post_proc_state_series(res, (name, :θt), dt, unique_timestamps) Vr_internal = Vt_internal .* cos.(θt_internal) Vi_internal = Vt_internal .* sin.(θt_internal) diff --git a/test/test_case_OMIB.jl b/test/test_case_OMIB.jl index 8cd2c90f7..9f1e007e2 100644 --- a/test/test_case_OMIB.jl +++ b/test/test_case_OMIB.jl @@ -87,6 +87,9 @@ Ybus_change = NetworkSwitch( @test isa(power, Tuple{Vector{Float64}, Vector{Float64}}) @test isa(rpower, Tuple{Vector{Float64}, Vector{Float64}}) + series_repeat_timestamps = + get_state_series(results, ("generator-102-1", :δ); unique_timestamps = false) + @test length(δ) + 1 == length(series_repeat_timestamps[2]) finally @info("removing test files") rm(path; force = true, recursive = true)