Skip to content

Commit

Permalink
Make storage virtual discharge turn-off-able with a setting (#638)
Browse files Browse the repository at this point in the history
  • Loading branch information
gmantegna authored Feb 29, 2024
1 parent 6e77c7d commit babed03
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add functions to compute conflicting constraints when model is infeasible if supported by the solver (#624).
- New settings parameter, VirtualChargeDischargeCost to test script and VREStor example case. The PR 608 attempts to
introduce this parameter as cost of virtual charging and discharging to avoid unusual results (#608).
- New settings parameter, StorageVirtualDischarge, to turn storage virtual charging and discharging off if desired by the user (#638).


### Fixed
Expand Down
3 changes: 3 additions & 0 deletions docs/src/data_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ Note that all settings parameters are case sensitive.
| WriteOutputs | Flag for writing the model outputs with hourly resolution or just the annual sum.|
|| "full" = write the model outputs with hourly resolution.|
|| "annual" = write only the annual sum of the model outputs.|
|StorageVirtualDischarge| Flag for whether to include the ability for storage to have a "virtual" charge and discharge that contributes to the capacity reserve margin.
||1 (default) = include virtual charging and discharging||
||0 = do not include virtual charging and discharging||

Additionally, Solver related settings parameters are specified in the appropriate .yml file (e.g. `gurobi_settings.yml` or `cplex_settings.yml`),
which should be located in the current working directory.
Expand Down
1 change: 1 addition & 0 deletions src/configure_settings/configure_settings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function default_settings()
"WriteOutputs" => "full",
"ComputeConflicts" => 0,
"ResourcePath" => "Resources",
"StorageVirtualDischarge" => 1,
)
end

Expand Down
9 changes: 7 additions & 2 deletions src/model/resources/storage/storage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A wide range of energy storage devices (all $o \in \mathcal{O}$) can be modeled in GenX, using one of two generic storage formulations: (1) storage technologies with symmetric charge and discharge capacity (all $o \in \mathcal{O}^{sym}$), such as Lithium-ion batteries and most other electrochemical storage devices that use the same components for both charge and discharge; and (2) storage technologies that employ distinct and potentially asymmetric charge and discharge capacities (all $o \in \mathcal{O}^{asym}$), such as most thermal storage technologies or hydrogen electrolysis/storage/fuel cell or combustion turbine systems.
If a capacity reserve margin is modeled, variables for virtual charge, $\Pi^{CRM}_{o,z,t}$, and virtual discharge, $\Theta^{CRM}_{o,z,t}$, are created to represent
contributions that a storage device makes to the capacity reserve margin without actually generating power. These represent power that the storage device could
contributions that a storage device makes to the capacity reserve margin without actually generating power. (This functionality can be turned off with the parameter StorageVirtualDischarge in the GenX settings file.) These represent power that the storage device could
have discharged or consumed if called upon to do so, based on its available state of charge. Importantly, a dedicated set of variables (those of the form $\Pi^{CRM}_{o,z,t}, \Theta^{CRM}_{o,z,t}$)
and constraints are created to ensure that any virtual contributions to the capacity reserve margin could be made as actual charge/discharge if necessary without
affecting system operations in any other timesteps. If a capacity reserve margin is not modeled, all related variables are fixed at 0. The overall contribution
Expand Down Expand Up @@ -143,6 +143,7 @@ function storage!(EP::Model, inputs::Dict, setup::Dict)
CapacityReserveMargin = setup["CapacityReserveMargin"]
IncludeLossesInESR = setup["IncludeLossesInESR"]
MultiStage = setup["MultiStage"]
StorageVirtualDischarge = setup["StorageVirtualDischarge"]

if !isempty(STOR_ALL)
investment_energy!(EP, inputs, setup)
Expand Down Expand Up @@ -173,7 +174,11 @@ function storage!(EP::Model, inputs::Dict, setup::Dict)

# Capacity Reserves Margin policy
if CapacityReserveMargin > 0
@expression(EP, eCapResMarBalanceStor[res=1:inputs["NCapacityReserveMargin"], t=1:T], sum(derating_factor(gen[y], tag=res) * (EP[:vP][y,t] + EP[:vCAPRES_discharge][y,t] - EP[:vCHARGE][y,t] - EP[:vCAPRES_charge][y,t]) for y in STOR_ALL))
@expression(EP, eCapResMarBalanceStor[res=1:inputs["NCapacityReserveMargin"], t=1:T], sum(derating_factor(gen[y], tag=res) * (EP[:vP][y,t] - EP[:vCHARGE][y,t]) for y in STOR_ALL))
if StorageVirtualDischarge > 0
@expression(EP, eCapResMarBalanceStorVirtual[res=1:inputs["NCapacityReserveMargin"], t=1:T], sum(derating_factor(gen[y], tag=res) * (EP[:vCAPRES_discharge][y,t] - EP[:vCAPRES_charge][y,t]) for y in STOR_ALL))
add_similar_to_expression!(eCapResMarBalanceStor,eCapResMarBalanceStorVirtual)
end
add_similar_to_expression!(EP[:eCapResMarBalance], eCapResMarBalanceStor)
end

Expand Down
17 changes: 13 additions & 4 deletions src/model/resources/vre_stor/vre_stor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1901,6 +1901,7 @@ function vre_stor_capres!(EP::Model, inputs::Dict, setup::Dict)
rep_periods = inputs["REP_PERIOD"]

virtual_discharge_cost = inputs["VirtualChargeDischargeCost"]
StorageVirtualDischarge = setup["StorageVirtualDischarge"]

by_rid(rid, sym) = by_rid_res(rid, sym, gen_VRE_STOR)

Expand Down Expand Up @@ -2032,10 +2033,18 @@ function vre_stor_capres!(EP::Model, inputs::Dict, setup::Dict)
@expression(EP, eCapResMarBalanceStor_VRE_STOR[res=1:inputs["NCapacityReserveMargin"], t=1:T],(
sum(derating_factor(gen[y],tag=res)*by_rid(y,:etainverter)*inputs["pP_Max_Solar"][y,t]*EP[:eTotalCap_SOLAR][y] for y in inputs["VS_SOLAR"])
+ sum(derating_factor(gen[y],tag=res)*inputs["pP_Max_Wind"][y,t]*EP[:eTotalCap_WIND][y] for y in inputs["VS_WIND"])
+ sum(derating_factor(gen[y],tag=res)*by_rid(y,:etainverter)*(EP[:vP_DC_DISCHARGE][y,t]+vCAPRES_DC_DISCHARGE[y,t]) for y in DC_DISCHARGE)
+ sum(derating_factor(gen[y],tag=res)*(EP[:vP_AC_DISCHARGE][y,t]+vCAPRES_AC_DISCHARGE[y,t]) for y in AC_DISCHARGE)
- sum(derating_factor(gen[y],tag=res)*(EP[:vP_DC_CHARGE][y,t]+vCAPRES_DC_CHARGE[y,t])/by_rid(y,:etainverter) for y in DC_CHARGE)
- sum(derating_factor(gen[y],tag=res)*(EP[:vP_AC_CHARGE][y,t]+vCAPRES_AC_CHARGE[y,t]) for y in AC_CHARGE)))
+ sum(derating_factor(gen[y],tag=res)*by_rid(y,:etainverter)*(EP[:vP_DC_DISCHARGE][y,t]) for y in DC_DISCHARGE)
+ sum(derating_factor(gen[y],tag=res)*(EP[:vP_AC_DISCHARGE][y,t]) for y in AC_DISCHARGE)
- sum(derating_factor(gen[y],tag=res)*(EP[:vP_DC_CHARGE][y,t])/by_rid(y,:etainverter) for y in DC_CHARGE)
- sum(derating_factor(gen[y],tag=res)*(EP[:vP_AC_CHARGE][y,t]) for y in AC_CHARGE)))
if StorageVirtualDischarge > 0
@expression(EP, eCapResMarBalanceStor_VRE_STOR_Virtual[res=1:inputs["NCapacityReserveMargin"], t=1:T],(
sum(derating_factor(gen[y],tag=res)*by_rid(y,:etainverter)*(vCAPRES_DC_DISCHARGE[y,t]) for y in DC_DISCHARGE)
+ sum(derating_factor(gen[y],tag=res)*(vCAPRES_AC_DISCHARGE[y,t]) for y in AC_DISCHARGE)
- sum(derating_factor(gen[y],tag=res)*(vCAPRES_DC_CHARGE[y,t])/by_rid(y,:etainverter) for y in DC_CHARGE)
- sum(derating_factor(gen[y],tag=res)*(vCAPRES_AC_CHARGE[y,t]) for y in AC_CHARGE)))
add_similar_to_expression!(eCapResMarBalanceStor_VRE_STOR,eCapResMarBalanceStor_VRE_STOR_Virtual)
end
EP[:eCapResMarBalance] += EP[:eCapResMarBalanceStor_VRE_STOR]

### OBJECTIVE FUNCTION ADDITIONS ###
Expand Down

0 comments on commit babed03

Please sign in to comment.