Skip to content
3 changes: 3 additions & 0 deletions src/PowerSystems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,9 @@ export get_time_series_array
export get_time_series_resolutions
export supports_time_series
export supports_supplemental_attributes
export supports_active_power
export supports_reactive_power
export supports_voltage_control
export get_time_series_timestamps
export get_time_series_values
export get_time_series_counts
Expand Down
15 changes: 15 additions & 0 deletions src/models/static_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ function get_services(device::Device)
return Vector{Service}()
end

"""
Return `true` if the device has active power as a controllable parameter.
"""
supports_active_power(::StaticInjection) = true

"""
Return `true` if the device has reactive power as a controllable parameter.
"""
supports_reactive_power(::StaticInjection) = true

"""
Return `true` if the device can control voltage at its connected bus.
"""
supports_voltage_control(::StaticInjection) = false

get_dynamic_injector(d::StaticInjection) = nothing

function get_frequency_droop(static_injector::StaticInjection)
Expand Down
36 changes: 36 additions & 0 deletions src/models/supplemental_accessors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,39 @@ end
function supports_services(::AreaInterchange)
return true
end

# supports_active_power overrides for types without controllable active power
supports_active_power(::SynchronousCondenser) = false
supports_active_power(::FixedAdmittance) = false
supports_active_power(::SwitchedAdmittance) = false

# supports_reactive_power overrides for types without controllable reactive power
supports_reactive_power(::InterconnectingConverter) = false
supports_reactive_power(::FixedAdmittance) = false

# FACTSControlDevice reactive power and voltage control depend on control_mode.
# control_mode is nothing for uninitialized devices (e.g. FACTSControlDevice(nothing)).
_facts_is_active(d::FACTSControlDevice) =
(mode = get_control_mode(d); !isnothing(mode) && mode != FACTSOperationModes.OOS)

# In NML mode both Series and Shunt links operate, enabling active power control.
# In BYP mode the Series link is bypassed and the Shunt acts as a STATCOM (reactive only).
function supports_active_power(d::FACTSControlDevice)
mode = get_control_mode(d)
return !isnothing(mode) && mode == FACTSOperationModes.NML
end

supports_reactive_power(d::FACTSControlDevice) = _facts_is_active(d)

# supports_voltage_control overrides for types that can control voltage
supports_voltage_control(::Generator) = true
supports_voltage_control(::Source) = true
supports_voltage_control(::Storage) = true
supports_voltage_control(::StaticInjectionSubsystem) = true

supports_voltage_control(d::FACTSControlDevice) = _facts_is_active(d)

function supports_voltage_control(d::SynchronousCondenser)
bustype = get_bustype(get_bus(d))
return bustype ∈ (ACBusTypes.PV, ACBusTypes.REF, ACBusTypes.SLACK)
end
106 changes: 106 additions & 0 deletions test/test_devices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,109 @@ end
get_component(ShiftablePowerLoad, sys2, "ShiftableLoadBus4"),
).max == 0.10
end

@testset "Test static injection traits" begin
# supports_active_power
@test supports_active_power(ThermalStandard(nothing)) == true
@test supports_active_power(ThermalMultiStart(nothing)) == true
@test supports_active_power(RenewableDispatch(nothing)) == true
@test supports_active_power(RenewableNonDispatch(nothing)) == true
@test supports_active_power(HydroDispatch(nothing)) == true
@test supports_active_power(HydroTurbine(nothing)) == true
@test supports_active_power(HydroPumpTurbine(nothing)) == true
@test supports_active_power(Source(nothing)) == true
@test supports_active_power(InterconnectingConverter(nothing)) == true
@test supports_active_power(EnergyReservoirStorage(nothing)) == true
@test supports_active_power(PowerLoad(nothing)) == true
@test supports_active_power(StandardLoad(nothing)) == true
@test supports_active_power(ExponentialLoad(nothing)) == true
@test supports_active_power(InterruptiblePowerLoad(nothing)) == true
@test supports_active_power(ShiftablePowerLoad(nothing)) == true
@test supports_active_power(HybridSystem(nothing)) == true
@test supports_active_power(SynchronousCondenser(nothing)) == false
@test supports_active_power(FixedAdmittance(nothing)) == false
@test supports_active_power(SwitchedAdmittance(nothing)) == false

