-
Notifications
You must be signed in to change notification settings - Fork 1
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
RFC: subtype System to enable modelling alternative market clearing formulations #34
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
const MARKET_WIDE_ZONE = -9999 | ||
const BidName = InlineString31 | ||
const ZoneNum = Int64 | ||
const PriceSensitiveBid = Vector{Tuple{Float64, Float64}} | ||
|
||
""" | ||
$TYPEDEF | ||
|
@@ -24,6 +25,14 @@ Base.@kwdef struct Zone | |
end | ||
const Zones = Dictionary{ZoneNum, Zone} | ||
|
||
Base.@kwdef struct FourRequirements | ||
number::ZoneNum | ||
regulation_up::Float64 | ||
regulation_down::Float64 | ||
responsive_regulation::Float64 | ||
non_spinning::Float64 | ||
end | ||
|
||
const UnitCode = Int64 | ||
""" | ||
$TYPEDEF | ||
|
@@ -236,6 +245,10 @@ end | |
|
||
###### Time Series types ###### | ||
|
||
const AncillaryServiceTypes = Union{ | ||
KeyedArray{Union{Missing, Float64}, 2}, | ||
KeyedArray{Union{Missing, PriceSensitiveBid}, 2} | ||
} | ||
""" | ||
$TYPEDEF | ||
|
||
|
@@ -249,11 +262,11 @@ Base.@kwdef struct GeneratorTimeSeries | |
"Generation of the generator at the start of the time period (pu)" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This type may also need additional fields for |
||
initial_generation::KeyedArray{Float64, 1} | ||
"Generator offer curves. `KeyedArray` where the axis keys are `generator names x datetimes`" | ||
offer_curve::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} | ||
offer_curve::KeyedArray{PriceSensitiveBid, 2} | ||
"Generator minimum output in the ancillary services market (pu)" | ||
regulation_min::KeyedArray{Float64, 2} | ||
regulation_min::Union{Missing, KeyedArray{Float64, 2}} | ||
"Generator maximum output in the ancillary services market (pu)" | ||
regulation_max::KeyedArray{Float64, 2} | ||
regulation_max::Union{Missing, KeyedArray{Float64, 2}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we intend to reuse code, is it easier to set the values to something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting question, this is how There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure but it looks like it would work out to a constraint that is always true? We might have to reformat the expression or add an extra constraint but it might be worth it to avoid having to special-case things to account for possible missings. |
||
"Generator minimum output (pu)" | ||
pmin::KeyedArray{Float64, 2} | ||
"Generator maximum output (pu)" | ||
|
@@ -262,22 +275,57 @@ Base.@kwdef struct GeneratorTimeSeries | |
Ancillary services regulation reserve offer prices (\$ /pu). | ||
Generators not providing the service will have `missing` offer data. | ||
""" | ||
regulation_offers::KeyedArray{Union{Missing, Float64}, 2} | ||
regulation_offers::AncillaryServiceTypes | ||
""" | ||
Ancillary services spinning reserve offer prices (\$ /pu). | ||
Generators not providing the service will have `missing` offer data. | ||
""" | ||
spinning_offers::KeyedArray{Union{Missing, Float64}, 2} | ||
spinning_offers::AncillaryServiceTypes | ||
""" | ||
Ancillary services online supplemental reserve offer prices (\$ /pu). | ||
Generators not providing the service will have `missing` offer data. | ||
""" | ||
on_supplemental_offers::KeyedArray{Union{Missing, Float64}, 2} | ||
on_supplemental_offers::AncillaryServiceTypes | ||
""" | ||
Ancillary services offline supplemental reserve offer prices (\$ /pu). | ||
Generators not providing the service will have `missing` offer data. | ||
""" | ||
off_supplemental_offers::KeyedArray{Union{Missing, Float64}, 2} | ||
off_supplemental_offers::AncillaryServiceTypes | ||
|
||
""" | ||
Ancillary services regulation reserve offer prices (\$ /pu). | ||
Generators not providing the service will have `missing` offer data. | ||
Some market clearing formulations do not include this service, so the field will be set | ||
to `missing` by default. | ||
""" | ||
regulation_down_offers::Union{Missing, AncillaryServiceTypes} = missing | ||
end | ||
|
||
function GeneratorTimeSeries( | ||
initial_generation, | ||
offer_curve, | ||
regulation_min, | ||
regulation_max, | ||
pmin, | ||
pmax, | ||
regulation_offers, | ||
spinning_offers, | ||
on_supplemental_offers, | ||
off_supplemental_offers, | ||
) | ||
return GeneratorTimeSeries( | ||
initial_generation, | ||
offer_curve, | ||
regulation_min, | ||
regulation_max, | ||
pmin, | ||
pmax, | ||
regulation_offers, | ||
spinning_offers, | ||
on_supplemental_offers, | ||
off_supplemental_offers, | ||
missing | ||
) | ||
end | ||
|
||
""" | ||
|
@@ -319,6 +367,31 @@ Base.@kwdef struct GeneratorStatusRT <: GeneratorStatus | |
regulation_commitment::KeyedArray{Bool, 2} | ||
end | ||
|
||
struct LoadServices | ||
pmin::KeyedArray{Float64, 2} | ||
pmax::KeyedArray{Float64, 2} | ||
regulation_up_offers::KeyedArray{Union{Missing, PriceSensitiveBid}, 2} | ||
regulation_down_offers::KeyedArray{Union{Missing, PriceSensitiveBid}, 2} | ||
spinning_offers::KeyedArray{Union{Missing, PriceSensitiveBid}, 2} | ||
on_supplemental_offers::KeyedArray{Union{Missing, PriceSensitiveBid}, 2} | ||
off_supplemental_offers::KeyedArray{Union{Missing, PriceSensitiveBid}, 2} | ||
end | ||
|
||
struct EnergyBids | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a good name? When we refer to "bids" it can mean bids in either direction. But in the market data "bid" means demand and "offer" means supply. |
||
""" | ||
Bool indicating whether the bid is part of a multi-hour block, where either all bids | ||
must clear or none do. | ||
""" | ||
multi_hour::KeyedArray{Bool, 2} | ||
""" | ||
Bool indicating whether the bid is a fixed type which can only clear at the given price | ||
and volume. | ||
""" | ||
is_fixed_type::KeyedArray{Bool, 2} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is a Boolean going to be sufficient here? There are three types of bid: curve, variable and fixed. In terms of how the formulation is implemented I think variable is just a curve with only one block. In which case we just have to distinguish which ones are fixed. |
||
"Bid curve, `KeyedArray` where the axis keys are `generator names x datetimes`." | ||
bid_curve::KeyedArray{PriceSensitiveBid, 2} | ||
end | ||
|
||
""" | ||
System | ||
|
||
|
@@ -387,11 +460,68 @@ Base.@kwdef mutable struct SystemDA <: System | |
|
||
# Virtuals/PSD time series | ||
"Increment bids time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" | ||
increments::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} | ||
increments::KeyedArray{PriceSensitiveBid, 2} | ||
"Decrement bids time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" | ||
decrements::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} | ||
decrements::KeyedArray{PriceSensitiveBid, 2} | ||
"Price sensitive load bids time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" | ||
price_sensitive_loads::KeyedArray{Vector{Tuple{Float64, Float64}}, 2} | ||
price_sensitive_loads::KeyedArray{PriceSensitiveBid, 2} | ||
end | ||
|
||
""" | ||
$TYPEDEF | ||
|
||
Subtype of a `System` for modelling a purely financial style of day-ahead market clearing. | ||
Key differences to the physical day-ahead market clearing approach are that _all_ offers and | ||
bids are price sensitive and virtual participants are not distinguished from physical | ||
participants. | ||
|
||
Fields: | ||
$TYPEDFIELDS | ||
""" | ||
Base.@kwdef mutable struct FinancialSystemDA <: System | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about this name since it clashes with another widely-used susbystem in our codebase that we are probably going to have to deal with eventually. Is there another appropriate name? |
||
"`Dictionary` where the keys are bus names and the values are generator ids at that bus" | ||
gens_per_bus::Dictionary{BusName, Vector{String}} | ||
"`Dictionary` where the keys are bus names and the values are increment bid ids at that bus" | ||
energy_offers_per_bus::Dictionary{BusName, Vector{BidName}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these similar to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think they are identical in terms of type. It's a question of different concepts. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we should go with the most generic option then use a type to differentiate if we need to? If we use them similarly and they behave in a similar way that is. |
||
"`Dictionary` where the keys are bus names and the values are decrement bid ids at that bus" | ||
energy_bids_per_bus::Dictionary{BusName, Vector{BidName}} | ||
"`Dictionary` where the keys are bus names and the values are load ids at that bus" | ||
load_resources_per_bus::Dictionary{BusName, Vector{BidName}} | ||
|
||
"Zones in the `System`, which will also include a `Requirements` entry for the market wide zone" | ||
zones::Dictionary{ZoneNum, FourRequirements} | ||
"Buses in the `System` indexed by bus name" | ||
buses::Buses | ||
"Generators in the `System` indexed by unit code" | ||
generators::Generators | ||
"Branches in the `System` indexed by branch name" | ||
branches::Branches | ||
""" | ||
The line outage distribution factor matrix of the system for a set of contingencies given | ||
by the keys of the `Dictionary`. Each entry is a `KeyedArray` with axis keys | ||
`branch names x branch on outage` | ||
""" | ||
lodfs::Dictionary{String, KeyedArray{Float64, 2}} | ||
""" | ||
Power transfer distribution factor of the system. `KeyedArray` where the axis keys are | ||
`branch names x bus names` | ||
""" | ||
ptdf::Union{KeyedArray{Float64, 2}, Missing} | ||
|
||
# Generator related time series | ||
"Generator related time series data" | ||
generator_time_series::GeneratorTimeSeries | ||
"Generator status time series needed for the day-ahead formulation" | ||
generator_status::GeneratorStatusDA | ||
|
||
# Load time series | ||
"Load time series data. `KeyedArray` where the axis keys are `load ids x datetimes`" | ||
load_services::LoadServices | ||
|
||
"Energy offers time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" | ||
energy_offers::EnergyBids | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we switch the other systems to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that could work. I suppose there would be some (minimal?) overhead to storing the extra information, but the advantages of reusable code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since it's repeated information and is generally read-only we could probably use an efficient data structure to avoid too much extra storage |
||
"Energy demands time series data. `KeyedArray` where the axis keys are `bid ids x datetimes`" | ||
energy_demands::EnergyBids | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do these field names make sense? Previously we have referred to increment and decrement bids but these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the names make sense but since we're already explaining things in terms of "supply" and "demand" to clarify, perhaps those are the more straightforward terms to use? Or will all markets use these terms consistently? |
||
end | ||
|
||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably document what the tuple members represent