diff --git a/src/PowerSimulationsDynamics.jl b/src/PowerSimulationsDynamics.jl index b87c0b8b6..690fce642 100644 --- a/src/PowerSimulationsDynamics.jl +++ b/src/PowerSimulationsDynamics.jl @@ -141,6 +141,7 @@ include("models/inverter_models/frequency_estimator_models.jl") include("models/inverter_models/outer_control_models.jl") include("models/inverter_models/inner_control_models.jl") include("models/inverter_models/converter_models.jl") +include("models/inverter_models/output_current_limiter_models.jl") #Injection Models include("models/load_models.jl") diff --git a/src/base/device_wrapper.jl b/src/base/device_wrapper.jl index b504b10e7..469fd6801 100644 --- a/src/base/device_wrapper.jl +++ b/src/base/device_wrapper.jl @@ -102,6 +102,9 @@ function state_port_mappings( component_state_mapping = Dict{Int, Vector{Int}}() input_port_mapping = Dict{Int, Vector{Int}}() for c in PSY.get_dynamic_components(dynamic_device) + if c isa PSY.OutputCurrentLimiter + continue + end ix = index(typeof(c)) component_state_mapping[ix] = _index_local_states(c, device_states) input_port_mapping[ix] = _index_port_mapping!(c, device_states) @@ -228,8 +231,8 @@ 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(device) + # IS.@assert_op PSY.get_R_th(dynamic_device) == PSY.get_R_th(device) return DynamicWrapper( dynamic_device, @@ -331,6 +334,8 @@ PSY.get_freq_estimator(wrapper::DynamicWrapper{T}) where {T <: PSY.DynamicInvert wrapper.device.freq_estimator PSY.get_filter(wrapper::DynamicWrapper{T}) where {T <: PSY.DynamicInverter} = wrapper.device.filter +PSY.get_limiter(wrapper::DynamicWrapper{T}) where {T <: PSY.DynamicInverter} = + PSY.get_limiter(wrapper.device) # PSY overloads of specific Dynamic Injectors diff --git a/src/initialization/inverter_components/init_DCside.jl b/src/initialization/inverter_components/init_DCside.jl index 95fe06f56..a148e3c90 100644 --- a/src/initialization/inverter_components/init_DCside.jl +++ b/src/initialization/inverter_components/init_DCside.jl @@ -11,7 +11,7 @@ function initialize_DCside!( IC <: PSY.InnerControl, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Update inner_vars diff --git a/src/initialization/inverter_components/init_converter.jl b/src/initialization/inverter_components/init_converter.jl index e4538b3a6..1667a38b2 100644 --- a/src/initialization/inverter_components/init_converter.jl +++ b/src/initialization/inverter_components/init_converter.jl @@ -11,7 +11,7 @@ function initialize_converter!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } end function initialize_converter!( @@ -27,7 +27,7 @@ function initialize_converter!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Get inner vars V_R = inner_vars[Vr_cnv_var] @@ -82,7 +82,7 @@ function initialize_converter!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Get inner vars Vr_filter = inner_vars[Vr_filter_var] diff --git a/src/initialization/inverter_components/init_filter.jl b/src/initialization/inverter_components/init_filter.jl index c59e391c7..77b7be634 100644 --- a/src/initialization/inverter_components/init_filter.jl +++ b/src/initialization/inverter_components/init_filter.jl @@ -9,7 +9,7 @@ function initialize_filter!( IC <: PSY.InnerControl, DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #PowerFlow Data P0 = PSY.get_active_power(static) @@ -101,7 +101,7 @@ function initialize_filter!( IC <: PSY.InnerControl, DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #PowerFlow Data P0 = PSY.get_active_power(static) diff --git a/src/initialization/inverter_components/init_frequency_estimator.jl b/src/initialization/inverter_components/init_frequency_estimator.jl index 3901e7905..b48347556 100644 --- a/src/initialization/inverter_components/init_frequency_estimator.jl +++ b/src/initialization/inverter_components/init_frequency_estimator.jl @@ -9,7 +9,7 @@ function initialize_frequency_estimator!( IC <: PSY.InnerControl, DC <: PSY.DCSource, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } Vr_filter = inner_vars[Vr_filter_var] Vi_filter = inner_vars[Vi_filter_var] @@ -76,7 +76,7 @@ function initialize_frequency_estimator!( IC <: PSY.InnerControl, DC <: PSY.DCSource, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } Vr_filter = inner_vars[Vr_filter_var] Vi_filter = inner_vars[Vi_filter_var] @@ -139,7 +139,7 @@ function initialize_frequency_estimator!( IC <: PSY.InnerControl, DC <: PSY.DCSource, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Get parameters pll_control = PSY.get_freq_estimator(dynamic_device) diff --git a/src/initialization/inverter_components/init_inner.jl b/src/initialization/inverter_components/init_inner.jl index 5a4184a6e..5b64741d8 100644 --- a/src/initialization/inverter_components/init_inner.jl +++ b/src/initialization/inverter_components/init_inner.jl @@ -11,7 +11,7 @@ function initialize_inner!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -156,7 +156,7 @@ function initialize_inner!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -242,7 +242,7 @@ function initialize_inner!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } # Obtain inner variables for component Vr_filter = inner_vars[Vr_filter_var] diff --git a/src/initialization/inverter_components/init_outer.jl b/src/initialization/inverter_components/init_outer.jl index 33f2163e3..1c5d49d0e 100644 --- a/src/initialization/inverter_components/init_outer.jl +++ b/src/initialization/inverter_components/init_outer.jl @@ -19,7 +19,7 @@ function initialize_outer!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -86,7 +86,7 @@ function initialize_outer!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -152,7 +152,7 @@ function initialize_outer!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -217,7 +217,7 @@ function initialize_outer!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -301,7 +301,7 @@ function initialize_outer!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } # Read inner vars Vr_filter = inner_vars[Vr_filter_var] diff --git a/src/models/device.jl b/src/models/device.jl index bc64c60c0..5e5a1c078 100644 --- a/src/models/device.jl +++ b/src/models/device.jl @@ -464,7 +464,7 @@ function _update_inner_vars!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } return end @@ -490,7 +490,7 @@ function _update_inner_vars!( IC <: PSY.InnerControl, DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } V_R = inner_vars[Vr_inv_var] V_I = inner_vars[Vi_inv_var] @@ -611,7 +611,7 @@ function _update_inner_vars!( IC <: PSY.InnerControl, DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } filter_ix = get_local_state_ix(dynamic_device, PSY.LCLFilter) filter_states = @view device_states[filter_ix] diff --git a/src/models/inverter_models/DCside_models.jl b/src/models/inverter_models/DCside_models.jl index aaf60125c..2d73aa008 100644 --- a/src/models/inverter_models/DCside_models.jl +++ b/src/models/inverter_models/DCside_models.jl @@ -22,7 +22,7 @@ function mdl_DCside_ode!( IC <: PSY.InnerControl, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Update inner_vars diff --git a/src/models/inverter_models/converter_models.jl b/src/models/inverter_models/converter_models.jl index ecde91846..c45831c94 100644 --- a/src/models/inverter_models/converter_models.jl +++ b/src/models/inverter_models/converter_models.jl @@ -21,7 +21,7 @@ function mdl_converter_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain inner variables for component @@ -61,7 +61,7 @@ function mdl_converter_ode!( IC <: PSY.InnerControl, DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain inner variables for component V_R = inner_vars[Vr_filter_var] @@ -196,7 +196,7 @@ function mdl_converter_ode!( IC <: PSY.InnerControl, DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain inner variables for component V_R = inner_vars[Vr_filter_var] diff --git a/src/models/inverter_models/filter_models.jl b/src/models/inverter_models/filter_models.jl index 93f3f5593..1d034c480 100644 --- a/src/models/inverter_models/filter_models.jl +++ b/src/models/inverter_models/filter_models.jl @@ -48,7 +48,7 @@ function mdl_filter_ode!( IC <: PSY.InnerControl, DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #external_ix = get_input_port_ix(dynamic_device, PSY.LCLFilter) @@ -132,7 +132,7 @@ function mdl_filter_ode!( IC <: PSY.InnerControl, DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain inner variables for component basepower = PSY.get_base_power(dynamic_device) diff --git a/src/models/inverter_models/frequency_estimator_models.jl b/src/models/inverter_models/frequency_estimator_models.jl index d08995730..358ef813e 100644 --- a/src/models/inverter_models/frequency_estimator_models.jl +++ b/src/models/inverter_models/frequency_estimator_models.jl @@ -20,7 +20,7 @@ function mdl_freq_estimator_ode!( IC <: PSY.InnerControl, DC <: PSY.DCSource, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -88,7 +88,7 @@ function mdl_freq_estimator_ode!( IC <: PSY.InnerControl, DC <: PSY.DCSource, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -151,7 +151,7 @@ function mdl_freq_estimator_ode!( IC <: PSY.InnerControl, DC <: PSY.DCSource, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Get parameters diff --git a/src/models/inverter_models/inner_control_models.jl b/src/models/inverter_models/inner_control_models.jl index b9583cbf8..a0f66bdee 100644 --- a/src/models/inverter_models/inner_control_models.jl +++ b/src/models/inverter_models/inner_control_models.jl @@ -45,7 +45,7 @@ function _mdl_ode_RE_inner_controller_B!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain inner variables for component V_t = sqrt(inner_vars[Vr_filter_var]^2 + inner_vars[Vi_filter_var]^2) @@ -102,7 +102,7 @@ function _mdl_ode_RE_inner_controller_B!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain inner variables for component V_t = sqrt(inner_vars[Vr_filter_var]^2 + inner_vars[Vi_filter_var]^2) @@ -165,7 +165,7 @@ function mdl_inner_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -262,6 +262,476 @@ function mdl_inner_ode!( return end +function mdl_inner_ode!( + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, + dynamic_device::DynamicWrapper{ + PSY.DynamicInverter{C, O, PSY.VoltageModeControl, DC, P, F, PSY.InstantaneousOutputCurrentLimiter}, + }, +) where { + C <: PSY.Converter, + O <: PSY.OuterControl, + DC <: PSY.DCSource, + P <: PSY.FrequencyEstimator, + F <: PSY.Filter, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, +} + + #Obtain external states inputs for component + external_ix = get_input_port_ix(dynamic_device, PSY.VoltageModeControl) + Ir_filter = device_states[external_ix[1]] + Ii_filter = device_states[external_ix[2]] + Ir_cnv = device_states[external_ix[3]] + Ii_cnv = device_states[external_ix[4]] + Vr_filter = device_states[external_ix[5]] + Vi_filter = device_states[external_ix[6]] + + #Obtain inner variables for component + ω_oc = inner_vars[ω_oc_var] + θ_oc = inner_vars[θ_oc_var] + 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) + + #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) + + #Obtain indices for component w/r to device + local_ix = get_local_state_ix(dynamic_device, PSY.VoltageModeControl) + + #Define internal states for frequency estimator + internal_states = @view device_states[local_ix] + ξ_d = internal_states[1] + ξ_q = internal_states[2] + γ_d = internal_states[3] + γ_q = internal_states[4] + ϕ_d = internal_states[5] + ϕ_q = internal_states[6] + + #Transformations to dq frame + I_dq_filter = ri_dq(θ_oc + pi / 2) * [Ir_filter; Ii_filter] + I_dq_cnv = ri_dq(θ_oc + pi / 2) * [Ir_cnv; Ii_cnv] + V_dq_filter = ri_dq(θ_oc + pi / 2) * [Vr_filter; Vi_filter] + + ### Compute 6 states ODEs (D'Arco EPSR122 Model) ### + ## SRF Voltage Control w/ Virtual Impedance ## + #Virtual Impedance + Vd_filter_ref = (v_refr - rv * I_dq_filter[d] + ω_oc * lv * I_dq_filter[q]) + Vq_filter_ref = (-rv * I_dq_filter[q] - ω_oc * lv * I_dq_filter[d]) + + #Voltage Control PI Blocks + Id_pi, dξd_dt = pi_block(Vd_filter_ref - V_dq_filter[d], ξ_d, kpv, kiv) + Iq_pi, dξq_dt = pi_block(Vq_filter_ref - V_dq_filter[q], ξ_q, kpv, kiv) + #PI Integrator (internal state) + output_ode[local_ix[1]] = dξd_dt + output_ode[local_ix[2]] = dξq_dt + + #Compensate output Control Signal - Links to SRF Current Controller + Id_cnv_ref = Id_pi - cf * ω_oc * V_dq_filter[q] + kffi * I_dq_filter[d] + Iq_cnv_ref = Iq_pi + cf * ω_oc * V_dq_filter[d] + kffi * I_dq_filter[q] + + #Get limiter and apply output current limiting + limiter = PSY.get_limiter(dynamic_device) + Id_cnv_ref2, Iq_cnv_ref2 = limit_output_current(limiter, Id_cnv_ref, Iq_cnv_ref) + + ## SRF Current Control ## + #Current Control PI Blocks + Vd_pi, dγd_dt = pi_block(Id_cnv_ref2 - I_dq_cnv[d], γ_d, kpc, kic) + Vq_pi, dγq_dt = pi_block(Iq_cnv_ref2 - I_dq_cnv[q], γ_q, kpc, kic) + #PI Integrator (internal state) + output_ode[local_ix[3]] = dγd_dt + output_ode[local_ix[4]] = dγq_dt + + #Compensate References for Converter Output Voltage + Vd_cnv_ref = + Vd_pi - ω_oc * lf * I_dq_cnv[q] + kffv * V_dq_filter[d] - + kad * (V_dq_filter[d] - ϕ_d) + Vq_cnv_ref = + Vq_pi + ω_oc * lf * I_dq_cnv[d] + kffv * V_dq_filter[q] - + kad * (V_dq_filter[q] - ϕ_q) + + #Active Damping LPF (internal state) + output_ode[local_ix[5]] = low_pass(V_dq_filter[d], ϕ_d, 1.0, 1.0 / ωad)[2] + output_ode[local_ix[6]] = low_pass(V_dq_filter[q], ϕ_q, 1.0, 1.0 / ωad)[2] + + #Update inner_vars + #Modulation Commands to Converter + inner_vars[md_var] = Vd_cnv_ref / Vdc + inner_vars[mq_var] = Vq_cnv_ref / Vdc + return +end + +function mdl_inner_ode!( + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, + dynamic_device::DynamicWrapper{ + PSY.DynamicInverter{C, O, PSY.VoltageModeControl, DC, P, F, PSY.MagnitudeOutputCurrentLimiter}, + }, +) where { + C <: PSY.Converter, + O <: PSY.OuterControl, + DC <: PSY.DCSource, + P <: PSY.FrequencyEstimator, + F <: PSY.Filter, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, +} + + #Obtain external states inputs for component + external_ix = get_input_port_ix(dynamic_device, PSY.VoltageModeControl) + Ir_filter = device_states[external_ix[1]] + Ii_filter = device_states[external_ix[2]] + Ir_cnv = device_states[external_ix[3]] + Ii_cnv = device_states[external_ix[4]] + Vr_filter = device_states[external_ix[5]] + Vi_filter = device_states[external_ix[6]] + + #Obtain inner variables for component + ω_oc = inner_vars[ω_oc_var] + θ_oc = inner_vars[θ_oc_var] + 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) + + #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) + + #Obtain indices for component w/r to device + local_ix = get_local_state_ix(dynamic_device, PSY.VoltageModeControl) + + #Define internal states for frequency estimator + internal_states = @view device_states[local_ix] + ξ_d = internal_states[1] + ξ_q = internal_states[2] + γ_d = internal_states[3] + γ_q = internal_states[4] + ϕ_d = internal_states[5] + ϕ_q = internal_states[6] + + #Transformations to dq frame + I_dq_filter = ri_dq(θ_oc + pi / 2) * [Ir_filter; Ii_filter] + I_dq_cnv = ri_dq(θ_oc + pi / 2) * [Ir_cnv; Ii_cnv] + V_dq_filter = ri_dq(θ_oc + pi / 2) * [Vr_filter; Vi_filter] + + ### Compute 6 states ODEs (D'Arco EPSR122 Model) ### + ## SRF Voltage Control w/ Virtual Impedance ## + #Virtual Impedance + Vd_filter_ref = (v_refr - rv * I_dq_filter[d] + ω_oc * lv * I_dq_filter[q]) + Vq_filter_ref = (-rv * I_dq_filter[q] - ω_oc * lv * I_dq_filter[d]) + + #Voltage Control PI Blocks + Id_pi, dξd_dt = pi_block(Vd_filter_ref - V_dq_filter[d], ξ_d, kpv, kiv) + Iq_pi, dξq_dt = pi_block(Vq_filter_ref - V_dq_filter[q], ξ_q, kpv, kiv) + #PI Integrator (internal state) + output_ode[local_ix[1]] = dξd_dt + output_ode[local_ix[2]] = dξq_dt + + #Compensate output Control Signal - Links to SRF Current Controller + Id_cnv_ref = Id_pi - cf * ω_oc * V_dq_filter[q] + kffi * I_dq_filter[d] + Iq_cnv_ref = Iq_pi + cf * ω_oc * V_dq_filter[d] + kffi * I_dq_filter[q] + + #Get limiter and apply output current limiting + limiter = PSY.get_limiter(dynamic_device) + Id_cnv_ref2, Iq_cnv_ref2 = limit_output_current(limiter, Id_cnv_ref, Iq_cnv_ref) + + ## SRF Current Control ## + #Current Control PI Blocks + Vd_pi, dγd_dt = pi_block(Id_cnv_ref2 - I_dq_cnv[d], γ_d, kpc, kic) + Vq_pi, dγq_dt = pi_block(Iq_cnv_ref2 - I_dq_cnv[q], γ_q, kpc, kic) + #PI Integrator (internal state) + output_ode[local_ix[3]] = dγd_dt + output_ode[local_ix[4]] = dγq_dt + + #Compensate References for Converter Output Voltage + Vd_cnv_ref = + Vd_pi - ω_oc * lf * I_dq_cnv[q] + kffv * V_dq_filter[d] - + kad * (V_dq_filter[d] - ϕ_d) + Vq_cnv_ref = + Vq_pi + ω_oc * lf * I_dq_cnv[d] + kffv * V_dq_filter[q] - + kad * (V_dq_filter[q] - ϕ_q) + + #Active Damping LPF (internal state) + output_ode[local_ix[5]] = low_pass(V_dq_filter[d], ϕ_d, 1.0, 1.0 / ωad)[2] + output_ode[local_ix[6]] = low_pass(V_dq_filter[q], ϕ_q, 1.0, 1.0 / ωad)[2] + + #Update inner_vars + #Modulation Commands to Converter + inner_vars[md_var] = Vd_cnv_ref / Vdc + inner_vars[mq_var] = Vq_cnv_ref / Vdc + return +end + +function mdl_inner_ode!( + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, + dynamic_device::DynamicWrapper{ + PSY.DynamicInverter{C, O, PSY.VoltageModeControl, DC, P, F, PSY.SaturationOutputCurrentLimiter}, + }, +) where { + C <: PSY.Converter, + O <: PSY.OuterControl, + DC <: PSY.DCSource, + P <: PSY.FrequencyEstimator, + F <: PSY.Filter, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, +} + + #Obtain external states inputs for component + external_ix = get_input_port_ix(dynamic_device, PSY.VoltageModeControl) + Ir_filter = device_states[external_ix[1]] + Ii_filter = device_states[external_ix[2]] + Ir_cnv = device_states[external_ix[3]] + Ii_cnv = device_states[external_ix[4]] + Vr_filter = device_states[external_ix[5]] + Vi_filter = device_states[external_ix[6]] + + #Obtain inner variables for component + ω_oc = inner_vars[ω_oc_var] + θ_oc = inner_vars[θ_oc_var] + 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) + + #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) + + #Obtain indices for component w/r to device + local_ix = get_local_state_ix(dynamic_device, PSY.VoltageModeControl) + + #Define internal states for frequency estimator + internal_states = @view device_states[local_ix] + ξ_d = internal_states[1] + ξ_q = internal_states[2] + γ_d = internal_states[3] + γ_q = internal_states[4] + ϕ_d = internal_states[5] + ϕ_q = internal_states[6] + + #Transformations to dq frame + I_dq_filter = ri_dq(θ_oc + pi / 2) * [Ir_filter; Ii_filter] + I_dq_cnv = ri_dq(θ_oc + pi / 2) * [Ir_cnv; Ii_cnv] + V_dq_filter = ri_dq(θ_oc + pi / 2) * [Vr_filter; Vi_filter] + + ### Compute 6 states ODEs (D'Arco EPSR122 Model) ### + ## SRF Voltage Control w/ Virtual Impedance ## + #Virtual Impedance + Vd_filter_ref = (v_refr - rv * I_dq_filter[d] + ω_oc * lv * I_dq_filter[q]) + Vq_filter_ref = (-rv * I_dq_filter[q] - ω_oc * lv * I_dq_filter[d]) + + #Voltage Control PI Blocks + Prop_d = kpv * (Vd_filter_ref - V_dq_filter[d]) + Prop_q = kpv * (Vq_filter_ref - V_dq_filter[q]) + Integral_d = ξ_d + Integral_q = ξ_q + + #Compensate output Control Signal - Links to SRF Current Controller + Id_cnv_ref = Prop_d + (kiv * Integral_d) - cf * ω_oc * V_dq_filter[q] + kffi * I_dq_filter[d] + Iq_cnv_ref = Prop_q + (kiv * Integral_q) + cf * ω_oc * V_dq_filter[d] + kffi * I_dq_filter[q] + + #Get limiter and apply output current limiting + limiter = PSY.get_limiter(dynamic_device) + Id_cnv_ref2, Iq_cnv_ref2, Del_Vv_d, Del_Vv_q = limit_output_current(limiter, Id_cnv_ref, Iq_cnv_ref) + + # Limiter anti- windup + dξd_dt = (Vd_filter_ref - V_dq_filter[d]) - Del_Vv_d + dξq_dt = (Vq_filter_ref - V_dq_filter[q]) - Del_Vv_q + + #PI Integrator (internal state) + output_ode[local_ix[1]] = dξd_dt + output_ode[local_ix[2]] = dξq_dt + + ## SRF Current Control ## + #Current Control PI Blocks + Vd_pi, dγd_dt = pi_block(Id_cnv_ref2 - I_dq_cnv[d], γ_d, kpc, kic) + Vq_pi, dγq_dt = pi_block(Iq_cnv_ref2 - I_dq_cnv[q], γ_q, kpc, kic) + #PI Integrator (internal state) + output_ode[local_ix[3]] = dγd_dt + output_ode[local_ix[4]] = dγq_dt + + #Compensate References for Converter Output Voltage + Vd_cnv_ref = + Vd_pi - ω_oc * lf * I_dq_cnv[q] + kffv * V_dq_filter[d] - + kad * (V_dq_filter[d] - ϕ_d) + Vq_cnv_ref = + Vq_pi + ω_oc * lf * I_dq_cnv[d] + kffv * V_dq_filter[q] - + kad * (V_dq_filter[q] - ϕ_q) + + #Active Damping LPF (internal state) + output_ode[local_ix[5]] = low_pass(V_dq_filter[d], ϕ_d, 1.0, 1.0 / ωad)[2] + output_ode[local_ix[6]] = low_pass(V_dq_filter[q], ϕ_q, 1.0, 1.0 / ωad)[2] + + #Update inner_vars + #Modulation Commands to Converter + inner_vars[md_var] = Vd_cnv_ref / Vdc + inner_vars[mq_var] = Vq_cnv_ref / Vdc + return +end + +function mdl_inner_ode!( + device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, + output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, + inner_vars::AbstractArray{<:ACCEPTED_REAL_TYPES}, + dynamic_device::DynamicWrapper{ + PSY.DynamicInverter{C, O, PSY.VoltageModeControl, DC, P, F, PSY.HybridOutputCurrentLimiter}, + }, +) where { + C <: PSY.Converter, + O <: PSY.OuterControl, + DC <: PSY.DCSource, + P <: PSY.FrequencyEstimator, + F <: PSY.Filter, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, +} + + #Obtain external states inputs for component + external_ix = get_input_port_ix(dynamic_device, PSY.VoltageModeControl) + Ir_filter = device_states[external_ix[1]] + Ii_filter = device_states[external_ix[2]] + Ir_cnv = device_states[external_ix[3]] + Ii_cnv = device_states[external_ix[4]] + Vr_filter = device_states[external_ix[5]] + Vi_filter = device_states[external_ix[6]] + + #Obtain inner variables for component + ω_oc = inner_vars[ω_oc_var] + θ_oc = inner_vars[θ_oc_var] + 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) + + #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) + + #Obtain indices for component w/r to device + local_ix = get_local_state_ix(dynamic_device, PSY.VoltageModeControl) + + #Define internal states for frequency estimator + internal_states = @view device_states[local_ix] + ξ_d = internal_states[1] + ξ_q = internal_states[2] + γ_d = internal_states[3] + γ_q = internal_states[4] + ϕ_d = internal_states[5] + ϕ_q = internal_states[6] + + #Transformations to dq frame + I_dq_filter = ri_dq(θ_oc + pi / 2) * [Ir_filter; Ii_filter] + I_dq_cnv = ri_dq(θ_oc + pi / 2) * [Ir_cnv; Ii_cnv] + V_dq_filter = ri_dq(θ_oc + pi / 2) * [Vr_filter; Vi_filter] + + ### Compute 6 states ODEs (D'Arco EPSR122 Model) ### + ## SRF Voltage Control w/ Virtual Impedance ## + #Virtual Impedance + Vd_filter_ref = (v_refr - rv * I_dq_filter[d] + ω_oc * lv * I_dq_filter[q]) + Vq_filter_ref = (-rv * I_dq_filter[q] - ω_oc * lv * I_dq_filter[d]) + + #Voltage Control PI Blocks + Prop_d = kpv * (Vd_filter_ref - V_dq_filter[d]) + Prop_q = kpv * (Vq_filter_ref - V_dq_filter[q]) + Integral_d = ξ_d + Integral_q = ξ_q + + #Compensate output Control Signal - Links to SRF Current Controller + Id_cnv_ref = Prop_d + (kiv * Integral_d) - cf * ω_oc * V_dq_filter[q] + kffi * I_dq_filter[d] + Iq_cnv_ref = Prop_q + (kiv * Integral_q) + cf * ω_oc * V_dq_filter[d] + kffi * I_dq_filter[q] + + #Get limiter and apply output current limiting + limiter = PSY.get_limiter(dynamic_device) + Id_cnv_ref2, Iq_cnv_ref2, Del_Vv_d, Del_Vv_q = limit_output_current(limiter, Id_cnv_ref, Iq_cnv_ref, ω_oc) + + # Limiter anti- windup + dξd_dt = (Vd_filter_ref - V_dq_filter[d]) - Del_Vv_d + dξq_dt = (Vq_filter_ref - V_dq_filter[q]) - Del_Vv_q + + #PI Integrator (internal state) + output_ode[local_ix[1]] = dξd_dt + output_ode[local_ix[2]] = dξq_dt + + ## SRF Current Control ## + #Current Control PI Blocks + Vd_pi, dγd_dt = pi_block(Id_cnv_ref2 - I_dq_cnv[d], γ_d, kpc, kic) + Vq_pi, dγq_dt = pi_block(Iq_cnv_ref2 - I_dq_cnv[q], γ_q, kpc, kic) + #PI Integrator (internal state) + output_ode[local_ix[3]] = dγd_dt + output_ode[local_ix[4]] = dγq_dt + + #Compensate References for Converter Output Voltage + Vd_cnv_ref = + Vd_pi - ω_oc * lf * I_dq_cnv[q] + kffv * V_dq_filter[d] - + kad * (V_dq_filter[d] - ϕ_d) + Vq_cnv_ref = + Vq_pi + ω_oc * lf * I_dq_cnv[d] + kffv * V_dq_filter[q] - + kad * (V_dq_filter[q] - ϕ_q) + + #Active Damping LPF (internal state) + output_ode[local_ix[5]] = low_pass(V_dq_filter[d], ϕ_d, 1.0, 1.0 / ωad)[2] + output_ode[local_ix[6]] = low_pass(V_dq_filter[q], ϕ_q, 1.0, 1.0 / ωad)[2] + + #Update inner_vars + #Modulation Commands to Converter + inner_vars[md_var] = Vd_cnv_ref / Vdc + inner_vars[mq_var] = Vq_cnv_ref / Vdc + return +end + function mdl_inner_ode!( device_states::AbstractArray{<:ACCEPTED_REAL_TYPES}, output_ode::AbstractArray{<:ACCEPTED_REAL_TYPES}, @@ -277,7 +747,7 @@ function mdl_inner_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -351,7 +821,7 @@ function mdl_inner_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Get Current Controller parameters inner_control = PSY.get_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 f7ad6d8e8..316d97ba7 100644 --- a/src/models/inverter_models/outer_control_models.jl +++ b/src/models/inverter_models/outer_control_models.jl @@ -42,7 +42,7 @@ function _mdl_ode_RE_active_controller_AB!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters @@ -134,7 +134,7 @@ function _mdl_ode_RE_active_controller_AB!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters @@ -194,7 +194,7 @@ function _mdl_ode_RE_reactive_controller_AB!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters @@ -278,7 +278,7 @@ function _mdl_ode_RE_reactive_controller_AB!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters q_ref = get_Q_ref(dynamic_device) @@ -355,7 +355,7 @@ function _mdl_ode_RE_reactive_controller_AB!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters V_ref = get_V_ref(dynamic_device) @@ -463,7 +463,7 @@ function _mdl_ode_RE_reactive_controller_AB!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external parameters V_ref = get_V_ref(dynamic_device) @@ -556,7 +556,7 @@ function mdl_outer_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -648,7 +648,7 @@ function mdl_outer_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -742,7 +742,7 @@ function mdl_outer_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -838,7 +838,7 @@ function mdl_outer_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component @@ -936,7 +936,7 @@ function mdl_outer_ode!( DC <: PSY.DCSource, P <: PSY.FrequencyEstimator, F <: PSY.Filter, - L <: Union{Nothing, PSY.InverterLimiter}, + L <: Union{Nothing, PSY.OutputCurrentLimiter}, } #Obtain external states inputs for component diff --git a/src/models/inverter_models/output_current_limiter_models.jl b/src/models/inverter_models/output_current_limiter_models.jl new file mode 100644 index 000000000..0d97dbe6f --- /dev/null +++ b/src/models/inverter_models/output_current_limiter_models.jl @@ -0,0 +1,57 @@ +function limit_output_current(limiter :: Nothing, Id_cnv_ref :: Union{Float64, ForwardDiff.Dual}, Iq_cnv_ref :: Union{Float64, ForwardDiff.Dual}) + return Id_cnv_ref, Iq_cnv_ref +end + +function limit_output_current(limiter:: PSY.InstantaneousOutputCurrentLimiter, Id_cnv_ref :: Union{Float64, ForwardDiff.Dual}, Iq_cnv_ref :: Union{Float64, ForwardDiff.Dual}) + d_lim = PSY.get_Id_max(limiter) + q_lim = PSY.get_Iq_max(limiter) + Id_cnv_ref2 = clamp(Id_cnv_ref, -d_lim, d_lim) + Iq_cnv_ref2 = clamp(Iq_cnv_ref, -q_lim, q_lim) + return Id_cnv_ref2, Iq_cnv_ref2 +end + +function limit_output_current(limiter :: PSY.MagnitudeOutputCurrentLimiter, Id_cnv_ref :: Union{Float64, ForwardDiff.Dual}, Iq_cnv_ref :: Union{Float64, ForwardDiff.Dual}) + limit_value = PSY.get_I_max(limiter) + theta = atan(Iq_cnv_ref, Id_cnv_ref) + if (Id_cnv_ref^2 + Iq_cnv_ref^2)^(1/2) > limit_value + Id_cnv_ref2 = limit_value*cos(theta) + Iq_cnv_ref2 = limit_value*sin(theta) + else + Id_cnv_ref2 = Id_cnv_ref + Iq_cnv_ref2 = Iq_cnv_ref + end + return Id_cnv_ref2, Iq_cnv_ref2 +end + +function limit_output_current(limiter :: PSY.SaturationOutputCurrentLimiter, Id_cnv_ref :: Union{Float64, ForwardDiff.Dual}, Iq_cnv_ref :: Union{Float64, ForwardDiff.Dual}) + limit_value = PSY.get_I_max(limiter) + gain = PSY.get_kw(limiter) + theta = atan(Iq_cnv_ref, Id_cnv_ref) + if (Id_cnv_ref^2 + Iq_cnv_ref^2)^(1/2) > limit_value + Id_cnv_ref2 = limit_value*cos(theta) + Iq_cnv_ref2 = limit_value*sin(theta) + else + Id_cnv_ref2 = Id_cnv_ref + Iq_cnv_ref2 = Iq_cnv_ref + end + Del_Vv_d = gain * (Id_cnv_ref - Id_cnv_ref2) + Del_Vv_q = gain * (Iq_cnv_ref - Iq_cnv_ref2) + return Id_cnv_ref2, Iq_cnv_ref2, Del_Vv_d, Del_Vv_q +end + +function limit_output_current(limiter :: PSY.HybridOutputCurrentLimiter, Id_cnv_ref :: Union{Float64, ForwardDiff.Dual}, Iq_cnv_ref :: Union{Float64, ForwardDiff.Dual}, ω :: Union{Float64, ForwardDiff.Dual}) + limit_value = PSY.get_I_max(limiter) + real_imped = PSY.get_rv(limiter) + imag_imped = PSY.get_lv(limiter) + theta = atan(Iq_cnv_ref, Id_cnv_ref) + if (Id_cnv_ref^2 + Iq_cnv_ref^2)^(1/2) > limit_value + Id_cnv_ref2 = limit_value*cos(theta) + Iq_cnv_ref2 = limit_value*sin(theta) + else + Id_cnv_ref2 = Id_cnv_ref + Iq_cnv_ref2 = Iq_cnv_ref + end + Del_Vv_d = ω * imag_imped * (Iq_cnv_ref2 - Iq_cnv_ref) + real_imped * (Id_cnv_ref - Id_cnv_ref2) + Del_Vv_q = ω * imag_imped * (Id_cnv_ref - Id_cnv_ref2) + real_imped * (Iq_cnv_ref - Iq_cnv_ref2) + return Id_cnv_ref2, Iq_cnv_ref2, Del_Vv_d, Del_Vv_q +end \ No newline at end of file