# FACTSControlDevice active power depends on control_mode (true only for NML)
@test supports_active_power(FACTSControlDevice(nothing)) == false
facts_nml = FACTSControlDevice(nothing)
set_control_mode!(facts_nml, FACTSOperationModes.NML)
@test supports_active_power(facts_nml) == true
facts_byp = FACTSControlDevice(nothing)
set_control_mode!(facts_byp, FACTSOperationModes.BYP)
@test supports_active_power(facts_byp) == false
facts_oos = FACTSControlDevice(nothing)
set_control_mode!(facts_oos, FACTSOperationModes.OOS)
@test supports_active_power(facts_oos) == false

# supports_reactive_power
@test supports_reactive_power(ThermalStandard(nothing)) == true
@test supports_reactive_power(RenewableDispatch(nothing)) == true
@test supports_reactive_power(Source(nothing)) == true
@test supports_reactive_power(SynchronousCondenser(nothing)) == true
@test supports_reactive_power(PowerLoad(nothing)) == true
@test supports_reactive_power(EnergyReservoirStorage(nothing)) == true
@test supports_reactive_power(HybridSystem(nothing)) == true
@test supports_reactive_power(InterconnectingConverter(nothing)) == false
@test supports_reactive_power(FixedAdmittance(nothing)) == false
@test supports_reactive_power(SwitchedAdmittance(nothing)) == true

# FACTSControlDevice reactive power depends on control_mode
@test supports_reactive_power(FACTSControlDevice(nothing)) == false
facts_nml = FACTSControlDevice(nothing)
set_control_mode!(facts_nml, FACTSOperationModes.NML)
@test supports_reactive_power(facts_nml) == true
facts_byp = FACTSControlDevice(nothing)
set_control_mode!(facts_byp, FACTSOperationModes.BYP)
@test supports_reactive_power(facts_byp) == true
facts_oos = FACTSControlDevice(nothing)
set_control_mode!(facts_oos, FACTSOperationModes.OOS)
@test supports_reactive_power(facts_oos) == false

# supports_voltage_control
@test supports_voltage_control(ThermalStandard(nothing)) == true
@test supports_voltage_control(ThermalMultiStart(nothing)) == true
@test supports_voltage_control(RenewableDispatch(nothing)) == true
@test supports_voltage_control(RenewableNonDispatch(nothing)) == true
@test supports_voltage_control(HydroDispatch(nothing)) == true
@test supports_voltage_control(Source(nothing)) == true
@test supports_voltage_control(EnergyReservoirStorage(nothing)) == true
@test supports_voltage_control(HybridSystem(nothing)) == true
@test supports_voltage_control(PowerLoad(nothing)) == false
@test supports_voltage_control(StandardLoad(nothing)) == false
@test supports_voltage_control(ExponentialLoad(nothing)) == false
@test supports_voltage_control(InterruptiblePowerLoad(nothing)) == false
@test supports_voltage_control(ShiftablePowerLoad(nothing)) == false
@test supports_voltage_control(InterconnectingConverter(nothing)) == false
@test supports_voltage_control(FixedAdmittance(nothing)) == false
@test supports_voltage_control(SwitchedAdmittance(nothing)) == false

# FACTSControlDevice voltage control depends on control_mode
@test supports_voltage_control(FACTSControlDevice(nothing)) == false
@test supports_voltage_control(facts_nml) == true
@test supports_voltage_control(facts_byp) == true
@test supports_voltage_control(facts_oos) == false

# SynchronousCondenser voltage control depends on bus type
sc_pv = SynchronousCondenser(nothing)
sc_pv.bus = ACBus(;
number = 1, name = "pv_bus", available = true, bustype = ACBusTypes.PV,
angle = 0.0, magnitude = 1.0, voltage_limits = (min = 0.9, max = 1.1),
base_voltage = 230.0,
)
@test supports_voltage_control(sc_pv) == true
sc_ref = SynchronousCondenser(nothing)
sc_ref.bus = ACBus(;
number = 2, name = "ref_bus", available = true, bustype = ACBusTypes.REF,
angle = 0.0, magnitude = 1.0, voltage_limits = (min = 0.9, max = 1.1),
base_voltage = 230.0,
)
@test supports_voltage_control(sc_ref) == true
sc_pq = SynchronousCondenser(nothing)
sc_pq.bus = ACBus(;
number = 2, name = "pq_bus", available = true, bustype = ACBusTypes.PQ,
angle = 0.0, magnitude = 1.0, voltage_limits = (min = 0.9, max = 1.1),
base_voltage = 230.0,
)
@test supports_voltage_control(sc_pq) == false
end
Loading