mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 00:08:52 -06:00
126 lines
4.6 KiB
Julia
126 lines
4.6 KiB
Julia
# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
|
|
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
|
# Released under the modified BSD license. See COPYING.md for more details.
|
|
|
|
function _add_storage_unit!(
|
|
model::JuMP.Model,
|
|
su::StorageUnit,
|
|
sc::UnitCommitmentScenario,
|
|
)::Nothing
|
|
# Initialize variables
|
|
storage_level = _init(model, :storage_level)
|
|
charge_rate = _init(model, :charge_rate)
|
|
discharge_rate = _init(model, :discharge_rate)
|
|
is_charging = _init(model, :is_charging)
|
|
is_discharging = _init(model, :is_discharging)
|
|
eq_min_charge_rate = _init(model, :eq_min_charge_rate)
|
|
eq_max_charge_rate = _init(model, :eq_max_charge_rate)
|
|
eq_min_discharge_rate = _init(model, :eq_min_discharge_rate)
|
|
eq_max_discharge_rate = _init(model, :eq_max_discharge_rate)
|
|
# Initialize constraints
|
|
net_injection = _init(model, :expr_net_injection)
|
|
eq_storage_transition = _init(model, :eq_storage_transition)
|
|
eq_ending_level = _init(model, :eq_ending_level)
|
|
# time in hours
|
|
time_step = sc.time_step / 60
|
|
|
|
for t in 1:model[:instance].time
|
|
# Decision variable
|
|
storage_level[sc.name, su.name, t] = @variable(
|
|
model,
|
|
lower_bound = su.min_level[t],
|
|
upper_bound = su.max_level[t]
|
|
)
|
|
charge_rate[sc.name, su.name, t] = @variable(model)
|
|
discharge_rate[sc.name, su.name, t] = @variable(model)
|
|
is_charging[sc.name, su.name, t] = @variable(model, binary = true)
|
|
is_discharging[sc.name, su.name, t] = @variable(model, binary = true)
|
|
|
|
# Objective function terms
|
|
add_to_expression!(
|
|
model[:obj],
|
|
charge_rate[sc.name, su.name, t],
|
|
su.charge_cost[t] * sc.probability,
|
|
)
|
|
|
|
add_to_expression!(
|
|
model[:obj],
|
|
discharge_rate[sc.name, su.name, t],
|
|
su.discharge_cost[t] * sc.probability,
|
|
)
|
|
|
|
# Net injection
|
|
add_to_expression!(
|
|
net_injection[sc.name, su.bus.name, t],
|
|
discharge_rate[sc.name, su.name, t],
|
|
1.0,
|
|
)
|
|
add_to_expression!(
|
|
net_injection[sc.name, su.bus.name, t],
|
|
charge_rate[sc.name, su.name, t],
|
|
-1.0,
|
|
)
|
|
|
|
# Simultaneous charging and discharging
|
|
if !su.simultaneous_charge_and_discharge[t]
|
|
# Initialize the model dictionary
|
|
eq_simultaneous_charge_and_discharge =
|
|
_init(model, :eq_simultaneous_charge_and_discharge)
|
|
# Constraints
|
|
eq_simultaneous_charge_and_discharge[sc.name, su.name, t] =
|
|
@constraint(
|
|
model,
|
|
is_charging[sc.name, su.name, t] +
|
|
is_discharging[sc.name, su.name, t] <= 1.0
|
|
)
|
|
end
|
|
|
|
# Charge and discharge constraints
|
|
eq_min_charge_rate[sc.name, su.name, t] = @constraint(
|
|
model,
|
|
charge_rate[sc.name, su.name, t] >=
|
|
is_charging[sc.name, su.name, t] * su.min_charge_rate[t]
|
|
)
|
|
eq_max_charge_rate[sc.name, su.name, t] = @constraint(
|
|
model,
|
|
charge_rate[sc.name, su.name, t] <=
|
|
is_charging[sc.name, su.name, t] * su.max_charge_rate[t]
|
|
)
|
|
eq_min_discharge_rate[sc.name, su.name, t] = @constraint(
|
|
model,
|
|
discharge_rate[sc.name, su.name, t] >=
|
|
is_discharging[sc.name, su.name, t] * su.min_discharge_rate[t]
|
|
)
|
|
eq_max_discharge_rate[sc.name, su.name, t] = @constraint(
|
|
model,
|
|
discharge_rate[sc.name, su.name, t] <=
|
|
is_discharging[sc.name, su.name, t] * su.max_discharge_rate[t]
|
|
)
|
|
|
|
# Storage energy transition constraint
|
|
prev_storage_level =
|
|
t == 1 ? su.initial_level : storage_level[sc.name, su.name, t-1]
|
|
eq_storage_transition[sc.name, su.name, t] = @constraint(
|
|
model,
|
|
storage_level[sc.name, su.name, t] ==
|
|
(1 - su.loss_factor[t]) * prev_storage_level +
|
|
charge_rate[sc.name, su.name, t] *
|
|
time_step *
|
|
su.charge_efficiency[t] -
|
|
discharge_rate[sc.name, su.name, t] * time_step /
|
|
su.discharge_efficiency[t]
|
|
)
|
|
|
|
# Storage ending level constraint
|
|
if t == sc.time
|
|
eq_ending_level[sc.name, su.name] = @constraint(
|
|
model,
|
|
su.min_ending_level <=
|
|
storage_level[sc.name, su.name, t] <=
|
|
su.max_ending_level
|
|
)
|
|
end
|
|
end
|
|
return
|
|
end
|