diff --git a/Project.toml b/Project.toml index 8ae07ff87..2ffab3813 100644 --- a/Project.toml +++ b/Project.toml @@ -4,40 +4,56 @@ authors = ["Jose Daniel Lara, Rodrigo Henriquez"] version = "0.15.0" [deps] +ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" FastClosures = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" InfrastructureSystems = "2cd47ed4-ca9b-11e9-27f2-ab636a7671f1" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" +NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" PowerFlows = "94fada2c-fd9a-4e89-8d82-81405f5cb4f6" PowerNetworkMatrices = "bed98974-b02a-5e2f-9fe0-a103f5c450dd" PowerSystems = "bcd98974-b02a-5e2f-9ee0-a103f5c450dd" PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [compat] +ChainRulesCore = "1.23" +ComponentArrays = "0.15" DataFrames = "1" DataStructures = "~0.18" +DiffEqBase = "6.151" DocStringExtensions = "~0.9" +Enzyme = "0.12" FastClosures = "^0.3" ForwardDiff = "~v0.10" InfrastructureSystems = "2" NLsolve = "4" +NonlinearSolve = "3.11" PowerSystems = "4" PowerFlows = "^0.7" PowerNetworkMatrices = "^0.11" PrettyTables = "1, 2" SciMLBase = "2" +SciMLSensitivity = "7.0" + TimerOutputs = "~0.5" LinearAlgebra = "1" Logging = "1" + Random = "1" SparseArrays = "1" +Zygote = "0.6" julia = "^1.6" diff --git a/docs/make.jl b/docs/make.jl index 733595e8b..f3069edd8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -23,6 +23,7 @@ makedocs(; "Reference Frames" => "reference_frames.md", "Perturbations" => "perturbations.md", "Time Delays" => "time_delays.md", + "Sensitivity Analysis" => "sensitivity_analysis.md", "Industrial Renewable Models" => "generic.md", "Generator Component Library" => Any[ "Machine" => "component_models/machines.md", diff --git a/docs/src/sensitivity_analysis.md b/docs/src/sensitivity_analysis.md new file mode 100644 index 000000000..4351bb453 --- /dev/null +++ b/docs/src/sensitivity_analysis.md @@ -0,0 +1,9 @@ +# Sensitivity Analysis + +PowerSimulationsDynamics has limited support for performing sensitivity analysis of system parameters with respect to a user defined loss function. See the tutorial for an example of sensitivity analysis. + +* `ForwardDiffSensitivity()` is used as the method for differentiating through the solve. See `SciMLSensitivity.jl` for more details. +* The gradient of the function provided by PSID can be calculated using `Zygote.jl`. +* The Jacobian is not passed to the ODE Problem during sensitivity analysis. +* Parameters for sensitivity analysis must not change the steady state operating condition. Parameters cannot be in the mass matrix. This limitation is expected to be relaxed in the future. +* Limited to mass matrix formulations and pure Julia solvers. Can check if a solver is compatible with `SciMLBase.isautodifferentiable()` diff --git a/src/PowerSimulationsDynamics.jl b/src/PowerSimulationsDynamics.jl index 690fce642..a983f4e4c 100644 --- a/src/PowerSimulationsDynamics.jl +++ b/src/PowerSimulationsDynamics.jl @@ -61,19 +61,26 @@ export is_valid export transform_load_to_constant_impedance export transform_load_to_constant_current export transform_load_to_constant_power +export get_parameter_values +export get_parameter_labels +export get_sensitivity_functions ####################################### Package Imports #################################### import Logging import InfrastructureSystems import SciMLBase +import SciMLSensitivity import DataStructures: OrderedDict import DataFrames: DataFrame +import DiffEqBase import Random import ForwardDiff import SparseArrays import LinearAlgebra import Base.to_index -import NLsolve +import Base.length +import NLsolve #Using Nlsovle wrapped using NonlinearSolve interfaace +import NonlinearSolve import PrettyTables import Base.ImmutableDict import PowerSystems @@ -81,12 +88,19 @@ import PowerFlows import PowerNetworkMatrices import TimerOutputs import FastClosures: @closure +import Enzyme +Enzyme.API.runtimeActivity!(true) #Needed for "activity unstable" code: https://enzymead.github.io/Enzyme.jl/stable/faq/ +Enzyme.API.looseTypeAnalysis!(true) #Required for using component arrays with Enzyme +import ChainRulesCore +import ComponentArrays +import Zygote const PSY = PowerSystems const IS = InfrastructureSystems const PSID = PowerSimulationsDynamics const PF = PowerFlows const PNM = PowerNetworkMatrices +const CRC = ChainRulesCore using DocStringExtensions @@ -104,6 +118,7 @@ include("base/device_wrapper.jl") include("base/branch_wrapper.jl") include("base/frequency_reference.jl") include("base/simulation_model.jl") +include("utils/parameters.jl") include("base/simulation_inputs.jl") include("base/perturbations.jl") include("base/caches.jl") @@ -179,6 +194,10 @@ include("post_processing/post_proc_results.jl") include("post_processing/post_proc_loads.jl") include("post_processing/post_proc_source.jl") +#Sensitivity Analysis +include("base/sensitivity_rule.jl") #Custom rule to support duplicated kwargs for callbacks. See: https://github.com/EnzymeAD/Enzyme.jl/issues/1491 +include("base/sensitivity_analysis.jl") + #Utils include("utils/psy_utils.jl") include("utils/immutable_dicts.jl") diff --git a/src/base/branch_wrapper.jl b/src/base/branch_wrapper.jl index afbe6691b..f779b9b78 100644 --- a/src/base/branch_wrapper.jl +++ b/src/base/branch_wrapper.jl @@ -2,11 +2,11 @@ Wraps DynamicBranch devices from PowerSystems to handle changes in controls and connection status, and allocate the required indexes of the state space. """ -struct BranchWrapper +mutable struct BranchWrapper branch::PSY.DynamicBranch system_base_power::Float64 system_base_frequency::Float64 - connection_status::Base.RefValue{Float64} + connection_status::Float64 bus_ix_from::Int bus_ix_to::Int ix_range::Vector{Int} @@ -26,7 +26,7 @@ struct BranchWrapper branch, sys_base_power, sys_base_freq, - Base.Ref(1.0), + 1.0, bus_ix_from, bus_ix_to, ix_range, diff --git a/src/base/caches.jl b/src/base/caches.jl index 81f6df6d8..f5e48ce00 100644 --- a/src/base/caches.jl +++ b/src/base/caches.jl @@ -70,11 +70,11 @@ get_global_vars(jc::JacobianCache, ::Type{T}) where {T <: ForwardDiff.Dual} = struct SimCache{F} <: Cache f!::F bus_count::Int - ode_output::Vector{Float64} - branches_ode::Vector{Float64} - current_balance::Vector{Float64} - inner_vars::Vector{Float64} - global_vars::Vector{Float64} + ode_output::Vector{Real} + branches_ode::Vector{Real} + current_balance::Vector{Real} + inner_vars::Vector{Real} + global_vars::Vector{Real} end function SimCache(f!, inputs::SimulationInputs) @@ -84,14 +84,19 @@ function SimCache(f!, inputs::SimulationInputs) bus_count = get_bus_count(inputs) inner_vars_count = get_inner_vars_count(inputs) n_global_vars = length(keys(get_global_vars_update_pointers(inputs))) + global_vars = setindex!( + zeros(Real, n_global_vars), + 1.0, + GLOBAL_VAR_SYS_FREQ_INDEX, + ) return SimCache{typeof(f!)}( f!, bus_count, - zeros(Float64, n_inj), - zeros(Float64, n_branches), - zeros(Float64, 2 * bus_count), - zeros(Float64, inner_vars_count), - setindex!(zeros(Float64, n_global_vars), 1.0, GLOBAL_VAR_SYS_FREQ_INDEX), + zeros(Real, n_inj), + zeros(Real, n_branches), + zeros(Real, 2 * bus_count), + zeros(Real, inner_vars_count), + global_vars, ) end @@ -103,11 +108,11 @@ function get_current_injections_i(sc::SimCache, ::Type{Float64}) return view(sc.current_balance, ((sc.bus_count + 1):(sc.bus_count * 2))) end -get_ode_output(sc::SimCache, ::Type{Float64}) = sc.ode_output -get_branches_ode(sc::SimCache, ::Type{Float64}) = sc.branches_ode -get_current_balance(sc::SimCache, ::Type{Float64}) = sc.current_balance -get_inner_vars(sc::SimCache, ::Type{Float64}) = sc.inner_vars -get_global_vars(sc::SimCache, ::Type{Float64}) = sc.global_vars +get_ode_output(sc::SimCache, ::Type{T}) where {T <: Real} = sc.ode_output +get_branches_ode(sc::SimCache, ::Type{T}) where {T <: Real} = sc.branches_ode +get_current_balance(sc::SimCache, ::Type{T}) where {T <: Real} = sc.current_balance +get_inner_vars(sc::SimCache, ::Type{T}) where {T <: Real} = sc.inner_vars +get_global_vars(sc::SimCache, ::Type{T}) where {T <: Real} = sc.global_vars get_ω_sys(cache::Cache, T::Type{<:ACCEPTED_REAL_TYPES}) = get_global_vars(cache, T)[GLOBAL_VAR_SYS_FREQ_INDEX] diff --git a/src/base/definitions.jl b/src/base/definitions.jl index c9aa22a60..33b935b06 100644 --- a/src/base/definitions.jl +++ b/src/base/definitions.jl @@ -126,7 +126,6 @@ get_vars_ix() = Dict{Int, Int}(GLOBAL_VAR_SYS_FREQ_INDEX => -1) const RELAXED_NLSOLVE_F_TOLERANCE = :1e-6 const STRICT_NLSOLVE_F_TOLERANCE = :1e-9 -const NLSOLVE_X_TOLERANCE = :1e-9 const MINIMAL_ACCEPTABLE_NLSOLVE_F_TOLERANCE = :1e-4 const MAX_INIT_RESIDUAL = 10.0 const MAX_NLSOLVE_INTERATIONS = 10 @@ -212,7 +211,7 @@ const DIFFEQ_SOLVE_KWARGS = [ """ Defines the status of the simulation object """ -@enum BUILD_STATUS begin +@enum STATUS begin BUILT = 0 BUILD_IN_PROGRESS = 1 BUILD_INCOMPLETE = 2 @@ -223,6 +222,16 @@ Defines the status of the simulation object SIMULATION_FAILED = 7 end +""" +Defines the level of initializing simulation +""" +@enum INITIALIZE_LEVEL begin + POWERFLOW_AND_DEVICES = 0 + DEVICES_ONLY = 1 + FLAT_START = 2 + INITIALIZED = 3 +end + const BUILD_TIMER = TimerOutputs.TimerOutput() -const ACCEPTED_REAL_TYPES = Union{Float64, ForwardDiff.Dual} +const ACCEPTED_REAL_TYPES = Union{Float64, ForwardDiff.Dual, Real} diff --git a/src/base/device_wrapper.jl b/src/base/device_wrapper.jl index 58b3f5d1c..ff0af3a8e 100644 --- a/src/base/device_wrapper.jl +++ b/src/base/device_wrapper.jl @@ -28,19 +28,15 @@ get_delays( """ Wraps DynamicInjection devices from PowerSystems to handle changes in controls and connection -status, and allocate the required indexes of the state space. +status, and allocate the required indexes of the state space and parameter space. """ -struct DynamicWrapper{T <: PSY.DynamicInjection} +mutable struct DynamicWrapper{T <: PSY.DynamicInjection} device::T system_base_power::Float64 system_base_frequency::Float64 - static_type::Type{<:PSY.StaticInjection} + static_device::PSY.StaticInjection bus_category::Type{<:BusCategory} - connection_status::Base.RefValue{Float64} - V_ref::Base.RefValue{Float64} - ω_ref::Base.RefValue{Float64} - P_ref::Base.RefValue{Float64} - Q_ref::Base.RefValue{Float64} + connection_status::Float64 inner_vars_index::Vector{Int} ix_range::Vector{Int} ode_range::Vector{Int} @@ -54,13 +50,9 @@ struct DynamicWrapper{T <: PSY.DynamicInjection} device::T, system_base_power::Float64, system_base_frequency::Float64, - static_type::Type{<:PSY.StaticInjection}, + static_device::PSY.StaticInjection, bus_category::Type{<:BusCategory}, - connection_status::Base.RefValue{Float64}, - V_ref::Base.RefValue{Float64}, - ω_ref::Base.RefValue{Float64}, - P_ref::Base.RefValue{Float64}, - Q_ref::Base.RefValue{Float64}, + connection_status::Float64, inner_vars_index, ix_range, ode_range, @@ -76,13 +68,9 @@ struct DynamicWrapper{T <: PSY.DynamicInjection} device, system_base_power, system_base_frequency, - static_type, + static_device, bus_category, connection_status, - V_ref, - ω_ref, - P_ref, - Q_ref, Vector{Int}(inner_vars_index), Vector{Int}(ix_range), Vector{Int}(ode_range), @@ -119,7 +107,7 @@ function state_port_mappings(dynamic_device::PSY.DynamicInjection, device_states end function DynamicWrapper( - device::T, + static_device::T, dynamic_device::D, bus_ix::Int, ix_range, @@ -129,28 +117,15 @@ function DynamicWrapper( sys_base_freq, ) where {T <: PSY.StaticInjection, D <: PSY.DynamicInjection} device_states = PSY.get_states(dynamic_device) - component_state_mapping, input_port_mapping = state_port_mappings(dynamic_device, device_states) - - # Consider the special case when the static device is StandardLoad - if isa(device, PSY.StandardLoad) - reactive_power = PF.get_total_q(device) - else - reactive_power = PSY.get_reactive_power(device) - end - return DynamicWrapper( dynamic_device, sys_base_power, sys_base_freq, - T, - BUS_MAP[PSY.get_bustype(PSY.get_bus(device))], - Base.Ref(1.0), - Base.Ref(PSY.get_V_ref(dynamic_device)), - Base.Ref(PSY.get_ω_ref(dynamic_device)), - Base.Ref(PSY.get_P_ref(dynamic_device)), - Base.Ref(reactive_power), + static_device, + BUS_MAP[PSY.get_bustype(PSY.get_bus(static_device))], + 1.0, inner_var_range, ix_range, ode_range, @@ -173,7 +148,7 @@ function DynamicWrapper( end function DynamicWrapper( - device::PSY.ThermalStandard, + static_device::PSY.ThermalStandard, dynamic_device::PSY.AggregateDistributedGenerationA, bus_ix::Int, ix_range, @@ -192,13 +167,9 @@ function DynamicWrapper( dynamic_device, sys_base_power, sys_base_freq, - PSY.ThermalStandard, - BUS_MAP[PSY.get_bustype(PSY.get_bus(device))], - Base.Ref(1.0), - Base.Ref(PSY.get_V_ref(dynamic_device)), - Base.Ref(PSY.get_ω_ref(dynamic_device)), - Base.Ref(PSY.get_P_ref(dynamic_device)), - Base.Ref(PSY.get_reactive_power(device)), + static_device, + BUS_MAP[PSY.get_bustype(PSY.get_bus(static_device))], + 1.0, inner_var_range, ix_range, ode_range, @@ -221,7 +192,7 @@ function DynamicWrapper( end function DynamicWrapper( - device::PSY.Source, + static_device::PSY.Source, dynamic_device::D, bus_ix::Int, ix_range, @@ -231,20 +202,16 @@ function DynamicWrapper( sys_base_freq, ) where {D <: PSY.DynamicInjection} device_states = PSY.get_states(dynamic_device) - IS.@assert_op PSY.get_X_th(dynamic_device) == PSY.get_X_th(device) - IS.@assert_op PSY.get_R_th(dynamic_device) == PSY.get_R_th(device) + IS.@assert_op PSY.get_X_th(dynamic_device) == PSY.get_X_th(static_device) + IS.@assert_op PSY.get_R_th(dynamic_device) == PSY.get_R_th(static_device) return DynamicWrapper( dynamic_device, sys_base_power, sys_base_freq, - PSY.Source, - BUS_MAP[PSY.get_bustype(PSY.get_bus(device))], - Base.Ref(1.0), - Base.Ref(0.0), - Base.Ref(0.0), - Base.Ref(0.0), - Base.Ref(0.0), + static_device, + BUS_MAP[PSY.get_bustype(PSY.get_bus(static_device))], + 1.0, collect(inner_var_range), collect(ix_range), collect(ode_range), @@ -280,6 +247,7 @@ function _index_port_mapping!( end get_device(wrapper::DynamicWrapper) = wrapper.device +get_static_device(wrapper::DynamicWrapper) = wrapper.static_device get_device_type(::DynamicWrapper{T}) where {T <: PSY.DynamicInjection} = T get_bus_category(wrapper::DynamicWrapper) = wrapper.bus_category get_inner_vars_index(wrapper::DynamicWrapper) = wrapper.inner_vars_index @@ -294,16 +262,6 @@ get_ext(wrapper::DynamicWrapper) = wrapper.ext get_system_base_power(wrapper::DynamicWrapper) = wrapper.system_base_power get_system_base_frequency(wrapper::DynamicWrapper) = wrapper.system_base_frequency -get_P_ref(wrapper::DynamicWrapper) = wrapper.P_ref[] -get_Q_ref(wrapper::DynamicWrapper) = wrapper.Q_ref[] -get_V_ref(wrapper::DynamicWrapper) = wrapper.V_ref[] -get_ω_ref(wrapper::DynamicWrapper) = wrapper.ω_ref[] - -set_P_ref(wrapper::DynamicWrapper, val::Float64) = wrapper.P_ref[] = val -set_Q_ref(wrapper::DynamicWrapper, val::Float64) = wrapper.Q_ref[] = val -set_V_ref(wrapper::DynamicWrapper, val::Float64) = wrapper.V_ref[] = val -set_ω_ref(wrapper::DynamicWrapper, val::Float64) = wrapper.ω_ref[] = val - # PSY overloads for the wrapper PSY.get_name(wrapper::DynamicWrapper) = PSY.get_name(wrapper.device) PSY.get_ext(wrapper::DynamicWrapper) = PSY.get_ext(wrapper.device) @@ -353,6 +311,13 @@ function get_local_state_ix( return wrapper.component_state_mapping[index(T)] end +function get_local_parameter_ix( + wrapper::DynamicWrapper, + ::Type{T}, +) where {T <: PSY.DynamicComponent} + return wrapper.component_parameter_mapping[index(T)] +end + function get_input_port_ix( wrapper::DynamicWrapper, ::Type{T}, @@ -368,13 +333,9 @@ function get_input_port_ix(wrapper::DynamicWrapper, ::T) where {T <: PSY.Dynamic return get_input_port_ix(wrapper, T) end -struct StaticWrapper{T <: PSY.StaticInjection, V} +mutable struct StaticWrapper{T <: PSY.StaticInjection, V} device::T - connection_status::Base.RefValue{Float64} - V_ref::Base.RefValue{Float64} - θ_ref::Base.RefValue{Float64} - P_ref::Base.RefValue{Float64} - Q_ref::Base.RefValue{Float64} + connection_status::Float64 bus_ix::Int ext::Dict{String, Any} end @@ -383,11 +344,8 @@ function DynamicWrapper(device::T, bus_ix::Int) where {T <: PSY.Device} bus = PSY.get_bus(device) StaticWrapper{T, BUS_MAP[PSY.get_bustype(bus)]}( device, - Base.Ref(1.0), - Base.Ref(PSY.get_magnitude(bus)), - Base.Ref(PSY.get_angle(bus)), - Base.Ref(PSY.get_active_power(device)), - Base.Ref(PSY.get_reactive_power(device)), + 1.0, + Vector{Int}(), bus_ix, Dict{String, Any}(), ) @@ -397,11 +355,7 @@ function StaticWrapper(device::T, bus_ix::Int) where {T <: PSY.Source} bus = PSY.get_bus(device) return StaticWrapper{T, BUS_MAP[PSY.get_bustype(bus)]}( device, - Base.Ref(1.0), - Base.Ref(PSY.get_internal_voltage(device)), - Base.Ref(PSY.get_internal_angle(device)), - Base.Ref(PSY.get_active_power(device)), - Base.Ref(PSY.get_reactive_power(device)), + 1.0, bus_ix, Dict{String, Any}(), ) @@ -415,16 +369,6 @@ get_device(wrapper::StaticWrapper) = wrapper.device get_bus_ix(wrapper::StaticWrapper) = wrapper.bus_ix get_ext(wrapper::StaticWrapper) = wrapper.ext -get_P_ref(wrapper::StaticWrapper) = wrapper.P_ref[] -get_Q_ref(wrapper::StaticWrapper) = wrapper.Q_ref[] -get_V_ref(wrapper::StaticWrapper) = wrapper.V_ref[] -get_θ_ref(wrapper::StaticWrapper) = wrapper.θ_ref[] - -set_P_ref(wrapper::StaticWrapper, val::Float64) = wrapper.P_ref[] = val -set_Q_ref(wrapper::StaticWrapper, val::Float64) = wrapper.Q_ref[] = val -set_V_ref(wrapper::StaticWrapper, val::Float64) = wrapper.V_ref[] = val -set_θ_ref(wrapper::StaticWrapper, val::Float64) = wrapper.θ_ref[] = val - PSY.get_bus(wrapper::StaticWrapper) = PSY.get_bus(wrapper.device) PSY.get_active_power(wrapper::StaticWrapper) = PSY.get_active_power(wrapper.device) PSY.get_reactive_power(wrapper::StaticWrapper) = PSY.get_reactive_power(wrapper.device) @@ -438,6 +382,8 @@ mutable struct ExpLoadParams Q_coeff::Float64 end +#Note: References are left in StaticLoadWrapper but transfered to ComponentArray. +#Can remove from the StaticLoadWrapper but would need access to the ElectricLoad components when calculating references. mutable struct StaticLoadWrapper bus::PSY.Bus V_ref::Float64 @@ -526,19 +472,11 @@ get_P_impedance(wrapper::StaticLoadWrapper) = wrapper.P_impedance get_Q_power(wrapper::StaticLoadWrapper) = wrapper.Q_power get_Q_current(wrapper::StaticLoadWrapper) = wrapper.Q_current get_Q_impedance(wrapper::StaticLoadWrapper) = wrapper.Q_impedance + get_exp_params(wrapper::StaticLoadWrapper) = wrapper.exp_params get_exp_names(wrapper::StaticLoadWrapper) = wrapper.exp_names get_bus_ix(wrapper::StaticLoadWrapper) = wrapper.bus_ix -set_V_ref!(wrapper::StaticLoadWrapper, val::Float64) = wrapper.V_ref = val -set_θ_ref!(wrapper::StaticLoadWrapper, val::Float64) = wrapper.θ_ref = val -set_P_power!(wrapper::StaticLoadWrapper, val::Float64) = wrapper.P_power = val -set_P_current!(wrapper::StaticLoadWrapper, val::Float64) = wrapper.P_current = val -set_P_impedance!(wrapper::StaticLoadWrapper, val::Float64) = wrapper.P_impedance = val -set_Q_power!(wrapper::StaticLoadWrapper, val::Float64) = wrapper.Q_power = val -set_Q_current!(wrapper::StaticLoadWrapper, val::Float64) = wrapper.Q_current = val -set_Q_impedance!(wrapper::StaticLoadWrapper, val::Float64) = wrapper.Q_impedance = val - function set_connection_status(wrapper::Union{StaticWrapper, DynamicWrapper}, val::Int) if val == 0 @debug "Generator $(PSY.get_name(wrapper)) status set to off" @@ -547,8 +485,8 @@ function set_connection_status(wrapper::Union{StaticWrapper, DynamicWrapper}, va else error("Invalid status $val. It can only take values 1 or 0") end - wrapper.connection_status[] = Float64(val) + wrapper.connection_status = Float64(val) end get_connection_status(wrapper::Union{StaticWrapper, DynamicWrapper}) = - wrapper.connection_status[] + wrapper.connection_status diff --git a/src/base/jacobian.jl b/src/base/jacobian.jl index 4eb4c2515..00f9ba2b9 100644 --- a/src/base/jacobian.jl +++ b/src/base/jacobian.jl @@ -22,12 +22,33 @@ end function (J::JacobianFunctionWrapper{NoDelays})( JM::U, x::AbstractVector{Float64}, + p, ) where {U <: Union{Matrix{Float64}, SparseArrays.SparseMatrixCSC{Float64, Int64}}} J.x .= x J.Jf(JM, x) return end +function (J::JacobianFunctionWrapper{NoDelays})( + JM::U, + x::AbstractVector{Float64}, +) where {U <: Union{Matrix{Float64}, SparseArrays.SparseMatrixCSC{Float64, Int64}}} + J.x .= x + J.Jf(JM, x) + return +end + +function (J::JacobianFunctionWrapper{HasDelays})( + JM::U, + x::AbstractVector{Float64}, + p, +) where {U <: Union{Matrix{Float64}, SparseArrays.SparseMatrixCSC{Float64, Int64}}} + h(p, t; idxs = nothing) = typeof(idxs) <: Number ? x[idxs] : x + J.x .= x + J.Jf(JM, x, h, 0.0) + return +end + function (J::JacobianFunctionWrapper{HasDelays})( JM::U, x::AbstractVector{Float64}, @@ -87,13 +108,14 @@ end function JacobianFunctionWrapper( m!::SystemModel{MassMatrixModel, NoDelays}, - x0_guess::Vector{Float64}; + x0_guess::Vector{Float64}, + p::AbstractArray{Float64}; # Improve the heuristic to do sparsity detection sparse_retrieve_loop::Int = 0, #max(3, length(x0_guess) ÷ 100), ) x0 = deepcopy(x0_guess) n = length(x0) - m_ = (residual, x) -> m!(residual, x, nothing, 0.0) + m_ = (residual, x) -> m!(residual, x, p, 0.0) jconfig = ForwardDiff.JacobianConfig(m_, similar(x0), x0, ForwardDiff.Chunk(x0)) Jf = (Jv, x) -> begin @debug "Evaluating Jacobian Function" @@ -127,12 +149,13 @@ end function JacobianFunctionWrapper( m!::SystemModel{ResidualModel}, - x0::Vector{Float64}; + x0::Vector{Float64}, + p::AbstractArray{Float64}; # Improve the heuristic to do sparsity detection sparse_retrieve_loop::Int = max(3, length(x0) ÷ 100), ) n = length(x0) - m_ = (residual, x) -> m!(residual, zeros(n), x, nothing, 0.0) + m_ = (residual, x) -> m!(residual, zeros(n), x, p, 0.0) jconfig = ForwardDiff.JacobianConfig(m_, similar(x0), x0, ForwardDiff.Chunk(x0)) Jf = (Jv, x) -> begin @debug "Evaluating Jacobian Function" @@ -167,7 +190,8 @@ end function JacobianFunctionWrapper( m!::SystemModel{MassMatrixModel, HasDelays}, - x0_guess::Vector{Float64}; + x0_guess::Vector{Float64}, + p::AbstractArray{Float64}; # Improve the heuristic to do sparsity detection sparse_retrieve_loop::Int = 0, #max(3, length(x0_guess) ÷ 100), ) @@ -176,7 +200,7 @@ function JacobianFunctionWrapper( Jf = (Jv, x, h, t) -> begin @debug "Evaluating Jacobian Function" - m_ = (residual, x) -> m!(residual, x, h, nothing, t) + m_ = (residual, x) -> m!(residual, x, h, p, t) jconfig = ForwardDiff.JacobianConfig(m_, similar(x0), x0, ForwardDiff.Chunk(x0)) ForwardDiff.jacobian!(Jv, m_, zeros(n), x, jconfig) @@ -211,11 +235,13 @@ function get_jacobian( ::Type{T}, inputs::SimulationInputs, x0_init::Vector{Float64}, + p::AbstractArray{Float64}, sparse_retrieve_loop::Int, ) where {T <: SimulationModel} return JacobianFunctionWrapper( T(inputs, x0_init, JacobianCache), - x0_init; + x0_init, + p; sparse_retrieve_loop = sparse_retrieve_loop, ) end @@ -242,7 +268,8 @@ function get_jacobian( # Deepcopy avoid system modifications simulation_system = deepcopy(system) inputs = SimulationInputs(T, simulation_system, ReferenceBus()) - x0_init = get_flat_start(inputs) + p = get_parameters(inputs) + x0_init = _get_flat_start(inputs) set_operating_point!(x0_init, inputs, system) - return get_jacobian(T, inputs, x0_init, sparse_retrieve_loop) + return get_jacobian(T, inputs, x0_init, p, sparse_retrieve_loop) end diff --git a/src/base/nlsolve_wrapper.jl b/src/base/nlsolve_wrapper.jl index 371e29bcf..b5dafddfc 100644 --- a/src/base/nlsolve_wrapper.jl +++ b/src/base/nlsolve_wrapper.jl @@ -11,80 +11,64 @@ failed(sol::NLsolveWrapper) = sol.failed function _get_model_closure( model::SystemModel{MassMatrixModel, NoDelays}, ::Vector{Float64}, + p::AbstractArray{Float64}, ) - return (residual, x) -> model(residual, x, nothing, 0.0) + return (residual, x, p) -> model(residual, x, p, 0.0) end function _get_model_closure( model::SystemModel{MassMatrixModel, HasDelays}, x0::Vector{Float64}, + p::AbstractArray{Float64}, ) h(p, t; idxs = nothing) = typeof(idxs) <: Number ? x0[idxs] : x0 - return (residual, x) -> model(residual, x, h, nothing, 0.0) + return (residual, x, p) -> model(residual, x, h, p, 0.0) end function _get_model_closure( model::SystemModel{ResidualModel, NoDelays}, x0::Vector{Float64}, + p::AbstractArray{Float64}, ) dx0 = zeros(length(x0)) - return (residual, x) -> model(residual, dx0, x, nothing, 0.0) + return (residual, x, p) -> model(residual, dx0, x, p, 0.0) end function _nlsolve_call( initial_guess::Vector{Float64}, + p::AbstractArray, f_eval::Function, jacobian::JacobianFunctionWrapper, f_tolerance::Float64, - solver::Symbol, + solver::NonlinearSolve.AbstractNonlinearSolveAlgorithm, show_trace::Bool, ) - df = NLsolve.OnceDifferentiable( - f_eval, - jacobian, - initial_guess, - similar(initial_guess), - jacobian.Jv, + f = SciMLBase.NonlinearFunction(f_eval; jac = jacobian) + prob = NonlinearSolve.NonlinearProblem{true}(f, initial_guess, p) + sol = NonlinearSolve.solve( + prob, + solver; + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + abstol = f_tolerance, + reltol = f_tolerance, + maxiters = MAX_NLSOLVE_INTERATIONS, + show_trace = Val(show_trace), ) - sys_solve = NLsolve.nlsolve( - df, - initial_guess; - xtol = NLSOLVE_X_TOLERANCE, - ftol = f_tolerance, - method = solver, - iterations = MAX_NLSOLVE_INTERATIONS, - show_trace = show_trace, - ) # Solve using initial guess x0 - return NLsolveWrapper(sys_solve.zero, NLsolve.converged(sys_solve), false) + return NLsolveWrapper(sol.u, SciMLBase.successful_retcode(sol), false) end -function _nlsolve_call( - initial_guess::Vector{Float64}, - f_eval::Function, - f_tolerance::Float64, - solver::Symbol, - show_trace::Bool, +function _convergence_check( + sys_solve::NLsolveWrapper, + tol::Float64, + solv::Symbol, ) - sys_solve = NLsolve.nlsolve( - f_eval, - initial_guess; - xtol = NLSOLVE_X_TOLERANCE, - ftol = f_tolerance, - iterations = MAX_NLSOLVE_INTERATIONS, - method = solver, - show_trace = show_trace, - ) # Solve using initial guess x0 - return NLsolveWrapper(sys_solve.zero, NLsolve.converged(sys_solve), false) -end - -function _convergence_check(sys_solve::NLsolveWrapper, tol::Float64, solv::Symbol) if converged(sys_solve) - @info( + @warn( "Initialization non-linear solve succeeded with a tolerance of $(tol) using solver $(solv). Saving solution." ) else @warn( - "Initialization non-linear solve convergence failed, initial conditions do not meet conditions for an stable equilibrium.\nAttempting again with reduced numeric tolerance and using another solver" + "Initialization non-linear solve convergence failed with a tolerance of $(tol) using solver $(solv), initial conditions do not meet conditions for an stable equilibrium.\nAttempting again with reduced numeric tolerance and using another solver" ) end return converged(sys_solve) @@ -116,7 +100,7 @@ function _check_residual( if sum_residual > tolerance state_map = make_global_state_map(inputs) for (k, val) in state_map - inputs.global_state_map[k] = val + get_global_state_map(inputs)[k] = val end gen_name = "" state = "" @@ -133,6 +117,7 @@ function _check_residual( Generator = $gen_name, state = $state. Residual error is too large to continue") else + bus_count = get_bus_count(inputs) bus_no = ix > bus_count ? ix - bus_count : ix component = ix > bus_count ? "imag" : "real" error("The initial residual in the state located at $ix has a value of $val. @@ -143,25 +128,56 @@ function _check_residual( return end +function _refine_initial_condition!(x0, p, prob) + h(p, t; idxs = nothing) = typeof(idxs) <: Number ? x0[idxs] : x0 + function ff(u, x0, p) + if typeof(prob) <: SciMLBase.ODEProblem + prob.f.f(u, x0, p, 0.0) + elseif typeof(prob) <: SciMLBase.DDEProblem + prob.f.f(u, x0, h, p, 0.0) + end + return + end + #residual = similar(x0) + #ff(residual, x0, p) #Error: ERROR: AssertionError: length(getcolptr(S)) == size(S, 2) + 1 && (getcolptr(S))[end] - 1 == length(rowvals(S)) == length(nonzeros(S)) + #_check_residual(residual, inputs, MAX_INIT_RESIDUAL) + solver = NonlinearSolve.TrustRegion() + probnl = NonlinearSolve.NonlinearProblem{true}(ff, x0, p) + #for tol in [STRICT_NLSOLVE_F_TOLERANCE, RELAXED_NLSOLVE_F_TOLERANCE] #ERROR: Enzyme execution failed., Enzyme: Non-constant keyword argument found for Tuple{UInt64, typeof(Core.kwcall), Duplicated{@NamedTuple{reltol::Float64, abstol::Float64}}, + for solver in [NonlinearSolve.TrustRegion(), NonlinearSolve.NewtonRaphson()] + sol = NonlinearSolve.solve( + probnl, + solver; + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + maxiters = MAX_NLSOLVE_INTERATIONS, + ) + converged = SciMLBase.successful_retcode(sol) + x0 .= sol.u + if converged + break + end + end + #end + return nothing +end + function refine_initial_condition!( sim::Simulation, model::SystemModel, jacobian::JacobianFunctionWrapper, ) @assert sim.status != BUILD_INCOMPLETE - - if sim.status == SIMULATION_INITIALIZED - @info "Simulation already initialized. Refinement not executed" - return - end converged = false - initial_guess = get_initial_conditions(sim) + initial_guess = get_x0(sim) inputs = get_simulation_inputs(sim) + parameters = get_parameters(inputs) bus_range = get_bus_range(inputs) powerflow_solution = deepcopy(initial_guess[bus_range]) - f! = _get_model_closure(model, initial_guess) + f! = _get_model_closure(model, initial_guess, parameters) residual = similar(initial_guess) - f!(residual, initial_guess) + f!(residual, initial_guess, parameters) _check_residual(residual, inputs, MAX_INIT_RESIDUAL) for tol in [STRICT_NLSOLVE_F_TOLERANCE, RELAXED_NLSOLVE_F_TOLERANCE] if converged @@ -170,8 +186,15 @@ function refine_initial_condition!( for solv in [:trust_region, :newton] @debug "Start NLSolve System Run with $(solv) and F_tol = $tol" show_trace = sim.console_level <= Logging.Info - sys_solve = _nlsolve_call(initial_guess, f!, jacobian, tol, solv, show_trace) - #sys_solve = _nlsolve_call(initial_guess, f!, tol, solv, show_trace) + sys_solve = _nlsolve_call( + initial_guess, + parameters, + f!, + jacobian, + tol, + NonlinearSolve.NLsolveJL(; method = solv), + show_trace, + ) failed(sys_solve) && return BUILD_FAILED converged = _convergence_check(sys_solve, tol, solv) @debug "Write initial guess vector using $solv with tol = $tol convergence = $converged" @@ -189,18 +212,19 @@ function refine_initial_condition!( ) end - f!(residual, initial_guess) + f!(residual, initial_guess, parameters) if !converged || (sum(residual) > MINIMAL_ACCEPTABLE_NLSOLVE_F_TOLERANCE) _check_residual(residual, inputs, MINIMAL_ACCEPTABLE_NLSOLVE_F_TOLERANCE) - @warn("Initialization didn't found a solution to desired tolerances.\\ - Initial conditions do not meet conditions for an stable equilibrium. \\ - Simulation might fail") + @warn( + "Initialization didn't found a solution to desired tolerances.\\ + Initial conditions do not meet conditions for an stable equilibrium. \\ + Simulation might fail" + ) end pf_diff = abs.(powerflow_solution .- initial_guess[bus_range]) if maximum(pf_diff) > MINIMAL_ACCEPTABLE_NLSOLVE_F_TOLERANCE @warn "The resulting voltages in the initial conditions differ from the power flow results" end - return end diff --git a/src/base/perturbations.jl b/src/base/perturbations.jl index 567145b4d..15bd841ed 100644 --- a/src/base/perturbations.jl +++ b/src/base/perturbations.jl @@ -74,7 +74,7 @@ function _get_branch_for_perturbation( return branch end -function get_affect(::SimulationInputs, sys::PSY.System, pert::BranchImpedanceChange) +function get_affect(inputs::SimulationInputs, sys::PSY.System, pert::BranchImpedanceChange) branch = _get_branch_for_perturbation(sys, pert) mult = 0.0 if pert.multiplier < 0.0 @@ -93,17 +93,17 @@ function get_affect(::SimulationInputs, sys::PSY.System, pert::BranchImpedanceCh return (integrator) -> begin @debug "Changing impedance line $(PSY.get_name(branch)) by a factor of $(pert.multiplier)" - ybus_update!(integrator.p, branch, mult) + ybus_update!(inputs, branch, mult) end return end -function get_affect(::SimulationInputs, sys::PSY.System, pert::BranchTrip) +function get_affect(inputs::SimulationInputs, sys::PSY.System, pert::BranchTrip) branch = _get_branch_for_perturbation(sys, pert) return (integrator) -> begin @debug "Tripping line $(PSY.get_name(branch))" - ybus_update!(integrator.p, branch, -1.0) + ybus_update!(inputs, branch, -1.0) end return end @@ -328,8 +328,8 @@ function ybus_update!( return end -function ybus_update!(integrator_params, branch::PSY.ACBranch, mult::Float64) - ybus_update!(integrator_params.ybus_rectangular, branch, integrator_params.lookup, mult) +function ybus_update!(inputs, branch::PSY.ACBranch, mult::Float64) + ybus_update!(get_ybus(inputs), branch, get_lookup(inputs), mult) return end @@ -367,12 +367,12 @@ function NetworkSwitch(time::Float64, ybus::PNM.Ybus) return NetworkSwitch(time, ybus.data) end -function get_affect(::SimulationInputs, ::PSY.System, pert::NetworkSwitch) +function get_affect(inputs::SimulationInputs, ::PSY.System, pert::NetworkSwitch) return (integrator) -> begin # TODO: This code can be more performant using SparseMatrix methods for (i, v) in enumerate(pert.ybus_rectangular) @debug "Changing Ybus network" - integrator.p.ybus_rectangular[i] = v + get_ybus(inputs)[i] = v end return end @@ -472,10 +472,16 @@ end function get_affect(inputs::SimulationInputs, ::PSY.System, pert::ControlReferenceChange) wrapped_device_ix = _find_device_index(inputs, pert.device) + p = get_parameters(inputs) return (integrator) -> begin - wrapped_device = get_dynamic_injectors(integrator.p)[wrapped_device_ix] + wrapped_device = get_dynamic_injectors(inputs)[wrapped_device_ix] + wrapped_device_name = _get_wrapper_name(wrapped_device) + p_change_ix = ComponentArrays.label2index( + p, + join((wrapped_device_name, "refs", pert.signal), "."), + ) @debug "Changing $(PSY.get_name(wrapped_device)) $(pert.signal) to $(pert.ref_value)" - getfield(wrapped_device, pert.signal)[] = pert.ref_value + integrator.p[p_change_ix] .= pert.ref_value return end end @@ -508,15 +514,21 @@ end function get_affect(inputs::SimulationInputs, ::PSY.System, pert::SourceBusVoltageChange) wrapped_device_ix = _find_device_index(inputs, pert.device) + wrapped_device = get_static_injectors(inputs)[wrapped_device_ix] + wrapped_device_name = _get_wrapper_name(wrapped_device) + p = get_parameters(inputs) + if pert.signal == :θ_ref || pert.signal == :V_ref + p_change_ix = ComponentArrays.label2index( + p, + join((wrapped_device_name, "refs", pert.signal), "."), + ) + else + error( + "Signal $(pert.signal) not accepted as a control reference change in SourceBus", + ) + end return (integrator) -> begin - wrapped_device = get_static_injectors(integrator.p)[wrapped_device_ix] - if pert.signal == :V_ref - set_V_ref(wrapped_device, pert.ref_value) - elseif pert.signal == :θ_ref - set_θ_ref(wrapped_device, pert.ref_value) - else - error("Signal $signal not accepted as a control reference change in SourceBus") - end + integrator.p[p_change_ix] .= pert.ref_value return end end @@ -542,7 +554,7 @@ end function get_affect(inputs::SimulationInputs, ::PSY.System, pert::GeneratorTrip) wrapped_device_ix = _find_device_index(inputs, pert.device) return (integrator) -> begin - wrapped_device = get_dynamic_injectors(integrator.p)[wrapped_device_ix] + wrapped_device = get_dynamic_injectors(inputs)[wrapped_device_ix] ix_range = get_ix_range(wrapped_device) @debug "Changing connection status $(PSY.get_name(wrapped_device)), setting states $ix_range to 0.0" if integrator.du !== nothing @@ -612,14 +624,23 @@ end function get_affect(inputs::SimulationInputs, sys::PSY.System, pert::LoadChange) sys_base_power = PSY.get_base_power(sys) wrapped_device_ix = _find_zip_load_ix(inputs, pert.device) + p = get_parameters(inputs) ld = pert.device if !PSY.get_available(ld) - @error("Load $(PSY.get_name(ld)) is unavailable. Perturbation ignored") + @error( + "Load $(PSY.get_name(ld)) is unavailable. Perturbation ignored" + ) return end ref_value = pert.ref_value signal = pert.signal if isa(ld, PSY.PowerLoad) + wrapped_zip = get_static_loads(inputs)[wrapped_device_ix] + wrapped_zip_name = _get_wrapper_name(wrapped_zip) + P_power_ix = + ComponentArrays.label2index(p, join((wrapped_zip_name, "refs", "P_power"), ".")) + Q_power_ix = + ComponentArrays.label2index(p, join((wrapped_zip_name, "refs", "P_power"), ".")) return (integrator) -> begin base_power_conversion = PSY.get_base_power(ld) / sys_base_power P_old = PSY.get_active_power(ld) @@ -635,49 +656,65 @@ function get_affect(inputs::SimulationInputs, sys::PSY.System, pert::LoadChange) "Signal is not accepted for Constant PowerLoad. Please specify the correct signal type.", ) end - wrapped_zip = get_static_loads(integrator.p)[wrapped_device_ix] - P_power = get_P_power(wrapped_zip) - Q_power = get_Q_power(wrapped_zip) - set_P_power!(wrapped_zip, P_power + P_change) - set_Q_power!(wrapped_zip, Q_power + Q_change) + integrator.p[P_power_ix] .= integrator.p[P_power_ix] .+ P_change + integrator.p[Q_power_ix] .= integrator.p[Q_power_ix] .+ Q_change @debug "Changing load at bus $(PSY.get_name(wrapped_zip)) $(pert.signal) to $(pert.ref_value)" return end elseif isa(ld, PSY.StandardLoad) return (integrator) -> begin base_power_conversion = PSY.get_base_power(ld) / sys_base_power - wrapped_zip = get_static_loads(integrator.p)[wrapped_device_ix] + wrapped_zip = get_static_loads(inputs)[wrapped_device_ix] + wrapped_zip_name = _get_wrapper_name(wrapped_zip) + P_impedance_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "P_impedance"), "."), + ) + Q_impedance_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "Q_impedance"), "."), + ) + P_power_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "P_power"), "."), + ) + Q_power_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "Q_power"), "."), + ) + P_current_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "P_current"), "."), + ) + Q_current_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "Q_current"), "."), + ) # List all cases for StandardLoad changes if signal ∈ [:P_ref, :P_ref_impedance] P_old = PSY.get_impedance_active_power(ld) P_change = (ref_value - P_old) * base_power_conversion - P_impedance = get_P_impedance(wrapped_zip) - set_P_impedance!(wrapped_zip, P_impedance + P_change) + integrator.p[P_impedance_ix] .= integrator.p[P_impedance_ix] .+ P_change elseif signal ∈ [:Q_ref, :Q_ref_impedance] Q_old = PSY.get_impedance_reactive_power(ld) Q_change = (ref_value - Q_old) * base_power_conversion - Q_impedance = get_Q_impedance(wrapped_zip) - set_Q_impedance!(wrapped_zip, Q_impedance + Q_change) + integrator.p[Q_impedance_ix] .= integrator.p[Q_impedance_ix] .+ Q_change elseif signal == :P_ref_power P_old = PSY.get_constant_active_power(ld) P_change = (ref_value - P_old) * base_power_conversion - P_power = get_P_power(wrapped_zip) - set_P_power!(wrapped_zip, P_power + P_change) + integrator.p[P_power_ix] .= integrator.p[P_power_ix] .+ P_change elseif signal == :Q_ref_power Q_old = PSY.get_constant_reactive_power(ld) Q_change = (ref_value - Q_old) * base_power_conversion - Q_power = get_Q_power(wrapped_zip) - set_Q_power!(wrapped_zip, Q_power + Q_change) + integrator.p[Q_power_ix] .= integrator.p[Q_power_ix] .+ Q_change elseif signal == :P_ref_current P_old = PSY.get_current_active_power(ld) P_change = (ref_value - P_old) * base_power_conversion - P_current = get_P_current(wrapped_zip) - set_P_current!(wrapped_zip, P_current + P_change) + integrator.p[P_current_ix] .= integrator.p[P_current_ix] .+ P_change elseif signal == :Q_ref_current Q_old = PSY.get_current_reactive_power(ld) Q_change = (ref_value - Q_old) * base_power_conversion - Q_current = get_Q_current(wrapped_zip) - set_Q_current!(wrapped_zip, Q_current + Q_change) + integrator.p[Q_current_ix] .= integrator.p[Q_current_ix] .+ Q_change else error("It should never be here. Should have failed in the constructor.") end @@ -685,7 +722,8 @@ function get_affect(inputs::SimulationInputs, sys::PSY.System, pert::LoadChange) elseif isa(ld, PSY.ExponentialLoad) return (integrator) -> begin ld_name = PSY.get_name(ld) - wrapped_zip = get_static_loads(integrator.p)[wrapped_device_ix] + wrapped_zip = get_static_loads(inputs)[wrapped_device_ix] + wrapped_zip_name = _get_wrapper_name(wrapped_zip) exp_names = get_exp_names(wrapped_zip) exp_vects = get_exp_params(wrapped_zip) tuple_ix = exp_names[ld_name] @@ -726,22 +764,28 @@ end function get_affect(inputs::SimulationInputs, sys::PSY.System, pert::LoadTrip) sys_base_power = PSY.get_base_power(sys) wrapped_device_ix = _find_zip_load_ix(inputs, pert.device) + p = get_parameters(inputs) ld = pert.device if !PSY.get_available(ld) - @error("Load $(PSY.get_name(ld)) is unavailable. Perturbation ignored") + @error( + "Load $(PSY.get_name(ld)) is unavailable. Perturbation ignored" + ) return end if isa(ld, PSY.PowerLoad) base_power_conversion = PSY.get_base_power(ld) / sys_base_power P_trip = PSY.get_active_power(ld) * base_power_conversion Q_trip = PSY.get_reactive_power(ld) * base_power_conversion + wrapped_zip = get_static_loads(inputs)[wrapped_device_ix] + wrapped_zip_name = _get_wrapper_name(wrapped_zip) + P_power_ix = + ComponentArrays.label2index(p, join((wrapped_zip_name, "refs", "P_power"), ".")) + Q_power_ix = + ComponentArrays.label2index(p, join((wrapped_zip_name, "refs", "Q_power"), ".")) return (integrator) -> begin PSY.set_available!(ld, false) - wrapped_zip = get_static_loads(integrator.p)[wrapped_device_ix] - P_power = get_P_power(wrapped_zip) - Q_power = get_Q_power(wrapped_zip) - set_P_power!(wrapped_zip, P_power - P_trip) - set_Q_power!(wrapped_zip, Q_power - Q_trip) + integrator.p[P_power_ix] .= integrator.p[P_power_ix] .- P_trip + integrator.p[Q_power_ix] .= integrator.p[Q_power_ix] .- Q_trip @debug "Removing load power values from ZIP load at $(PSY.get_name(wrapped_zip))" return end @@ -755,22 +799,43 @@ function get_affect(inputs::SimulationInputs, sys::PSY.System, pert::LoadTrip) Q_impedance_trip = PSY.get_impedance_reactive_power(ld) * base_power_conversion return (integrator) -> begin PSY.set_available!(ld, false) - wrapped_zip = get_static_loads(integrator.p)[wrapped_device_ix] + wrapped_zip = get_static_loads(inputs)[wrapped_device_ix] + wrapped_zip_name = _get_wrapper_name(wrapped_zip) + P_impedance_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "P_impedance"), "."), + ) + Q_impedance_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "Q_impedance"), "."), + ) + P_power_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "P_power"), "."), + ) + Q_power_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "Q_power"), "."), + ) + P_current_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "P_current"), "."), + ) + Q_current_ix = ComponentArrays.label2index( + p, + join((wrapped_zip_name, "refs", "Q_current"), "."), + ) # Update Constant Power - P_power = get_P_power(wrapped_zip) - Q_power = get_Q_power(wrapped_zip) - set_P_power!(wrapped_zip, P_power - P_power_trip) - set_Q_power!(wrapped_zip, Q_power - Q_power_trip) + integrator.p[P_power_ix] .= integrator.p[P_power_ix] .- P_power_trip + integrator.p[Q_power_ix] .= integrator.p[Q_power_ix] .- Q_power_trip # Update Constant Current - P_current = get_P_current(wrapped_zip) - Q_current = get_Q_current(wrapped_zip) - set_P_current!(wrapped_zip, P_current - P_current_trip) - set_Q_current!(wrapped_zip, Q_current - Q_current_trip) + integrator.p[P_current_ix] .= integrator.p[P_current_ix] .- P_current_trip + integrator.p[Q_current_ix] .= integrator.p[Q_current_ix] .- Q_current_trip # Update Constant Impedance - P_impedance = get_P_impedance(wrapped_zip) - Q_impedance = get_Q_impedance(wrapped_zip) - set_P_impedance!(wrapped_zip, P_impedance - P_impedance_trip) - set_Q_impedance!(wrapped_zip, Q_impedance - Q_impedance_trip) + integrator.p[P_impedance_ix] .= + integrator.p[P_impedance_ix] .- P_impedance_trip + integrator.p[Q_impedance_ix] .= + integrator.p[Q_impedance_ix] .- Q_impedance_trip @debug "Removing load power values from ZIP load at $(PSY.get_name(wrapped_zip))" return end @@ -778,7 +843,7 @@ function get_affect(inputs::SimulationInputs, sys::PSY.System, pert::LoadTrip) return (integrator) -> begin PSY.set_available!(ld, false) ld_name = PSY.get_name(ld) - wrapped_zip = get_static_loads(integrator.p)[wrapped_device_ix] + wrapped_zip = get_static_loads(inputs)[wrapped_device_ix] exp_names = get_exp_names(wrapped_zip) exp_params = get_exp_params(wrapped_zip) tuple_ix = exp_names[ld_name] diff --git a/src/base/sensitivity_analysis.jl b/src/base/sensitivity_analysis.jl new file mode 100644 index 000000000..8c1340a1f --- /dev/null +++ b/src/base/sensitivity_analysis.jl @@ -0,0 +1,362 @@ +function get_indices_in_parameter_vector(p, device_param_pairs) + indices = Int[] + for entry in device_param_pairs + if isa(entry, Tuple) + label = join((entry[1], "params", entry[2:end]...), ".") + else + label = entry + end + ix = ComponentArrays.label2index(p, label) + if ix === nothing + @error "Index not found for entry $entry" + return nothing + end + if isa(ix, AbstractArray) + indices = vcat(indices, ix) + else + push!(indices, ix) + end + end + return indices +end + +function get_required_initialization_level(p_metadata, param_ixs) + #Check for invalid parameters + for metadata_entry in p_metadata[param_ixs] + if metadata_entry.type === DEVICE_SETPOINT + @error "The parameter given is unsupported because it is a device setpoint." + return nothing + end + if metadata_entry.in_mass_matrix === true + @error "The parameter given is not yet supported because it appears in the mass matrix" + return nothing + end + end + #Check for parameters which change the power flow + for metadata_entry in p_metadata[param_ixs] + if metadata_entry.type === NETWORK_PARAM || metadata_entry.type === NETWORK_SETPOINT + return POWERFLOW_AND_DEVICES + end + end + #Check for parameters which change device initialization + for metadata_entry in p_metadata[param_ixs] + if metadata_entry.impacts_ic == true + return DEVICES_ONLY + end + end + return INITIALIZED +end + +function get_indices_in_state_vector(sim, state_data) + @assert sim.results !== nothing + res = sim.results + global_state_index = get_global_index(res) + state_ixs = Vector{Int64}(undef, length(state_data)) + for (ix, ref) in enumerate(state_data) + if haskey(global_state_index, ref[1]) + state_ixs[ix] = get(global_state_index[ref[1]], ref[2], 0) + elseif ref[2] ∈ [:Vr, :Vi] + bus_ix = get_lookup(get_simulation_inputs(sim))[ref[1]] + if ref[2] == :Vr + state_ixs[ix] = bus_ix + else + state_ixs[ix] = bus_ix + length(get_lookup(get_simulation_inputs(sim))) + end + else + @error "Available devices: $(keys(global_state_index))" + error("State $(ref[2]) device $(ref[1]) not found in the system. ") + end + end + return state_ixs +end + +function get_parameter_values(sim, device_param_pairs) + p = sim.inputs.parameters + p_metadata = sim.inputs.parameters_metadata + ixs, _ = get_ix_and_level(p, p_metadata, device_param_pairs) + if ixs === nothing + return nothing + else + return p[ixs] + end +end + +function get_parameter_labels(sim, device_param_pairs) + p = sim.inputs.parameters + p_metadata = sim.inputs.parameters_metadata + labels = ComponentArrays.labels(p) + ixs, _ = get_ix_and_level(p, p_metadata, device_param_pairs) + if ixs === nothing + return nothing + else + return labels[ixs] + end +end + +function get_ix_and_level(p, p_metadata, param_data) + param_ixs = get_indices_in_parameter_vector(p, param_data) + metadata_ixs = get_indices_in_parameter_vector(p_metadata, param_data) + init_level = get_required_initialization_level(p_metadata, metadata_ixs) + return param_ixs, init_level +end + +function get_ix_and_level(p, p_metadata, param_data::Symbol) + @assert param_data == :All + @warn "Device setpoints and network parameters not yet supported; returning all supported parameters." + param_ixs = Int64[] + @assert ComponentArrays.labels(p) == ComponentArrays.labels(p_metadata) + for label in ComponentArrays.labels(p) + ix_metadata = ComponentArrays.label2index(p_metadata, label) + ix_param = ComponentArrays.label2index(p_metadata, label)[1] + metadata_entry = p_metadata[ix_metadata][1] + + if (metadata_entry.type === DEVICE_PARAM) && + (metadata_entry.in_mass_matrix === false) + push!(param_ixs, ix_param) + end + end + return param_ixs, DEVICES_ONLY +end + +function convert_perturbations_to_callbacks(sys, sim_inputs, perturbations) + perturbations_count = length(perturbations) + cb = Vector{SciMLBase.DiscreteCallback}(undef, perturbations_count) + tstops = Float64[] + for (ix, pert) in enumerate(perturbations) + condition = (x, t, integrator) -> t in [pert.time] + affect = get_affect(sim_inputs, sys, pert) + cb[ix] = SciMLBase.DiscreteCallback(condition, affect) + push!(tstops, pert.time) + end + callbacks = SciMLBase.CallbackSet((), tuple(cb...)) + return callbacks, tstops +end + +#Dummy function that is overloaded in PowerSimulationsDynamicsSurrogates. +#Eventually transition to EnzymeAdjoint() and remove Zygote altogether: https://github.com/SciML/OrdinaryDiffEq.jl/pull/2282 +function _non_mutating_initialization_of_ml_surrogates(x0, p_new, sim_inputs) + return x0, p_new +end + +function get_sensitivity_functions( + sim, + param_data, + state_data, + solver, + f_loss, + sys_reinit = false, + force_device_level = nothing; + kwargs..., +) + p_metadata = sim.inputs.parameters_metadata + p = sim.inputs.parameters + param_ixs, init_level = get_ix_and_level(p, p_metadata, param_data) + if force_device_level !== nothing + init_level = force_device_level + @warn "Forcing initialization level of $init_level regardless of parameters." + end + if init_level === nothing + return nothing + end + state_ixs = get_indices_in_state_vector(sim, state_data) + sim_inputs = deepcopy(sim.inputs_init) + + prob_old = sim.problem + f_old = prob_old.f + if typeof(prob_old) <: SciMLBase.ODEProblem + f_new = SciMLBase.ODEFunction{true}( + f_old.f; + mass_matrix = f_old.mass_matrix, + tgrad = (dT, u, p, t) -> dT .= false, + ) + elseif typeof(prob_old) <: SciMLBase.DDEProblem + f_new = SciMLBase.DDEFunction{true}( + f_old.f; + mass_matrix = f_old.mass_matrix, + ) + else + @error "Problem type not supported" + end + prob_new = SciMLBase.remake( + prob_old; + f = f_new, + advance_to_tstop = true, + initializealg = SciMLBase.NoInit(), + kwargs..., + ) + if param_ixs === nothing + return nothing + else + x0 = deepcopy(sim.x0_init) + sys = deepcopy(sim.sys) + function f_enzyme( + p, + x0, + sys, + sim_inputs, + prob, + data, + init_level, + sys_reinit, + callbacks, + tstops, + aux, + ) + p_new = sim_inputs.parameters + p_new[param_ixs] .= p + if init_level == POWERFLOW_AND_DEVICES + @error "POWERFLOW AND DEVICES -- not yet supported" + #_initialize_powerflow_and_devices!(x0, inputs, sys) + elseif init_level == DEVICES_ONLY + if sys_reinit + @info "Reinitializing inividual devices and full system" + _initialize_devices_only!(x0, sim_inputs) + _refine_initial_condition!(x0, p_new, prob) + else + @info "Reinitializing devices only" + _initialize_devices_only!(x0, sim_inputs) + end + elseif init_level == INITIALIZED + @info "I.C.s not impacted by parameter change" + end + if typeof(prob) <: SciMLBase.AbstractODEProblem + prob_new = SciMLBase.remake(prob; p = p_new, u0 = x0, tstops = tstops) + elseif typeof(prob) <: SciMLBase.AbstractDDEProblem + h(p, t; idxs = nothing) = begin + typeof(idxs) <: Number ? x0[idxs] : x0 + end + prob_new = + SciMLBase.remake(prob; h = h, p = p_new, u0 = x0, tstops = tstops) + end + sol = solve_with_callback(prob_new, callbacks, solver) + ix_t = unique(i -> sol.t[i], eachindex(sol.t)) + states = [sol[ix, ix_t] for ix in state_ixs] + return f_loss(p, states, data, aux) + end + function f_Zygote( + p, + x0, + sys, + sim_inputs, + prob, + data, + init_level, + sys_reinit, + perts, + aux, + ) + p_new = sim_inputs.parameters + p_new_buff = Zygote.Buffer(p_new) + for ix in eachindex(p_new) + p_new_buff[ix] = p_new[ix] + end + for (i, ix) in enumerate(param_ixs) + p_new_buff[ix] = p[i] + end + p_new = copy(p_new_buff) + #Converting perturbations to callbacks, restore after : https://github.com/EnzymeAD/Enzyme.jl/issues/1650 + @warn "Zygote assumes single perturbation" + cb = Vector{SciMLBase.DiscreteCallback}(undef, 1) + pert = perts[1] + condition = (x, t, integrator) -> t in [pert.time] + affect = get_affect(sim_inputs, sys, pert) + cb = [SciMLBase.DiscreteCallback(condition, affect)] + callbacks = SciMLBase.CallbackSet((), tuple(cb...)) + tstops = [pert.time] + + if init_level == POWERFLOW_AND_DEVICES + @error "POWERFLOW AND DEVICES -- not yet supported" + #_initialize_powerflow_and_devices!(x0, inputs, sys) + elseif init_level == DEVICES_ONLY + @warn "Reinitializing of most devices not supported with Zygote" + x0, p_new = _non_mutating_initialization_of_ml_surrogates(x0, p_new, sim_inputs) + elseif init_level == INITIALIZED + @info "I.C.s not impacted by parameter change" + end + if typeof(prob) <: SciMLBase.AbstractODEProblem + prob_new = SciMLBase.remake(prob; p = p_new, u0 = x0, tstops = tstops) + elseif typeof(prob) <: SciMLBase.AbstractDDEProblem + h(p, t; idxs = nothing) = begin + typeof(idxs) <: Number ? x0[idxs] : x0 + end + prob_new = + SciMLBase.remake(prob; h = h, p = p_new, u0 = x0, tstops = tstops) + end + sol = SciMLBase.solve(prob_new, solver; callback = callbacks) + #Hack to avoid unique(i -> sol.t[i], eachindex(sol.t)) which mutates and is incompatible with Zygote: + if sol.t[end] > pert.time + ix_first = findfirst(x -> x == pert.time, sol.t) + ix_last = findlast(x -> x == pert.time, sol.t) + ix_t = vcat(1:ix_first, (ix_last + 1):(length(sol.t))) + else + ix_t = vact(1:length(sol.t)) + end + states = [sol[ix, ix_t] for ix in state_ixs] + return f_loss(p, states, data, aux) + end + function f_forward(p, perts, data, aux) + callbacks, tstops = convert_perturbations_to_callbacks(sys, sim_inputs, perts) + f_enzyme( + p, + x0, + sys, + deepcopy(sim.inputs_init), + prob_new, + data, + init_level, + sys_reinit, + callbacks, + tstops, + aux, + ) + end + function f_grad(p, perts, data, aux) + callbacks, tstops = convert_perturbations_to_callbacks(sys, sim_inputs, perts) + dp = Enzyme.make_zero(p) + dx0 = Enzyme.make_zero(x0) + dsys = Enzyme.make_zero(sys) + sim_inputs = deepcopy(sim.inputs_init) + dsim_inputs = Enzyme.make_zero(sim_inputs) + dprob_new = Enzyme.make_zero(prob_new) + ddata = Enzyme.make_zero(data) + dcallbacks = Enzyme.make_zero(callbacks) + dtstops = Enzyme.make_zero(tstops) + Enzyme.autodiff( + Enzyme.Reverse, + f_enzyme, + Enzyme.Active, + Enzyme.Duplicated(p, dp), + Enzyme.Duplicated(x0, dx0), + Enzyme.Duplicated(sys, dsys), + Enzyme.Duplicated(sim_inputs, dsim_inputs), + Enzyme.Duplicated(prob_new, dprob_new), + Enzyme.Duplicated(data, ddata), + Enzyme.Const(init_level), + Enzyme.Const(sys_reinit), + Enzyme.Duplicated(callbacks, dcallbacks), + Enzyme.Duplicated(tstops, dtstops), + Enzyme.Const(aux), + ) + return dp + end + function f_forward_zygote(p, perts, data, aux) + f_Zygote( + p, + x0, + sys, + deepcopy(sim.inputs_init), + prob_new, + data, + init_level, + sys_reinit, + perts, + aux, + ) + end + f_forward, f_grad, f_forward_zygote + end +end + +#Inactive Rules; potential path to improving API +#Enzyme.EnzymeRules.inactive(::typeof(SimulationResults), args...) = nothing +#Enzyme.EnzymeRules.inactive(::typeof(get_activepower_branch_flow), args...) = nothing diff --git a/src/base/sensitivity_rule.jl b/src/base/sensitivity_rule.jl new file mode 100644 index 000000000..aa4c71efb --- /dev/null +++ b/src/base/sensitivity_rule.jl @@ -0,0 +1,107 @@ +function solve_up_with_callback( + prob::Union{DiffEqBase.AbstractDEProblem, DiffEqBase.NonlinearProblem}, sensealg, u0, p, + cb, + args...; kwargs...) + alg = DiffEqBase.extract_alg( + args, + kwargs, + DiffEqBase.has_kwargs(prob) ? prob.kwargs : kwargs, + ) + if isnothing(alg) || !(alg isa DiffEqBase.AbstractDEAlgorithm) # Default algorithm handling + _prob = DiffEqBase.get_concrete_problem(prob, !(prob isa DiscreteProblem); u0 = u0, + p = p, callback = cb, kwargs...) + solve_call(_prob, args...; callback = cb, kwargs...) + else + _prob = DiffEqBase.get_concrete_problem( + prob, + DiffEqBase.isadaptive(alg); + u0 = u0, + p = p, + callback = cb, + kwargs..., + ) + _alg = DiffEqBase.prepare_alg(alg, _prob.u0, _prob.p, _prob) + DiffEqBase.check_prob_alg_pairing(_prob, alg) # use alg for improved inference + if length(args) > 1 + DiffEqBase.solve_call(_prob, _alg, Base.tail(args)...; callback = cb, kwargs...) + else + DiffEqBase.solve_call(_prob, _alg; callback = cb, kwargs...) + end + end +end + +function solve_with_callback(prob::DiffEqBase.AbstractDEProblem, cb, args...; + sensealg = nothing, #add cb as second argument. + u0 = nothing, p = nothing, wrap = Val(true), kwargs...) + if sensealg === nothing && haskey(prob.kwargs, :sensealg) + sensealg = prob.kwargs[:sensealg] + end + + u0 = u0 !== nothing ? u0 : prob.u0 + p = p !== nothing ? p : prob.p + + if wrap isa Val{true} + DiffEqBase.wrap_sol( + solve_up_with_callback(prob, sensealg, u0, p, cb, args...; kwargs...), + ) + else + solve_up_with_callback(prob, sensealg, u0, p, cb, args...; kwargs...) + end +end + +function Enzyme.EnzymeRules.augmented_primal(config::Enzyme.EnzymeRules.ConfigWidth{1}, + func::Enzyme.Const{typeof(solve_up_with_callback)}, + ::Type{<:Union{Enzyme.Duplicated{RT}, Enzyme.DuplicatedNoNeed{RT}}}, prob, + sensealg::Union{ + Enzyme.Const{Nothing}, + Enzyme.Const{<:DiffEqBase.AbstractSensitivityAlgorithm}, + }, + u0, p, cb, args...; kwargs...) where {RT} + @inline function copy_or_reuse(val, idx) + if Enzyme.EnzymeRules.overwritten(config)[idx] && ismutable(val) + return deepcopy(val) + else + return val + end + end + + @inline function arg_copy(i) + copy_or_reuse(args[i].val, i + 5) + end + + res = DiffEqBase._solve_adjoint( + copy_or_reuse(prob.val, 2), copy_or_reuse(sensealg.val, 3), + copy_or_reuse(u0.val, 4), copy_or_reuse(p.val, 5), + SciMLBase.EnzymeOriginator(), ntuple(arg_copy, Val(length(args)))...; + callback = copy_or_reuse(cb.val, 6), + kwargs...) + + dres = Enzyme.make_zero(res[1])::RT + tup = (dres, res[2]) + return Enzyme.EnzymeRules.AugmentedReturn{RT, RT, Any}(res[1], dres, tup::Any) +end + +function Enzyme.EnzymeRules.reverse(config::Enzyme.EnzymeRules.ConfigWidth{1}, + func::Enzyme.Const{typeof(solve_up_with_callback)}, + ::Type{<:Union{Enzyme.Duplicated{RT}, Enzyme.DuplicatedNoNeed{RT}}}, tape, + prob, + sensealg::Union{ + Enzyme.Const{Nothing}, + Enzyme.Const{<:DiffEqBase.AbstractSensitivityAlgorithm}, + }, + u0, p, cb, args...; kwargs...) where {RT} + dres, clos = tape + dres = dres::RT + dargs = clos(dres) + for (darg, ptr) in zip(dargs, (func, prob, sensealg, u0, p, cb, args...)) + if ptr isa Enzyme.Const + continue + end + if darg == ChainRulesCore.NoTangent() + continue + end + ptr.dval .+= darg + end + Enzyme.make_zero!(dres.u) + return ntuple(_ -> nothing, Val(length(args) + 5)) +end diff --git a/src/base/simulation.jl b/src/base/simulation.jl index 193dc884d..3e40dbd20 100644 --- a/src/base/simulation.jl +++ b/src/base/simulation.jl @@ -1,15 +1,17 @@ mutable struct Simulation{T <: SimulationModel} - status::BUILD_STATUS + status::STATUS problem::Union{Nothing, SciMLBase.DEProblem} tspan::NTuple{2, Float64} sys::PSY.System perturbations::Vector{<:Perturbation} + initialize_level::INITIALIZE_LEVEL + x0::Vector{Float64} x0_init::Vector{Float64} - initialized::Bool tstops::Vector{Float64} callbacks::Vector simulation_folder::String inputs::Union{Nothing, SimulationInputs} + inputs_init::Union{Nothing, SimulationInputs} results::Union{Nothing, SimulationResults} console_level::Base.CoreLogging.LogLevel file_level::Base.CoreLogging.LogLevel @@ -19,7 +21,8 @@ end get_system(sim::Simulation) = sim.sys get_simulation_inputs(sim::Simulation) = sim.inputs -get_initial_conditions(sim::Simulation) = sim.x0_init +get_x0(sim::Simulation) = sim.x0 +get_x0_init(sim::Simulation) = sim.x0_init get_tspan(sim::Simulation) = sim.tspan function Simulation( @@ -35,20 +38,31 @@ function Simulation( frequency_reference, ) where {T <: SimulationModel} PSY.set_units_base_system!(sys, "DEVICE_BASE") - + if initialize_simulation && !isempty(initial_conditions) + @warn "initial_conditions were provided with initialize_simulation. User's initial_conditions will be overwritten." + init_level = POWERFLOW_AND_DEVICES + elseif initialize_simulation + init_level = POWERFLOW_AND_DEVICES + elseif !initialize_simulation && isempty(initial_conditions) + init_level = FLAT_START + else + init_level = INITIALIZED + end return Simulation{T}( BUILD_INCOMPLETE, nothing, tspan, sys, perturbations, + init_level, + initial_conditions, initial_conditions, - !initialize_simulation, Vector{Float64}(), Vector{SciMLBase.AbstractDiscreteCallback}(), simulation_folder, nothing, nothing, + nothing, console_level, file_level, false, @@ -194,9 +208,7 @@ end function reset!(sim::Simulation{T}) where {T <: SimulationModel} @info "Rebuilding the simulation after reset" - sim.inputs = SimulationInputs(T, get_system(sim), sim.frequency_reference) sim.status = BUILD_INCOMPLETE - sim.initialized = false build!(sim) @info "Simulation reset to status $(sim.status)" return @@ -219,7 +231,11 @@ end function _build_inputs!(sim::Simulation{T}) where {T <: SimulationModel} simulation_system = get_system(sim) - sim.inputs = SimulationInputs(T, simulation_system, sim.frequency_reference) + sim.inputs = SimulationInputs( + T, + simulation_system, + sim.frequency_reference, + ) @debug "Simulation Inputs Created" return end @@ -232,45 +248,30 @@ function _get_flat_start(inputs::SimulationInputs) return initial_conditions end -function _initialize_state_space(sim::Simulation{T}) where {T <: SimulationModel} - simulation_inputs = get_simulation_inputs(sim) - x0_init = sim.x0_init - if isempty(x0_init) && sim.initialized - @warn "Initial Conditions set to flat start" - sim.x0_init = _get_flat_start(simulation_inputs) - elseif isempty(x0_init) && !sim.initialized - sim.x0_init = _get_flat_start(simulation_inputs) - elseif !isempty(x0_init) && sim.initialized - if length(sim.x0_init) != get_variable_count(simulation_inputs) +function _pre_initialize_simulation!(sim::Simulation) + sys = get_system(sim) + inputs = get_simulation_inputs(sim) + @assert sim.status == BUILD_INCOMPLETE + if sim.initialize_level == POWERFLOW_AND_DEVICES + sim.x0 = _get_flat_start(inputs) + sim.status = _initialize_powerflow_and_devices!(sim.x0, inputs, sys) + elseif sim.initialize_level == DEVICES_ONLY + sim.x0 = deepcopy(sim.x0_init) + sim.status = _initialize_devices_only!(sim.x0, inputs) + elseif sim.initialize_level == FLAT_START + sim.x0 = _get_flat_start(inputs) + elseif sim.initialize_level == INITIALIZED + sim.x0 = deepcopy(sim.x0_init) + if length(sim.x0) != get_variable_count(inputs) throw( IS.ConflictingInputsError( "The size of the provided initial state space does not match the model's state space.", ), ) end - elseif !isempty(x0_init) && !sim.initialized - @warn "initial_conditions were provided with initialize_simulation. User's initial_conditions will be overwritten." - if length(sim.x0_init) != get_variable_count(simulation_inputs) - sim.x0_init = _get_flat_start(simulation_inputs) - end - else - @assert false - end - return -end - -function _pre_initialize_simulation!(sim::Simulation) - _initialize_state_space(sim) - if sim.initialized != true - @info("Pre-Initializing Simulation States") - sim.initialized = precalculate_initial_conditions!(sim.x0_init, sim) - if !sim.initialized - error( - "The simulation failed to find an adequate initial guess for the initialization. Check the intialization routine.", - ) - end - else - @warn("Using existing initial conditions value for simulation initialization") + @warn( + "Using existing initial conditions value for simulation initialization" + ) sim.status = SIMULATION_INITIALIZED end return @@ -278,20 +279,24 @@ end function _get_jacobian(sim::Simulation{ResidualModel}) inputs = get_simulation_inputs(sim) - x0_init = get_initial_conditions(sim) + x0_init = get_x0(sim) + parameters = get_parameters(inputs) return JacobianFunctionWrapper( ResidualModel(inputs, x0_init, JacobianCache), x0_init, + parameters, # sparse_retrieve_loop = 0, ) end function _get_jacobian(sim::Simulation{MassMatrixModel}) inputs = get_simulation_inputs(sim) - x0_init = get_initial_conditions(sim) + x0_init = get_x0(sim) + parameters = get_parameters(inputs) return JacobianFunctionWrapper( MassMatrixModel(inputs, x0_init, JacobianCache), x0_init, + parameters, ) end @@ -335,9 +340,11 @@ function _get_diffeq_problem( model::SystemModel{ResidualModel, NoDelays}, jacobian::JacobianFunctionWrapper, ) - x0 = get_initial_conditions(sim) + x0 = get_x0(sim) + sim.x0_init = deepcopy(x0) dx0 = zeros(length(x0)) simulation_inputs = get_simulation_inputs(sim) + p = get_parameters(simulation_inputs) sim.problem = SciMLBase.DAEProblem( SciMLBase.DAEFunction{true}( model; @@ -348,7 +355,7 @@ function _get_diffeq_problem( dx0, x0, get_tspan(sim), - simulation_inputs; + p; differential_vars = get_DAE_vector(simulation_inputs), ) sim.status = BUILT @@ -360,7 +367,10 @@ function _get_diffeq_problem( model::SystemModel{MassMatrixModel, NoDelays}, jacobian::JacobianFunctionWrapper, ) + x0 = get_x0(sim) + sim.x0_init = deepcopy(x0) simulation_inputs = get_simulation_inputs(sim) + p = get_parameters(simulation_inputs) sim.problem = SciMLBase.ODEProblem( SciMLBase.ODEFunction{true}( model; @@ -370,16 +380,16 @@ function _get_diffeq_problem( # Necessary to avoid unnecessary calculations in Rosenbrock methods tgrad = (dT, u, p, t) -> dT .= false, ), - sim.x0_init, + x0, get_tspan(sim), - simulation_inputs, + p; ) sim.status = BUILT return end function get_history_function(simulation_inputs::Simulation{MassMatrixModel}) - x0 = get_initial_conditions(simulation_inputs) + x0 = get_x0(simulation_inputs) h(p, t; idxs = nothing) = typeof(idxs) <: Number ? x0[idxs] : x0 return h end @@ -389,8 +399,11 @@ function _get_diffeq_problem( model::SystemModel{MassMatrixModel, HasDelays}, jacobian::JacobianFunctionWrapper, ) + x0 = get_x0(sim) + sim.x0_init = deepcopy(x0) simulation_inputs = get_simulation_inputs(sim) h = get_history_function(sim) + p = get_parameters(simulation_inputs) sim.problem = SciMLBase.DDEProblem( SciMLBase.DDEFunction{true}( model; @@ -398,17 +411,19 @@ function _get_diffeq_problem( jac = jacobian, jac_prototype = jacobian.Jv, ), - sim.x0_init, + x0, h, get_tspan(sim), - simulation_inputs; - constant_lags = filter!(x -> x != 0, unique(simulation_inputs.delays)), + p; + constant_lags = filter(x -> x != 0, get_constant_lags(simulation_inputs)), ) sim.status = BUILT return end +#Timers on this level are fine; but not for initializing individual types of components. +#Seems like a reasonable tradeoff for reduced complexity; allows getting rid of "enable_sensitivity" function _build!(sim::Simulation{T}; kwargs...) where {T <: SimulationModel} check_kwargs(kwargs, SIMULATION_ACCEPTED_KWARGS, "Simulation") # Branches are a super set of Lines. Passing both kwargs will @@ -448,10 +463,17 @@ function _build!(sim::Simulation{T}; kwargs...) where {T <: SimulationModel} jacobian = _get_jacobian(sim) end TimerOutputs.@timeit BUILD_TIMER "Make Model Function" begin - model = T(simulation_inputs, get_initial_conditions(sim), SimCache) + model = T(simulation_inputs, get_x0(sim), SimCache) end TimerOutputs.@timeit BUILD_TIMER "Initial Condition NLsolve refinement" begin - refine_initial_condition!(sim, model, jacobian) + if (sim.initialize_level == POWERFLOW_AND_DEVICES) || + (sim.initialize_level == DEVICES_ONLY) + refine_initial_condition!( + sim, + model, + jacobian, + ) + end end TimerOutputs.@timeit BUILD_TIMER "Build Perturbations" begin _build_perturbations!(sim) @@ -459,6 +481,7 @@ function _build!(sim::Simulation{T}; kwargs...) where {T <: SimulationModel} TimerOutputs.@timeit BUILD_TIMER "Make DiffEq Problem" begin _get_diffeq_problem(sim, model, jacobian) end + sim.inputs_init = deepcopy(sim.inputs) @info "Simulations status = $(sim.status)" else @error "The simulation couldn't be initialized correctly. Simulations status = $(sim.status)" @@ -514,7 +537,7 @@ function _prog_meter_enabled() end function _filter_kwargs(kwargs) - return Dict(k => v for (k, v) in kwargs if in(k, DIFFEQ_SOLVE_KWARGS)) + return filter(x -> in(x[1], DIFFEQ_SOLVE_KWARGS), kwargs) end function _execute!(sim::Simulation, solver; kwargs...) @@ -528,7 +551,7 @@ function _execute!(sim::Simulation, solver; kwargs...) else callbacks = SciMLBase.CallbackSet((), tuple(sim.callbacks...)) end - + progress_enable = _prog_meter_enabled() solution, time_log[:timed_solve_time], time_log[:solve_bytes_alloc], @@ -537,7 +560,7 @@ function _execute!(sim::Simulation, solver; kwargs...) solver; callback = callbacks, tstops = !isempty(sim.tstops) ? [sim.tstops[1] ÷ 2, sim.tstops...] : [], - progress = get(kwargs, :enable_progress_bar, _prog_meter_enabled()), + progress = get(kwargs, :enable_progress_bar, progress_enable), progress_steps = 1, advance_to_tstop = !isempty(sim.tstops), initializealg = SciMLBase.NoInit(), @@ -552,7 +575,9 @@ function _execute!(sim::Simulation, solver; kwargs...) if SciMLBase.successful_retcode(solution) sim.status = SIMULATION_FINALIZED else - @error("The simulation failed with return code $(solution.retcode)") + @error( + "The simulation failed with return code $(solution.retcode)" + ) sim.status = SIMULATION_FAILED end end @@ -578,7 +603,8 @@ function execute!(sim::Simulation, solver; kwargs...) try _execute!(sim, solver; kwargs...) catch e - @error "Execution failed" exception = (e, catch_backtrace()) + @error "Execution failed" exception = + (e, catch_backtrace()) sim.status = SIMULATION_FAILED end end diff --git a/src/base/simulation_initialization.jl b/src/base/simulation_initialization.jl index a37786cd8..02e3abf06 100644 --- a/src/base/simulation_initialization.jl +++ b/src/base/simulation_initialization.jl @@ -1,11 +1,3 @@ -function get_flat_start(inputs::SimulationInputs) - bus_count = get_bus_count(inputs) - var_count = get_variable_count(inputs) - initial_conditions = zeros(var_count) - initial_conditions[1:bus_count] .= 1.0 - return initial_conditions -end - function power_flow_solution!( initial_guess::Vector{Float64}, sys::PSY.System, @@ -31,69 +23,36 @@ end function initialize_static_injection!(inputs::SimulationInputs) @debug "Updating Source internal voltage magnitude and angle" static_injection_devices = get_static_injectors(inputs) + parameters = get_parameters(inputs) if !isempty(static_injection_devices) - try - for s in static_injection_devices - initialize_static_device!(s) - end - catch e - bt = catch_backtrace() - @error "Static Injection Failed to Initialize" exception = e, bt - return BUILD_FAILED + for s in static_injection_devices + local_parameters = @view parameters[_get_wrapper_name(s)] + initialize_static_device!(s, local_parameters) end end return BUILD_INCOMPLETE end -function _initialization_debug(dynamic_device, static, x0_device::Vector{Float64}) - residual = similar(x0_device) - Vm = PSY.get_magnitude(PSY.get_bus(static)) - θ = PSY.get_angle(PSY.get_bus(static)) - device!( - x0_device, - residual, - Vm * cos(θ), - Vm * sin(θ), - zeros(10), - zeros(10), - [1.0], - zeros(100), - dynamic_device, - 0, - ) - for (ix, state) in enumerate(PSY.get_states(dynamic_device)) - @debug state residual[ix] - end - return -end - function initialize_dynamic_injection!( - initial_guess::Vector{Float64}, + initial_guess::AbstractArray, inputs::SimulationInputs, - system::PSY.System, ) @debug "Updating Dynamic Injection Component Initial Guess" initial_inner_vars = zeros(get_inner_vars_count(inputs)) - try - for dynamic_device in get_dynamic_injectors(inputs) - static = PSY.get_component( - dynamic_device.static_type, - system, - PSY.get_name(dynamic_device), - ) - @debug "Initializing $(PSY.get_name(dynamic_device)) - $(typeof(dynamic_device.device))" - n_states = PSY.get_n_states(dynamic_device) - _inner_vars = @view initial_inner_vars[get_inner_vars_index(dynamic_device)] - x0_device = initialize_dynamic_device!(dynamic_device, static, _inner_vars) - @assert length(x0_device) == n_states - ix_range = get_ix_range(dynamic_device) - initial_guess[ix_range] = x0_device - @debug _initialization_debug(dynamic_device, static, x0_device) - end - catch e - bt = catch_backtrace() - @error "Dynamic Injection Failed to Initialize" exception = e, bt - return BUILD_FAILED + parameters = get_parameters(inputs) + for dynamic_device in get_dynamic_injectors(inputs) + static = get_static_device(dynamic_device) + @debug "Initializing $(PSY.get_name(dynamic_device)) - $(typeof(dynamic_device.device))" + _inner_vars = @view initial_inner_vars[get_inner_vars_index(dynamic_device)] + _parameters = @view parameters[_get_wrapper_name(dynamic_device)] + _states = @view initial_guess[get_ix_range(dynamic_device)] + initialize_dynamic_device!( + dynamic_device, + static, + _inner_vars, + _parameters, + _states, + ) end return BUILD_INCOMPLETE end @@ -102,20 +61,14 @@ function initialize_dynamic_branches!( initial_guess::Vector{Float64}, inputs::SimulationInputs, ) - try - @debug "Initializing Dynamic Branches" - for br in get_dynamic_branches(inputs) - @debug "$(PSY.get_name(br)) - $(typeof(br))" - n_states = PSY.get_n_states(br) - x0_branch = initialize_dynamic_device!(br) - IS.@assert_op length(x0_branch) == n_states - ix_range = get_ix_range(br) - initial_guess[ix_range] = x0_branch - end - catch e - bt = catch_backtrace() - @error "Dynamic Branches Failed to Initialize" exception = e, bt - return BUILD_FAILED + parameters = get_parameters(inputs) + @debug "Initializing Dynamic Branches" + for br in get_dynamic_branches(inputs) + @debug "$(PSY.get_name(br)) - $(typeof(br))" + wrapper_name = _get_wrapper_name(br) + _parameters = @view parameters[wrapper_name] + _states = @view initial_guess[get_ix_range(br)] + initialize_dynamic_device!(br, _parameters, _states) end return BUILD_INCOMPLETE end @@ -145,44 +98,75 @@ function check_valid_values(initial_guess::Vector{Float64}, inputs::SimulationIn end if !isempty(invalid_initial_guess) - @error("Invalid initial condition values $invalid_initial_guess") + @error( + "Invalid initial condition values $invalid_initial_guess" + ) return BUILD_FAILED end return BUILD_IN_PROGRESS end +#Setter functions for CA +function set_V_ref!(array, value) + @view(array["refs"])["V_ref"] = value +end +function set_ω_ref!(array, value) + @view(array["refs"])["ω_ref"] = value +end +function set_θ_ref!(array, value) + @view(array["refs"])["θ_ref"] = value +end +function set_P_ref!(array, value) + @view(array["refs"])["P_ref"] = value +end +function set_Q_ref!(array, value) + @view(array["refs"])["Q_ref"] = value +end + # Default implementation for both models. This implementation is to future proof if there is # a divergence between the required build methods -function _calculate_initial_guess!(x0_init::Vector{Float64}, sim::Simulation) - inputs = get_simulation_inputs(sim) - @assert sim.status == BUILD_INCOMPLETE - while sim.status == BUILD_INCOMPLETE +function _initialize_powerflow_and_devices!( + x0::AbstractArray, + inputs::SimulationInputs, + sys::PSY.System, +) + #x0 = _get_flat_start(inputs) + @info("Pre-Initializing Simulation States") + status = BUILD_INCOMPLETE + while status == BUILD_INCOMPLETE @debug "Start state intialization routine" - TimerOutputs.@timeit BUILD_TIMER "Power Flow solution" begin - sim.status = power_flow_solution!(sim.x0_init, get_system(sim), inputs) - end - TimerOutputs.@timeit BUILD_TIMER "Initialize Static Injectors" begin - sim.status = initialize_static_injection!(inputs) - end - TimerOutputs.@timeit BUILD_TIMER "Initialize Dynamic Injectors" begin - sim.status = initialize_dynamic_injection!(sim.x0_init, inputs, get_system(sim)) - end + status = power_flow_solution!(x0, sys, inputs) + status = initialize_static_injection!(inputs) + status = initialize_dynamic_injection!(x0, inputs) if has_dyn_lines(inputs) - TimerOutputs.@timeit BUILD_TIMER "Initialize Dynamic Branches" begin - sim.status = initialize_dynamic_branches!(sim.x0_init, inputs) - end + status = initialize_dynamic_branches!(x0, inputs) else @debug "No Dynamic Branches in the system" end - sim.status = check_valid_values(sim.x0_init, inputs) + status = check_valid_values(x0, inputs) end - return + return status end -function precalculate_initial_conditions!(x0_init::Vector{Float64}, sim::Simulation) - _calculate_initial_guess!(x0_init, sim) - return sim.status != BUILD_FAILED +function _initialize_devices_only!( + x0::AbstractArray, + inputs::SimulationInputs, +) + @info("Pre-Initializing Simulation States") + status = BUILD_INCOMPLETE + while status == BUILD_INCOMPLETE + @debug "Start state intialization routine" + status = initialize_static_injection!(inputs) + status = initialize_dynamic_injection!(x0, inputs) + if has_dyn_lines(inputs) + status = initialize_dynamic_branches!(x0, inputs) + else + @debug "No Dynamic Branches in the system" + end + status = check_valid_values(x0, inputs) + end + return status end """ @@ -199,15 +183,15 @@ function read_initial_conditions(sim::Simulation) for bus in PSY.get_components(PSY.Bus, system) bus_n = PSY.get_number(bus) bus_ix = get_lookup(simulation_inputs)[bus_n] - V_R[bus_n] = get_initial_conditions(sim)[bus_ix] - V_I[bus_n] = get_initial_conditions(sim)[bus_ix + bus_size] + V_R[bus_n] = get_x0(sim)[bus_ix] + V_I[bus_n] = get_x0(sim)[bus_ix + bus_size] Vm[bus_n] = sqrt( - get_initial_conditions(sim)[bus_ix]^2 + - get_initial_conditions(sim)[bus_ix + bus_size]^2, + get_x0(sim)[bus_ix]^2 + + get_x0(sim)[bus_ix + bus_size]^2, ) θ[bus_n] = atan( - get_initial_conditions(sim)[bus_ix + bus_size], - get_initial_conditions(sim)[bus_ix], + get_x0(sim)[bus_ix + bus_size], + get_x0(sim)[bus_ix], ) end results = Dict{String, Any}("V_R" => V_R, "V_I" => V_I, "Vm" => Vm, "θ" => θ) @@ -217,7 +201,7 @@ function read_initial_conditions(sim::Simulation) global_index = get_global_index(device) x0_device = Dict{Symbol, Float64}() for s in states - x0_device[s] = get_initial_conditions(sim)[global_index[s]] + x0_device[s] = get_x0(sim)[global_index[s]] end results[name] = x0_device end @@ -229,7 +213,7 @@ function read_initial_conditions(sim::Simulation) global_index = get_global_index(br) x0_br = Dict{Symbol, Float64}() for s in states - x0_br[s] = get_initial_conditions(sim)[global_index[s]] + x0_br[s] = get_x0(sim)[global_index[s]] end printed_name = "Line " * name results[printed_name] = x0_br @@ -247,7 +231,7 @@ function set_operating_point!( while status == BUILD_INCOMPLETE status = power_flow_solution!(x0_init, system, inputs) status = initialize_static_injection!(inputs) - status = initialize_dynamic_injection!(x0_init, inputs, system) + status = initialize_dynamic_injection!(x0_init, inputs) status = initialize_dynamic_branches!(x0_init, inputs) status = SIMULATION_INITIALIZED end diff --git a/src/base/simulation_inputs.jl b/src/base/simulation_inputs.jl index 29a3afff1..9e0caaf4d 100644 --- a/src/base/simulation_inputs.jl +++ b/src/base/simulation_inputs.jl @@ -1,4 +1,4 @@ -struct SimulationInputs +mutable struct SimulationInputs dynamic_injectors::Vector{DynamicWrapper{<:PSY.DynamicInjection}} static_injectors::Vector static_loads::Vector @@ -9,113 +9,123 @@ struct SimulationInputs inner_vars_count::Int bus_count::Int ode_range::UnitRange{Int} - ybus_rectangular::SparseArrays.SparseMatrixCSC{Float64, Int} dyn_lines::Bool - total_shunts::SparseArrays.SparseMatrixCSC{Float64, Int} lookup::Dict{Int, Int} - DAE_vector::Vector{Bool} - mass_matrix::LinearAlgebra.Diagonal{Float64} global_vars_update_pointers::Dict{Int, Int} global_state_map::MAPPING_DICT global_inner_var_map::Dict{String, Dict} - delays::Vector - - function SimulationInputs( - sys::PSY.System, - ::T, - ) where {T <: Union{ConstantFrequency, ReferenceBus}} - n_buses = get_n_buses(sys) - Ybus, lookup = _get_ybus(sys) - - TimerOutputs.@timeit BUILD_TIMER "Wrap Branches" begin - wrapped_branches = _wrap_dynamic_branches(sys, lookup) - has_dyn_lines = !isempty(wrapped_branches) - aux_states = 0 - for br in wrapped_branches - aux_states += PSY.get_n_states(br) - end - branch_state_counts = aux_states - injection_start = 2 * n_buses + branch_state_counts + 1 - end - - TimerOutputs.@timeit BUILD_TIMER "Wrap Dynamic Injectors" begin - wrapped_injectors = _wrap_dynamic_injector_data(sys, lookup, injection_start) - delays = get_system_delays(sys) - var_count = wrapped_injectors[end].ix_range[end] - end - - TimerOutputs.@timeit BUILD_TIMER "Calculate MM, DAE_vector, Total Shunts" begin - mass_matrix = _make_mass_matrix(wrapped_injectors, var_count, n_buses) - DAE_vector = _make_DAE_vector(mass_matrix, var_count, n_buses) - total_shunts = _make_total_shunts(wrapped_branches, n_buses) - end - - TimerOutputs.@timeit BUILD_TIMER "Wrap Static Injectors" begin - wrapped_loads = _wrap_loads(sys, lookup) - wrapped_static_injectors = _wrap_static_injectors(sys, lookup) - end - - _adjust_states!( - DAE_vector, - mass_matrix, - total_shunts, - n_buses, - PSY.get_frequency(sys), - ) + ybus_rectangular::SparseArrays.SparseMatrixCSC{Float64, Int} + total_shunts::SparseArrays.SparseMatrixCSC{Float64, Int} + DAE_vector::Vector{Bool} + mass_matrix::LinearAlgebra.Diagonal{Float64} + parameters::ComponentArrays.ComponentVector{Float64} + parameters_metadata::ComponentArrays.ComponentVector{ParamsMetadata} + constant_lags::Vector +end - global_vars = - _make_global_variable_index(wrapped_injectors, wrapped_static_injectors, T) +function SimulationInputs( + sys::PSY.System, + ::T, +) where {T <: Union{ConstantFrequency, ReferenceBus}} + n_buses = get_n_buses(sys) + _, lookup = _get_ybus(sys) + state_count = 2 * n_buses + 1 + TimerOutputs.@timeit BUILD_TIMER "Wrap Branches" begin + wrapped_branches, state_count = + _wrap_dynamic_branches(sys, lookup, state_count) + has_dyn_lines = !isempty(wrapped_branches) + end + n_branch_states = state_count - (2 * n_buses + 1) + injection_start = state_count - inner_vars_count = 0 - for i in length(wrapped_injectors):-1:1 - if length(wrapped_injectors[i].inner_vars_index) > 0 - inner_vars_count = wrapped_injectors[i].inner_vars_index[end] - break - end - end + TimerOutputs.@timeit BUILD_TIMER "Wrap Dynamic Injectors" begin + wrapped_injectors, state_count = + _wrap_dynamic_injector_data(sys, lookup, state_count) + n_vars = state_count - 1 + end - if !isempty(delays) - @info "System has delays. Use the correct solver for delay differential equations." + TimerOutputs.@timeit BUILD_TIMER "Wrap Static Injectors" begin + wrapped_loads = _wrap_loads(sys, lookup) + wrapped_static_injectors = + _wrap_static_injectors(sys, lookup) + end + global_vars = + _make_global_variable_index(wrapped_injectors, wrapped_static_injectors, T) + + inner_vars_count = 0 + for i in length(wrapped_injectors):-1:1 + if length(wrapped_injectors[i].inner_vars_index) > 0 + inner_vars_count = wrapped_injectors[i].inner_vars_index[end] + break end + end + Ybus, _ = _get_ybus(sys) + total_shunts = _make_total_shunts(wrapped_branches, n_buses) + TimerOutputs.@timeit BUILD_TIMER "Build initial parameters" begin + initial_parameters = ComponentArrays.ComponentVector{Float64}() + initial_parameters = _add_parameters(initial_parameters, wrapped_branches) + initial_parameters = _add_parameters(initial_parameters, wrapped_injectors) + initial_parameters = _add_parameters(initial_parameters, wrapped_loads) + initial_parameters = _add_parameters(initial_parameters, wrapped_static_injectors) + #initial_parameters = _add_bus_parameters(initial_parameters, PSY.get_components(PSY.ACBus, sys)) #For extending sensitivity API to Power Flow + parameter_metadata = ComponentArrays.ComponentVector{ParamsMetadata}() + parameter_metadata = _add_parameters_metadata(parameter_metadata, wrapped_branches) + parameter_metadata = _add_parameters_metadata(parameter_metadata, wrapped_injectors) + parameter_metadata = _add_parameters_metadata(parameter_metadata, wrapped_loads) + parameter_metadata = + _add_parameters_metadata(parameter_metadata, wrapped_static_injectors) + end + #@assert length(initial_parameters) == length(parameter_metadata) Can be different with parameters that are vectors (e.g. NN) + mass_matrix = _make_mass_matrix(wrapped_injectors, n_vars, n_buses) + DAE_vector = _make_DAE_vector(mass_matrix, n_vars, n_buses) + _adjust_states!( + DAE_vector, + mass_matrix, + total_shunts, + n_buses, + PSY.get_frequency(sys), + ) - new( - wrapped_injectors, - wrapped_static_injectors, - wrapped_loads, - wrapped_branches, - var_count - 2 * n_buses - branch_state_counts, - branch_state_counts, - var_count, - inner_vars_count, - n_buses, - injection_start:var_count, - Ybus, - has_dyn_lines, - total_shunts, - lookup, - DAE_vector, - mass_matrix, - global_vars, - MAPPING_DICT(), - Dict{String, Dict}(), - delays, - ) + delays = get_constant_lags(sys) + if !isempty(delays) + @info "System has delays. Use the correct solver for delay differential equations." end -end + return SimulationInputs( + wrapped_injectors, + wrapped_static_injectors, + wrapped_loads, + wrapped_branches, + n_vars - 2 * n_buses - n_branch_states, + n_branch_states, + n_vars, + inner_vars_count, + n_buses, + injection_start:n_vars, + has_dyn_lines, + lookup, + global_vars, + MAPPING_DICT(), + Dict{String, Dict}(), + Ybus, + total_shunts, + DAE_vector, + mass_matrix, + initial_parameters, + parameter_metadata, + delays, + ) +end +#LEVEL ONE INPUTS get_dynamic_injectors(inputs::SimulationInputs) = inputs.dynamic_injectors get_dynamic_branches(inputs::SimulationInputs) = inputs.dynamic_branches get_static_injectors(inputs::SimulationInputs) = inputs.static_injectors get_static_loads(inputs::SimulationInputs) = inputs.static_loads -get_ybus(inputs::SimulationInputs) = inputs.ybus_rectangular -get_total_shunts(inputs::SimulationInputs) = inputs.total_shunts get_lookup(inputs::SimulationInputs) = inputs.lookup -get_DAE_vector(inputs::SimulationInputs) = inputs.DAE_vector -get_mass_matrix(inputs::SimulationInputs) = inputs.mass_matrix has_dyn_lines(inputs::SimulationInputs) = inputs.dyn_lines get_global_vars_update_pointers(inputs::SimulationInputs) = inputs.global_vars_update_pointers - +get_global_state_map(inputs::SimulationInputs) = inputs.global_state_map get_injection_n_states(inputs::SimulationInputs) = inputs.injection_n_states get_branches_n_states(inputs::SimulationInputs) = inputs.branches_n_states get_variable_count(inputs::SimulationInputs) = inputs.variable_count @@ -124,6 +134,16 @@ get_ode_ouput_range(inputs::SimulationInputs) = inputs.ode_range get_bus_count(inputs::SimulationInputs) = inputs.bus_count get_bus_range(inputs::SimulationInputs) = 1:(2 * inputs.bus_count) +#LEVEL TWO INPUTS +get_ybus(inputs::SimulationInputs) = inputs.ybus_rectangular +get_total_shunts(inputs::SimulationInputs) = inputs.total_shunts + +#LEVEL THREE INPUTS +get_DAE_vector(inputs::SimulationInputs) = inputs.DAE_vector +get_mass_matrix(inputs::SimulationInputs) = inputs.mass_matrix +get_parameters(inputs::SimulationInputs) = inputs.parameters +get_constant_lags(inputs::SimulationInputs) = inputs.constant_lags + # Utility function not to be used for performance sensitive operations function get_voltage_buses_ix(inputs::SimulationInputs) n_buses = get_bus_count(inputs::SimulationInputs) @@ -158,7 +178,175 @@ function SimulationInputs( return SimulationInputs(sys, frequency_reference) end -function _wrap_dynamic_injector_data(sys::PSY.System, lookup, injection_start::Int) +function _get_wrapper_name(wrapped_device::Union{DynamicWrapper, StaticWrapper}) + Symbol(PSY.get_name(get_device(wrapped_device))) +end +function _get_wrapper_name(wrapped_device::StaticLoadWrapper) + Symbol(PSY.get_name(PSY.get_bus(wrapped_device))) +end +function _get_wrapper_name(wrapped_device::BranchWrapper) + Symbol(PSY.get_name(get_branch(wrapped_device))) +end + +# For Extending Sensitivity API to Power Flow +#= function _add_bus_parameters(initial_parameters, buses) + for bus in buses + name = Symbol(PSY.get_name(bus)) + bus_type = PSY.get_bustype(bus) + if bus_type === PSY.ACBusTypes.REF + refs = ( + V_ref = PSY.get_magnitude(bus), + θ_ref = PSY.get_angle(bus), + P_ref = 0.0, + Q_ref = 0.0, + ) + else #TODO- set appropriate references for PV and PQ buses. + refs = ( + V_ref = 0.0, + θ_ref = 0.0, + P_ref = 0.0, + Q_ref = 0.0, + ) + end + initial_parameters = ComponentArrays.ComponentVector( + initial_parameters; + name => ( + refs = refs, + ), + ) + end + return initial_parameters +end =# + +function _get_refs_metadata(x) + return (;) +end + +function _get_refs_metadata(wrapped_device::DynamicWrapper) + return ( + V_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + ω_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + P_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + Q_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + ) +end + +function _get_refs_metadata(wrapped_device::StaticLoadWrapper) + return ( + V_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + θ_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + P_power = ParamsMetadata(DEVICE_SETPOINT, false, true), + P_current = ParamsMetadata(DEVICE_SETPOINT, false, true), + P_impedance = ParamsMetadata(DEVICE_SETPOINT, false, true), + Q_power = ParamsMetadata(DEVICE_SETPOINT, false, true), + Q_current = ParamsMetadata(DEVICE_SETPOINT, false, true), + Q_impedance = ParamsMetadata(DEVICE_SETPOINT, false, true), + ) +end + +function _get_refs_metadata(wrapped_device::StaticWrapper) + return ( + V_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + θ_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + P_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + Q_ref = ParamsMetadata(DEVICE_SETPOINT, false, true), + ) +end + +function _add_parameters_metadata(parameter_metadata, wrapped_devices) + for wrapped_device in wrapped_devices + p_metadata = get_params_metadata(wrapped_device) + name = _get_wrapper_name(wrapped_device) + refs_metadata = _get_refs_metadata(wrapped_device) + parameter_metadata = ComponentArrays.ComponentVector( + parameter_metadata; + name => ( + params = p_metadata, + refs = refs_metadata, + ), + ) + end + return parameter_metadata +end + +function _get_refs(x) + return (;) +end + +function _get_refs(wrapped_device::DynamicWrapper) + static_device = get_static_device(wrapped_device) + if isa(static_device, PSY.StandardLoad) + reactive_power = PF.get_total_q(static_device) + else + reactive_power = PSY.get_reactive_power(static_device) + end + refs = ( + V_ref = PSY.get_V_ref(get_device(wrapped_device)), + ω_ref = PSY.get_ω_ref(get_device(wrapped_device)), + P_ref = PSY.get_P_ref(get_device(wrapped_device)), + Q_ref = reactive_power, + ) + return refs +end + +function _get_refs(wrapped_device::StaticLoadWrapper) + refs = ( + V_ref = get_V_ref(wrapped_device), + θ_ref = get_θ_ref(wrapped_device), + P_power = get_P_power(wrapped_device), + P_current = get_P_current(wrapped_device), + P_impedance = get_P_impedance(wrapped_device), + Q_power = get_Q_power(wrapped_device), + Q_current = get_Q_current(wrapped_device), + Q_impedance = get_Q_impedance(wrapped_device), + ) + return refs +end + +function _get_refs(wrapped_device::StaticWrapper) + device = get_device(wrapped_device) + bus = PSY.get_bus(device) + refs = ( + V_ref = PSY.get_magnitude(bus), + θ_ref = PSY.get_angle(bus), + P_ref = PSY.get_active_power(device), + Q_ref = PSY.get_reactive_power(device), + ) + return refs +end + +function _get_refs(wrapped_device::StaticWrapper{PSY.Source}) + device = get_device(wrapped_device) + refs = ( + V_ref = PSY.get_internal_voltage(device), + θ_ref = PSY.get_internal_angle(device), + P_ref = PSY.get_active_power(device), + Q_ref = PSY.get_reactive_power(device), + ) + return refs +end + +function _add_parameters(initial_parameters, wrapped_devices) + for wrapped_device in wrapped_devices + p = get_params(wrapped_device) + name = _get_wrapper_name(wrapped_device) + refs = _get_refs(wrapped_device) + initial_parameters = ComponentArrays.ComponentVector( + initial_parameters; + name => ( + params = p, + refs = refs, + ), + ) + end + return initial_parameters +end + +function _wrap_dynamic_injector_data( + sys::PSY.System, + lookup, + state_count::Int, +) #injection_start injector_data = get_injectors_with_dynamics(sys) isempty(injector_data) && error("System doesn't contain any DynamicInjection devices") # TODO: Needs a better container that isn't parametrized on an abstract type @@ -172,15 +360,16 @@ function _wrap_dynamic_injector_data(sys::PSY.System, lookup, injection_start::I @debug "Wrapping $(PSY.get_name(device))" dynamic_device = PSY.get_dynamic_injector(device) n_states = PSY.get_n_states(dynamic_device) - ix_range = range(injection_start; length = n_states) + n_inner_vars = get_inner_vars_count(dynamic_device) + ix_range = range(state_count; length = n_states) ode_range = range(injection_count; length = n_states) bus_n = PSY.get_number(PSY.get_bus(device)) bus_ix = lookup[bus_n] - inner_vars_range = - range(inner_vars_count; length = get_inner_vars_count(dynamic_device)) + inner_vars_range = range(inner_vars_count; length = n_inner_vars) @debug "ix_range=$ix_range ode_range=$ode_range inner_vars_range= $inner_vars_range" dynamic_device = PSY.get_dynamic_injector(device) @assert dynamic_device !== nothing + #TODO - add check if name of device is unique? wrapped_injector[ix] = DynamicWrapper( device, dynamic_device, @@ -192,14 +381,13 @@ function _wrap_dynamic_injector_data(sys::PSY.System, lookup, injection_start::I sys_base_freq, ) injection_count += n_states - injection_start += n_states - inner_vars_count = - length(inner_vars_range) > 0 ? (inner_vars_range[end] + 1) : inner_vars_count + state_count += n_states + inner_vars_count += n_inner_vars end - return wrapped_injector + return wrapped_injector, state_count end -function get_system_delays(sys::PSY.System) +function get_constant_lags(sys::PSY.System) delays = [] for injector in get_injectors_with_dynamics(sys) device_delays = get_delays(PSY.get_dynamic_injector(injector)) @@ -207,11 +395,14 @@ function get_system_delays(sys::PSY.System) delays = vcat(delays, device_delays) end end - return delays + return unique(delays) end -function _wrap_dynamic_branches(sys::PSY.System, lookup::Dict{Int, Int}) - branches_start = 2 * get_n_buses(sys) + 1 +function _wrap_dynamic_branches( + sys::PSY.System, + lookup::Dict{Int, Int}, + state_count::Int, +) sys_base_power = PSY.get_base_power(sys) sys_base_freq = PSY.get_frequency(sys) dynamic_branches = get_dynamic_branches(sys) @@ -226,7 +417,7 @@ function _wrap_dynamic_branches(sys::PSY.System, lookup::Dict{Int, Int}) to_bus_number = PSY.get_number(arc.to) bus_ix_from = lookup[from_bus_number] bus_ix_to = lookup[to_bus_number] - ix_range = range(branches_start; length = n_states) + ix_range = range(state_count; length = n_states) ode_range = range(branches_count; length = n_states) @debug "ix_range=$ix_range ode_range=$ode_range" wrapped_branches[ix] = BranchWrapper( @@ -239,15 +430,18 @@ function _wrap_dynamic_branches(sys::PSY.System, lookup::Dict{Int, Int}) sys_base_freq, ) branches_count += n_states - branches_start += n_states + state_count += n_states end else @debug("System doesn't contain Dynamic Branches") end - return wrapped_branches + return wrapped_branches, state_count end -function _wrap_static_injectors(sys::PSY.System, lookup::Dict{Int, Int}) +function _wrap_static_injectors( + sys::PSY.System, + lookup::Dict{Int, Int}, +) static_injection_data = get_injection_without_dynamics(sys) container = Vector{StaticWrapper}(undef, length(static_injection_data)) for (ix, ld) in enumerate(static_injection_data) @@ -423,12 +617,13 @@ end function get_setpoints(inputs::SimulationInputs) dic = Dict{String, Dict{String, Float64}}() + p = inputs.parameters for w in get_dynamic_injectors(inputs) + wrapped_device_name = _get_wrapper_name(w) dic_w = Dict{String, Float64}() - dic_w["P_ref"] = get_P_ref(w) - dic_w["Q_ref"] = get_Q_ref(w) - dic_w["ω_ref"] = get_ω_ref(w) - dic_w["V_ref"] = get_V_ref(w) + for ref_name in ComponentArrays.labels( p[wrapped_device_name][:refs]) + dic_w[ref_name] = p[wrapped_device_name][:refs][ref_name] + end dic[PSY.get_name(w)] = dic_w end return dic diff --git a/src/base/simulation_results.jl b/src/base/simulation_results.jl index 38bf4b5f9..0d036a0b0 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 @@ -61,7 +65,7 @@ containing the name of the Dynamic Device and the symbol of the state. function post_proc_state_series( res::SimulationResults, ref::Tuple{String, Symbol}, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 +73,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 """ @@ -79,7 +83,7 @@ of the Dynamic Device. function post_proc_voltage_current_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 +95,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 @@ -110,11 +116,11 @@ function post_proc_voltage_series( solution, bus_ix::Int, n_buses::Int, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -126,9 +132,9 @@ string name of the Dynamic Injection device. function post_proc_real_current_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 """ @@ -139,9 +145,9 @@ string name of the Dynamic Injection device. function post_proc_imaginary_current_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -153,9 +159,10 @@ string name of the Dynamic Injection device. function post_proc_activepower_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -167,9 +174,10 @@ string name of the Dynamic Injection device. function post_proc_reactivepower_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -181,7 +189,7 @@ string name of the Dynamic Injection device. function post_proc_field_current_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) system = get_system(res) bus_lookup = get_bus_lookup(res) @@ -189,9 +197,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 @@ -203,12 +212,12 @@ string name of the Dynamic Injection device. function post_proc_field_voltage_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -220,12 +229,12 @@ name of the Dynamic Injection device. function post_proc_pss_output_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -237,12 +246,12 @@ string name of the Dynamic Injection device. function post_proc_mechanical_torque_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -253,7 +262,7 @@ The current is computed through the `from` bus into the `to` bus. function post_proc_branch_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) system = get_system(res) bus_lookup = get_bus_lookup(res) @@ -267,8 +276,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) @@ -281,7 +292,7 @@ Function to compute the frequency of a Dynamic Injection component. function post_proc_frequency_series( res::SimulationResults, name::String, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) system = get_system(res) device = PSY.get_component(PSY.StaticInjection, system, name) @@ -289,7 +300,7 @@ 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 """ @@ -307,8 +318,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 +340,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 +366,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 +392,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 +414,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 +436,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 +458,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 +480,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 +502,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 +524,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 +546,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 +567,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 @@ -519,9 +592,9 @@ Function to obtain the imaginary current flowing through the series element of a function get_imaginary_current_branch_flow( res::SimulationResults, name::String; - dt = nothing, + 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 @@ -548,9 +621,10 @@ function get_activepower_branch_flow( res::SimulationResults, name::String, location::Symbol; - dt = nothing, + 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 @@ -584,9 +658,10 @@ function get_reactivepower_branch_flow( res::SimulationResults, name::String, location::Symbol; - dt = nothing, + 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 +685,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/base/small_signal.jl b/src/base/small_signal.jl index 441c15e5b..3412db1f2 100644 --- a/src/base/small_signal.jl +++ b/src/base/small_signal.jl @@ -17,40 +17,6 @@ function _determine_stability(vals::Vector{Complex{Float64}}) return true end -function _calculate_forwardiff_jacobian( - sim::Simulation{ResidualModel}, - x_eval::Vector{Float64}, -) - var_count = get_variable_count(sim.inputs) - dx0 = zeros(var_count) #Define a vector of zeros for the derivative - sysf! = (out, x) -> system_implicit!( - out, #output of the function - dx0, #derivatives equal to zero - x, #states - sim.inputs, #Parameters - 0.0, #time equals to zero. - ) - out = zeros(var_count) #Define a vector of zeros for the output - jacobian = ForwardDiff.jacobian(sysf!, out, x_eval) - return jacobian -end - -function _calculate_forwardiff_jacobian( - sim::Simulation{MassMatrixModel}, - x_eval::Vector{Float64}, -) - var_count = get_variable_count(sim.inputs) - sysf! = (dx, x) -> system_mass_matrix!( - dx, #derivatives equal to zero - x, #states - sim.inputs, #Parameters - 0.0, #time equals to zero. - ) - dx = zeros(var_count) #Define a vector of zeros for the output - jacobian = ForwardDiff.jacobian(sysf!, dx, x_eval) - return jacobian -end - """ Finds the location of the differential states in the reduced Jacobian """ @@ -185,9 +151,10 @@ function _small_signal_analysis( ::Type{T}, inputs::SimulationInputs, x_eval::Vector{Float64}, + p::AbstractArray{Float64}, multimachine = true, ) where {T <: SimulationModel} - jacwrapper = get_jacobian(T, inputs, x_eval, 0) + jacwrapper = get_jacobian(T, inputs, x_eval, p, 0) return _small_signal_analysis(jacwrapper.Jv, jacwrapper.x, inputs, multimachine) end @@ -203,19 +170,21 @@ Returns the Small Signal Output object that contains the eigenvalues and partici """ function small_signal_analysis(sim::Simulation{T}; kwargs...) where {T <: SimulationModel} inputs = get_simulation_inputs(sim) - if !(isempty(inputs.delays)) + if !(isempty(get_constant_lags(inputs))) return error("Small signal analysis not compatible with system model with delays") end - x_eval = get(kwargs, :operating_point, get_initial_conditions(sim)) - return _small_signal_analysis(T, inputs, x_eval, sim.multimachine) + x_eval = get(kwargs, :operating_point, get_x0(sim)) + p = get_parameters(inputs) + return _small_signal_analysis(T, inputs, x_eval, p, sim.multimachine) end function small_signal_analysis(::Type{T}, system::PSY.System) where {T <: SimulationModel} simulation_system = deepcopy(system) inputs = SimulationInputs(T, simulation_system, ReferenceBus) - x0_init = get_flat_start(inputs) + p = get_parameters(inputs) + x0_init = _get_flat_start(inputs) set_operating_point!(x0_init, inputs, system) - return _small_signal_analysis(T, inputs, x0_init) + return _small_signal_analysis(T, inputs, x0_init, p) end function summary_participation_factors( diff --git a/src/initialization/generator_components/init_avr.jl b/src/initialization/generator_components/init_avr.jl index 87965eadd..3e77ac038 100644 --- a/src/initialization/generator_components/init_avr.jl +++ b/src/initialization/generator_components/init_avr.jl @@ -1,5 +1,6 @@ function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.AVRFixed, TG, P}}, inner_vars::AbstractVector, @@ -8,7 +9,7 @@ function initialize_avr!( Vf = inner_vars[Vf_var] #Update Control Refs avr = PSY.get_avr(dynamic_device) - set_V_ref(dynamic_device, Vf) + set_V_ref!(p, Vf) PSY.set_Vf!(avr, Vf) PSY.set_V_ref!(avr, Vf) return @@ -16,6 +17,7 @@ end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.AVRSimple, TG, P}}, inner_vars::AbstractVector, @@ -32,12 +34,13 @@ function initialize_avr!( #Set V_ref PSY.set_V_ref!(PSY.get_avr(dynamic_device), Vm) #Update Control Refs - set_V_ref(dynamic_device, Vm) + set_V_ref!(p, Vm) return end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.AVRTypeI, TG, P}}, inner_vars::AbstractVector, @@ -49,35 +52,46 @@ function initialize_avr!( #Get parameters avr = PSY.get_avr(dynamic_device) - Ka = PSY.get_Ka(avr) - Ke = PSY.get_Ke(avr) - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) - Ae = PSY.get_Ae(avr) - Be = PSY.get_Be(avr) - #Obtain saturated Vf - Se_Vf = Ae * exp(Be * abs(Vf0)) + params = p[:params][:AVR] #States of AVRTypeI are Vf, Vr1, Vr2, Vm #To solve V_ref, Vr1, Vr2 - function f!(out, x) + function f!(out, x, params) V_ref = x[1] Vr1 = x[2] Vr2 = x[3] + Ka = params[:Ka] + Ke = params[:Ke] + Kf = params[:Kf] + Tf = params[:Tf] + Ae = params[:Ae] + Be = params[:Be] + #Obtain saturated Vf + Se_Vf = Ae * exp(Be * abs(Vf0)) + out[1] = Vf0 * (Ke + Se_Vf) - Vr1 #16.12c out[2] = Ka * (V_ref - Vm - Vr2 - (Kf / Tf) * Vf0) - Vr1 #16.12a out[3] = (Kf / Tf) * Vf0 + Vr2 #16.12b end x0 = [1.0, Vf0, Vf0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization of AVR in $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of AVR in $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update V_ref PSY.set_V_ref!(avr, sol_x0[1]) - set_V_ref(dynamic_device, sol_x0[1]) + set_V_ref!(p, sol_x0[1]) #Update AVR states avr_ix = get_local_state_ix(dynamic_device, PSY.AVRTypeI) avr_states = @view device_states[avr_ix] @@ -91,6 +105,7 @@ end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.AVRTypeII, TG, P}}, inner_vars::AbstractVector, @@ -101,26 +116,33 @@ function initialize_avr!( Vm = sqrt(inner_vars[VR_gen_var]^2 + inner_vars[VI_gen_var]^2) #Get parameters + params = p[:params][:AVR] + K0 = params[:K0] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + T4 = params[:T4] + Va_min = params[:Va_lim][:min] + Va_max = params[:Va_lim][:max] + Ae = params[:Ae] + Be = params[:Be] avr = PSY.get_avr(dynamic_device) - K0 = PSY.get_K0(avr) - T1 = PSY.get_T1(avr) - T2 = PSY.get_T2(avr) - T3 = PSY.get_T3(avr) - T4 = PSY.get_T4(avr) - Te = PSY.get_Te(avr) - Tr = PSY.get_Tr(avr) - Ae = PSY.get_Ae(avr) - Be = PSY.get_Be(avr) - #Obtain saturated Vf - Se_Vf = Ae * (exp(Be * abs(Vf0))) - Va_min, Va_max = PSY.get_Va_lim(avr) #States of AVRTypeII are Vf, Vr1, Vr2, Vm #To solve V_ref, Vr1, Vr2 - function f!(out, x) + function f!(out, x, params) V_ref = x[1] Vr1 = x[2] Vr2 = x[3] + K0 = params[:K0] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + T4 = params[:T4] + Te = params[:Te] + + #Obtain saturated Vf + Se_Vf = Ae * (exp(Be * abs(Vf0))) y_ll1, dVr1_dt = lead_lag(V_ref - Vm, Vr1, K0, T2, T1) y_ll2, dVr2_dt = lead_lag(y_ll1, K0 * Vr2, 1.0, K0 * T4, K0 * T3) @@ -132,14 +154,23 @@ function initialize_avr!( out[3] = dVr2_dt #16.20 end x0 = [1.0, Vf0, Vf0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization of AVR in $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of AVR in $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update V_ref PSY.set_V_ref!(avr, sol_x0[1]) - set_V_ref(dynamic_device, sol_x0[1]) + set_V_ref!(p, sol_x0[1]) #Update AVR states avr_ix = get_local_state_ix(dynamic_device, PSY.AVRTypeII) avr_states = @view device_states[avr_ix] @@ -150,7 +181,9 @@ function initialize_avr!( y_ll1, _ = lead_lag(sol_x0[1] - Vm, sol_x0[2], K0, T2, T1) y_ll2, _ = lead_lag(y_ll1, K0 * sol_x0[3], 1.0, K0 * T4, K0 * T3) if (y_ll2 > Va_max) || (y_ll2 < Va_min) - @error("Regulator Voltage V_r = $(y_ll2) outside the limits") + @error( + "Regulator Voltage V_r = $(y_ll2) outside the limits" + ) end end return @@ -158,6 +191,7 @@ end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.ESAC1A, TG, P}}, inner_vars::AbstractVector, @@ -171,37 +205,43 @@ function initialize_avr!( #Get parameters avr = PSY.get_avr(dynamic_device) - Tr = PSY.get_Tr(avr) - Tb = PSY.get_Tb(avr) - Tc = PSY.get_Tc(avr) - Ka = PSY.get_Ka(avr) - Ta = PSY.get_Ta(avr) - Va_min, Va_max = PSY.get_Va_lim(avr) - Te = PSY.get_Te(avr) - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) - Kc = PSY.get_Kc(avr) - Kd = PSY.get_Kd(avr) - Ke = PSY.get_Ke(avr) - E1, E2 = PSY.get_E_sat(avr) - SE1, SE2 = PSY.get_Se(avr) - Vr_min, Vr_max = PSY.get_Vr_lim(avr) + #Get parameters + params = p[:params][:AVR] + Tb = params[:Tb] + Tc = params[:Tc] + Ka = params[:Ka] + Kf = params[:Kf] + Tf = params[:Tf] + Kc = params[:Kc] + Kd = params[:Kd] + Ke = params[:Ke] + Vr_min = params[:Vr_lim][:min] + Vr_max = params[:Vr_lim][:max] #Obtain saturation #Se_Vf = saturation_function(Vm) #Solve Ve from rectifier function - function f_Ve!(out, x) + function f_Ve!(out, x, params) V_e0 = x[1] I_N0 = Kc * Xad_Ifd0 / V_e0 out[1] = Vf0 - V_e0 * rectifier_function(I_N0) end x0 = [1.0] - sol = NLsolve.nlsolve(f_Ve!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization of AVR in $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f_Ve!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of AVR in $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u V_e0 = sol_x0[1] I_N0 = Kc * Xad_Ifd0 / V_e0 end @@ -209,7 +249,9 @@ function initialize_avr!( V_FE0 = Kd * Xad_Ifd0 + Ke * V_e0 + Se0 * V_e0 V_r20 = V_FE0 if (V_r20 > Vr_max) || (V_r20 < Vr_min) - @error("Regulator Voltage V_R = $(V_r20) outside the limits") + @error( + "Regulator Voltage V_R = $(V_r20) outside the limits" + ) end Tc_Tb_ratio = Tb <= eps() ? 0.0 : Tc / Tb V_r30 = -(Kf / Tf) * V_FE0 @@ -218,13 +260,18 @@ function initialize_avr!( #States of ESAC1A are Vm, Vr1, Vr2, Ve, Vr3 #To solve V_ref, Vr1, Vr2, Ve, Vr3 - function f!(out, x) + function f!(out, x, params) V_ref = x[1] Vr1 = x[2] Vr2 = x[3] Ve = x[4] Vr3 = x[5] - + Ka = params[:Ka] + Kf = params[:Kf] + Tf = params[:Tf] + Kc = params[:Kc] + Kd = params[:Kd] + Ke = params[:Ke] #Compute auxiliary variables I_N = Kc * Xad_Ifd0 / Ve @@ -242,14 +289,23 @@ function initialize_avr!( out[5] = Vf0 - Ve * rectifier_function(I_N) end x0 = [V_ref0, V_r10, V_r20, V_e0, V_r30] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization of AVR in $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of AVR in $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update V_ref PSY.set_V_ref!(avr, sol_x0[1]) - set_V_ref(dynamic_device, sol_x0[1]) + set_V_ref!(p, sol_x0[1]) #Update AVR states avr_ix = get_local_state_ix(dynamic_device, typeof(avr)) avr_states = @view device_states[avr_ix] @@ -263,6 +319,7 @@ end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.SEXS, TG, P}}, inner_vars::AbstractVector, @@ -274,15 +331,17 @@ function initialize_avr!( #Get parameters avr = PSY.get_avr(dynamic_device) - Ta_Tb = PSY.get_Ta_Tb(avr) - K = PSY.get_K(avr) - V_min, V_max = PSY.get_V_lim(avr) + params = p[:params][:AVR] + V_min = params[:V_lim][:min] + V_max = params[:V_lim][:max] #States of AVRTypeI are Vf, Vr1, Vr2, Vm #To solve V_ref, Vr - function f!(out, x) + function f!(out, x, params) V_ref = x[1] Vr = x[2] + Ta_Tb = params[:Ta_Tb] + K = params[:K] V_in = V_ref - Vm V_LL = Vr + Ta_Tb * V_in @@ -291,11 +350,20 @@ function initialize_avr!( out[2] = V_in * (1 - Ta_Tb) - Vr end x0 = [1.0, Vf0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization of AVR in $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of AVR in $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u if (sol_x0[2] >= V_max + BOUNDS_TOLERANCE) || (sol_x0[2] <= V_min - BOUNDS_TOLERANCE) @error( @@ -304,7 +372,8 @@ function initialize_avr!( end #Update V_ref PSY.set_V_ref!(avr, sol_x0[1]) - set_V_ref(dynamic_device, sol_x0[1]) + set_V_ref!(p, sol_x0[1]) + #Update AVR states avr_ix = get_local_state_ix(dynamic_device, PSY.SEXS) avr_states = @view device_states[avr_ix] @@ -315,6 +384,7 @@ end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.SCRX, TG, P}}, inner_vars::AbstractVector, @@ -327,23 +397,26 @@ function initialize_avr!( Vm = sqrt(inner_vars[VR_gen_var]^2 + inner_vars[VI_gen_var]^2) #Get parameters + params = p[:params][:AVR] avr = PSY.get_avr(dynamic_device) - Ta_Tb = PSY.get_Ta_Tb(avr) - Tb = PSY.get_Tb(avr) - Ta = Tb * Ta_Tb - # dont need Te >> no derivative so block is 0 (multiply by K in steady state) - K = PSY.get_K(avr) - V_min, V_max = PSY.get_Efd_lim(avr) #Efd_lim (V_lim) **n - switch = PSY.get_switch(avr) # reads switch parameters **n - rc_rfd = PSY.get_rc_rfd(avr) + V_min, V_max = params[:Efd_lim] + Ta_Tb = params[:Ta_Tb] + K = params[:K] # do the negative current and switch? or is that alr counted in? #States of AVRTypeI are Vf, Vr1, Vr2, Vm #To solve V_ref, Vr - function f!(out, x) + function f!(out, x, params) V_ref = x[1] Vr1 = x[2] + Ta_Tb = params[:Ta_Tb] + Tb = params[:Tb] + Ta = Tb * Ta_Tb + switch = PSY.get_switch(avr) + K = params[:K] + rc_rfd = params[:rc_rfd] + V_in = V_ref - Vm # assume Vs is 0 when init #lead lag block #V_LL = Vr2 + Ta_Tb * V_in @@ -372,11 +445,20 @@ function initialize_avr!( end # solve for Vref x0 = [1.0, Vf0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization of AVR in $(PSY.get_name(static)) failed") - else # if converge - sol_x0 = sol.zero + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of AVR in $(PSY.get_name(static)) failed" + ) + else + sol_x0 = sol.u Vr2_0 = (sol_x0[2] + Ta_Tb * (sol_x0[1] - Vm)) * K # K * V_LL #check the limits if (Vr2_0 >= V_max + BOUNDS_TOLERANCE) || (Vr2_0 <= V_min - BOUNDS_TOLERANCE) @@ -386,7 +468,7 @@ function initialize_avr!( end #Update V_ref PSY.set_V_ref!(avr, sol_x0[1]) - set_V_ref(dynamic_device, sol_x0[1]) + set_V_ref!(p, sol_x0[1]) #Update AVR states avr_ix = get_local_state_ix(dynamic_device, PSY.SCRX) avr_states = @view device_states[avr_ix] @@ -397,6 +479,7 @@ end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.EXST1, TG, P}}, inner_vars::AbstractVector, @@ -410,13 +493,15 @@ function initialize_avr!( #Get parameters avr = PSY.get_avr(dynamic_device) - Ka = PSY.get_Ka(avr) - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) - Tc = PSY.get_Tc(avr) - Tb = PSY.get_Tb(avr) - Kc = PSY.get_Kc(avr) - Vr_min, Vr_max = PSY.get_Vr_lim(avr) + params = p[:params][:AVR] + Tc = params[:Tc] + Tb = params[:Tb] + Ka = params[:Ka] + Vr_min = params[:Vr_lim][:min] + Vr_max = params[:Vr_lim][:max] + Kc = params[:Kc] + Kf = params[:Kf] + Tf = params[:Tf] # Check limits to field voltage if (Vt * Vr_min - Kc * Ifd > Vf0) || (Vf0 > Vt * Vr_max - Kc * Ifd) @@ -429,7 +514,7 @@ function initialize_avr!( Vref0 = Vt + Vf0 / Ka PSY.set_V_ref!(avr, Vref0) - set_V_ref(dynamic_device, Vref0) + set_V_ref!(p, Vref0) #States of EXST1_PTI are Vm, Vll, Vr, Vfb @@ -445,6 +530,7 @@ end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.EXAC1, TG, P}}, inner_vars::AbstractVector, @@ -458,29 +544,42 @@ function initialize_avr!( #Get parameters avr = PSY.get_avr(dynamic_device) - Tb = PSY.get_Tb(avr) - Tc = PSY.get_Tc(avr) - Ka = PSY.get_Ka(avr) - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) - Kc = PSY.get_Kc(avr) - Kd = PSY.get_Kd(avr) - Ke = PSY.get_Ke(avr) - Vr_min, Vr_max = PSY.get_Vr_lim(avr) - + #Get parameters + params = p[:params][:AVR] + Tb = params[:Tb] + Tc = params[:Tc] + Ka = params[:Ka] + Vr_min = params[:Vr_lim][:min] + Vr_max = params[:Vr_lim][:max] + Kf = params[:Kf] + Tf = params[:Tf] + Kc = params[:Kc] + Kd = params[:Kd] + Ke = params[:Ke] + params_nl = [Ifd0, Kc] #Solve Ve from rectifier function - function f_Ve!(out, x) + function f_Ve!(out, x, params) Ve = x[1] + Ifd0, Kc = params IN = Kc * Ifd0 / Ve out[1] = Vf0 - Ve * rectifier_function(IN) end x0 = [10.0] # initial guess for Ve - sol = NLsolve.nlsolve(f_Ve!, x0) - if !NLsolve.converged(sol) - @warn("Initialization of AVR in $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f_Ve!, x0, params_nl) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of AVR in $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u Ve = sol_x0[1] IN = Kc * Ifd0 / Ve end @@ -498,7 +597,7 @@ function initialize_avr!( #Update V_ref PSY.set_V_ref!(avr, Vref0) - set_V_ref(dynamic_device, Vref0) + set_V_ref!(p, Vref0) #States of EXAC1 are Vm, Vr1, Vr2, Ve, Vr3 @@ -515,6 +614,7 @@ end function initialize_avr!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.ESST1A, TG, P}}, inner_vars::AbstractVector, @@ -528,17 +628,19 @@ function initialize_avr!( #Get parameters avr = PSY.get_avr(dynamic_device) - Tc = PSY.get_Tc(avr) - Tb = PSY.get_Tb(avr) - Tc1 = PSY.get_Tc1(avr) - Tb1 = PSY.get_Tb1(avr) - Ka = PSY.get_Ka(avr) - Vr_min, Vr_max = PSY.get_Vr_lim(avr) - Kc = PSY.get_Kc(avr) - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) - K_lr = PSY.get_K_lr(avr) - I_lr = PSY.get_I_lr(avr) + params = p[:params][:AVR] + Tc = params[:Tc] + Tb = params[:Tb] + Tc1 = params[:Tc1] + Tb1 = params[:Tb1] + Ka = params[:Ka] + Vr_min = params[:Vr_lim][:min] + Vr_max = params[:Vr_lim][:max] + Kc = params[:Kc] + Kf = params[:Kf] + Tf = params[:Tf] + K_lr = params[:K_lr] + I_lr = params[:I_lr] # Check limits to field voltage if (Vt * Vr_min > Vf0) || (Vf0 > Vt * Vr_max - Kc * Ifd) @@ -557,7 +659,7 @@ function initialize_avr!( Vref0 = Vt + Va / Ka PSY.set_V_ref!(avr, Vref0) - set_V_ref(dynamic_device, Vref0) + set_V_ref!(p, Vref0) #States of ESST1A_PTI are Vm, Vr1, Vr2, Va, Vr3 diff --git a/src/initialization/generator_components/init_machine.jl b/src/initialization/generator_components/init_machine.jl index b400bd7fc..b580447be 100644 --- a/src/initialization/generator_components/init_machine.jl +++ b/src/initialization/generator_components/init_machine.jl @@ -4,6 +4,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations """ function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{PSY.BaseMachine, S, A, TG, P}}, inner_vars::AbstractVector, @@ -21,20 +22,21 @@ function initialize_mach_shaft!( #Machine Data machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - #Assumption of Classical Machine: Xq = Xd_p - Xd_p = PSY.get_Xd_p(machine) + params = p[:params][:Machine] + R = params[:R] + Xd_p = params[:Xd_p] δ0 = angle(V + (R + Xd_p * 1im) * I) ω0 = 1.0 τm0 = real(V * conj(I)) @assert isapprox(τm0, P0; atol = STRICT_NLSOLVE_F_TOLERANCE) τm0, P0 #To solve: δ, τm, Vf0 - function f!(out, x) + p_nl = [R, Xd_p, V_R, V_I, P0, Q0] + function f!(out, x, p_nl) δ = x[1] τm = x[2] Vf0 = x[3] - + R, Xd_p, V_R, V_I, P0, Q0 = p_nl V_dq = ri_dq(δ) * [V_R; V_I] i_d = (1.0 / (R^2 + Xd_p^2)) * (Xd_p * (Vf0 - V_dq[2]) - R * V_dq[1]) #15.36 i_q = (1.0 / (R^2 + Xd_p^2)) * (Xd_p * V_dq[1] + R * (Vf0 - V_dq[2])) #15.36 @@ -44,11 +46,20 @@ function initialize_mach_shaft!( out[3] = Q0 - (V_dq[2] * i_d - V_dq[1] * i_q) #Output Reactive Power end x0 = [δ0, τm0, 1.0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, p_nl) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -62,6 +73,7 @@ function initialize_mach_shaft!( inner_vars[τm_var] = sol_x0[2] #Not necessary to update Vf for AVR in Base Machine. Update eq_p: PSY.set_eq_p!(machine, sol_x0[3]) + params[:eq_p] = sol_x0[3] #eq_p should not be a parameter. inner_vars[Vf_var] = sol_x0[3] end return @@ -73,6 +85,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations """ function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{PSY.OneDOneQMachine, S, A, TG, P}}, inner_vars::AbstractVector, @@ -89,12 +102,9 @@ function initialize_mach_shaft!( I = conj(S0 / V) #Machine Data - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) + params = p[:params][:Machine] + R = params[:R] + Xq = params[:Xq] #States of OneDOneQMachine are [1] eq_p and [2] ed_p δ0 = angle(V + (R + Xq * 1im) * I) @@ -102,13 +112,19 @@ function initialize_mach_shaft!( τm0 = real(V * conj(I)) @assert isapprox(τm0, P0; atol = STRICT_NLSOLVE_F_TOLERANCE) τm0, P0 #To solve: δ, τm, Vf0, eq_p, ed_p - function f!(out, x) + function f!(out, x, params) δ = x[1] τm = x[2] Vf0 = x[3] eq_p = x[4] ed_p = x[5] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + V_dq = ri_dq(δ) * [V_R; V_I] i_d = (1.0 / (R^2 + Xd_p * Xq_p)) * (Xq_p * (eq_p - V_dq[2]) + R * (ed_p - V_dq[1])) #15.32 i_q = @@ -122,11 +138,20 @@ function initialize_mach_shaft!( end V_dq0 = ri_dq(δ0) * [V_R; V_I] x0 = [δ0, τm0, 1.0, V_dq0[2], V_dq0[1]] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -155,6 +180,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations """ function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{PSY.SauerPaiMachine, S, A, TG, P}}, inner_vars::AbstractVector, @@ -172,20 +198,9 @@ function initialize_mach_shaft!( I = conj(S0 / V) #Machine Data - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xq_pp = PSY.get_Xq_pp(machine) - Xl = PSY.get_Xl(machine) - Td0_p = PSY.get_Td0_p(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) - γ_q2 = PSY.get_γ_q2(machine) + params = p[:params][:Machine] + R = params[:R] + Xq = params[:Xq] #States of SauerPaiMachine are [1] ψq, [2] ψd, [3] eq_p, [4] ed_p, [5] ψd_pp and [6] ψq_pp δ0 = angle(V + (R + Xq * 1im) * I) @@ -193,7 +208,7 @@ function initialize_mach_shaft!( τm0 = real(V * conj(I)) @assert isapprox(τm0, P0; atol = STRICT_NLSOLVE_F_TOLERANCE) #To solve: δ, τm, Vf0, eq_p, ed_p - function f!(out, x) + function f!(out, x, params) δ = x[1] τm = x[2] Vf0 = x[3] @@ -204,6 +219,20 @@ function initialize_mach_shaft!( ψd_pp = x[8] ψq_pp = x[9] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xq_pp = params[:Xq_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] + γ_q2 = params[:γ_q2] + ω0 = 1.0 + V_dq = ri_dq(δ) * [V_R; V_I] i_d = (1.0 / Xd_pp) * (γ_d1 * eq_p - ψd + (1 - γ_d1) * ψd_pp) #15.15 i_q = (1.0 / Xq_pp) * (-γ_q1 * ed_p - ψq + (1 - γ_q1) * ψq_pp) #15.15 @@ -223,11 +252,20 @@ function initialize_mach_shaft!( V_dq0 = ri_dq(δ0) * [V_R; V_I] x0 = [δ0, τm0, 1.0, V_dq0[1], V_dq0[2], V_dq0[2], V_dq0[1], V_dq0[2], V_dq0[1]] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of Machine in $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -263,6 +301,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations """ function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{PSY.MarconatoMachine, S, A, TG, P}}, inner_vars::AbstractVector, @@ -280,18 +319,9 @@ function initialize_mach_shaft!( I = conj(S0 / V) #Machine Data - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xq_pp = PSY.get_Xq_pp(machine) - T_AA = PSY.get_T_AA(machine) - Td0_p = PSY.get_Td0_p(machine) - γd = PSY.get_γd(machine) - γq = PSY.get_γq(machine) + params = p[:params][:Machine] + R = params[:R] + Xq = params[:Xq] #States of MarconatoMachine are [1] ψq, [2] ψd, [3] eq_p, [4] ed_p, [5] eq_pp and [6] ed_pp δ0 = angle(V + (R + Xq * 1im) * I) @@ -299,7 +329,7 @@ function initialize_mach_shaft!( τm0 = real(V * conj(I)) @assert isapprox(τm0, P0; atol = STRICT_NLSOLVE_F_TOLERANCE) #To solve: δ, τm, Vf0, eq_p, ed_p - function f!(out, x) + function f!(out, x, params) δ = x[1] τm = x[2] Vf0 = x[3] @@ -310,6 +340,18 @@ function initialize_mach_shaft!( eq_pp = x[8] ed_pp = x[9] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xq_pp = params[:Xq_pp] + Td0_p = params[:Td0_p] + T_AA = params[:T_AA] + γd = params[:γd] + γq = params[:γq] + ω0 = 1.0 V_dq = ri_dq(δ) * [V_R; V_I] i_d = (1.0 / Xd_pp) * (eq_pp - ψd) #15.18 i_q = (1.0 / Xq_pp) * (-ed_pp - ψq) #15.18 @@ -327,11 +369,20 @@ function initialize_mach_shaft!( V_dq0 = ri_dq(δ0) * [V_R; V_I] x0 = [δ0, τm0, 1.0, V_dq0[1], V_dq0[2], V_dq0[2], V_dq0[1], V_dq0[2], V_dq0[1]] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -367,6 +418,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations """ function initialize_mach_shaft!( device_states, + device_parameters, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicGenerator{PSY.SimpleMarconatoMachine, S, A, TG, P}, @@ -434,7 +486,9 @@ function initialize_mach_shaft!( x0 = [δ0, τm0, 1.0, V_dq0[2], V_dq0[1], V_dq0[2], V_dq0[1]] sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else sol_x0 = sol.zero #Update terminal voltages @@ -467,6 +521,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations """ function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicGenerator{PSY.AndersonFouadMachine, S, A, TG, P}, @@ -486,14 +541,9 @@ function initialize_mach_shaft!( I = conj(S0 / V) #Machine Data - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xq_pp = PSY.get_Xq_pp(machine) + params = p[:params][:Machine] + R = params[:R] + Xq = params[:Xq] #States of Anderson-Fouad are [1] ψq, [2] ψd, [3] eq_p, [4] ed_p, [5] eq_pp and [6] ed_pp δ0 = angle(V + (R + Xq * 1im) * I) @@ -502,7 +552,7 @@ function initialize_mach_shaft!( @assert isapprox(τm0, P0; atol = STRICT_NLSOLVE_F_TOLERANCE) #To solve: δ, τm, Vf0, eq_p, ed_p - function f!(out, x) + function f!(out, x, params) δ = x[1] τm = x[2] Vf0 = x[3] @@ -512,6 +562,14 @@ function initialize_mach_shaft!( ed_p = x[7] eq_pp = x[8] ed_pp = x[9] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xq_pp = params[:Xq_pp] + ω0 = 1.0 V_dq = ri_dq(δ) * [V_R; V_I] i_d = (1.0 / Xd_pp) * (eq_pp - ψd) #15.18 @@ -530,11 +588,20 @@ function initialize_mach_shaft!( V_dq0 = ri_dq(δ0) * [V_R; V_I] x0 = [δ0, τm0, 1.0, V_dq0[1], V_dq0[2], V_dq0[2], V_dq0[1], V_dq0[2], V_dq0[1]] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -570,6 +637,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations """ function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{PSY.SimpleAFMachine, S, A, TG, P}}, inner_vars::AbstractVector, @@ -587,14 +655,9 @@ function initialize_mach_shaft!( I = conj(S0 / V) #Get parameters - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xq_pp = PSY.get_Xq_pp(machine) + params = p[:params][:Machine] + R = params[:R] + Xq = params[:Xq] #States of SimpleAndersonFouad are [1] eq_p, [2] ed_p, [3] eq_pp and [4] ed_pp δ0 = angle(V + (R + Xq * 1im) * I) @@ -602,7 +665,7 @@ function initialize_mach_shaft!( τm0 = real(V * conj(I)) @assert isapprox(τm0, P0; atol = STRICT_NLSOLVE_F_TOLERANCE) #To solve: δ, τm, Vf0, eq_p, ed_p - function f!(out, x) + function f!(out, x, params) δ = x[1] τm = x[2] Vf0 = x[3] @@ -610,6 +673,13 @@ function initialize_mach_shaft!( ed_p = x[5] eq_pp = x[6] ed_pp = x[7] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xq_pp = params[:Xq_pp] V_dq = ri_dq(δ) * [V_R; V_I] i_d = @@ -629,11 +699,20 @@ function initialize_mach_shaft!( end V_dq0 = ri_dq(δ0) * [V_R; V_I] x0 = [δ0, τm0, 1.0, V_dq0[2], V_dq0[1], V_dq0[2], V_dq0[1]] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -660,6 +739,7 @@ end function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, P}}, inner_vars::AbstractVector, @@ -684,26 +764,22 @@ function initialize_mach_shaft!( #Get parameters machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Td0_p = PSY.get_Td0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_p = PSY.get_Tq0_p(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) + params = p[:params][:Machine] + #parameters needed for initial guess: + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_q1 = params[:γ_q1] + γ_q2 = params[:γ_q2] + γ_qd = params[:γ_qd] + Xq_pp = Xd_pp - Xl = PSY.get_Xl(machine) - Se = PSY.get_Se(machine) # Initialization doesn't consider saturation #Sat_A, Sat_B = PSY.get_saturation_coeffs(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) - γ_q2 = PSY.get_γ_q2(machine) - γ_qd = PSY.get_γ_qd(machine) ## Initialization ## ## Fluxes @@ -741,7 +817,7 @@ function initialize_mach_shaft!( (Xq - Xq_p) * (γ_q2 * ed_p0 - γ_q2 * ψ_kq0 - γ_q1 * I_q0) + Se0 * ψq_pp0 * γ_qd - function f!(out, x) + function f!(out, x, params) δ = x[1] τm = x[2] Vf = x[3] @@ -751,6 +827,23 @@ function initialize_mach_shaft!( ψ_kq = x[7] Xad_Ifd_aux = x[8] + R = params[:R] + Td0_p = params[:Td0_p] + Td0_pp = params[:Td0_pp] + Tq0_p = params[:Tq0_p] + Tq0_pp = params[:Tq0_pp] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] + γ_q2 = params[:γ_q2] + γ_qd = params[:γ_qd] + V_dq = ri_dq(δ) * [V_R; V_I] ψq_pp = γ_q1 * ed_p + ψ_kq * (1 - γ_q1) ψd_pp = γ_d1 * eq_p + γ_d2 * (Xd_p - Xl) * ψ_kd @@ -779,11 +872,20 @@ function initialize_mach_shaft!( out[8] = Xad_Ifd_aux - Xad_Ifd end x0 = [δ0, τm0, Vf0, eq_p0, ed_p0, ψ_kd0, ψ_kq0, Xad_Ifd0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -812,6 +914,7 @@ end function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicGenerator{PSY.SalientPoleQuadratic, S, A, TG, P}, @@ -831,21 +934,23 @@ function initialize_mach_shaft!( V = V_R + V_I * 1im I = conj(S0 / V) - #Get parameters machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Td0_p = PSY.get_Td0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) + #Get parameters + params = @view(p[:params][:Machine]) + R = params[:R] + # Td0_p = params[:Td0_p] + # Td0_pp = params[:Td0_pp] + # Tq0_pp = params[:Tq0_pp] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] + Xq_pp = Xd_pp - Xl = PSY.get_Xl(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) ## Initialization ## E = V + (R + Xq * 1im) * I @@ -870,7 +975,7 @@ function initialize_mach_shaft!( Se0 * eq_p0 + (Xd - Xd_p) * (I_d0 + γ_d2 * (eq_p0 - ψ_kd0 - (Xd_p - Xl) * I_d0)) - function f!(out, x) + function f!(out, x, params) δ = x[1] τm = x[2] Vf = x[3] @@ -879,6 +984,20 @@ function initialize_mach_shaft!( ψq_pp = x[6] Xad_Ifd_aux = x[7] + R = params[:R] + Td0_p = params[:Td0_p] + Td0_pp = params[:Td0_pp] + Tq0_pp = params[:Tq0_pp] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] + Xq_pp = Xd_pp + V_d, V_q = ri_dq(δ) * [V_R; V_I] #Additional Fluxes ψd_pp = γ_d1 * eq_p + γ_q1 * ψ_kd @@ -902,11 +1021,20 @@ function initialize_mach_shaft!( out[7] = Xad_Ifd_aux - Xad_Ifd end x0 = [δ0, τm0, Vf0, eq_p0, ψ_kd0, ψq_pp0, Xad_Ifd0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -934,6 +1062,7 @@ end function initialize_mach_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicGenerator{PSY.SalientPoleExponential, S, A, TG, P}, @@ -953,21 +1082,19 @@ function initialize_mach_shaft!( V = V_R + V_I * 1im I = conj(S0 / V) - #Get parameters machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Td0_p = PSY.get_Td0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) + #Get parameters + params = @view(p[:params][:Machine]) + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] Xq_pp = Xd_pp - Xl = PSY.get_Xl(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) γ_qd = (Xq - Xl) / (Xd - Xl) ## Initialization ## @@ -1004,7 +1131,7 @@ function initialize_mach_shaft!( Se0 * ψd_pp0 + (Xd - Xd_p) * (I_d0 + γ_d2 * (eq_p0 - ψ_kd0 - (Xd_p - Xl) * I_d0)) - function f!(out, x) + function f!(out, x, params) δ = x[1] τm = x[2] Vf = x[3] @@ -1013,6 +1140,19 @@ function initialize_mach_shaft!( ψq_pp = x[6] Xad_Ifd_aux = x[7] + R = params[:R] + Td0_p = params[:Td0_p] + Td0_pp = params[:Td0_pp] + Tq0_pp = params[:Tq0_pp] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] + V_d, V_q = ri_dq(δ) * [V_R; V_I] #Additional Fluxes ψd_pp = γ_d1 * eq_p + γ_q1 * ψ_kd @@ -1036,11 +1176,20 @@ function initialize_mach_shaft!( out[7] = Xad_Ifd_aux - Xad_Ifd end x0 = [δ0, τm0, Vf0, eq_p0, ψ_kd0, ψq_pp0, Xad_Ifd0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Machine $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Machine $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[VR_gen_var] = V_R inner_vars[VI_gen_var] = V_I @@ -1073,6 +1222,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations """ function initialize_mach_shaft!( device_states, + device_parameters, static::PSY.StaticInjection, dynamic_device::PSY.DynamicGenerator{PSY.FullMachine, S, A, TG, P}}, ) where {S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov, P <: PSY.PSS} diff --git a/src/initialization/generator_components/init_pss.jl b/src/initialization/generator_components/init_pss.jl index f971bc675..218695247 100644 --- a/src/initialization/generator_components/init_pss.jl +++ b/src/initialization/generator_components/init_pss.jl @@ -1,5 +1,6 @@ function initialize_pss!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.PSSFixed}}, inner_vars::AbstractVector, @@ -7,6 +8,7 @@ function initialize_pss!( function initialize_pss!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.IEEEST}}, inner_vars::AbstractVector, @@ -30,20 +32,22 @@ function initialize_pss!( pss_states = @view device_states[pss_ix] # Get Parameters - A1 = PSY.get_A1(pss) - A2 = PSY.get_A2(pss) - A5 = PSY.get_A5(pss) - A6 = PSY.get_A6(pss) - T1 = PSY.get_T1(pss) - T2 = PSY.get_T2(pss) - T3 = PSY.get_T3(pss) - T4 = PSY.get_T4(pss) - T5 = PSY.get_T5(pss) - T6 = PSY.get_T6(pss) - Ks = PSY.get_Ks(pss) - Ls_min, Ls_max = PSY.get_Ls_lim(pss) - V_cu = PSY.get_Vcu(pss) - V_cl = PSY.get_Vcl(pss) + params = p[:params][:PSS] + A1 = params[:A1] + A2 = params[:A2] + A5 = params[:A5] + A6 = params[:A6] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + T4 = params[:T4] + T5 = params[:T5] + T6 = params[:T6] + Ks = params[:Ks] + Ls_min = params[:Ls_lim1] + Ls_max = params[:Ls_lim2] + V_cu = params[:Vcu] + V_cl = params[:Vcl] #Error non-valid parameters if A6 > eps() && A2 < eps() @@ -116,15 +120,14 @@ end function initialize_pss!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.STAB1}}, inner_vars::AbstractVector, ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov} - #Get Signal Input Integer - pss = PSY.get_pss(dynamic_device) #Obtain PSS States - pss_ix = get_local_state_ix(dynamic_device, typeof(pss)) + pss_ix = get_local_state_ix(dynamic_device, PSY.STAB1) pss_states = @view device_states[pss_ix] #Compute steady-state values @@ -147,6 +150,7 @@ end function initialize_pss!( device_states, + device_parameters, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.PSS2A}}, inner_vars::AbstractVector, @@ -270,6 +274,7 @@ end function initialize_pss!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.PSS2B}}, inner_vars::AbstractVector, @@ -316,11 +321,12 @@ function initialize_pss!( pss_states = @view device_states[pss_ix] # Get Required Parameters + params = p[:params][:PSS] M_rtf = PSY.get_M_rtf(pss) N_rtf = PSY.get_N_rtf(pss) - Tw1 = PSY.get_Tw1(pss) - Tw3 = PSY.get_Tw3(pss) - T9 = PSY.get_T9(pss) + Tw1 = params[:Tw1] + Tw3 = params[:Tw3] + T9 = params[:T9] Vs1_min, Vs1_max = PSY.get_Vs1_lim(pss) Vs2_min, Vs2_max = PSY.get_Vs2_lim(pss) @@ -406,6 +412,7 @@ end function initialize_pss!( device_states, + device_parameters, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.PSS2C}}, inner_vars::AbstractVector, diff --git a/src/initialization/generator_components/init_shaft.jl b/src/initialization/generator_components/init_shaft.jl index 05669e62f..4ae1fafc0 100644 --- a/src/initialization/generator_components/init_shaft.jl +++ b/src/initialization/generator_components/init_shaft.jl @@ -1,5 +1,6 @@ function initialize_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, PSY.SingleMass, A, TG, P}}, inner_vars::AbstractVector, @@ -7,6 +8,7 @@ function initialize_shaft!( function initialize_shaft!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, PSY.FiveMassShaft, A, TG, P}}, inner_vars::AbstractVector, @@ -22,32 +24,32 @@ function initialize_shaft!( ω_ex = ω ω_sys = ω - #Obtain parameters - shaft = PSY.get_shaft(dynamic_device) - D = PSY.get_D(shaft) - D_hp = PSY.get_D_hp(shaft) - D_ip = PSY.get_D_ip(shaft) - D_lp = PSY.get_D_lp(shaft) - D_ex = PSY.get_D_ex(shaft) - D_12 = PSY.get_D_12(shaft) - D_23 = PSY.get_D_23(shaft) - D_34 = PSY.get_D_34(shaft) - D_45 = PSY.get_D_45(shaft) - K_hp = PSY.get_K_hp(shaft) - K_ip = PSY.get_K_ip(shaft) - K_lp = PSY.get_K_lp(shaft) - K_ex = PSY.get_K_ex(shaft) + #Get parameters + params = p[:params][:Shaft] #Obtain inner vars τe = inner_vars[τe_var] τm0 = inner_vars[τm_var] - function f!(out, x) + function f!(out, x, params) τm = x[1] δ_hp = x[2] δ_ip = x[3] δ_lp = x[4] δ_ex = x[5] + D = params[:D] + D_hp = params[:D_hp] + D_ip = params[:D_ip] + D_lp = params[:D_lp] + D_ex = params[:D_ex] + D_12 = params[:D_12] + D_23 = params[:D_23] + D_34 = params[:D_34] + D_45 = params[:D_45] + K_hp = params[:K_hp] + K_ip = params[:K_ip] + K_lp = params[:K_lp] + K_ex = params[:K_ex] out[1] = ( -τe - D * (ω - ω_sys) - D_34 * (ω - ω_lp) - D_45 * (ω - ω_ex) + @@ -72,11 +74,20 @@ function initialize_shaft!( end x0 = [τm0, δ0, δ0, δ0, δ0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Shaft failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Shaft failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u inner_vars[τm_var] = sol_x0[1] #τm shaft_states[3] = sol_x0[2] #δ_hp shaft_states[4] = ω #ω_hp diff --git a/src/initialization/generator_components/init_tg.jl b/src/initialization/generator_components/init_tg.jl index e0c4b1028..fe2383bb8 100644 --- a/src/initialization/generator_components/init_tg.jl +++ b/src/initialization/generator_components/init_tg.jl @@ -1,21 +1,24 @@ function initialize_tg!( device_states, + p, ::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.TGFixed, P}}, inner_vars::AbstractVector, ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS} tg = PSY.get_prime_mover(dynamic_device) + eff = p[:params][:TurbineGov][:efficiency] τm0 = inner_vars[τm_var] - eff = PSY.get_efficiency(tg) + P_ref = τm0 / eff - PSY.set_P_ref!(tg, P_ref) #Update Control Refs - set_P_ref(dynamic_device, P_ref) + PSY.set_P_ref!(tg, P_ref) + set_P_ref!(p, P_ref) return end function initialize_tg!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.TGTypeI, P}}, inner_vars::AbstractVector, @@ -23,25 +26,35 @@ function initialize_tg!( #Get mechanical torque to SyncMach τm0 = inner_vars[τm_var] - #Get Parameters + tg = PSY.get_prime_mover(dynamic_device) - inv_R = PSY.get_R(tg) < eps() ? 0.0 : (1.0 / PSY.get_R(tg)) - Tc = PSY.get_Tc(tg) - T3 = PSY.get_T3(tg) - T4 = PSY.get_T4(tg) - T5 = PSY.get_T5(tg) - V_min, V_max = PSY.get_valve_position_limits(tg) + #Get Parameters + params = p[:params][:TurbineGov] + R = params[:R] + Tc = params[:Tc] + T3 = params[:T3] + T4 = params[:T4] + T5 = params[:T5] + V_min = params[:valve_position_limits][:min] + V_max = params[:valve_position_limits][:max] #Get References - ω_ref = get_ω_ref(dynamic_device) + ω_ref = p[:refs][:ω_ref] ω0 = 1.0 - function f!(out, x) + function f!(out, x, params) P_ref = x[1] x_g1 = x[2] x_g2 = x[3] x_g3 = x[4] + R = params[:R] + Tc = params[:Tc] + T3 = params[:T3] + T4 = params[:T4] + T5 = params[:T5] + ω0 = 1.0 #Compute auxiliary parameters + inv_R = R < eps() ? 0.0 : (1.0 / R) P_in = P_ref + inv_R * (ω_ref - ω0) out[1] = P_in - x_g1 @@ -55,14 +68,21 @@ function initialize_tg!( (1.0 - T3 / Tc) * τm0, (1.0 - T4 / T5) * ((1.0 - T3 / Tc) * τm0 + (T3 / Tc) * τm0), ] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization of Turbine Governor $(PSY.get_name(static)) failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Update Control Refs PSY.set_P_ref!(tg, sol_x0[1]) - set_P_ref(dynamic_device, sol_x0[1]) + set_P_ref!(p, sol_x0[1]) #Update states tg_ix = get_local_state_ix(dynamic_device, PSY.TGTypeI) tg_states = @view device_states[tg_ix] @@ -80,6 +100,7 @@ end function initialize_tg!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.TGTypeII, P}}, inner_vars::AbstractVector, @@ -87,29 +108,40 @@ function initialize_tg!( #Get mechanical torque to SyncMach τm0 = inner_vars[τm_var] - #Get parameters tg = PSY.get_prime_mover(dynamic_device) - inv_R = PSY.get_R(tg) < eps() ? 0.0 : (1.0 / PSY.get_R(tg)) - T1 = PSY.get_T1(tg) - T2 = PSY.get_T2(tg) - ω_ref = get_ω_ref(dynamic_device) - ω0 = ω_ref - function f!(out, x) + params = [ + p[:params][:TurbineGov][:R], + p[:params][:TurbineGov][:T1], + p[:params][:TurbineGov][:T2], + p[:refs][:ω_ref], + ] + + function f!(out, x, params) P_ref = x[1] xg = x[2] + R, T1, T2, ω_ref = params + ω0 = ω_ref + inv_R = R < eps() ? 0.0 : (1.0 / R) out[1] = inv_R * (T1 / T2) * (ω_ref - ω0) + P_ref / 1.0 + xg - τm0 out[2] = (1.0 / T2) * (inv_R * (1 - T2 / T1) * (ω_ref - ω0) - xg) end x0 = [τm0, 0.0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization of Turbine Governor $(PSY.get_name(static)) failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Update Control Refs PSY.set_P_ref!(tg, sol_x0[1]) - set_P_ref(dynamic_device, sol_x0[1]) + set_P_ref!(p, sol_x0[1]) #Update states tg_ix = get_local_state_ix(dynamic_device, PSY.TGTypeII) tg_states = @view device_states[tg_ix] @@ -120,6 +152,7 @@ end function initialize_tg!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.GasTG, P}}, inner_vars::AbstractVector, @@ -128,21 +161,26 @@ function initialize_tg!( #Get mechanical torque to SyncMach τm0 = inner_vars[τm_var] Δω = 0.0 - #Get parameters + tg = PSY.get_prime_mover(dynamic_device) - inv_R = PSY.get_R(tg) < eps() ? 0.0 : (1.0 / PSY.get_R(tg)) - D_turb = PSY.get_D_turb(tg) - AT = PSY.get_AT(tg) - KT = PSY.get_Kt(tg) - V_min, V_max = PSY.get_V_lim(tg) + #Get parameters + params = p[:params][:TurbineGov] + R = params[:R] + inv_R = R < eps() ? 0.0 : (1.0 / R) - function f!(out, x) + function f!(out, x, params) P_ref = x[1] x_g1 = x[2] x_g2 = x[3] x_g3 = x[4] - x_in = min((P_ref - inv_R * Δω), (AT + KT * (AT - x_g3))) + AT = params[:AT] + Kt = params[:Kt] + V_min = params[:V_lim][:min] + V_max = params[:V_lim][:max] + D_turb = params[:D_turb] + + x_in = min((P_ref - inv_R * Δω), (AT + Kt * (AT - x_g3))) out[1] = (x_in - x_g1) #dx_g1/dt x_g1_sat = V_min < x_g1 < V_max ? x_g1 : max(V_min, min(V_max, x_g1)) out[2] = (x_g1_sat - x_g2) @@ -150,14 +188,21 @@ function initialize_tg!( out[4] = (x_g2 - D_turb * Δω) - τm0 end x0 = [1.0 / inv_R, τm0, τm0, τm0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @error("Initialization of Turbine Governor $(PSY.get_name(static)) failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn("Initialization in TG failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Update Control Refs PSY.set_P_ref!(tg, sol_x0[1]) - set_P_ref(dynamic_device, sol_x0[1]) + set_P_ref!(p, sol_x0[1]) #Update states tg_ix = get_local_state_ix(dynamic_device, typeof(tg)) tg_states = @view device_states[tg_ix] @@ -170,6 +215,7 @@ end function initialize_tg!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.DEGOV, P}}, inner_vars::AbstractVector, @@ -178,7 +224,7 @@ function initialize_tg!( #Get mechanical torque to SyncMach τm0 = inner_vars[τm_var] PSY.set_P_ref!(tg, τm0) - set_P_ref(dynamic_device, τm0) + set_P_ref!(p, τm0) #Update states tg_ix = get_local_state_ix(dynamic_device, typeof(tg)) tg_states = @view device_states[tg_ix] @@ -192,6 +238,7 @@ end function initialize_tg!( device_states, + p, ::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.SteamTurbineGov1, P}}, inner_vars::AbstractVector, @@ -200,20 +247,29 @@ function initialize_tg!( #Get mechanical torque to SyncMach τm0 = inner_vars[τm_var] Δω = 0.0 - #Get parameters + tg = PSY.get_prime_mover(dynamic_device) - inv_R = PSY.get_R(tg) < eps() ? 0.0 : (1.0 / PSY.get_R(tg)) - T1 = PSY.get_T1(tg) - T2 = PSY.get_T2(tg) - T3 = PSY.get_T3(tg) - V_min, V_max = PSY.get_valve_position_limits(tg) - D_T = PSY.get_D_T(tg) + #Get Parameters + params = p[:params][:TurbineGov] + R = params[:R] + V_min = params[:valve_position_limits][:min] + V_max = params[:valve_position_limits][:max] + inv_R = R < eps() ? 0.0 : (1.0 / R) - function f!(out, x) + function f!(out, x, params) P_ref = x[1] x_g1 = x[2] x_g2 = x[3] + R = params[:R] + T1 = params[:T1] + V_min = params[:valve_position_limits][:min] + V_max = params[:valve_position_limits][:max] + T2 = params[:T2] + T3 = params[:T3] + D_T = params[:D_T] + inv_R = R < eps() ? 0.0 : (1.0 / R) + ref_in = inv_R * (P_ref - Δω) Pm = x_g2 + (T2 / T3) * x_g1 @@ -223,11 +279,18 @@ function initialize_tg!( out[3] = (Pm - D_T * Δω) - τm0 end x0 = [1.0 / inv_R, τm0, τm0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in TG failed") else - sol_x0 = sol.zero + sol_x0 = sol.u if (sol_x0[2] >= V_max + BOUNDS_TOLERANCE) || (sol_x0[2] <= V_min - BOUNDS_TOLERANCE) @error( @@ -236,7 +299,7 @@ function initialize_tg!( end #Update Control Refs PSY.set_P_ref!(tg, sol_x0[1]) - set_P_ref(dynamic_device, sol_x0[1]) + set_P_ref!(p, sol_x0[1]) #Update states tg_ix = get_local_state_ix(dynamic_device, typeof(tg)) tg_states = @view device_states[tg_ix] @@ -248,6 +311,7 @@ end function initialize_tg!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.HydroTurbineGov, P}}, inner_vars::AbstractVector, @@ -256,25 +320,31 @@ function initialize_tg!( #Get mechanical torque to SyncMach τm0 = inner_vars[τm_var] Δω = 0.0 - #Get parameters - tg = PSY.get_prime_mover(dynamic_device) - R = PSY.get_R(tg) - r = PSY.get_r(tg) - Tr = PSY.get_Tr(tg) - #Gate velocity limits not implemented - #VELM = PSY.get_VELM(tg) - G_min, G_max = PSY.get_gate_position_limits(tg) - At = PSY.get_At(tg) - D_T = PSY.get_D_T(tg) - q_nl = PSY.get_q_nl(tg) - function f!(out, x) + tg = PSY.get_prime_mover(dynamic_device) + #Get Parameters + params = p[:params][:TurbineGov] + R = params[:R] + r = params[:r] + Tr = params[:Tr] + G_min = params[:gate_position_limits][:min] + G_max = params[:gate_position_limits][:max] + At = params[:At] + q_nl = params[:q_nl] + + function f!(out, x, params) P_ref = x[1] x_g1 = x[2] x_g2 = x[3] x_g3 = x[4] x_g4 = x[5] + R = params[:R] + r = params[:r] + Tr = params[:Tr] + At = params[:At] + D_T = params[:D_T] + q_nl = params[:q_nl] c = (1.0 / r) * x_g1 + (1.0 / (r * Tr)) * x_g2 P_in = P_ref - Δω - R * c h = (x_g4 / x_g3)^2 @@ -287,11 +357,18 @@ function initialize_tg!( end P0 = R * (q_nl + τm0 / At) # mechanical power initial guess. It migth be different than electrical output power x0 = [P0, 0, (r * Tr) * P0 / R, P0 / R, P0 / R] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization of Turbine Governor $(PSY.get_name(static)) failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Error if x_g3 is outside PI limits if sol_x0[4] > G_max || sol_x0[4] < G_min error( @@ -300,7 +377,7 @@ function initialize_tg!( end #Update Control Refs PSY.set_P_ref!(tg, sol_x0[1]) - set_P_ref(dynamic_device, sol_x0[1]) + set_P_ref!(p, sol_x0[1]) #Update states tg_ix = get_local_state_ix(dynamic_device, typeof(tg)) tg_states = @view device_states[tg_ix] diff --git a/src/initialization/init_device.jl b/src/initialization/init_device.jl index 76592cf33..8c3729d5e 100644 --- a/src/initialization/init_device.jl +++ b/src/initialization/init_device.jl @@ -2,59 +2,63 @@ function initialize_dynamic_device!( dynamic_device::DynamicWrapper{DynG}, static::PSY.StaticInjection, initial_inner_vars::AbstractVector, + parameters::AbstractVector, + states::AbstractVector, ) where {DynG <: PSY.DynamicGenerator} #Obtain States - device_states = zeros(PSY.get_n_states(dynamic_device)) - #Initialize Machine and Shaft: δ and ω - initialize_mach_shaft!(device_states, static, dynamic_device, initial_inner_vars) + initialize_mach_shaft!(states, parameters, static, dynamic_device, initial_inner_vars) #Initialize extra Shaft states - initialize_shaft!(device_states, static, dynamic_device, initial_inner_vars) + initialize_shaft!(states, parameters, static, dynamic_device, initial_inner_vars) #Initialize AVR - initialize_avr!(device_states, static, dynamic_device, initial_inner_vars) + initialize_avr!(states, parameters, static, dynamic_device, initial_inner_vars) #Initialize TG - initialize_tg!(device_states, static, dynamic_device, initial_inner_vars) + initialize_tg!(states, parameters, static, dynamic_device, initial_inner_vars) #Initialize PSS - initialize_pss!(device_states, static, dynamic_device, initial_inner_vars) - - return device_states + initialize_pss!(states, parameters, static, dynamic_device, initial_inner_vars) + return end function initialize_dynamic_device!( dynamic_device::DynamicWrapper{DynI}, static::PSY.StaticInjection, initial_inner_vars::AbstractVector, + parameters::AbstractVector, + states::AbstractVector, ) where {DynI <: PSY.DynamicInverter} - #Obtain States - device_states = zeros(PSY.get_n_states(dynamic_device)) #Initialize Machine and Shaft: V and I - initialize_filter!(device_states, static, dynamic_device, initial_inner_vars) + initialize_filter!(states, parameters, static, dynamic_device, initial_inner_vars) #Initialize freq estimator initialize_frequency_estimator!( - device_states, + states, + parameters, static, dynamic_device, initial_inner_vars, ) #Initialize OuterLoop - initialize_outer!(device_states, static, dynamic_device, initial_inner_vars) + initialize_outer!(states, parameters, static, dynamic_device, initial_inner_vars) #Initialize DCside - initialize_DCside!(device_states, static, dynamic_device, initial_inner_vars) + initialize_DCside!(states, parameters, static, dynamic_device, initial_inner_vars) #Initialize Converter - initialize_converter!(device_states, static, dynamic_device, initial_inner_vars) + initialize_converter!(states, parameters, static, dynamic_device, initial_inner_vars) #Initialize InnerLoop - initialize_inner!(device_states, static, dynamic_device, initial_inner_vars) - return device_states + initialize_inner!(states, parameters, static, dynamic_device, initial_inner_vars) + return end -function initialize_static_device!(::StaticLoadWrapper) +function initialize_static_device!( + ::StaticLoadWrapper, + local_parameters::AbstractArray{V}, +) where {V <: ACCEPTED_REAL_TYPES} return end function initialize_static_device!( device::StaticWrapper{PSY.Source, T}, -) where {T <: BusCategory} + p::AbstractArray{U}, +) where {T <: BusCategory, U <: ACCEPTED_REAL_TYPES} #PowerFlow Data P0 = PSY.get_active_power(device) Q0 = PSY.get_reactive_power(device) @@ -67,13 +71,13 @@ function initialize_static_device!( I = conj(S0 / V) I_R = real(I) I_I = imag(I) - R_th = PSY.get_R_th(device.device) - X_th = PSY.get_X_th(device.device) - Zmag = R_th^2 + X_th^2 - - function f!(out, x) + params = p[:params] + function f!(out, x, params) V_R_internal = x[1] V_I_internal = x[2] + R_th = params[:R_th] + X_th = params[:X_th] + Zmag = R_th^2 + X_th^2 out[1] = R_th * (V_R_internal - V_R) / Zmag + X_th * (V_I_internal - V_I) / Zmag - I_R @@ -81,18 +85,25 @@ function initialize_static_device!( R_th * (V_I_internal - V_I) / Zmag - X_th * (V_R_internal - V_R) / Zmag - I_I end x0 = [V_R, V_I] - sol = NLsolve.nlsolve(f!, x0) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in Source failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages V_internal = sqrt(sol_x0[1]^2 + sol_x0[2]^2) θ_internal = atan(sol_x0[2], sol_x0[1]) PSY.set_internal_voltage!(device.device, V_internal) PSY.set_internal_angle!(device.device, θ_internal) - set_V_ref(device, PSY.get_internal_voltage(device.device)) - set_θ_ref(device, PSY.get_internal_angle(device.device)) + set_V_ref!(p, PSY.get_internal_voltage(device.device)) + set_θ_ref!(p, PSY.get_internal_angle(device.device)) end return end @@ -100,10 +111,10 @@ end function initialize_dynamic_device!( dynamic_device::DynamicWrapper{PSY.PeriodicVariableSource}, source::PSY.Source, - ::AbstractVector, + initial_inner_vars::AbstractVector, + parameters::AbstractVector, + device_states::AbstractVector, ) - device_states = zeros(PSY.get_n_states(dynamic_device)) - #PowerFlow Data P0 = PSY.get_active_power(source) Q0 = PSY.get_reactive_power(source) @@ -119,7 +130,7 @@ function initialize_dynamic_device!( R_th = PSY.get_R_th(source) X_th = PSY.get_X_th(source) Zmag = R_th^2 + X_th^2 - function f!(out, x) + function f!(out, x, p) V_R_internal = x[1] V_I_internal = x[2] @@ -129,11 +140,18 @@ function initialize_dynamic_device!( R_th * (V_I_internal - V_I) / Zmag - X_th * (V_R_internal - V_R) / Zmag - I_I end x0 = [V_R, V_I] - sol = NLsolve.nlsolve(f!, x0) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, nothing) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in Periodic Variable Source failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages V_internal = sqrt(sol_x0[1]^2 + sol_x0[2]^2) θ_internal = atan(sol_x0[2], sol_x0[1]) @@ -159,11 +177,12 @@ function initialize_dynamic_device!( PSY.set_internal_voltage_bias!(get_device(dynamic_device), V_internal_bias) PSY.set_internal_angle_bias!(get_device(dynamic_device), θ_internal_bias) end - return device_states + return end -function initialize_dynamic_device!(branch::BranchWrapper) - device_states = zeros(PSY.get_n_states(branch)) +function initialize_dynamic_device!(branch::BranchWrapper, + p::AbstractVector, + device_states::AbstractVector) #PowerFlow Data arc = PSY.get_arc(branch) Vm_from = PSY.get_magnitude(PSY.get_from(arc)) @@ -174,8 +193,8 @@ function initialize_dynamic_device!(branch::BranchWrapper) V_I_from = Vm_from * sin(θ_from) V_R_to = Vm_to * cos(θ_to) V_I_to = Vm_to * sin(θ_to) - R = PSY.get_r(branch) - X = PSY.get_x(branch) + R = p[:params][:r] + X = p[:params][:x] Zmag_sq = R^2 + X^2 #Compute Current I_R = R * (V_R_from - V_R_to) / Zmag_sq + X * (V_I_from - V_I_to) / Zmag_sq @@ -183,31 +202,28 @@ function initialize_dynamic_device!(branch::BranchWrapper) #Update Current device_states[1] = I_R device_states[2] = I_I - return device_states + return end function initialize_dynamic_device!( dynamic_wrapper::DynamicWrapper{PSY.SingleCageInductionMachine}, device::PSY.StaticInjection, ::AbstractVector, + p::AbstractVector, + device_states::AbstractVector, ) Sbase = get_system_base_power(dynamic_wrapper) - #Obtain States - device_states = zeros(PSY.get_n_states(dynamic_wrapper)) - # Get parameters dynamic_device = get_device(dynamic_wrapper) - R_s = PSY.get_R_s(dynamic_device) - X_ls = PSY.get_X_ls(dynamic_device) - R_r = PSY.get_R_r(dynamic_device) - X_lr = PSY.get_X_lr(dynamic_device) - A = PSY.get_A(dynamic_device) - B = PSY.get_B(dynamic_device) - C = PSY.get_C(dynamic_device) - base_power = PSY.get_base_power(dynamic_device) - X_ad = PSY.get_X_ad(dynamic_device) - X_aq = PSY.get_X_aq(dynamic_device) + params = @view(p[:params]) + R_s = params[:R_s] + X_ls = params[:X_ls] + X_lr = params[:X_lr] + base_power = params[:base_power] + τ_m0 = params[:τ_ref] + X_ad = params[:X_ad] + X_aq = params[:X_aq] #PowerFlow Data if isa(device, PSY.StandardLoad) @@ -245,8 +261,7 @@ function initialize_dynamic_device!( τ_m00 = P0 / ωr0 x0 = [i_qs0, i_ds0, B_sh0, ψ_qs0, ψ_ds0, ψ_qr0, ψ_dr0, ωr0, τ_m00] - # - function f!(out, x) + function f!(out, x, params) i_qs = x[1] i_ds = x[2] B_sh = x[3] @@ -256,6 +271,17 @@ function initialize_dynamic_device!( ψ_dr = x[7] ωr = x[8] τ_m0 = x[9] + + R_s = params[:R_s] + R_r = params[:R_r] + X_ls = params[:X_ls] + X_lr = params[:X_lr] + A = params[:A] + B = params[:B] + C = params[:C] + X_ad = params[:X_ad] + X_aq = params[:X_aq] + ψ_mq = ψ_qs - i_qs * X_ls ψ_md = ψ_ds - i_ds * X_ls out[1] = -I_R + i_ds - V_I * B_sh # network interface @@ -268,47 +294,50 @@ function initialize_dynamic_device!( out[8] = (1.0 - ωr) * ψ_qr + R_r / X_lr * (ψ_md - ψ_dr) # dψ_dr/dt = 0 out[9] = ψ_ds * i_qs - ψ_qs * i_ds - τ_m0 * (A * ωr^2 + B * ωr + C) # dωr/dt = 0 end - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in Ind. Motor $(PSY.get_name(device)) failed") else - sol_x0 = sol.zero + sol_x0 = sol.u device_states[1] = sol_x0[4] # ψ_qs device_states[2] = sol_x0[5] # ψ_ds device_states[3] = sol_x0[6] # ψ_qr device_states[4] = sol_x0[7] # ψ_dr device_states[5] = sol_x0[8] # ωr # update τ_ref and B_sh + params[:B_shunt] = sol_x0[3] # B_sh PSY.set_B_shunt!(dynamic_device, sol_x0[3]) # B_sh - #set_B_shunt(dynamic_device, sol_x0[3]) # B_sh - PSY.set_τ_ref!(dynamic_device, sol_x0[9]) # τ_m0 - set_P_ref(dynamic_wrapper, sol_x0[9]) # τ_m0 + params[:τ_ref] = sol_x0[9] # τ_ref + PSY.set_τ_ref!(dynamic_device, sol_x0[9]) # τ_ref + set_P_ref!(p, sol_x0[9]) # τ_m0 end - return device_states + return end function initialize_dynamic_device!( dynamic_wrapper::DynamicWrapper{PSY.SimplifiedSingleCageInductionMachine}, device::PSY.StaticInjection, ::AbstractVector, + p::AbstractVector, + device_states::AbstractVector, ) Sbase = get_system_base_power(dynamic_wrapper) - #Obtain States - device_states = zeros(PSY.get_n_states(dynamic_wrapper)) - - # Get parameters dynamic_device = get_device(dynamic_wrapper) - R_s = PSY.get_R_s(dynamic_device) - X_m = PSY.get_X_m(dynamic_device) - R_r = PSY.get_R_r(dynamic_device) - A = PSY.get_A(dynamic_device) - B = PSY.get_B(dynamic_device) - C = PSY.get_C(dynamic_device) - base_power = PSY.get_base_power(dynamic_device) - X_ss = PSY.get_X_ss(dynamic_device) - X_rr = PSY.get_X_rr(dynamic_device) - X_p = PSY.get_X_p(dynamic_device) + #Get parameters + params = @view(p[:params]) + R_s = params[:R_s] + X_m = params[:X_m] + base_power = params[:base_power] + X_rr = params[:X_rr] + X_p = params[:X_p] #PowerFlow Data if isa(device, PSY.StandardLoad) @@ -346,8 +375,7 @@ function initialize_dynamic_device!( τ_m00 = P0 / ωr0 x0 = [i_qs0, i_ds0, i_qr0, i_dr0, B_sh0, ψ_qs0, ψ_ds0, ψ_qr0, ψ_dr0, ωr0, τ_m00] - # - function f!(out, x) + function f!(out, x, params) i_qs = x[1] i_ds = x[2] i_qr = x[3] @@ -360,6 +388,15 @@ function initialize_dynamic_device!( ωr = x[10] τ_m0 = x[11] + R_s = params[:R_s] + R_r = params[:R_r] + X_m = params[:X_m] + A = params[:A] + B = params[:B] + C = params[:C] + X_ss = params[:X_ss] + X_rr = params[:X_rr] + out[1] = -I_R + i_ds - V_I * B_sh # network interface out[2] = -I_I + i_qs + V_R * B_sh # network interface out[3] = v_qs - ψ_ds - R_s * i_qs # dψ_qs/dt = 0 @@ -372,19 +409,27 @@ function initialize_dynamic_device!( out[10] = (1.0 - ωr) * ψ_qr - R_r * i_dr # dψ_dr/dt = 0 out[11] = ψ_qr * i_dr - ψ_dr * i_qr - τ_m0 * (A * ωr^2 + B * ωr + C) # dωr/dt = 0 end - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in Ind. Motor $(PSY.get_name(device)) failed") else - sol_x0 = sol.zero + sol_x0 = sol.u device_states[1] = sol_x0[8] # ψ_qr device_states[2] = sol_x0[9] # ψ_dr device_states[3] = sol_x0[10] # ωr # update τ_ref and B_sh PSY.set_B_shunt!(dynamic_device, sol_x0[5]) # B_sh - #set_B_shunt(dynamic_device, sol_x0[5]) # B_sh + params[:B_shunt] = sol_x0[5] # B_sh PSY.set_τ_ref!(dynamic_device, sol_x0[11]) # τ_m0 - set_P_ref(dynamic_wrapper, sol_x0[11]) # τ_m0 + params[:τ_ref] = sol_x0[11] # τ_m0 + set_P_ref!(p, sol_x0[11]) # τ_m0 end return device_states end @@ -393,27 +438,20 @@ function initialize_dynamic_device!( dynamic_wrapper::DynamicWrapper{PSY.CSVGN1}, device::PSY.StaticInjection, ::AbstractVector, + p::AbstractVector, + device_states::AbstractVector, ) Sbase = get_system_base_power(dynamic_wrapper) - - # Obtain States - device_states = zeros(PSY.get_n_states(dynamic_wrapper)) - # Get parameters - dynamic_device = get_device(dynamic_wrapper) - K = PSY.get_K(dynamic_device) - T1 = PSY.get_T1(dynamic_device) - T2 = PSY.get_T2(dynamic_device) - T3 = PSY.get_T3(dynamic_device) - T4 = PSY.get_T4(dynamic_device) - T5 = PSY.get_T5(dynamic_device) - Rmin = PSY.get_Rmin(dynamic_device) - Vmax = PSY.get_Vmax(dynamic_device) - Vmin = PSY.get_Vmin(dynamic_device) - Cbase = PSY.get_CBase(dynamic_device) + params = p[:params] + K = params[:K] + Rmin = params[:Rmin] + Vmax = params[:Vmax] + Vmin = params[:Vmin] + Cbase = params[:CBase] + Mbase = params[:base_power] # FIXME: base_power is changed to system's base_power when a CSVGN1 is attached to a Source using add_component!() # Temporarily, to avoid that, set_dynamic_injector!() could be used - Mbase = PSY.get_base_power(dynamic_device) Rbase = Mbase # PowerFlow Data @@ -425,7 +463,7 @@ function initialize_dynamic_device!( V_ref0 = V_abs - (Cbase / Sbase - Y) * 1 / K * Sbase / Mbase # update V_ref - set_V_ref(dynamic_wrapper, V_ref0) + set_V_ref!(p, V_ref0) thy = K * (V_abs - V_ref0) vr2 = thy @@ -437,7 +475,6 @@ function initialize_dynamic_device!( if (vr2 > 1) || (vr2 < Rmin / Rbase) @error("Regulator state vr2 = $(vr2) outside the limits") end - device_states[1] = K * (V_abs - V_ref0) # thy device_states[2] = 0.0 # vr1 device_states[3] = K * (V_abs - V_ref0) # vr2 @@ -449,23 +486,22 @@ function initialize_dynamic_device!( dynamic_wrapper::DynamicWrapper{PSY.ActiveConstantPowerLoad}, device::PSY.StaticInjection, ::AbstractVector, + p::AbstractVector, + device_states::AbstractVector, ) Sbase = get_system_base_power(dynamic_wrapper) - #Obtain States - device_states = zeros(PSY.get_n_states(dynamic_wrapper)) - - # Get parameters dynamic_device = get_device(dynamic_wrapper) - r_load = PSY.get_r_load(dynamic_device) - rf = PSY.get_rf(dynamic_device) - lf = PSY.get_lf(dynamic_device) - cf = PSY.get_cf(dynamic_device) - rg = PSY.get_rg(dynamic_device) - lg = PSY.get_lg(dynamic_device) - kiv = PSY.get_kiv(dynamic_device) - kic = PSY.get_kic(dynamic_device) - base_power = PSY.get_base_power(dynamic_device) + + #Get parameters + params = p[:params] + r_load = params[:r_load] + rf = params[:rf] + lf = params[:lf] + cf = params[:cf] + rg = params[:rg] + lg = params[:lg] + base_power = params[:base_power] #PowerFlow Data if isa(device, PSY.StandardLoad) @@ -502,12 +538,15 @@ function initialize_dynamic_device!( V_ref0 = sqrt(P_cnv0 * r_load) x0 = [θ_pll0, 0.0, 0.0, 0.0, 0.0] - function f!(out, x) + function f!(out, x, params) θ_pll = x[1] η = x[2] γd = x[3] γq = x[4] Iq_ref = x[5] + lf = params[:lf] + kiv = params[:kiv] + kic = params[:kic] V_dq_pll = ri_dq(θ_pll + pi / 2) * [Vr_filter0; Vi_filter0] I_dq_cnv = ri_dq(θ_pll + pi / 2) * [Ir_cnv0; Ii_cnv0] @@ -525,11 +564,18 @@ function initialize_dynamic_device!( out[4] = V_dq_cnv0[q] - Vq_cnv out[5] = V_dq_cnv0[d] - Vd_cnv end - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in Active Load $(PSY.get_name(device)) failed") else - sol_x0 = sol.zero + sol_x0 = sol.u device_states[1] = sol_x0[1] # θ_pll device_states[2] = 0.0 # ϵ device_states[3] = sol_x0[2] # η @@ -545,8 +591,8 @@ function initialize_dynamic_device!( # update V_ref PSY.set_V_ref!(dynamic_device, V_ref0) - set_V_ref(dynamic_wrapper, V_ref0) - set_Q_ref(dynamic_wrapper, sol_x0[5]) + set_V_ref!(p, V_ref0) + set_Q_ref!(p, sol_x0[5]) end return device_states end @@ -554,11 +600,11 @@ end function initialize_dynamic_device!( dynamic_wrapper::DynamicWrapper{PSY.AggregateDistributedGenerationA}, static::PSY.StaticInjection, - initial_inner_vars::AbstractVector, + ::AbstractVector, + p::AbstractVector, + device_states::AbstractVector, ) - device_states = zeros(PSY.get_n_states(dynamic_wrapper)) dynamic_device = get_device(dynamic_wrapper) - #Get PowerFlow Data P0 = PSY.get_active_power(static) Q0 = PSY.get_reactive_power(static) @@ -629,7 +675,7 @@ function initialize_dynamic_device!( #See Note 2 on PSSE Documentation Vref0 = PSY.get_V_ref(dynamic_device) - K_qv = PSY.get_K_qv(dynamic_device) + K_qv = p[:params][:K_qv] (dbd1, dbd2) = PSY.get_dbd_pnts(dynamic_device) if Vref0 == 0.0 Vref = Vmeas @@ -639,12 +685,12 @@ function initialize_dynamic_device!( Vref = Vmeas end - set_P_ref(dynamic_wrapper, Pref) + set_P_ref!(p, Pref) PSY.set_P_ref!(dynamic_device, Pref) - set_Q_ref(dynamic_wrapper, Qref) - set_V_ref(dynamic_wrapper, Vref) - set_ω_ref(dynamic_wrapper, Freq_ref) + set_Q_ref!(p, Qref) + set_V_ref!(p, Vref) + set_ω_ref!(p, Freq_ref) PSY.set_Pfa_ref!(dynamic_device, pfaref) - - return device_states + @view(p[:params])[:Pfa_ref] = pfaref + return end diff --git a/src/initialization/inverter_components/init_DCside.jl b/src/initialization/inverter_components/init_DCside.jl index a148e3c90..c1ee9439f 100644 --- a/src/initialization/inverter_components/init_DCside.jl +++ b/src/initialization/inverter_components/init_DCside.jl @@ -1,5 +1,6 @@ function initialize_DCside!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{C, O, IC, PSY.FixedDCSource, P, F, L}, @@ -13,8 +14,7 @@ function initialize_DCside!( F <: PSY.Filter, L <: Union{Nothing, PSY.OutputCurrentLimiter}, } - #Update inner_vars - inner_vars[Vdc_var] = PSY.get_voltage(PSY.get_dc_source(dynamic_device)) + inner_vars[Vdc_var] = p[:params][:DCSource][:voltage] return end diff --git a/src/initialization/inverter_components/init_converter.jl b/src/initialization/inverter_components/init_converter.jl index 1667a38b2..65d0ecc53 100644 --- a/src/initialization/inverter_components/init_converter.jl +++ b/src/initialization/inverter_components/init_converter.jl @@ -1,5 +1,6 @@ function initialize_converter!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{PSY.AverageConverter, O, IC, DC, P, F, L}, @@ -16,6 +17,7 @@ function initialize_converter!( function initialize_converter!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{PSY.RenewableEnergyConverterTypeA, O, IC, DC, P, F, L}, @@ -42,12 +44,12 @@ function initialize_converter!( #Reference Transformation Ip = Ip_external * cos(-θ) - Iq_external * sin(-θ) Iq = Ip_external * sin(-θ) + Iq_external * cos(-θ) - converter = PSY.get_converter(dynamic_device) - Io_lim = PSY.get_Io_lim(converter) - Vo_lim = PSY.get_Vo_lim(converter) - # Lv_pnt0 is unused in the initialization - _, Lv_pnt1 = PSY.get_Lv_pnts(converter) + #Get Converter parameters + params = p[:params][:Converter] + Vo_lim = params[:Vo_lim] + Lv_pnt1 = params[:Lv_pnts][:max] + Io_lim = params[:Io_lim] if (Iq < Io_lim) || (V_t > Vo_lim) || (V_t < Lv_pnt1) @error( @@ -71,6 +73,7 @@ end function initialize_converter!( device_states, + device_parameters, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{PSY.RenewableEnergyVoltageConverterTypeA, O, IC, DC, P, F, L}, diff --git a/src/initialization/inverter_components/init_filter.jl b/src/initialization/inverter_components/init_filter.jl index 77b7be634..273d0a8cd 100644 --- a/src/initialization/inverter_components/init_filter.jl +++ b/src/initialization/inverter_components/init_filter.jl @@ -1,5 +1,6 @@ function initialize_filter!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicInverter{C, O, IC, DC, P, PSY.LCLFilter, L}}, inner_vars::AbstractVector, @@ -24,19 +25,8 @@ function initialize_filter!( Ir_filter = real(I) Ii_filter = imag(I) - #Get Parameters - filter = PSY.get_filter(dynamic_device) - lf = PSY.get_lf(filter) - rf = PSY.get_rf(filter) - cf = PSY.get_cf(filter) - lg = PSY.get_lg(filter) - rg = PSY.get_rg(filter) - - #Set parameters - ω_sys = get_ω_ref(dynamic_device) - #To solve Vr_cnv, Vi_cnv, Ir_cnv, Ii_cnv, Vr_filter, Vi_filter - function f!(out, x) + function f!(out, x, p) Vr_cnv = x[1] Vi_cnv = x[2] Ir_cnv = x[3] @@ -44,6 +34,16 @@ function initialize_filter!( Vr_filter = x[5] Vi_filter = x[6] + #Get Parameters + params = p[:params][:Filter] + lf = params[:lf] + rf = params[:rf] + cf = params[:cf] + lg = params[:lg] + rg = params[:rg] + #Set parameters + ω_sys = p[:refs][:ω_ref] + #𝜕Ir_cnv/𝜕t out[1] = Vr_cnv - Vr_filter - rf * Ir_cnv + ω_sys * lf * Ii_cnv #𝜕Ii_cnv/𝜕t @@ -58,11 +58,20 @@ function initialize_filter!( out[6] = Vi_filter - V_I - rg * Ii_filter - ω_sys * lg * Ir_filter end x0 = [V_R, V_I, Ir_filter, Ii_filter, V_R, V_I] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Filter failed $(PSY.get_name(static))") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, p) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization in Filter failed $(PSY.get_name(static))" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update terminal voltages inner_vars[Vr_inv_var] = V_R inner_vars[Vi_inv_var] = V_I @@ -92,11 +101,21 @@ end function initialize_filter!( device_states, + p, static::PSY.StaticInjection, - dynamic_device::DynamicWrapper{PSY.DynamicInverter{C, O, IC, DC, P, PSY.RLFilter, L}}, + dynamic_device::DynamicWrapper{ + PSY.DynamicInverter{ + PSY.RenewableEnergyConverterTypeA, + O, + IC, + DC, + P, + PSY.RLFilter, + L, + }, + }, inner_vars::AbstractVector, ) where { - C <: PSY.Converter, O <: PSY.OuterControl, IC <: PSY.InnerControl, DC <: PSY.DCSource, @@ -120,12 +139,12 @@ function initialize_filter!( I_I = imag(I) #Get Parameters - filt = PSY.get_filter(dynamic_device) - rf = PSY.get_rf(filt) - lf = PSY.get_lf(filt) - converter = PSY.get_converter(dynamic_device) - R_source = PSY.get_R_source(converter) - X_source = PSY.get_X_source(converter) + params = p[:params][:Filter] + rf = params[:rf] + lf = params[:lf] + + R_source = p[:params][:Converter][:R_source] + X_source = p[:params][:Converter][:X_source] #Update terminal voltages inner_vars[Vr_inv_var] = V_R diff --git a/src/initialization/inverter_components/init_frequency_estimator.jl b/src/initialization/inverter_components/init_frequency_estimator.jl index b48347556..a33bcc7ee 100644 --- a/src/initialization/inverter_components/init_frequency_estimator.jl +++ b/src/initialization/inverter_components/init_frequency_estimator.jl @@ -1,5 +1,6 @@ function initialize_frequency_estimator!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{PSY.DynamicInverter{C, O, IC, DC, PSY.KauraPLL, F, L}}, inner_vars::AbstractVector, @@ -15,9 +16,8 @@ function initialize_frequency_estimator!( Vi_filter = inner_vars[Vi_filter_var] #Get parameters - pll_control = PSY.get_freq_estimator(dynamic_device) - kp_pll = PSY.get_kp_pll(pll_control) - ki_pll = PSY.get_ki_pll(pll_control) + params = p[:params][:FrequencyEstimator] + ω_ref = p[:refs][:ω_ref] #Get initial guess θ0_pll = atan(Vi_filter, Vr_filter) @@ -25,11 +25,13 @@ function initialize_frequency_estimator!( Vpll_q0 = 0.0 ϵ_pll0 = 0.0 - function f!(out, x) + function f!(out, x, params) vpll_d = x[1] vpll_q = x[2] ϵ_pll = x[3] θ_pll = x[4] + kp_pll = params[:kp_pll] + ki_pll = params[:ki_pll] V_dq_pll = ri_dq(θ_pll + pi / 2) * [Vr_filter; Vi_filter] @@ -41,11 +43,18 @@ function initialize_frequency_estimator!( end x0 = [Vpll_d0, Vpll_q0, ϵ_pll0, θ0_pll] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in PLL failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.KauraPLL) @@ -58,13 +67,14 @@ function initialize_frequency_estimator!( pll_states[4] = sol_x0[4] #Update guess of frequency estimator - inner_vars[ω_freq_estimator_var] = get_ω_ref(dynamic_device) + inner_vars[ω_freq_estimator_var] = ω_ref inner_vars[θ_freq_estimator_var] = sol_x0[4] end end function initialize_frequency_estimator!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{C, O, IC, DC, PSY.ReducedOrderPLL, F, L}, @@ -82,20 +92,20 @@ function initialize_frequency_estimator!( Vi_filter = inner_vars[Vi_filter_var] #Get parameters - pll_control = PSY.get_freq_estimator(dynamic_device) - kp_pll = PSY.get_kp_pll(pll_control) - ki_pll = PSY.get_ki_pll(pll_control) + params = p[:params][:FrequencyEstimator] #Get initial guess θ0_pll = atan(Vi_filter, Vr_filter) Vpll_q0 = 0.0 ϵ_pll0 = 0.0 - function f!(out, x) + function f!(out, x, params) vpll_q = x[1] ϵ_pll = x[2] θ_pll = x[3] + kp_pll = params[:kp_pll] + ki_pll = params[:ki_pll] V_dq_pll = ri_dq(θ_pll + pi / 2) * [Vr_filter; Vi_filter] out[1] = V_dq_pll[q] - vpll_q @@ -104,11 +114,18 @@ function initialize_frequency_estimator!( end x0 = [Vpll_q0, ϵ_pll0, θ0_pll] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in PLL failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.ReducedOrderPLL) @@ -120,7 +137,7 @@ function initialize_frequency_estimator!( pll_states[3] = sol_x0[3] #Update guess of frequency estimator - inner_vars[ω_freq_estimator_var] = get_ω_ref(dynamic_device) + inner_vars[ω_freq_estimator_var] = p[:refs][:ω_ref] inner_vars[θ_freq_estimator_var] = sol_x0[3] end return @@ -128,6 +145,7 @@ end function initialize_frequency_estimator!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{C, O, IC, DC, PSY.FixedFrequency, F, L}, @@ -142,8 +160,7 @@ function initialize_frequency_estimator!( L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Get parameters - pll_control = PSY.get_freq_estimator(dynamic_device) - frequency = PSY.get_frequency(pll_control) + frequency = p[:params][:FrequencyEstimator][:frequency] #Update guess of frequency estimator inner_vars[ω_freq_estimator_var] = frequency diff --git a/src/initialization/inverter_components/init_inner.jl b/src/initialization/inverter_components/init_inner.jl index 5b64741d8..17f919d23 100644 --- a/src/initialization/inverter_components/init_inner.jl +++ b/src/initialization/inverter_components/init_inner.jl @@ -1,5 +1,6 @@ function initialize_inner!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{C, O, PSY.VoltageModeControl, DC, P, F, L}, @@ -24,7 +25,7 @@ function initialize_inner!( Vi_filter = device_states[external_ix[6]] #Obtain inner variables for component - ω_oc = get_ω_ref(dynamic_device) + ω_oc = p[:refs][:ω_ref] θ0_oc = inner_vars[θ_oc_var] Vdc = inner_vars[Vdc_var] @@ -33,24 +34,10 @@ function initialize_inner!( Vi_cnv0 = inner_vars[Vi_cnv_var] #Get Voltage Controller parameters - inner_control = PSY.get_inner_control(dynamic_device) - filter = PSY.get_filter(dynamic_device) - kpv = PSY.get_kpv(inner_control) - kiv = PSY.get_kiv(inner_control) - kffi = PSY.get_kffi(inner_control) - cf = PSY.get_cf(filter) - rv = PSY.get_rv(inner_control) - lv = PSY.get_lv(inner_control) - - #Get Current Controller parameters - kpc = PSY.get_kpc(inner_control) - kic = PSY.get_kic(inner_control) - kffv = PSY.get_kffv(inner_control) - lf = PSY.get_lf(filter) - ωad = PSY.get_ωad(inner_control) - kad = PSY.get_kad(inner_control) - - function f!(out, x) + params = p[:params][:InnerControl] + cf = p[:params][:Filter][:cf] + lf = p[:params][:Filter][:lf] + function f!(out, x, params) θ_oc = x[1] v_refr = x[2] ξ_d = x[3] @@ -60,6 +47,16 @@ function initialize_inner!( ϕ_d = x[7] ϕ_q = x[8] + kpv = params[:kpv] + kiv = params[:kiv] + kffv = params[:kffv] + rv = params[:rv] + lv = params[:lv] + kpc = params[:kpc] + kic = params[:kic] + kffi = params[:kffi] + kad = params[:kad] + #Reference Frame Transformations I_dq_filter = ri_dq(θ_oc + pi / 2) * [Ir_filter; Ii_filter] I_dq_cnv = ri_dq(θ_oc + pi / 2) * [Ir_cnv; Ii_cnv] @@ -104,11 +101,18 @@ function initialize_inner!( out[8] = Vq_cnv_ref - V_dq_cnv0[q] end x0 = [θ0_oc, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) @warn("Initialization in Inner Control failed") else - sol_x0 = sol.zero + sol_x0 = sol.u #Update angle: inner_vars[θ_oc_var] = sol_x0[1] outer_ix = get_local_state_ix(dynamic_device, O) @@ -116,7 +120,7 @@ function initialize_inner!( #Assumes that angle is in second position outer_states[1] = sol_x0[1] inner_vars[θ_oc_var] = sol_x0[1] - set_V_ref(dynamic_device, sol_x0[2]) + set_V_ref!(p, sol_x0[2]) PSY.set_V_ref!( PSY.get_reactive_power_control(PSY.get_outer_control(dynamic_device)), sol_x0[2], @@ -145,6 +149,7 @@ end function initialize_inner!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{C, O, PSY.CurrentModeControl, DC, P, F, L}, @@ -168,7 +173,7 @@ function initialize_inner!( Vi_filter = device_states[external_ix[6]] #Obtain inner variables for component - ω_oc = get_ω_ref(dynamic_device) + ω_oc = p[:refs][:ω_ref] θ0_oc = inner_vars[θ_freq_estimator_var] Vdc = inner_vars[Vdc_var] Id_cnv_ref = inner_vars[Id_oc_var] @@ -179,17 +184,15 @@ function initialize_inner!( Vi_cnv0 = inner_vars[Vi_cnv_var] #Get Current Controller parameters - inner_control = PSY.get_inner_control(dynamic_device) - filter = PSY.get_filter(dynamic_device) - kpc = PSY.get_kpc(inner_control) - kic = PSY.get_kic(inner_control) - kffv = PSY.get_kffv(inner_control) - lf = PSY.get_lf(filter) - - function f!(out, x) + kpc = p[:params][:InnerControl][:kpc] + kic = p[:params][:InnerControl][:kic] + kffv = p[:params][:InnerControl][:kffv] + lf = p[:params][:Filter][:lf] + params = [kpc, kic, kffv, lf] + function f!(out, x, params) γ_d = x[1] γ_q = x[2] - + kpc, kic, kffv, lf = params #Reference Frame Transformations I_dq_cnv = ri_dq(θ0_oc + pi / 2) * [Ir_cnv; Ii_cnv] V_dq_filter = ri_dq(θ0_oc + pi / 2) * [Vr_filter; Vi_filter] @@ -211,11 +214,20 @@ function initialize_inner!( out[2] = Vq_cnv_ref - V_dq_cnv0[q] end x0 = [0.0, 0.0] - sol = NLsolve.nlsolve(f!, x0; ftol = STRICT_NLSOLVE_F_TOLERANCE) - if !NLsolve.converged(sol) - @warn("Initialization in Inner Control failed") + prob = NonlinearSolve.NonlinearProblem{true}(f!, x0, params) + sol = NonlinearSolve.solve( + prob, + NonlinearSolve.TrustRegion(); + sensealg = SciMLSensitivity.SteadyStateAdjoint(), + reltol = STRICT_NLSOLVE_F_TOLERANCE, + abstol = STRICT_NLSOLVE_F_TOLERANCE, + ) + if !SciMLBase.successful_retcode(sol) + @warn( + "Initialization of AVR in $(PSY.get_name(static)) failed" + ) else - sol_x0 = sol.zero + sol_x0 = sol.u #Update Converter modulation m0_dq = (ri_dq(θ0_oc + pi / 2) * [Vr_cnv0; Vi_cnv0]) ./ Vdc inner_vars[md_var] = m0_dq[d] @@ -231,6 +243,7 @@ end function initialize_inner!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{C, O, PSY.RECurrentControlB, DC, P, F, L}, @@ -256,6 +269,9 @@ function initialize_inner!( inner_control = PSY.get_inner_control(dynamic_device) Q_Flag = PSY.get_Q_Flag(inner_control) PQ_Flag = PSY.get_PQ_Flag(inner_control) + params = @view(@view(p[:params])[:InnerControl]) + V_ref0 = params[:V_ref0] + K_vi = params[:K_vi] Ip_min, Ip_max, Iq_min, Iq_max = current_limit_logic(inner_control, Val(PQ_Flag), V_t, Ip_oc, Iq_cmd) @@ -290,8 +306,8 @@ function initialize_inner!( #Update additional variables # Based on PSS/E manual, if user does not provide V_ref0, then # V_ref0 is considered to be the output voltage of the PF solution - if PSY.get_V_ref0(inner_control) == 0.0 - PSY.set_V_ref0!(inner_control, V_t) + if V_ref0 == 0.0 + params[:V_ref0] = V_t end return end diff --git a/src/initialization/inverter_components/init_outer.jl b/src/initialization/inverter_components/init_outer.jl index 1c5d49d0e..df528ae69 100644 --- a/src/initialization/inverter_components/init_outer.jl +++ b/src/initialization/inverter_components/init_outer.jl @@ -1,5 +1,6 @@ function initialize_outer!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{ @@ -36,6 +37,7 @@ function initialize_outer!( Vi_cnv = inner_vars[Vi_cnv_var] θ0_oc = atan(Vi_cnv, Vr_cnv) + ω_ref = p[:refs][:ω_ref] #Obtain additional expressions p_elec_out = Ir_filter * Vr_filter + Ii_filter * Vi_filter q_elec_out = -Ii_filter * Vr_filter + Ir_filter * Vi_filter @@ -49,24 +51,25 @@ function initialize_outer!( ) outer_states = @view device_states[outer_ix] outer_states[1] = θ0_oc #θ_oc - outer_states[2] = get_ω_ref(dynamic_device) #ω + outer_states[2] = ω_ref outer_states[3] = q_elec_out #qm #Update inner vars inner_vars[θ_oc_var] = θ0_oc - inner_vars[ω_oc_var] = get_ω_ref(dynamic_device) - #Update Q_ref. Initialization assumes q_ref = q_elec_out of PF solution - set_P_ref(dynamic_device, p_elec_out) + inner_vars[ω_oc_var] = ω_ref + set_P_ref!(p, p_elec_out) PSY.set_P_ref!( PSY.get_active_power_control(PSY.get_outer_control(dynamic_device)), p_elec_out, ) - set_Q_ref(dynamic_device, q_elec_out) + #Update Q_ref. Initialization assumes q_ref = q_elec_out of PF solution + set_Q_ref!(p, q_elec_out) return end function initialize_outer!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{ @@ -121,18 +124,19 @@ function initialize_outer!( #Update inner vars inner_vars[θ_oc_var] = θ0_oc - inner_vars[ω_oc_var] = get_ω_ref(dynamic_device) + inner_vars[ω_oc_var] = p[:refs][:ω_ref] #Update Q_ref. Initialization assumes q_ref = q_elec_out of PF solution - set_P_ref(dynamic_device, p_elec_out) + set_P_ref!(p, p_elec_out) PSY.set_P_ref!( PSY.get_active_power_control(PSY.get_outer_control(dynamic_device)), p_elec_out, ) - set_Q_ref(dynamic_device, q_elec_out) + set_Q_ref!(p, q_elec_out) end function initialize_outer!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{ @@ -186,18 +190,19 @@ function initialize_outer!( #Update inner vars inner_vars[θ_oc_var] = θ0_oc - inner_vars[ω_oc_var] = get_ω_ref(dynamic_device) + inner_vars[ω_oc_var] = p[:refs][:ω_ref] #Update Q_ref. Initialization assumes q_ref = q_elec_out of PF solution - set_P_ref(dynamic_device, p_elec_out) + set_P_ref!(p, p_elec_out) PSY.set_P_ref!( PSY.get_active_power_control(PSY.get_outer_control(dynamic_device)), p_elec_out, ) - set_Q_ref(dynamic_device, q_elec_out) + set_Q_ref!(p, q_elec_out) end function initialize_outer!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{ @@ -239,12 +244,8 @@ function initialize_outer!( q_elec_out = -Ii_filter * Vr_filter + Ir_filter * Vi_filter #Get Outer Controller parameters - outer_control = PSY.get_outer_control(dynamic_device) - active_power_control = PSY.get_active_power_control(outer_control) - Ki_p = PSY.get_Ki_p(active_power_control) #Integral Gain - reactive_power_control = PSY.get_reactive_power_control(outer_control) - Ki_q = PSY.get_Ki_q(reactive_power_control) #Integral Gain - + Ki_p = p[:params][:OuterControl][:ActivePowerControl][:Ki_p] + Ki_q = p[:params][:OuterControl][:ReactivePowerControl][:Ki_q] #Update inner_vars inner_vars[P_ES_var] = p_elec_out #Update states @@ -260,16 +261,16 @@ function initialize_outer!( #Update inner vars inner_vars[θ_oc_var] = θ0_oc - inner_vars[ω_oc_var] = get_ω_ref(dynamic_device) + inner_vars[ω_oc_var] = p[:refs][:ω_ref] inner_vars[Id_oc_var] = I_dq_cnv[d] inner_vars[Iq_oc_var] = I_dq_cnv[q] #Update Q_ref. Initialization assumes q_ref = q_elec_out from PF solution - set_P_ref(dynamic_device, p_elec_out) + set_P_ref!(p, p_elec_out) PSY.set_P_ref!( PSY.get_active_power_control(PSY.get_outer_control(dynamic_device)), p_elec_out, ) - set_Q_ref(dynamic_device, q_elec_out) + set_Q_ref!(p, q_elec_out) PSY.set_Q_ref!( PSY.get_reactive_power_control(PSY.get_outer_control(dynamic_device)), q_elec_out, @@ -279,6 +280,7 @@ end function initialize_outer!( device_states, + p, static::PSY.StaticInjection, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{ @@ -322,7 +324,7 @@ function initialize_outer!( ## Set references Vm = V_t PSY.set_Q_ref!(PSY.get_converter(dynamic_device), q_elec_out) - set_Q_ref(dynamic_device, q_elec_out) + set_Q_ref!(p, q_elec_out) PSY.set_Q_ref!( PSY.get_reactive_power_control(PSY.get_outer_control(dynamic_device)), q_elec_out, @@ -331,17 +333,18 @@ function initialize_outer!( PSY.get_active_power_control(PSY.get_outer_control(dynamic_device)), p_elec_out, ) - set_P_ref(dynamic_device, p_elec_out) + set_P_ref!(p, p_elec_out) PSY.set_V_ref!( PSY.get_reactive_power_control(PSY.get_outer_control(dynamic_device)), Vm, ) - set_V_ref(dynamic_device, Vm) + set_V_ref!(p, Vm) #Get Outer Controller parameters - q_ref = get_Q_ref(dynamic_device) + q_ref = p[:refs][:Q_ref] outer_control = PSY.get_outer_control(dynamic_device) active_power_control = PSY.get_active_power_control(outer_control) + reactive_power_control = PSY.get_reactive_power_control(outer_control) Freq_Flag = PSY.get_Freq_Flag(active_power_control) #Frequency Flag #Set state counter for variable number of states due to flags @@ -360,9 +363,17 @@ function initialize_outer!( ) internal_states = @view device_states[local_ix] + #Get parameters OuterControl + K_ig = p[:params][:OuterControl][:ActivePowerControl][:K_ig] + T_p = p[:params][:OuterControl][:ActivePowerControl][:T_p] + K_i = p[:params][:OuterControl][:ReactivePowerControl][:K_i] + R_c = p[:params][:OuterControl][:ReactivePowerControl][:R_c] + X_c = p[:params][:OuterControl][:ReactivePowerControl][:X_c] + K_c = p[:params][:OuterControl][:ReactivePowerControl][:K_c] + T_p = p[:params][:OuterControl][:ReactivePowerControl][:T_p] + K_qi = p[:params][:OuterControl][:ReactivePowerControl][:K_qi] + if Freq_Flag == 1 - #Obtain Parameters - K_ig = PSY.get_K_ig(active_power_control) #Update States internal_states[state_ct] = p_elec_out internal_states[state_ct + 1] = p_elec_out / K_ig @@ -387,9 +398,6 @@ function initialize_outer!( V_Flag = PSY.get_V_Flag(reactive_power_control) # Update references if Ref_Flag == 0 && PF_Flag == 0 && V_Flag == 1 - #Get Reactive Controller Parameters - K_i = PSY.get_K_i(reactive_power_control) - K_qi = PSY.get_K_qi(reactive_power_control) #Update states internal_states[state_ct] = q_elec_out internal_states[state_ct + 1] = q_elec_out / K_i @@ -400,7 +408,6 @@ function initialize_outer!( inner_vars[V_oc_var] = 0.0 inner_vars[Iq_oc_var] = q_elec_out / max(V_t, 0.01) elseif Ref_Flag == 0 && PF_Flag == 0 && V_Flag == 0 - K_i = PSY.get_K_i(reactive_power_control) #Update states internal_states[state_ct] = q_ref internal_states[state_ct + 1] = q_ref / K_i @@ -438,11 +445,6 @@ function initialize_outer!( inner_vars[Iq_oc_var] = q_elec_out / max(V_t, 0.01) elseif Ref_Flag == 1 && PF_Flag == 0 && V_Flag == 0 # TODO: Fix and debug this case when Q_Flag = 1 - K_i = PSY.get_K_i(reactive_power_control) - K_qi = PSY.get_K_qi(reactive_power_control) - K_c = PSY.get_K_c(reactive_power_control) - R_c = PSY.get_R_c(reactive_power_control) - X_c = PSY.get_R_c(reactive_power_control) VC_Flag = PSY.get_VC_Flag(reactive_power_control) V_reg = sqrt(Vr_filter^2 + Vi_filter^2) # Compute input to the compensated voltage filter diff --git a/src/models/branch.jl b/src/models/branch.jl index e33256836..e6770fda2 100644 --- a/src/models/branch.jl +++ b/src/models/branch.jl @@ -1,6 +1,7 @@ function branch!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r_from::T, voltage_i_from::T, voltage_r_to::T, @@ -15,6 +16,7 @@ function branch!( mdl_branch_ode!( device_states, output_ode, + device_parameters, voltage_r_from, voltage_i_from, voltage_r_to, @@ -30,8 +32,9 @@ function branch!( end function branch!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r_from::T, voltage_i_from::T, voltage_r_to::T, @@ -46,6 +49,7 @@ function branch!( mdl_transformer_Lshape_ode!( device_states, output_ode, + device_parameters, voltage_r_from, voltage_i_from, voltage_r_to, diff --git a/src/models/common_controls.jl b/src/models/common_controls.jl index d420f3205..841594f27 100644 --- a/src/models/common_controls.jl +++ b/src/models/common_controls.jl @@ -11,22 +11,22 @@ u -> │ ────────────│ -> y └─────────────┘ """ function low_pass_modified_mass_matrix( - u::Z, - y::V, - K::Float64, - K_den::W, - ::Float64, -) where {V <: ACCEPTED_REAL_TYPES, W <: ACCEPTED_REAL_TYPES, Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + K_den::ACCEPTED_REAL_TYPES, + ::ACCEPTED_REAL_TYPES, +) return y, K * u - K_den * y end function low_pass_modified( - u::Z, - y::V, - K::Float64, - K_den::W, - T::Float64, -) where {V <: ACCEPTED_REAL_TYPES, W <: ACCEPTED_REAL_TYPES, Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + K_den::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, +) return y, (1.0 / T) * low_pass_modified_mass_matrix(u, y, K, K_den, T)[2] end @@ -41,20 +41,20 @@ u -> │ ────── │ -> y # Use this one if T = 0 is allowed, and let the mass matrix take care of it. function low_pass_mass_matrix( - u::Z, - y::V, - K::Float64, - T::Float64, -) where {V <: ACCEPTED_REAL_TYPES, Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, +) return low_pass_modified_mass_matrix(u, y, K, 1.0, T) end function low_pass( - u::Z, - y::V, - K::Float64, - T::Float64, -) where {V <: ACCEPTED_REAL_TYPES, Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, +) return low_pass_modified(u, y, K, 1.0, T) end @@ -72,13 +72,13 @@ u -> │ ────── │ -> y """ function low_pass_nonwindup_mass_matrix( - u::Z, - y::V, - K::Float64, - ::Float64, - y_min::Float64, - y_max::Float64, -) where {V <: ACCEPTED_REAL_TYPES, Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + ::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) dydt = K * u - y y_sat = clamp(y, y_min, y_max) # Non Windup logic from IEEE Std 421.5 @@ -88,28 +88,28 @@ end # Does not accept T = 0 function low_pass_nonwindup( - u::Z, - y::V, - K::Float64, - T::Float64, - y_min::Float64, - y_max::Float64, -) where {V <: ACCEPTED_REAL_TYPES, Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) y_sat, dydt_scaled = low_pass_nonwindup_mass_matrix(u, y, K, T, y_min, y_max) return y_sat, (1.0 / T) * dydt_scaled end # Does not accept T = 0 function low_pass_nonwindup_ramp_limits( - u::Z, - y::V, - K::Float64, - T::Float64, - y_min::Float64, - y_max::Float64, - dy_min::Float64, - dy_max::Float64, -) where {V <: ACCEPTED_REAL_TYPES, Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, + dy_min::ACCEPTED_REAL_TYPES, + dy_max::ACCEPTED_REAL_TYPES, +) y, dydt = low_pass(u, y, K, T) y_sat = clamp(y, y_min, y_max) dydt_sat = clamp(dydt, dy_min, dy_max) @@ -131,16 +131,21 @@ Internal State: x """ function high_pass_mass_matrix( - u::Z, - x::Z, - K::Float64, - T::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, +) K_T = T < eps() ? 0.0 : (K / T) return x + K_T * u, -(K_T * u + x) end -function high_pass(u::Z, x::Z, K::Float64, T::Float64) where {Z <: ACCEPTED_REAL_TYPES} +function high_pass( + u::ACCEPTED_REAL_TYPES, + x::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, +) y, dxdt_scaled = high_pass_mass_matrix(u, x, K, T) return y, (1.0 / T) * dxdt_scaled end @@ -157,23 +162,23 @@ Internal State: x """ function lead_lag_mass_matrix( - u::Z, - x::Z, - K::Float64, - T1::Float64, - T2::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, +) T1_T2 = T2 < eps() ? 0.0 : (T1 / T2) return x + (K * T1_T2) * u, K * (1 - T1_T2) * u - x end function lead_lag( - u::Z, - x::Z, - K::Float64, - T1::Float64, - T2::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, +) y, dxdt_scaled = lead_lag_mass_matrix(u, x, K, T1, T2) return y, (1.0 / T2) * dxdt_scaled end @@ -190,26 +195,26 @@ Internal state: x """ function low_pass_2nd( - u::Z, - x::Z, - y::Z, - K::Float64, - T1::Float64, - T2::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, +) dxdt = (1.0 / T2) * (K * u - T1 * x - y) dydt = x return y, dxdt, dydt end function low_pass_2nd_mass_matrix( - u::Z, - x::Z, - y::Z, - K::Float64, - T1::Float64, - T2::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, +) dxdt = K * u - T1 * x - y dydt = x return y, dxdt, dydt @@ -227,14 +232,14 @@ Internal States: x1, x2 """ function lead_lag_2nd_mass_matrix( - u::Z, - x1::Z, - x2::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, +) dx1dt = u - T1 * x1 - x2 dx2dt = x1 T4_T2 = T2 < eps() ? 0.0 : (T4 / T2) @@ -243,14 +248,14 @@ function lead_lag_2nd_mass_matrix( end function lead_lag_2nd( - u::Z, - x1::Z, - x2::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, +) y, dx1dt_scaled, dx2dt = lead_lag_2nd_mass_matrix(u, x1, x2, T1, T2, T3, T4) return y, (1.0 / T2) * dx1dt_scaled, dx2dt end @@ -271,16 +276,16 @@ Internal States: x1, x2 """ function lead_lag_2nd_mass_matrix_nonwindup( - u::Z, - x1::Z, - x2::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - y_min::Float64, - y_max::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) dx1dt_scaled = u - T1 * x1 - x2 dx2dt = x1 T4_T2 = T2 < eps() ? 0.0 : (T4 / T2) @@ -291,16 +296,16 @@ function lead_lag_2nd_mass_matrix_nonwindup( end function lead_lag_2nd_nonwindup( - u::Z, - x1::Z, - x2::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - y_min::Float64, - y_max::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) y, dx1dt_scaled, dx2dt = lead_lag_2nd_mass_matrix_nonwindup(u, x1, x2, T1, T2, T3, T4, y_min, y_max) return y, (1.0 / T2) * dx1dt_scaled, dx2dt @@ -318,17 +323,17 @@ Internal States: x1, x2, x3 """ function lead_lag_3rd_mass_matrix( - u::Z, - x1::Z, - x2::Z, - x3::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, +) dx1dt = u - T2 * x1 - T1 * x2 - x3 dx2dt = x1 dx3dt = x2 @@ -338,17 +343,17 @@ function lead_lag_3rd_mass_matrix( end function lead_lag_3rd( - u::Z, - x1::Z, - x2::Z, - x3::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, +) y, dx1dt_scaled, dx2dt, dx3dt = lead_lag_3rd_mass_matrix(u, x1, x2, x3, T1, T2, T3, T4, T5, T6) return y, (1.0 / T3) * dx1dt_scaled, dx2dt, dx3dt @@ -366,20 +371,20 @@ Internal States: x1, x2, x3, x4 """ function lead_lag_4th_mass_matrix( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, +) dx1dt = u - T3 * x1 - T2 * x2 - T1 * x3 - x4 dx2dt = x1 dx3dt = x2 @@ -395,20 +400,20 @@ function lead_lag_4th_mass_matrix( end function lead_lag_4th( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, +) y, dx1dt_scaled, dx2dt, dx3dt, dx4dt = lead_lag_4th_mass_matrix(u, x1, x2, x3, x4, T1, T2, T3, T4, T5, T6, T7, T8) return y, (1.0 / T4) * dx1dt_scaled, dx2dt, dx3dt, dx4dt @@ -426,23 +431,23 @@ Internal States: x1, x2, x3, x4, x5 """ function lead_lag_5th_mass_matrix( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, - T9::Float64, - T10::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, + T9::ACCEPTED_REAL_TYPES, + T10::ACCEPTED_REAL_TYPES, +) dx1dt = u - T4 * x1 - T3 * x2 - T2 * x3 - T1 * x4 - x5 dx2dt = x1 dx3dt = x2 @@ -460,23 +465,23 @@ function lead_lag_5th_mass_matrix( end function lead_lag_5th( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, - T9::Float64, - T10::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, + T9::ACCEPTED_REAL_TYPES, + T10::ACCEPTED_REAL_TYPES, +) y, dx1dt_scaled, dx2dt, dx3dt, dx4dt, dx5dt = lead_lag_5th_mass_matrix( u, x1, @@ -510,26 +515,26 @@ Internal States: x1, x2, x3, x4, x5, x6 """ function lead_lag_6th_mass_matrix( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - x6::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, - T9::Float64, - T10::Float64, - T11::Float64, - T12::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + x6::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, + T9::ACCEPTED_REAL_TYPES, + T10::ACCEPTED_REAL_TYPES, + T11::ACCEPTED_REAL_TYPES, + T12::ACCEPTED_REAL_TYPES, +) dx1dt = u - T5 * x1 - T4 * x2 - T3 * x3 - T2 * x4 - T1 * x5 - x6 dx2dt = x1 dx3dt = x2 @@ -549,26 +554,26 @@ function lead_lag_6th_mass_matrix( end function lead_lag_6th( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - x6::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, - T9::Float64, - T10::Float64, - T11::Float64, - T12::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + x6::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, + T9::ACCEPTED_REAL_TYPES, + T10::ACCEPTED_REAL_TYPES, + T11::ACCEPTED_REAL_TYPES, + T12::ACCEPTED_REAL_TYPES, +) y, dx1dt_scaled, dx2dt, dx3dt, dx4dt, dx5dt, dx6dt = lead_lag_6th_mass_matrix( u, x1, @@ -605,29 +610,29 @@ Internal States: x1, x2, x3, x4, x5, x6, x7 """ function lead_lag_7th_mass_matrix( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - x6::Z, - x7::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, - T9::Float64, - T10::Float64, - T11::Float64, - T12::Float64, - T13::Float64, - T14::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + x6::ACCEPTED_REAL_TYPES, + x7::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, + T9::ACCEPTED_REAL_TYPES, + T10::ACCEPTED_REAL_TYPES, + T11::ACCEPTED_REAL_TYPES, + T12::ACCEPTED_REAL_TYPES, + T13::ACCEPTED_REAL_TYPES, + T14::ACCEPTED_REAL_TYPES, +) dx1dt = u - T6 * x1 - T5 * x2 - T4 * x3 - T3 * x4 - T2 * x5 - T1 * x6 - x7 dx2dt = x1 dx3dt = x2 @@ -649,29 +654,29 @@ function lead_lag_7th_mass_matrix( end function lead_lag_7th( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - x6::Z, - x7::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, - T9::Float64, - T10::Float64, - T11::Float64, - T12::Float64, - T13::Float64, - T14::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + x6::ACCEPTED_REAL_TYPES, + x7::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, + T9::ACCEPTED_REAL_TYPES, + T10::ACCEPTED_REAL_TYPES, + T11::ACCEPTED_REAL_TYPES, + T12::ACCEPTED_REAL_TYPES, + T13::ACCEPTED_REAL_TYPES, + T14::ACCEPTED_REAL_TYPES, +) y, dx1dt_scaled, dx2dt, dx3dt, dx4dt, dx5dt, dx6dt, dx7dt = lead_lag_7th_mass_matrix( u, x1, @@ -711,32 +716,32 @@ Internal States: x1, x2, x3, x4, x5, x6, x7, x8 """ function lead_lag_8th_mass_matrix( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - x6::Z, - x7::Z, - x8::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, - T9::Float64, - T10::Float64, - T11::Float64, - T12::Float64, - T13::Float64, - T14::Float64, - T15::Float64, - T16::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + x6::ACCEPTED_REAL_TYPES, + x7::ACCEPTED_REAL_TYPES, + x8::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, + T9::ACCEPTED_REAL_TYPES, + T10::ACCEPTED_REAL_TYPES, + T11::ACCEPTED_REAL_TYPES, + T12::ACCEPTED_REAL_TYPES, + T13::ACCEPTED_REAL_TYPES, + T14::ACCEPTED_REAL_TYPES, + T15::ACCEPTED_REAL_TYPES, + T16::ACCEPTED_REAL_TYPES, +) dx1dt = u - T7 * x1 - T6 * x2 - T5 * x3 - T4 * x4 - T3 * x5 - T2 * x6 - T1 * x7 - x8 dx2dt = x1 dx3dt = x2 @@ -760,32 +765,32 @@ function lead_lag_8th_mass_matrix( end function lead_lag_8th( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - x6::Z, - x7::Z, - x8::Z, - T1::Float64, - T2::Float64, - T3::Float64, - T4::Float64, - T5::Float64, - T6::Float64, - T7::Float64, - T8::Float64, - T9::Float64, - T10::Float64, - T11::Float64, - T12::Float64, - T13::Float64, - T14::Float64, - T15::Float64, - T16::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + x6::ACCEPTED_REAL_TYPES, + x7::ACCEPTED_REAL_TYPES, + x8::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, + T3::ACCEPTED_REAL_TYPES, + T4::ACCEPTED_REAL_TYPES, + T5::ACCEPTED_REAL_TYPES, + T6::ACCEPTED_REAL_TYPES, + T7::ACCEPTED_REAL_TYPES, + T8::ACCEPTED_REAL_TYPES, + T9::ACCEPTED_REAL_TYPES, + T10::ACCEPTED_REAL_TYPES, + T11::ACCEPTED_REAL_TYPES, + T12::ACCEPTED_REAL_TYPES, + T13::ACCEPTED_REAL_TYPES, + T14::ACCEPTED_REAL_TYPES, + T15::ACCEPTED_REAL_TYPES, + T16::ACCEPTED_REAL_TYPES, +) y, dx1dt_scaled, dx2dt, dx3dt, dx4dt, dx5dt, dx6dt, dx7dt, dx8dt = lead_lag_8th_mass_matrix( u, @@ -842,20 +847,20 @@ Internal States: x1, x2, x3, x4, x5, x6, x7, x8 """ function ramp_tracking_filter( - u::Z, - x1::Z, - x2::Z, - x3::Z, - x4::Z, - x5::Z, - x6::Z, - x7::Z, - x8::Z, - T1::Float64, - T2::Float64, + u::ACCEPTED_REAL_TYPES, + x1::ACCEPTED_REAL_TYPES, + x2::ACCEPTED_REAL_TYPES, + x3::ACCEPTED_REAL_TYPES, + x4::ACCEPTED_REAL_TYPES, + x5::ACCEPTED_REAL_TYPES, + x6::ACCEPTED_REAL_TYPES, + x7::ACCEPTED_REAL_TYPES, + x8::ACCEPTED_REAL_TYPES, + T1::ACCEPTED_REAL_TYPES, + T2::ACCEPTED_REAL_TYPES, M::Int64, N::Int64, -) where {Z <: ACCEPTED_REAL_TYPES} +) dx1dt = 0.0 dx2dt = 0.0 dx3dt = 0.0 @@ -1494,18 +1499,23 @@ u -> │kp + ─── │ -> y Internal State: x """ -function pi_block(u::Z, x::Z, kp::Float64, ki::Float64) where {Z <: ACCEPTED_REAL_TYPES} +function pi_block( + u::ACCEPTED_REAL_TYPES, + x::ACCEPTED_REAL_TYPES, + kp::ACCEPTED_REAL_TYPES, + ki::ACCEPTED_REAL_TYPES, +) return kp * u + ki * x, u end function pi_block_nonwindup( - u::Z, - x::Z, - kp::Float64, - ki::Float64, - y_min::Float64, - y_max::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + x::ACCEPTED_REAL_TYPES, + kp::ACCEPTED_REAL_TYPES, + ki::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) y, _ = pi_block(u, x, kp, ki) y_sat = clamp(y, y_min, y_max) binary_logic = y_min < y < y_max ? 1.0 : 0.0 @@ -1525,13 +1535,13 @@ Integrator with windup limits """ function integrator_windup_mass_matrix( - u::Z, - y::Z, - K::Float64, - ::Float64, - y_min::Float64, - y_max::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + ::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) dydt_scaled = K * u y_sat = clamp(y, y_min, y_max) return y_sat, dydt_scaled @@ -1539,13 +1549,13 @@ end # Does not accept T = 0 function integrator_windup( - u::Z, - y::Z, - K::Float64, - T::Float64, - y_min::Float64, - y_max::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) y_sat = clamp(y, y_min, y_max) return y_sat, (1.0 / T) * integrator_windup_mass_matrix(u, y, K, T, y_min, y_max)[2] end @@ -1564,13 +1574,13 @@ u -> │ ────── │ -> y """ function integrator_nonwindup_mass_matrix( - u::Z, - y::Z, - K::Float64, - ::Float64, - y_min::Float64, - y_max::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + ::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) dydt_scaled = K * u y_sat = clamp(y, y_min, y_max) upper_lim = (y >= y_max) && (dydt_scaled > 0) @@ -1581,13 +1591,13 @@ end # Does not accept T = 0 function integrator_nonwindup( - u::Z, - y::Z, - K::Float64, - T::Float64, - y_min::Float64, - y_max::Float64, -) where {Z <: ACCEPTED_REAL_TYPES} + u::ACCEPTED_REAL_TYPES, + y::ACCEPTED_REAL_TYPES, + K::ACCEPTED_REAL_TYPES, + T::ACCEPTED_REAL_TYPES, + y_min::ACCEPTED_REAL_TYPES, + y_max::ACCEPTED_REAL_TYPES, +) y_sat, dydt_scaled = integrator_nonwindup_mass_matrix(u, y, K, T, y_min, y_max) return y_sat, (1.0 / T) * dydt_scaled end diff --git a/src/models/device.jl b/src/models/device.jl index 4d1fd9549..33be9fbe2 100644 --- a/src/models/device.jl +++ b/src/models/device.jl @@ -16,8 +16,9 @@ function device_mass_matrix_entries!( end function device!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -39,21 +40,55 @@ function device!( sys_ω = global_vars[GLOBAL_VAR_SYS_FREQ_INDEX] #Update Inner Vars - _update_inner_vars!(device_states, output_ode, sys_ω, inner_vars, dynamic_device) + _update_inner_vars!( + device_states, + output_ode, + device_parameters, + sys_ω, + inner_vars, + dynamic_device, + ) #Obtain ODEs and Mechanical Power for Turbine Governor - mdl_tg_ode!(device_states, output_ode, inner_vars, sys_ω, dynamic_device, h, t) + mdl_tg_ode!( + device_states, + output_ode, + device_parameters, + inner_vars, + sys_ω, + dynamic_device, + h, + t, + ) #Obtain ODEs for PSS - mdl_pss_ode!(device_states, output_ode, inner_vars, sys_ω, dynamic_device, h, t) + mdl_pss_ode!( + device_states, + output_ode, + device_parameters, + inner_vars, + sys_ω, + dynamic_device, + h, + t, + ) #Obtain ODEs for AVR - mdl_avr_ode!(device_states, output_ode, inner_vars, dynamic_device, h, t) + mdl_avr_ode!( + device_states, + output_ode, + device_parameters, + inner_vars, + dynamic_device, + h, + t, + ) #Obtain ODEs for Machine mdl_machine_ode!( device_states, output_ode, + device_parameters, inner_vars, current_r, current_i, @@ -63,11 +98,21 @@ function device!( ) #Obtain ODEs for PSY.Shaft - mdl_shaft_ode!(device_states, output_ode, inner_vars, sys_ω, dynamic_device, h, t) + mdl_shaft_ode!( + device_states, + output_ode, + device_parameters, + inner_vars, + sys_ω, + dynamic_device, + h, + t, + ) return end function device!( + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -77,11 +122,12 @@ function device!( device::StaticWrapper{PSY.Source, U}, t, ) where {T <: ACCEPTED_REAL_TYPES, U <: BusCategory} - mdl_source!(voltage_r, voltage_i, current_r, current_i, device) + mdl_source!(device_parameters, voltage_r, voltage_i, current_r, current_i, device) return end function device!( + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -91,7 +137,7 @@ function device!( device::StaticLoadWrapper, t, ) where {T <: ACCEPTED_REAL_TYPES} - mdl_zip_load!(voltage_r, voltage_i, current_r, current_i, device) + mdl_zip_load!(device_parameters, voltage_r, voltage_i, current_r, current_i, device) return end @@ -136,8 +182,9 @@ function device_mass_matrix_entries!( end function device!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -161,19 +208,36 @@ function device!( inner_vars[Vi_inv_var] = voltage_i #Update V_ref - V_ref = get_V_ref(dynamic_device) + V_ref = p[:refs][:V_ref] inner_vars[V_oc_var] = V_ref #Update current inner_vars - _update_inner_vars!(device_states, output_ode, sys_ω, inner_vars, dynamic_device) + _update_inner_vars!( + device_states, + output_ode, + p, + sys_ω, + inner_vars, + dynamic_device, + ) #Obtain ODES for DC side - mdl_DCside_ode!(device_states, output_ode, sys_ω, inner_vars, dynamic_device, h, t) + mdl_DCside_ode!( + device_states, + output_ode, + p, + sys_ω, + inner_vars, + dynamic_device, + h, + t, + ) #Obtain ODEs for PLL mdl_freq_estimator_ode!( device_states, output_ode, + p, inner_vars, sys_ω, dynamic_device, @@ -182,18 +246,44 @@ function device!( ) #Obtain ODEs for OuterLoop - mdl_outer_ode!(device_states, output_ode, inner_vars, sys_ω, dynamic_device, h, t) + mdl_outer_ode!( + device_states, + output_ode, + p, + inner_vars, + sys_ω, + dynamic_device, + h, + t, + ) #Obtain inner controller ODEs and modulation commands - mdl_inner_ode!(device_states, output_ode, inner_vars, dynamic_device, h, t) + mdl_inner_ode!( + device_states, + output_ode, + p, + inner_vars, + dynamic_device, + h, + t, + ) #Obtain converter relations - mdl_converter_ode!(device_states, output_ode, inner_vars, dynamic_device, h, t) + mdl_converter_ode!( + device_states, + output_ode, + p, + inner_vars, + dynamic_device, + h, + t, + ) #Obtain ODEs for output filter mdl_filter_ode!( device_states, output_ode, + p, current_r, current_i, inner_vars, @@ -224,8 +314,9 @@ function mass_matrix_pvs_entries!( end function device!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -270,6 +361,7 @@ function device!( end function _update_inner_vars!( + ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, @@ -282,6 +374,7 @@ end function _update_inner_vars!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, P}}, @@ -312,16 +405,16 @@ function _update_inner_vars!( V_tI = inner_vars[VI_gen_var] #Get parameters - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xd_p = PSY.get_Xd_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) + params = p[:params][:Machine] + R = params[:R] + Xd = params[:Xd] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] Xq_pp = Xd_pp - Xl = PSY.get_Xl(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) - #RI to dq transformation V_dq = ri_dq(δ) * [V_tR; V_tI] @@ -348,6 +441,7 @@ end function _update_inner_vars!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ @@ -373,14 +467,15 @@ function _update_inner_vars!( V_tI = inner_vars[VI_gen_var] #Get parameters - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xd_p = PSY.get_Xd_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xl = PSY.get_Xl(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) + params = p[:params][:Machine] + R = params[:R] + Xd = params[:Xd] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] #RI to dq transformation V_d, V_q = ri_dq(δ) * [V_tR; V_tI] @@ -405,6 +500,7 @@ end function _update_inner_vars!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ @@ -430,15 +526,16 @@ function _update_inner_vars!( V_tI = inner_vars[VI_gen_var] #Get parameters - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xd_p = PSY.get_Xd_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) + params = p[:params][:Machine] + R = params[:R] + Xd = params[:Xd] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] Xq_pp = Xd_pp - Xl = PSY.get_Xl(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) #RI to dq transformation V_d, V_q = ri_dq(δ) * [V_tR; V_tI] @@ -462,6 +559,7 @@ function _update_inner_vars!( end function _update_inner_vars!( + ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, @@ -482,6 +580,7 @@ end function _update_inner_vars!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ @@ -509,17 +608,24 @@ function _update_inner_vars!( #Get Converter parameters converter = PSY.get_converter(dynamic_device) - Brkpt = PSY.get_Brkpt(converter) - Zerox = PSY.get_Zerox(converter) - Lvpl1 = PSY.get_Lvpl1(converter) - Vo_lim = PSY.get_Vo_lim(converter) - Lv_pnt0, Lv_pnt1 = PSY.get_Lv_pnts(converter) - K_hv = PSY.get_K_hv(converter) + params = p[:params][:Converter] + Brkpt = params[:Brkpt] + Zerox = params[:Zerox] + Lvpl1 = params[:Lvpl1] + Vo_lim = params[:Vo_lim] + Lv_pnt0 = params[:Lv_pnts][:min] + Lv_pnt1 = params[:Lv_pnts][:max] + K_hv = params[:K_hv] + R_source = params[:R_source] + X_source = params[:X_source] + Lvpl_sw = PSY.get_Lvpl_sw(converter) - R_source = PSY.get_R_source(converter) - X_source = PSY.get_X_source(converter) Z_source_sq = R_source^2 + X_source^2 + #Obtain filter parameters + rf = p[:params][:Filter][:rf] + lf = p[:params][:Filter][:lf] + #Define internal states for Converter converter_ix = get_local_state_ix(dynamic_device, PSY.RenewableEnergyConverterTypeA) converter_states = @view device_states[converter_ix] @@ -541,11 +647,6 @@ function _update_inner_vars!( Ir_cnv = Id_cnv * cos(θ) - Iq_cnv * sin(θ) Ii_cnv = Id_cnv * sin(θ) + Iq_cnv * cos(θ) - #Obtain parameters - filt = PSY.get_filter(dynamic_device) - rf = PSY.get_rf(filt) - lf = PSY.get_lf(filt) - function V_cnv_calc(Ir_cnv, Ii_cnv, Vr_inv, Vi_inv) if lf != 0.0 || rf != 0.0 Z_source_mag_sq = R_source^2 + X_source^2 @@ -603,6 +704,7 @@ end function _update_inner_vars!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ @@ -671,8 +773,9 @@ Refer to "Analysis of Electric Machinery and Drive Systems" by Paul Krause, Oleg Wasynczuk and Scott Sudhoff for the equations """ function device!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -701,20 +804,20 @@ function device!( ωr = device_states[5] #Get parameters - dynamic_device = get_device(dynamic_wrapper) - R_s = PSY.get_R_s(dynamic_device) - X_ls = PSY.get_X_ls(dynamic_device) - R_r = PSY.get_R_r(dynamic_device) - X_lr = PSY.get_X_lr(dynamic_device) - A = PSY.get_A(dynamic_device) - B = PSY.get_B(dynamic_device) - C = PSY.get_C(dynamic_device) - H = PSY.get_H(dynamic_device) - base_power = PSY.get_base_power(dynamic_device) - B_sh = PSY.get_B_shunt(dynamic_device) - τ_m0 = PSY.get_τ_ref(dynamic_device) - X_ad = PSY.get_X_ad(dynamic_device) - X_aq = PSY.get_X_aq(dynamic_device) + params = p[:params] + R_s = params[:R_s] + R_r = params[:R_r] + X_ls = params[:X_ls] + X_lr = params[:X_lr] + H = params[:H] + A = params[:A] + B = params[:B] + base_power = params[:base_power] + C = params[:C] + τ_m0 = params[:τ_ref] + B_sh = params[:B_shunt] + X_ad = params[:X_ad] + X_aq = params[:X_aq] # voltages in QD v_qs = voltage_i @@ -755,8 +858,9 @@ equations in "Analysis of Electric Machinery and Drive Systems" by Paul Krause, Oleg Wasynczuk and Scott Sudhoff. """ function device!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -783,19 +887,21 @@ function device!( ωr = device_states[3] #Get parameters - dynamic_device = get_device(dynamic_wrapper) - R_s = PSY.get_R_s(dynamic_device) - X_m = PSY.get_X_m(dynamic_device) - R_r = PSY.get_R_r(dynamic_device) - A = PSY.get_A(dynamic_device) - B = PSY.get_B(dynamic_device) - C = PSY.get_C(dynamic_device) - H = PSY.get_H(dynamic_device) - base_power = PSY.get_base_power(dynamic_device) - B_sh = PSY.get_B_shunt(dynamic_device) - τ_m0 = PSY.get_τ_ref(dynamic_device) - X_rr = PSY.get_X_rr(dynamic_device) - X_p = PSY.get_X_p(dynamic_device) + params = p[:params] + R_s = params[:R_s] + R_r = params[:R_r] + X_ls = params[:X_ls] + X_lr = params[:X_lr] + X_m = params[:X_m] + H = params[:H] + A = params[:A] + B = params[:B] + base_power = params[:base_power] + C = params[:C] + τ_m0 = params[:τ_ref] + B_sh = params[:B_shunt] + X_rr = params[:X_rr] + X_p = params[:X_p] # voltages in QD v_qs = voltage_i @@ -852,8 +958,9 @@ end Model of Static Shunt Compensator: CSVGN1. """ function device!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -865,11 +972,12 @@ function device!( t, ) where {T <: ACCEPTED_REAL_TYPES} Sbase = get_system_base_power(dynamic_wrapper) - V_ref = get_V_ref(dynamic_wrapper) + V_ref = p[:refs][:V_ref] # TODO: V_abs is the voltage magnitude on the high side of generator step-up transformer, if present. V_abs = sqrt(voltage_r^2 + voltage_i^2) if get_connection_status(dynamic_wrapper) < 1.0 + @error "NOT CONNECTED? " output_ode .= zero(T) return end @@ -880,20 +988,21 @@ function device!( vr2 = device_states[3] #Get parameters - dynamic_device = get_device(dynamic_wrapper) - K = PSY.get_K(dynamic_device) - T1 = PSY.get_T1(dynamic_device) - T2 = PSY.get_T2(dynamic_device) - T3 = PSY.get_T3(dynamic_device) - T4 = PSY.get_T4(dynamic_device) - T5 = PSY.get_T5(dynamic_device) - Rmin = PSY.get_Rmin(dynamic_device) - Vmax = PSY.get_Vmax(dynamic_device) - Vmin = PSY.get_Vmin(dynamic_device) - Cbase = PSY.get_CBase(dynamic_device) + params = p[:params] + K = params[:K] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + T4 = params[:T4] + T5 = params[:T5] + Rmin = params[:Rmin] + Vmax = params[:Vmax] + Vmin = params[:Vmin] + Cbase = params[:CBase] + Mbase = params[:base_power] + # FIXME: base_power is changed to system's base_power when a CSVGN1 is attached to a Source using add_component!() # Temporarily, to avoid that, set_dynamic_injector!() could be used - Mbase = PSY.get_base_power(dynamic_device) Rbase = Mbase # Regulator @@ -961,8 +1070,9 @@ Based on the paper `Malicious Control of an Active Load in an Islanded Mixed-Sou by C. Roberts, U. Markovic, D. Arnold and D. Callaway. """ function device!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -975,7 +1085,7 @@ function device!( ) where {T <: ACCEPTED_REAL_TYPES} Sbase = get_system_base_power(dynamic_wrapper) f0 = get_system_base_frequency(dynamic_wrapper) - V_ref = get_V_ref(dynamic_wrapper) + V_ref = p[:refs][:V_ref] if get_connection_status(dynamic_wrapper) < 1.0 output_ode .= zero(T) return @@ -1006,21 +1116,21 @@ function device!( I_dq_cnv = ri_dq(θ_pll + pi / 2) * [Ir_cnv; Ii_cnv] #Get parameters - dynamic_device = get_device(dynamic_wrapper) - r_load = PSY.get_r_load(dynamic_device) - c_dc = PSY.get_c_dc(dynamic_device) - rf = PSY.get_rf(dynamic_device) - lf = PSY.get_lf(dynamic_device) - cf = PSY.get_cf(dynamic_device) - rg = PSY.get_rg(dynamic_device) - lg = PSY.get_lg(dynamic_device) - kp_pll = PSY.get_kp_pll(dynamic_device) - ki_pll = PSY.get_ki_pll(dynamic_device) - kpv = PSY.get_kpv(dynamic_device) - kiv = PSY.get_kiv(dynamic_device) - kpc = PSY.get_kpc(dynamic_device) - kic = PSY.get_kic(dynamic_device) - base_power = PSY.get_base_power(dynamic_device) + params = p[:params] + r_load = params[:r_load] + c_dc = params[:c_dc] + rf = params[:rf] + lf = params[:lf] + cf = params[:cf] + rg = params[:rg] + lg = params[:lg] + kp_pll = params[:kp_pll] + ki_pll = params[:ki_pll] + kpv = params[:kpv] + kiv = params[:kiv] + kpc = params[:kpc] + kic = params[:kic] + base_power = params[:base_power] # Compute PLL expressions V_dq_pll = ri_dq(θ_pll + pi / 2) * [Vr_filter; Vi_filter] @@ -1029,8 +1139,7 @@ function device!( # Compute DC side output Id_ref, dη_dt = pi_block(V_ref - v_dc, η, kpv, kiv) - Iq_ref = get_Q_ref(dynamic_wrapper) - + Iq_ref = p[:refs][:Q_ref] # Compute AC controller expressions Vd_ref_uncomp, dγd_dt = pi_block(-Id_ref + I_dq_cnv[d], γd, kpc, kic) Vq_ref_uncomp, dγq_dt = pi_block(-Iq_ref + I_dq_cnv[q], γq, kpc, kic) @@ -1108,22 +1217,24 @@ function mass_matrix_dera_entries!( end function device!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, current_i::AbstractArray{T}, global_vars::AbstractArray{T}, inner_vars::AbstractArray{T}, - dynamic_device::DynamicWrapper{PSY.AggregateDistributedGenerationA}, + dynamic_wrapper::DynamicWrapper{PSY.AggregateDistributedGenerationA}, h, t, ) where {T <: ACCEPTED_REAL_TYPES} - Freq_Flag = PSY.get_Freq_Flag(get_device(dynamic_device)) + Freq_Flag = PSY.get_Freq_Flag(get_device(dynamic_wrapper)) _mdl_ode_AggregateDistributedGenerationA!( device_states, output_ode, + device_parameters, Val(Freq_Flag), voltage_r, voltage_i, @@ -1131,7 +1242,7 @@ function device!( current_i, global_vars, inner_vars, - dynamic_device, + dynamic_wrapper, t, ) return @@ -1143,8 +1254,9 @@ end #Freq_Flag = 0 function _mdl_ode_AggregateDistributedGenerationA!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::Val{0}, voltage_r::T, voltage_i::T, @@ -1152,21 +1264,21 @@ function _mdl_ode_AggregateDistributedGenerationA!( current_i::AbstractArray{T}, global_vars::AbstractArray{T}, inner_vars::AbstractArray{T}, - dynamic_device::DynamicWrapper{PSY.AggregateDistributedGenerationA}, + dynamic_wrapper::DynamicWrapper{PSY.AggregateDistributedGenerationA}, t, ) where {T <: ACCEPTED_REAL_TYPES} sys_ω = global_vars[GLOBAL_VAR_SYS_FREQ_INDEX] - Sbase = get_system_base_power(dynamic_device) + Sbase = get_system_base_power(dynamic_wrapper) Vt = sqrt(voltage_r^2 + voltage_i^2) - + dynamic_device = get_device(dynamic_wrapper) #Obtain References (from wrapper and device) - Pfa_ref = PSY.get_Pfa_ref(get_device(dynamic_device)) - P_ref = get_P_ref(dynamic_device) - Q_ref = get_Q_ref(dynamic_device) - V_ref = get_V_ref(dynamic_device) + Pfa_ref = PSY.get_Pfa_ref(dynamic_device) + P_ref = p[:refs][:P_ref] + Q_ref = p[:refs][:Q_ref] + V_ref = p[:refs][:V_ref] #Get flags - Pf_Flag = PSY.get_Pf_Flag(get_device(dynamic_device)) + Pf_Flag = PSY.get_Pf_Flag(dynamic_device) #Get device states Vmeas = device_states[1] @@ -1181,18 +1293,26 @@ function _mdl_ode_AggregateDistributedGenerationA!( Iq_cmd = Iq #Get parameters - T_rv = PSY.get_T_rv(get_device(dynamic_device)) - Trf = PSY.get_Trf(get_device(dynamic_device)) - (dbd1, dbd2) = PSY.get_dbd_pnts(get_device(dynamic_device)) - K_qv = PSY.get_K_qv(get_device(dynamic_device)) - Tp = PSY.get_Tp(get_device(dynamic_device)) - T_iq = PSY.get_T_iq(get_device(dynamic_device)) - - Tg = PSY.get_Tg(get_device(dynamic_device)) - rrpwr = PSY.get_rrpwr(get_device(dynamic_device)) - Tv = PSY.get_Tv(get_device(dynamic_device)) - Iq_lim = PSY.get_Iq_lim(get_device(dynamic_device)) - basepower = PSY.get_base_power(get_device(dynamic_device)) + params = p[:params] + T_rv = params[:T_rv] + Trf = params[:Trf] + dbd1 = params[:dbd_pnts1] + dbd2 = params[:dbd_pnts2] + K_qv = params[:K_qv] + Tp = params[:Tp] + T_iq = params[:T_iq] + D_dn = params[:D_dn] + D_up = params[:D_up] + fdbd_pnts1 = params[:fdbd_pnts1] + fdbd_pnts2 = params[:fdbd_pnts2] + Tg = params[:Tg] + rrpwr = params[:rrpwr] + Tv = params[:Tv] + Iq_min = params[:Iq_lim][:min] + Iq_max = params[:Iq_lim][:max] + basepower = params[:base_power] + Pfa_ref = params[:Pfa_ref] + base_power_ratio = basepower / Sbase #STATE Vmeas @@ -1204,21 +1324,21 @@ function _mdl_ode_AggregateDistributedGenerationA!( elseif Pf_Flag == 0 _, dQ_V_dt = low_pass_mass_matrix(Q_ref / max(Vmeas, 0.01), Q_V, 1.0, T_iq) else - @error @error "Unsupported value of PQ_Flag" + @error "Unsupported value of PQ_Flag" end #STATE Iq - Ip_min, Ip_max, Iq_min, Iq_max = - current_limit_logic(get_device(dynamic_device), Ip_cmd, Iq_cmd) + Ip_min, Ip_max, _Iq_min, _Iq_max = + current_limit_logic(dynamic_device, Ip_cmd, Iq_cmd) Iq_input = clamp( clamp( deadband_function(V_ref - Vmeas, dbd1, dbd2) * K_qv, - Iq_lim[:min], - Iq_lim[:max], + Iq_min, + Iq_max, ) + Q_V, - Iq_min, - Iq_max, + _Iq_min, + _Iq_max, ) * Mult _, dIq_dt = low_pass(Iq_input, Iq, 1.0, Tg) @@ -1265,8 +1385,9 @@ end #Freq_Flag = 1 function _mdl_ode_AggregateDistributedGenerationA!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::Val{1}, voltage_r::T, voltage_i::T, @@ -1274,22 +1395,22 @@ function _mdl_ode_AggregateDistributedGenerationA!( current_i::AbstractArray{T}, global_vars::AbstractArray{T}, inner_vars::AbstractArray{T}, - dynamic_device::DynamicWrapper{PSY.AggregateDistributedGenerationA}, + dynamic_wrapper::DynamicWrapper{PSY.AggregateDistributedGenerationA}, t, ) where {T <: ACCEPTED_REAL_TYPES} sys_ω = global_vars[GLOBAL_VAR_SYS_FREQ_INDEX] - Sbase = get_system_base_power(dynamic_device) + Sbase = get_system_base_power(dynamic_wrapper) Vt = sqrt(voltage_r^2 + voltage_i^2) - + dynamic_device = get_device(dynamic_wrapper) #Obtain References (from wrapper and device) - Pfa_ref = PSY.get_Pfa_ref(get_device(dynamic_device)) - P_ref = get_P_ref(dynamic_device) - Q_ref = get_Q_ref(dynamic_device) - V_ref = get_V_ref(dynamic_device) - ω_ref = get_ω_ref(dynamic_device) + Pfa_ref = PSY.get_Pfa_ref(dynamic_device) + P_ref = p[:refs][:P_ref] + Q_ref = p[:refs][:Q_ref] + V_ref = p[:refs][:V_ref] + ω_ref = p[:refs][:ω_ref] #Get flags - Pf_Flag = PSY.get_Pf_Flag(get_device(dynamic_device)) + Pf_Flag = PSY.get_Pf_Flag(dynamic_device) #Get device states Vmeas = device_states[1] @@ -1307,27 +1428,35 @@ function _mdl_ode_AggregateDistributedGenerationA!( Iq_cmd = Iq #Get parameters - T_rv = PSY.get_T_rv(get_device(dynamic_device)) - Trf = PSY.get_Trf(get_device(dynamic_device)) - (dbd1, dbd2) = PSY.get_dbd_pnts(get_device(dynamic_device)) - K_qv = PSY.get_K_qv(get_device(dynamic_device)) - Tp = PSY.get_Tp(get_device(dynamic_device)) - T_iq = PSY.get_T_iq(get_device(dynamic_device)) - D_dn = PSY.get_D_dn(get_device(dynamic_device)) - D_up = PSY.get_D_up(get_device(dynamic_device)) - (fdbd1, fdbd2) = PSY.get_fdbd_pnts(get_device(dynamic_device)) - fe_lim = PSY.get_fe_lim(get_device(dynamic_device)) - P_lim = PSY.get_P_lim(get_device(dynamic_device)) - dP_lim = PSY.get_dP_lim(get_device(dynamic_device)) - Tpord = PSY.get_Tpord(get_device(dynamic_device)) - Kpg = PSY.get_Kpg(get_device(dynamic_device)) - Kig = PSY.get_Kig(get_device(dynamic_device)) - - Tg = PSY.get_Tg(get_device(dynamic_device)) - rrpwr = PSY.get_rrpwr(get_device(dynamic_device)) - Tv = PSY.get_Tv(get_device(dynamic_device)) - Iq_lim = PSY.get_Iq_lim(get_device(dynamic_device)) - basepower = PSY.get_base_power(get_device(dynamic_device)) + params = p[:params] + T_rv = params[:T_rv] + Trf = params[:Trf] + dbd1 = params[:dbd_pnts1] + dbd2 = params[:dbd_pnts2] + K_qv = params[:K_qv] + Tp = params[:Tp] + T_iq = params[:T_iq] + D_dn = params[:D_dn] + D_up = params[:D_up] + fdbd1 = params[:fdbd_pnts1] + fdbd2 = params[:fdbd_pnts2] + fe_min = params[:fe_lim][:min] + fe_max = params[:fe_lim][:max] + P_min = params[:P_lim][:min] + P_max = params[:P_lim][:max] + dP_min = params[:dP_lim][:min] + dP_max = params[:dP_lim][:max] + Tpord = params[:Tpord] + Kpg = params[:Kpg] + Kig = params[:Kig] + Tg = params[:Tg] + rrpwr = params[:rrpwr] + Tv = params[:Tv] + Iq_min = params[:Iq_lim][:min] + Iq_max = params[:Iq_lim][:max] + basepower = params[:base_power] + Pfa_ref = params[:Pfa_ref] + base_power_ratio = basepower / Sbase #STATE Vmeas @@ -1343,17 +1472,17 @@ function _mdl_ode_AggregateDistributedGenerationA!( end #STATE Iq - Ip_min, Ip_max, Iq_min, Iq_max = - current_limit_logic(get_device(dynamic_device), Ip_cmd, Iq_cmd) + Ip_min, Ip_max, _Iq_min, _Iq_max = + current_limit_logic(dynamic_device, Ip_cmd, Iq_cmd) Iq_input = clamp( clamp( deadband_function(V_ref - Vmeas, dbd1, dbd2) * K_qv, - Iq_lim[:min], - Iq_lim[:max], + Iq_min, + Iq_max, ) + Q_V, - Iq_min, - Iq_max, + _Iq_min, + _Iq_max, ) * Mult _, dIq_dt = low_pass(Iq_input, Iq, 1.0, Tg) @@ -1379,24 +1508,24 @@ function _mdl_ode_AggregateDistributedGenerationA!( PowerPI_input = clamp( min(deadband_function(ω_ref - Fmeas, fdbd1, fdbd2) * D_dn, 0.0) + max(deadband_function(ω_ref - Fmeas, fdbd1, fdbd2) * D_up, 0.0) - Pmeas + P_ref, - fe_lim[:min], - fe_lim[:max], + fe_min, + fe_max, ) _, dPowerPI_dt = - pi_block_nonwindup(PowerPI_input, PowerPI, Kpg, Kig, P_lim[:min], P_lim[:max]) + pi_block_nonwindup(PowerPI_input, PowerPI, Kpg, Kig, P_min, P_max) #STATE dPord - if dPowerPI_dt > dP_lim[:max] - ddPord_dt = dP_lim[:max] - elseif dPowerPI_dt < dP_lim[:min] - ddPord_dt = dP_lim[:min] + if dPowerPI_dt > dP_max + ddPord_dt = dP_max + elseif dPowerPI_dt < dP_min + ddPord_dt = dP_min else ddPord_dt = dPowerPI_dt end #State Pord Pord_limited, dPord_dt = - low_pass_nonwindup_mass_matrix(dPord, Pord, 1.0, Tpord, P_lim[:min], P_lim[:max]) + low_pass_nonwindup_mass_matrix(dPord, Pord, 1.0, Tpord, P_min, P_max) #STATE Ip Ip_input = clamp(Pord_limited / max(Vmeas, 0.01), Ip_min, Ip_max) * Mult diff --git a/src/models/dynline_model.jl b/src/models/dynline_model.jl index 00c6f6764..f581406bb 100644 --- a/src/models/dynline_model.jl +++ b/src/models/dynline_model.jl @@ -1,6 +1,7 @@ function mdl_branch_ode!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r_from::T, voltage_i_from::T, voltage_r_to::T, @@ -11,8 +12,9 @@ function mdl_branch_ode!( current_i_to::AbstractArray{T}, branch::BranchWrapper, ) where {T <: ACCEPTED_REAL_TYPES} - L = PSY.get_x(branch) - R = PSY.get_r(branch) + R = p[:params][:r] + L = p[:params][:x] + ω_b = get_system_base_frequency(branch) * 2 * π Il_r = device_states[1] @@ -28,8 +30,9 @@ function mdl_branch_ode!( end function mdl_transformer_Lshape_ode!( - device_states::AbstractArray{T}, + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{T}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r_from::T, voltage_i_from::T, voltage_r_to::T, diff --git a/src/models/generator_models/avr_models.jl b/src/models/generator_models/avr_models.jl index 748e38362..c0501bbce 100644 --- a/src/models/generator_models/avr_models.jl +++ b/src/models/generator_models/avr_models.jl @@ -82,21 +82,22 @@ end function mdl_avr_ode!( ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.AVRFixed, TG, P}}, h, t, ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} - + V_ref = p[:refs][:V_ref] #Update Vf voltage on inner vars. In AVRFixed, Vf = V_ref - inner_vars[Vf_var] = get_V_ref(dynamic_device) - + inner_vars[Vf_var] = V_ref return end function mdl_avr_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.AVRSimple, TG, P}}, h, @@ -104,7 +105,7 @@ function mdl_avr_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} #Obtain references - V_ref = get_V_ref(dynamic_device) + V_ref = p[:refs][:V_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.AVRSimple) @@ -117,7 +118,7 @@ function mdl_avr_ode!( V_th = sqrt(inner_vars[VR_gen_var]^2 + inner_vars[VI_gen_var]^2) #Get Parameters - Kv = PSY.get_Kv(PSY.get_avr(dynamic_device)) + Kv = p[:params][:AVR][:Kv] #Compute ODEs output_ode[local_ix[1]] = Kv * (V_ref - V_th) @@ -131,14 +132,14 @@ end function mdl_avr_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.AVRTypeI, TG, P}}, h, t, ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} - #Obtain references - V0_ref = get_V_ref(dynamic_device) + V0_ref = p[:refs][:V_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.AVRTypeI) @@ -155,16 +156,16 @@ function mdl_avr_ode!( Vs = inner_vars[V_pss_var] #Get parameters - avr = PSY.get_avr(dynamic_device) - Ka = PSY.get_Ka(avr) - Ke = PSY.get_Ke(avr) - Kf = PSY.get_Kf(avr) - Ta = PSY.get_Ta(avr) - Te = PSY.get_Te(avr) - Tf = PSY.get_Tf(avr) - Tr = PSY.get_Tr(avr) - Ae = PSY.get_Ae(avr) - Be = PSY.get_Be(avr) + params = p[:params][:AVR] + Ka = params[:Ka] + Ke = params[:Ke] + Kf = params[:Kf] + Ta = params[:Ta] + Te = params[:Te] + Tf = params[:Tf] + Tr = params[:Tr] + Ae = params[:Ae] + Be = params[:Be] #Compute auxiliary parameters Se_Vf = Ae * exp(Be * abs(Vf)) #16.13 @@ -191,6 +192,7 @@ end function mdl_avr_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.AVRTypeII, TG, P}}, h, @@ -198,7 +200,7 @@ function mdl_avr_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} #Obtain references - V0_ref = get_V_ref(dynamic_device) + V0_ref = p[:refs][:V_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.AVRTypeII) @@ -215,17 +217,18 @@ function mdl_avr_ode!( Vs = inner_vars[V_pss_var] #Get parameters - avr = PSY.get_avr(dynamic_device) - K0 = PSY.get_K0(avr) - T1 = PSY.get_T1(avr) - T2 = PSY.get_T2(avr) - T3 = PSY.get_T3(avr) - T4 = PSY.get_T4(avr) - Te = PSY.get_Te(avr) - Tr = PSY.get_Tr(avr) - Ae = PSY.get_Ae(avr) - Be = PSY.get_Be(avr) - Va_min, Va_max = PSY.get_Va_lim(avr) + params = p[:params][:AVR] + K0 = params[:K0] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + T4 = params[:T4] + Te = params[:Te] + Tr = params[:Tr] + Va_min = params[:Va_lim][:min] + Va_max = params[:Va_lim][:max] + Ae = params[:Ae] + Be = params[:Be] #Compute auxiliary parameters Se_Vf = Ae * exp(Be * abs(Vf)) #16.13 @@ -253,6 +256,7 @@ end function mdl_avr_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.ESAC1A, TG, P}}, h, @@ -260,7 +264,7 @@ function mdl_avr_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} #Obtain references - V_ref = get_V_ref(dynamic_device) + V_ref = p[:refs][:V_ref] #Obtain avr avr = PSY.get_avr(dynamic_device) @@ -282,20 +286,23 @@ function mdl_avr_ode!( Xad_Ifd = inner_vars[Xad_Ifd_var] #Get parameters - Tr = PSY.get_Tr(avr) - Ta = PSY.get_Ta(avr) - Tb = PSY.get_Tb(avr) - Tc = PSY.get_Tc(avr) + params = p[:params][:AVR] + Tr = params[:Tr] + Tb = params[:Tb] + Tc = params[:Tc] + Ka = params[:Ka] + Ta = params[:Ta] + Va_min = params[:Va_lim][:min] + Va_max = params[:Va_lim][:max] + Te = params[:Te] + Kf = params[:Kf] + Tf = params[:Tf] + Kc = params[:Kc] + Kd = params[:Kd] + Ke = params[:Ke] + Vr_min = params[:Vr_lim][:min] + Vr_max = params[:Vr_lim][:max] inv_Tr = Tr < eps() ? 1.0 : 1.0 / Tr - Ka = PSY.get_Ka(avr) - Va_min, Va_max = PSY.get_Va_lim(avr) #Not used without UEL or OEL - Te = PSY.get_Te(avr) # Te > 0 - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) # Te > 0 - Kc = PSY.get_Kc(avr) - Kd = PSY.get_Kd(avr) - Ke = PSY.get_Ke(avr) - Vr_min, Vr_max = PSY.get_Vr_lim(avr) #Obtain saturation Se = saturation_function(avr, Ve) @@ -330,14 +337,14 @@ end function mdl_avr_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.SEXS, TG, P}}, h, t, ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} #Obtain references - V0_ref = get_V_ref(dynamic_device) - + V0_ref = p[:refs][:V_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.SEXS) @@ -351,13 +358,14 @@ function mdl_avr_ode!( Vs = inner_vars[V_pss_var] #Get parameters - avr = PSY.get_avr(dynamic_device) - Ta_Tb = PSY.get_Ta_Tb(avr) - Tb = PSY.get_Tb(avr) + params = @view(p[:params][:AVR]) + Ta_Tb = params[:Ta_Tb] + Tb = params[:Tb] + K = params[:K] + Te = params[:Te] + V_min = params[:V_lim][:min] + V_max = params[:V_lim][:max] Ta = Tb * Ta_Tb - Te = PSY.get_Te(avr) - K = PSY.get_K(avr) - V_min, V_max = PSY.get_V_lim(avr) #Compute auxiliary parameters V_in = V0_ref + Vs - V_th @@ -377,6 +385,7 @@ end function mdl_avr_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.SCRX, TG, P}}, h, @@ -384,7 +393,7 @@ function mdl_avr_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} #Obtain references - V0_ref = get_V_ref(dynamic_device) + V0_ref = p[:refs][:V_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.SCRX) # @@ -401,14 +410,15 @@ function mdl_avr_ode!( #Get parameters << keep avr = PSY.get_avr(dynamic_device) - Ta_Tb = PSY.get_Ta_Tb(avr) # Ta/Tb - Tb = PSY.get_Tb(avr) + params = p[:params][:AVR] + Ta_Tb = params[:Ta_Tb] + Tb = params[:Tb] Ta = Tb * Ta_Tb - Te = PSY.get_Te(avr) - K = PSY.get_K(avr) - V_min, V_max = PSY.get_Efd_lim(avr) #Efd_lim (V_lim) - switch = PSY.get_switch(avr) # reads switch parameters - rc_rfd = PSY.get_rc_rfd(avr) + Te = params[:Te] + K = params[:K] + V_min, V_max = params[:Efd_lim] + switch = PSY.get_switch(avr) + rc_rfd = params[:rc_rfd] #Compute auxiliary parameters << keep V_in = V0_ref + Vs - V_th #sum of V @@ -439,6 +449,7 @@ end function mdl_avr_ode!( device_states::AbstractArray, output_ode::AbstractArray, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.EXST1, TG, P}}, h, @@ -446,7 +457,7 @@ function mdl_avr_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} #Obtain references - V0_ref = get_V_ref(dynamic_device) + V0_ref = p[:refs][:V_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.EXST1) @@ -463,18 +474,20 @@ function mdl_avr_ode!( Vs = inner_vars[V_pss_var] # PSS output Ifd = inner_vars[Xad_Ifd_var] # machine's field current in exciter base - #Get parameters - avr = PSY.get_avr(dynamic_device) - Tr = PSY.get_Tr(avr) - Vi_min, Vi_max = PSY.get_Vi_lim(avr) - Tc = PSY.get_Tc(avr) - Tb = PSY.get_Tb(avr) - Ka = PSY.get_Ka(avr) - Ta = PSY.get_Ta(avr) - Vr_min, Vr_max = PSY.get_Vr_lim(avr) - Kc = PSY.get_Kc(avr) - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) + #Get Parameters + params = p[:params][:AVR] + Tr = params[:Tr] + Vi_min = params[:Vi_lim][:min] + Vi_max = params[:Vi_lim][:max] + Tc = params[:Tc] + Tb = params[:Tb] + Ka = params[:Ka] + Ta = params[:Ta] + Vr_min = params[:Vr_lim][:min] + Vr_max = params[:Vr_lim][:max] + Kc = params[:Kc] + Kf = params[:Kf] + Tf = params[:Tf] #Compute auxiliary parameters V_ref = V0_ref + Vs @@ -501,6 +514,7 @@ end function mdl_avr_ode!( device_states::AbstractArray, output_ode::AbstractArray, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.EXAC1, TG, P}}, h, @@ -508,7 +522,7 @@ function mdl_avr_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} #Obtain references - V_ref = get_V_ref(dynamic_device) + V_ref = p[:refs][:V_ref] #Obtain avr avr = PSY.get_avr(dynamic_device) @@ -530,18 +544,20 @@ function mdl_avr_ode!( Xad_Ifd = inner_vars[Xad_Ifd_var] # machine's field current in exciter base #Get parameters - Tr = PSY.get_Tr(avr) - Tb = PSY.get_Tb(avr) - Tc = PSY.get_Tc(avr) - Ka = PSY.get_Ka(avr) - Ta = PSY.get_Ta(avr) - Vr_min, Vr_max = PSY.get_Vr_lim(avr) - Te = PSY.get_Te(avr) # Te > 0 - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) # Tf > 0 - Kc = PSY.get_Kc(avr) - Kd = PSY.get_Kd(avr) - Ke = PSY.get_Ke(avr) + params = p[:params][:AVR] + Tr = params[:Tr] + Tb = params[:Tb] + Tc = params[:Tc] + Ka = params[:Ka] + Ta = params[:Ta] + Vr_min = params[:Vr_lim][:min] + Vr_max = params[:Vr_lim][:max] + Te = params[:Te] + Kf = params[:Kf] + Tf = params[:Tf] + Kc = params[:Kc] + Kd = params[:Kd] + Ke = params[:Ke] #Obtain saturation Se = saturation_function(avr, Ve) @@ -574,6 +590,7 @@ end function mdl_avr_ode!( device_states::AbstractArray, output_ode::AbstractArray, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.ESST1A, TG, P}}, h, @@ -581,7 +598,7 @@ function mdl_avr_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS} #Obtain references - V0_ref = get_V_ref(dynamic_device) + V0_ref = p[:refs][:V_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.ESST1A) @@ -601,23 +618,27 @@ function mdl_avr_ode!( #Get parameters avr = PSY.get_avr(dynamic_device) + params = p[:params][:AVR] UEL = PSY.get_UEL_flags(avr) VOS = PSY.get_PSS_flags(avr) - Tr = PSY.get_Tr(avr) - Vi_min, Vi_max = PSY.get_Vi_lim(avr) - Tc = PSY.get_Tc(avr) - Tb = PSY.get_Tb(avr) - Tc1 = PSY.get_Tc1(avr) - Tb1 = PSY.get_Tb1(avr) - Ka = PSY.get_Ka(avr) - Ta = PSY.get_Ta(avr) - Va_min, Va_max = PSY.get_Va_lim(avr) - Vr_min, Vr_max = PSY.get_Vr_lim(avr) - Kc = PSY.get_Kc(avr) - Kf = PSY.get_Kf(avr) - Tf = PSY.get_Tf(avr) - K_lr = PSY.get_K_lr(avr) - I_lr = PSY.get_I_lr(avr) + Tr = params[:Tr] + Vi_min = params[:Vi_lim][:min] + Vi_max = params[:Vi_lim][:max] + Tc = params[:Tc] + Tb = params[:Tb] + Tc1 = params[:Tc1] + Tb1 = params[:Tb1] + Ka = params[:Ka] + Ta = params[:Ta] + Va_min = params[:Va_lim][:min] + Va_max = params[:Va_lim][:max] + Vr_min = params[:Vr_lim][:min] + Vr_max = params[:Vr_lim][:max] + Kc = params[:Kc] + Kf = params[:Kf] + Tf = params[:Tf] + K_lr = params[:K_lr] + I_lr = params[:I_lr] #Compute auxiliary parameters Itemp = K_lr * (Ifd - I_lr) diff --git a/src/models/generator_models/machine_models.jl b/src/models/generator_models/machine_models.jl index a21e74e4d..ecef0cb32 100644 --- a/src/models/generator_models/machine_models.jl +++ b/src/models/generator_models/machine_models.jl @@ -13,6 +13,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -29,11 +30,11 @@ function mdl_machine_ode!( V_tR = inner_vars[VR_gen_var] V_tI = inner_vars[VI_gen_var] - #Get parameters - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd_p = PSY.get_Xd_p(machine) - eq_p = PSY.get_eq_p(machine) + params = p[:params][:Machine] + R = params[:R] + Xd_p = params[:Xd_p] + eq_p = PSY.get_eq_p(PSY.get_machine(get_device(dynamic_device))) + basepower = PSY.get_base_power(dynamic_device) #RI to dq transformation @@ -64,6 +65,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -91,14 +93,15 @@ function mdl_machine_ode!( Vf = inner_vars[Vf_var] #Get parameters - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Td0_p = PSY.get_Td0_p(machine) - Tq0_p = PSY.get_Tq0_p(machine) + params = p[:params][:Machine] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Td0_p = params[:Td0_p] + Tq0_p = params[:Tq0_p] + basepower = PSY.get_base_power(dynamic_device) #RI to dq transformation @@ -133,6 +136,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -166,23 +170,24 @@ function mdl_machine_ode!( Vf = inner_vars[Vf_var] #Get parameters - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xq_pp = PSY.get_Xq_pp(machine) - Xl = PSY.get_Xl(machine) - Td0_p = PSY.get_Td0_p(machine) - Tq0_p = PSY.get_Tq0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) - γ_q2 = PSY.get_γ_q2(machine) + params = p[:params][:Machine] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xq_pp = params[:Xq_pp] + Xl = params[:Xl] + Td0_p = params[:Td0_p] + Tq0_p = params[:Tq0_p] + Td0_pp = params[:Td0_pp] + Tq0_pp = params[:Tq0_pp] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] + γ_q2 = params[:γ_q2] + basepower = PSY.get_base_power(dynamic_device) `` #RI to dq transformation @@ -225,6 +230,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -258,21 +264,22 @@ function mdl_machine_ode!( Vf = inner_vars[Vf_var] #Get parameters - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xq_pp = PSY.get_Xq_pp(machine) - Td0_p = PSY.get_Td0_p(machine) - Tq0_p = PSY.get_Tq0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) - T_AA = PSY.get_T_AA(machine) - γd = PSY.get_γd(machine) - γq = PSY.get_γq(machine) + params = p[:params][:Machine] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xq_pp = params[:Xq_pp] + Td0_p = params[:Td0_p] + Tq0_p = params[:Tq0_p] + Td0_pp = params[:Td0_pp] + Tq0_pp = params[:Tq0_pp] + T_AA = params[:T_AA] + γd = params[:γd] + γq = params[:γq] + basepower = PSY.get_base_power(dynamic_device) #RI to dq transformation @@ -313,6 +320,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -400,6 +408,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -435,18 +444,18 @@ function mdl_machine_ode!( Vf = inner_vars[Vf_var] #Get parameters - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xq_pp = PSY.get_Xq_pp(machine) - Td0_p = PSY.get_Td0_p(machine) - Tq0_p = PSY.get_Tq0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) + params = p[:params][:Machine] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xq_pp = params[:Xq_pp] + Td0_p = params[:Td0_p] + Tq0_p = params[:Tq0_p] + Td0_pp = params[:Td0_pp] + Tq0_pp = params[:Tq0_pp] basepower = PSY.get_base_power(dynamic_device) #RI to dq transformation @@ -485,6 +494,7 @@ Refer to Power System Modelling and Scripting by F. Milano for the equations function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -516,18 +526,18 @@ function mdl_machine_ode!( Vf = inner_vars[Vf_var] #Get parameters - machine = PSY.get_machine(dynamic_device) - R = PSY.get_R(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) - Xq_pp = PSY.get_Xq_pp(machine) - Td0_p = PSY.get_Td0_p(machine) - Tq0_p = PSY.get_Tq0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) + params = p[:params][:Machine] + R = params[:R] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xq_pp = params[:Xq_pp] + Td0_p = params[:Td0_p] + Tq0_p = params[:Tq0_p] + Td0_pp = params[:Td0_pp] + Tq0_pp = params[:Tq0_pp] basepower = PSY.get_base_power(dynamic_device) #RI to dq transformation @@ -562,6 +572,7 @@ end function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -599,23 +610,24 @@ function mdl_machine_ode!( Vf = inner_vars[Vf_var] #E_fd: Field voltage #Get parameters - R = PSY.get_R(machine) - Td0_p = PSY.get_Td0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_p = PSY.get_Tq0_p(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xq_p = PSY.get_Xq_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) + params = @view(p[:params][:Machine]) + R = params[:R] + Td0_p = params[:Td0_p] + Td0_pp = params[:Td0_pp] + Tq0_p = params[:Tq0_p] + Tq0_pp = params[:Tq0_pp] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xq_p = params[:Xq_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] + γ_q2 = params[:γ_q2] + γ_qd = params[:γ_qd] Xq_pp = Xd_pp - Xl = PSY.get_Xl(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) - γ_q2 = PSY.get_γ_q2(machine) - γ_qd = PSY.get_γ_qd(machine) basepower = PSY.get_base_power(dynamic_device) #RI to dq transformation @@ -660,6 +672,7 @@ end function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -692,19 +705,20 @@ function mdl_machine_ode!( Vf = inner_vars[Vf_var] #E_fd: Field voltage #Get parameters - R = PSY.get_R(machine) - Td0_p = PSY.get_Td0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) + params = p[:params][:Machine] + R = params[:R] + Td0_p = params[:Td0_p] + Td0_pp = params[:Td0_pp] + Tq0_pp = params[:Tq0_pp] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] Xq_pp = Xd_pp - Xl = PSY.get_Xl(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) basepower = PSY.get_base_power(dynamic_device) #RI to dq transformation @@ -743,6 +757,7 @@ end function mdl_machine_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -775,19 +790,20 @@ function mdl_machine_ode!( Vf = inner_vars[Vf_var] #E_fd: Field voltage #Get parameters - R = PSY.get_R(machine) - Td0_p = PSY.get_Td0_p(machine) - Td0_pp = PSY.get_Td0_pp(machine) - Tq0_pp = PSY.get_Tq0_pp(machine) - Xd = PSY.get_Xd(machine) - Xq = PSY.get_Xq(machine) - Xd_p = PSY.get_Xd_p(machine) - Xd_pp = PSY.get_Xd_pp(machine) + params = p[:params][:Machine] + R = params[:R] + Td0_p = params[:Td0_p] + Td0_pp = params[:Td0_pp] + Tq0_pp = params[:Tq0_pp] + Xd = params[:Xd] + Xq = params[:Xq] + Xd_p = params[:Xd_p] + Xd_pp = params[:Xd_pp] + Xl = params[:Xl] + γ_d1 = params[:γ_d1] + γ_q1 = params[:γ_q1] + γ_d2 = params[:γ_d2] Xq_pp = Xd_pp - Xl = PSY.get_Xl(machine) - γ_d1 = PSY.get_γ_d1(machine) - γ_q1 = PSY.get_γ_q1(machine) - γ_d2 = PSY.get_γ_d2(machine) γ_qd = (Xq - Xl) / (Xd - Xl) basepower = PSY.get_base_power(dynamic_device) @@ -836,6 +852,7 @@ or Power System Stability and Control by P. Kundur, for the equations function mdl_machine_ode!( device_states, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode, inner_vars, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, diff --git a/src/models/generator_models/pss_models.jl b/src/models/generator_models/pss_models.jl index aa7ba9867..a6f4f70b2 100644 --- a/src/models/generator_models/pss_models.jl +++ b/src/models/generator_models/pss_models.jl @@ -115,22 +115,23 @@ end function mdl_pss_ode!( ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.PSSFixed}}, h, t, ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov} - #Update V_pss on inner vars - inner_vars[V_pss_var] = PSY.get_V_pss(PSY.get_pss(dynamic_device)) - + V_pss = p[:params][:PSS][:V_pss] + inner_vars[V_pss_var] = V_pss return end function mdl_pss_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.IEEEST}}, @@ -165,22 +166,24 @@ function mdl_pss_ode!( x_p7 = internal_states[7] # Get Parameters - A1 = PSY.get_A1(pss) - A2 = PSY.get_A2(pss) - A3 = PSY.get_A3(pss) - A4 = PSY.get_A4(pss) - A5 = PSY.get_A5(pss) - A6 = PSY.get_A6(pss) - T1 = PSY.get_T1(pss) - T2 = PSY.get_T2(pss) - T3 = PSY.get_T3(pss) - T4 = PSY.get_T4(pss) - T5 = PSY.get_T5(pss) - T6 = PSY.get_T6(pss) - Ks = PSY.get_Ks(pss) - Ls_min, Ls_max = PSY.get_Ls_lim(pss) - V_cu = PSY.get_Vcu(pss) - V_cl = PSY.get_Vcl(pss) + params = p[:params][:PSS] + A1 = params[:A1] + A2 = params[:A2] + A3 = params[:A3] + A4 = params[:A4] + A5 = params[:A5] + A6 = params[:A6] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + T4 = params[:T4] + T5 = params[:T5] + T6 = params[:T6] + Ks = params[:Ks] + Ls_min = params[:Ls_lim1] + Ls_max = params[:Ls_lim2] + V_cu = params[:Vcu] + V_cl = params[:Vcl] #Compute Parameter Ratios #A6_A2 = A2 < eps() ? 0.0 : (A6 / A2) @@ -230,6 +233,7 @@ end function mdl_pss_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.STAB1}}, @@ -237,9 +241,6 @@ function mdl_pss_ode!( t, ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, TG <: PSY.TurbineGov} - #Get Signal Input Integer - pss = PSY.get_pss(dynamic_device) - # Get Input Signal - only speed deviation for STAB1 external_ix = get_input_port_ix(dynamic_device, PSY.STAB1) ω = device_states[external_ix[1]] # get machine speed @@ -255,13 +256,14 @@ function mdl_pss_ode!( x_p3 = internal_states[3] # state for Lead Lag 2 # Get Parameters - KT = PSY.get_KT(pss) - T = PSY.get_T(pss) - T1T3 = PSY.get_T1T3(pss) - T3 = PSY.get_T3(pss) - T2T4 = PSY.get_T2T4(pss) - T4 = PSY.get_T4(pss) - H_lim = PSY.get_H_lim(pss) + params = p[:params][:PSS] + KT = params[:KT] + T = params[:T] + T1T3 = params[:T1T3] + T3 = params[:T3] + T2T4 = params[:T2T4] + T4 = params[:T4] + H_lim = params[:H_lim] K = T * KT T1 = T3 * T1T3 @@ -287,6 +289,7 @@ end function mdl_pss_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.PSS2A}}, @@ -457,6 +460,7 @@ end function mdl_pss_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.PSS2B}}, @@ -527,26 +531,27 @@ function mdl_pss_ode!( # Get Parameters M_rtf = PSY.get_M_rtf(pss) N_rtf = PSY.get_N_rtf(pss) - Tw1 = PSY.get_Tw1(pss) - Tw2 = PSY.get_Tw2(pss) - T6 = PSY.get_T6(pss) - Tw3 = PSY.get_Tw3(pss) - Tw4 = PSY.get_Tw4(pss) - T7 = PSY.get_T7(pss) - Ks2 = PSY.get_Ks2(pss) - Ks3 = PSY.get_Ks3(pss) - T8 = PSY.get_T8(pss) - T9 = PSY.get_T9(pss) - Ks1 = PSY.get_Ks1(pss) - T1 = PSY.get_T1(pss) - T2 = PSY.get_T2(pss) - T3 = PSY.get_T3(pss) - T4 = PSY.get_T4(pss) - T10 = PSY.get_T10(pss) - T11 = PSY.get_T11(pss) Vs1_min, Vs1_max = PSY.get_Vs1_lim(pss) Vs2_min, Vs2_max = PSY.get_Vs2_lim(pss) Vst_min, Vst_max = PSY.get_Vst_lim(pss) + params = p[:params][:PSS] + Tw1 = params[:Tw1] + Tw2 = params[:Tw2] + T6 = params[:T6] + Tw3 = params[:Tw3] + Tw4 = params[:Tw4] + T7 = params[:T7] + Ks2 = params[:Ks2] + Ks3 = params[:Ks3] + T8 = params[:T8] + T9 = params[:T9] + Ks1 = params[:Ks1] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + T4 = params[:T4] + T10 = params[:T10] + T11 = params[:T11] # Clamp inputs u_1 = clamp(u_1, Vs1_min, Vs1_max) @@ -638,6 +643,7 @@ end function mdl_pss_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, TG, PSY.PSS2C}}, diff --git a/src/models/generator_models/shaft_models.jl b/src/models/generator_models/shaft_models.jl index 1894c692a..58b6f5cfd 100644 --- a/src/models/generator_models/shaft_models.jl +++ b/src/models/generator_models/shaft_models.jl @@ -9,6 +9,7 @@ end function mdl_shaft_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, PSY.SingleMass, A, TG, P}}, @@ -28,9 +29,8 @@ function mdl_shaft_ode!( τm = inner_vars[τm_var] #Get parameters - shaft = PSY.get_shaft(dynamic_device) - H = PSY.get_H(shaft) - D = PSY.get_D(shaft) + H = p[:params][:Shaft][:H] + D = p[:params][:Shaft][:D] #Compute 2 states ODEs output_ode[local_ix[1]] = 2 * π * f0 * (ω - ω_sys) #15.5 @@ -42,6 +42,7 @@ end function mdl_shaft_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, PSY.FiveMassShaft, A, TG, P}}, @@ -71,25 +72,25 @@ function mdl_shaft_ode!( τm = inner_vars[τm_var] #Get parameters - shaft = PSY.get_shaft(dynamic_device) - H = PSY.get_H(shaft) - H_hp = PSY.get_H_hp(shaft) - H_ip = PSY.get_H_ip(shaft) - H_lp = PSY.get_H_lp(shaft) - H_ex = PSY.get_H_ex(shaft) - D = PSY.get_D(shaft) - D_hp = PSY.get_D_hp(shaft) - D_ip = PSY.get_D_ip(shaft) - D_lp = PSY.get_D_lp(shaft) - D_ex = PSY.get_D_ex(shaft) - D_12 = PSY.get_D_12(shaft) - D_23 = PSY.get_D_23(shaft) - D_34 = PSY.get_D_34(shaft) - D_45 = PSY.get_D_45(shaft) - K_hp = PSY.get_K_hp(shaft) - K_ip = PSY.get_K_ip(shaft) - K_lp = PSY.get_K_lp(shaft) - K_ex = PSY.get_K_ex(shaft) + params = p[:params][:Shaft] + H = params[:H] + H_hp = params[:H_hp] + H_ip = params[:H_ip] + H_lp = params[:H_lp] + H_ex = params[:H_ex] + D = params[:D] + D_hp = params[:D_hp] + D_ip = params[:D_ip] + D_lp = params[:D_lp] + D_ex = params[:D_ex] + D_12 = params[:D_12] + D_23 = params[:D_23] + D_34 = params[:D_34] + D_45 = params[:D_45] + K_hp = params[:K_hp] + K_ip = params[:K_ip] + K_lp = params[:K_lp] + K_ex = params[:K_ex] #Compute 10 states ODEs #15.51 output_ode[local_ix[1]] = 2.0 * π * f0 * (ω - ω_sys) diff --git a/src/models/generator_models/tg_models.jl b/src/models/generator_models/tg_models.jl index 2cf8baad4..60f547962 100644 --- a/src/models/generator_models/tg_models.jl +++ b/src/models/generator_models/tg_models.jl @@ -59,23 +59,23 @@ end function mdl_tg_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.TGFixed, P}}, h, t, ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS} - - #Update inner vars - P_ref = get_P_ref(device) - inner_vars[τm_var] = P_ref * PSY.get_efficiency(PSY.get_prime_mover(device)) - + efficiency = p[:params][:TurbineGov][:efficiency] + P_ref = p[:refs][:P_ref] + inner_vars[τm_var] = P_ref * efficiency return end function mdl_tg_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.TGTypeI, P}}, @@ -84,8 +84,8 @@ function mdl_tg_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS} #Obtain references - ω_ref = get_ω_ref(device) - P_ref = get_P_ref(device) + ω_ref = p[:refs][:ω_ref] + P_ref = p[:refs][:P_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(device, PSY.TGTypeI) @@ -101,14 +101,16 @@ function mdl_tg_ode!( ω = @view device_states[external_ix] #Get Parameters - tg = PSY.get_prime_mover(device) - inv_R = PSY.get_R(tg) < eps() ? 0.0 : (1.0 / PSY.get_R(tg)) - Ts = PSY.get_Ts(tg) - Tc = PSY.get_Tc(tg) - T3 = PSY.get_T3(tg) - T4 = PSY.get_T4(tg) - T5 = PSY.get_T5(tg) - V_min, V_max = PSY.get_valve_position_limits(tg) + params = p[:params][:TurbineGov] + R = params[:R] + Ts = params[:Ts] + Tc = params[:Tc] + T3 = params[:T3] + T4 = params[:T4] + T5 = params[:T5] + V_min = params[:valve_position_limits][:min] + V_max = params[:valve_position_limits][:max] + inv_R = R < eps() ? 0.0 : (1.0 / R) #Compute auxiliary parameters P_in_sat = clamp(P_ref + inv_R * (ω_ref - ω[1]), V_min, V_max) @@ -132,6 +134,7 @@ end function mdl_tg_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.TGTypeII, P}}, @@ -140,8 +143,8 @@ function mdl_tg_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS} #Obtain references - ω_ref = get_ω_ref(device) - P_ref = get_P_ref(device) + ω_ref = p[:refs][:ω_ref] + P_ref = p[:refs][:P_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(device, PSY.TGTypeII) @@ -154,11 +157,12 @@ function mdl_tg_ode!( external_ix = get_input_port_ix(device, PSY.TGTypeII) ω = @view device_states[external_ix] - #Get Parameters - tg = PSY.get_prime_mover(device) - inv_R = PSY.get_R(tg) < eps() ? 0.0 : (1.0 / PSY.get_R(tg)) - T1 = PSY.get_T1(tg) - T2 = PSY.get_T2(tg) + #Get parameters + params = p[:params][:TurbineGov] + R = params[:R] + T1 = params[:T1] + T2 = params[:T2] + inv_R = R < eps() ? 0.0 : (1.0 / R) #Compute block derivatives y_ll1, dxg_dt = lead_lag(inv_R * (ω_ref - ω[1]), xg, 1.0, T1, T2) @@ -176,6 +180,7 @@ end function mdl_tg_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.SteamTurbineGov1, P}}, @@ -186,7 +191,7 @@ function mdl_tg_ode!( #Obtain TG tg = PSY.get_prime_mover(device) #Obtain references - P_ref = get_P_ref(device) + P_ref = p[:refs][:P_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(device, typeof(tg)) @@ -201,13 +206,15 @@ function mdl_tg_ode!( ω = @view device_states[external_ix] #Get Parameters - tg = PSY.get_prime_mover(device) - inv_R = PSY.get_R(tg) < eps() ? 0.0 : (1.0 / PSY.get_R(tg)) - T1 = PSY.get_T1(tg) - T2 = PSY.get_T2(tg) - V_min, V_max = PSY.get_valve_position_limits(tg) - T3 = PSY.get_T3(tg) - D_T = PSY.get_D_T(tg) + params = @view(p[:params][:TurbineGov]) + R = params[:R] + T1 = params[:T1] + V_min = params[:valve_position_limits][:min] + V_max = params[:valve_position_limits][:max] + T2 = params[:T2] + T3 = params[:T3] + D_T = params[:D_T] + inv_R = R < eps() ? 0.0 : (1.0 / R) #Compute auxiliary parameters ref_in = inv_R * (P_ref - (ω[1] - 1.0)) @@ -230,6 +237,7 @@ end function mdl_tg_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.GasTG, P}}, @@ -238,7 +246,7 @@ function mdl_tg_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS} #Obtain references - P_ref = get_P_ref(device) + P_ref = p[:refs][:P_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(device, PSY.GasTG) @@ -254,18 +262,20 @@ function mdl_tg_ode!( ω = @view device_states[external_ix] #Get Parameters - tg = PSY.get_prime_mover(device) - inv_R = PSY.get_R(tg) < eps() ? 0.0 : (1.0 / PSY.get_R(tg)) - T1 = PSY.get_T1(tg) - T2 = PSY.get_T2(tg) - T3 = PSY.get_T3(tg) - D_turb = PSY.get_D_turb(tg) - AT = PSY.get_AT(tg) - KT = PSY.get_Kt(tg) - V_min, V_max = PSY.get_V_lim(tg) + params = p[:params][:TurbineGov] + R = params[:R] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + AT = params[:AT] + Kt = params[:Kt] + V_min = params[:V_lim][:min] + V_max = params[:V_lim][:max] + D_turb = params[:D_turb] + inv_R = R < eps() ? 0.0 : (1.0 / R) #Compute auxiliary parameters - x_in = min((P_ref - inv_R * (ω[1] - 1.0)), (AT + KT * (AT - x_g3))) + x_in = min((P_ref - inv_R * (ω[1] - 1.0)), (AT + Kt * (AT - x_g3))) #Compute block derivatives x_g1_sat, dxg1_dt = low_pass_nonwindup(x_in, x_g1, 1.0, T1, V_min, V_max) @@ -289,6 +299,7 @@ end function mdl_tg_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.HydroTurbineGov, P}}, @@ -297,8 +308,8 @@ function mdl_tg_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS} #Obtain references - P_ref = get_P_ref(device) - ω_ref = get_ω_ref(device) + P_ref = p[:refs][:P_ref] + ω_ref = p[:refs][:ω_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(device, PSY.HydroTurbineGov) @@ -316,19 +327,19 @@ function mdl_tg_ode!( Δω = ω - ω_ref #Get Parameters - tg = PSY.get_prime_mover(device) - R = PSY.get_R(tg) - r = PSY.get_r(tg) - Tr = PSY.get_Tr(tg) - Tf = PSY.get_Tf(tg) - Tg = PSY.get_Tg(tg) - - VELM = PSY.get_VELM(tg) - G_min, G_max = PSY.get_gate_position_limits(tg) - Tw = PSY.get_Tw(tg) - At = PSY.get_At(tg) - D_T = PSY.get_D_T(tg) - q_nl = PSY.get_q_nl(tg) + params = p[:params][:TurbineGov] + R = params[:R] + r = params[:r] + Tr = params[:Tr] + Tf = params[:Tf] + Tg = params[:Tg] + VELM = params[:VELM] + G_min = params[:gate_position_limits][:min] + G_max = params[:gate_position_limits][:max] + Tw = params[:Tw] + At = params[:At] + D_T = params[:D_T] + q_nl = params[:q_nl] #Compute block derivatives c, dxg2_dt = pi_block_nonwindup(x_g1, x_g2, 1.0 / r, 1.0 / (r * Tr), G_min, G_max) @@ -359,6 +370,7 @@ end function mdl_tg_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.DEGOV, P}}, @@ -367,7 +379,7 @@ function mdl_tg_ode!( ) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS} #Obtain references - P_ref = get_P_ref(device) + P_ref = p[:refs][:P_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix(device, PSY.DEGOV) @@ -385,14 +397,15 @@ function mdl_tg_ode!( ω = @view device_states[external_ix] #Get Parameters + params = p[:params][:TurbineGov] + T1 = params[:T1] + T2 = params[:T2] + T3 = params[:T3] + K = params[:K] + T4 = params[:T4] + T5 = params[:T5] + T6 = params[:T6] tg = PSY.get_prime_mover(device) - T1 = PSY.get_T1(tg) - T2 = PSY.get_T2(tg) - T3 = PSY.get_T3(tg) - K = PSY.get_K(tg) - T4 = PSY.get_T4(tg) - T5 = PSY.get_T5(tg) - T6 = PSY.get_T6(tg) Td = PSY.get_Td(tg) #Compute block derivatives diff --git a/src/models/inverter_models/DCside_models.jl b/src/models/inverter_models/DCside_models.jl index 2d73aa008..5bae6610e 100644 --- a/src/models/inverter_models/DCside_models.jl +++ b/src/models/inverter_models/DCside_models.jl @@ -9,6 +9,7 @@ end function mdl_DCside_ode!( ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ @@ -24,7 +25,6 @@ function mdl_DCside_ode!( F <: PSY.Filter, L <: Union{Nothing, PSY.OutputCurrentLimiter}, } - #Update inner_vars - inner_vars[Vdc_var] = PSY.get_voltage(PSY.get_dc_source(dynamic_device)) + inner_vars[Vdc_var] = p[:params][:DCSource][:voltage] end diff --git a/src/models/inverter_models/converter_models.jl b/src/models/inverter_models/converter_models.jl index c45831c94..ba90b61fb 100644 --- a/src/models/inverter_models/converter_models.jl +++ b/src/models/inverter_models/converter_models.jl @@ -9,6 +9,7 @@ end function mdl_converter_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{PSY.AverageConverter, O, IC, DC, P, F, L}, @@ -42,6 +43,7 @@ end function mdl_converter_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{ @@ -72,22 +74,24 @@ function mdl_converter_ode!( Iq_cmd = inner_vars[Iq_ic_var] #Get Converter parameters + params = p[:params][:Converter] + T_g = params[:T_g] + Rrpwr = params[:Rrpwr] + Brkpt = params[:Brkpt] + Zerox = params[:Zerox] + Lvpl1 = params[:Lvpl1] + Vo_lim = params[:Vo_lim] + Lv_pnt0 = params[:Lv_pnts][:min] + Lv_pnt1 = params[:Lv_pnts][:max] + T_fltr = params[:T_fltr] + K_hv = params[:K_hv] + Iqr_min = params[:Iqr_lims][:min] + Iqr_max = params[:Iqr_lims][:max] + Q_ref = params[:Q_ref] + R_source = params[:R_source] + X_source = params[:X_source] converter = PSY.get_converter(dynamic_device) - T_g = PSY.get_T_g(converter) - Rrpwr = PSY.get_Rrpwr(converter) - Brkpt = PSY.get_Brkpt(converter) - Zerox = PSY.get_Zerox(converter) - Lvpl1 = PSY.get_Lvpl1(converter) - Vo_lim = PSY.get_Vo_lim(converter) - Lv_pnt0, Lv_pnt1 = PSY.get_Lv_pnts(converter) - # Io_lim = PSY.get_Io_lim(converter) - T_fltr = PSY.get_T_fltr(converter) - K_hv = PSY.get_K_hv(converter) - Iqr_min, Iqr_max = PSY.get_Iqr_lims(converter) Lvpl_sw = PSY.get_Lvpl_sw(converter) - Q_ref = PSY.get_Q_ref(converter) - R_source = PSY.get_R_source(converter) - X_source = PSY.get_X_source(converter) #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.RenewableEnergyConverterTypeA) @@ -160,7 +164,6 @@ function mdl_converter_ode!( #Compute converter voltage Vr_cnv, Vi_cnv = V_cnv_calc(Ir_cnv, Ii_cnv, V_R, V_I) - #Update ODEs output_ode[local_ix[1]] = Ip_binary * (1.0 / T_g) * Ip_in #(Ip_cmd - Ip) output_ode[local_ix[2]] = (1.0 / T_g) * Iq_in # (Iq_cmd - Iq) @@ -177,6 +180,7 @@ end function mdl_converter_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + device_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{ diff --git a/src/models/inverter_models/filter_models.jl b/src/models/inverter_models/filter_models.jl index 1d034c480..3aeb0d8d5 100644 --- a/src/models/inverter_models/filter_models.jl +++ b/src/models/inverter_models/filter_models.jl @@ -35,6 +35,7 @@ end function mdl_filter_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -61,14 +62,13 @@ function mdl_filter_ode!( Vi_cnv = inner_vars[Vi_cnv_var] #Get parameters - filter = PSY.get_filter(dynamic_device) f0 = get_system_base_frequency(dynamic_device) ωb = 2 * pi * f0 - lf = PSY.get_lf(filter) - rf = PSY.get_rf(filter) - cf = PSY.get_cf(filter) - lg = PSY.get_lg(filter) - rg = PSY.get_rg(filter) + lf = p[:params][:Filter][:lf] + rf = p[:params][:Filter][:rf] + cf = p[:params][:Filter][:cf] + lg = p[:params][:Filter][:lg] + rg = p[:params][:Filter][:rg] basepower = PSY.get_base_power(dynamic_device) sys_Sbase = get_system_base_power(dynamic_device) @@ -119,6 +119,7 @@ end function mdl_filter_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_r::AbstractArray{<:ACCEPTED_REAL_TYPES}, current_i::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -140,9 +141,8 @@ function mdl_filter_ode!( ratio_power = basepower / sys_Sbase #Obtain parameters - filt = PSY.get_filter(dynamic_device) - rf = PSY.get_rf(filt) - lf = PSY.get_lf(filt) + rf = p[:params][:Filter][:rf] + lf = p[:params][:Filter][:lf] Vr_cnv = inner_vars[Vr_cnv_var] Vi_cnv = inner_vars[Vi_cnv_var] diff --git a/src/models/inverter_models/frequency_estimator_models.jl b/src/models/inverter_models/frequency_estimator_models.jl index 358ef813e..ba43e5485 100644 --- a/src/models/inverter_models/frequency_estimator_models.jl +++ b/src/models/inverter_models/frequency_estimator_models.jl @@ -9,6 +9,7 @@ end function mdl_freq_estimator_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{PSY.DynamicInverter{C, O, IC, DC, PSY.KauraPLL, F, L}}, @@ -29,10 +30,11 @@ function mdl_freq_estimator_ode!( Vi_filter = device_states[external_ix[2]] #Get parameters - pll_control = PSY.get_freq_estimator(dynamic_device) - ω_lp = PSY.get_ω_lp(pll_control) - kp_pll = PSY.get_kp_pll(pll_control) - ki_pll = PSY.get_ki_pll(pll_control) + params = p[:params][:FrequencyEstimator] + ω_lp = params[:ω_lp] + kp_pll = params[:kp_pll] + ki_pll = params[:ki_pll] + f0 = get_system_base_frequency(dynamic_device) ωb = 2.0 * pi * f0 @@ -75,6 +77,7 @@ end function mdl_freq_estimator_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{ @@ -97,10 +100,10 @@ function mdl_freq_estimator_ode!( Vi_filter = device_states[external_ix[2]] #Get parameters - pll_control = PSY.get_freq_estimator(dynamic_device) - ω_lp = PSY.get_ω_lp(pll_control) - kp_pll = PSY.get_kp_pll(pll_control) - ki_pll = PSY.get_ki_pll(pll_control) + params = p[:params][:FrequencyEstimator] + ω_lp = params[:ω_lp] + kp_pll = params[:kp_pll] + ki_pll = params[:ki_pll] f0 = get_system_base_frequency(dynamic_device) ωb = 2.0 * pi * f0 @@ -138,6 +141,7 @@ end function mdl_freq_estimator_ode!( ::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{ @@ -153,11 +157,8 @@ function mdl_freq_estimator_ode!( F <: PSY.Filter, L <: Union{Nothing, PSY.OutputCurrentLimiter}, } - #Get parameters - pll_control = PSY.get_freq_estimator(dynamic_device) - frequency = PSY.get_frequency(pll_control) - + frequency = p[:params][:FrequencyEstimator][:frequency] #Update inner_vars #PLL frequency inner_vars[ω_freq_estimator_var] = frequency diff --git a/src/models/inverter_models/inner_control_models.jl b/src/models/inverter_models/inner_control_models.jl index 92c83502f..6b4b4c369 100644 --- a/src/models/inverter_models/inner_control_models.jl +++ b/src/models/inverter_models/inner_control_models.jl @@ -31,6 +31,7 @@ end function _mdl_ode_RE_inner_controller_B!( inner_controller_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_controller_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + inner_controller_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::Val{0}, inner_control::PSY.RECurrentControlB, dynamic_device::DynamicWrapper{ @@ -54,12 +55,14 @@ function _mdl_ode_RE_inner_controller_B!( #Get Current Controller parameters PQ_Flag = PSY.get_PQ_Flag(inner_control) - dbd1, dbd2 = PSY.get_dbd_pnts(inner_control) - K_qv = PSY.get_K_qv(inner_control) - I_ql1, I_qh1 = PSY.get_Iqinj_lim(inner_control) - V_ref0 = PSY.get_V_ref0(inner_control) - T_rv = PSY.get_T_rv(inner_control) - T_iq = PSY.get_T_iq(inner_control) + T_rv = inner_controller_parameters[:T_rv] + dbd1 = inner_controller_parameters[:dbd_pnts1] + dbd2 = inner_controller_parameters[:dbd_pnts2] + K_qv = inner_controller_parameters[:K_qv] + I_ql1 = inner_controller_parameters[:Iqinj_lim][:min] + I_qh1 = inner_controller_parameters[:Iqinj_lim][:max] + V_ref0 = inner_controller_parameters[:V_ref0] + T_iq = inner_controller_parameters[:T_iq] #Read local states Vt_filt = inner_controller_states[1] @@ -88,6 +91,7 @@ end function _mdl_ode_RE_inner_controller_B!( inner_controller_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_controller_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + inner_controller_parameters::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::Val{1}, inner_control::PSY.RECurrentControlB, dynamic_device::DynamicWrapper{ @@ -111,13 +115,15 @@ function _mdl_ode_RE_inner_controller_B!( #Get Current Controller parameters PQ_Flag = PSY.get_PQ_Flag(inner_control) - K_vp = PSY.get_K_vp(inner_control) - K_vi = PSY.get_K_vi(inner_control) - V_ref0 = PSY.get_V_ref0(inner_control) - dbd1, dbd2 = PSY.get_dbd_pnts(inner_control) - K_qv = PSY.get_K_qv(inner_control) - I_ql1, I_qh1 = PSY.get_Iqinj_lim(inner_control) - T_rv = PSY.get_T_rv(inner_control) + T_rv = inner_controller_parameters[:T_rv] + dbd1 = inner_controller_parameters[:dbd_pnts1] + dbd2 = inner_controller_parameters[:dbd_pnts2] + K_qv = inner_controller_parameters[:K_qv] + I_ql1 = inner_controller_parameters[:Iqinj_lim][:min] + I_qh1 = inner_controller_parameters[:Iqinj_lim][:max] + K_vp = inner_controller_parameters[:K_vp] + K_vi = inner_controller_parameters[:K_vi] + V_ref0 = inner_controller_parameters[:V_ref0] #Read local states Vt_filt = inner_controller_states[1] @@ -153,6 +159,7 @@ end function mdl_inner_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{ @@ -191,23 +198,21 @@ function mdl_inner_ode!( v_refr = inner_vars[V_oc_var] Vdc = inner_vars[Vdc_var] - #Get Voltage Controller parameters - inner_control = PSY.get_inner_control(dynamic_device) - filter = PSY.get_filter(dynamic_device) - kpv = PSY.get_kpv(inner_control) - kiv = PSY.get_kiv(inner_control) - kffi = PSY.get_kffi(inner_control) - cf = PSY.get_cf(filter) - rv = PSY.get_rv(inner_control) - lv = PSY.get_lv(inner_control) + cf = p[:params][:Filter][:cf] + lf = p[:params][:Filter][:lf] - #Get Current Controller parameters - kpc = PSY.get_kpc(inner_control) - kic = PSY.get_kic(inner_control) - kffv = PSY.get_kffv(inner_control) - lf = PSY.get_lf(filter) - ωad = PSY.get_ωad(inner_control) - kad = PSY.get_kad(inner_control) + #Get Voltage Controller parameters + params = p[:params][:InnerControl] + kpv = params[:kpv] + kiv = params[:kiv] + kffv = params[:kffv] + rv = params[:rv] + lv = params[:lv] + kpc = params[:kpc] + kic = params[:kic] + kffi = params[:kffi] + ωad = params[:ωad] + kad = params[:kad] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.VoltageModeControl) @@ -411,6 +416,7 @@ end function mdl_inner_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{C, O, PSY.CurrentModeControl, DC, P, F, L}, @@ -441,12 +447,10 @@ function mdl_inner_ode!( Vdc = inner_vars[Vdc_var] #Get Current Controller parameters - inner_control = PSY.get_inner_control(dynamic_device) - filter = PSY.get_filter(dynamic_device) - kpc = PSY.get_kpc(inner_control) - kic = PSY.get_kic(inner_control) - kffv = PSY.get_kffv(inner_control) - lf = PSY.get_lf(filter) + kpc = p[:params][:InnerControl][:kpc] + kic = p[:params][:InnerControl][:kic] + kffv = p[:params][:InnerControl][:kffv] + lf = p[:params][:Filter][:lf] #Obtain indices for component w/r to device local_ix = get_local_state_ix(dynamic_device, PSY.CurrentModeControl) @@ -485,6 +489,7 @@ end function mdl_inner_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, dynamic_device::DynamicWrapper{ PSY.DynamicInverter{C, O, PSY.RECurrentControlB, DC, P, F, L}, @@ -508,13 +513,14 @@ function mdl_inner_ode!( #Define internal states for Inner Control internal_states = @view device_states[local_ix] internal_ode = @view output_ode[local_ix] - + internal_parameters = @view p[:params][:InnerControl] # TODO: Voltage Dip Freeze logic #Dispatch inner controller ODE calculation _mdl_ode_RE_inner_controller_B!( internal_ode, internal_states, + internal_parameters, Val(Q_Flag), inner_control, dynamic_device, diff --git a/src/models/inverter_models/outer_control_models.jl b/src/models/inverter_models/outer_control_models.jl index 316d97ba7..703fdc1c6 100644 --- a/src/models/inverter_models/outer_control_models.jl +++ b/src/models/inverter_models/outer_control_models.jl @@ -16,6 +16,7 @@ end function _mdl_ode_RE_active_controller_AB!( active_controller_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, active_controller_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, p_elec_out::ACCEPTED_REAL_TYPES, ω_sys::ACCEPTED_REAL_TYPES, Vt_filt::ACCEPTED_REAL_TYPES, @@ -46,24 +47,31 @@ function _mdl_ode_RE_active_controller_AB!( } #Obtain external parameters - p_ref = get_P_ref(dynamic_device) - ω_ref = get_ω_ref(dynamic_device) + p_ref = p[:refs][:P_ref] + ω_ref = p[:refs][:ω_ref] # To do: Obtain proper frequency for a plant. For now using the system frequency. ω_plant = ω_sys - #Obtain additional Active Power Controller parameters - K_pg = PSY.get_K_pg(active_power_control) - K_ig = PSY.get_K_ig(active_power_control) - T_p = PSY.get_T_p(active_power_control) + #Obtain additional Active Power Controller parameters + params = p[:params][:OuterControl][:ActivePowerControl] + K_pg = params[:K_pg] + K_ig = params[:K_ig] + T_p = params[:T_p] + fe_min = params[:fe_lim][:min] + fe_max = params[:fe_lim][:max] + P_min = params[:P_lim][:min] + P_max = params[:P_lim][:max] + T_g = params[:T_g] + D_dn = params[:D_dn] + D_up = params[:D_up] + dP_min = params[:dP_lim][:min] + dP_max = params[:dP_lim][:max] + P_min_inner = params[:P_lim_inner][:min] + P_max_inner = params[:P_lim_inner][:max] + T_pord = params[:T_pord] + + # Not considered parameters because not named tuple fdbd1, fdbd2 = PSY.get_fdbd_pnts(active_power_control) - fe_min, fe_max = PSY.get_fe_lim(active_power_control) - P_min, P_max = PSY.get_P_lim(active_power_control) - T_g = PSY.get_T_g(active_power_control) - D_dn = PSY.get_D_dn(active_power_control) - D_up = PSY.get_D_up(active_power_control) - dP_min, dP_max = PSY.get_dP_lim(active_power_control) - P_min_inner, P_max_inner = PSY.get_P_lim_inner(active_power_control) - T_pord = PSY.get_T_pord(active_power_control) #Define internal states for outer control p_flt = active_controller_states[1] @@ -108,6 +116,7 @@ end function _mdl_ode_RE_active_controller_AB!( active_controller_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, active_controller_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, p_elec_out::ACCEPTED_REAL_TYPES, ω_sys::ACCEPTED_REAL_TYPES, Vt_filt::ACCEPTED_REAL_TYPES, @@ -138,9 +147,9 @@ function _mdl_ode_RE_active_controller_AB!( } #Obtain external parameters - p_ref = get_P_ref(dynamic_device) + p_ref = p[:refs][:P_ref] #Obtain additional Active Power Controller parameters - T_pord = PSY.get_T_pord(active_power_control) + T_pord = p[:params][:OuterControl][:ActivePowerControl][:T_pord] #Define internal states for outer control p_ord = active_controller_states[1] @@ -167,6 +176,7 @@ end function _mdl_ode_RE_reactive_controller_AB!( reactive_controller_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, reactive_controller_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, q_elec_out::ACCEPTED_REAL_TYPES, Vt_filt::ACCEPTED_REAL_TYPES, ::Val{0}, @@ -198,23 +208,27 @@ function _mdl_ode_RE_reactive_controller_AB!( } #Obtain external parameters - q_ref = get_Q_ref(dynamic_device) - + q_ref = p[:refs][:Q_ref] + params = p[:params][:OuterControl][:ReactivePowerControl] # Get Reactive Controller parameters - T_fltr = PSY.get_T_fltr(reactive_power_control) - K_p = PSY.get_K_p(reactive_power_control) - K_i = PSY.get_K_i(reactive_power_control) - T_ft = PSY.get_T_ft(reactive_power_control) - T_fv = PSY.get_T_fv(reactive_power_control) + T_fltr = params[:T_fltr] + K_p = params[:K_p] + K_i = params[:K_i] + T_ft = params[:T_ft] + T_fv = params[:T_fv] + e_min = params[:e_lim][:min] + e_max = params[:e_lim][:max] + dbd1 = params[:dbd_pnts1] + dbd2 = params[:dbd_pnts2] + Q_min = params[:Q_lim][:min] + Q_max = params[:Q_lim][:max] # V_frz not implemented yet - # V_frz = PSY.get_V_frz(reactive_power_control) - e_min, e_max = PSY.get_e_lim(reactive_power_control) - dbd1, dbd2 = PSY.get_dbd_pnts(reactive_power_control) - Q_min, Q_max = PSY.get_Q_lim(reactive_power_control) - Q_min_inner, Q_max_inner = PSY.get_Q_lim_inner(reactive_power_control) - V_min, V_max = PSY.get_V_lim(reactive_power_control) - K_qp = PSY.get_K_qp(reactive_power_control) - K_qi = PSY.get_K_qi(reactive_power_control) + Q_min_inner = params[:Q_lim_inner][:min] + Q_max_inner = params[:Q_lim_inner][:max] + V_min = params[:V_lim][:min] + V_max = params[:V_lim][:max] + K_qp = params[:K_qp] + K_qi = params[:K_qi] #Define internal states for Reactive Control q_flt = reactive_controller_states[1] @@ -251,6 +265,7 @@ end function _mdl_ode_RE_reactive_controller_AB!( reactive_controller_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, reactive_controller_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, q_elec_out::ACCEPTED_REAL_TYPES, Vt_filt::ACCEPTED_REAL_TYPES, ::Val{0}, @@ -281,20 +296,21 @@ function _mdl_ode_RE_reactive_controller_AB!( L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters - q_ref = get_Q_ref(dynamic_device) - + q_ref = p[:refs][:Q_ref] # Get Reactive Controller parameters - T_fltr = PSY.get_T_fltr(reactive_power_control) - K_p = PSY.get_K_p(reactive_power_control) - K_i = PSY.get_K_i(reactive_power_control) - T_ft = PSY.get_T_ft(reactive_power_control) - T_fv = PSY.get_T_fv(reactive_power_control) + params = p[:params][:OuterControl][:ReactivePowerControl] + T_fltr = params[:T_fltr] + K_p = params[:K_p] + K_i = params[:K_i] + T_ft = params[:T_ft] + T_fv = params[:T_fv] + e_min = params[:e_lim][:min] + e_max = params[:e_lim][:max] + dbd1 = params[:dbd_pnts1] + dbd2 = params[:dbd_pnts2] + Q_min = params[:Q_lim][:min] + Q_max = params[:Q_lim][:max] # V_frz not implemented yet - # V_frz = PSY.get_V_frz(reactive_power_control) - e_min, e_max = PSY.get_e_lim(reactive_power_control) - dbd1, dbd2 = PSY.get_dbd_pnts(reactive_power_control) - Q_min, Q_max = PSY.get_Q_lim(reactive_power_control) - #Define internal states for Reactive Control q_flt = reactive_controller_states[1] ξq_oc = reactive_controller_states[2] @@ -328,6 +344,7 @@ end function _mdl_ode_RE_reactive_controller_AB!( reactive_controller_ode, reactive_controller_states, + p, q_elec_out, Vt_filt, ::Val{1}, @@ -358,32 +375,38 @@ function _mdl_ode_RE_reactive_controller_AB!( L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters - V_ref = get_V_ref(dynamic_device) + V_ref = p[:refs][:V_ref] #Obtain regulated voltage (assumed to be terminal voltage) V_reg = sqrt(inner_vars[Vr_inv_var]^2 + inner_vars[Vi_inv_var]^2) I_R = inner_vars[Ir_inv_var] I_I = inner_vars[Ii_inv_var] - # Get Reactive Controller parameters - T_fltr = PSY.get_T_fltr(reactive_power_control) - K_p = PSY.get_K_p(reactive_power_control) - K_c = PSY.get_K_c(reactive_power_control) - R_c = PSY.get_R_c(reactive_power_control) - X_c = PSY.get_R_c(reactive_power_control) VC_Flag = PSY.get_VC_Flag(reactive_power_control) - K_i = PSY.get_K_i(reactive_power_control) - T_ft = PSY.get_T_ft(reactive_power_control) - T_fv = PSY.get_T_fv(reactive_power_control) + + # Get Reactive Controller parameters + params = p[:params][:OuterControl][:ReactivePowerControl] + T_fltr = params[:T_fltr] + K_p = params[:K_p] + K_i = params[:K_i] + T_ft = params[:T_ft] + T_fv = params[:T_fv] + e_min = params[:e_lim][:min] + e_max = params[:e_lim][:max] + dbd1 = params[:dbd_pnts1] + dbd2 = params[:dbd_pnts2] + Q_min = params[:Q_lim][:min] + Q_max = params[:Q_lim][:max] # V_frz not implemented yet - # V_frz = PSY.get_V_frz(reactive_power_control) - e_min, e_max = PSY.get_e_lim(reactive_power_control) - dbd1, dbd2 = PSY.get_dbd_pnts(reactive_power_control) - Q_min, Q_max = PSY.get_Q_lim(reactive_power_control) - Q_min_inner, Q_max_inner = PSY.get_Q_lim_inner(reactive_power_control) - V_min, V_max = PSY.get_V_lim(reactive_power_control) - K_qp = PSY.get_K_qp(reactive_power_control) - K_qi = PSY.get_K_qi(reactive_power_control) + R_c = params[:R_c] + X_c = params[:X_c] + K_c = params[:K_c] + Q_min_inner = params[:Q_lim_inner][:min] + Q_max_inner = params[:Q_lim_inner][:max] + V_min = params[:V_lim][:min] + V_max = params[:V_lim][:max] + K_qp = params[:K_qp] + K_qi = params[:K_qi] #Define internal states for Reactive Control V_cflt = reactive_controller_states[1] @@ -436,6 +459,7 @@ end function _mdl_ode_RE_reactive_controller_AB!( reactive_controller_ode, reactive_controller_states, + p, q_elec_out, Vt_filt, ::Val{1}, @@ -466,7 +490,7 @@ function _mdl_ode_RE_reactive_controller_AB!( L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters - V_ref = get_V_ref(dynamic_device) + V_ref = p[:refs][:V_ref] #Obtain regulated voltage (assumed to be terminal voltage) V_reg = sqrt(inner_vars[Vr_inv_var]^2 + inner_vars[Vi_inv_var]^2) @@ -474,21 +498,26 @@ function _mdl_ode_RE_reactive_controller_AB!( I_I = inner_vars[Ii_inv_var] # Get Reactive Controller parameters - T_fltr = PSY.get_T_fltr(reactive_power_control) - K_p = PSY.get_K_p(reactive_power_control) - K_c = PSY.get_K_c(reactive_power_control) - R_c = PSY.get_R_c(reactive_power_control) - X_c = PSY.get_R_c(reactive_power_control) VC_Flag = PSY.get_VC_Flag(reactive_power_control) - K_i = PSY.get_K_i(reactive_power_control) - T_ft = PSY.get_T_ft(reactive_power_control) - T_fv = PSY.get_T_fv(reactive_power_control) + + params = p[:params][:OuterControl][:ReactivePowerControl] + T_fltr = params[:T_fltr] + K_p = params[:K_p] + K_i = params[:K_i] + T_ft = params[:T_ft] + T_fv = params[:T_fv] + e_min = params[:e_lim][:min] + e_max = params[:e_lim][:max] + dbd1 = params[:dbd_pnts1] + dbd2 = params[:dbd_pnts2] + Q_min = params[:Q_lim][:min] + Q_max = params[:Q_lim][:max] # V_frz not implemented yet - # V_frz = PSY.get_V_frz(reactive_power_control) - e_min, e_max = PSY.get_e_lim(reactive_power_control) - dbd1, dbd2 = PSY.get_dbd_pnts(reactive_power_control) - Q_min, Q_max = PSY.get_Q_lim(reactive_power_control) - V_min, V_max = PSY.get_V_lim(reactive_power_control) + R_c = params[:R_c] + X_c = params[:X_c] + K_c = params[:K_c] + V_min = params[:V_lim][:min] + V_max = params[:V_lim][:max] #Define internal states for Reactive Control V_cflt = reactive_controller_states[1] @@ -535,6 +564,7 @@ end function mdl_outer_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{ @@ -572,25 +602,19 @@ function mdl_outer_ode!( #Obtain inner variables for component ω_pll = inner_vars[ω_freq_estimator_var] - #Get Active Power Controller parameters - outer_control = PSY.get_outer_control(dynamic_device) - active_power_control = PSY.get_active_power_control(outer_control) - Ta = PSY.get_Ta(active_power_control) #VSM Inertia constant - kd = PSY.get_kd(active_power_control) #VSM damping constant - kω = PSY.get_kω(active_power_control) #Frequency droop gain - f0 = get_system_base_frequency(dynamic_device) - ωb = 2 * pi * f0 #Rated angular frequency + Ta = p[:params][:OuterControl][:ActivePowerControl][:Ta] + kd = p[:params][:OuterControl][:ActivePowerControl][:kd] + kω = p[:params][:OuterControl][:ActivePowerControl][:kω] + kq = p[:params][:OuterControl][:ReactivePowerControl][:kq] + ωf = p[:params][:OuterControl][:ReactivePowerControl][:ωf] - #Get Reactive Power Controller parameters - reactive_power_control = PSY.get_reactive_power_control(outer_control) - kq = PSY.get_kq(reactive_power_control) #Reactive power droop gain - ωf = PSY.get_ωf(reactive_power_control) #Reactive power filter cutoff frequency + q_ref = p[:refs][:Q_ref] + V_ref = p[:refs][:V_ref] + ω_ref = p[:refs][:ω_ref] + p_ref = p[:refs][:P_ref] - #Obtain external parameters - p_ref = get_P_ref(dynamic_device) - ω_ref = get_ω_ref(dynamic_device) - V_ref = get_V_ref(dynamic_device) - q_ref = get_Q_ref(dynamic_device) + f0 = get_system_base_frequency(dynamic_device) + ωb = 2 * pi * f0 #Rated angular frequency #Obtain indices for component w/r to device local_ix = get_local_state_ix( @@ -627,6 +651,7 @@ end function mdl_outer_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{ @@ -662,23 +687,21 @@ function mdl_outer_ode!( Ii_filter = device_states[external_ix[4]] #Get Active Power Controller parameters - outer_control = PSY.get_outer_control(dynamic_device) - active_power_control = PSY.get_active_power_control(outer_control) - Rp = PSY.get_Rp(active_power_control) #Droop Gain - ωz = PSY.get_ωz(active_power_control) #Frequency cutoff frequency + Rp = p[:params][:OuterControl][:ActivePowerControl][:Rp] #Droop Gain + ωz = p[:params][:OuterControl][:ActivePowerControl][:ωz] #Frequency cutoff frequency + f0 = get_system_base_frequency(dynamic_device) ωb = 2 * pi * f0 #Rated angular frequency #Get Reactive Power Controller parameters - reactive_power_control = PSY.get_reactive_power_control(outer_control) - kq = PSY.get_kq(reactive_power_control) #Reactive power droop gain - ωf = PSY.get_ωf(reactive_power_control) #Reactive power filter cutoff frequency + kq = p[:params][:OuterControl][:ReactivePowerControl][:kq] + ωf = p[:params][:OuterControl][:ReactivePowerControl][:ωf] #Obtain external parameters - p_ref = get_P_ref(dynamic_device) - ω_ref = get_ω_ref(dynamic_device) - V_ref = get_V_ref(dynamic_device) - q_ref = get_Q_ref(dynamic_device) + p_ref = p[:refs][:P_ref] + ω_ref = p[:refs][:ω_ref] + V_ref = p[:refs][:V_ref] + q_ref = p[:refs][:Q_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix( @@ -703,6 +726,7 @@ function mdl_outer_ode!( _, dpm_dt = low_pass(p_elec_out, pm, 1.0, 1.0 / ωz) _, dqm_dt = low_pass(q_elec_out, qm, 1.0, 1.0 / ωf) + outer_control = PSY.get_outer_control(dynamic_device) ext = PSY.get_ext(outer_control) bool_val = get(ext, "is_not_reference", 1.0) @@ -721,6 +745,7 @@ end function mdl_outer_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{ @@ -756,21 +781,20 @@ function mdl_outer_ode!( Ii_filter = device_states[external_ix[4]] #Get Active Power Controller parameters - outer_control = PSY.get_outer_control(dynamic_device) - active_power_control = PSY.get_active_power_control(outer_control) - k1 = PSY.get_k1(active_power_control) - ψ = PSY.get_ψ(active_power_control) + params_active = p[:params][:OuterControl][:ActivePowerControl] + k1 = params_active[:k1] + ψ = params_active[:ψ] f0 = get_system_base_frequency(dynamic_device) ωb = 2 * pi * f0 #Rated angular frequency #Get Reactive Power Controller parameters - reactive_power_control = PSY.get_reactive_power_control(outer_control) - k2 = PSY.get_k2(reactive_power_control) + params_reactive = p[:params][:OuterControl][:ReactivePowerControl] + k2 = params_reactive[:k2] #Obtain external parameters - p_ref = get_P_ref(dynamic_device) - V_ref = get_V_ref(dynamic_device) - q_ref = get_Q_ref(dynamic_device) + p_ref = p[:refs][:P_ref] + V_ref = p[:refs][:V_ref] + q_ref = p[:refs][:Q_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix( @@ -799,7 +823,7 @@ function mdl_outer_ode!( (k1 / E_oc) * (-sin(γ) * (p_ref - p_elec_out) + cos(γ) * (q_ref - q_elec_out)) + k2 * (V_ref^2 - E_oc^2) * E_oc ) - + outer_control = PSY.get_outer_control(dynamic_device) ext = PSY.get_ext(outer_control) bool_val = get(ext, "is_not_reference", 1.0) @@ -817,6 +841,7 @@ end function mdl_outer_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{ @@ -856,21 +881,18 @@ function mdl_outer_ode!( ω_pll = inner_vars[ω_freq_estimator_var] #Get Active Power Controller parameters - outer_control = PSY.get_outer_control(dynamic_device) - active_power_control = PSY.get_active_power_control(outer_control) - Kp_p = PSY.get_Kp_p(active_power_control) #Proportional Gain - Ki_p = PSY.get_Ki_p(active_power_control) #Integral Gain - ωz = PSY.get_ωz(active_power_control) #Frequency cutoff frequency + Kp_p = p[:params][:OuterControl][:ActivePowerControl][:Kp_p] + Ki_p = p[:params][:OuterControl][:ActivePowerControl][:Ki_p] + ωz = p[:params][:OuterControl][:ActivePowerControl][:ωz] #Get Reactive Power Controller parameters - reactive_power_control = PSY.get_reactive_power_control(outer_control) - Kp_q = PSY.get_Kp_q(reactive_power_control) #Proportional Gain - Ki_q = PSY.get_Ki_q(reactive_power_control) #Integral Gain - ωf = PSY.get_ωf(reactive_power_control) #Reactive power filter cutoff frequency + Kp_q = p[:params][:OuterControl][:ReactivePowerControl][:Kp_q] + Ki_q = p[:params][:OuterControl][:ReactivePowerControl][:Ki_q] + ωf = p[:params][:OuterControl][:ReactivePowerControl][:ωf] #Obtain external parameters - p_ref = get_P_ref(dynamic_device) - q_ref = get_Q_ref(dynamic_device) + p_ref = p[:refs][:P_ref] + q_ref = p[:refs][:Q_ref] #Obtain indices for component w/r to device local_ix = get_local_state_ix( @@ -912,6 +934,7 @@ end function mdl_outer_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, ω_sys::ACCEPTED_REAL_TYPES, dynamic_device::DynamicWrapper{ @@ -987,6 +1010,7 @@ function mdl_outer_ode!( PSY.ReactiveRenewableControllerAB, }, ) + internal_states = @view device_states[local_ix] internal_ode = @view output_ode[local_ix] active_n_states = PSY.get_n_states(active_power_control) @@ -1002,6 +1026,7 @@ function mdl_outer_ode!( _mdl_ode_RE_active_controller_AB!( active_ode, active_states, + p, p_elec_out, ω_sys, Vt_filt, @@ -1015,6 +1040,7 @@ function mdl_outer_ode!( _mdl_ode_RE_reactive_controller_AB!( reactive_ode, reactive_states, + p, q_elec_out, Vt_filt, Val(Ref_Flag), diff --git a/src/models/load_models.jl b/src/models/load_models.jl index 1ad8a88cf..8465fffa0 100644 --- a/src/models/load_models.jl +++ b/src/models/load_models.jl @@ -37,14 +37,14 @@ Ii_im = V_i * P0 * (V^(α - 2) / V0^α) - V_r * Q0 * (V^(β - 2)/ V0^β) """ function mdl_zip_load!( + p::AbstractArray{<:ACCEPTED_REAL_TYPES}, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, current_i::AbstractArray{T}, wrapper::StaticLoadWrapper, ) where {T <: ACCEPTED_REAL_TYPES} - # Read power flow voltages - #V0_mag_inv = 1.0 / get_V_ref(wrapper) + #V0_mag_inv = 1.0 / p[:refs][:V_ref] V0_mag_inv = 1.0 / PSY.get_magnitude(PSY.get_bus(wrapper)) V0_mag_sq_inv = V0_mag_inv^2 @@ -52,13 +52,13 @@ function mdl_zip_load!( V_mag_inv = 1.0 / V_mag V_mag_sq_inv = V_mag_inv^2 - # Load device parameters - P_power = get_P_power(wrapper) - P_current = get_P_current(wrapper) - P_impedance = get_P_impedance(wrapper) - Q_power = get_Q_power(wrapper) - Q_current = get_Q_current(wrapper) - Q_impedance = get_Q_impedance(wrapper) + # Load device references + P_power = p[:refs][:P_power] + P_current = p[:refs][:P_current] + P_impedance = p[:refs][:P_impedance] + Q_power = p[:refs][:Q_power] + Q_current = p[:refs][:Q_current] + Q_impedance = p[:refs][:Q_impedance] exp_params = get_exp_params(wrapper) Ir_exp = zero(T) Ii_exp = zero(T) diff --git a/src/models/network_model.jl b/src/models/network_model.jl index 640038990..fd26a8aad 100644 --- a/src/models/network_model.jl +++ b/src/models/network_model.jl @@ -1,7 +1,7 @@ function network_model( inputs::SimulationInputs, I_balance::AbstractArray{T}, - voltages::AbstractArray{T}, + voltages::AbstractArray{<:ACCEPTED_REAL_TYPES}, ) where {T <: ACCEPTED_REAL_TYPES} # This operation might need improvement, when the total shunts aren't added the # function is not allocating diff --git a/src/models/source_models.jl b/src/models/source_models.jl index a40342875..17d3d8760 100644 --- a/src/models/source_models.jl +++ b/src/models/source_models.jl @@ -1,4 +1,5 @@ function mdl_source!( + p, voltage_r::T, voltage_i::T, current_r::AbstractArray{T}, @@ -6,11 +7,14 @@ function mdl_source!( static_device::StaticWrapper{PSY.Source}, ) where {T <: ACCEPTED_REAL_TYPES} #Load device parameters - V_R = get_V_ref(static_device) * cos(get_θ_ref(static_device)) - V_I = get_V_ref(static_device) * sin(get_θ_ref(static_device)) - # TODO: field forwarding - R_th = PSY.get_R_th(static_device.device) - X_th = PSY.get_X_th(static_device.device) + R_th = p[:params][:R_th] + X_th = p[:params][:X_th] + #Load device references + V_ref = p[:refs][:V_ref] + θ_ref = p[:refs][:θ_ref] + #@error V_ref + V_R = V_ref * cos(θ_ref) + V_I = V_ref * sin(θ_ref) Zmag = R_th^2 + X_th^2 #update current diff --git a/src/models/system.jl b/src/models/system.jl index ba641f7df..db2216f2e 100644 --- a/src/models/system.jl +++ b/src/models/system.jl @@ -11,7 +11,7 @@ function ResidualModel( T, ForwardDiff.pickchunksize(length(x0_init)), } - if isempty(inputs.delays) + if isempty(get_constant_lags(inputs)) return SystemModel{ResidualModel, NoDelays}( inputs, Ctype{U}(system_residual!, inputs), @@ -27,7 +27,7 @@ end Instantiate an ResidualModel for ODE inputs. """ function ResidualModel(inputs, ::Vector{Float64}, ::Type{Ctype}) where {Ctype <: SimCache} - if isempty(inputs.delays) + if isempty(get_constant_lags(inputs)) return SystemModel{ResidualModel, NoDelays}(inputs, Ctype(system_residual!, inputs)) else error( @@ -40,15 +40,16 @@ function (m::SystemModel{ResidualModel, NoDelays, C})( out::AbstractArray{T}, du::AbstractArray{U}, u::AbstractArray{V}, - p, + p::AbstractArray{W}, t, ) where { C <: Cache, T <: ACCEPTED_REAL_TYPES, U <: ACCEPTED_REAL_TYPES, V <: ACCEPTED_REAL_TYPES, + W <: ACCEPTED_REAL_TYPES, } - system_residual!(out, du, u, m.inputs, m.cache, t) + system_residual!(out, du, u, p, m.inputs, m.cache, t) return end @@ -56,10 +57,16 @@ function system_residual!( out::AbstractVector{T}, dx::AbstractVector{U}, x::AbstractVector{V}, + p::AbstractVector{W}, inputs::SimulationInputs, cache::Cache, t::Float64, -) where {T <: ACCEPTED_REAL_TYPES, U <: ACCEPTED_REAL_TYPES, V <: ACCEPTED_REAL_TYPES} +) where { + T <: ACCEPTED_REAL_TYPES, + U <: ACCEPTED_REAL_TYPES, + V <: ACCEPTED_REAL_TYPES, + W <: ACCEPTED_REAL_TYPES, +} update_global_vars!(cache, inputs, x) M = get_mass_matrix(inputs) @@ -82,10 +89,12 @@ function system_residual!( device_inner_vars = @view inner_vars[get_inner_vars_index(dynamic_device)] device_states = @view x[ix_range] bus_ix = get_bus_ix(dynamic_device) - + device_name = _get_wrapper_name(dynamic_device) + device_parameters = p[device_name] device!( device_states, device_ode_output, + device_parameters, voltage_r[bus_ix], voltage_i[bus_ix], view(current_r, bus_ix), @@ -102,7 +111,10 @@ function system_residual!( for static_load in get_static_loads(inputs) bus_ix = get_bus_ix(static_load) + device_name = _get_wrapper_name(static_load) + device_parameters = p[device_name] device!( + device_parameters, voltage_r[bus_ix], voltage_i[bus_ix], view(current_r, bus_ix), @@ -116,7 +128,10 @@ function system_residual!( for static_device in get_static_injectors(inputs) bus_ix = get_bus_ix(static_device) + device_name = _get_wrapper_name(static_device) + device_parameters = p[device_name] device!( + device_parameters, voltage_r[bus_ix], voltage_i[bus_ix], view(current_r, bus_ix), @@ -134,6 +149,8 @@ function system_residual!( dyn_branch = get_branch(dynamic_branch) # DynamicBranch branch = PSY.get_branch(dyn_branch) # Line or Transformer2W ix_range = get_ix_range(dynamic_branch) + branch_name = _get_wrapper_name(dynamic_branch) + branch_parameters = @view p[branch_name] branch_output_ode = @view branches_ode[get_ode_ouput_range(dynamic_branch)] branch_states = @view x[ix_range] bus_ix_from = get_bus_ix_from(dynamic_branch) @@ -141,6 +158,7 @@ function system_residual!( branch!( branch_states, branch_output_ode, + branch_parameters, #Get Voltage data voltage_r[bus_ix_from], voltage_i[bus_ix_from], @@ -168,7 +186,7 @@ end Instantiate a MassMatrixModel for ODE inputs. """ function MassMatrixModel(inputs, ::Vector{Float64}, ::Type{Ctype}) where {Ctype <: SimCache} - if isempty(inputs.delays) + if isempty(get_constant_lags(inputs)) return SystemModel{MassMatrixModel, NoDelays}( inputs, Ctype(system_mass_matrix!, inputs), @@ -194,7 +212,7 @@ function MassMatrixModel( T, ForwardDiff.pickchunksize(length(x0_init)), } - if isempty(inputs.delays) + if isempty(get_constant_lags(inputs)) return SystemModel{MassMatrixModel, NoDelays}( inputs, Ctype{U}(system_mass_matrix!, inputs), @@ -213,27 +231,33 @@ function (m::SystemModel{MassMatrixModel, NoDelays, C})( p, t, ) where {C <: Cache, U <: ACCEPTED_REAL_TYPES, T <: ACCEPTED_REAL_TYPES} - system_mass_matrix!(du, u, nothing, m.inputs, m.cache, t) + system_mass_matrix!(du, u, nothing, p, m.inputs, m.cache, t) end function (m::SystemModel{MassMatrixModel, HasDelays, C})( du::AbstractArray{T}, u::AbstractArray{U}, h, - p, + p::AbstractArray{V}, t, -) where {C <: Cache, U <: ACCEPTED_REAL_TYPES, T <: ACCEPTED_REAL_TYPES} - system_mass_matrix!(du, u, h, m.inputs, m.cache, t) +) where { + C <: Cache, + U <: ACCEPTED_REAL_TYPES, + T <: ACCEPTED_REAL_TYPES, + V <: ACCEPTED_REAL_TYPES, +} + system_mass_matrix!(du, u, h, p, m.inputs, m.cache, t) end function system_mass_matrix!( - dx::Vector{T}, - x::Vector{V}, + dx::AbstractArray{T}, + x::AbstractArray{V}, h, + p::AbstractArray{U}, inputs::SimulationInputs, cache::Cache, t, -) where {T <: ACCEPTED_REAL_TYPES, V <: ACCEPTED_REAL_TYPES} +) where {T <: ACCEPTED_REAL_TYPES, U <: ACCEPTED_REAL_TYPES, V <: ACCEPTED_REAL_TYPES} update_global_vars!(cache, inputs, x) # Global quantities @@ -255,10 +279,12 @@ function system_mass_matrix!( device_inner_vars = @view inner_vars[get_inner_vars_index(dynamic_device)] device_states = @view x[ix_range] bus_ix = get_bus_ix(dynamic_device) - + device_name = _get_wrapper_name(dynamic_device) + device_parameters = p[device_name] #TODO should be a view... device!( device_states, device_ode_output, + device_parameters, voltage_r[bus_ix], voltage_i[bus_ix], view(current_r, bus_ix), @@ -274,7 +300,10 @@ function system_mass_matrix!( for static_load in get_static_loads(inputs) bus_ix = get_bus_ix(static_load) + device_name = _get_wrapper_name(static_load) + device_parameters = p[device_name] device!( + device_parameters, voltage_r[bus_ix], voltage_i[bus_ix], view(current_r, bus_ix), @@ -288,7 +317,10 @@ function system_mass_matrix!( for static_device in get_static_injectors(inputs) bus_ix = get_bus_ix(static_device) + device_name = _get_wrapper_name(static_device) + device_parameters = p[device_name] device!( + device_parameters, voltage_r[bus_ix], voltage_i[bus_ix], view(current_r, bus_ix), @@ -306,6 +338,8 @@ function system_mass_matrix!( dyn_branch = get_branch(dynamic_branch) # DynamicBranch branch = PSY.get_branch(dyn_branch) # Line or Transformer2W ix_range = get_ix_range(dynamic_branch) + branch_name = _get_wrapper_name(dynamic_branch) + branch_parameters = @view p[branch_name] branch_output_ode = @view branches_ode[get_ode_ouput_range(dynamic_branch)] branch_states = @view x[ix_range] bus_ix_from = get_bus_ix_from(dynamic_branch) @@ -313,6 +347,7 @@ function system_mass_matrix!( branch!( branch_states, branch_output_ode, + branch_parameters, #Get Voltage data voltage_r[bus_ix_from], voltage_i[bus_ix_from], diff --git a/src/post_processing/post_proc_common.jl b/src/post_processing/post_proc_common.jl index 60aefaf49..04a3493b4 100644 --- a/src/post_processing/post_proc_common.jl +++ b/src/post_processing/post_proc_common.jl @@ -1,5 +1,5 @@ function make_global_state_map(inputs::SimulationInputs) - dic = inputs.global_state_map + dic = get_global_state_map(inputs) if !isempty(dic) return dic end diff --git a/src/post_processing/post_proc_generator.jl b/src/post_processing/post_proc_generator.jl index d3da8ca96..2038c39e4 100644 --- a/src/post_processing/post_proc_generator.jl +++ b/src/post_processing/post_proc_generator.jl @@ -27,7 +27,7 @@ function compute_output_current( dynamic_device::G, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) where {G <: PSY.DynamicGenerator} #Obtain Data @@ -46,6 +46,7 @@ function compute_output_current( base_power_ratio, res, dt, + unique_timestamps, ) end @@ -59,7 +60,7 @@ function compute_output_current( dynamic_device::PSY.AggregateDistributedGenerationA, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) #Obtain Data @@ -70,18 +71,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 @@ -152,7 +153,7 @@ function compute_field_current( dynamic_device::G, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) where {G <: PSY.DynamicGenerator} #Obtain Data @@ -160,7 +161,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 """ @@ -171,12 +180,12 @@ the dynamic device and bus voltage. It is dispatched for device type to compute function compute_field_voltage( res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 """ @@ -187,12 +196,12 @@ the dynamic device and bus voltage. It is dispatched for device type to compute function compute_pss_output( res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 """ @@ -203,12 +212,12 @@ the dynamic device and bus voltage. It is dispatched for device type to compute function compute_mechanical_torque( res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 """ @@ -218,10 +227,10 @@ Function to obtain the output frequency time series of a DynamicGenerator function compute_frequency( res::SimulationResults, dyn_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -236,9 +245,9 @@ function _machine_current( V_I::Vector{Float64}, base_power_ratio::Float64, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -270,11 +279,11 @@ function _machine_current( V_I::Vector{Float64}, base_power_ratio::Float64, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -308,11 +317,11 @@ function _machine_current( V_I::Vector{Float64}, base_power_ratio::Float64, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -349,13 +358,13 @@ function _machine_current( ::Vector{Float64}, base_power_ratio::Float64, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -387,15 +396,15 @@ function _machine_current( ::Vector{Float64}, base_power_ratio::Float64, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -429,13 +438,13 @@ function _machine_current( V_I::Vector{Float64}, base_power_ratio::Float64, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -481,12 +490,12 @@ function _machine_current( V_I::Vector{Float64}, base_power_ratio::Float64, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -526,10 +535,12 @@ function _field_current( V_R::Vector{Float64}, V_I::Vector{Float64}, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) + @warn( + "Field current is not supported in the machine type $(M). Returning zeros." + ) + ts, _ = post_proc_state_series(res, (name, :δ), dt, unique_timestamps) I_fd = zeros(length(ts)) return ts, I_fd end @@ -544,13 +555,13 @@ function _field_current( V_R::Vector{Float64}, V_I::Vector{Float64}, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -598,12 +609,12 @@ function _field_current( V_R::Vector{Float64}, V_I::Vector{Float64}, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -645,12 +656,12 @@ function _field_current( V_R::Vector{Float64}, V_I::Vector{Float64}, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -694,9 +705,9 @@ function _field_voltage( ::A, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 """ @@ -707,10 +718,10 @@ function _field_voltage( avr::PSY.AVRFixed, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -723,10 +734,10 @@ function _field_voltage( avr::Union{PSY.ESAC1A, PSY.EXAC1}, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -741,10 +752,10 @@ function _field_voltage( avr::PSY.SCRX, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -777,16 +788,16 @@ function _field_voltage( avr::PSY.ESST1A, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -902,9 +913,9 @@ function _pss_output( pss::PSY.PSSFixed, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -916,15 +927,15 @@ function _pss_output( pss::PSY.IEEEST, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -983,10 +994,10 @@ function _mechanical_torque( tg::PSY.TGFixed, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) @@ -1002,7 +1013,7 @@ function _mechanical_torque( tg::PSY.TGTypeI, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) # Get params Tc = PSY.get_Tc(tg) @@ -1010,9 +1021,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) @@ -1030,7 +1041,7 @@ function _mechanical_torque( tg::PSY.TGTypeII, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 +1052,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) @@ -1059,7 +1070,7 @@ function _mechanical_torque( tg::PSY.SteamTurbineGov1, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 +1083,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) @@ -1094,13 +1105,13 @@ function _mechanical_torque( tg::PSY.GasTG, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 +1127,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 """ @@ -1127,7 +1138,7 @@ function _mechanical_torque( tg::PSY.HydroTurbineGov, name::String, res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) # Get params q_nl = PSY.get_q_nl(tg) @@ -1136,9 +1147,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..3897e4138 100644 --- a/src/post_processing/post_proc_inverter.jl +++ b/src/post_processing/post_proc_inverter.jl @@ -8,7 +8,7 @@ function compute_output_current( dynamic_device::G, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} #Obtain Data @@ -30,6 +30,7 @@ function compute_output_current( res, dynamic_device, dt, + unique_timestamps, ) end @@ -43,9 +44,11 @@ function compute_field_current( dynamic_device::G, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, 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.") + @warn( + "Field current does not exist in inverters. Returning zeros." + ) return (nothing, zeros(length(V_R))) end @@ -57,10 +60,12 @@ the dynamic device and bus voltage. It must return nothing since field voltage d function compute_field_voltage( res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) + @warn( + "Field voltage does not exist in inverters. Returning zeros." + ) + _, state = _post_proc_state_series(res.solution, 1, dt, unique_timestamps) return (nothing, zeros(length(state))) end @@ -72,17 +77,19 @@ the dynamic device and bus voltage. It must return nothing since mechanical torq function compute_mechanical_torque( res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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) + @warn( + "Mechanical torque is not used in inverters. Returning zeros." + ) + _, state = _post_proc_state_series(res.solution, 1, dt, unique_timestamps) return (nothing, zeros(length(state))) end function compute_frequency( res::SimulationResults, dyn_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 +100,7 @@ function compute_frequency( res, dyn_device, dt, + unique_timestamps, ) end @@ -106,9 +114,9 @@ function _frequency( name::String, res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -122,11 +130,11 @@ function _frequency( name::String, res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -145,7 +153,7 @@ function _frequency( name::String, res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 +161,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) .* @@ -174,12 +182,12 @@ function _frequency( name::String, res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -195,13 +203,13 @@ function _frequency( name::String, res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -210,7 +218,7 @@ end function _system_frequency_series( res::SimulationResults, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 +227,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 @@ -237,10 +245,10 @@ function _output_current( base_power_ratio::Float64, res::SimulationResults, dynamic_device::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + 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 @@ -258,7 +266,7 @@ function _output_current( base_power_ratio::Float64, res::SimulationResults, ::G, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) where {G <: PSY.DynamicInverter} #Get Converter parameters @@ -281,9 +289,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..d3b22eda7 100644 --- a/src/post_processing/post_proc_loads.jl +++ b/src/post_processing/post_proc_loads.jl @@ -8,7 +8,7 @@ function compute_output_current( dynamic_device::PSY.SingleCageInductionMachine, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) #Obtain Data sys = get_system(res) @@ -29,10 +29,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 @@ -58,7 +58,7 @@ function compute_output_current( dynamic_device::PSY.SimplifiedSingleCageInductionMachine, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, Vector{Float64}}, + dt::Union{Nothing, Float64, Vector{Float64}}, unique_timestamps::Bool, ) #Obtain Data sys = get_system(res) @@ -75,8 +75,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 @@ -112,7 +112,7 @@ function compute_output_current( device::PSY.PowerLoad, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, 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. @@ -152,7 +152,7 @@ function compute_output_current( device::PSY.ExponentialLoad, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, 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. @@ -193,7 +193,7 @@ function compute_output_current( device::PSY.StandardLoad, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, 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_results.jl b/src/post_processing/post_proc_results.jl index 534acdf42..bfcf41c94 100644 --- a/src/post_processing/post_proc_results.jl +++ b/src/post_processing/post_proc_results.jl @@ -22,8 +22,8 @@ function show_states_initial_value(sim::Simulation) println("====================") bus_n = PSY.get_number(bus) bus_ix = PSID.get_lookup(sim.inputs)[bus_n] - V_R = sim.x0_init[bus_ix] - V_I = sim.x0_init[bus_ix + bus_size] + V_R = sim.x0[bus_ix] + V_I = sim.x0[bus_ix + bus_size] Vm = sqrt(V_R^2 + V_I^2) θ = angle(V_R + V_I * 1im) print("Vm ", round(Vm; digits = 4), "\n") @@ -39,7 +39,7 @@ function show_states_initial_value(sim::Simulation) println("====================") global_index = global_state_map[name] for s in states - print(s, " ", round(sim.x0_init[global_index[s]]; digits = 4), "\n") + print(s, " ", round(sim.x0[global_index[s]]; digits = 4), "\n") end println("====================") end @@ -56,7 +56,7 @@ function show_states_initial_value(sim::Simulation) global_index = global_state_map[name] x0_br = Dict{Symbol, Float64}() for (i, s) in enumerate(states) - print(s, " ", round(sim.x0_init[global_index[s]]; digits = 5), "\n") + print(s, " ", round(sim.x0[global_index[s]]; digits = 5), "\n") end println("====================") end diff --git a/src/post_processing/post_proc_source.jl b/src/post_processing/post_proc_source.jl index 7ee34d2ce..d242b8e0e 100644 --- a/src/post_processing/post_proc_source.jl +++ b/src/post_processing/post_proc_source.jl @@ -5,7 +5,7 @@ Function to obtain voltage and output currents for a source. It receives the sim function post_proc_source_voltage_current_series( res::SimulationResults, name::String, - dt = nothing, + dt = nothing, unique_timestamps = true, ) system = get_system(res) bus_lookup = get_bus_lookup(res) @@ -31,7 +31,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 +45,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 @@ -57,9 +64,10 @@ the Source name and an optional argument of the time step of the results. function get_source_imaginary_current_series( res::SimulationResults, name::String, - dt = nothing, + 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 @@ -73,11 +81,11 @@ function compute_output_current( dynamic_device::PSY.PeriodicVariableSource, V_R::Vector{Float64}, V_I::Vector{Float64}, - dt::Union{Nothing, Float64, 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/src/utils/parameters.jl b/src/utils/parameters.jl new file mode 100644 index 000000000..e9f492156 --- /dev/null +++ b/src/utils/parameters.jl @@ -0,0 +1,1328 @@ + +@enum PARAM_TYPES begin + DEVICE_PARAM = 0 + DEVICE_SETPOINT = 1 + NETWORK_PARAM = 2 + NETWORK_SETPOINT = 3 +end + +struct ParamsMetadata + type::PARAM_TYPES + in_mass_matrix::Bool + impacts_ic::Bool +end +Base.length(::ParamsMetadata) = 1 + +function get_params(x) + @error "Parameters not yet defined for component: $(typeof(x))" + (;) +end +function get_params_metadata(x) + @error "Parameters metadata not yet defined for component: $(typeof(x))" + (;) +end +#Overloads for refs +PSY.get_V_ref(x) = 0.0 +PSY.get_ω_ref(x) = 0.0 +PSY.get_P_ref(x) = 0.0 + +get_params_metadata(::T) where {T <: PSY.DynamicComponent} = (;) + +function get_params(x::PSY.ActivePowerControl) + @error "Parameters not yet defined for component: $(typeof(x))" + (;) +end +function get_params_metadata(x::PSY.ActivePowerControl) + @error "Parameters metadata not yet defined for component: $(typeof(x))" + (;) +end +function get_params(x::PSY.ReactivePowerControl) + @error "Parameters not yet defined for component: $(typeof(x))" + (;) +end +function get_params_metadata(x::PSY.ReactivePowerControl) + @error "Parameters metadata not yet defined for component: $(typeof(x))" + (;) +end + +get_params( + d::DynamicWrapper{T}, +) where {T <: Union{PSY.DynamicGenerator, PSY.DynamicInverter}} = get_params(get_device(d)) +get_params_metadata( + d::DynamicWrapper{T}, +) where {T <: Union{PSY.DynamicGenerator, PSY.DynamicInverter}} = + get_params_metadata(get_device(d)) + +get_params(d::DynamicWrapper) = get_params(get_device(d)) +get_params_metadata(d::DynamicWrapper) = get_params_metadata(get_device(d)) + +get_params(d::StaticWrapper) = get_params(get_device(d)) +get_params_metadata(d::StaticWrapper) = get_params_metadata(get_device(d)) + +get_params(x::BranchWrapper) = get_params(get_branch(x)) +get_params_metadata(x::BranchWrapper) = get_params_metadata(get_branch(x)) + +get_params(x::PSY.DynamicBranch) = get_params(PSY.get_branch(x)) +get_params_metadata(x::PSY.DynamicBranch) = get_params_metadata(PSY.get_branch(x)) + +get_params(x::PSY.Line) = (r = PSY.get_r(x), x = PSY.get_x(x)) +get_params_metadata(::PSY.Line) = ( + r = ParamsMetadata(NETWORK_PARAM, false, true), + x = ParamsMetadata(NETWORK_PARAM, false, true), +) +get_params(::StaticLoadWrapper) = (;) +get_params_metadata(::StaticLoadWrapper) = (;) +########### INVERTERS ############# +function get_params(g::PSY.DynamicInverter) + ( + Converter = get_params(PSY.get_converter(g)), + DCSource = get_params(PSY.get_dc_source(g)), + Filter = get_params(PSY.get_filter(g)), + FrequencyEstimator = get_params(PSY.get_freq_estimator(g)), + InnerControl = get_params(PSY.get_inner_control(g)), + OuterControl = get_params(PSY.get_outer_control(g)), + ) +end +function get_params_metadata(g::PSY.DynamicInverter) + ( + Converter = get_params_metadata(PSY.get_converter(g)), + DCSource = get_params_metadata(PSY.get_dc_source(g)), + Filter = get_params_metadata(PSY.get_filter(g)), + FrequencyEstimator = get_params_metadata(PSY.get_freq_estimator(g)), + InnerControl = get_params_metadata(PSY.get_inner_control(g)), + OuterControl = get_params_metadata(PSY.get_outer_control(g)), + ) +end + +#FILTERS +get_params(x::PSY.LCLFilter) = + ( + lf = PSY.get_lf(x), + rf = PSY.get_rf(x), + cf = PSY.get_cf(x), + lg = PSY.get_lg(x), + rg = PSY.get_rg(x), + ) +get_params_metadata(::PSY.LCLFilter) = ( + lf = ParamsMetadata(DEVICE_PARAM, true, true), + rf = ParamsMetadata(DEVICE_PARAM, false, true), + cf = ParamsMetadata(DEVICE_PARAM, true, true), + lg = ParamsMetadata(DEVICE_PARAM, true, true), + rg = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.RLFilter) = (rf = PSY.get_rf(x), lf = PSY.get_lf(x)) +get_params_metadata(::PSY.RLFilter) = ( + rf = ParamsMetadata(DEVICE_PARAM, false, true), + lf = ParamsMetadata(DEVICE_PARAM, false, true), +) + +#OUTER CONTROL, +get_params(x::PSY.OuterControl) = ( + ActivePowerControl = get_params(PSY.get_active_power_control(x)), + ReactivePowerControl = get_params(PSY.get_reactive_power_control(x)), +) +get_params_metadata(x::PSY.OuterControl) = ( + ActivePowerControl = get_params_metadata(PSY.get_active_power_control(x)), + ReactivePowerControl = get_params_metadata(PSY.get_reactive_power_control(x)), +) +#ACTIVE POWER CONTROL +get_params(x::PSY.ActivePowerPI) = ( + Kp_p = PSY.get_Kp_p(x), + Ki_p = PSY.get_Ki_p(x), + ωz = PSY.get_ωz(x), +) +get_params_metadata(::PSY.ActivePowerPI) = ( + Kp_p = ParamsMetadata(DEVICE_PARAM, false, false), + Ki_p = ParamsMetadata(DEVICE_PARAM, false, true), + ωz = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.ActivePowerDroop) = (Rp = PSY.get_Rp(x), ωz = PSY.get_ωz(x)) +get_params_metadata(::PSY.ActivePowerDroop) = ( + Rp = ParamsMetadata(DEVICE_PARAM, false, false), + ωz = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.VirtualInertia) = + (Ta = PSY.get_Ta(x), kd = PSY.get_kd(x), kω = PSY.get_kω(x)) +get_params_metadata(::PSY.VirtualInertia) = ( + Ta = ParamsMetadata(DEVICE_PARAM, false, false), + kd = ParamsMetadata(DEVICE_PARAM, false, false), + kω = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.ActiveVirtualOscillator) = (k1 = PSY.get_k1(x), ψ = PSY.get_ψ(x)) +get_params_metadata(::PSY.ActiveVirtualOscillator) = ( + k1 = ParamsMetadata(DEVICE_PARAM, false, false), + ψ = ParamsMetadata(DEVICE_PARAM, false, false), +) +#Note: Removed fbdd_pnts from parameters because it is not a NamedTuple +get_params(x::PSY.ActiveRenewableControllerAB) = ( + K_pg = PSY.get_K_pg(x), + K_ig = PSY.get_K_ig(x), + T_p = PSY.get_T_p(x), + fe_lim = PSY.get_fe_lim(x), + P_lim = PSY.get_P_lim(x), + T_g = PSY.get_T_g(x), + D_dn = PSY.get_D_dn(x), + D_up = PSY.get_D_up(x), + dP_lim = PSY.get_dP_lim(x), + P_lim_inner = PSY.get_P_lim_inner(x), + T_pord = PSY.get_T_pord(x), +) +get_params_metadata(::PSY.ActiveRenewableControllerAB) = ( + K_pg = ParamsMetadata(DEVICE_PARAM, false, false), + K_ig = ParamsMetadata(DEVICE_PARAM, false, true), + T_p = ParamsMetadata(DEVICE_PARAM, false, true), + fe_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + P_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + T_g = ParamsMetadata(DEVICE_PARAM, false, false), + D_dn = ParamsMetadata(DEVICE_PARAM, false, false), + D_up = ParamsMetadata(DEVICE_PARAM, false, false), + dP_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + P_lim_innem_inner = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + T_pord = ParamsMetadata(DEVICE_PARAM, false, false), +) +#REACTIVE POWER CONTROL +get_params(x::PSY.ReactivePowerPI) = ( + Kp_q = PSY.get_Kp_q(x), + Ki_q = PSY.get_Ki_q(x), + ωf = PSY.get_ωf(x), +) +get_params_metadata(::PSY.ReactivePowerPI) = ( + Kp_q = ParamsMetadata(DEVICE_PARAM, false, false), + Ki_q = ParamsMetadata(DEVICE_PARAM, false, true), + ωf = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.ReactivePowerDroop) = (kq = PSY.get_kq(x), ωf = PSY.get_ωf(x)) +get_params_metadata(::PSY.ReactivePowerDroop) = ( + kq = ParamsMetadata(DEVICE_PARAM, false, false), + ωf = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.ReactiveVirtualOscillator) = (k2 = PSY.get_k2(x),) +get_params_metadata(::PSY.ReactiveVirtualOscillator) = + (k2 = ParamsMetadata(DEVICE_PARAM, false, false),) +get_params(x::PSY.ReactiveRenewableControllerAB) = ( + T_fltr = PSY.get_T_fltr(x), + K_p = PSY.get_K_p(x), + K_i = PSY.get_K_i(x), + T_ft = PSY.get_T_ft(x), + T_fv = PSY.get_T_fv(x), + V_frz = PSY.get_V_frz(x), + R_c = PSY.get_R_c(x), + X_c = PSY.get_X_c(x), + K_c = PSY.get_K_c(x), + e_lim = (min = PSY.get_e_lim(x)[:min], max = PSY.get_e_lim(x)[:max]), + dbd_pnts1 = PSY.get_dbd_pnts(x)[1], + dbd_pnts2 = PSY.get_dbd_pnts(x)[2], + Q_lim = (min = PSY.get_Q_lim(x)[:min], max = PSY.get_Q_lim(x)[:max]), + T_p = PSY.get_T_p(x), + Q_lim_inner = (min = PSY.get_Q_lim_inner(x)[:min], max = PSY.get_Q_lim_inner(x)[:max]), + V_lim = (min = PSY.get_V_lim(x)[:min], max = PSY.get_V_lim(x)[:max]), + K_qp = PSY.get_K_qp(x), + K_qi = PSY.get_K_qi(x), +) +get_params_metadata(::PSY.ReactiveRenewableControllerAB) = + (T_fltr = ParamsMetadata(DEVICE_PARAM, false, false), + K_p = ParamsMetadata(DEVICE_PARAM, false, false), + K_i = ParamsMetadata(DEVICE_PARAM, false, true), + T_ft = ParamsMetadata(DEVICE_PARAM, false, false), + T_fv = ParamsMetadata(DEVICE_PARAM, false, false), + V_frz = ParamsMetadata(DEVICE_PARAM, false, false), + R_c = ParamsMetadata(DEVICE_PARAM, false, true), + X_c = ParamsMetadata(DEVICE_PARAM, false, true), + K_c = ParamsMetadata(DEVICE_PARAM, false, true), + e_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + dbd_pnts1 = ParamsMetadata(DEVICE_PARAM, false, false), + dbd_pnts2 = ParamsMetadata(DEVICE_PARAM, false, false), + Q_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + T_p = ParamsMetadata(DEVICE_PARAM, false, true), + Q_lim_inner = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + V_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + K_qp = ParamsMetadata(DEVICE_PARAM, false, false), + K_qi = ParamsMetadata(DEVICE_PARAM, false, true), + ) +#INNER CONTROL +get_params(x::PSY.VoltageModeControl) = ( + kpv = PSY.get_kpv(x), + kiv = PSY.get_kiv(x), + kffv = PSY.get_kffv(x), + rv = PSY.get_rv(x), + lv = PSY.get_lv(x), + kpc = PSY.get_kpc(x), + kic = PSY.get_kic(x), + kffi = PSY.get_kffi(x), + ωad = PSY.get_ωad(x), + kad = PSY.get_kad(x), +) +get_params_metadata(::PSY.VoltageModeControl) = ( + kpv = ParamsMetadata(DEVICE_PARAM, false, true), + kiv = ParamsMetadata(DEVICE_PARAM, false, true), + kffv = ParamsMetadata(DEVICE_PARAM, false, true), + rv = ParamsMetadata(DEVICE_PARAM, false, true), + lv = ParamsMetadata(DEVICE_PARAM, false, true), + kpc = ParamsMetadata(DEVICE_PARAM, false, true), + kic = ParamsMetadata(DEVICE_PARAM, false, true), + kffi = ParamsMetadata(DEVICE_PARAM, false, true), + ωad = ParamsMetadata(DEVICE_PARAM, false, false), + kad = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.CurrentModeControl) = ( + kpc = PSY.get_kpc(x), + kic = PSY.get_kic(x), + kffv = PSY.get_kffv(x), +) +get_params_metadata(::PSY.CurrentModeControl) = ( + kpc = ParamsMetadata(DEVICE_PARAM, false, true), + kic = ParamsMetadata(DEVICE_PARAM, false, true), + kffv = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.RECurrentControlB) = ( + Vdip_lim = PSY.get_Vdip_lim(x), + T_rv = PSY.get_T_rv(x), + dbd_pnts1 = PSY.get_dbd_pnts(x)[1], + dbd_pnts2 = PSY.get_dbd_pnts(x)[2], + K_qv = PSY.get_K_qv(x), + Iqinj_lim = PSY.get_Iqinj_lim(x), + V_ref0 = PSY.get_V_ref0(x), + K_vp = PSY.get_K_vp(x), + K_vi = PSY.get_K_vi(x), + T_iq = PSY.get_T_iq(x), + I_max = PSY.get_I_max(x), +) +get_params_metadata(::PSY.RECurrentControlB) = ( + Vdip_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + T_rv = ParamsMetadata(DEVICE_PARAM, true, false), + dbd_pnts1 = ParamsMetadata(DEVICE_PARAM, false, false), + dbd_pnts2 = ParamsMetadata(DEVICE_PARAM, false, false), + K_qv = ParamsMetadata(DEVICE_PARAM, false, false), + Iqinj_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + V_ref0 = ParamsMetadata(DEVICE_PARAM, false, true), + K_vp = ParamsMetadata(DEVICE_PARAM, false, false), + K_vi = ParamsMetadata(DEVICE_PARAM, false, true), + T_iq = ParamsMetadata(DEVICE_PARAM, true, false), + I_max = ParamsMetadata(DEVICE_PARAM, false, false), +) + +#DC SOURCE +get_params(x::PSY.FixedDCSource) = (voltage = PSY.get_voltage(x),) +get_params_metadata(::PSY.FixedDCSource) = + (voltage = ParamsMetadata(DEVICE_PARAM, false, false),) + +#FREQ ESTIMATOR +get_params(x::PSY.KauraPLL) = ( + ω_lp = PSY.get_ω_lp(x), + kp_pll = PSY.get_kp_pll(x), + ki_pll = PSY.get_ki_pll(x), +) +get_params_metadata(::PSY.KauraPLL) = ( + ω_lp = ParamsMetadata(DEVICE_PARAM, false, false), + kp_pll = ParamsMetadata(DEVICE_PARAM, false, true), + ki_pll = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.ReducedOrderPLL) = ( + ω_lp = PSY.get_ω_lp(x), + kp_pll = PSY.get_kp_pll(x), + ki_pll = PSY.get_ki_pll(x), +) +get_params_metadata(::PSY.ReducedOrderPLL) = ( + ω_lp = ParamsMetadata(DEVICE_PARAM, false, false), + kp_pll = ParamsMetadata(DEVICE_PARAM, false, true), + ki_pll = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.FixedFrequency) = (; frequency = PSY.get_frequency(x)) +get_params_metadata(::PSY.FixedFrequency) = + (; frequency = ParamsMetadata(DEVICE_PARAM, false, true)) + +#CONVERTER +get_params(::PSY.AverageConverter) = (;) +get_params_metadata(::PSY.AverageConverter) = (;) +get_params(x::PSY.RenewableEnergyConverterTypeA) = ( + T_g = PSY.get_T_g(x), + Rrpwr = PSY.get_Rrpwr(x), + Brkpt = PSY.get_Brkpt(x), + Zerox = PSY.get_Zerox(x), + Lvpl1 = PSY.get_Lvpl1(x), + Vo_lim = PSY.get_Vo_lim(x), + Lv_pnts = PSY.get_Lv_pnts(x), + Io_lim = PSY.get_Io_lim(x), + T_fltr = PSY.get_T_fltr(x), + K_hv = PSY.get_K_hv(x), + Iqr_lims = PSY.get_Iqr_lims(x), + Accel = PSY.get_Accel(x), + Q_ref = PSY.get_Q_ref(x), + R_source = PSY.get_R_source(x), + X_source = PSY.get_X_source(x), +) +get_params_metadata(::PSY.RenewableEnergyConverterTypeA) = ( + T_g = ParamsMetadata(DEVICE_PARAM, false, false), + Rrpwr = ParamsMetadata(DEVICE_PARAM, false, false), + Brkpt = ParamsMetadata(DEVICE_PARAM, false, false), + Zerox = ParamsMetadata(DEVICE_PARAM, false, false), + Lvpl1 = ParamsMetadata(DEVICE_PARAM, false, false), + Vo_lim = ParamsMetadata(DEVICE_PARAM, false, true), + Lv_pnts = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), #confirm + max = ParamsMetadata(DEVICE_PARAM, false, true), #confirm + ), + Io_lim = ParamsMetadata(DEVICE_PARAM, false, true), + T_fltr = ParamsMetadata(DEVICE_PARAM, false, false), + K_hv = ParamsMetadata(DEVICE_PARAM, false, false), + Iqr_lims = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + Accel = ParamsMetadata(DEVICE_PARAM, false, false), + Q_ref = ParamsMetadata(DEVICE_PARAM, false, false), + R_source = ParamsMetadata(DEVICE_PARAM, false, false), + X_source = ParamsMetadata(DEVICE_PARAM, false, false), +) +########### GENERATORS ############# +function get_params(g::PSY.DynamicGenerator) + return ( + Machine = get_params(PSY.get_machine(g)), + Shaft = get_params(PSY.get_shaft(g)), + AVR = get_params(PSY.get_avr(g)), + TurbineGov = get_params(PSY.get_prime_mover(g)), + PSS = get_params(PSY.get_pss(g)), + ) +end +function get_params_metadata(g::PSY.DynamicGenerator) + return ( + Machine = get_params_metadata(PSY.get_machine(g)), + Shaft = get_params_metadata(PSY.get_shaft(g)), + AVR = get_params_metadata(PSY.get_avr(g)), + TurbineGov = get_params_metadata(PSY.get_prime_mover(g)), + PSS = get_params_metadata(PSY.get_pss(g)), + ) +end + +#MACHINES +get_params(x::PSY.BaseMachine) = + (R = PSY.get_R(x), Xd_p = PSY.get_Xd_p(x), eq_p = PSY.get_eq_p(x)) +get_params_metadata(::PSY.BaseMachine) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_p = ParamsMetadata(DEVICE_PARAM, false, true), + eq_p = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.OneDOneQMachine) = ( + R = PSY.get_R(x), + Xd = PSY.get_Xd(x), + Xq = PSY.get_Xq(x), + Xd_p = PSY.get_Xd_p(x), + Xq_p = PSY.get_Xq_p(x), + Td0_p = PSY.get_Td0_p(x), + Tq0_p = PSY.get_Tq0_p(x), +) +get_params_metadata(::PSY.OneDOneQMachine) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + Xd = ParamsMetadata(DEVICE_PARAM, false, true), + Xq = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xq_p = ParamsMetadata(DEVICE_PARAM, false, true), + Td0_p = ParamsMetadata(DEVICE_PARAM, false, false), + Tq0_p = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.SauerPaiMachine) = ( + R = PSY.get_R(x), + Xd = PSY.get_Xd(x), + Xq = PSY.get_Xq(x), + Xd_p = PSY.get_Xd_p(x), + Xq_p = PSY.get_Xq_p(x), + Xd_pp = PSY.get_Xd_pp(x), + Xq_pp = PSY.get_Xq_pp(x), + Xl = PSY.get_Xl(x), + Td0_p = PSY.get_Td0_p(x), + Tq0_p = PSY.get_Tq0_p(x), + Td0_pp = PSY.get_Td0_pp(x), + Tq0_pp = PSY.get_Tq0_pp(x), + γ_d1 = PSY.get_γ_d1(x), + γ_q1 = PSY.get_γ_q1(x), + γ_d2 = PSY.get_γ_d2(x), + γ_q2 = PSY.get_γ_q2(x), +) +get_params_metadata(::PSY.SauerPaiMachine) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + Xd = ParamsMetadata(DEVICE_PARAM, false, true), + Xq = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xq_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Xq_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Xl = ParamsMetadata(DEVICE_PARAM, false, true), + Td0_p = ParamsMetadata(DEVICE_PARAM, false, false), + Tq0_p = ParamsMetadata(DEVICE_PARAM, false, false), + Td0_pp = ParamsMetadata(DEVICE_PARAM, false, false), + Tq0_pp = ParamsMetadata(DEVICE_PARAM, false, false), + γ_d1 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_q1 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_d2 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_q2 = ParamsMetadata(DEVICE_PARAM, false, true), +) +#TODO - SimpleMarconatoMachine +get_params(x::PSY.MarconatoMachine) = ( + R = PSY.get_R(x), + Xd = PSY.get_Xd(x), + Xq = PSY.get_Xq(x), + Xd_p = PSY.get_Xd_p(x), + Xq_p = PSY.get_Xq_p(x), + Xd_pp = PSY.get_Xd_pp(x), + Xq_pp = PSY.get_Xq_pp(x), + Td0_p = PSY.get_Td0_p(x), + Tq0_p = PSY.get_Tq0_p(x), + Td0_pp = PSY.get_Td0_pp(x), + Tq0_pp = PSY.get_Tq0_pp(x), + T_AA = PSY.get_T_AA(x), + γd = PSY.get_γd(x), + γq = PSY.get_γq(x), +) +get_params_metadata(::PSY.MarconatoMachine) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + Xd = ParamsMetadata(DEVICE_PARAM, false, true), + Xq = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xq_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Xq_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Td0_p = ParamsMetadata(DEVICE_PARAM, false, true), + Tq0_p = ParamsMetadata(DEVICE_PARAM, false, false), + Td0_pp = ParamsMetadata(DEVICE_PARAM, false, false), + Tq0_pp = ParamsMetadata(DEVICE_PARAM, false, false), + γ_d = ParamsMetadata(DEVICE_PARAM, false, true), + γ_q = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::Union{PSY.AndersonFouadMachine, PSY.SimpleAFMachine}) = ( + R = PSY.get_R(x), + Xd = PSY.get_Xd(x), + Xq = PSY.get_Xq(x), + Xd_p = PSY.get_Xd_p(x), + Xq_p = PSY.get_Xq_p(x), + Xd_pp = PSY.get_Xd_pp(x), + Xq_pp = PSY.get_Xq_pp(x), + Td0_p = PSY.get_Td0_p(x), + Tq0_p = PSY.get_Tq0_p(x), + Td0_pp = PSY.get_Td0_pp(x), + Tq0_pp = PSY.get_Tq0_pp(x), +) +get_params_metadata(::Union{PSY.AndersonFouadMachine, PSY.SimpleAFMachine}) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + Xd = ParamsMetadata(DEVICE_PARAM, false, true), + Xq = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xq_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Xq_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Td0_p = ParamsMetadata(DEVICE_PARAM, false, false), + Tq0_p = ParamsMetadata(DEVICE_PARAM, false, false), + Td0_pp = ParamsMetadata(DEVICE_PARAM, false, false), + Tq0_pp = ParamsMetadata(DEVICE_PARAM, false, false), +) +#NOTE: Saturation not considered as paramters +get_params( + x::Union{PSY.RoundRotorMachine, PSY.RoundRotorExponential, PSY.RoundRotorQuadratic}, +) = ( + R = PSY.get_R(x), + Td0_p = PSY.get_Td0_p(x), + Td0_pp = PSY.get_Td0_pp(x), + Tq0_p = PSY.get_Tq0_p(x), + Tq0_pp = PSY.get_Tq0_pp(x), + Xd = PSY.get_Xd(x), + Xq = PSY.get_Xq(x), + Xd_p = PSY.get_Xd_p(x), + Xq_p = PSY.get_Xq_p(x), + Xd_pp = PSY.get_Xd_pp(x), + Xl = PSY.get_Xl(x), + γ_d1 = PSY.get_γ_d1(x), + γ_q1 = PSY.get_γ_q1(x), + γ_d2 = PSY.get_γ_d2(x), + γ_q2 = PSY.get_γ_q2(x), + γ_qd = PSY.get_γ_qd(x), +) +get_params_metadata( + ::Union{PSY.RoundRotorMachine, PSY.RoundRotorExponential, PSY.RoundRotorQuadratic}, +) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + Td0_p = ParamsMetadata(DEVICE_PARAM, false, true), + Td0_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Tq0_p = ParamsMetadata(DEVICE_PARAM, false, true), + Tq0_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Xd = ParamsMetadata(DEVICE_PARAM, false, true), + Xq = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xq_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Xl = ParamsMetadata(DEVICE_PARAM, false, true), + γ_d1 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_q1 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_d2 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_q2 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_qd = ParamsMetadata(DEVICE_PARAM, false, true), +) + +get_params( + x::Union{PSY.SalientPoleMachine, PSY.SalientPoleExponential, PSY.SalientPoleQuadratic}, +) = ( + R = PSY.get_R(x), + Td0_p = PSY.get_Td0_p(x), + Td0_pp = PSY.get_Td0_pp(x), + Tq0_pp = PSY.get_Tq0_pp(x), + Xd = PSY.get_Xd(x), + Xq = PSY.get_Xq(x), + Xd_p = PSY.get_Xd_p(x), + Xd_pp = PSY.get_Xd_pp(x), + Xl = PSY.get_Xl(x), + γ_d1 = PSY.get_γ_d1(x), + γ_q1 = PSY.get_γ_q1(x), + γ_d2 = PSY.get_γ_d2(x), +) +get_params_metadata( + ::Union{PSY.SalientPoleMachine, PSY.SalientPoleExponential, PSY.SalientPoleQuadratic}, +) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + Td0_p = ParamsMetadata(DEVICE_PARAM, false, true), + Td0_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Tq0_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Xd = ParamsMetadata(DEVICE_PARAM, false, true), + Xq = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_p = ParamsMetadata(DEVICE_PARAM, false, true), + Xd_pp = ParamsMetadata(DEVICE_PARAM, false, true), + Xl = ParamsMetadata(DEVICE_PARAM, false, true), + γ_d1 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_q1 = ParamsMetadata(DEVICE_PARAM, false, true), + γ_d2 = ParamsMetadata(DEVICE_PARAM, false, true), +) + +#SHAFTS +get_params(x::PSY.SingleMass) = (H = PSY.get_H(x), D = PSY.get_D(x)) +get_params_metadata(::PSY.SingleMass) = ( + H = ParamsMetadata(DEVICE_PARAM, false, false), + D = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.FiveMassShaft) = ( + H = PSY.get_H(x), + H_hp = PSY.get_H_hp(x), + H_ip = PSY.get_H_ip(x), + H_lp = PSY.get_H_lp(x), + H_ex = PSY.get_H_ex(x), + D = PSY.get_D(x), + D_hp = PSY.get_D_hp(x), + D_ip = PSY.get_D_ip(x), + D_lp = PSY.get_D_lp(x), + D_ex = PSY.get_D_ex(x), + D_12 = PSY.get_D_12(x), + D_23 = PSY.get_D_23(x), + D_34 = PSY.get_D_34(x), + D_45 = PSY.get_D_45(x), + K_hp = PSY.get_K_hp(x), + K_ip = PSY.get_K_ip(x), + K_lp = PSY.get_K_lp(x), + K_ex = PSY.get_K_ex(x), +) +get_params_metadata(::PSY.FiveMassShaft) = ( + H = ParamsMetadata(DEVICE_PARAM, false, false), + H_hp = ParamsMetadata(DEVICE_PARAM, false, false), + H_ip = ParamsMetadata(DEVICE_PARAM, false, false), + H_lp = ParamsMetadata(DEVICE_PARAM, false, false), + H_ex = ParamsMetadata(DEVICE_PARAM, false, false), + D = ParamsMetadata(DEVICE_PARAM, false, true), + D_hp = ParamsMetadata(DEVICE_PARAM, false, true), + D_ip = ParamsMetadata(DEVICE_PARAM, false, true), + D_lp = ParamsMetadata(DEVICE_PARAM, false, true), + D_ex = ParamsMetadata(DEVICE_PARAM, false, true), + D_12 = ParamsMetadata(DEVICE_PARAM, false, true), + D_23 = ParamsMetadata(DEVICE_PARAM, false, true), + D_34 = ParamsMetadata(DEVICE_PARAM, false, true), + D_45 = ParamsMetadata(DEVICE_PARAM, false, true), + K_hp = ParamsMetadata(DEVICE_PARAM, false, true), + K_ip = ParamsMetadata(DEVICE_PARAM, false, true), + K_lp = ParamsMetadata(DEVICE_PARAM, false, true), + K_ex = ParamsMetadata(DEVICE_PARAM, false, true), +) + +#AVRS +get_params(::PSY.AVRFixed) = (;) +get_params_metadata(::PSY.AVRFixed) = (;) +get_params(x::PSY.AVRSimple) = (Kv = PSY.get_Kv(x),) +get_params_metadata(::PSY.AVRSimple) = (Kv = ParamsMetadata(DEVICE_PARAM, false, false),) +get_params(x::PSY.AVRTypeI) = ( + Ka = PSY.get_Ka(x), + Ke = PSY.get_Ke(x), + Kf = PSY.get_Kf(x), + Ta = PSY.get_Ta(x), + Te = PSY.get_Te(x), + Tf = PSY.get_Tf(x), + Tr = PSY.get_Tr(x), + Ae = PSY.get_Ae(x), + Be = PSY.get_Be(x), +) +get_params_metadata(::PSY.AVRTypeI) = ( + Ka = ParamsMetadata(DEVICE_PARAM, false, true), + Ke = ParamsMetadata(DEVICE_PARAM, false, true), + Kf = ParamsMetadata(DEVICE_PARAM, false, true), + Ta = ParamsMetadata(DEVICE_PARAM, false, false), + Te = ParamsMetadata(DEVICE_PARAM, false, false), + Tf = ParamsMetadata(DEVICE_PARAM, false, true), + Tr = ParamsMetadata(DEVICE_PARAM, false, false), + Ae = ParamsMetadata(DEVICE_PARAM, false, true), + Be = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.SEXS) = ( + Ta_Tb = PSY.get_Ta_Tb(x), + Tb = PSY.get_Tb(x), + K = PSY.get_K(x), + Te = PSY.get_Te(x), + V_lim = PSY.get_V_lim(x), +) +get_params_metadata(::PSY.SEXS) = ( + Ta_Tb = ParamsMetadata(DEVICE_PARAM, false, true), + Tb = ParamsMetadata(DEVICE_PARAM, false, false), + K = ParamsMetadata(DEVICE_PARAM, false, true), + Te = ParamsMetadata(DEVICE_PARAM, false, false), + V_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), +) +get_params(x::PSY.AVRTypeII) = ( + K0 = PSY.get_K0(x), + T1 = PSY.get_T1(x), + T2 = PSY.get_T2(x), + T3 = PSY.get_T3(x), + T4 = PSY.get_T4(x), + Te = PSY.get_Te(x), + Tr = PSY.get_Tr(x), + Va_lim = PSY.get_Va_lim(x), + Ae = PSY.get_Ae(x), + Be = PSY.get_Be(x), +) +get_params_metadata(::PSY.AVRTypeII) = ( + K0 = ParamsMetadata(DEVICE_PARAM, false, true), + T1 = ParamsMetadata(DEVICE_PARAM, false, true), + T2 = ParamsMetadata(DEVICE_PARAM, false, true), + T3 = ParamsMetadata(DEVICE_PARAM, false, true), + T4 = ParamsMetadata(DEVICE_PARAM, false, true), + Te = ParamsMetadata(DEVICE_PARAM, false, true), + Tr = ParamsMetadata(DEVICE_PARAM, false, false), + Va_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), + Ae = ParamsMetadata(DEVICE_PARAM, false, true), + Be = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.ESAC1A) = ( + Tr = PSY.get_Tr(x), + Tb = PSY.get_Tb(x), + Tc = PSY.get_Tc(x), + Ka = PSY.get_Ka(x), + Ta = PSY.get_Ta(x), + Va_lim = PSY.get_Va_lim(x), + Te = PSY.get_Te(x), + Kf = PSY.get_Kf(x), + Tf = PSY.get_Tf(x), + Kc = PSY.get_Kc(x), + Kd = PSY.get_Kd(x), + Ke = PSY.get_Ke(x), + Vr_lim = PSY.get_Vr_lim(x), +) +get_params_metadata(::PSY.ESAC1A) = ( + Tr = ParamsMetadata(DEVICE_PARAM, true, false), + Tb = ParamsMetadata(DEVICE_PARAM, true, false), + Tc = ParamsMetadata(DEVICE_PARAM, true, false), + Ka = ParamsMetadata(DEVICE_PARAM, true, false), + Ta = ParamsMetadata(DEVICE_PARAM, false, false), + Va_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + Te = ParamsMetadata(DEVICE_PARAM, false, false), + Kf = ParamsMetadata(DEVICE_PARAM, true, false), + Tf = ParamsMetadata(DEVICE_PARAM, true, false), + Kc = ParamsMetadata(DEVICE_PARAM, true, false), + Kd = ParamsMetadata(DEVICE_PARAM, true, false), + Ke = ParamsMetadata(DEVICE_PARAM, true, false), + Vr_lim = ( + min = ParamsMetadata(DEVICE_PARAM, true, false), + max = ParamsMetadata(DEVICE_PARAM, true, false), + ), +) +get_params(x::PSY.EXST1) = ( + Tr = PSY.get_Tr(x), + Vi_lim = PSY.get_Vi_lim(x), + Tc = PSY.get_Tc(x), + Tb = PSY.get_Tb(x), + Ka = PSY.get_Ka(x), + Ta = PSY.get_Ta(x), + Vr_lim = PSY.get_Vr_lim(x), + Kc = PSY.get_Kc(x), + Kf = PSY.get_Kf(x), + Tf = PSY.get_Tf(x), +) +get_params_metadata(::PSY.EXST1) = ( + Tr = ParamsMetadata(DEVICE_PARAM, true, false), + Vi_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + Tc = ParamsMetadata(DEVICE_PARAM, false, true), + Tb = ParamsMetadata(DEVICE_PARAM, true, true), + Ka = ParamsMetadata(DEVICE_PARAM, false, true), + Ta = ParamsMetadata(DEVICE_PARAM, true, false), + Vr_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), + Kc = ParamsMetadata(DEVICE_PARAM, false, true), + Kf = ParamsMetadata(DEVICE_PARAM, false, true), + Tf = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.EXAC1) = ( + Tr = PSY.get_Tr(x), + Tb = PSY.get_Tb(x), + Tc = PSY.get_Tc(x), + Ka = PSY.get_Ka(x), + Ta = PSY.get_Ta(x), + Vr_lim = PSY.get_Vr_lim(x), + Te = PSY.get_Te(x), + Kf = PSY.get_Kf(x), + Tf = PSY.get_Tf(x), + Kc = PSY.get_Kc(x), + Kd = PSY.get_Kd(x), + Ke = PSY.get_Ke(x), +) +get_params_metadata(::PSY.EXAC1) = ( + Tr = ParamsMetadata(DEVICE_PARAM, true, false), + Tb = ParamsMetadata(DEVICE_PARAM, true, true), + Tc = ParamsMetadata(DEVICE_PARAM, false, true), + Ka = ParamsMetadata(DEVICE_PARAM, false, true), + Ta = ParamsMetadata(DEVICE_PARAM, true, false), + Vr_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), + Te = ParamsMetadata(DEVICE_PARAM, false, false), + Kf = ParamsMetadata(DEVICE_PARAM, false, true), + Tf = ParamsMetadata(DEVICE_PARAM, false, true), + Kc = ParamsMetadata(DEVICE_PARAM, false, true), + Kd = ParamsMetadata(DEVICE_PARAM, false, true), + Ke = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.SCRX) = ( + Ta_Tb = PSY.get_Ta_Tb(x), + Tb = PSY.get_Tb(x), + K = PSY.get_K(x), + Te = PSY.get_Te(x), + Efd_lim = PSY.get_Efd_lim(x), + rc_rfd = PSY.get_rc_rfd(x), +) +get_params_metadata(::PSY.SCRX) = ( + Ta_Tb = ParamsMetadata(DEVICE_PARAM, false, true), + Tb = ParamsMetadata(DEVICE_PARAM, true, true), + K = ParamsMetadata(DEVICE_PARAM, false, true), + Te = ParamsMetadata(DEVICE_PARAM, true, false), + Efd_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), + rc_rfd = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.ESST1A) = ( + Tr = PSY.get_Tr(x), + Vi_lim = (min = PSY.get_Vi_lim(x)[1], max = min = PSY.get_Vi_lim(x)[2]), + Tc = PSY.get_Tc(x), + Tb = PSY.get_Tb(x), + Tc1 = PSY.get_Tc1(x), + Tb1 = PSY.get_Tb1(x), + Ka = PSY.get_Ka(x), + Ta = PSY.get_Ta(x), + Va_lim = PSY.get_Va_lim(x), + Vr_lim = PSY.get_Vr_lim(x), + Kc = PSY.get_Kc(x), + Kf = PSY.get_Kf(x), + Tf = PSY.get_Tf(x), + K_lr = PSY.get_K_lr(x), + I_lr = PSY.get_I_lr(x), +) +get_params_metadata(::PSY.ESST1A) = ( + Tr = ParamsMetadata(DEVICE_PARAM, true, false), + Vi_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + Tc = ParamsMetadata(DEVICE_PARAM, false, true), + Tb = ParamsMetadata(DEVICE_PARAM, true, true), + Tc1 = ParamsMetadata(DEVICE_PARAM, false, true), + Tb1 = ParamsMetadata(DEVICE_PARAM, true, true), + Ka = ParamsMetadata(DEVICE_PARAM, false, true), + Ta = ParamsMetadata(DEVICE_PARAM, true, false), + Va_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + Vr_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), + Kc = ParamsMetadata(DEVICE_PARAM, false, true), + Kf = ParamsMetadata(DEVICE_PARAM, false, true), + Tf = ParamsMetadata(DEVICE_PARAM, false, true), + K_lr = ParamsMetadata(DEVICE_PARAM, false, true), + I_lr = ParamsMetadata(DEVICE_PARAM, false, true), +) +#TurbineGov +get_params(x::PSY.TGFixed) = (; efficiency = PSY.get_efficiency(x)) +get_params_metadata(::PSY.TGFixed) = + (; efficiency = ParamsMetadata(DEVICE_PARAM, false, true)) +get_params(x::PSY.DEGOV) = ( + T1 = PSY.get_T1(x), + T2 = PSY.get_T2(x), + T3 = PSY.get_T3(x), + K = PSY.get_K(x), + T4 = PSY.get_T4(x), + T5 = PSY.get_T5(x), + T6 = PSY.get_T6(x), +) +get_params_metadata(::PSY.DEGOV) = ( + T1 = ParamsMetadata(DEVICE_PARAM, true, false), + T2 = ParamsMetadata(DEVICE_PARAM, true, false), + T3 = ParamsMetadata(DEVICE_PARAM, false, false), + K = ParamsMetadata(DEVICE_PARAM, false, false), + T4 = ParamsMetadata(DEVICE_PARAM, false, false), + T5 = ParamsMetadata(DEVICE_PARAM, true, false), + T6 = ParamsMetadata(DEVICE_PARAM, true, false), +) +get_params(x::PSY.TGTypeII) = ( + R = PSY.get_R(x), + T1 = PSY.get_T1(x), + T2 = PSY.get_T2(x), +) +get_params_metadata(::PSY.TGTypeII) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + T1 = ParamsMetadata(DEVICE_PARAM, false, true), + T2 = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.GasTG) = ( + R = PSY.get_R(x), + T1 = PSY.get_T1(x), + T2 = PSY.get_T2(x), + T3 = PSY.get_T3(x), + AT = PSY.get_AT(x), + Kt = PSY.get_Kt(x), + V_lim = (min = PSY.get_V_lim(x)[1], max = PSY.get_V_lim(x)[2]), + D_turb = PSY.get_D_turb(x), +) +get_params_metadata(::PSY.GasTG) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + T1 = ParamsMetadata(DEVICE_PARAM, false, false), + T2 = ParamsMetadata(DEVICE_PARAM, false, false), + T3 = ParamsMetadata(DEVICE_PARAM, false, false), + AT = ParamsMetadata(DEVICE_PARAM, false, true), + Kt = ParamsMetadata(DEVICE_PARAM, false, true), + V_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), + D_turb = ParamsMetadata(DEVICE_PARAM, false, true), +) + +get_params(x::PSY.TGTypeI) = ( + R = PSY.get_R(x), + Ts = PSY.get_Ts(x), + Tc = PSY.get_Tc(x), + T3 = PSY.get_T3(x), + T4 = PSY.get_T4(x), + T5 = PSY.get_T5(x), + valve_position_limits = PSY.get_valve_position_limits(x), +) + +get_params_metadata(::PSY.TGTypeI) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + Ts = ParamsMetadata(DEVICE_PARAM, false, false), + Tc = ParamsMetadata(DEVICE_PARAM, false, true), + T3 = ParamsMetadata(DEVICE_PARAM, false, true), + T4 = ParamsMetadata(DEVICE_PARAM, false, true), + T5 = ParamsMetadata(DEVICE_PARAM, false, true), + valve_position_limits = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), +) +get_params(x::PSY.SteamTurbineGov1) = ( + R = PSY.get_R(x), + T1 = PSY.get_T1(x), + valve_position_limits = PSY.get_valve_position_limits(x), + T2 = PSY.get_T2(x), + T3 = PSY.get_T3(x), + D_T = PSY.get_D_T(x), +) +get_params_metadata(::PSY.SteamTurbineGov1) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + T1 = ParamsMetadata(DEVICE_PARAM, false, true), + valve_position_limits = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), + T2 = ParamsMetadata(DEVICE_PARAM, false, true), + T3 = ParamsMetadata(DEVICE_PARAM, false, true), + D_T = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.HydroTurbineGov) = ( + R = PSY.get_R(x), + r = PSY.get_r(x), + Tr = PSY.get_Tr(x), + Tf = PSY.get_Tf(x), + Tg = PSY.get_Tg(x), + VELM = PSY.get_VELM(x), + gate_position_limits = PSY.get_gate_position_limits(x), + Tw = PSY.get_Tw(x), + At = PSY.get_At(x), + D_T = PSY.get_D_T(x), + q_nl = PSY.get_q_nl(x), +) +get_params_metadata(::PSY.HydroTurbineGov) = ( + R = ParamsMetadata(DEVICE_PARAM, false, true), + r = ParamsMetadata(DEVICE_PARAM, false, true), + Tr = ParamsMetadata(DEVICE_PARAM, false, true), + Tf = ParamsMetadata(DEVICE_PARAM, true, false), + Tg = ParamsMetadata(DEVICE_PARAM, true, false), + VELM = ParamsMetadata(DEVICE_PARAM, false, false), + gate_position_limits = ( + min = ParamsMetadata(DEVICE_PARAM, false, true), + max = ParamsMetadata(DEVICE_PARAM, false, true), + ), + Tw = ParamsMetadata(DEVICE_PARAM, false, false), + At = ParamsMetadata(DEVICE_PARAM, false, true), + D_T = ParamsMetadata(DEVICE_PARAM, false, true), + q_nl = ParamsMetadata(DEVICE_PARAM, false, true), +) +#PSS +get_params(x::PSY.PSSFixed) = (; V_pss = PSY.get_V_pss(x)) +get_params_metadata(::PSY.PSSFixed) = (; V_pss = ParamsMetadata(DEVICE_PARAM, false, false)) +get_params(x::PSY.STAB1) = ( + KT = PSY.get_KT(x), + T = PSY.get_T(x), + T1T3 = PSY.get_T1T3(x), + T3 = PSY.get_T3(x), + T2T4 = PSY.get_T2T4(x), + T4 = PSY.get_T4(x), + H_lim = PSY.get_H_lim(x), +) +get_params_metadata(::PSY.STAB1) = ( + KT = ParamsMetadata(DEVICE_PARAM, false, false), + T = ParamsMetadata(DEVICE_PARAM, false, false), + T1T3 = ParamsMetadata(DEVICE_PARAM, false, false), + T3 = ParamsMetadata(DEVICE_PARAM, false, false), + T2T4 = ParamsMetadata(DEVICE_PARAM, false, false), + T4 = ParamsMetadata(DEVICE_PARAM, false, false), + H_lim = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.IEEEST) = ( + A1 = PSY.get_A1(x), + A2 = PSY.get_A2(x), + A3 = PSY.get_A3(x), + A4 = PSY.get_A4(x), + A5 = PSY.get_A5(x), + A6 = PSY.get_A6(x), + T1 = PSY.get_T1(x), + T2 = PSY.get_T2(x), + T3 = PSY.get_T3(x), + T4 = PSY.get_T4(x), + T5 = PSY.get_T5(x), + T6 = PSY.get_T6(x), + Ks = PSY.get_Ks(x), + Ls_lim1 = PSY.get_Ls_lim(x)[1], + Ls_lim2 = PSY.get_Ls_lim(x)[2], + Vcu = PSY.get_Vcu(x), + Vcl = PSY.get_Vcl(x), +) +get_params_metadata(::PSY.IEEEST) = ( + A1 = ParamsMetadata(DEVICE_PARAM, false, true), + A2 = ParamsMetadata(DEVICE_PARAM, true, true), + A3 = ParamsMetadata(DEVICE_PARAM, false, false), + A4 = ParamsMetadata(DEVICE_PARAM, true, false), + A5 = ParamsMetadata(DEVICE_PARAM, false, true), + A6 = ParamsMetadata(DEVICE_PARAM, false, true), + T1 = ParamsMetadata(DEVICE_PARAM, false, true), + T2 = ParamsMetadata(DEVICE_PARAM, true, true), + T3 = ParamsMetadata(DEVICE_PARAM, false, true), + T4 = ParamsMetadata(DEVICE_PARAM, true, true), + T5 = ParamsMetadata(DEVICE_PARAM, false, true), + T6 = ParamsMetadata(DEVICE_PARAM, true, true), + Ks = ParamsMetadata(DEVICE_PARAM, false, true), + Ls_lim1 = ParamsMetadata(DEVICE_PARAM, false, true), + Ls_lim2 = ParamsMetadata(DEVICE_PARAM, false, true), + Vcu = ParamsMetadata(DEVICE_PARAM, false, true), + Vcl = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.PSS2B) = ( + Tw1 = PSY.get_Tw1(x), + Tw2 = PSY.get_Tw2(x), + T6 = PSY.get_T6(x), + Tw3 = PSY.get_Tw3(x), + Tw4 = PSY.get_Tw4(x), + T7 = PSY.get_T7(x), + Ks2 = PSY.get_Ks2(x), + Ks3 = PSY.get_Ks3(x), + T8 = PSY.get_T8(x), + T9 = PSY.get_T9(x), + Ks1 = PSY.get_Ks1(x), + T1 = PSY.get_T1(x), + T2 = PSY.get_T2(x), + T3 = PSY.get_T3(x), + T4 = PSY.get_T4(x), + T10 = PSY.get_T10(x), + T11 = PSY.get_T11(x), +) +get_params_metadata(::PSY.PSS2B) = ( + Tw1 = ParamsMetadata(DEVICE_PARAM, false, true), + Tw2 = ParamsMetadata(DEVICE_PARAM, false, false), + T6 = ParamsMetadata(DEVICE_PARAM, false, false), + Tw3 = ParamsMetadata(DEVICE_PARAM, false, true), + Tw4 = ParamsMetadata(DEVICE_PARAM, false, false), + T7 = ParamsMetadata(DEVICE_PARAM, false, false), + Ks2 = ParamsMetadata(DEVICE_PARAM, false, false), + Ks3 = ParamsMetadata(DEVICE_PARAM, false, false), + T8 = ParamsMetadata(DEVICE_PARAM, false, false), + T9 = ParamsMetadata(DEVICE_PARAM, false, true), + Ks1 = ParamsMetadata(DEVICE_PARAM, false, false), + T1 = ParamsMetadata(DEVICE_PARAM, false, false), + T2 = ParamsMetadata(DEVICE_PARAM, false, false), + T3 = ParamsMetadata(DEVICE_PARAM, false, false), + T4 = ParamsMetadata(DEVICE_PARAM, false, false), + T10 = ParamsMetadata(DEVICE_PARAM, false, false), + T11 = ParamsMetadata(DEVICE_PARAM, false, false), +) +#SOURCE +get_params(x::PSY.Source) = ( + R_th = PSY.get_R_th(x), + X_th = PSY.get_X_th(x), +) +get_params_metadata(::PSY.Source) = ( + R_th = ParamsMetadata(DEVICE_PARAM, false, true), + X_th = ParamsMetadata(DEVICE_PARAM, false, true), +) +#Parameters not implemented for PeriodicVariableSource - requires change in PSY Struct to have information required to construct and deconstruct parameter vector + +#DYNAMIC LOADS +get_params(x::PSY.ActiveConstantPowerLoad) = ( + r_load = PSY.get_r_load(x), + c_dc = PSY.get_c_dc(x), + rf = PSY.get_rf(x), + lf = PSY.get_lf(x), + cf = PSY.get_cf(x), + rg = PSY.get_rg(x), + lg = PSY.get_lg(x), + kp_pll = PSY.get_kp_pll(x), + ki_pll = PSY.get_ki_pll(x), + kpv = PSY.get_kpv(x), + kiv = PSY.get_kiv(x), + kpc = PSY.get_kpc(x), + kic = PSY.get_kic(x), + base_power = PSY.get_base_power(x), +) +get_params_metadata(::PSY.ActiveConstantPowerLoad) = ( + r_load = ParamsMetadata(DEVICE_PARAM, false, true), + c_dc = ParamsMetadata(DEVICE_PARAM, false, false), + rf = ParamsMetadata(DEVICE_PARAM, false, true), + lf = ParamsMetadata(DEVICE_PARAM, true, true), + cf = ParamsMetadata(DEVICE_PARAM, true, true), + rg = ParamsMetadata(DEVICE_PARAM, false, true), + lg = ParamsMetadata(DEVICE_PARAM, true, true), + kp_pll = ParamsMetadata(DEVICE_PARAM, false, false), + ki_pll = ParamsMetadata(DEVICE_PARAM, false, false), + kpv = ParamsMetadata(DEVICE_PARAM, false, false), + kiv = ParamsMetadata(DEVICE_PARAM, false, true), + kpc = ParamsMetadata(DEVICE_PARAM, false, false), + kic = ParamsMetadata(DEVICE_PARAM, false, true), + base_power = ParamsMetadata(DEVICE_PARAM, false, false), +) +get_params(x::PSY.SingleCageInductionMachine) = ( + R_s = PSY.get_R_s(x), + R_r = PSY.get_R_r(x), + X_ls = PSY.get_X_ls(x), + X_lr = PSY.get_X_lr(x), + X_m = PSY.get_X_m(x), + H = PSY.get_H(x), + A = PSY.get_A(x), + B = PSY.get_B(x), + base_power = PSY.get_base_power(x), + C = PSY.get_C(x), + τ_ref = PSY.get_τ_ref(x), + B_shunt = PSY.get_B_shunt(x), + X_ad = PSY.get_X_ad(x), + X_aq = PSY.get_X_aq(x), +) +get_params_metadata(::PSY.SingleCageInductionMachine) = ( + R_s = ParamsMetadata(DEVICE_PARAM, false, true), + R_r = ParamsMetadata(DEVICE_PARAM, false, true), + X_ls = ParamsMetadata(DEVICE_PARAM, false, true), + X_lr = ParamsMetadata(DEVICE_PARAM, false, true), + X_m = ParamsMetadata(DEVICE_PARAM, false, false), + H = ParamsMetadata(DEVICE_PARAM, false, false), + A = ParamsMetadata(DEVICE_PARAM, false, true), + B = ParamsMetadata(DEVICE_PARAM, false, true), + base_power = ParamsMetadata(DEVICE_PARAM, false, true), + C = ParamsMetadata(DEVICE_PARAM, false, true), + τ_ref = ParamsMetadata(DEVICE_PARAM, false, true), + B_shunt = ParamsMetadata(DEVICE_PARAM, false, true), + X_ad = ParamsMetadata(DEVICE_PARAM, false, true), + X_aq = ParamsMetadata(DEVICE_PARAM, false, true), +) + +get_params(x::PSY.SimplifiedSingleCageInductionMachine) = ( + R_s = PSY.get_R_s(x), + R_r = PSY.get_R_r(x), + X_ls = PSY.get_X_ls(x), + X_lr = PSY.get_X_lr(x), + X_m = PSY.get_X_m(x), + H = PSY.get_H(x), + A = PSY.get_A(x), + B = PSY.get_B(x), + base_power = PSY.get_base_power(x), + C = PSY.get_C(x), + τ_ref = PSY.get_τ_ref(x), + B_shunt = PSY.get_B_shunt(x), + X_ss = PSY.get_X_ss(x), + X_rr = PSY.get_X_rr(x), + X_p = PSY.get_X_p(x), +) +get_params_metadata(::PSY.SimplifiedSingleCageInductionMachine) = ( + R_s = ParamsMetadata(DEVICE_PARAM, false, true), + R_r = ParamsMetadata(DEVICE_PARAM, false, true), + X_ls = ParamsMetadata(DEVICE_PARAM, false, false), + X_lr = ParamsMetadata(DEVICE_PARAM, false, false), + X_m = ParamsMetadata(DEVICE_PARAM, false, true), + H = ParamsMetadata(DEVICE_PARAM, false, false), + A = ParamsMetadata(DEVICE_PARAM, false, true), + B = ParamsMetadata(DEVICE_PARAM, false, true), + base_power = ParamsMetadata(DEVICE_PARAM, false, true), + C = ParamsMetadata(DEVICE_PARAM, false, true), + τ_ref = ParamsMetadata(DEVICE_PARAM, false, true), + B_shunt = ParamsMetadata(DEVICE_PARAM, false, true), + X_ss = ParamsMetadata(DEVICE_PARAM, false, true), + X_rr = ParamsMetadata(DEVICE_PARAM, false, true), + X_p = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.AggregateDistributedGenerationA) = ( + T_rv = PSY.get_T_rv(x), + Trf = PSY.get_Trf(x), + dbd_pnts1 = PSY.get_dbd_pnts(x)[1], + dbd_pnts2 = PSY.get_dbd_pnts(x)[2], + K_qv = PSY.get_K_qv(x), + Tp = PSY.get_Tp(x), + T_iq = PSY.get_T_iq(x), + D_dn = PSY.get_D_dn(x), + D_up = PSY.get_D_up(x), + fdbd_pnts1 = PSY.get_fdbd_pnts(x)[1], + fdbd_pnts2 = PSY.get_fdbd_pnts(x)[2], + fe_lim = PSY.get_fe_lim(x), + P_lim = PSY.get_P_lim(x), + dP_lim = PSY.get_dP_lim(x), + Tpord = PSY.get_Tpord(x), + Kpg = PSY.get_Kpg(x), + Kig = PSY.get_Kig(x), + I_max = PSY.get_I_max(x), + Tg = PSY.get_Tg(x), + rrpwr = PSY.get_rrpwr(x), + Tv = PSY.get_Tv(x), + Vpr = PSY.get_Vpr(x), + Iq_lim = PSY.get_Iq_lim(x), + base_power = PSY.get_base_power(x), + Pfa_ref = PSY.get_Pfa_ref(x), +) +get_params_metadata(::PSY.AggregateDistributedGenerationA) = ( + T_rv = ParamsMetadata(DEVICE_PARAM, true, false), + Trf = ParamsMetadata(DEVICE_PARAM, true, false), + dbd_pnts1 = ParamsMetadata(DEVICE_PARAM, false, false), + dbd_pnts2 = ParamsMetadata(DEVICE_PARAM, false, false), + K_qv = ParamsMetadata(DEVICE_PARAM, false, true), + Tp = ParamsMetadata(DEVICE_PARAM, true, false), + T_iq = ParamsMetadata(DEVICE_PARAM, true, false), + D_dn = ParamsMetadata(DEVICE_PARAM, false, false), + D_up = ParamsMetadata(DEVICE_PARAM, false, false), + fdbd_pnts1 = ParamsMetadata(DEVICE_PARAM, false, false), + fdbd_pnts2 = ParamsMetadata(DEVICE_PARAM, false, false), + fe_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + P_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + dP_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + Tpord = ParamsMetadata(DEVICE_PARAM, true, false), + Kpg = ParamsMetadata(DEVICE_PARAM, false, false), + Kig = ParamsMetadata(DEVICE_PARAM, false, false), + I_max = ParamsMetadata(DEVICE_PARAM, false, false), + Tg = ParamsMetadata(DEVICE_PARAM, false, false), + rrpwr = ParamsMetadata(DEVICE_PARAM, false, false), + Tv = ParamsMetadata(DEVICE_PARAM, true, false), + Vpr = ParamsMetadata(DEVICE_PARAM, false, false), + Iq_lim = ( + min = ParamsMetadata(DEVICE_PARAM, false, false), + max = ParamsMetadata(DEVICE_PARAM, false, false), + ), + base_power = ParamsMetadata(DEVICE_PARAM, false, false), + Pfa_ref = ParamsMetadata(DEVICE_PARAM, false, true), +) +get_params(x::PSY.CSVGN1) = ( + K = PSY.get_K(x), + T1 = PSY.get_T1(x), + T2 = PSY.get_T2(x), + T3 = PSY.get_T3(x), + T4 = PSY.get_T4(x), + T5 = PSY.get_T5(x), + Rmin = PSY.get_Rmin(x), + Vmax = PSY.get_Vmax(x), + Vmin = PSY.get_Vmin(x), + CBase = PSY.get_CBase(x), + base_power = PSY.get_base_power(x), + R_th = PSY.get_R_th(x), + X_th = PSY.get_X_th(x), +) +get_params_metadata(::PSY.CSVGN1) = ( + K = ParamsMetadata(DEVICE_PARAM, false, false), + T1 = ParamsMetadata(DEVICE_PARAM, false, false), + T2 = ParamsMetadata(DEVICE_PARAM, false, false), + T3 = ParamsMetadata(DEVICE_PARAM, false, false), + T4 = ParamsMetadata(DEVICE_PARAM, false, false), + T5 = ParamsMetadata(DEVICE_PARAM, false, false), + Rmin = ParamsMetadata(DEVICE_PARAM, false, false), + Vmax = ParamsMetadata(DEVICE_PARAM, false, false), + Vmin = ParamsMetadata(DEVICE_PARAM, false, false), + CBase = ParamsMetadata(DEVICE_PARAM, false, false), + base_power = ParamsMetadata(DEVICE_PARAM, false, false), + R_th = ParamsMetadata(DEVICE_PARAM, false, false), + X_th = ParamsMetadata(DEVICE_PARAM, false, false), +) diff --git a/src/utils/print.jl b/src/utils/print.jl index f67b02a45..3bcd96d19 100644 --- a/src/utils/print.jl +++ b/src/utils/print.jl @@ -63,7 +63,7 @@ function show_simulation_table( ) where {T <: SimulationModel} header = ["Property", "Value"] val_multimachine = sim.multimachine ? "Yes" : "No" - val_initialized = sim.initialized ? "Yes" : "No" + val_initialized = (sim.initialize_level == INITIALIZED) ? "Yes" : "No" val_model = T == ResidualModel ? "Residual Model" : "Mass Matrix Model" table = [ "Status" sim.status @@ -71,7 +71,7 @@ function show_simulation_table( "Initialized?" val_initialized "Multimachine system?" val_multimachine "Time Span" string(sim.tspan) - "Number of States" string(length(sim.x0_init)) + "Number of States" string(length(sim.x0)) "Number of Perturbations" string(length(sim.perturbations)) ] PrettyTables.pretty_table( diff --git a/src/utils/psy_utils.jl b/src/utils/psy_utils.jl index 36a8fd9ca..3393703ce 100644 --- a/src/utils/psy_utils.jl +++ b/src/utils/psy_utils.jl @@ -33,14 +33,6 @@ function get_dynamic_branches(sys::PSY.System) return PSY.get_components(x -> PSY.get_available(x), PSY.DynamicBranch, sys) end -function _transform_all_lines!(sys::PSY.System) - for br in PSY.get_components(PSY.DynamicBranch, sys) - dyn_br = DynamicBranch(br) - @debug "Converted $(PSY.get_name(dyn_br)) to DynamicBranch" - add_component!(sys, dyn_br) - end -end - function transform_ybus_to_rectangular( ybus::SparseArrays.SparseMatrixCSC{Complex{Float64}, Int}, ) diff --git a/test/Project.toml b/test/Project.toml index 89fcb075f..d61612060 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,24 +1,32 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DelayDiffEq = "bcd4f6db-9728-5f36-b5f7-82caef46ccdb" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" InfrastructureSystems = "2cd47ed4-ca9b-11e9-27f2-ab636a7671f1" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" +Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" +OptimizationOptimisers = "42dfb2eb-d2b4-4451-abcd-913932933ac1" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" PowerFlows = "94fada2c-fd9a-4e89-8d82-81405f5cb4f6" PowerNetworkMatrices = "bed98974-b02a-5e2f-9fe0-a103f5c450dd" PowerSimulationsDynamics = "398b2ede-47ed-4edc-b52e-69e4a48b4336" PowerSystemCaseBuilder = "f00506e0-b84f-492a-93c2-c0a9afc4364e" PowerSystems = "bcd98974-b02a-5e2f-9ee0-a103f5c450dd" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [compat] SciMLBase = "2" diff --git a/test/runtests.jl b/test/runtests.jl index 76b8cf297..14ec6567b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,4 @@ +using Revise using PowerSimulationsDynamics using PowerSystems using Test @@ -14,6 +15,13 @@ using PowerFlows using PowerNetworkMatrices import LinearAlgebra using Logging +using SciMLSensitivity +using Enzyme +Enzyme.API.runtimeActivity!(true) #Needed for "activity unstable" code: https://enzymead.github.io/Enzyme.jl/stable/faq/ +Enzyme.API.looseTypeAnalysis!(true) #Required for using component arrays with Enzyme +using Zygote +using Optimization +using OptimizationOptimisers import Aqua Aqua.test_unbound_args(PowerSimulationsDynamics) @@ -97,7 +105,7 @@ function get_logging_level(env_name::String, default) end function run_tests() - console_level = get_logging_level("SYS_CONSOLE_LOG_LEVEL", "Error") + console_level = get_logging_level("SYS_CONSOLE_LOG_LEVEL", "Debug") console_logger = ConsoleLogger(stderr, console_level) file_level = get_logging_level("SYS_LOG_LEVEL", "Info") diff --git a/test/test_base.jl b/test/test_base.jl index 8fe3a9875..44f6fa19a 100644 --- a/test/test_base.jl +++ b/test/test_base.jl @@ -200,7 +200,7 @@ end sim_inputs = sim.inputs DAE_vector = PSID.get_DAE_vector(sim_inputs) @test all(DAE_vector) - @test all(LinearAlgebra.diag(sim_inputs.mass_matrix) .> 0) + @test all(LinearAlgebra.diag(PSID.PSID.get_mass_matrix(sim_inputs)) .> 0) total_shunts = PSID.get_total_shunts(sim_inputs) # Total shunts matrix follows same pattern as the rectangular Ybus for v in LinearAlgebra.diag(total_shunts[1:3, 4:end]) @@ -211,7 +211,7 @@ end @test v < 0 end - for entry in LinearAlgebra.diag(sim_inputs.mass_matrix) + for entry in LinearAlgebra.diag(PSID.get_mass_matrix(sim_inputs)) @test entry > 0 end voltage_buses_ix = PSID.get_voltage_buses_ix(sim_inputs) @@ -234,9 +234,9 @@ end @test sum(.!DAE_vector) == 6 for (ix, entry) in enumerate(DAE_vector) if !entry - @test LinearAlgebra.diag(sim_inputs.mass_matrix)[ix] == 0 + @test LinearAlgebra.diag(PSID.get_mass_matrix(sim_inputs))[ix] == 0 elseif entry - @test LinearAlgebra.diag(sim_inputs.mass_matrix)[ix] > 0 + @test LinearAlgebra.diag(PSID.get_mass_matrix(sim_inputs))[ix] > 0 else @test false end @@ -271,7 +271,7 @@ end initial_conditions = x0_test, console_level = Logging.Error, ) - @test LinearAlgebra.norm(sim.x0_init - x0_test) <= 1e-6 + @test LinearAlgebra.norm(sim.x0 - x0_test) <= 1e-6 #Initialize System normally sim_normal = Simulation( ResidualModel, @@ -282,7 +282,7 @@ end console_level = Logging.Error, ) #Save states without generator at bus 2 - x0 = sim_normal.x0_init + x0 = sim_normal.x0 x0_no_gen = vcat(x0[1:6], x0[13:end]) #Make Generator 2 unavailable and transform bus into PQ bus gen = PSY.get_component(ThermalStandard, sys, "generator-102-1") @@ -299,7 +299,7 @@ end initial_conditions = x0_no_gen, console_level = Logging.Error, ) - @test LinearAlgebra.norm(sim_trip_gen.x0_init - x0_no_gen) <= 1e-6 + @test LinearAlgebra.norm(sim_trip_gen.x0 - x0_no_gen) <= 1e-6 #Create Simulation without Gen 2 at steady state sim_normal_no_gen = Simulation( ResidualModel, @@ -309,7 +309,7 @@ end BranchTrip(1.0, Line, "BUS 1-BUS 2-i_1"); console_level = Logging.Error, ) - @test length(sim_normal_no_gen.x0_init) == 17 + @test length(sim_normal_no_gen.x0) == 17 #Ignore Initial Conditions without passing initialize_simulation = false sim_ignore_init = Simulation( ResidualModel, @@ -319,7 +319,7 @@ end initial_conditions = x0_no_gen, console_level = Logging.Error, ) - @test LinearAlgebra.norm(sim_ignore_init.x0_init - sim_normal_no_gen.x0_init) <= 1e-6 + @test LinearAlgebra.norm(sim_ignore_init.x0 - sim_normal_no_gen.x0) <= 1e-6 #Pass wrong vector size x0_wrong = zeros(20) # @test_logs (:error, "Build failed") match_mode = :any Simulation( @@ -342,7 +342,7 @@ end ) x0_flat = zeros(17) x0_flat[1:3] .= 1.0 - @test LinearAlgebra.norm(sim_flat.x0_init - x0_flat) <= 1e-6 + @test LinearAlgebra.norm(sim_flat.x0 - x0_flat) <= 1e-6 end @testset "Test Network Kirchoff Calculation" begin @@ -356,7 +356,11 @@ end V_i = voltages[3:end] ybus_ = PNM.Ybus(omib_sys).data I_balance_ybus = -1 * ybus_ * (V_r + V_i .* 1im) - inputs = PSID.SimulationInputs(ResidualModel, omib_sys, ConstantFrequency()) + inputs = PSID.SimulationInputs( + ResidualModel, + omib_sys, + ConstantFrequency(), + ) I_balance_sim = zeros(4) PSID.network_model(inputs, I_balance_sim, voltages) for i in 1:2 @@ -384,15 +388,18 @@ end end ybus_original = PNM.Ybus(threebus_sys) - - inputs = PSID.SimulationInputs(ResidualModel, threebus_sys, ConstantFrequency()) + inputs = PSID.SimulationInputs( + ResidualModel, + threebus_sys, + ConstantFrequency(), + ) for i in 1:3, j in 1:3 complex_ybus = ybus_original.data[i, j] - @test inputs.ybus_rectangular[i, j] == real(complex_ybus) - @test inputs.ybus_rectangular[i + 3, j + 3] == real(complex_ybus) - @test inputs.ybus_rectangular[i + 3, j] == -imag(complex_ybus) - @test inputs.ybus_rectangular[i, j + 3] == imag(complex_ybus) + @test PSID.get_ybus(inputs)[i, j] == real(complex_ybus) + @test PSID.get_ybus(inputs)[i + 3, j + 3] == real(complex_ybus) + @test PSID.get_ybus(inputs)[i + 3, j] == -imag(complex_ybus) + @test PSID.get_ybus(inputs)[i, j + 3] == imag(complex_ybus) end br = get_component(Line, threebus_sys, "BUS 1-BUS 3-i_1") @@ -405,14 +412,14 @@ end # floating point than the inversion of a single float for i in 1:3, j in 1:3 complex_ybus = ybus_line_trip.data[i, j] - @test isapprox(inputs.ybus_rectangular[i, j], real(complex_ybus), atol = 1e-10) + @test isapprox(PSID.get_ybus(inputs)[i, j], real(complex_ybus), atol = 1e-10) @test isapprox( - inputs.ybus_rectangular[i + 3, j + 3], + PSID.get_ybus(inputs)[i + 3, j + 3], real(complex_ybus), atol = 1e-10, ) - @test isapprox(inputs.ybus_rectangular[i + 3, j], -imag(complex_ybus), atol = 1e-10) - @test isapprox(inputs.ybus_rectangular[i, j + 3], imag(complex_ybus), atol = 1e-10) + @test isapprox(PSID.get_ybus(inputs)[i + 3, j], -imag(complex_ybus), atol = 1e-10) + @test isapprox(PSID.get_ybus(inputs)[i, j + 3], imag(complex_ybus), atol = 1e-10) end threebus_sys = System(three_bus_file_dir; runchecks = false) @@ -455,20 +462,23 @@ end cref = ControlReferenceChange(1.0, mach, :P_ref, 10.0) ωref = ControlReferenceChange(1.0, inv, :ω_ref, 0.9) - - inputs = PSID.SimulationInputs(ResidualModel, threebus_sys, ConstantFrequency()) + inputs = PSID.SimulationInputs( + ResidualModel, + threebus_sys, + ConstantFrequency(), + ) integrator_for_test = MockIntegrator(inputs) - cref_affect_f = PSID.get_affect(inputs, threebus_sys, cref) ωref_affect_f = PSID.get_affect(inputs, threebus_sys, ωref) - cref_affect_f(integrator_for_test) ωref_affect_f(integrator_for_test) - - @test PSID.get_P_ref(inputs.dynamic_injectors[1]) == 10.0 - @test PSID.get_ω_ref(inputs.dynamic_injectors[2]) == 0.9 - - inputs = PSID.SimulationInputs(ResidualModel, threebus_sys, ConstantFrequency()) + @test inputs.parameters["generator-102-1"][:refs][:P_ref] == 10.0 + @test inputs.parameters["generator-103-1"][:refs][:ω_ref] == 0.9 + inputs = PSID.SimulationInputs( + ResidualModel, + threebus_sys, + ConstantFrequency(), + ) integrator_for_test = MockIntegrator(inputs) mach_trip = PSID.GeneratorTrip(1.0, mach) @@ -480,8 +490,8 @@ end mtrip_affect_f(integrator_for_test) itrip_affect_f(integrator_for_test) - @test PSID.get_connection_status(inputs.dynamic_injectors[1]) == 0.0 - @test PSID.get_connection_status(inputs.dynamic_injectors[2]) == 0.0 + @test PSID.get_connection_status(PSID.get_dynamic_injectors(inputs)[1]) == 0.0 + @test PSID.get_connection_status(PSID.get_dynamic_injectors(inputs)[2]) == 0.0 end @testset "Test Load perturbations callback affects" begin @@ -507,20 +517,21 @@ end load_val = LoadChange(1.0, load_1, :P_ref, 10.0) load_trip = LoadTrip(1.0, load_2) - - inputs = PSID.SimulationInputs(ResidualModel, threebus_sys, ConstantFrequency()) + inputs = PSID.SimulationInputs( + ResidualModel, + threebus_sys, + ConstantFrequency(), + ) integrator_for_test = MockIntegrator(inputs) lref_affect_f = PSID.get_affect(inputs, threebus_sys, load_val) ltrip_affect_f = PSID.get_affect(inputs, threebus_sys, load_trip) lref_affect_f(integrator_for_test) - zip_load1 = first(filter(x -> PSY.get_name(x) == "BUS 1", inputs.static_loads)) - @test PSID.get_P_impedance(zip_load1) == 10.0 + @test inputs.parameters["BUS 1"][:refs][:P_impedance] == 10.0 ltrip_affect_f(integrator_for_test) - zip_load2 = first(filter(x -> PSY.get_name(x) == "BUS 2", inputs.static_loads)) - @test PSID.get_P_impedance(zip_load2) == 0.0 - @test PSID.get_Q_impedance(zip_load2) == 0.0 + @test inputs.parameters["BUS 2"][:refs][:P_impedance] == 0.0 + @test inputs.parameters["BUS 2"][:refs][:Q_impedance] == 0.0 end @testset "Global Index" begin @@ -614,7 +625,7 @@ end # (0.0, 20.0), # # Not initialized to speed up the test # initialize_simulation = false, - # frequency_reference = ConstantFrequency(), #time span + # frequency_reference = ConstantFrequency()), #time span # ) end @@ -808,3 +819,52 @@ end 60.0, ) end + +@testset "Test Initialize Levels" begin + path = mktempdir() + try + #Compute Y_bus after fault + fault_branch = deepcopy(collect(get_components(Branch, omib_sys))[1]) + fault_branch.r = 0.00 + fault_branch.x = 0.1 + Ybus_fault = + PNM.Ybus([fault_branch], collect(get_components(ACBus, omib_sys)))[:, :] + + Ybus_change = NetworkSwitch( + 1.0, #change at t = 1.0 + Ybus_fault, + ) #New YBus + # Define Simulation Problem + sim = Simulation!( + ResidualModel, + omib_sys, #system + path, + (0.0, 20.0), #time span + Ybus_change; + console_level = Logging.Error, + ) + @test execute!(sim, IDA(); dtmax = 0.005, saveat = 0.005) == + PSID.SIMULATION_FINALIZED + results_original = read_results(sim) + series_original = + get_state_series(results_original, ("generator-102-1", :δ); dt = 0.01) + for initialize_level in + [PSID.POWERFLOW_AND_DEVICES, PSID.INITIALIZED, PSID.DEVICES_ONLY] + sim.initialize_level = initialize_level + @test execute!(sim, IDA(); dtmax = 0.005, saveat = 0.005) == + PSID.SIMULATION_FINALIZED + results = read_results(sim) + series = get_state_series(results, ("generator-102-1", :δ); dt = 0.01) + #Will not be identical for PSID.DEVICES_ONLY, the other two should be + if initialize_level == PSID.DEVICES_ONLY + @test sum(abs.(series[2] .- series_original[2])) < + PSID.STRICT_NLSOLVE_F_TOLERANCE + else + @test series == series_original + end + end + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end diff --git a/test/test_case22_SteamTurbineGov1.jl b/test/test_case22_SteamTurbineGov1.jl index fbb953c9a..77feda043 100644 --- a/test/test_case22_SteamTurbineGov1.jl +++ b/test/test_case22_SteamTurbineGov1.jl @@ -12,7 +12,7 @@ raw_file = joinpath(TEST_FILES_DIR, "benchmarks/psse/TGOV1/ThreeBusMulti.raw") dyr_file = joinpath(TEST_FILES_DIR, "benchmarks/psse/TGOV1/ThreeBus_TGOV1.dyr") csv_file = joinpath(TEST_FILES_DIR, "benchmarks/psse/TGOV1/TEST_TGOV1.csv") -@testset "Test 21 SteamTurbineGov1 ResidualModel" begin +@testset "Test 22 SteamTurbineGov1 ResidualModel" begin path = mktempdir() try sys = System(raw_file, dyr_file) @@ -72,7 +72,7 @@ csv_file = joinpath(TEST_FILES_DIR, "benchmarks/psse/TGOV1/TEST_TGOV1.csv") end end -@testset "Test 21 SteamTurbineGov1 MassMatrixModel" begin +@testset "Test 22 SteamTurbineGov1 MassMatrixModel" begin path = (joinpath(pwd(), "test-psse-tgov1")) !isdir(path) && mkdir(path) try diff --git a/test/test_case34_exp_load.jl b/test/test_case34_exp_load.jl index e39d266cc..7846c340e 100644 --- a/test/test_case34_exp_load.jl +++ b/test/test_case34_exp_load.jl @@ -56,7 +56,7 @@ end ) # Test Initial Conditions - @test LinearAlgebra.norm(sim_power.x0_init - sim_exp.x0_init) < 1e-4 + @test LinearAlgebra.norm(sim_power.x0 - sim_exp.x0) < 1e-4 # Test Small Signal ss_power = small_signal_analysis(sim_power) @@ -114,7 +114,7 @@ end ) # Test Initial Conditions - @test LinearAlgebra.norm(sim_power.x0_init - sim_exp.x0_init) < 1e-4 + @test LinearAlgebra.norm(sim_power.x0 - sim_exp.x0) < 1e-4 # Test Small Signal ss_power = small_signal_analysis(sim_power) diff --git a/test/test_case37_InductionMotor.jl b/test/test_case37_InductionMotor.jl index 3c086de81..34078dfe1 100644 --- a/test/test_case37_InductionMotor.jl +++ b/test/test_case37_InductionMotor.jl @@ -34,8 +34,8 @@ dyr_file = joinpath(TEST_FILES_DIR, "data_tests/TVC_System_motor.dyr") sim = Simulation(ResidualModel, sys, path, time_span, perturbation_trip) # Test initial voltages between two systems are equivalent - voltages_P = sim_P.x0_init[1:8] - voltages_motor = sim.x0_init[1:8] + voltages_P = sim_P.x0[1:8] + voltages_motor = sim.x0[1:8] @test LinearAlgebra.norm(voltages_P - voltages_motor) < 1e-4 # Test Initial Condition @@ -87,8 +87,8 @@ end sim = Simulation(MassMatrixModel, sys, path, time_span, perturbation_trip) # Test initial voltages between two systems are equivalent - voltages_P = sim_P.x0_init[1:8] - voltages_motor = sim.x0_init[1:8] + voltages_P = sim_P.x0[1:8] + voltages_motor = sim.x0[1:8] @test LinearAlgebra.norm(voltages_P - voltages_motor) < 1e-4 # Test Initial Condition diff --git a/test/test_case38_SimplifiedIndMotor.jl b/test/test_case38_SimplifiedIndMotor.jl index 5066c8fe5..57b1d25c8 100644 --- a/test/test_case38_SimplifiedIndMotor.jl +++ b/test/test_case38_SimplifiedIndMotor.jl @@ -34,8 +34,8 @@ dyr_file = joinpath(TEST_FILES_DIR, "data_tests/TVC_System_motor.dyr") sim = Simulation(ResidualModel, sys, path, time_span, perturbation_trip) # Test initial voltages between two systems are equivalent - voltages_P = sim_P.x0_init[1:8] - voltages_motor = sim.x0_init[1:8] + voltages_P = sim_P.x0[1:8] + voltages_motor = sim.x0[1:8] @test LinearAlgebra.norm(voltages_P - voltages_motor) < 1e-4 # Test Initial Condition @@ -87,8 +87,8 @@ end sim = Simulation(MassMatrixModel, sys, path, time_span, perturbation_trip) # Test initial voltages between two systems are equivalent - voltages_P = sim_P.x0_init[1:8] - voltages_motor = sim.x0_init[1:8] + voltages_P = sim_P.x0[1:8] + voltages_motor = sim.x0[1:8] @test LinearAlgebra.norm(voltages_P - voltages_motor) < 1e-4 # Test Initial Condition diff --git a/test/test_case56_powerload.jl b/test/test_case56_powerload.jl index 010cc4f20..a1e8aaea3 100644 --- a/test/test_case56_powerload.jl +++ b/test/test_case56_powerload.jl @@ -57,7 +57,7 @@ end ) # Test Initial Conditions - @test LinearAlgebra.norm(sim_power.x0_init - sim_standard.x0_init) < 1e-4 + @test LinearAlgebra.norm(sim_power.x0 - sim_standard.x0) < 1e-4 # Test Small Signal ss_power = small_signal_analysis(sim_power) @@ -116,7 +116,7 @@ end ) # Test Initial Conditions - @test LinearAlgebra.norm(sim_power.x0_init - sim_standard.x0_init) < 1e-4 + @test LinearAlgebra.norm(sim_power.x0 - sim_standard.x0) < 1e-4 # Test Small Signal ss_power = small_signal_analysis(sim_power) diff --git a/test/test_case_OMIB.jl b/test/test_case_OMIB.jl index 8cd2c90f7..9d8582e2a 100644 --- a/test/test_case_OMIB.jl +++ b/test/test_case_OMIB.jl @@ -87,6 +87,10 @@ 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) diff --git a/test/test_case_enzyme.jl b/test/test_case_enzyme.jl new file mode 100644 index 000000000..76a71e89a --- /dev/null +++ b/test/test_case_enzyme.jl @@ -0,0 +1,906 @@ +""" +Case 1: +This case study defines a classical machine against an infinite bus. Sensitivitiy +Analysis is performed for the inertia of the generator. The fault is a change in the +source voltage. This test also checks that incorrect user data is handled correctly. +""" +################################################## +############### LOAD DATA ###################### ## +################################################## + +ieee_9bus_sys = build_system(PSIDTestSystems, "psid_test_ieee_9bus") +andes_11bus_sys = build_system(PSIDSystems, "psid_11bus_andes") +################################################## +############### SOLVE PROBLEM #################### +################################################## + +using PlotlyJS + +#NOTES ON SENSITIVITY ALGORITHMS FROM SCIMLSENSITIVITY +#ReverseDiffVJP and EnzymeVJP only options compatible with Hybrid DEs (DEs with callbacks) +#BacksolveAdjoint prone to instabilities whenever the Lipschitz constant is sufficiently large (stiff equations, PDE discretizations, and many other contexts) +#For ForwardDiffSensitivity, convert_tspan=true is needed for hybrid equations. +@testset "Test Gradients - OMIB; H; SourceBusVoltageChange" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + + #GET GROUND TRUTH DATA + execute!(sim, Rodas4(); abstol = 1e-9, reltol = 1e-9, dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Shaft, :H)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + ) + execute!(sim, Rodas4()) + + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + Rodas4(), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + loss_zero = f_forward(p, [s_change], δ_gt, []) + loss_non_zero_1 = f_forward([3.2], [s_change], δ_gt, []) + loss_non_zero_2 = f_forward(p, [s_change], δ_gt .* 2, []) + @test loss_zero == 0.0 + @test loss_non_zero_1 != 0.0 + @test loss_non_zero_2 != 0.0 + @test get_parameter_labels(sim, [("generator-102-1", :Shaft, :H)]) == + ["generator-102-1.params.Shaft.H"] + @test isapprox(f_grad(p, [s_change], δ_gt, [])[1], -0.299332838697076, atol = 1e-3) + @test isapprox(f_grad([3.14], [s_change], δ_gt, [])[1], -8.174549313199039, atol = 1e-3) + @test isapprox(f_grad([3.15], [s_change], δ_gt, [])[1], 8.044840967274856; atol = 1e-3) + + #Add in parameter regularization to loss + function f_loss(p, states, δ_gt, aux) + return sum(abs.(states[1] - δ_gt)) + sum(abs.(p)) + end + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + Rodas4(), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + @test isapprox(f_grad(p, [s_change], δ_gt, [])[1], 0.7006671613029241, atol = 1e-3) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Gradients - OMIB; H; SourceBusVoltageChange; InterpolatingAdjoint" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + + #GET GROUND TRUTH DATA + execute!(sim, Rodas4(); abstol = 1e-9, reltol = 1e-9, dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Shaft, :H)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + ) + execute!(sim, Rodas4()) + + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + Rodas4(), + f_loss; + sensealg = InterpolatingAdjoint(; autojacvec = ReverseDiffVJP()), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + loss_zero = f_forward(p, [s_change], δ_gt, []) + loss_non_zero_1 = f_forward([3.2], [s_change], δ_gt, []) + loss_non_zero_2 = f_forward(p, [s_change], δ_gt .* 2, []) + @test loss_zero == 0.0 + @test loss_non_zero_1 == 0.36199910927656687 + @test loss_non_zero_2 == 172.66293171283323 + grad_zero = f_grad(p, [s_change], δ_gt, []) + grad_nonzero_1 = f_grad([3.14], [s_change], δ_gt, []) + grad_nonzero_2 = f_grad([3.15], [s_change], δ_gt, []) + @test isapprox(grad_zero[1], -1.0, atol = 1.0) + @test isapprox(grad_nonzero_1[1], -8.0, atol = 1.0) + @test isapprox(grad_nonzero_2[1], 8.0; atol = 1.0) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Gradients - OMIB; All; SourceBusVoltageChange" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + + #GET GROUND TRUTH DATA + execute!(sim, Rodas4(); abstol = 1e-9, reltol = 1e-9, dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + #GET PARAMETER VALUES + p = get_parameter_values(sim, :All) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + ) + execute!(sim, Rodas4()) + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + :All, + [("generator-102-1", :δ)], + Rodas4(), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + loss_zero = f_forward(p, [s_change], δ_gt, []) + loss_non_zero_1 = f_forward(p .* 1.01, [s_change], δ_gt, []) + + @test isapprox(loss_zero, 0.0, atol = 1e-9) + @test isapprox(loss_non_zero_1, 1.49, atol = 1e-3) + + grad_zero = f_grad(p, [s_change], δ_gt, []) + @test isapprox(sum(grad_zero), 524.5865384550988, atol = 1e-3) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Gradients - OMIB; H; PerturbState" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + #p_branch = BranchImpedanceChange(1.0, Line, "BUS 1-BUS 2-i_1", 2) BranchPerturbations not supported + p_state = PerturbState(1.0, 5, 0.18) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + p_state, + ) + #GET GROUND TRUTH DATA + execute!(sim, Rodas4(); abstol = 1e-9, reltol = 1e-9, dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ); unique_timestamps = true) #Avoid filtering of repeated timesteps + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Shaft, :H)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + ) + execute!(sim, Rodas4()) + f_forward, f_grad, f_zygote_forward = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + Rodas4(), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + @test f_forward(p, [p_state], δ_gt, []) == + f_zygote_forward(p, [p_state], δ_gt, []) + @test f_forward([3.14], [p_state], δ_gt, []) == + f_zygote_forward([3.14], [p_state], δ_gt, []) + @test f_forward([3.15], [p_state], δ_gt, []) == + f_zygote_forward([3.15], [p_state], δ_gt, []) + @test f_grad(p, [p_state], δ_gt, []) == + Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt, []), p)[1] + + _, _, f_zygote_forward = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + Rodas4(), + f_loss; + sensealg = ReverseDiffAdjoint(), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + @test isapprox( + Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt, []), p)[1][1], + -223.7406308892161, + atol = 1e-6, + ) + @test isapprox( + Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt, []), [3.14])[1][1], + -256.88284936919246, + atol = 1e-6, + ) + @test isapprox( + Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt, []), [3.15])[1][1], + 256.36101155595964, + atol = 1e-6, + ) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Optimization - OMIB; H" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + #GET GROUND TRUTH DATA + execute!(sim, Rodas4(); abstol = 1e-9, reltol = 1e-9, dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Shaft, :H)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + ) + execute!(sim, Rodas4()) + + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + Rodas4(), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + #H_values = [] + #loss_values = [] + function callback(u, l) + #push!(H_values, u.u[1]) + #push!(loss_values, l) + return false + end + optfun = OptimizationFunction{false}( + (u, p) -> f_forward(u, [s_change], δ_gt, []); + grad = (res, u, p) -> res .= f_grad(u, [s_change], δ_gt, []), + ) + optprob = OptimizationProblem{false}(optfun, [3.14]) + sol = Optimization.solve( + optprob, + OptimizationOptimisers.Adam(0.002); + callback = callback, + maxiters = 3, + ) + @test isapprox(sol.u[1], 3.144000187737475, atol = 1e-6) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Gradients - OMIB; Xd_p" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + + #GET GROUND TRUTH DATA + execute!(sim, Rodas4(); abstol = 1e-9, reltol = 1e-9, dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Machine, :Xd_p)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + ) + execute!(sim, Rodas4()) + + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + [("generator-102-1", :Machine, :Xd_p)], + [("generator-102-1", :δ)], + Rodas4(), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + loss_zero = f_forward(p, [s_change], δ_gt, []) + loss_non_zero_1 = f_forward(p * 1.01, [s_change], δ_gt, []) + loss_non_zero_2 = f_forward(p * 0.99, [s_change], δ_gt, []) + @test isapprox(loss_zero, 0.0, atol = 1e-9) + @test loss_non_zero_1 != 0.0 + @test loss_non_zero_2 != 0.0 + grad_zero = f_grad(p, [s_change], δ_gt, []) + grad_nonzero_1 = f_grad(p * 1.01, [s_change], δ_gt, []) + grad_nonzero_2 = f_grad(p * 0.99, [s_change], δ_gt, []) + @test isapprox(grad_zero[1], 499.3490613579809, atol = 1e-3) + @test isapprox(grad_nonzero_1[1], 500.17141744869343, atol = 1e-3) + @test isapprox(grad_nonzero_2[1], -498.73349996053315; atol = 1e-3) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Gradients - OMIB; Xd_p - force no init" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + + #GET GROUND TRUTH DATA + execute!(sim, Rodas4(); abstol = 1e-9, reltol = 1e-9, dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Machine, :Xd_p)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + ) + execute!(sim, Rodas4()) + + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + [("generator-102-1", :Machine, :Xd_p)], + [("generator-102-1", :δ)], + Rodas4(), + f_loss, + false, + PSID.INITIALIZED; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-9, + reltol = 1e-9, + dtmax = 0.005, + saveat = 0.005, + ) + loss_zero = f_forward(p, [s_change], δ_gt, []) + loss_non_zero_1 = f_forward(p * 1.01, [s_change], δ_gt, []) + loss_non_zero_2 = f_forward(p * 0.99, [s_change], δ_gt, []) + @test isapprox(loss_zero, 0.0, atol = 1e-9) + @test loss_non_zero_1 != 0.0 + @test loss_non_zero_2 != 0.0 + grad_zero = f_grad(p, [s_change], δ_gt, []) + grad_nonzero_1 = f_grad(p * 1.01, [s_change], δ_gt, []) + grad_nonzero_2 = f_grad(p * 0.99, [s_change], δ_gt, []) + @test isapprox(grad_zero[1], 496.9588401248536, atol = 1e-3) + @test isapprox(grad_nonzero_1[1], 496.450046454591, atol = 1e-3) + @test isapprox(grad_nonzero_2[1], -498.19053389100594; atol = 1e-3) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +function transform_power_load_to_constant_impedance(x::PowerLoad) + l = StandardLoad(; + name = get_name(x), + available = get_available(x), + bus = get_bus(x), + base_power = get_base_power(x), + constant_active_power = 0.0, + constant_reactive_power = 0.0, + impedance_active_power = get_active_power(x), + impedance_reactive_power = get_reactive_power(x), + current_active_power = 0.0, + current_reactive_power = 0.0, + max_constant_active_power = 0.0, + max_constant_reactive_power = 0.0, + max_impedance_active_power = get_max_active_power(x), + max_impedance_reactive_power = get_max_reactive_power(x), + max_current_active_power = 0.0, + max_current_reactive_power = 0.0, + services = get_services(x), + dynamic_injector = get_dynamic_injector(x), + ) + return l +end + +@testset "Test Gradients - 9 bus; Xd_p" begin + path = mktempdir() + try + sys = build_system(PSIDTestSystems, "psid_test_ieee_9bus") + for l in get_components(PowerLoad, sys) + l_new = transform_power_load_to_constant_impedance(l) + remove_component!(sys, l) + add_component!(sys, l_new) + end + dyn_gen = get_component(DynamicGenerator, sys, "generator-3-1") + get_machine(dyn_gen) + + p_ctrl = ControlReferenceChange(1.0, dyn_gen, :P_ref, 0.5) + + sim = Simulation!( + MassMatrixModel, + sys, + path, + (0.0, 5.0), + p_ctrl, + ) + + #GET GROUND TRUTH DATA + execute!(sim, Rodas4(); abstol = 1e-6, reltol = 1e-6, dtmax = 0.05, saveat = 0.05) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-3-1", :δ)) + + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-3-1", :Machine, :Xd_p)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + sys, + path, + (0.0, 5.0), + ) + execute!(sim, Rodas4()) + + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + [("generator-3-1", :Machine, :Xd_p)], + [("generator-3-1", :δ)], + Rodas4(), + f_loss; + sensealg = InterpolatingAdjoint(; autojacvec = ReverseDiffVJP()), + abstol = 1e-6, + reltol = 1e-6, + dtmax = 0.05, + saveat = 0.05, + ) + loss_zero = f_forward(p, [p_ctrl], δ_gt, []) + loss_non_zero_1 = f_forward(p * 1.01, [p_ctrl], δ_gt, []) + loss_non_zero_2 = f_forward(p * 0.99, [p_ctrl], δ_gt, []) + @test isapprox(loss_zero, 0.0, atol = 2e-9) + @test loss_non_zero_1 != 0.0 + @test loss_non_zero_2 != 0.0 + grad_zero = f_grad(p, [p_ctrl], δ_gt, []) + grad_nonzero_1 = f_grad(p * 1.01, [p_ctrl], δ_gt, []) + grad_nonzero_2 = f_grad(p * 0.99, [p_ctrl], δ_gt, []) + @test isapprox(grad_zero[1], 0.8876855633591412, atol = 1e-6) #should pass --> once we convert outside of the test. + @test isapprox(grad_nonzero_1[1], 0.5946989856464833, atol = 1.0) + @test isapprox(grad_nonzero_2[1], -0.9432527319604077; atol = 1.0) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +#NOTE: "Only the discretize-then-optimize methods are applicable to delay differential equations." +function add_degov_to_omib!(omib_sys) + gen = get_component(ThermalStandard, omib_sys, "generator-102-1") + dyn_gen = get_component(DynamicGenerator, omib_sys, "generator-102-1") + new_gov = PSY.DEGOV(; + T1 = 1.0, + T2 = 0.5, + T3 = 0.0, + K = 18.0, + T4 = 12.0, + T5 = 5.0, + T6 = 0.2, + Td = 0.5, + P_ref = 0.0, + ) + dyn_gen_new = DynamicGenerator(; + name = get_name(dyn_gen), + ω_ref = get_ω_ref(dyn_gen), + machine = get_machine(dyn_gen), + shaft = get_shaft(dyn_gen), + avr = get_avr(dyn_gen), + prime_mover = new_gov, + pss = get_pss(dyn_gen), + base_power = get_base_power(dyn_gen), + ) + remove_component!(omib_sys, dyn_gen) + add_component!(omib_sys, dyn_gen_new, gen) +end + +@testset "Test Gradients - OMIB; H; Delays" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + add_degov_to_omib!(omib_sys) + sim = Simulation!( + MassMatrixModel, + omib_sys, + pwd(), + (0.0, 5.0), + s_change, + ) + + #GET GROUND TRUTH DATA + execute!( + sim, + MethodOfSteps(Rodas5()); + abstol = 1e-6, + reltol = 1e-6, + saveat = 0.05, + ) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Shaft, :H)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + pwd(), + (0.0, 5.0), + ) + execute!(sim, MethodOfSteps(Rodas4())) + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad, _ = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + MethodOfSteps(Rodas5()), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-6, + reltol = 1e-6, + saveat = 0.05, + ) + loss_zero = f_forward(p, [s_change], δ_gt, []) + loss_non_zero_1 = f_forward([5.2], [s_change], δ_gt, []) + loss_non_zero_2 = f_forward(p, [s_change], δ_gt .* 2, []) + @test isapprox(loss_zero, 0.0, atol = 1e-6) + @test isapprox(loss_non_zero_1, 0.266806977990823, atol = 1e-6) + @test isapprox(loss_non_zero_2, 17.41277928345796, atol = 1e-6) + grad_zero = f_grad(p, [s_change], δ_gt, []) + @test isapprox(grad_zero[1], -0.034893046793070925, atol = 1e-9) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +#Test delays + reversediffAdjoint. +@testset "Test Gradients - OMIB; H; PerturbState" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + s_device = get_component(Source, omib_sys, "InfBus") + p_state = PerturbState(1.0, 5, 0.18) + add_degov_to_omib!(omib_sys) + + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + p_state, + ) + #GET GROUND TRUTH DATA + execute!(sim, MethodOfSteps(Rodas5()); abstol = 1e-6, reltol = 1e-6, dtmax = 0.05, saveat = 0.05) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ); unique_timestamps = true) #Avoid filtering of repeated timesteps + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Shaft, :H)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + ) + execute!(sim, MethodOfSteps(Rodas5())) + f_forward, f_grad, f_zygote_forward = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + MethodOfSteps(Rodas5(autodiff=false)), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-6, + reltol = 1e-6, + dtmax = 0.05, + saveat = 0.05, + ) + @test f_forward(p, [p_state], δ_gt, []) == + f_zygote_forward(p, [p_state], δ_gt, []) + @test f_forward([3.14], [p_state], δ_gt, []) == + f_zygote_forward([3.14], [p_state], δ_gt, []) + @test f_forward([3.15], [p_state], δ_gt, []) == + f_zygote_forward([3.15], [p_state], δ_gt, []) + #@test f_grad(p, [p_state], δ_gt) == + # Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt), p)[1] + @test Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt, []), p)[1][1] == -10.336102683050685 + + _, _, f_zygote_forward = get_sensitivity_functions( + sim, + [("generator-102-1", :Shaft, :H)], + [("generator-102-1", :δ)], + MethodOfSteps(Rodas5(autodiff=false)), + f_loss; + sensealg = ReverseDiffAdjoint(), + abstol = 1e-6, + reltol = 1e-6, + dtmax = 0.05, + saveat = 0.05, + ) + + + @test isapprox( + Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt, []), p)[1][1], + -15.973318599159727, + atol = 1e-6, + ) + @test isapprox( + Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt, []), [3.14])[1][1], + -25.735942518785052, + atol = 1e-6, + ) + @test isapprox( + Zygote.gradient(p -> f_zygote_forward(p, [p_state], δ_gt, []), [3.15])[1][1], + 25.684384617470563, + atol = 1e-6, + ) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Gradients - OMIB; Xd_p; delays" begin + path = mktempdir() + try + omib_sys = build_system(PSIDTestSystems, "psid_test_omib") + add_degov_to_omib!(omib_sys) + s_device = get_component(Source, omib_sys, "InfBus") + s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + + #GET GROUND TRUTH DATA + execute!( + sim, + MethodOfSteps(Rodas5()); + abstol = 1e-9, + reltol = 1e-9, + saveat = 0.005, + ) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + + #GET PARAMETER VALUES + p = get_parameter_values(sim, [("generator-102-1", :Machine, :Xd_p)]) + + function plot_traces(δ, δ_gt) + display(plot([scatter(; y = δ_gt), scatter(; y = δ)])) + end + EnzymeRules.inactive(::typeof(plot_traces), args...) = nothing + function f_loss(p, states, δ_gt, aux) + #plot_traces(states[1], δ_gt) + return sum(abs.(states[1] - δ_gt)) + end + + #GET SENSITIVITY FUNCTIONS + f_forward, f_grad = get_sensitivity_functions( + sim, + [("generator-102-1", :Machine, :Xd_p)], + [("generator-102-1", :δ)], + MethodOfSteps(Rodas5()), + f_loss; + sensealg = ForwardDiffSensitivity(), + abstol = 1e-6, + reltol = 1e-6, + saveat = 0.005, + ) + + loss_zero = f_forward(p, [s_change], δ_gt, []) + loss_non_zero_1 = f_forward(p * 1.01, [s_change], δ_gt, []) + loss_non_zero_2 = f_forward(p * 0.99, [s_change], δ_gt, []) + @test isapprox(loss_zero, 0.0, atol = 0.02) + @test isapprox(loss_non_zero_1, 1.4914986021363859, atol = 1e-6) + @test isapprox(loss_non_zero_2, 1.489669782933298, atol = 1e-6) + grad_zero = f_grad(p, [s_change], δ_gt, []) + grad_nonzero_1 = f_grad(p * 1.01, [s_change], δ_gt, []) + grad_nonzero_2 = f_grad(p * 0.99, [s_change], δ_gt, []) + @test isapprox(grad_zero[1], -5.337145710251008, atol = 1e-6) + @test isapprox(grad_nonzero_1[1], 500.0118950662703, atol = 1e-6) + @test isapprox(grad_nonzero_2[1], -498.56135720277706; atol = 1e-6) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end diff --git a/test/test_case_sensitivity.jl b/test/test_case_sensitivity.jl new file mode 100644 index 000000000..1c163b2f5 --- /dev/null +++ b/test/test_case_sensitivity.jl @@ -0,0 +1,413 @@ +#Replaced by test_case_enzyme; this is the old version for Zygote +#= +""" +Case 1: +This case study defines a classical machine against an infinite bus. Sensitivitiy +Analysis is performed for the inertia of the generator. The fault is a change in the +source voltage. This test also checks that incorrect user data is handled correctly. +""" +################################################## +############### LOAD DATA ###################### ## +################################################## + +omib_sys = build_system(PSIDTestSystems, "psid_test_omib") +ieee_9bus_sys = build_system(PSIDTestSystems, "psid_test_ieee_9bus") +andes_11bus_sys = build_system(PSIDSystems, "psid_11bus_andes") +################################################## +############### SOLVE PROBLEM #################### +################################################## + +s_device = get_component(Source, omib_sys, "InfBus") +s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) +using PlotlyJS + +#NOTES ON SENSITIVITY ALGORITHMS FROM SCIMLSENSITIVITY +#ReverseDiffVJP and EnzymeVJP only options compatible with Hybrid DEs (DEs with callbacks) +#BacksolveAdjoint prone to instabilities whenever the Lipschitz constant is sufficiently large (stiff equations, PDE discretizations, and many other contexts) +#For ForwardDiffSensitivity, convert_tspan=true is needed for hybrid equations. +@testset "Test Gradients - Mass Matrix no delays" begin + path = mktempdir() + try + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + H_gt = + get_H(get_shaft(get_component(DynamicGenerator, omib_sys, "generator-102-1"))) + execute!(sim, Rodas5(); dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + for solver in [FBDF(), Rodas5(), QNDF()] + for tol in [1e-6, 1e-9] + function f(sim, δ_gt) + execute!( + sim, + solver; + sensealg = ForwardDiffSensitivity(), + abstol = tol, + reltol = tol, + dtmax = 0.005, + saveat = 0.005, + ) + res = read_results(sim) + t, δ = get_state_series(res, ("generator-102-1", :δ)) + #display(plot(scatter(x=t, y = δ))) + return sum(abs.(δ - δ_gt)) + end + g = PSID.get_parameter_sensitivity_function!( + sim, + [("generator-102-1", :Shaft, :H)], + f, + ) + #p = PSID.get_parameter_sensitivity_values(sim, [("generator-102-1", SingleMass, :H)]) + #@error Zygote.gradient(g, [3.15])[1][1] + @test isapprox( + Zygote.gradient((p) -> g(p, δ_gt), [3.14])[1][1], + -8.0, + atol = 1.0, + ) + @test isapprox( + Zygote.gradient((p) -> g(p, δ_gt), [3.15])[1][1], + 8.0, + atol = 1.0, + ) + end + end + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Parameter Optimization Problem - no delays" begin + path = mktempdir() + try + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + H_gt = + get_H(get_shaft(get_component(DynamicGenerator, omib_sys, "generator-102-1"))) + execute!(sim, Rodas5(; autodiff = true); dtmax = 0.005, saveat = 0.005) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + + function f(sim, δ_gt) + execute!( + sim, + FBDF(; autodiff = true); + sensealg = ForwardDiffSensitivity(), + dtmax = 0.005, + saveat = 0.005, + ) + res = read_results(sim) + t, δ = get_state_series(res, ("generator-102-1", :δ)) + #display(plot(scatter(x=t, y = δ))) + return sum(abs.(δ - δ_gt)) + end + g = PSID.get_parameter_sensitivity_function!( + sim, + [("generator-102-1", :Shaft, :H)], + f, + ) + p = PSID.get_parameter_sensitivity_values( + sim, + [("generator-102-1", :Shaft, :H)], + ) + #H_values = [] + #loss_values = [] + function callback(u, l) + #push!(H_values, u.u[1]) + #push!(loss_values, l) + return false + end + optfun = OptimizationFunction((u, _) -> g(u, δ_gt), Optimization.AutoZygote()) + optprob = OptimizationProblem(optfun, [3.14]) + sol = Optimization.solve( + optprob, + OptimizationOptimisers.Adam(0.002); + callback = callback, + maxiters = 3, + ) + @test isapprox(sol.u[1], 3.144000187737475, atol = 1e-6) + #display(plot(scatter(y=H_values))) + #display(plot(scatter(y=loss_values))) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Gradients - Mass Matrix with delays" begin + path = mktempdir() + try + gen = get_component(ThermalStandard, omib_sys, "generator-102-1") + dyn_gen = get_component(DynamicGenerator, omib_sys, "generator-102-1") + new_gov = PSY.DEGOV(; + T1 = 0.0, + T2 = 0.0, + T3 = 0.0, + K = 18.0, + T4 = 12.0, + T5 = 5.0, + T6 = 0.2, + Td = 0.5, + P_ref = 0.0, + ) + dyn_gen_new = DynamicGenerator(; + name = get_name(dyn_gen), + ω_ref = get_ω_ref(dyn_gen), + machine = get_machine(dyn_gen), + shaft = get_shaft(dyn_gen), + avr = get_avr(dyn_gen), + prime_mover = new_gov, + pss = get_pss(dyn_gen), + base_power = get_base_power(dyn_gen), + ) + remove_component!(omib_sys, dyn_gen) + add_component!(omib_sys, dyn_gen_new, gen) + + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + H_gt = + get_H(get_shaft(get_component(DynamicGenerator, omib_sys, "generator-102-1"))) + execute!( + sim, + MethodOfSteps(Rodas5(; autodiff = false)); + dtmax = 0.005, + saveat = 0.005, + ) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + for solver in [ + MethodOfSteps(Rodas5(; autodiff = true)), + MethodOfSteps(QNDF(; autodiff = true)), + ] + for tol in [1e-6] + function f(sim, δ_gt) + execute!( + sim, + solver; + sensealg = ForwardDiffSensitivity(), + abstol = tol, + reltol = tol, + dtmax = 0.005, + saveat = 0.005, + ) + res = read_results(sim) + t, δ = get_state_series(res, ("generator-102-1", :δ)) + #display(plot(scatter(x=t, y = δ))) + return sum(abs.(δ - δ_gt)) + end + g = PSID.get_parameter_sensitivity_function!( + sim, + [("generator-102-1", :Shaft, :H)], + f, + ) + #p = PSID.get_parameter_sensitivity_values(sim, [("generator-102-1", SingleMass, :H)]) + #display(Zygote.gradient(g, [3.14])) + @test isapprox( + Zygote.gradient((p) -> g(p, δ_gt), [3.14])[1][1], + -10.0, + atol = 1.0, + ) + @test isapprox( + Zygote.gradient((p) -> g(p, δ_gt), [3.15])[1][1], + 10.0, + atol = 1.0, + ) + end + end + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +@testset "Test Parameter Optimization Problem - with delays" begin + path = mktempdir() + try + gen = get_component(ThermalStandard, omib_sys, "generator-102-1") + dyn_gen = get_component(DynamicGenerator, omib_sys, "generator-102-1") + new_gov = PSY.DEGOV(; + T1 = 0.0, + T2 = 0.0, + T3 = 0.0, + K = 18.0, + T4 = 12.0, + T5 = 5.0, + T6 = 0.2, + Td = 0.5, + P_ref = 0.0, + ) + dyn_gen_new = DynamicGenerator(; + name = get_name(dyn_gen), + ω_ref = get_ω_ref(dyn_gen), + machine = get_machine(dyn_gen), + shaft = get_shaft(dyn_gen), + avr = get_avr(dyn_gen), + prime_mover = new_gov, + pss = get_pss(dyn_gen), + base_power = get_base_power(dyn_gen), + ) + remove_component!(omib_sys, dyn_gen) + add_component!(omib_sys, dyn_gen_new, gen) + sim = Simulation!( + MassMatrixModel, + omib_sys, + path, + (0.0, 5.0), + s_change, + ) + H_gt = + get_H(get_shaft(get_component(DynamicGenerator, omib_sys, "generator-102-1"))) + execute!( + sim, + MethodOfSteps(Rodas5(; autodiff = true)); + dtmax = 0.005, + saveat = 0.005, + ) + res = read_results(sim) + t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + + function f(sim, δ_gt) + execute!( + sim, + MethodOfSteps(Rodas5(; autodiff = true)); + sensealg = ForwardDiffSensitivity(), + abstol = 1e-6, + reltol = 1e-6, + dtmax = 0.005, + saveat = 0.005, + ) + res = read_results(sim) + t, δ = get_state_series(res, ("generator-102-1", :δ)) + #display(plot(scatter(x=t, y = δ))) + return sum(abs.(δ - δ_gt)) + end + g = PSID.get_parameter_sensitivity_function!( + sim, + [("generator-102-1", :Shaft, :H)], + f, + ) + p = PSID.get_parameter_sensitivity_values( + sim, + [("generator-102-1", :Shaft, :H)], + ) + #H_values = [] + #loss_values = [] + function callback(u, l) + #push!(H_values, u.u[1]) + #push!(loss_values, l) + return false + end + optfun = OptimizationFunction((u, _) -> g(u, δ_gt), Optimization.AutoZygote()) + optprob = OptimizationProblem(optfun, [3.14]) + sol = Optimization.solve( + optprob, + OptimizationOptimisers.Adam(0.002); + callback = callback, + maxiters = 3, + ) + @test isapprox(sol.u[1], 3.144001551579776, atol = 1e-6) + #display(plot(scatter(y=H_values))) + #display(plot(scatter(y=loss_values))) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +andes_11bus_sys = build_system(PSIDSystems, "psid_11bus_andes") +@testset "Test Gradients - Initialization" begin + path = mktempdir() + try + + #s_device = get_component(Source, omib_sys, "InfBus") + #s_change = SourceBusVoltageChange(1.0, s_device, :V_ref, 1.02) + # TODO - get an appropraite (valid) fault + sim = Simulation!( + MassMatrixModel, + andes_11bus_sys, + path, + (0.0, 5.0), + #s_change, TODO - new fault for this system + ) + #H_gt = + # get_H(get_shaft(get_component(DynamicGenerator, omib_sys, "generator-102-1"))) + #execute!(sim, Rodas5(); dtmax = 0.005, saveat = 0.005) + #res = read_results(sim) + #t, δ_gt = get_state_series(res, ("generator-102-1", :δ)) + function f(sim, data) + execute!( + sim, + Rodas5(); + sensealg = ForwardDiffSensitivity(), + abstol = 1e-6, + reltol = 1e-6, + dtmax = 0.005, + saveat = 0.005, + ) + res = read_results(sim) + t, δ = get_state_series(res, ("generator-2-1", :δ)) + display(plot(scatter(; x = t, y = δ))) + return sum(abs.(δ)) + end + g = PSID.get_parameter_sensitivity_function!( + sim, + [("generator-2-1", :Machine, :R)], + f, + ) + println(f(sim, nothing)) #TODO - to get here, we need to convert a few more models to NonlinearSolve.jl + @assert false + display(g) #Is returning nothing because initialization is explicitly not allowed: Change this and try to back propagate through eventually. + p = PSID.get_parameter_sensitivity_values(sim, [("generator-2-1", :Machine, :R)]) + println(p) + @error g(p, nothing) + #@error Zygote.gradient((x) -> g(x, nothing), p) + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end + +#Requires more models to be re-written: Check surrogate first. +#= @testset "Test unsupported options" begin + path = mktempdir() + try + sim = Simulation!( + MassMatrixModel, + ieee_9bus_sys, + path, + (0.0, 5.0), + ) + invalid_options = [ + ("Bus 7-Bus 5-i_3", Line, :x), #not a wrapped component + ("generator-1-1", :Machine, :R), #part of initialization + ("generator-1-1", :Machine, :XXX), #incorrect symbol + ] + for option in invalid_options + g = PSID.get_parameter_sensitivity_function!( + sim, + [option], + (sim) -> 0.0, + ) + @test g === nothing + end + finally + @info("removing test files") + rm(path; force = true, recursive = true) + end +end =# + =# diff --git a/test/test_case_source_bus_voltage_change.jl b/test/test_case_source_bus_voltage_change.jl index 82ca453cf..81d97ff3d 100644 --- a/test/test_case_source_bus_voltage_change.jl +++ b/test/test_case_source_bus_voltage_change.jl @@ -1,7 +1,7 @@ """ Case 27: This case study a three bus system with 1 machine (One d- One q-: 4th order model), a VSM of 19 states and an infinite source. -The test changes botht he voltage magnitude and phase angle of the source bus. +The test changes both the voltage magnitude and phase angle of the source bus. """ ################################################## diff --git a/test/test_sundials.jl b/test/test_sundials.jl index f0b582190..7764a2fdc 100644 --- a/test/test_sundials.jl +++ b/test/test_sundials.jl @@ -23,7 +23,7 @@ solver_list = [ # :BCG, # :PCG, # :TFQMR, - :KLU, + # :KLU, ] #time span diff --git a/test/utils/get_results.jl b/test/utils/get_results.jl index b49352e82..864ee1323 100644 --- a/test/utils/get_results.jl +++ b/test/utils/get_results.jl @@ -8,8 +8,8 @@ function get_init_values_for_comparison(sim::Simulation) for bus in PSY.get_components(PSY.Bus, system) bus_n = PSY.get_number(bus) bus_ix = PSID.get_lookup(sim.inputs)[bus_n] - V_R[bus_ix] = sim.x0_init[bus_ix] - V_I[bus_ix] = sim.x0_init[bus_ix + bus_size] + V_R[bus_ix] = sim.x0[bus_ix] + V_I[bus_ix] = sim.x0[bus_ix + bus_size] Vm[bus_ix] = sqrt(V_R[bus_ix]^2 + V_I[bus_ix]^2) θ[bus_ix] = angle(V_R[bus_ix] + V_I[bus_ix] * 1im) end @@ -21,7 +21,7 @@ function get_init_values_for_comparison(sim::Simulation) global_index = PSID.get_global_index(device) x0_device = Vector{Float64}(undef, length(states)) for (i, s) in enumerate(states) - x0_device[i] = sim.x0_init[global_index[s]] + x0_device[i] = sim.x0[global_index[s]] end results[name] = x0_device end @@ -31,7 +31,7 @@ function get_init_values_for_comparison(sim::Simulation) global_index = PSID.get_global_index(br) x0_br = Vector{Float64}(undef, length(states)) for (i, s) in enumerate(states) - x0_br[i] = sim.x0_init[global_index[s]] + x0_br[i] = sim.x0[global_index[s]] end printed_name = "Line " * name results[printed_name] = x0_br diff --git a/test/utils/mock_structs.jl b/test/utils/mock_structs.jl index 435fceb46..e26217d5f 100644 --- a/test/utils/mock_structs.jl +++ b/test/utils/mock_structs.jl @@ -8,6 +8,6 @@ function MockIntegrator(inputs) return MockIntegrator( zeros(PSID.get_variable_count(inputs)), zeros(PSID.get_variable_count(inputs)), - inputs, + PSID.get_parameters(inputs), ) end