Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions psy_data/data_10bus_ac_dc_pu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ branches10_ac(nodes10) = [
),
]

# base_current (A) = S_base / V_base = 100 MVA / 500 kV (the DC bus base voltage) = 200 A.
branches10_dc(nodes10_dc) = [
TModelHVDCLine(
name = "nodeC_DC-nodeC2_DC",
Expand All @@ -183,6 +184,7 @@ branches10_dc(nodes10_dc) = [
c = 0.0,
active_power_limits_from=(min=-10.0, max=10.0),
active_power_limits_to=(min=-10.0, max=10.0),
base_current = 200.0,
),
TModelHVDCLine(
name = "nodeD_DC-nodeD2_DC",
Expand All @@ -194,6 +196,7 @@ branches10_dc(nodes10_dc) = [
c = 0.0,
active_power_limits_from=(min=-10.0, max=10.0),
active_power_limits_to=(min=-10.0, max=10.0),
base_current = 200.0,
),
TModelHVDCLine(
name = "nodeC_DC-nodeD2_DC",
Expand All @@ -205,6 +208,7 @@ branches10_dc(nodes10_dc) = [
c = 0.0,
active_power_limits_from=(min=-10.0, max=10.0),
active_power_limits_to=(min=-10.0, max=10.0),
base_current = 200.0,
),
]

Expand Down
59 changes: 33 additions & 26 deletions psy_data/data_5bus_pu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1017,64 +1017,71 @@ interruptible(nodes5) = [InterruptiblePowerLoad(
# Natural Units: First vector: Power in MW, Second Vector: Slopes in $/MWh
ORDC_cost = CostCurve(PiecewiseIncrementalCurve(0.0, [0.0, 20.0, 40.0, 60.0, 80.0], [150.0, 27.5, 24.5, 0.5]))

# Keyword form: `OnlineReserve`'s positional order differs from the old `ConstantReserve`.
constant_reserve5() =
ConstantReserve{ReserveUp}(
"ReserveUp",
true,
300,
1.0,
3600.0,
1.0,
1.0,
0.0,
OnlineReserve{ReserveUp}(;
name = "ReserveUp",
available = true,
time_frame = 300,
requirement = 1.0,
sustained_time = 3600.0,
max_output_fraction = 1.0,
max_participation_factor = 1.0,
deployed_fraction = 0.0,
)


# The ORDC carries its demand curve on `variable`; non-spinning is `OfflineReserve` (no direction).
reserve5(thermal_generators5) = [
VariableReserve{ReserveUp}(
OnlineReserve{ReserveUp}(
"Reserve1",
true,
0.6,
maximum([gen.active_power_limits[:max] for gen in thermal_generators5]) .* 0.001,
),
VariableReserve{ReserveDown}(
OnlineReserve{ReserveDown}(
"Reserve2",
true,
0.3,
maximum([gen.active_power_limits[:max] for gen in thermal_generators5]) .* 0.005,
),
VariableReserve{ReserveUp}(
OnlineReserve{ReserveUp}(
"Reserve11",
true,
0.8,
maximum([gen.active_power_limits[:max] for gen in thermal_generators5]) .* 0.001,
),
ReserveDemandCurve{ReserveUp}(nothing, "ORDC1", true, 0.6),
VariableReserveNonSpinning("NonSpinningReserve", true, 0.5, maximum([gen.active_power_limits[:max] for gen in thermal_generators5]) .* 0.001),
OnlineReserve{ReserveUp}(;
name = "ORDC1",
available = true,
time_frame = 0.6,
variable = ORDC_cost,
),
OfflineReserve("NonSpinningReserve", true, 0.5, maximum([gen.active_power_limits[:max] for gen in thermal_generators5]) .* 0.001),
]

reserve5_re(renewable_generators5) = [
VariableReserve{ReserveUp}("Reserve3", true, 30, 100),
VariableReserve{ReserveDown}("Reserve4", true, 5, 50),
ReserveDemandCurve{ReserveUp}(nothing, "ORDC1", true, 0.6),
OnlineReserve{ReserveUp}("Reserve3", true, 30, 100),
OnlineReserve{ReserveDown}("Reserve4", true, 5, 50),
OnlineReserve{ReserveUp}(; name = "ORDC1", available = true, time_frame = 0.6, variable = ORDC_cost),
]
reserve5_hy(hydro_generators5) = [
VariableReserve{ReserveUp}("Reserve5", true, 30, 100),
VariableReserve{ReserveDown}("Reserve6", true, 5, 50),
ReserveDemandCurve{ReserveUp}(nothing, "ORDC1", true, 0.6),
OnlineReserve{ReserveUp}("Reserve5", true, 30, 100),
OnlineReserve{ReserveDown}("Reserve6", true, 5, 50),
OnlineReserve{ReserveUp}(; name = "ORDC1", available = true, time_frame = 0.6, variable = ORDC_cost),
]

reserve5_il(interruptible_loads) = [
VariableReserve{ReserveUp}("Reserve7", true, 30, 100),
VariableReserve{ReserveDown}("Reserve8", true, 5, 50),
ReserveDemandCurve{ReserveUp}(nothing, "ORDC1", true, 0.6),
OnlineReserve{ReserveUp}("Reserve7", true, 30, 100),
OnlineReserve{ReserveDown}("Reserve8", true, 5, 50),
OnlineReserve{ReserveUp}(; name = "ORDC1", available = true, time_frame = 0.6, variable = ORDC_cost),
]


reserve5_phes(phes5) = [
VariableReserve{ReserveUp}("Reserve9", true, 30, 100),
VariableReserve{ReserveDown}("Reserve10", true, 5, 50),
ReserveDemandCurve{ReserveUp}(nothing, "ORDC1", true, 0.6),
OnlineReserve{ReserveUp}("Reserve9", true, 30, 100),
OnlineReserve{ReserveDown}("Reserve10", true, 5, 50),
OnlineReserve{ReserveUp}(; name = "ORDC1", available = true, time_frame = 0.6, variable = ORDC_cost),
]

# TODO: add a sensible cost for hybrid devices
Expand Down
83 changes: 51 additions & 32 deletions psy_data/generation_cost_function_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -338,13 +338,11 @@ end
## Single Bid MarketBid Cost Function

function thermal_generators_market_bid(node)
market_bid1 = MarketBidCost(
30.0,
(hot = 1.5, warm = 1.5, cold = 1.5),
0.75,
CostCurve(PiecewiseIncrementalCurve(0.0, [10.0, 30.0, 50.0, 100.0], [63.37, 98.155, 102.276])),
nothing,
Vector{Service}(),
market_bid1 = MarketBidCost(;
no_load_cost = LinearCurve(30.0),
start_up = (hot = 1.5, warm = 1.5, cold = 1.5),
shut_down = LinearCurve(0.75),
incremental_offer_curves = CostCurve(PiecewiseIncrementalCurve(0.0, [10.0, 30.0, 50.0, 100.0], [63.37, 98.155, 102.276])),
)

gen1 = ThermalStandard(
Expand All @@ -365,13 +363,11 @@ function thermal_generators_market_bid(node)
base_power=100.0,
)

market_bid2 = MarketBidCost(
50.0,
(hot = 1.5, warm = 1.5, cold = 1.5),
0.75,
CostCurve(PiecewiseIncrementalCurve(0.0, [10.0, 30.0, 50.0, 100.0], [66.37, 88.155, 109.276])),
nothing,
Vector{Service}(),
market_bid2 = MarketBidCost(;
no_load_cost = LinearCurve(50.0),
start_up = (hot = 1.5, warm = 1.5, cold = 1.5),
shut_down = LinearCurve(0.75),
incremental_offer_curves = CostCurve(PiecewiseIncrementalCurve(0.0, [10.0, 30.0, 50.0, 100.0], [66.37, 88.155, 109.276])),
)

gen2 = ThermalStandard(
Expand All @@ -395,6 +391,42 @@ function thermal_generators_market_bid(node)
return [gen1, gen2]
end

# Constant-valued forecast matching the market-bid windows (2 windows x 5 steps, hourly).
_mbts_constant_forecast(ini_time, val) = Dict(
ini_time => fill(val, 5),
ini_time + Hour(1) => fill(val, 5),
)

# Attach the series backing a `MarketBidTimeSeriesCost` (fields reference them by key,
# so `gen` must already be in the system) and set it as the operation cost.
function _set_ts_market_bid_cost!(sys, gen, incremental_bid, ini_time, no_load)
_det(name, val) = PSY.Deterministic(;
name = name,
data = _mbts_constant_forecast(ini_time, val),
resolution = Hour(1),
)
pwl_key = PSY.add_time_series!(sys, gen, incremental_bid)
init_key = PSY.add_time_series!(sys, gen, _det("initial_input_incremental", 0.0))
dec_key = PSY.add_time_series!(
sys, gen, _det("variable_cost_decremental", PiecewiseStepData([0.0, 0.0], [0.0])),
)
dec_init_key = PSY.add_time_series!(sys, gen, _det("initial_input_decremental", 0.0))
nl_key = PSY.add_time_series!(sys, gen, _det("no_load_cost", no_load))
su_key = PSY.add_time_series!(sys, gen, _det("start_up", (1.5, 1.5, 1.5)))
sd_key = PSY.add_time_series!(sys, gen, _det("shut_down", 0.75))
PSY.set_operation_cost!(
gen,
PSY.MarketBidTimeSeriesCost(;
no_load_cost = PSY.TimeSeriesLinearCurve(nl_key),
start_up = PSY.IS.TupleTimeSeries{PSY.StartUpStages}(su_key),
shut_down = PSY.TimeSeriesLinearCurve(sd_key),
incremental_offer_curves = PSY.make_market_bid_ts_curve(pwl_key, init_key),
decremental_offer_curves = PSY.make_market_bid_ts_curve(dec_key, dec_init_key),
),
)
return
end

function thermal_generators_market_bid_ts(sys, node)
ini_time = DateTime("1/1/2024 0:00:00", "d/m/y H:M:S")
market_bid_gen1_data = Dict(
Expand Down Expand Up @@ -441,14 +473,8 @@ function thermal_generators_market_bid_ts(sys, node)
resolution = Hour(1),
)

market_bid1 = MarketBidCost(
30.0,
(hot = 1.5, warm = 1.5, cold = 1.5),
0.75,
nothing,
nothing,
Vector{Service}(),
)
# Placeholder, replaced by the TS-backed cost after `add_component!`.
market_bid1 = MarketBidCost(nothing)

gen1 = ThermalStandard(
name="Test Unit1",
Expand All @@ -468,14 +494,7 @@ function thermal_generators_market_bid_ts(sys, node)
base_power=100.0,
)

market_bid2 = MarketBidCost(
50.0,
(hot = 1.5, warm = 1.5, cold = 1.5),
0.75,
nothing,
nothing,
Vector{Service}(),
)
market_bid2 = MarketBidCost(nothing)

gen2 = ThermalStandard(
name="Test Unit2",
Expand All @@ -498,8 +517,8 @@ function thermal_generators_market_bid_ts(sys, node)

PSY.add_component!(sys, gen1)
PSY.add_component!(sys, gen2)
PSY.set_variable_cost!(sys, gen1, market_bid_gen1, PSY.UnitSystem.NATURAL_UNITS)
PSY.set_variable_cost!(sys, gen2, market_bid_gen2, PSY.UnitSystem.NATURAL_UNITS)
_set_ts_market_bid_cost!(sys, gen1, market_bid_gen1, ini_time, 30.0)
_set_ts_market_bid_cost!(sys, gen2, market_bid_gen2, ini_time, 50.0)

return [gen1, gen2]
end