Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ST8C, DEGOV1 and TGSimple models. Also, remove Windows PR Testing temporarily #390

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion .github/workflows/main-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
matrix:
julia-version: ['1', 'nightly']
julia-arch: [x64]
os: [ubuntu-latest, windows-latest]
os: [ubuntu-latest, macOS-latest]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr_testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
matrix:
julia-version: ['1']
julia-arch: [x64]
os: [ubuntu-latest, windows-latest, macOS-latest]
os: [ubuntu-latest, macOS-latest]

steps:
- uses: actions/checkout@v2
Expand Down
113 changes: 113 additions & 0 deletions src/initialization/generator_components/init_avr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -652,3 +652,116 @@ function initialize_avr!(

return
end

function initialize_avr!(
device_states,
static::PSY.StaticInjection,
dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.ST8C, TG, P}},
inner_vars::AbstractVector,
) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS}
#Obtain Vf0 solved from Machine
Vf0 = inner_vars[Vf_var]
#Obtain Ifd limiter
Ifd = inner_vars[Xad_Ifd_var] # read Lad Ifd (field current times Lad)
#Obtain measured terminal voltage
Vt0 = sqrt(inner_vars[VR_gen_var]^2 + inner_vars[VI_gen_var]^2)

#Get parameters
avr = PSY.get_avr(dynamic_device)
SW1_Flag = PSY.get_SW1_Flag(avr)
if SW1_Flag == 1
error("Source from generator terminal voltage not supported.")
end
K_pr = PSY.get_K_pr(avr)
K_ir = PSY.get_K_ir(avr)
Vpi_min, Vpi_max = PSY.get_Vpi_lim(avr)
K_pa = PSY.get_K_pa(avr)
K_ia = PSY.get_K_ia(avr)
Va_min, Va_max = PSY.get_Va_lim(avr)
K_a = PSY.get_K_a(avr)
T_a = PSY.get_T_a(avr)
Vr_min, Vr_max = PSY.get_Vr_lim(avr)
K_f = PSY.get_K_f(avr)
T_f = PSY.get_T_f(avr)
K_c1 = PSY.get_K_c1(avr)
K_p = PSY.get_K_p(avr)
K_i2 = PSY.get_K_i2(avr)

if K_i2 != 0.0
error("Feedforward Current for AVR ST8C not implemented yet.")
end
if K_ia == 0.0
error(
"Integrator gain cannot be zero for AVR ST8C of $(PSY.get_name(dynamic_device))",
)
end
#To solve V_ref, Vr
function f!(out, x)
V_ref = x[1]
Vm = x[2] # Sensed Voltage
x_a1 = x[3] # Regulator Integrator
x_a2 = x[4] # Field Regulator
x_a3 = x[5] # Controller Integrator
x_a4 = x[6] # Regulator Feedback

#TODO: Implement Terminal Current FF for AVR
V_b2 = 0.0
#TODO: Implement Voltage Compensation if needed
V_e = K_p

# Compute block derivatives
Vs = 0.0
V_pi_in = V_ref + Vs - Vm
Ifd_ref, dxa1_dt = pi_block_nonwindup(V_pi_in, x_a1, K_pr, K_ir, Vpi_min, Vpi_max)
Ifd_diff = Ifd_ref - x_a4
pi_out, dxa2_dt = pi_block_nonwindup(Ifd_diff, x_a2, K_pa, K_ia, Va_min, Va_max)
_, dxa3_dt = low_pass_nonwindup_mass_matrix(pi_out, x_a3, K_a, T_a, Vr_min, Vr_max)
_, dxa4_dt = low_pass_mass_matrix(Ifd, x_a4, K_f, T_f)

# Compute V_b1
I_n1 = K_c1 * Ifd / V_e
F_ex = rectifier_function(I_n1)
V_b1 = F_ex * V_e

Efd = V_b1 * x_a3 + V_b2

