Skip to content

Commit

Permalink
Merge hotfix-572
Browse files Browse the repository at this point in the history
Fixed #572 : allow long duration hydro reservoirs 
to operate as long duration storage resources
  • Loading branch information
lbonaldo authored Feb 1, 2024
2 parents 5ccb3ca + 9249449 commit ac7e929
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix factor of 0.5 when writing out transmission losses. (#480)
- Fix summation error when a set of hours is empty (in thermal_commit.jl).
- Fix access to eELOSSByZone expr before initialization (#541)
- Fix modeling of hydro reservoir with long duration storage (#572).

### Changed

Expand Down
11 changes: 4 additions & 7 deletions src/model/resources/hydro/hydro_inter_period_linkage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ function hydro_inter_period_linkage!(EP::Model, inputs::Dict)

STOR_HYDRO_LONG_DURATION = inputs["STOR_HYDRO_LONG_DURATION"]

START_SUBPERIODS = inputs["START_SUBPERIODS"]

hours_per_subperiod = inputs["hours_per_subperiod"] #total number of hours per subperiod

dfPeriodMap = inputs["Period_Map"] # Dataframe that maps modeled periods to representative periods
Expand All @@ -81,21 +79,20 @@ function hydro_inter_period_linkage!(EP::Model, inputs::Dict)
# Modified initial state of storage for long-duration storage - initialize wth value carried over from last period
# Alternative to cSoCBalStart constraint which is included when not modeling operations wrapping and long duration storage
# Note: tw_min = hours_per_subperiod*(w-1)+1; tw_max = hours_per_subperiod*w
@constraint(EP, cSoCBalLongDurationStorageStart_H[w=1:REP_PERIOD, y in STOR_HYDRO_LONG_DURATION],
@constraint(EP, cHydroReservoirLongDurationStorageStart[w=1:REP_PERIOD, y in STOR_HYDRO_LONG_DURATION],
EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+1] == (EP[:vS_HYDRO][y,hours_per_subperiod*w]-vdSOC_HYDRO[y,w])-(1/dfGen[y,:Eff_Down]*EP[:vP][y,hours_per_subperiod*(w-1)+1])-EP[:vSPILL][y,hours_per_subperiod*(w-1)+1]+inputs["pP_Max"][y,hours_per_subperiod*(w-1)+1]*EP[:eTotalCap][y])

# Storage at beginning of period w = storage at beginning of period w-1 + storage built up in period w (after n representative periods)
## Multiply storage build up term from prior period with corresponding weight
@constraint(EP, cSoCBalLongDurationStorage_H[y in STOR_HYDRO_LONG_DURATION, r in MODELED_PERIODS_INDEX],
@constraint(EP, cHydroReservoirLongDurationStorage[y in STOR_HYDRO_LONG_DURATION, r in MODELED_PERIODS_INDEX],
vSOC_HYDROw[y, mod1(r+1, NPeriods)] == vSOC_HYDROw[y,r] + vdSOC_HYDRO[y,dfPeriodMap[r,:Rep_Period_Index]])

# Storage at beginning of each modeled period cannot exceed installed energy capacity
@constraint(EP, cSoCBalLongDurationStorageUpper_H[y in STOR_HYDRO_LONG_DURATION, r in MODELED_PERIODS_INDEX],
@constraint(EP, cHydroReservoirLongDurationStorageUpper[y in STOR_HYDRO_LONG_DURATION, r in MODELED_PERIODS_INDEX],
vSOC_HYDROw[y,r] <= dfGen[y,:Hydro_Energy_to_Power_Ratio]*EP[:eTotalCap][y])

# Initial storage level for representative periods must also adhere to sub-period storage inventory balance
# Initial storage = Final storage - change in storage inventory across representative period
@constraint(EP, cSoCBalLongDurationStorageSub_H[y in STOR_HYDRO_LONG_DURATION, r in REP_PERIODS_INDEX],
@constraint(EP, cHydroReservoirLongDurationStorageSub[y in STOR_HYDRO_LONG_DURATION, r in REP_PERIODS_INDEX],
vSOC_HYDROw[y,r] == EP[:vS_HYDRO][y,hours_per_subperiod*dfPeriodMap[r,:Rep_Period_Index]] - vdSOC_HYDRO[y,dfPeriodMap[r,:Rep_Period_Index]])


Expand Down
17 changes: 15 additions & 2 deletions src/model/resources/hydro/hydro_res.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ function hydro_res!(EP::Model, inputs::Dict, setup::Dict)
HYDRO_RES = inputs["HYDRO_RES"] # Set of all reservoir hydro resources, used for common constraints
HYDRO_RES_KNOWN_CAP = inputs["HYDRO_RES_KNOWN_CAP"] # Reservoir hydro resources modeled with unknown reservoir energy capacity

STOR_HYDRO_SHORT_DURATION = inputs["STOR_HYDRO_SHORT_DURATION"]
representative_periods = inputs["REP_PERIOD"]

START_SUBPERIODS = inputs["START_SUBPERIODS"]
INTERIOR_SUBPERIODS = inputs["INTERIOR_SUBPERIODS"]

# These variables are used in the ramp-up and ramp-down expressions
reserves_term = @expression(EP, [y in HYDRO_RES, t in 1:T], 0)
regulation_term = @expression(EP, [y in HYDRO_RES, t in 1:T], 0)
Expand Down Expand Up @@ -107,6 +113,14 @@ function hydro_res!(EP::Model, inputs::Dict, setup::Dict)

### Constratints ###

if representative_periods > 1 && !isempty(inputs["STOR_HYDRO_LONG_DURATION"])
CONSTRAINTSET = STOR_HYDRO_SHORT_DURATION
else
CONSTRAINTSET = HYDRO_RES
end

@constraint(EP, cHydroReservoirStart[y in CONSTRAINTSET,t in START_SUBPERIODS], EP[:vS_HYDRO][y,t] == EP[:vS_HYDRO][y, hoursbefore(p,t,1)]- (1/dfGen[y,:Eff_Down]*EP[:vP][y,t]) - vSPILL[y,t] + inputs["pP_Max"][y,t]*EP[:eTotalCap][y])

### Constraints commmon to all reservoir hydro (y in set HYDRO_RES) ###
@constraints(EP, begin
### NOTE: time coupling constraints in this block do not apply to first hour in each sample period;
Expand All @@ -115,8 +129,7 @@ function hydro_res!(EP::Model, inputs::Dict, setup::Dict)
# DEV NOTE: Last inputs["pP_Max"][y,t] term above is inflows; currently part of capacity factors inputs in Generators_variability.csv but should be moved to its own Hydro_inflows.csv input in future.

# Constraints for reservoir hydro
cHydroReservoir[y in HYDRO_RES, t in 1:T], EP[:vS_HYDRO][y,t] == (EP[:vS_HYDRO][y, hoursbefore(p,t,1)]
- (1/dfGen[y,:Eff_Down]*EP[:vP][y,t]) - vSPILL[y,t] + inputs["pP_Max"][y,t]*EP[:eTotalCap][y])
cHydroReservoirInterior[y in HYDRO_RES, t in INTERIOR_SUBPERIODS], EP[:vS_HYDRO][y,t] == (EP[:vS_HYDRO][y, hoursbefore(p,t,1)]- (1/dfGen[y,:Eff_Down]*EP[:vP][y,t]) - vSPILL[y,t] + inputs["pP_Max"][y,t]*EP[:eTotalCap][y])

# Maximum ramp up and down
cRampUp[y in HYDRO_RES, t in 1:T], EP[:vP][y,t] + regulation_term[y,t] + reserves_term[y,t] - EP[:vP][y, hoursbefore(p,t,1)] <= dfGen[y,:Ramp_Up_Percentage]*EP[:eTotalCap][y]
Expand Down

0 comments on commit ac7e929

Please sign in to comment.