PowerSystems MarketBidCost

Is an OperationalCost data structure that allows the user to run a production cost model that is very similar to most US electricity market auctions with bids for energy and ancillary services jointly. This page showcases how to create data for this cost function.

Adding Energy bids to MarketBidCost

Step 1: Constructiong device with MarketBidCost

When using MarketBidCost, the user can add the cost struct to the device specifying only certain elements, at this point the actual energy cost bids don't need to be populated/passed.

The code below shows an example how we can create a thermal device with MarketBidCost.

julia> using PowerSystems, Dates
julia> bus = ACBus(1, "nodeE", "REF", 0, 1.0, (min = 0.9, max = 1.05), 230, nothing, nothing) nodeE (ACBus): number: 1 name: nodeE bustype: ACBusTypes.REF = 3 angle: 0.0 magnitude: 1.0 voltage_limits: (min = 0.9, max = 1.05) base_voltage: 230.0 area: nothing load_zone: nothing ext: Dict{String, Any}() internal: InfrastructureSystems.InfrastructureSystemsInternal
julia> generator = ThermalStandard( name = "Brighton", available = true, status = true, bus = bus, active_power = 6.0, reactive_power = 1.50, rating = 0.75, prime_mover_type = PrimeMovers.ST, fuel = ThermalFuels.COAL, active_power_limits = (min = 0.0, max = 6.0), reactive_power_limits = (min = -4.50, max = 4.50), time_limits = (up = 0.015, down = 0.015), ramp_limits = (up = 5.0, down = 3.0), operation_cost = MarketBidCost( no_load = 0.0, start_up = (hot = 0.0, warm = 0.0, cold = 0.0), shut_down = 0.0, ), base_power = 100.0, ) Brighton (ThermalStandard): name: Brighton available: true status: true bus: nodeE (ACBus) active_power: 600.0 reactive_power: 150.0 rating: 75.0 active_power_limits: (min = 0.0, max = 600.0) reactive_power_limits: (min = -450.0, max = 450.0) ramp_limits: (up = 500.0, down = 300.0) operation_cost: MarketBidCost base_power: 100.0 time_limits: (up = 0.015, down = 0.015) must_run: false prime_mover_type: PrimeMovers.ST = 20 fuel: ThermalFuels.COAL = 1 services: 0-element Vector{Service} time_at_status: 10000.0 dynamic_injector: nothing ext: Dict{String, Any}() time_series_container: internal: InfrastructureSystems.InfrastructureSystemsInternal

Step 2: Creating the TimeSeriesData for the Market Bid