#V_ll output first block
out[1] = Efd - Vf0 # we are asking for Vf0
out[2] = Vm - Vt0 # Low Pass Sensor
out[3] = dxa1_dt
out[4] = dxa2_dt
out[5] = dxa3_dt
out[6] = dxa4_dt
end
# Initial Guess
V_e0 = K_p
I_n10 = K_c1 * Ifd / V_e0
F_ex0 = rectifier_function(I_n10)
V_b10 = F_ex0 * V_e0
x_a30 = Vf0 / V_b10
x_a20 = x_a30 / (K_a * K_ia)
x0 = [Vt0, Vt0, K_f * Ifd / K_ir, x_a20, x_a30, Ifd * K_f]
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
Vreg = sol_x0[6]
if (Vreg > Vr_max) || (Vreg < Vr_min)
@error(
"Regulator Voltage V_R = $(Vreg) outside the limits for AVR of $(PSY.get_name(dynamic_device)). Consider updating its limits."
)
end
#Update V_ref
PSY.set_V_ref!(avr, sol_x0[1])
set_V_ref(dynamic_device, sol_x0[1])
#Update AVR states
avr_ix = get_local_state_ix(dynamic_device, PSY.ST8C)
avr_states = @view device_states[avr_ix]
avr_states[1] = sol_x0[2]
avr_states[2] = sol_x0[3]
avr_states[3] = sol_x0[4]
avr_states[4] = sol_x0[5]
avr_states[5] = sol_x0[6]
end
end
53 changes: 53 additions & 0 deletions src/initialization/generator_components/init_tg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,26 @@ function initialize_tg!(
return
end

function initialize_tg!(
device_states,
static::PSY.StaticInjection,
dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.TGSimple, P}},
inner_vars::AbstractVector,
) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS}

#Get mechanical torque to SyncMach
τm0 = inner_vars[τm_var]
#Set Parameters
tg = PSY.get_prime_mover(dynamic_device)

PSY.set_P_ref!(tg, τm0)
set_P_ref(dynamic_device, τm0)
tg_ix = get_local_state_ix(dynamic_device, PSY.TGSimple)
tg_states = @view device_states[tg_ix]
tg_states[1] = τm0
return
end

function initialize_tg!(
device_states,
static::PSY.StaticInjection,
Expand Down Expand Up @@ -190,6 +210,39 @@ function initialize_tg!(
return
end

function initialize_tg!(
device_states,
static::PSY.StaticInjection,
dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, A, PSY.DEGOV1, P}},
inner_vars::AbstractVector,
) where {M <: PSY.Machine, S <: PSY.Shaft, A <: PSY.AVR, P <: PSY.PSS}
tg = PSY.get_prime_mover(dynamic_device)
droop_flag = PSY.get_droop_flag(tg)
R = PSY.get_R(tg)
#Get mechanical torque to SyncMach
τm0 = inner_vars[τm_var]
τe0 = inner_vars[τe_var]
if droop_flag == 0
PSY.set_P_ref!(tg, τm0 * R)
set_P_ref(dynamic_device, τm0 * R)
else
PSY.set_P_ref!(tg, τe0 * R)
set_P_ref(dynamic_device, τe0 * R)
end
#Update states
tg_ix = get_local_state_ix(dynamic_device, typeof(tg))
tg_states = @view device_states[tg_ix]
tg_states[1] = 0.0
tg_states[2] = 0.0
tg_states[3] = 0.0
tg_states[4] = 0.0
tg_states[5] = τm0
if droop_flag == 1
tg_states[6] = τe0
end
return
end

function initialize_tg!(
device_states,
::PSY.StaticInjection,
Expand Down
97 changes: 97 additions & 0 deletions src/models/generator_models/avr_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ function mass_matrix_avr_entries!(
return
end

function mass_matrix_avr_entries!(
mass_matrix,
avr::PSY.ST8C,
global_index::Base.ImmutableDict{Symbol, Int64},
)
mass_matrix[global_index[:Vm], global_index[:Vm]] = PSY.get_Tr(avr)
mass_matrix[global_index[:x_a3], global_index[:x_a3]] = PSY.get_T_a(avr)
mass_matrix[global_index[:x_a4], global_index[:x_a4]] = PSY.get_T_f(avr)
return
end

##################################
##### Differential Equations #####
##################################
Expand Down Expand Up @@ -724,3 +735,89 @@ function mdl_avr_ode!(
inner_vars[Vf_var] = E_fd
return
end

function mdl_avr_ode!(
device_states::AbstractArray,
output_ode::AbstractArray,
inner_vars::AbstractArray,
dynamic_device::DynamicWrapper{PSY.DynamicGenerator{M, S, PSY.ST8C, TG, P}},
h,
t,
) where {M <: PSY.Machine, S <: PSY.Shaft, TG <: PSY.TurbineGov, P <: PSY.PSS}

#Obtain references
V_ref = get_V_ref(dynamic_device)

#Obtain indices for component w/r to device
local_ix = get_local_state_ix(dynamic_device, PSY.ST8C)

#Define inner states for component
internal_states = @view device_states[local_ix]
Vm = internal_states[1] # Sensed Voltage
x_a1 = internal_states[2] # Regulator Integrator
x_a2 = internal_states[3] # Field Regulator
x_a3 = internal_states[4] # Controller Integrator
x_a4 = internal_states[5] # Regulator Feedback

#Define external states for device
Vt = sqrt(inner_vars[VR_gen_var]^2 + inner_vars[VI_gen_var]^2) # machine's terminal voltage
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)
SW1_Flag = PSY.get_SW1_Flag(avr)
if SW1_Flag == 1
error("Source from generator terminal voltage not supported.")
end
Tr = PSY.get_Tr(avr)
K_pr = PSY.get_K_pr(avr)
K_ir = PSY.get_K_ir(avr)
Vpi_min, Vpi_max = PSY.get_Vpi_lim(avr)
K_pa = PSY.get_K_pa(avr)
K_ia = PSY.get_K_ia(avr)
Va_min, Va_max = PSY.get_Va_lim(avr)
K_a = PSY.get_K_a(avr)
T_a = PSY.get_T_a(avr)
Vr_min, Vr_max = PSY.get_Vr_lim(avr)
K_f = PSY.get_K_f(avr)
T_f = PSY.get_T_f(avr)
K_c1 = PSY.get_K_c1(avr)
K_p = PSY.get_K_p(avr)
K_i2 = PSY.get_K_i2(avr)
VB1_max = PSY.get_VB1_max(avr)

if K_i2 != 0.0
error("Feedforward Current for AVR ST8C not implemented yet.")
end
#TODO: Implement Terminal Current FF for AVR
V_b2 = 0.0
#TODO: Implement Voltage Compensation if needed
V_e = K_p

# Compute block derivatives
_, dVm_dt = low_pass_mass_matrix(Vt, Vm, 1.0, Tr)
V_pi_in = V_ref + Vs - Vm
Ifd_ref, dxa1_dt = pi_block_nonwindup(V_pi_in, x_a1, K_pr, K_ir, Vpi_min, Vpi_max)
Ifd_diff = Ifd_ref - x_a4
pi_out, dxa2_dt = pi_block_nonwindup(Ifd_diff, x_a2, K_pa, K_ia, Va_min, Va_max)
_, dxa3_dt = low_pass_nonwindup_mass_matrix(pi_out, x_a3, K_a, T_a, Vr_min, Vr_max)
_, dxa4_dt = low_pass_mass_matrix(Ifd, x_a4, K_f, T_f)

# Compute V_b1
I_n1 = K_c1 * Ifd / V_e
F_ex = rectifier_function(I_n1)
V_b1 = min(F_ex * V_e, VB1_max)

#Compute 5 States AVR ODE:
output_ode[local_ix[1]] = dVm_dt
output_ode[local_ix[2]] = dxa1_dt
output_ode[local_ix[3]] = dxa2_dt
output_ode[local_ix[4]] = dxa3_dt
output_ode[local_ix[5]] = dxa4_dt

#Update inner_vars
Efd = V_b1 * x_a3 + V_b2
inner_vars[Vf_var] = Efd
return
end
Loading
Loading