The user is expected to pass the TimeSeriesData that holds the energy bid data which can be of any type (i.e. SingleTimeSeries or Deterministic) and data can be Array{Float64}, Array{Tuple{Float64, Float64}} or Array{Array{Tuple{Float64,Float64}}. If the data is just floats then the cost in the optimization is seen as a constant variable cost, but if data is a Tuple or Array{Tuple} then the model expects the tuples to be cost & power-point pairs (cost in /p.u-hr & power-point in p.u-hr), which is modeled same as TwoPartCost or ThreePartCost. Code below shows an example of how to build a TimeSeriesData.

julia> data =
           Dict(Dates.DateTime("2020-01-01") => [
               [(0.0, 0.05), (290.1, 0.0733), (582.72, 0.0967), (894.1, 0.120)],
               [(0.0, 0.05), (300.1, 0.0733), (600.72, 0.0967), (900.1, 0.120)],]
           )Dict{Dates.DateTime, Vector{Vector{Tuple{Float64, Float64}}}} with 1 entry:
  DateTime("2020-01-01T00:00:00") => [[(0.0, 0.05), (290.1, 0.0733), (582.72, 0…
julia> time_series_data = Deterministic( name = "variable_cost", data = data, resolution = Dates.Hour(1) )Deterministic time_series (1): name: variable_cost data: DataStructures.SortedDict(Dates.DateTime("2020-01-01T00:00:00") => [[(0.0, 0.05), (290.1, 0.0733), (582.72, 0.0967), (894.1, 0.12)], [(0.0, 0.05), (300.1, 0.0733), (600.72, 0.0967), (900.1, 0.12)]]) resolution: 1 hour scaling_factor_multiplier: nothing

NOTE: Due to limitations in DataStructures.jl, in PowerSystems.jl when creating Forecasts or TimeSeries for your MarketBidCost, you need to define your data as in the example or with a very explicit container. Otherwise, it won't discern the types properly in the constructor and will return SortedDict{Any,Any,Base.Order.ForwardOrdering} which causes the constructor in PowerSystems.jl to fail. For instance, you need to define the Dict with the data as follows:

    # Very verbose dict definition
    data = Dict{DateTime,Array{Array{Tuple{Float64,Float64},1},1}}()
    for t in range(initial_time_sys; step = Hour(1), length = window_count)
        data[t] = MY_BID_DATA
    end

Step 3a: Adding Energy Bid TimeSeriesData to the device

To add energy market bids time-series to the MarketBidCost, the use of set_variable_cost! is recommended. The arguments for set_variable_cost! are:

  • sys::System: PowerSystem System
  • component::StaticInjection: Static injection device
  • time_series_data::TimeSeriesData: TimeSeriesData
julia> sys = System(100.0, [bus], [generator])┌ Warning: There are no ElectricLoad Components in the System
└ @ PowerSystems ~/work/PowerSystems.jl/PowerSystems.jl/src/utils/IO/system_checks.jl:59
System
┌───────────────────┬─────────────┐
│ Property          │ Value       │
├───────────────────┼─────────────┤
│ Name              │             │
│ Description       │             │
│ System Units Base │ SYSTEM_BASE │
│ Base Power        │ 100.0       │
│ Base Frequency    │ 60.0        │
│ Num Components    │ 2           │
└───────────────────┴─────────────┘

Static Components
┌─────────────────┬───────┬────────────────────────┬───────────────┐
│ Type            │ Count │ Has Static Time Series │ Has Forecasts │
├─────────────────┼───────┼────────────────────────┼───────────────┤
│ ACBus           │ 1     │ false                  │ false         │
│ ThermalStandard │ 1     │ false                  │ false         │
└─────────────────┴───────┴────────────────────────┴───────────────┘
julia> set_variable_cost!(sys, generator, time_series_data)

Step 3b: Adding Service Bid TimeSeriesData to the device

Similar to adding energy market bids, for adding bids for ancillary services the use of set_service_bid! is recommended.

julia> service = VariableReserve{ReserveUp}("example_reserve", true, 0.6, 2.0)
example_reserve (VariableReserve{ReserveUp}):
   name: example_reserve
   available: true
   time_frame: 0.6
   requirement: 2.0
   sustained_time: 3600.0
   max_output_fraction: 1.0
   max_participation_factor: 1.0
   deployed_fraction: 0.0
   ext: Dict{String, Any}()
   time_series_container:
   internal: InfrastructureSystems.InfrastructureSystemsInternal
julia> add_service!(sys, service, get_component(ThermalStandard, sys, "Brighton"))
julia> data = Dict(Dates.DateTime("2020-01-01") => [650.3, 750.0])Dict{Dates.DateTime, Vector{Float64}} with 1 entry: DateTime("2020-01-01T00:00:00") => [650.3, 750.0]
julia> time_series_data = Deterministic( name = get_name(service), data = data, resolution = Dates.Hour(1) )Deterministic time_series (1): name: example_reserve data: DataStructures.SortedDict(Dates.DateTime("2020-01-01T00:00:00") => [650.3, 750.0]) resolution: 1 hour scaling_factor_multiplier: nothing
julia> set_service_bid!(sys, generator, service, time_series_data)