From 65e25d798a742de26bef3582adb474be767026a1 Mon Sep 17 00:00:00 2001
From: ql0320 <46303054+qluo0320github@users.noreply.github.com>
Date: Mon, 18 Nov 2024 12:01:21 -0500
Subject: [PATCH 01/17] Add new charging capacity constraint to VRE_STOR (#770)
---
CHANGELOG.md | 4 ++++
Project.toml | 2 +-
src/model/resources/vre_stor/vre_stor.jl | 18 +++++++++++++++++-
3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4cfb0dcbda..32f942db70 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,10 @@ number of concurrent Gurobi uses is limited (#783).
charge and storage variables (#760 and #763).
- Deduplicated docs on optimized scheduled maintenance for thermal resources (#745).
+### Fixed
+- Add constraint to ensure that electricity charged from the grid cannot exceed
+the charging capacity of the storage component in VRE_STOR (#770).
+
## [0.4.1] - 2024-08-20
### Added
diff --git a/Project.toml b/Project.toml
index 6cbaf9b300..4cfd601cfe 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.9"
+version = "0.4.1-dev.10"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/src/model/resources/vre_stor/vre_stor.jl b/src/model/resources/vre_stor/vre_stor.jl
index c367199a67..2e93bbc024 100644
--- a/src/model/resources/vre_stor/vre_stor.jl
+++ b/src/model/resources/vre_stor/vre_stor.jl
@@ -927,12 +927,20 @@ The following two constraints track the state of charge of the storage resources
\end{aligned}
```
-The last constraint limits the volume of energy stored at any time, $\Gamma_{y,z,t}$, to be less than the installed energy storage capacity, $\Delta^{total, energy}_{y,z}$.
+This constraint limits the volume of energy stored at any time, $\Gamma_{y,z,t}$, to be less than the installed energy storage capacity, $\Delta^{total, energy}_{y,z}$.
```math
\begin{aligned}
& \Gamma_{y,z,t} \leq \Delta^{total, energy}_{y,z} & \quad \forall y \in \mathcal{VS}^{stor}, z \in \mathcal{Z}, t \in \mathcal{T}
\end{aligned}
```
+
+The last constraint limits the volume of energy exported from the grid to the storage at any time, $\Pi_{y,z,t}$, to be less than the electricity charged to the energy storage component, $\Pi_{y,z,t}^{ac} + \frac{\Pi^{dc}_{y,z,t}}{\eta^{inverter}_{y,z}}$.
+```math
+\begin{aligned}
+ & \Pi_{y,z,t} = \Pi_{y,z,t}^{ac} + \frac{\Pi^{dc}_{y,z,t}}{\eta^{inverter}_{y,z}}
+\end{aligned}
+```
+
The next set of constraints only apply to symmetric storage resources (all $y \in \mathcal{VS}^{sym,dc} \cup y \in \mathcal{VS}^{sym,ac}$).
For storage technologies with symmetric charge and discharge capacity (all $y \in \mathcal{VS}^{sym,dc} \cup y \in \mathcal{VS}^{sym,ac}$),
since storage resources generally represent a 'cluster' of multiple similar storage devices of the same type/cost in the same zone, GenX
@@ -1131,6 +1139,9 @@ function stor_vre_stor!(EP::Model, inputs::Dict, setup::Dict)
CONSTRAINTSET = STOR
end
+ # total charging expressions: total storage charge (including both AC and DC) [MWh]
+ @expression(EP, eCHARGE_VS_STOR[y in STOR, t = 1:T], JuMP.AffExpr())
+
# SoC expressions
@expression(EP, eSoCBalStart_VRE_STOR[y in CONSTRAINTSET, t in START_SUBPERIODS],
vS_VRE_STOR[y,
@@ -1180,6 +1191,7 @@ function stor_vre_stor!(EP::Model, inputs::Dict, setup::Dict)
by_rid(y, :etainverter) for t in 1:T)
for t in 1:T
EP[:eInvACBalance][y, t] -= vP_DC_CHARGE[y, t] / by_rid(y, :etainverter)
+ EP[:eCHARGE_VS_STOR][y, t] += vP_DC_CHARGE[y, t] / by_rid(y, :etainverter)
EP[:eInverterExport][y, t] += vP_DC_CHARGE[y, t] / by_rid(y, :etainverter)
end
for t in INTERIOR_SUBPERIODS
@@ -1204,6 +1216,7 @@ function stor_vre_stor!(EP::Model, inputs::Dict, setup::Dict)
EP[:eELOSS_VRE_STOR][y] += sum(inputs["omega"][t] * vP_AC_CHARGE[y, t] for t in 1:T)
for t in 1:T
EP[:eInvACBalance][y, t] -= vP_AC_CHARGE[y, t]
+ EP[:eCHARGE_VS_STOR][y, t] += vP_AC_CHARGE[y, t]
end
for t in INTERIOR_SUBPERIODS
eSoCBalInterior_VRE_STOR[y, t] += by_rid(y, :eff_up_ac) *
@@ -1286,6 +1299,9 @@ function stor_vre_stor!(EP::Model, inputs::Dict, setup::Dict)
if rep_periods > 1 && !isempty(VS_LDS)
lds_vre_stor!(EP, inputs)
end
+
+ # Constraint 4: electricity charged from the grid cannot exceed the charging capacity of the storage component in VRE_STOR
+ @constraint(EP, [y in STOR, t = 1:T], vCHARGE_VRE_STOR[y,t] <= eCHARGE_VS_STOR[y,t])
end
@doc raw"""
From 6fabc96908c57ead2fa1e44e21cab73fbd12e431 Mon Sep 17 00:00:00 2001
From: federicoparolin <104208378+federicoparolin@users.noreply.github.com>
Date: Mon, 18 Nov 2024 18:27:03 +0100
Subject: [PATCH 02/17] Additional constraints to prevent violation of state of
charge limits in non-representative periods (#781)
---
CHANGELOG.md | 2 +
Project.toml | 2 +-
.../hydro/hydro_inter_period_linkage.jl | 66 +++++++++++++++++++
.../storage/long_duration_storage.jl | 65 +++++++++++++++++-
.../write_opwrap_lds_stor_init.jl | 50 ++++++++++++++
5 files changed, 183 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32f942db70..2dc2de7b9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fusion plant optional features for thermal plants (#743).
- Support for reusing the same Gurobi environment for multiple solves when
number of concurrent Gurobi uses is limited (#783).
+- Additional long-duration storage constraints to bound state of charge in
+non-representative periods (#781).
### Changed
- The `charge.csv` and `storage.csv` files now include only resources with
diff --git a/Project.toml b/Project.toml
index 4cfd601cfe..709dd79860 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.10"
+version = "0.4.1-dev.11"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/src/model/resources/hydro/hydro_inter_period_linkage.jl b/src/model/resources/hydro/hydro_inter_period_linkage.jl
index c1812173b4..e1ca04e975 100644
--- a/src/model/resources/hydro/hydro_inter_period_linkage.jl
+++ b/src/model/resources/hydro/hydro_inter_period_linkage.jl
@@ -42,6 +42,43 @@ Finally, the next constraint enforces that the initial storage level for each in
\quad \forall n \in \mathcal{N}, o \in \mathcal{O}^{LDES}
\end{aligned}
```
+
+**Bound storage inventory in non-representative periods**
+We need additional variables and constraints to ensure that the storage content is within zero and the installed energy capacity in non-representative periods. We introduce
+the variables $\Delta Q^{max,pos}_{o,z,m}$ and $\Delta Q^{max,neg}_{o,z,m}$ that represent the maximum positive and negative storage content variations within the representative
+period $m$, respectively, extracted as:
+
+```math
+\begin{aligned}
+& \Delta Q^{max,pos}_{o,z,m} \geq \Gamma_{o,z,(m-1)\times \tau^{period}+t } - \Gamma_{o,z,(m-1)\times \tau^{period}+1 } \quad \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, m \in \mathcal{M}, t \in \mathcal{T}
+& \end{aligned}
+```
+
+```math
+\begin{aligned}
+& \Delta Q^{max,neg}_{o,z,m} \leq \Gamma_{o,z,(m-1)\times \tau^{period}+t } - \Gamma_{o,z,(m-1)\times \tau^{period}+1 } \quad \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, m \in \mathcal{M}, t \in \mathcal{T}
+& \end{aligned}
+```
+
+For every input period $n \in \mathcal{N}$, the maximum storage is computed and constrained to be less than or equal to the installed energy capacity as:
+
+```math
+\begin{aligned}
+& Q_{o,z,n} \times \left(1-\eta_{o,z}^{loss}\right) - \frac{1}{\eta_{o,z}^{discharge}}\Theta_{o,z,(m-1)\times \tau^{period}+1} + \eta_{o,z}^{charge}\Pi_{o,z,(m-1)\times \tau^{period}+1} + \Delta Q^{max,pos}_{o,z,f(n)} \leq \Delta^{total, energy}_{o,z} \\
+& \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, n \in \mathcal{N}
+& \end{aligned}
+```
+
+Similarly, the minimum storage content is imposed to be positive in every period of the time horizon:
+
+```math
+\begin{aligned}
+& Q_{o,z,n} \times \left(1-\eta_{o,z}^{loss}\right) - \frac{1}{\eta_{o,z}^{discharge}}\Theta_{o,z,(m-1)\times \tau^{period}+1} + \eta_{o,z}^{charge}\Pi_{o,z,(m-1)\times \tau^{period}+1} + \Delta Q^{max,neg}_{o,z,f(n)} \geq 0 \\
+& \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, n \in \mathcal{N}
+& \end{aligned}
+```
+
+Additional details on this approach are available in [Parolin et al., 2024](https://doi.org/10.48550/arXiv.2409.19079).
"""
function hydro_inter_period_linkage!(EP::Model, inputs::Dict)
println("Long Duration Storage Module for Hydro Reservoir")
@@ -71,6 +108,12 @@ function hydro_inter_period_linkage!(EP::Model, inputs::Dict)
# Build up inventory can be positive or negative
@variable(EP, vdSOC_HYDRO[y in STOR_HYDRO_LONG_DURATION, w = 1:REP_PERIOD])
+ # Maximum positive storage inventory change within subperiod
+ @variable(EP, vdSOC_maxPos_HYDRO[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD] >= 0)
+
+ # Maximum negative storage inventory change within subperiod
+ @variable(EP, vdSOC_maxNeg_HYDRO[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD] <= 0)
+
### Constraints ###
# Links last time step with first time step, ensuring position in hour 1 is within eligible change from final hour position
@@ -111,4 +154,27 @@ function hydro_inter_period_linkage!(EP::Model, inputs::Dict)
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]])
+
+ # Extract maximum storage level variation (positive) within subperiod
+ @constraint(EP, cMaxSoCVarPos_H[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
+ vdSOC_maxPos_HYDRO[y,w] >= EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+t] - EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+1])
+
+ # Extract maximum storage level variation (negative) within subperiod
+ @constraint(EP, cMaxSoCVarNeg_H[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
+ vdSOC_maxNeg_HYDRO[y,w] <= EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+t] - EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+1])
+
+ # Max storage content within each modeled period cannot exceed installed energy capacity
+ @constraint(EP, cSoCLongDurationStorageMaxInt_H[y in STOR_HYDRO_LONG_DURATION, r in MODELED_PERIODS_INDEX],
+ vSOC_HYDROw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ -EP[:vSPILL][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]
+ +inputs["pP_Max"][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]*EP[:eTotalCap][y]
+ +vdSOC_maxPos_HYDRO[y,dfPeriodMap[r,:Rep_Period_Index]] <= hydro_energy_to_power_ratio(gen[y])*EP[:eTotalCap][y])
+
+ # Min storage content within each modeled period cannot be negative
+ @constraint(EP, cSoCLongDurationStorageMinInt_H[y in STOR_HYDRO_LONG_DURATION, r in MODELED_PERIODS_INDEX],
+ vSOC_HYDROw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ -EP[:vSPILL][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]
+ +inputs["pP_Max"][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]*EP[:eTotalCap][y]
+ +vdSOC_maxPos_HYDRO[y,dfPeriodMap[r,:Rep_Period_Index]] >= 0)
+
end
diff --git a/src/model/resources/storage/long_duration_storage.jl b/src/model/resources/storage/long_duration_storage.jl
index 3730d3dff1..05c2e56b1e 100644
--- a/src/model/resources/storage/long_duration_storage.jl
+++ b/src/model/resources/storage/long_duration_storage.jl
@@ -55,7 +55,44 @@ If the capacity reserve margin constraint is enabled, a similar set of constrain
& \frac{1}{\eta_{o,z}^{discharge}}\Theta^{CRM}_{o,z,(m-1)\times \tau^{period}+1} - \eta_{o,z}^{charge}\Pi^{CRM}_{o,z,(m-1)\times \tau^{period}+1} \quad \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, m \in \mathcal{M}
\end{aligned}
```
-All other constraints are identical to those used to track the actual state of charge, except with the new variables $Q^{CRM}_{o,z,n}$ and $\Delta Q^{CRM}_{o,z,n}$ used in place of $Q_{o,z,n}$ and $\Delta Q_{o,z,n}$, respectively.
+All other constraints are identical to those used to track the actual state of charge, except with the new variables $Q^{CRM}_{o,z,n}$ and $\Delta Q^{CRM}_{o,z,n}$ used in place of $Q_{o,z,n}$ and $\Delta Q_{o,z,n}$, respectively. \
+
+**Bound storage inventory in non-representative periods**
+We need additional variables and constraints to ensure that the storage content is within zero and the installed energy capacity in non-representative periods. We introduce
+the variables $\Delta Q^{max,pos}_{o,z,m}$ and $\Delta Q^{max,neg}_{o,z,m}$ that represent the maximum positive and negative storage content variations within the representative
+period $m$, respectively, extracted as:
+
+```math
+\begin{aligned}
+& \Delta Q^{max,pos}_{o,z,m} \geq \Gamma_{o,z,(m-1)\times \tau^{period}+t } - \Gamma_{o,z,(m-1)\times \tau^{period}+1 } \quad \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, m \in \mathcal{M}, t \in \mathcal{T}
+& \end{aligned}
+```
+
+```math
+\begin{aligned}
+& \Delta Q^{max,neg}_{o,z,m} \leq \Gamma_{o,z,(m-1)\times \tau^{period}+t } - \Gamma_{o,z,(m-1)\times \tau^{period}+1 } \quad \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, m \in \mathcal{M}, t \in \mathcal{T}
+& \end{aligned}
+```
+
+For every input period $n \in \mathcal{N}$, the maximum storage is computed and constrained to be less than or equal to the installed energy capacity as:
+
+```math
+\begin{aligned}
+& Q_{o,z,n} \times \left(1-\eta_{o,z}^{loss}\right) - \frac{1}{\eta_{o,z}^{discharge}}\Theta_{o,z,(m-1)\times \tau^{period}+1} + \eta_{o,z}^{charge}\Pi_{o,z,(m-1)\times \tau^{period}+1} + \Delta Q^{max,pos}_{o,z,f(n)} \leq \Delta^{total, energy}_{o,z} \\
+& \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, n \in \mathcal{N}
+& \end{aligned}
+```
+
+Similarly, the minimum storage content is imposed to be positive in every period of the time horizon:
+
+```math
+\begin{aligned}
+& Q_{o,z,n} \times \left(1-\eta_{o,z}^{loss}\right) - \frac{1}{\eta_{o,z}^{discharge}}\Theta_{o,z,(m-1)\times \tau^{period}+1} + \eta_{o,z}^{charge}\Pi_{o,z,(m-1)\times \tau^{period}+1} + \Delta Q^{max,neg}_{o,z,f(n)} \geq 0 \\
+& \forall o \in \mathcal{O}^{LDES}, z \in \mathcal{Z}, n \in \mathcal{N}
+& \end{aligned}
+```
+
+Additional details on this approach are available in [Parolin et al., 2024](https://doi.org/10.48550/arXiv.2409.19079).
"""
function long_duration_storage!(EP::Model, inputs::Dict, setup::Dict)
println("Long Duration Storage Module")
@@ -96,6 +133,12 @@ function long_duration_storage!(EP::Model, inputs::Dict, setup::Dict)
@variable(EP, vCAPRES_dsoc[y in STOR_LONG_DURATION, w = 1:REP_PERIOD])
end
+ # Maximum positive storage inventory change within subperiod
+ @variable(EP, vdSOC_maxPos[y in STOR_LONG_DURATION, w=1:REP_PERIOD] >= 0)
+
+ # Maximum negative storage inventory change within subperiod
+ @variable(EP, vdSOC_maxNeg[y in STOR_LONG_DURATION, w=1:REP_PERIOD] <= 0)
+
### Constraints ###
# Links last time step with first time step, ensuring position in hour 1 is within eligible change from final hour position
@@ -181,4 +224,24 @@ function long_duration_storage!(EP::Model, inputs::Dict, setup::Dict)
r in MODELED_PERIODS_INDEX],
vSOCw[y, r]>=vCAPRES_socw[y, r])
end
+
+ # Extract maximum storage level variation (positive) within subperiod
+ @constraint(EP, cMaxSoCVarPos[y in STOR_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
+ vdSOC_maxPos[y,w] >= EP[:vS][y,hours_per_subperiod*(w-1)+t] - EP[:vS][y,hours_per_subperiod*(w-1)+1])
+
+ # Extract maximum storage level variation (negative) within subperiod
+ @constraint(EP, cMaxSoCVarNeg[y in STOR_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
+ vdSOC_maxNeg[y,w] <= EP[:vS][y,hours_per_subperiod*(w-1)+t] - EP[:vS][y,hours_per_subperiod*(w-1)+1])
+
+ # Max storage content within each modeled period cannot exceed installed energy capacity
+ @constraint(EP, cSoCLongDurationStorageMaxInt[y in STOR_LONG_DURATION, r in MODELED_PERIODS_INDEX],
+ (1-self_discharge(gen[y]))*vSOCw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ +(efficiency_up(gen[y])*EP[:vCHARGE][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ +vdSOC_maxPos[y,dfPeriodMap[r,:Rep_Period_Index]] <= EP[:eTotalCapEnergy][y])
+
+ # Min storage content within each modeled period cannot be negative
+ @constraint(EP, cSoCLongDurationStorageMinInt[y in STOR_LONG_DURATION, r in MODELED_PERIODS_INDEX],
+ (1-self_discharge(gen[y]))*vSOCw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ +(efficiency_up(gen[y])*EP[:vCHARGE][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ +vdSOC_maxNeg[y,dfPeriodMap[r,:Rep_Period_Index]] >= 0)
end
diff --git a/src/write_outputs/long_duration_storage/write_opwrap_lds_stor_init.jl b/src/write_outputs/long_duration_storage/write_opwrap_lds_stor_init.jl
index 66c8ed7efb..e0a731d177 100644
--- a/src/write_outputs/long_duration_storage/write_opwrap_lds_stor_init.jl
+++ b/src/write_outputs/long_duration_storage/write_opwrap_lds_stor_init.jl
@@ -32,4 +32,54 @@ function write_opwrap_lds_stor_init(path::AbstractString,
CSV.write(joinpath(path, "StorageInit.csv"),
dftranspose(dfStorageInit, false),
header = false)
+
+ # Write storage evolution over full time horizon
+ hours_per_subperiod = inputs["hours_per_subperiod"];
+ t_interior = 2:hours_per_subperiod
+ T_hor = hours_per_subperiod*NPeriods # total number of time steps in time horizon
+ SOC_t = zeros(G, T_hor)
+ stor_long_duration = inputs["STOR_LONG_DURATION"]
+ stor_hydro_long_duration = inputs["STOR_HYDRO_LONG_DURATION"]
+ period_map = inputs["Period_Map"].Rep_Period_Index
+ pP_max = inputs["pP_Max"]
+ e_total_cap = value.(EP[:eTotalCap])
+ v_charge = value.(EP[:vCHARGE])
+ v_P = value.(EP[:vP])
+ if setup["ParameterScale"] == 1
+ v_charge *= ModelScalingFactor
+ v_P *= ModelScalingFactor
+ end
+ if !isempty(stor_hydro_long_duration)
+ v_spill = value.(EP[:vSPILL])
+ end
+ for r in 1:NPeriods
+ w = period_map[r]
+ t_r = hours_per_subperiod * (r - 1) + 1
+ t_start_w = hours_per_subperiod * (w - 1) + 1
+ t_interior = 2:hours_per_subperiod
+
+ if !isempty(stor_long_duration)
+ SOC_t[stor_long_duration, t_r] = socw[stor_long_duration, r] .* (1 .- self_discharge.(gen[stor_long_duration])) .+ efficiency_up.(gen[stor_long_duration]) .* v_charge[stor_long_duration, t_start_w] .- 1 ./ efficiency_down.(gen[stor_long_duration]) .* v_P[stor_long_duration, t_start_w]
+
+ for t_int in t_interior
+ t = hours_per_subperiod * (w - 1) + t_int
+ SOC_t[stor_long_duration, t_r + t_int - 1] = SOC_t[stor_long_duration, t_r + t_int - 2] .* (1 .- self_discharge.(gen[stor_long_duration])) .+ efficiency_up.(gen[stor_long_duration]) .* v_charge[stor_long_duration, t] .- 1 ./ efficiency_down.(gen[stor_long_duration]) .* v_P[stor_long_duration, t]
+ end
+ end
+
+ if !isempty(stor_hydro_long_duration)
+ SOC_t[stor_hydro_long_duration, t_r] = socw[stor_hydro_long_duration, r] .- 1 ./ efficiency_down.(gen[stor_long_duration]) .* v_P[stor_hydro_long_duration, t_start_w] .- v_spill[stor_hydro_long_duration, t_start_w] .+ pP_max[stor_hydro_long_duration, t_start_w] .* e_total_cap[stor_hydro_long_duration]
+
+ for t_int in t_interior
+ t = hours_per_subperiod * (w - 1) + t_int
+ SOC_t[stor_hydro_long_duration, t_r + t_int - 1] = SOC_t[stor_hydro_long_duration, t_r + t_int - 2] .- 1 ./ efficiency_down.(gen[stor_long_duration]) .* v_P[stor_hydro_long_duration, t] .- v_spill[stor_hydro_long_duration, t] .+ pP_max[stor_hydro_long_duration, t] .* e_total_cap[stor_hydro_long_duration]
+ end
+ end
+ end
+ df_SOC_t = DataFrame(Resource = inputs["RESOURCE_NAMES"], Zone = zones)
+ df_SOC_t = hcat(df_SOC_t, DataFrame(SOC_t, :auto))
+ auxNew_Names = [Symbol("Resource"); Symbol("Zone"); [Symbol("n$t") for t in 1:T_hor]]
+ rename!(df_SOC_t,auxNew_Names)
+ CSV.write(joinpath(path, "StorageEvol.csv"), dftranspose(df_SOC_t, false), writeheader=false)
+
end
From 7147493e1535dbe892619c84ab6ba9055be3b658 Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Mon, 18 Nov 2024 12:54:14 -0500
Subject: [PATCH 03/17] Fix getproperty for `Vector{Resources}` to align with
updated array interface (#785)
---
CHANGELOG.md | 2 +
Project.toml | 2 +-
src/case_runners/case_runner.jl | 1 +
src/load_inputs/load_resources_data.jl | 4 +-
src/model/resources/resources.jl | 24 +++---
src/startup/genx_startup.jl | 4 -
.../write_reserve_margin_revenue.jl | 35 +++------
.../write_esr_revenue.jl | 39 +++-------
src/write_outputs/write_curtailment.jl | 9 +--
src/write_outputs/write_net_revenue.jl | 75 ++++++++-----------
src/write_outputs/write_subsidy_revenue.jl | 54 +++++--------
src/write_outputs/write_vre_stor.jl | 6 +-
12 files changed, 93 insertions(+), 162 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2dc2de7b9a..477b0211c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,8 @@ charge and storage variables (#760 and #763).
### Fixed
- Add constraint to ensure that electricity charged from the grid cannot exceed
the charging capacity of the storage component in VRE_STOR (#770).
+- Update `getproperty` function for vectors of resources to ensure compatibility
+with Julia v1.11 (#785).
## [0.4.1] - 2024-08-20
diff --git a/Project.toml b/Project.toml
index 709dd79860..f74ef99f28 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.11"
+version = "0.4.1-dev.12"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/src/case_runners/case_runner.jl b/src/case_runners/case_runner.jl
index 84e2726e52..afea227f29 100644
--- a/src/case_runners/case_runner.jl
+++ b/src/case_runners/case_runner.jl
@@ -29,6 +29,7 @@ run_genx_case!("path/to/case", Gurobi.Optimizer)
```
"""
function run_genx_case!(case::AbstractString, optimizer::Any = HiGHS.Optimizer)
+ print_genx_version() # Log the GenX version
genx_settings = get_settings_path(case, "genx_settings.yml") # Settings YAML file path
writeoutput_settings = get_settings_path(case, "output_settings.yml") # Write-output settings YAML file path
mysetup = configure_settings(genx_settings, writeoutput_settings) # mysetup dictionary stores settings and GenX-specific parameters
diff --git a/src/load_inputs/load_resources_data.jl b/src/load_inputs/load_resources_data.jl
index a88c48db6e..7966038a0a 100644
--- a/src/load_inputs/load_resources_data.jl
+++ b/src/load_inputs/load_resources_data.jl
@@ -522,7 +522,9 @@ function check_retrofit_id(rs::Vector{T}) where {T <: AbstractResource}
retrofit_options = ids_retrofit_options(rs)
# check that all retrofit_ids for resources that can retrofit and retrofit options match
- if Set(rs[units_can_retrofit].retrofit_id) != Set(rs[retrofit_options].retrofit_id)
+ can_retrofit_retro_ids = [r.retrofit_id for r in rs[units_can_retrofit]] # retrofit cluster ids for units that can retrofit
+ retrofit_option_retro_ids = [r.retrofit_id for r in rs[retrofit_options]] # retrofit cluster ids for retrofit options
+ if Set(can_retrofit_retro_ids) != Set(retrofit_option_retro_ids)
msg = string("Retrofit IDs for resources that \"can retrofit\" and \"retrofit options\" do not match.\n" *
"All retrofitting units must be associated with a retrofit option.")
push!(warning_strings, msg)
diff --git a/src/model/resources/resources.jl b/src/model/resources/resources.jl
index 671f90dd01..7f42700bfd 100644
--- a/src/model/resources/resources.jl
+++ b/src/model/resources/resources.jl
@@ -66,9 +66,7 @@ Allows to set the attribute `sym` of an `AbstractResource` object using dot synt
- `value`: The value to set for the attribute.
"""
-Base.setproperty!(r::AbstractResource, sym::Symbol, value) = setindex!(parent(r),
- value,
- sym)
+Base.setproperty!(r::AbstractResource, sym::Symbol, value) = setindex!(parent(r), value, sym)
"""
haskey(r::AbstractResource, sym::Symbol)
@@ -106,32 +104,31 @@ end
"""
Base.getproperty(rs::Vector{<:AbstractResource}, sym::Symbol)
-Allows to access attributes of a vector of `AbstractResource` objects using dot syntax. If the `sym` is an element of the `resource_types` constant, it returns all resources of that type. Otherwise, it returns the value of the attribute for each resource in the vector.
+Allows to return all resources of a given type of a vector of `AbstractResource` objects using dot syntax.
# Arguments:
- `rs::Vector{<:AbstractResource}`: The vector of `AbstractResource` objects.
-- `sym::Symbol`: The symbol representing the attribute name or a type from `resource_types`.
+- `sym::Symbol`: The symbol representing the type from `resource_types`.
# Returns:
- If `sym` is an element of the `resource_types` constant, it returns a vector containing all resources of that type.
-- If `sym` is an attribute name, it returns a vector containing the value of the attribute for each resource.
## Examples
```julia
julia> vre_gen = gen.Vre; # gen vector of resources
julia> typeof(vre_gen)
Vector{Vre} (alias for Array{Vre, 1})
-julia> vre_gen.zone
+julia> GenX.zone_id.(vre_gen)
```
"""
function Base.getproperty(rs::Vector{<:AbstractResource}, sym::Symbol)
- # if sym is Type then return a vector resources of that type
+ # if sym is one of the resource types then return a vector resources of that type
if sym ∈ resource_types
res_type = eval(sym)
return Vector{res_type}(rs[isa.(rs, res_type)])
+ else
+ return getfield(rs, sym)
end
- # if sym is a field of the resource then return that field for all resources
- return [getproperty(r, sym) for r in rs]
end
"""
@@ -255,8 +252,7 @@ julia> findall(r -> max_cap_mwh(r) != 0, gen.Storage)
50
```
"""
-Base.findall(f::Function, rs::Vector{<:AbstractResource}) = resource_id.(filter(r -> f(r),
- rs))
+Base.findall(f::Function, rs::Vector{<:AbstractResource}) = resource_id.(filter(r -> f(r), rs))
"""
interface(name, default=default_zero, type=AbstractResource)
@@ -531,14 +527,14 @@ const default_zero = 0
# INTERFACE FOR ALL RESOURCES
resource_name(r::AbstractResource) = r.resource
-resource_name(rs::Vector{T}) where {T <: AbstractResource} = rs.resource
+resource_name(rs::Vector{T}) where {T <: AbstractResource} = resource_name.(rs)
resource_id(r::AbstractResource)::Int64 = r.id
resource_id(rs::Vector{T}) where {T <: AbstractResource} = resource_id.(rs)
resource_type_mga(r::AbstractResource) = r.resource_type
zone_id(r::AbstractResource) = r.zone
-zone_id(rs::Vector{T}) where {T <: AbstractResource} = rs.zone
+zone_id(rs::Vector{T}) where {T <: AbstractResource} = zone_id.(rs)
# getter for boolean attributes (true or false) with validation
function new_build(r::AbstractResource)
diff --git a/src/startup/genx_startup.jl b/src/startup/genx_startup.jl
index 58f1e7ba70..847af0b8b1 100644
--- a/src/startup/genx_startup.jl
+++ b/src/startup/genx_startup.jl
@@ -1,7 +1,3 @@
-function __init__()
- print_genx_version()
-end
-
function print_genx_version()
v = pkgversion(GenX)
ascii_art = raw"""
diff --git a/src/write_outputs/capacity_reserve_margin/write_reserve_margin_revenue.jl b/src/write_outputs/capacity_reserve_margin/write_reserve_margin_revenue.jl
index 707147556b..418b14eeab 100644
--- a/src/write_outputs/capacity_reserve_margin/write_reserve_margin_revenue.jl
+++ b/src/write_outputs/capacity_reserve_margin/write_reserve_margin_revenue.jl
@@ -73,29 +73,18 @@ function write_reserve_margin_revenue(path::AbstractString,
gen_VRE_STOR = gen.VreStorage
tempresrev[VRE_STOR] = derating_factor.(gen_VRE_STOR, tag = i) .*
((value.(EP[:vP][VRE_STOR, :])) * weighted_price)
- tempresrev[VRE_STOR_STOR] .-= derating_factor.(
- gen_VRE_STOR[(gen_VRE_STOR.stor_dc_discharge .!= 0) .| (gen_VRE_STOR.stor_dc_charge .!= 0) .| (gen_VRE_STOR.stor_ac_discharge .!= 0) .| (gen_VRE_STOR.stor_ac_charge .!= 0)],
- tag = i) .* (value.(EP[:vCHARGE_VRE_STOR][VRE_STOR_STOR,
- :]).data * weighted_price)
- tempresrev[DC_DISCHARGE] .+= derating_factor.(
- gen_VRE_STOR[(gen_VRE_STOR.stor_dc_discharge .!= 0)],
- tag = i) .* ((value.(EP[:vCAPRES_DC_DISCHARGE][DC_DISCHARGE,
- :]).data .*
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.stor_dc_discharge .!= 0)])) *
- weighted_price)
- tempresrev[AC_DISCHARGE] .+= derating_factor.(
- gen_VRE_STOR[(gen_VRE_STOR.stor_ac_discharge .!= 0)],
- tag = i) .* ((value.(EP[:vCAPRES_AC_DISCHARGE][AC_DISCHARGE,
- :]).data) * weighted_price)
- tempresrev[DC_CHARGE] .-= derating_factor.(
- gen_VRE_STOR[(gen_VRE_STOR.stor_dc_charge .!= 0)],
- tag = i) .* ((value.(EP[:vCAPRES_DC_CHARGE][DC_CHARGE, :]).data ./
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.stor_dc_charge .!= 0)])) *
- weighted_price)
- tempresrev[AC_CHARGE] .-= derating_factor.(
- gen_VRE_STOR[(gen_VRE_STOR.stor_ac_charge .!= 0)],
- tag = i) .* ((value.(EP[:vCAPRES_AC_CHARGE][AC_CHARGE, :]).data) *
- weighted_price)
+ tempresrev[VRE_STOR_STOR] .-= derating_factor.(gen[VRE_STOR_STOR], tag = i) .*
+ (value.(EP[:vCHARGE_VRE_STOR][VRE_STOR_STOR, :]).data * weighted_price)
+ tempresrev[DC_DISCHARGE] .+= derating_factor.(gen[DC_DISCHARGE], tag = i) .*
+ ((value.(EP[:vCAPRES_DC_DISCHARGE][DC_DISCHARGE, :]).data .*
+ etainverter.(gen[DC_DISCHARGE])) * weighted_price)
+ tempresrev[AC_DISCHARGE] .+= derating_factor.(gen[AC_DISCHARGE], tag = i) .*
+ ((value.(EP[:vCAPRES_AC_DISCHARGE][AC_DISCHARGE, :]).data) * weighted_price)
+ tempresrev[DC_CHARGE] .-= derating_factor.(gen[DC_CHARGE], tag = i) .*
+ ((value.(EP[:vCAPRES_DC_CHARGE][DC_CHARGE, :]).data ./
+ etainverter.(gen[DC_CHARGE])) * weighted_price)
+ tempresrev[AC_CHARGE] .-= derating_factor.(gen[AC_CHARGE], tag = i) .*
+ ((value.(EP[:vCAPRES_AC_CHARGE][AC_CHARGE, :]).data) * weighted_price)
end
tempresrev *= scale_factor
annual_sum .+= tempresrev
diff --git a/src/write_outputs/energy_share_requirement/write_esr_revenue.jl b/src/write_outputs/energy_share_requirement/write_esr_revenue.jl
index a9298663e8..580ee45837 100644
--- a/src/write_outputs/energy_share_requirement/write_esr_revenue.jl
+++ b/src/write_outputs/energy_share_requirement/write_esr_revenue.jl
@@ -47,39 +47,20 @@ function write_esr_revenue(path::AbstractString,
if !isempty(VRE_STOR)
if !isempty(SOLAR_ONLY)
- solar_resources = ((gen_VRE_STOR.wind .== 0) .& (gen_VRE_STOR.solar .!= 0))
- dfESRRev[SOLAR, esr_col] = (value.(EP[:vP_SOLAR][SOLAR, :]).data
- .*
- etainverter.(gen_VRE_STOR[solar_resources]) *
- weight) .*
- esr_vrestor.(gen_VRE_STOR[solar_resources],
- tag = i) * price
+ dfESRRev[SOLAR, esr_col] = (value.(EP[:vP_SOLAR][SOLAR, :]).data .*
+ etainverter.(gen[SOLAR]) * weight) .*
+ esr_vrestor.(gen[SOLAR], tag = i) * price
end
if !isempty(WIND_ONLY)
- wind_resources = ((gen_VRE_STOR.wind .!= 0) .& (gen_VRE_STOR.solar .== 0))
- dfESRRev[WIND, esr_col] = (value.(EP[:vP_WIND][WIND, :]).data
- *
- weight) .*
- esr_vrestor.(gen_VRE_STOR[wind_resources],
- tag = i) * price
+ dfESRRev[WIND, esr_col] = (value.(EP[:vP_WIND][WIND, :]).data * weight) .*
+ esr_vrestor.(gen[WIND], tag = i) * price
end
if !isempty(SOLAR_WIND)
- solar_and_wind_resources = ((gen_VRE_STOR.wind .!= 0) .&
- (gen_VRE_STOR.solar .!= 0))
- dfESRRev[SOLAR_WIND, esr_col] = (((value.(EP[:vP_WIND][SOLAR_WIND,
- :]).data * weight)
- .*
- esr_vrestor.(
- gen_VRE_STOR[solar_and_wind_resources],
- tag = i) * price) +
- (value.(EP[:vP_SOLAR][SOLAR_WIND, :]).data
- .*
- etainverter.(gen_VRE_STOR[solar_and_wind_resources])
- *
- weight) .*
- esr_vrestor.(
- gen_VRE_STOR[solar_and_wind_resources],
- tag = i) * price)
+ dfESRRev[SOLAR_WIND, esr_col] = (((value.(EP[:vP_WIND][SOLAR_WIND, :]).data * weight) .*
+ esr_vrestor.(gen[SOLAR_WIND], tag = i) * price) +
+ (value.(EP[:vP_SOLAR][SOLAR_WIND, :]).data .*
+ etainverter.(gen[SOLAR_WIND]) * weight) .*
+ esr_vrestor.(gen[SOLAR_WIND], tag = i) * price)
end
end
end
diff --git a/src/write_outputs/write_curtailment.jl b/src/write_outputs/write_curtailment.jl
index aaa9234e59..4d41289750 100644
--- a/src/write_outputs/write_curtailment.jl
+++ b/src/write_outputs/write_curtailment.jl
@@ -25,12 +25,11 @@ function write_curtailment(path::AbstractString, inputs::Dict, setup::Dict, EP::
SOLAR = setdiff(inputs["VS_SOLAR"], inputs["VS_WIND"])
WIND = setdiff(inputs["VS_WIND"], inputs["VS_SOLAR"])
SOLAR_WIND = intersect(inputs["VS_SOLAR"], inputs["VS_WIND"])
- gen_VRE_STOR = gen.VreStorage
if !isempty(SOLAR)
curtailment[SOLAR, :] = (value.(EP[:eTotalCap_SOLAR][SOLAR]).data .*
- inputs["pP_Max_Solar"][SOLAR, :] .-
- value.(EP[:vP_SOLAR][SOLAR, :]).data) .*
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.solar .!= 0)])
+ inputs["pP_Max_Solar"][SOLAR, :] .-
+ value.(EP[:vP_SOLAR][SOLAR, :]).data) .*
+ etainverter.(gen[SOLAR])
end
if !isempty(WIND)
curtailment[WIND, :] = (value.(EP[:eTotalCap_WIND][WIND]).data .*
@@ -41,7 +40,7 @@ function write_curtailment(path::AbstractString, inputs::Dict, setup::Dict, EP::
curtailment[SOLAR_WIND, :] = ((value.(EP[:eTotalCap_SOLAR])[SOLAR_WIND].data .*
inputs["pP_Max_Solar"][SOLAR_WIND, :] .-
value.(EP[:vP_SOLAR][SOLAR_WIND, :]).data) .*
- etainverter.(gen_VRE_STOR[((gen_VRE_STOR.wind .!= 0) .& (gen_VRE_STOR.solar .!= 0))])
+ etainverter.(gen[SOLAR_WIND])
+
(value.(EP[:eTotalCap_WIND][SOLAR_WIND]).data .*
inputs["pP_Max_Wind"][SOLAR_WIND, :] .-
diff --git a/src/write_outputs/write_net_revenue.jl b/src/write_outputs/write_net_revenue.jl
index ef42e9ca87..2311c8d1a4 100644
--- a/src/write_outputs/write_net_revenue.jl
+++ b/src/write_outputs/write_net_revenue.jl
@@ -87,39 +87,37 @@ function write_net_revenue(path::AbstractString,
dfCap[1:G, :EndEnergyCap]
dfNetRevenue.Fixed_OM_cost_charge_MW = fixed_om_cost_charge_per_mwyr.(gen) .*
dfCap[1:G, :EndChargeCap]
-
dfNetRevenue.Var_OM_cost_out = var_om_cost_per_mwh.(gen) .* dfPower[1:G, :AnnualSum]
if !isempty(VRE_STOR)
if !isempty(SOLAR)
dfNetRevenue.Fixed_OM_cost_MW[VRE_STOR] += fixed_om_solar_cost_per_mwyr.(gen_VRE_STOR) .*
dfVreStor[1:VRE_STOR_LENGTH,
:EndCapSolar]
- dfNetRevenue.Var_OM_cost_out[SOLAR] += var_om_cost_per_mwh_solar.(gen_VRE_STOR[(gen_VRE_STOR.solar .!= 0)]) .*
- (value.(EP[:vP_SOLAR][SOLAR, :]).data .*
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.solar .!= 0)]) *
+ dfNetRevenue.Var_OM_cost_out[SOLAR] += var_om_cost_per_mwh_solar.(gen[SOLAR]) .*
+ (value.(EP[:vP_SOLAR][SOLAR, :]).data .*
+ etainverter.(gen[SOLAR]) *
inputs["omega"])
end
if !isempty(WIND)
dfNetRevenue.Fixed_OM_cost_MW[VRE_STOR] += fixed_om_wind_cost_per_mwyr.(gen_VRE_STOR) .*
- dfVreStor[1:VRE_STOR_LENGTH,
- :EndCapWind]
- dfNetRevenue.Var_OM_cost_out[WIND] += var_om_cost_per_mwh_wind.(gen_VRE_STOR[(gen_VRE_STOR.wind .!= 0)]) .*
- (value.(EP[:vP_WIND][WIND, :]).data *
- inputs["omega"])
+ dfVreStor[1:VRE_STOR_LENGTH, :EndCapWind]
+
+ dfNetRevenue.Var_OM_cost_out[WIND] += var_om_cost_per_mwh_wind.(gen[WIND]) .*
+ (value.(EP[:vP_WIND][WIND, :]).data *
+ inputs["omega"])
end
if !isempty(DC)
dfNetRevenue.Fixed_OM_cost_MW[VRE_STOR] += fixed_om_inverter_cost_per_mwyr.(gen_VRE_STOR) .*
- dfVreStor[1:VRE_STOR_LENGTH,
- :EndCapDC]
+ dfVreStor[1:VRE_STOR_LENGTH, :EndCapDC]
end
if !isempty(DC_DISCHARGE)
- dfNetRevenue.Var_OM_cost_out[DC_DISCHARGE] += var_om_cost_per_mwh_discharge_dc.(gen_VRE_STOR[(gen_VRE_STOR.stor_dc_discharge .!= 0)]) .*
- (value.(EP[:vP_DC_DISCHARGE][DC_DISCHARGE,:]).data .*
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.stor_dc_discharge .!= 0)]) *
- inputs["omega"])
+ dfNetRevenue.Var_OM_cost_out[DC_DISCHARGE] += var_om_cost_per_mwh_discharge_dc.(gen[DC_DISCHARGE]) .*
+ (value.(EP[:vP_DC_DISCHARGE][DC_DISCHARGE,:]).data .*
+ etainverter.(gen[DC_DISCHARGE]) *
+ inputs["omega"])
end
if !isempty(AC_DISCHARGE)
- dfNetRevenue.Var_OM_cost_out[AC_DISCHARGE] += var_om_cost_per_mwh_discharge_ac.(gen_VRE_STOR[(gen_VRE_STOR.stor_ac_discharge .!= 0)]) .*
+ dfNetRevenue.Var_OM_cost_out[AC_DISCHARGE] += var_om_cost_per_mwh_discharge_ac.(gen[AC_DISCHARGE]) .*
(value.(EP[:vP_AC_DISCHARGE][AC_DISCHARGE,:]).data * inputs["omega"])
end
end
@@ -145,15 +143,14 @@ function write_net_revenue(path::AbstractString,
end
if !isempty(VRE_STOR)
if !isempty(DC_CHARGE)
- dfNetRevenue.Var_OM_cost_in[DC_CHARGE] += var_om_cost_per_mwh_charge_dc.(gen_VRE_STOR[(gen_VRE_STOR.stor_dc_charge .!= 0)]) .*
+ dfNetRevenue.Var_OM_cost_in[DC_CHARGE] += var_om_cost_per_mwh_charge_dc.(gen[DC_CHARGE]) .*
(value.(EP[:vP_DC_CHARGE][DC_CHARGE,:]).data ./
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.stor_dc_charge .!= 0)]) *
+ etainverter.(gen[DC_CHARGE]) *
inputs["omega"])
end
if !isempty(AC_CHARGE)
- dfNetRevenue.Var_OM_cost_in[AC_CHARGE] += var_om_cost_per_mwh_charge_ac.(gen_VRE_STOR[(gen_VRE_STOR.stor_ac_charge .!= 0)]) .*
- (value.(EP[:vP_AC_CHARGE][AC_CHARGE,
- :]).data * inputs["omega"])
+ dfNetRevenue.Var_OM_cost_in[AC_CHARGE] += var_om_cost_per_mwh_charge_ac.(gen[AC_CHARGE]) .*
+ (value.(EP[:vP_AC_CHARGE][AC_CHARGE, :]).data * inputs["omega"])
end
end
@@ -258,30 +255,18 @@ function write_net_revenue(path::AbstractString,
.+dfNetRevenue.OperatingReserveRevenue
.+dfNetRevenue.OperatingRegulationRevenue
- dfNetRevenue.Cost = (dfNetRevenue.Inv_cost_MW
- .+
- dfNetRevenue.Inv_cost_MWh
- .+
- dfNetRevenue.Inv_cost_charge_MW
- .+
- dfNetRevenue.Fixed_OM_cost_MW
- .+
- dfNetRevenue.Fixed_OM_cost_MWh
- .+
- dfNetRevenue.Fixed_OM_cost_charge_MW
- .+
- dfNetRevenue.Var_OM_cost_out
- .+
- dfNetRevenue.Var_OM_cost_in
- .+
- dfNetRevenue.Fuel_cost
- .+
- dfNetRevenue.Charge_cost
- .+
- dfNetRevenue.EmissionsCost
- .+
- dfNetRevenue.StartCost
- .+
+ dfNetRevenue.Cost = (dfNetRevenue.Inv_cost_MW .+
+ dfNetRevenue.Inv_cost_MWh .+
+ dfNetRevenue.Inv_cost_charge_MW .+
+ dfNetRevenue.Fixed_OM_cost_MW .+
+ dfNetRevenue.Fixed_OM_cost_MWh .+
+ dfNetRevenue.Fixed_OM_cost_charge_MW .+
+ dfNetRevenue.Var_OM_cost_out .+
+ dfNetRevenue.Var_OM_cost_in .+
+ dfNetRevenue.Fuel_cost .+
+ dfNetRevenue.Charge_cost .+
+ dfNetRevenue.EmissionsCost .+
+ dfNetRevenue.StartCost .+
dfNetRevenue.CO2SequestrationCost)
dfNetRevenue.Profit = dfNetRevenue.Revenue .- dfNetRevenue.Cost
diff --git a/src/write_outputs/write_subsidy_revenue.jl b/src/write_outputs/write_subsidy_revenue.jl
index f74bede14c..3262ec94d7 100644
--- a/src/write_outputs/write_subsidy_revenue.jl
+++ b/src/write_outputs/write_subsidy_revenue.jl
@@ -54,59 +54,39 @@ function write_subsidy_revenue(path::AbstractString, inputs::Dict, setup::Dict,
if !isempty(inputs["VRE_STOR"])
gen_VRE_STOR = gen.VreStorage
HAS_MIN_CAP_STOR = ids_with_policy(gen_VRE_STOR, min_cap_stor, tag = mincap)
- MIN_CAP_GEN_SOLAR = ids_with_policy(gen_VRE_STOR,
- min_cap_solar,
- tag = mincap)
+ MIN_CAP_GEN_SOLAR = ids_with_policy(gen_VRE_STOR, min_cap_solar, tag = mincap)
MIN_CAP_GEN_WIND = ids_with_policy(gen_VRE_STOR, min_cap_wind, tag = mincap)
- MIN_CAP_GEN_ASYM_DC_DIS = intersect(inputs["VS_ASYM_DC_DISCHARGE"],
- HAS_MIN_CAP_STOR)
- MIN_CAP_GEN_ASYM_AC_DIS = intersect(inputs["VS_ASYM_AC_DISCHARGE"],
- HAS_MIN_CAP_STOR)
+ MIN_CAP_GEN_ASYM_DC_DIS = intersect(inputs["VS_ASYM_DC_DISCHARGE"], HAS_MIN_CAP_STOR)
+ MIN_CAP_GEN_ASYM_AC_DIS = intersect(inputs["VS_ASYM_AC_DISCHARGE"], HAS_MIN_CAP_STOR)
MIN_CAP_GEN_SYM_DC = intersect(inputs["VS_SYM_DC"], HAS_MIN_CAP_STOR)
MIN_CAP_GEN_SYM_AC = intersect(inputs["VS_SYM_AC"], HAS_MIN_CAP_STOR)
if !isempty(MIN_CAP_GEN_SOLAR)
- dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_SOLAR] .+= ((value.(EP[:eTotalCap_SOLAR][MIN_CAP_GEN_SOLAR]).data)
- .*
- etainverter.(gen[ids_with_policy(
- gen,
- min_cap_solar,
- tag = mincap)])
- *
- (dual.(EP[:cZoneMinCapReq][mincap])))
+ dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_SOLAR] .+= ((value.(EP[:eTotalCap_SOLAR][MIN_CAP_GEN_SOLAR]).data) .*
+ etainverter.(gen[MIN_CAP_GEN_SOLAR]) *
+ (dual.(EP[:cZoneMinCapReq][mincap])))
end
if !isempty(MIN_CAP_GEN_WIND)
- dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_WIND] .+= ((value.(EP[:eTotalCap_WIND][MIN_CAP_GEN_WIND]).data)
- *
+ dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_WIND] .+= ((value.(EP[:eTotalCap_WIND][MIN_CAP_GEN_WIND]).data) *
(dual.(EP[:cZoneMinCapReq][mincap])))
end
if !isempty(MIN_CAP_GEN_ASYM_DC_DIS)
- MIN_CAP_GEN_ASYM_DC_DIS = intersect(inputs["VS_ASYM_DC_DISCHARGE"],
- HAS_MIN_CAP_STOR)
- dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_ASYM_DC_DIS] .+= ((value.(EP[:eTotalCapDischarge_DC][MIN_CAP_GEN_ASYM_DC_DIS].data)
- .*
- etainverter.(gen_VRE_STOR[min_cap_stor.(gen_VRE_STOR, tag = mincap) .== 1 .& (gen_VRE_STOR.stor_dc_discharge .== 2)]))
- *
- (dual.(EP[:cZoneMinCapReq][mincap])))
+ dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_ASYM_DC_DIS] .+= ((value.(EP[:eTotalCapDischarge_DC][MIN_CAP_GEN_ASYM_DC_DIS].data) .*
+ etainverter.(gen[MIN_CAP_GEN_ASYM_DC_DIS])) *
+ (dual.(EP[:cZoneMinCapReq][mincap])))
end
if !isempty(MIN_CAP_GEN_ASYM_AC_DIS)
- dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_ASYM_AC_DIS] .+= ((value.(EP[:eTotalCapDischarge_AC][MIN_CAP_GEN_ASYM_AC_DIS]).data)
- *
- (dual.(EP[:cZoneMinCapReq][mincap])))
+ dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_ASYM_AC_DIS] .+= ((value.(EP[:eTotalCapDischarge_AC][MIN_CAP_GEN_ASYM_AC_DIS]).data) *
+ (dual.(EP[:cZoneMinCapReq][mincap])))
end
if !isempty(MIN_CAP_GEN_SYM_DC)
- dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_SYM_DC] .+= ((value.(EP[:eTotalCap_STOR][MIN_CAP_GEN_SYM_DC]).data
- .*
- power_to_energy_dc.(gen_VRE_STOR[(min_cap_stor.(gen_VRE_STOR, tag = mincap) .== 1 .& (gen_VRE_STOR.stor_dc_discharge .== 1))])
- .*
- etainverter.(gen_VRE_STOR[(min_cap_stor.(gen_VRE_STOR, tag = mincap) .== 1 .& (gen_VRE_STOR.stor_dc_discharge .== 1))]))
- *
+ dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_SYM_DC] .+= ((value.(EP[:eTotalCap_STOR][MIN_CAP_GEN_SYM_DC]).data .*
+ power_to_energy_dc.(gen[MIN_CAP_GEN_SYM_DC]) .*
+ etainverter.(gen[MIN_CAP_GEN_SYM_DC])) *
(dual.(EP[:cZoneMinCapReq][mincap])))
end
if !isempty(MIN_CAP_GEN_SYM_AC)
- dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_SYM_AC] .+= ((value.(EP[:eTotalCap_STOR][MIN_CAP_GEN_SYM_AC]).data
- .*
- power_to_energy_ac.(gen_VRE_STOR[(min_cap_stor.(gen_VRE_STOR, tag = mincap) .== 1 .& (gen_VRE_STOR.stor_ac_discharge .== 1))]))
- *
+ dfRegSubRevenue.SubsidyRevenue[MIN_CAP_GEN_SYM_AC] .+= ((value.(EP[:eTotalCap_STOR][MIN_CAP_GEN_SYM_AC]).data .*
+ power_to_energy_ac.(gen[MIN_CAP_GEN_SYM_AC])) *
(dual.(EP[:cZoneMinCapReq][mincap])))
end
end
diff --git a/src/write_outputs/write_vre_stor.jl b/src/write_outputs/write_vre_stor.jl
index 26e1c72686..a50687c957 100644
--- a/src/write_outputs/write_vre_stor.jl
+++ b/src/write_outputs/write_vre_stor.jl
@@ -354,7 +354,7 @@ function write_vre_stor_charge(path::AbstractString, inputs::Dict, setup::Dict,
AnnualSum = Array{Union{Missing, Float32}}(undef, size(DC_CHARGE)[1]))
charge_dc = zeros(size(DC_CHARGE)[1], T)
charge_dc = value.(EP[:vP_DC_CHARGE]).data ./
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.stor_dc_discharge .!= 0)]) *
+ etainverter.(gen[DC_CHARGE]) *
(setup["ParameterScale"] == 1 ? ModelScalingFactor : 1)
dfCharge_DC.AnnualSum .= charge_dc * inputs["omega"]
@@ -410,7 +410,7 @@ function write_vre_stor_discharge(path::AbstractString,
Zone = inputs["ZONES_DC_DISCHARGE"],
AnnualSum = Array{Union{Missing, Float32}}(undef, size(DC_DISCHARGE)[1]))
power_vre_stor = value.(EP[:vP_DC_DISCHARGE]).data .*
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.stor_dc_discharge .!= 0)])
+ etainverter.(gen[DC_DISCHARGE])
if setup["ParameterScale"] == 1
power_vre_stor *= ModelScalingFactor
end
@@ -486,7 +486,7 @@ function write_vre_stor_discharge(path::AbstractString,
Zone = inputs["ZONES_SOLAR"],
AnnualSum = Array{Union{Missing, Float32}}(undef, size(SOLAR)[1]))
vre_vre_stor = value.(EP[:vP_SOLAR]).data .*
- etainverter.(gen_VRE_STOR[(gen_VRE_STOR.solar .!= 0)])
+ etainverter.(gen[SOLAR])
if setup["ParameterScale"] == 1
vre_vre_stor *= ModelScalingFactor
end
From f1bbba45176aac4ded5de3403aaca6b9306017d9 Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Tue, 19 Nov 2024 17:44:08 -0500
Subject: [PATCH 04/17] Fix description of TDR setting parameters in Tutorial 1
(#793)
---
Project.toml | 2 +-
docs/src/Tutorials/Tutorial_1_configuring_settings.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Project.toml b/Project.toml
index f74ef99f28..4d2eb5c882 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.12"
+version = "0.4.1-dev.13"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/docs/src/Tutorials/Tutorial_1_configuring_settings.md b/docs/src/Tutorials/Tutorial_1_configuring_settings.md
index 37d121f057..c7d8c7f08c 100644
--- a/docs/src/Tutorials/Tutorial_1_configuring_settings.md
+++ b/docs/src/Tutorials/Tutorial_1_configuring_settings.md
@@ -9,7 +9,7 @@ GenX is easy to customize to fit a variety of problems. In this tutorial, we sho
There are 21 settings available to edit in GenX, found in the file `genx_settings.yml`. These settings are described at the [Model settings parameters
](@ref) page of the documentation. The file is located in the `settings` folder in the working directory. To change the location of the file, edit the `settings_path` variable in `Run.jl` within your directory.
-Most settings are set as either 0 or 1, which correspond to whether or not to include a specific feature. For example, to use `TimeDomainReduction`, you would set its parameter to 0 within `genx_settings.yml`. If you would like to run GenX without it, you would set its parameter to 1.
+Most settings are set as either 0 or 1, which correspond to whether or not to include a specific feature. For example, to use `TimeDomainReduction`, you would set its parameter to 1 within `genx_settings.yml`. If you would like to run GenX without it, you would set its parameter to 0.
Other settings, such as `CO2Cap`, have more options corresponding to integers, while some settings such as `ModelingtoGenerateAlternativeSlack` take a numerical input directly (in this case, the slack value). Two settings, `Solver` and `TimeDomainReductionFolder` take in text as input. To learn more about different solvers, read [here](https://github.com/GenXProject/GenX.jl/blob/main/docs/src/User_Guide/solver_configuration.md). For `TimeDomainReductionFolder`, specify the name of the directory you wish to see the results in. For a more comprehensive description of the input options, see the documentation linked above.
From 5b0c5ffb9135840a2a3f5cea81f5118db5b9eff9 Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Tue, 26 Nov 2024 17:24:58 -0500
Subject: [PATCH 05/17] Remove unused `CapRes_*` columns from `Network.csv`
(784)
---
CHANGELOG.md | 1 +
Project.toml | 2 +-
docs/src/User_Guide/model_input.md | 10 ++++-----
.../10_IEEE_9_bus_DC_OPF/system/Network.csv | 22 +++++++++----------
.../1_three_zones/system/Network.csv | 8 +++----
.../system/Network.csv | 8 +++----
.../system/Network.csv | 8 +++----
.../system/Network.csv | 8 +++----
.../system/Network.csv | 8 +++----
.../inputs/inputs_p1/system/Network.csv | 8 +++----
.../inputs/inputs_p2/system/Network.csv | 8 +++----
.../inputs/inputs_p3/system/Network.csv | 8 +++----
.../system/Network.csv | 8 +++----
.../system/Network.csv | 8 +++----
.../system/Network.csv | 8 +++----
precompile/case/system/Network.csv | 8 +++----
test/DCOPF/system/Network.csv | 20 ++++++++---------
.../test_gen_non_colocated/system/Network.csv | 8 +++----
test/three_zones/system/Network.csv | 8 +++----
19 files changed, 84 insertions(+), 83 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 477b0211c5..a34174b7fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ non-representative periods (#781).
- The `charge.csv` and `storage.csv` files now include only resources with
charge and storage variables (#760 and #763).
- Deduplicated docs on optimized scheduled maintenance for thermal resources (#745).
+- Removed the `CapRes_*` columns from `Network.csv` since they were not being used (#784).
### Fixed
- Add constraint to ensure that electricity charged from the grid cannot exceed
diff --git a/Project.toml b/Project.toml
index 4d2eb5c882..3e1de040a2 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.13"
+version = "0.4.1-dev.14"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/docs/src/User_Guide/model_input.md b/docs/src/User_Guide/model_input.md
index 84a0da97ad..19f64dbf5a 100644
--- a/docs/src/User_Guide/model_input.md
+++ b/docs/src/User_Guide/model_input.md
@@ -75,11 +75,11 @@ This input file contains input parameters related to: 1) definition of model zon
|Ohms | Line resistance in Ohms (used to calculate I^2R losses)|
|kV | Line voltage in kV (used to calculate I^2R losses)|
|**CapacityReserveMargin > 0**||
-|CapRes\_* | Eligibility of the transmission line for adding firm capacity to the capacity reserve margin constraint. * represents the number of the capacity reserve margin constraint.|
-||1 = the transmission line is eligible for adding firm capacity to the region|
-||0 = the transmission line is not eligible for adding firm capacity to the region|
-|DerateCapRes\_* | (0,1) value represents the derating of the firm transmission capacity for the capacity reserve margin constraint.|
-|CapResExcl\_* | (-1,1,0) = -1 if the designated direction of the transmission line is inbound to locational deliverability area (LDA) modeled by the capacity reserve margin constraint. = 1 if the designated direction of the transmission line is outbound from the LDA modeled by the capacity reserve margin constraint. Zero otherwise.|
+|CapResExcl\_* | {-1,1,0} Eligibility of the transmission line for adding firm capacity to the capacity reserve margin constraint, as well as whether the line flow is into or out of the constraint region. * represents the number of the capacity reserve margin constraint.|
+|| 0 = the transmission line is not eligible/part of any capacity reserve constraint.|
+|| -1 = the transmission line is eligible for the capacity reserve margin constraint and the designated direction of the transmission line is **inbound** to locational deliverability area (LDA) modeled by the capacity reserve margin constraint.|
+|| 1 = the transmission line is eligible for the capacity reserve margin constraint and the designated direction of the transmission line is **outbound** from the locational deliverability area (LDA) modeled by the capacity reserve margin constraint.|
+|DerateCapRes\_* | [0,1] value represents the derating of the firm transmission capacity for the capacity reserve margin constraint. * represents the number of the capacity reserve margin constraint.|
|**MultiStage == 1**|
|Capital\_Recovery\_Period |Capital recovery period (in years) used for determining overnight capital costs from annualized investment costs for network transmission line expansion. |
|Line\_Max\_Flow\_Possible\_MW |Maximum possible line flow in the current model period. Overrides Line\_Max\_Reinforcement\_MW, which is not used when performing multi-stage modeling. |
diff --git a/example_systems/10_IEEE_9_bus_DC_OPF/system/Network.csv b/example_systems/10_IEEE_9_bus_DC_OPF/system/Network.csv
index b9a66cd006..0677a9450a 100644
--- a/example_systems/10_IEEE_9_bus_DC_OPF/system/Network.csv
+++ b/example_systems/10_IEEE_9_bus_DC_OPF/system/Network.csv
@@ -1,11 +1,11 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1,Angle_Limit_Rad,Line_Voltage_kV,Line_Resistance_Ohms,Line_Reactance_Ohms
-BUS1,z1,1,1,4,250,BUS1_to_BUS4,0.5,0.015,500,12000,0.95,0,0,0.785398,345,0,68.5584
-BUS2,z2,2,4,5,250,BUS4_to_BUS5,0.5,0.015,500,12000,0.95,0,0,0.785398,345,20.23425,109.503
-BUS3,z3,3,5,6,150,BUS5_to_BUS6,0.5,0.015,500,12000,0.95,0,0,0.785398,345,46.41975,202.3425
-BUS4,z4,4,3,6,300,BUS3_to_BUS6,0.5,0.015,500,12000,0.95,0,0,0.785398,345,0,69.74865
-BUS5,z5,5,6,7,150,BUS6_to_BUS7,0.5,0.015,500,12000,0.95,0,0,0.785398,345,14.163975,119.9772
-BUS6,z6,6,7,8,250,BUS7_to_BUS8,0.5,0.015,500,12000,0.95,0,0,0.785398,345,10.117125,85.698
-BUS7,z7,7,8,2,250,BUS8_to_BUS2,0.5,0.015,500,12000,0.95,0,0,0.785398,345,0,74.390625
-BUS8,z8,8,8,9,250,BUS8_to_BUS9,0.5,0.015,500,12000,0.95,0,0,0.785398,345,38.088,191.63025
-BUS9,z9,9,9,4,250,BUS9_to_BUS4,0.5,0.015,500,12000,0.95,0,0,0.785398,345,11.9025,101.17125
-,,10,1,6,250,BUS1_to_BUS6,0.5,0.015,500,12000,0.95,0,0,0.785398,345,11.9025,101.17125
\ No newline at end of file
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1,Angle_Limit_Rad,Line_Voltage_kV,Line_Resistance_Ohms,Line_Reactance_Ohms
+BUS1,z1,1,1,4,250,BUS1_to_BUS4,0.5,0.015,500,12000,0.95,0,0.785398,345,0,68.5584
+BUS2,z2,2,4,5,250,BUS4_to_BUS5,0.5,0.015,500,12000,0.95,0,0.785398,345,20.23425,109.503
+BUS3,z3,3,5,6,150,BUS5_to_BUS6,0.5,0.015,500,12000,0.95,0,0.785398,345,46.41975,202.3425
+BUS4,z4,4,3,6,300,BUS3_to_BUS6,0.5,0.015,500,12000,0.95,0,0.785398,345,0,69.74865
+BUS5,z5,5,6,7,150,BUS6_to_BUS7,0.5,0.015,500,12000,0.95,0,0.785398,345,14.163975,119.9772
+BUS6,z6,6,7,8,250,BUS7_to_BUS8,0.5,0.015,500,12000,0.95,0,0.785398,345,10.117125,85.698
+BUS7,z7,7,8,2,250,BUS8_to_BUS2,0.5,0.015,500,12000,0.95,0,0.785398,345,0,74.390625
+BUS8,z8,8,8,9,250,BUS8_to_BUS9,0.5,0.015,500,12000,0.95,0,0.785398,345,38.088,191.63025
+BUS9,z9,9,9,4,250,BUS9_to_BUS4,0.5,0.015,500,12000,0.95,0,0.785398,345,11.9025,101.17125
+,,10,1,6,250,BUS1_to_BUS6,0.5,0.015,500,12000,0.95,0,0.785398,345,11.9025,101.17125
\ No newline at end of file
diff --git a/example_systems/1_three_zones/system/Network.csv b/example_systems/1_three_zones/system/Network.csv
index 82d03a60e7..c2acbc65a1 100644
--- a/example_systems/1_three_zones/system/Network.csv
+++ b/example_systems/1_three_zones/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
\ No newline at end of file
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/2_three_zones_w_electrolyzer/system/Network.csv b/example_systems/2_three_zones_w_electrolyzer/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/example_systems/2_three_zones_w_electrolyzer/system/Network.csv
+++ b/example_systems/2_three_zones_w_electrolyzer/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/3_three_zones_w_co2_capture/system/Network.csv b/example_systems/3_three_zones_w_co2_capture/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/example_systems/3_three_zones_w_co2_capture/system/Network.csv
+++ b/example_systems/3_three_zones_w_co2_capture/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/4_three_zones_w_policies_slack/system/Network.csv b/example_systems/4_three_zones_w_policies_slack/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/example_systems/4_three_zones_w_policies_slack/system/Network.csv
+++ b/example_systems/4_three_zones_w_policies_slack/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/5_three_zones_w_piecewise_fuel/system/Network.csv b/example_systems/5_three_zones_w_piecewise_fuel/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/example_systems/5_three_zones_w_piecewise_fuel/system/Network.csv
+++ b/example_systems/5_three_zones_w_piecewise_fuel/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/6_three_zones_w_multistage/inputs/inputs_p1/system/Network.csv b/example_systems/6_three_zones_w_multistage/inputs/inputs_p1/system/Network.csv
index e233912403..b9d254d5a6 100644
--- a/example_systems/6_three_zones_w_multistage/inputs/inputs_p1/system/Network.csv
+++ b/example_systems/6_three_zones_w_multistage/inputs/inputs_p1/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1,Line_Max_Flow_Possible_MW,WACC,Capital_Recovery_Period
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0,7000,0.062,30
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0,5000,0.062,30
-ME,z3,,,,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1,Line_Max_Flow_Possible_MW,WACC,Capital_Recovery_Period
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,7000,0.062,30
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,5000,0.062,30
+ME,z3,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/6_three_zones_w_multistage/inputs/inputs_p2/system/Network.csv b/example_systems/6_three_zones_w_multistage/inputs/inputs_p2/system/Network.csv
index e233912403..b9d254d5a6 100644
--- a/example_systems/6_three_zones_w_multistage/inputs/inputs_p2/system/Network.csv
+++ b/example_systems/6_three_zones_w_multistage/inputs/inputs_p2/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1,Line_Max_Flow_Possible_MW,WACC,Capital_Recovery_Period
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0,7000,0.062,30
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0,5000,0.062,30
-ME,z3,,,,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1,Line_Max_Flow_Possible_MW,WACC,Capital_Recovery_Period
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,7000,0.062,30
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,5000,0.062,30
+ME,z3,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/6_three_zones_w_multistage/inputs/inputs_p3/system/Network.csv b/example_systems/6_three_zones_w_multistage/inputs/inputs_p3/system/Network.csv
index e233912403..b9d254d5a6 100644
--- a/example_systems/6_three_zones_w_multistage/inputs/inputs_p3/system/Network.csv
+++ b/example_systems/6_three_zones_w_multistage/inputs/inputs_p3/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1,Line_Max_Flow_Possible_MW,WACC,Capital_Recovery_Period
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0,7000,0.062,30
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0,5000,0.062,30
-ME,z3,,,,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1,Line_Max_Flow_Possible_MW,WACC,Capital_Recovery_Period
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,7000,0.062,30
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,5000,0.062,30
+ME,z3,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/7_three_zones_w_colocated_VRE_storage/system/Network.csv b/example_systems/7_three_zones_w_colocated_VRE_storage/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/example_systems/7_three_zones_w_colocated_VRE_storage/system/Network.csv
+++ b/example_systems/7_three_zones_w_colocated_VRE_storage/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/8_three_zones_w_colocated_VRE_storage_electrolyzers/system/Network.csv b/example_systems/8_three_zones_w_colocated_VRE_storage_electrolyzers/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/example_systems/8_three_zones_w_colocated_VRE_storage_electrolyzers/system/Network.csv
+++ b/example_systems/8_three_zones_w_colocated_VRE_storage_electrolyzers/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/example_systems/9_three_zones_w_retrofit/system/Network.csv b/example_systems/9_three_zones_w_retrofit/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/example_systems/9_three_zones_w_retrofit/system/Network.csv
+++ b/example_systems/9_three_zones_w_retrofit/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/precompile/case/system/Network.csv b/precompile/case/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/precompile/case/system/Network.csv
+++ b/precompile/case/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
diff --git a/test/DCOPF/system/Network.csv b/test/DCOPF/system/Network.csv
index 3da5f52659..5bd44c8a9e 100644
--- a/test/DCOPF/system/Network.csv
+++ b/test/DCOPF/system/Network.csv
@@ -1,10 +1,10 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1,Angle_Limit_Rad,Line_Voltage_kV,Line_Resistance_Ohms,Line_Reactance_Ohms
-BUS1,z1,1,1,4,250,BUS1_to_BUS4,0.5,0.015,500,12000,0.95,0,0,0.785398,345,0,68.5584
-BUS2,z2,2,4,5,250,BUS4_to_BUS5,0.5,0.015,500,12000,0.95,0,0,0.785398,345,20.23425,109.503
-BUS3,z3,3,5,6,150,BUS5_to_BUS6,0.5,0.015,500,12000,0.95,0,0,0.785398,345,46.41975,202.3425
-BUS4,z4,4,3,6,300,BUS3_to_BUS6,0.5,0.015,500,12000,0.95,0,0,0.785398,345,0,69.74865
-BUS5,z5,5,6,7,150,BUS6_to_BUS7,0.5,0.015,500,12000,0.95,0,0,0.785398,345,14.163975,119.9772
-BUS6,z6,6,7,8,250,BUS7_to_BUS8,0.5,0.015,500,12000,0.95,0,0,0.785398,345,10.117125,85.698
-BUS7,z7,7,8,2,250,BUS8_to_BUS2,0.5,0.015,500,12000,0.95,0,0,0.785398,345,0,74.390625
-BUS8,z8,8,8,9,250,BUS8_to_BUS9,0.5,0.015,500,12000,0.95,0,0,0.785398,345,38.088,191.63025
-BUS9,z9,9,9,4,250,BUS9_to_BUS4,0.5,0.015,500,12000,0.95,0,0,0.785398,345,11.9025,101.17125
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1,Angle_Limit_Rad,Line_Voltage_kV,Line_Resistance_Ohms,Line_Reactance_Ohms
+BUS1,z1,1,1,4,250,BUS1_to_BUS4,0.5,0.015,500,12000,0.95,0,0.785398,345,0,68.5584
+BUS2,z2,2,4,5,250,BUS4_to_BUS5,0.5,0.015,500,12000,0.95,0,0.785398,345,20.23425,109.503
+BUS3,z3,3,5,6,150,BUS5_to_BUS6,0.5,0.015,500,12000,0.95,0,0.785398,345,46.41975,202.3425
+BUS4,z4,4,3,6,300,BUS3_to_BUS6,0.5,0.015,500,12000,0.95,0,0.785398,345,0,69.74865
+BUS5,z5,5,6,7,150,BUS6_to_BUS7,0.5,0.015,500,12000,0.95,0,0.785398,345,14.163975,119.9772
+BUS6,z6,6,7,8,250,BUS7_to_BUS8,0.5,0.015,500,12000,0.95,0,0.785398,345,10.117125,85.698
+BUS7,z7,7,8,2,250,BUS8_to_BUS2,0.5,0.015,500,12000,0.95,0,0.785398,345,0,74.390625
+BUS8,z8,8,8,9,250,BUS8_to_BUS9,0.5,0.015,500,12000,0.95,0,0.785398,345,38.088,191.63025
+BUS9,z9,9,9,4,250,BUS9_to_BUS4,0.5,0.015,500,12000,0.95,0,0.785398,345,11.9025,101.17125
\ No newline at end of file
diff --git a/test/load_resources/test_gen_non_colocated/system/Network.csv b/test/load_resources/test_gen_non_colocated/system/Network.csv
index a26c95a6be..b26a04379e 100644
--- a/test/load_resources/test_gen_non_colocated/system/Network.csv
+++ b/test/load_resources/test_gen_non_colocated/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,z1,z2,z3,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-NENGREST,z1,1,1,-1,0,2950,NENGREST_to_NENG_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-NENG_CT,z2,2,1,0,-1,2000,NENGREST_to_NENG_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-NENG_ME,z3,,,,,,,,,,,,,
\ No newline at end of file
+,Network_zones,Network_Lines,z1,z2,z3,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+NENGREST,z1,1,1,-1,0,2950,NENGREST_to_NENG_CT,123.0584,0.012305837,2950,12060,0.95,0
+NENG_CT,z2,2,1,0,-1,2000,NENGREST_to_NENG_ME,196.5385,0.019653847,2000,19261,0.95,0
+NENG_ME,z3,,,,,,,,,,,,
\ No newline at end of file
diff --git a/test/three_zones/system/Network.csv b/test/three_zones/system/Network.csv
index c6413479ff..c2acbc65a1 100644
--- a/test/three_zones/system/Network.csv
+++ b/test/three_zones/system/Network.csv
@@ -1,4 +1,4 @@
-,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_1,CapRes_Excl_1
-MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0,0
-CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0,0
-ME,z3,,,,,,,,,,,,
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,2,1,3,2000,MA_to_ME,196.5385,0.019653847,2000,19261,0.95,0
+ME,z3,,,,,,,,,,,
\ No newline at end of file
From d20118448284b2a2effedc68b3fd63aefa81af80 Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Tue, 3 Dec 2024 15:20:04 -0500
Subject: [PATCH 06/17] Fix writing cost for zones with no resources (#796)
---
CHANGELOG.md | 2 +
Project.toml | 2 +-
src/write_outputs/write_costs.jl | 2 +-
.../writing_outputs/test_zone_no_resources.jl | 77 +++++++++++
.../zone_no_resources/highs_settings.yml | 11 ++
.../zone_no_resources/policies/CO2_cap.csv | 3 +
.../zone_no_resources/resources/Storage.csv | 2 +
.../zone_no_resources/resources/Thermal.csv | 2 +
.../zone_no_resources/resources/Vre.csv | 3 +
.../zone_no_resources/system/Demand_data.csv | 121 +++++++++++++++++
.../zone_no_resources/system/Fuels_data.csv | 122 ++++++++++++++++++
.../system/Generators_variability.csv | 121 +++++++++++++++++
.../zone_no_resources/system/Network.csv | 3 +
13 files changed, 469 insertions(+), 2 deletions(-)
create mode 100644 test/writing_outputs/test_zone_no_resources.jl
create mode 100644 test/writing_outputs/zone_no_resources/highs_settings.yml
create mode 100644 test/writing_outputs/zone_no_resources/policies/CO2_cap.csv
create mode 100644 test/writing_outputs/zone_no_resources/resources/Storage.csv
create mode 100644 test/writing_outputs/zone_no_resources/resources/Thermal.csv
create mode 100644 test/writing_outputs/zone_no_resources/resources/Vre.csv
create mode 100644 test/writing_outputs/zone_no_resources/system/Demand_data.csv
create mode 100644 test/writing_outputs/zone_no_resources/system/Fuels_data.csv
create mode 100644 test/writing_outputs/zone_no_resources/system/Generators_variability.csv
create mode 100644 test/writing_outputs/zone_no_resources/system/Network.csv
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a34174b7fa..809a969555 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,8 @@ charge and storage variables (#760 and #763).
the charging capacity of the storage component in VRE_STOR (#770).
- Update `getproperty` function for vectors of resources to ensure compatibility
with Julia v1.11 (#785).
+- Fixed cost calculation in `write_costs.jl` when no resources are present in
+a zone. (#796)
## [0.4.1] - 2024-08-20
diff --git a/Project.toml b/Project.toml
index 3e1de040a2..2bc7109521 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.14"
+version = "0.4.1-dev.15"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/src/write_outputs/write_costs.jl b/src/write_outputs/write_costs.jl
index c54206fcae..70bda8516e 100644
--- a/src/write_outputs/write_costs.jl
+++ b/src/write_outputs/write_costs.jl
@@ -166,7 +166,7 @@ function write_costs(path::AbstractString, inputs::Dict, setup::Dict, EP::Model)
ELECTROLYZERS_ZONE = intersect(inputs["ELECTROLYZER"], Y_ZONE)
CCS_ZONE = intersect(inputs["CCS"], Y_ZONE)
- eCFix = sum(value.(EP[:eCFix][Y_ZONE]))
+ eCFix = sum(value.(EP[:eCFix][Y_ZONE]), init = 0.0)
tempCFix += eCFix
tempCTotal += eCFix
diff --git a/test/writing_outputs/test_zone_no_resources.jl b/test/writing_outputs/test_zone_no_resources.jl
new file mode 100644
index 0000000000..8b19423fc4
--- /dev/null
+++ b/test/writing_outputs/test_zone_no_resources.jl
@@ -0,0 +1,77 @@
+module TestZoneNoResources
+
+using Test
+using DataFrames
+
+include(joinpath(@__DIR__, "../utilities.jl"))
+
+function prepare_costs_test(test_path, inputs, genx_setup, EP)
+ settings = GenX.default_settings()
+ merge!(settings, genx_setup)
+ GenX.write_costs(test_path, inputs, settings, EP)
+ costs_path = joinpath(test_path, "costs.csv")
+ costs_test = CSV.read(costs_path, DataFrame)
+ costs_test[!, :Zone1] = tryparse.(Float64, replace(costs_test[!, :Zone1], "-" => "0.0"))
+ costs_test[!, :Zone2] = tryparse.(Float64, replace(costs_test[!, :Zone2], "-" => "0.0"))
+ costs_test[!, :Zone2] = replace(costs_test[!, :Zone2], nothing => 0.0)
+ return costs_test
+end
+
+function prepare_costs_true()
+ df = DataFrame(
+ ["cTotal" 5.177363815260002e12 4.027191550200002e12 1.1501722650599993e12;
+ "cFix" 0.0 0.0 0.0;
+ "cVar" 5.849292224195126e-8 0.0 5.849292224195126e-8;
+ "cFuel" 0.0 0.0 0.0;
+ "cNSE" 5.177363815260002e12 4.027191550200002e12 1.1501722650599993e12;
+ "cStart" 0.0 0.0 0.0;
+ "cUnmetRsv" 0.0 0.0 0.0;
+ "cNetworkExp" 0.0 0.0 0.0;
+ "cUnmetPolicyPenalty" 0.0 0.0 0.0;
+ "cCO2" 0.0 0.0 0.0],
+ [:Costs, :Total, :Zone1, :Zone2])
+
+ df[!, :Costs] = convert(Vector{String}, df[!, :Costs])
+ df[!, :Total] = convert(Vector{Float64}, df[!, :Total])
+ df[!, :Zone1] = convert(Vector{Float64}, df[!, :Zone1])
+ df[!, :Zone2] = convert(Vector{Float64}, df[!, :Zone2])
+ return df
+end
+
+function test_case()
+ test_path = joinpath(@__DIR__, "zone_no_resources")
+ obj_true = 5.1773638153e12
+ costs_true = prepare_costs_true()
+
+ # Define test setup
+ genx_setup = Dict("NetworkExpansion" => 1,
+ "Trans_Loss_Segments" => 1,
+ "UCommit" => 2,
+ "CO2Cap" => 2,
+ "StorageLosses" => 1,
+ "WriteShadowPrices" => 1)
+
+ # Run the case and get the objective value and tolerance
+ EP, inputs, _ = redirect_stdout(devnull) do
+ run_genx_case_testing(test_path, genx_setup)
+ end
+ obj_test = objective_value(EP)
+ optimal_tol_rel = get_attribute(EP, "dual_feasibility_tolerance")
+ optimal_tol = optimal_tol_rel * obj_test # Convert to absolute tolerance
+
+ # Test the objective value
+ test_result = @test obj_test≈obj_true atol=optimal_tol
+
+ # Test the costs
+ costs_test = prepare_costs_test(test_path, inputs, genx_setup, EP)
+ test_result = @test costs_test[!, Not(:Costs)] ≈ costs_true[!, Not(:Costs)]
+
+ # Remove the costs file
+ rm(joinpath(test_path, "costs.csv"))
+
+ return nothing
+end
+
+test_case()
+
+end # module TestZoneNoResources
diff --git a/test/writing_outputs/zone_no_resources/highs_settings.yml b/test/writing_outputs/zone_no_resources/highs_settings.yml
new file mode 100644
index 0000000000..e4f1ad0245
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/highs_settings.yml
@@ -0,0 +1,11 @@
+# HiGHS Solver Parameters
+# Common solver settings
+Feasib_Tol: 1.0e-05 # Primal feasibility tolerance # [type: double, advanced: false, range: [1e-10, inf], default: 1e-07]
+Optimal_Tol: 1.0e-05 # Dual feasibility tolerance # [type: double, advanced: false, range: [1e-10, inf], default: 1e-07]
+TimeLimit: 1.0e23 # Time limit # [type: double, advanced: false, range: [0, inf], default: inf]
+Pre_Solve: choose # Presolve option: "off", "choose" or "on" # [type: string, advanced: false, default: "choose"]
+Method: ipm #HiGHS-specific solver settings # Solver option: "simplex", "choose" or "ipm" # [type: string, advanced: false, default: "choose"]
+
+# run the crossover routine for ipx
+# [type: string, advanced: "on", range: {"off", "on"}, default: "off"]
+run_crossover: "on"
diff --git a/test/writing_outputs/zone_no_resources/policies/CO2_cap.csv b/test/writing_outputs/zone_no_resources/policies/CO2_cap.csv
new file mode 100644
index 0000000000..dee326e6cb
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/policies/CO2_cap.csv
@@ -0,0 +1,3 @@
+,Network_zones,CO_2_Cap_Zone_1,CO_2_Cap_Zone_2,CO_2_Max_tons_MWh_1,CO_2_Max_tons_MWh_2,CO_2_Max_Mtons_1,CO_2_Max_Mtons_2
+MA,z1,1,0,0.05,0,0.018,0
+CT,z2,0,1,0,0.05,0,0.025
\ No newline at end of file
diff --git a/test/writing_outputs/zone_no_resources/resources/Storage.csv b/test/writing_outputs/zone_no_resources/resources/Storage.csv
new file mode 100644
index 0000000000..d5a892cca1
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/resources/Storage.csv
@@ -0,0 +1,2 @@
+Resource,Zone,Model,New_Build,Can_Retire,Existing_Cap_MW,Existing_Cap_MWh,Max_Cap_MW,Max_Cap_MWh,Min_Cap_MW,Min_Cap_MWh,Inv_Cost_per_MWyr,Inv_Cost_per_MWhyr,Fixed_OM_Cost_per_MWyr,Fixed_OM_Cost_per_MWhyr,Var_OM_Cost_per_MWh,Var_OM_Cost_per_MWh_In,Self_Disch,Eff_Up,Eff_Down,Min_Duration,Max_Duration,Reg_Max,Rsv_Max,Reg_Cost,Rsv_Cost,region,cluster
+CT_battery,2,1,1,0,0,0,-1,-1,0,0,19584,22494,4895,5622,0.15,0.15,0,0.92,0.92,1,10,0,0,0,0,CT,0
\ No newline at end of file
diff --git a/test/writing_outputs/zone_no_resources/resources/Thermal.csv b/test/writing_outputs/zone_no_resources/resources/Thermal.csv
new file mode 100644
index 0000000000..2a8f68172e
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/resources/Thermal.csv
@@ -0,0 +1,2 @@
+Resource,Zone,Model,New_Build,Can_Retire,Existing_Cap_MW,Max_Cap_MW,Min_Cap_MW,Inv_Cost_per_MWyr,Fixed_OM_Cost_per_MWyr,Var_OM_Cost_per_MWh,Heat_Rate_MMBTU_per_MWh,Fuel,Cap_Size,Start_Cost_per_MW,Start_Fuel_MMBTU_per_MW,Up_Time,Down_Time,Ramp_Up_Percentage,Ramp_Dn_Percentage,Min_Power,Reg_Max,Rsv_Max,Reg_Cost,Rsv_Cost,region,cluster
+CT_natural_gas_combined_cycle,2,1,1,0,0,-1,0,65400,10287,3.55,7.43,CT_NG,250,91,2,6,6,0.64,0.64,0.468,0.25,0.5,0,0,CT,1
\ No newline at end of file
diff --git a/test/writing_outputs/zone_no_resources/resources/Vre.csv b/test/writing_outputs/zone_no_resources/resources/Vre.csv
new file mode 100644
index 0000000000..3f32d9b84b
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/resources/Vre.csv
@@ -0,0 +1,3 @@
+Resource,Zone,Num_VRE_Bins,New_Build,Can_Retire,Existing_Cap_MW,Max_Cap_MW,Min_Cap_MW,Inv_Cost_per_MWyr,Fixed_OM_Cost_per_MWyr,Var_OM_Cost_per_MWh,Reg_Max,Rsv_Max,Reg_Cost,Rsv_Cost,region,cluster
+CT_onshore_wind,2,1,0,0,0,-1,0,97200,43205,0.1,0,0,0,0,CT,1
+CT_solar_pv,2,1,0,0,0,-1,0,85300,18760,0,0,0,0,0,CT,1
\ No newline at end of file
diff --git a/test/writing_outputs/zone_no_resources/system/Demand_data.csv b/test/writing_outputs/zone_no_resources/system/Demand_data.csv
new file mode 100644
index 0000000000..4234d3f830
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/system/Demand_data.csv
@@ -0,0 +1,121 @@
+Voll,Demand_Segment,Cost_of_Demand_Curtailment_per_MW,Max_Demand_Curtailment,$/MWh,Rep_Periods,Timesteps_per_Rep_Period,Sub_Weights,Time_Index,Demand_MW_z1,Demand_MW_z2
+50000,1,1.0,1.0,2000,5,24,24.0,1,7822.0,2234.0
+,2,0.9,0.04,1800,,,2592.0,2,7494.0,2140.0
+,3,0.55,0.024,1100,,,6096.0,3,7343.0,2097.0
+,4,0.2,0.003,400,,,24.0,4,7289.0,2082.0
+,,,,,,,24.0,5,7482.0,2137.0
+,,,,,,,,6,8142.0,2325.0
+,,,,,,,,7,9388.0,2681.0
+,,,,,,,,8,10233.0,2922.0
+,,,,,,,,9,10494.0,2997.0
+,,,,,,,,10,10665.0,3046.0
+,,,,,,,,11,10780.0,3079.0
+,,,,,,,,12,10817.0,3089.0
+,,,,,,,,13,10743.0,3068.0
+,,,,,,,,14,10657.0,3044.0
+,,,,,,,,15,10516.0,3003.0
+,,,,,,,,16,10468.0,2990.0
+,,,,,,,,17,10788.0,3081.0
+,,,,,,,,18,11254.0,3214.0
+,,,,,,,,19,11088.0,3167.0
+,,,,,,,,20,10715.0,3060.0
+,,,,,,,,21,10312.0,2945.0
+,,,,,,,,22,9767.0,2790.0
+,,,,,,,,23,9041.0,2582.0
+,,,,,,,,24,8305.0,2372.0
+,,,,,,,,25,7611.0,2174.0
+,,,,,,,,26,7284.0,2080.0
+,,,,,,,,27,7110.0,2031.0
+,,,,,,,,28,7082.0,2023.0
+,,,,,,,,29,7266.0,2075.0
+,,,,,,,,30,7941.0,2268.0
+,,,,,,,,31,9190.0,2625.0
+,,,,,,,,32,9935.0,2837.0
+,,,,,,,,33,10145.0,2897.0
+,,,,,,,,34,10199.0,2913.0
+,,,,,,,,35,10213.0,2917.0
+,,,,,,,,36,10135.0,2895.0
+,,,,,,,,37,9964.0,2845.0
+,,,,,,,,38,9842.0,2811.0
+,,,,,,,,39,9677.0,2764.0
+,,,,,,,,40,9588.0,2739.0
+,,,,,,,,41,9757.0,2786.0
+,,,,,,,,42,10423.0,2976.0
+,,,,,,,,43,10732.0,3065.0
+,,,,,,,,44,10465.0,2989.0
+,,,,,,,,45,10112.0,2888.0
+,,,,,,,,46,9608.0,2744.0
+,,,,,,,,47,8902.0,2543.0
+,,,,,,,,48,8169.0,2333.0
+,,,,,,,,49,7338.0,2096.0
+,,,,,,,,50,6938.0,1982.0
+,,,,,,,,51,6751.0,1928.0
+,,,,,,,,52,6676.0,1907.0
+,,,,,,,,53,6840.0,1953.0
+,,,,,,,,54,7300.0,2085.0
+,,,,,,,,55,8454.0,2414.0
+,,,,,,,,56,9469.0,2704.0
+,,,,,,,,57,10006.0,2858.0
+,,,,,,,,58,10341.0,2954.0
+,,,,,,,,59,10626.0,3035.0
+,,,,,,,,60,10780.0,3079.0
+,,,,,,,,61,10849.0,3099.0
+,,,,,,,,62,10977.0,3135.0
+,,,,,,,,63,10950.0,3127.0
+,,,,,,,,64,10892.0,3111.0
+,,,,,,,,65,10868.0,3104.0
+,,,,,,,,66,10767.0,3075.0
+,,,,,,,,67,10550.0,3013.0
+,,,,,,,,68,10414.0,2974.0
+,,,,,,,,69,10478.0,2992.0
+,,,,,,,,70,10018.0,2861.0
+,,,,,,,,71,9029.0,2579.0
+,,,,,,,,72,8087.0,2309.0
+,,,,,,,,73,10503.0,3000.0
+,,,,,,,,74,9889.0,2825.0
+,,,,,,,,75,9493.0,2711.0
+,,,,,,,,76,9245.0,2640.0
+,,,,,,,,77,9268.0,2647.0
+,,,,,,,,78,9643.0,2754.0
+,,,,,,,,79,10684.0,3051.0
+,,,,,,,,80,12036.0,3437.0
+,,,,,,,,81,13120.0,3747.0
+,,,,,,,,82,14080.0,4021.0
+,,,,,,,,83,14910.0,4258.0
+,,,,,,,,84,15478.0,4421.0
+,,,,,,,,85,15870.0,4533.0
+,,,,,,,,86,16225.0,4633.0
+,,,,,,,,87,16448.0,4698.0
+,,,,,,,,88,16617.0,4746.0
+,,,,,,,,89,16717.0,4774.0
+,,,,,,,,90,16579.0,4735.0
+,,,,,,,,91,16199.0,4626.0
+,,,,,,,,92,15701.0,4484.0
+,,,,,,,,93,15416.0,4403.0
+,,,,,,,,94,14854.0,4243.0
+,,,,,,,,95,13581.0,3878.0
+,,,,,,,,96,12317.0,3518.0
+,,,,,,,,97,7899.0,2256.0
+,,,,,,,,98,7613.0,2174.0
+,,,,,,,,99,7477.0,2135.0
+,,,,,,,,100,7470.0,2134.0
+,,,,,,,,101,7699.0,2199.0
+,,,,,,,,102,8428.0,2407.0
+,,,,,,,,103,9761.0,2787.0
+,,,,,,,,104,10471.0,2991.0
+,,,,,,,,105,10643.0,3040.0
+,,,,,,,,106,10719.0,3061.0
+,,,,,,,,107,10802.0,3085.0
+,,,,,,,,108,10835.0,3095.0
+,,,,,,,,109,10820.0,3090.0
+,,,,,,,,110,10811.0,3087.0
+,,,,,,,,111,10750.0,3070.0
+,,,,,,,,112,10888.0,3110.0
+,,,,,,,,113,11635.0,3323.0
+,,,,,,,,114,12129.0,3464.0
+,,,,,,,,115,12036.0,3437.0
+,,,,,,,,116,11714.0,3346.0
+,,,,,,,,117,11207.0,3201.0
+,,,,,,,,118,10396.0,2969.0
+,,,,,,,,119,9383.0,2680.0
+,,,,,,,,120,8476.0,2420.0
diff --git a/test/writing_outputs/zone_no_resources/system/Fuels_data.csv b/test/writing_outputs/zone_no_resources/system/Fuels_data.csv
new file mode 100644
index 0000000000..ae37d8b77b
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/system/Fuels_data.csv
@@ -0,0 +1,122 @@
+Time_Index,CT_NG,None
+0,0.05306,0.0
+1,5.45,0.0
+2,5.45,0.0
+3,5.45,0.0
+4,5.45,0.0
+5,5.45,0.0
+6,5.45,0.0
+7,5.45,0.0
+8,5.45,0.0
+9,5.45,0.0
+10,5.45,0.0
+11,5.45,0.0
+12,5.45,0.0
+13,5.45,0.0
+14,5.45,0.0
+15,5.45,0.0
+16,5.45,0.0
+17,5.45,0.0
+18,5.45,0.0
+19,5.45,0.0
+20,5.45,0.0
+21,5.45,0.0
+22,5.45,0.0
+23,5.45,0.0
+24,5.45,0.0
+25,4.09,0.0
+26,4.09,0.0
+27,4.09,0.0
+28,4.09,0.0
+29,4.09,0.0
+30,4.09,0.0
+31,4.09,0.0
+32,4.09,0.0
+33,4.09,0.0
+34,4.09,0.0
+35,4.09,0.0
+36,4.09,0.0
+37,4.09,0.0
+38,4.09,0.0
+39,4.09,0.0
+40,4.09,0.0
+41,4.09,0.0
+42,4.09,0.0
+43,4.09,0.0
+44,4.09,0.0
+45,4.09,0.0
+46,4.09,0.0
+47,4.09,0.0
+48,4.09,0.0
+49,1.82,0.0
+50,1.82,0.0
+51,1.82,0.0
+52,1.82,0.0
+53,1.82,0.0
+54,1.82,0.0
+55,1.82,0.0
+56,1.82,0.0
+57,1.82,0.0
+58,1.82,0.0
+59,1.82,0.0
+60,1.82,0.0
+61,1.82,0.0
+62,1.82,0.0
+63,1.82,0.0
+64,1.82,0.0
+65,1.82,0.0
+66,1.82,0.0
+67,1.82,0.0
+68,1.82,0.0
+69,1.82,0.0
+70,1.82,0.0
+71,1.82,0.0
+72,1.82,0.0
+73,1.89,0.0
+74,1.89,0.0
+75,1.89,0.0
+76,1.89,0.0
+77,1.89,0.0
+78,1.89,0.0
+79,1.89,0.0
+80,1.89,0.0
+81,1.89,0.0
+82,1.89,0.0
+83,1.89,0.0
+84,1.89,0.0
+85,1.89,0.0
+86,1.89,0.0
+87,1.89,0.0
+88,1.89,0.0
+89,1.89,0.0
+90,1.89,0.0
+91,1.89,0.0
+92,1.89,0.0
+93,1.89,0.0
+94,1.89,0.0
+95,1.89,0.0
+96,1.89,0.0
+97,2.78,0.0
+98,2.78,0.0
+99,2.78,0.0
+100,2.78,0.0
+101,2.78,0.0
+102,2.78,0.0
+103,2.78,0.0
+104,2.78,0.0
+105,2.78,0.0
+106,2.78,0.0
+107,2.78,0.0
+108,2.78,0.0
+109,2.78,0.0
+110,2.78,0.0
+111,2.78,0.0
+112,2.78,0.0
+113,2.78,0.0
+114,2.78,0.0
+115,2.78,0.0
+116,2.78,0.0
+117,2.78,0.0
+118,2.78,0.0
+119,2.78,0.0
+120,2.78,0.0
diff --git a/test/writing_outputs/zone_no_resources/system/Generators_variability.csv b/test/writing_outputs/zone_no_resources/system/Generators_variability.csv
new file mode 100644
index 0000000000..80a537f30c
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/system/Generators_variability.csv
@@ -0,0 +1,121 @@
+Time_Index,CT_onshore_wind,CT_solar_pv
+1,0.705949306,0
+2,0.834924579,0
+3,0.832703173,0
+4,0.727586865,0
+5,0.626110256,0
+6,0.721315265,0
+7,0.785158873,0
+8,0.59819752,0
+9,0.567111433,0
+10,0.326491237,0.0064
+11,0.390583217,0.116
+12,0.287067473,0.0999
+13,0.229321212,0.1202
+14,0.154025629,0.192
+15,0.115687042,0.1404
+16,0.054644316,0.0697
+17,0.088804618,0
+18,0.72049433,0
+19,0.834395289,0
+20,0.950648248,0
+21,0.999782085,0
+22,1,0
+23,1,0
+24,1,0
+25,0.465583175,0
+26,0.707297444,0
+27,0.895804107,0
+28,0.819945991,0
+29,0.610500693,0
+30,0.34757489,0
+31,0.285657108,0
+32,0.317218393,0
+33,0.254971772,0.1509
+34,0.306124657,0.3546
+35,0.72285372,0.5514
+36,0.749075055,0.5828
+37,0.766450584,0.5721
+38,0.583024323,0.5944
+39,0.89966023,0.5804
+40,0.768344879,0.5083
+41,0.941289306,0.3311
+42,0.691129565,0.0839
+43,0.369385242,0
+44,0.543988705,0
+45,0.627581239,0
+46,0.891589403,0
+47,0.663651288,0
+48,0.636843503,0
+49,0.431610733,0
+50,0.574139476,0
+51,0.5398283,0
+52,0.201132476,0
+53,0.107555799,0
+54,0.144015923,0.0126
+55,0.071583487,0.1019
+56,0.205921009,0.2045
+57,0.161220312,0.3112
+58,0.336054265,0.3663
+59,0.368090123,0.4167
+60,0.454866886,0.4684
+61,0.460774302,0.4928
+62,0.431218863,0.4656
+63,0.424021393,0.3782
+64,0.402401239,0.3149
+65,0.201657325,0.2465
+66,0.31398356,0.1617
+67,0.642302394,0.0083
+68,0.458561152,0
+69,0.278454691,0
+70,0.406244844,0
+71,0.48908928,0
+72,0.247558758,0
+73,0.46454832,0
+74,0.619871557,0
+75,0.782924116,0
+76,0.544351637,0
+77,0.388339579,0
+78,0.188761607,0.0003
+79,0.056012779,0.0996
+80,0.011642265,0.2184
+81,0.005336904,0.3801
+82,0.036412083,0.497
+83,0.113800742,0.566
+84,0.309363514,0.5632
+85,0.537328064,0.5305
+86,0.75558275,0.5783
+87,0.804839015,0.5735
+88,0.814335048,0.4853
+89,0.82010901,0.4051
+90,0.700861871,0.2135
+91,0.377394527,0.0909
+92,0.301600695,0
+93,0.539320409,0
+94,0.604777336,0
+95,0.605847716,0
+96,0.867583036,0
+97,1.83E-05,0
+98,0,0
+99,0,0
+100,0,0
+101,0,0
+102,0,0
+103,0.000799759,0
+104,0.00032822,0
+105,0,0.0745
+106,0,0.2522
+107,5.05E-06,0.3334
+108,0,0.3485
+109,0,0.3388
+110,0,0.3379
+111,6.15E-05,0.3133
+112,0.000322065,0.1924
+113,0.000258478,0
+114,0.000254347,0
+115,0.00095264,0
+116,0.001598039,0
+117,0.002736128,0
+118,0.004819703,0
+119,0.002815315,0
+120,0.001111662,0
\ No newline at end of file
diff --git a/test/writing_outputs/zone_no_resources/system/Network.csv b/test/writing_outputs/zone_no_resources/system/Network.csv
new file mode 100644
index 0000000000..8924aa60bd
--- /dev/null
+++ b/test/writing_outputs/zone_no_resources/system/Network.csv
@@ -0,0 +1,3 @@
+,Network_zones,Network_Lines,Start_Zone,End_Zone,Line_Max_Flow_MW,transmission_path_name,distance_mile,Line_Loss_Percentage,Line_Max_Reinforcement_MW,Line_Reinforcement_Cost_per_MWyr,DerateCapRes_1,CapRes_Excl_1
+MA,z1,1,1,2,2950,MA_to_CT,123.0584,0.012305837,2950,12060,0.95,0
+CT,z2,,,,,,,,,,,
\ No newline at end of file
From 4eb44e84bdc89b33664c1ea459e81b1acf522391 Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Tue, 3 Dec 2024 17:08:12 -0500
Subject: [PATCH 07/17] Fix demand_data path in tutorial 8 (#794)
---
Project.toml | 2 +-
docs/src/Tutorials/Tutorial_8_outputs.md | 65 +++++-------------------
docs/src/Tutorials/files/t8_cap.svg | 2 +-
3 files changed, 16 insertions(+), 53 deletions(-)
diff --git a/Project.toml b/Project.toml
index 2bc7109521..a6d2b1048d 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.15"
+version = "0.4.1-dev.16"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/docs/src/Tutorials/Tutorial_8_outputs.md b/docs/src/Tutorials/Tutorial_8_outputs.md
index b344c3aab3..e6b9f130f5 100644
--- a/docs/src/Tutorials/Tutorial_8_outputs.md
+++ b/docs/src/Tutorials/Tutorial_8_outputs.md
@@ -28,7 +28,6 @@ using StatsPlots
case = joinpath("example_systems/1_three_zones");
```
-
```julia
include("example_systems/1_three_zones/Run.jl")
```
@@ -40,15 +39,12 @@ include("example_systems/1_three_zones/Run.jl")
Demand (load) data Successfully Read!
Fuels_data.csv Successfully Read!
-
Thermal.csv Successfully Read.
Vre.csv Successfully Read.
Storage.csv Successfully Read.
Resource_energy_share_requirement.csv Successfully Read.
Resource_capacity_reserve_margin.csv Successfully Read.
Resource_minimum_capacity_requirement.csv Successfully Read.
-
-
Summary of resources loaded into the model:
-------------------------------------------------------
@@ -89,8 +85,7 @@ include("example_systems/1_three_zones/Run.jl")
CSV Files Successfully Read In From /Users/mayamutic/Desktop/GenX-Tutorials/Tutorials/example_systems/1_three_zones
Generating the Optimization Model
-
- Thermal.csv Successfully Read.
+ Thermal.csv Successfully Read.
Vre.csv Successfully Read.
Storage.csv Successfully Read.
Resource_energy_share_requirement.csv Successfully Read.
@@ -115,6 +110,7 @@ include("example_systems/1_three_zones/Run.jl")
Minimum Capacity Requirement Module
Time elapsed for model building is
5.887781667
+
Solving Model
Running HiGHS 1.6.0: Copyright (c) 2023 HiGHS under MIT licence terms
Presolving model
@@ -251,6 +247,7 @@ include("example_systems/1_three_zones/Run.jl")
Objective value : 9.4121364078e+03
HiGHS run time : 107.89
LP solved for primal
+
Writing Output
Time elapsed for writing costs is
0.8427745
@@ -312,17 +309,12 @@ include("example_systems/1_three_zones/Run.jl")
Time elapsed for writing is
6.909353542
-
Below are all 33 files output by running GenX:
-
```julia
results = cd(readdir,joinpath(case,"results"))
```
-
-
-
33-element Vector{String}:
"CO2_prices_and_penalties.csv"
"ChargingCost.csv"
@@ -351,17 +343,15 @@ results = cd(readdir,joinpath(case,"results"))
"time_weights.csv"
"tlosses.csv"
-
-
### Power
-The file `power.csv`, shown below, outputs the power in MW discharged by each node at each time step. Note that if TimeDomainReduction is in use the file will be shorter. The first row states which zone each node is part of, and the total power per year is located in the second row. After that, each row represents one time step of the series.
-
+The file `power.csv`, shown below, contains the power output in MW discharged by each node at each time step. Note that if `TimeDomainReduction` is enabled, the file will have fewer rows compared to the number of time steps in the `system/Demand_data.csv` file. In this case, the corresponding `Demand_data.csv` file that matches the time series in `power.csv` can be found in the `TDR_results` folder. The first row of `power.csv` indicates the zone each node belongs to, while the second row contains the total power per year. Each subsequent row represents one time step in the series.
```julia
power = CSV.read(joinpath(case,"results/power.csv"),DataFrame,missingstring="NA")
```
-``` @raw html
+
+```@raw html
1850×12 DataFrame
1825 rows omitted
Row
Resource
MA_natural_gas_combined_cycle
CT_natural_gas_combined_cycle
ME_natural_gas_combined_cycle
MA_solar_pv
CT_onshore_wind
CT_solar_pv
ME_onshore_wind
MA_battery
CT_battery
ME_battery
Total
String15
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
1
Zone
1.0
2.0
3.0
1.0
2.0
2.0
3.0
1.0
2.0
3.0
0.0
2
AnnualSum
1.04015e7
3.42459e6
8.94975e5
2.47213e7
2.90683e7
2.69884e7
2.625e7
5.06354e6
1.45833e7
4.90368e6
1.463e8
3
t1
-0.0
-0.0
-0.0
-0.0
8510.78
-0.0
5300.61
0.0
2537.45
673.34
17022.2
4
t2
-0.0
-0.0
-0.0
-0.0
8420.78
-0.0
6282.04
0.0
2537.45
0.0
17240.3
5
t3
-0.0
-0.0
-0.0
-0.0
8367.78
-0.0
2409.84
0.0
2537.45
1828.24
15143.3
6
t4
-0.0
-0.0
-0.0
-0.0
8353.78
-0.0
2762.24
1591.46
2537.45
0.0
15244.9
7
t5
-0.0
-0.0
-0.0
-0.0
7482.39
-0.0
0.0
1617.46
2980.64
1384.62
13465.1
8
t6
-0.0
-0.0
-0.0
-0.0
2429.93
-0.0
2797.24
1717.96
5535.37
0.0
12480.5
9
t7
-0.0
-0.0
-0.0
-0.0
11868.8
-0.0
1374.73
1320.78
871.443
1340.67
16776.4
10
t8
-0.0
-0.0
-0.0
-0.0
2656.93
-0.0
0.0
2115.96
5535.37
1452.62
11760.9
11
t9
-0.0
-0.0
-0.0
3061.28
0.0
3110.8
2982.24
868.817
5389.44
0.0
15412.6
12
t10
-0.0
-0.0
-0.0
6100.22
7597.99
5543.69
0.0
0.0
0.0
1521.12
20763.0
13
t11
-0.0
-0.0
-0.0
8314.29
0.0
6341.98
3080.24
0.0
2458.82
0.0
20195.3
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
1839
t1837
-0.0
-0.0
-0.0
6712.18
2541.6
6736.37
305.608
1410.33
763.726
1427.82
19897.6
1840
t1838
-0.0
-0.0
-0.0
6514.15
0.0
6847.24
3153.24
0.0
3464.22
0.0
19978.9
1841
t1839
-0.0
-0.0
-0.0
5582.07
3848.88
6280.2
0.0
195.422
2048.3
1571.12
19526.0
1842
t1840
-0.0
-0.0
-0.0
3688.13
9349.98
4892.7
3490.61
1006.02
0.0
0.0
22427.4
1843
t1841
-0.0
-0.0
-0.0
509.22
8124.99
1351.08
3653.06
1218.5
2507.8
1828.24
19192.9
1844
t1842
-0.0
-0.0
-0.0
-0.0
2918.2
-0.0
6896.82
2194.61
5535.37
256.863
17801.9
1845
t1843
-0.0
-0.0
-0.0
-0.0
6800.37
-0.0
7324.66
1838.11
3950.15
41.9472
19955.2
1846
t1844
-0.0
-0.0
-0.0
-0.0
9505.82
-0.0
5683.66
1744.78
2567.93
838.077
20340.3
1847
t1845
-0.0
-0.0
-0.0
-0.0
3491.93
-0.0
5128.56
1597.61
5535.37
1107.49
16861.0
1848
t1846
-0.0
-0.0
-0.0
-0.0
12135.6
-0.0
5021.75
1341.11
1140.56
1125.9
20764.9
1849
t1847
-0.0
-0.0
-0.0
-0.0
8875.71
-0.0
3605.98
974.61
2665.48
1783.79
17905.6
1850
t1848
-0.0
-0.0
-0.0
-0.0
13549.1
-0.0
4098.0
541.61
205.31
1478.27
19872.3
```
@@ -386,11 +376,16 @@ for i in range(2,4)
power_plot = [power_plot; power_plot_temp]
end
-demands = CSV.read(joinpath(case,"system/Demand_data.csv"),DataFrame,missingstring="NA")
+demands = CSV.read(joinpath(case,"TDR_results/Demand_data.csv"),DataFrame,missingstring="NA")
demands_tot = demands[!,"Demand_MW_z1"]+demands[!,"Demand_MW_z2"]+demands[!,"Demand_MW_z3"]
power_plot[!,"Demand_Total"] = repeat(demands_tot[tstart:tend],4);
```
+Note that since the `power.csv` file is generated by running GenX with `TimeDomainReduction: 1`, the demands time series must be taken from the `Demand_data.csv` file located in the `TDR_results` folder.
+
+GenX also has the ability to output the reconstructed version of power generation by setting `OutputFullTimeSeries: 1` in `genx_settings.yml`. In this case, a second version of the `power.csv` file will be created inside the `results/Full_TimeSeries` folder. To plot the reconstructed version against the demand, ensure you use the `Demand_data.csv` from the `settings` folder, not the one in the `TDR_results` folder.
+
+Finally, if `TimeDomainReduction: 0` is set, the `power.csv` file will contain the full time series of power generation, and the `Demand_data.csv` should be taken from the `settings` folder.
```julia
power_plot |>
@@ -402,11 +397,9 @@ power_plot |>
```
![svg](./files/t8_cap.svg)
-
We can separate it by zone in the following plot:
-
```julia
Zone1 = [power[2,2] power[2,5] 0 power[2,9]]
Zone2 = [power[2,3] power[2,7] power[2,6] power[2,10]]
@@ -448,7 +441,6 @@ end
```
-
```julia
Plots.heatmap(heat,yticks=0:4:24,xticks=([15:30:364;],
["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec"]),
@@ -458,7 +450,6 @@ Plots.heatmap(heat,yticks=0:4:24,xticks=([15:30:364;],
![svg](./files/t8_heatmap.svg)
-
### Cost and Revenue
The basic cost of each power plant and the revenue it generates can be found in files `costs.csv`, `NetRevenue.csv`,and `EnergyRevenue.csv`. `NetRevenue.csv` breaks down each specific cost per node in each zone, which is useful to visualize what the cost is coming from.
@@ -468,14 +459,10 @@ The basic cost of each power plant and the revenue it generates can be found in
netrevenue = CSV.read(joinpath(case,"results/NetRevenue.csv"),DataFrame,missingstring="NA")
```
-
-
``` @raw html
10×28 DataFrame
Row
region
Resource
zone
Cluster
R_ID
Inv_cost_MW
Inv_cost_MWh
Inv_cost_charge_MW
Fixed_OM_cost_MW
Fixed_OM_cost_MWh
Fixed_OM_cost_charge_MW
Var_OM_cost_out
Fuel_cost
Var_OM_cost_in
StartCost
Charge_cost
CO2SequestrationCost
EnergyRevenue
SubsidyRevenue
OperatingReserveRevenue
OperatingRegulationRevenue
ReserveMarginRevenue
ESRRevenue
EmissionsCost
RegSubsidyRevenue
Revenue
Cost
Profit
String3
String31
Int64
Int64
Int64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
1
MA
MA_natural_gas_combined_cycle
1
1
1
5.54734e8
0.0
0.0
8.72561e7
0.0
0.0
3.69253e7
2.10416e8
0.0
3.84832e7
0.0
0.0
2.77103e9
0.0
0.0
0.0
0.0
0.0
1.84321e9
0.0
2.77103e9
2.77103e9
1.43051e-6
2
CT
CT_natural_gas_combined_cycle
2
1
2
1.42906e8
0.0
0.0
2.11911e7
0.0
0.0
1.22258e7
4.97792e7
0.0
7.75292e6
0.0
0.0
8.4423e8
0.0
0.0
0.0
0.0
0.0
6.10375e8
0.0
8.4423e8
8.4423e8
1.19209e-7
3
ME
ME_natural_gas_combined_cycle
3
1
3
3.52336e7
0.0
0.0
8.77661e6
0.0
0.0
4.02739e6
2.26505e7
0.0
3.33663e6
0.0
0.0
2.19267e8
0.0
0.0
0.0
0.0
0.0
1.45243e8
0.0
2.19267e8
2.19267e8
0.0
4
MA
MA_solar_pv
1
1
4
1.27007e9
0.0
0.0
2.79327e8
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
1.5494e9
0.0
0.0
0.0
0.0
0.0
0.0
0.0
1.5494e9
1.5494e9
-2.86102e-6
5
CT
CT_onshore_wind
2
1
5
1.40748e9
0.0
0.0
6.25617e8
0.0
0.0
2.90683e6
0.0
0.0
0.0
0.0
0.0
2.036e9
0.0
0.0
0.0
0.0
0.0
0.0
0.0
2.036e9
2.036e9
-5.00679e-6
6
CT
CT_solar_pv
2
1
6
1.35108e9
0.0
0.0
2.97142e8
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
1.64822e9
0.0
0.0
0.0
0.0
0.0
0.0
0.0
1.64822e9
1.64822e9
9.53674e-7
7
ME
ME_onshore_wind
3
1
7
1.03673e9
0.0
0.0
4.60821e8
0.0
0.0
2.625e6
0.0
0.0
0.0
0.0
0.0
1.50017e9
0.0
0.0
0.0
0.0
0.0
0.0
0.0
1.50017e9
1.50017e9
2.38419e-6
8
MA
MA_battery
1
0
8
4.29792e7
2.23673e8
0.0
1.07426e7
5.59033e7
0.0
7.59532e5
0.0
8.97367e5
0.0
1.3432e8
0.0
4.48833e8
0.0
0.0
0.0
0.0
0.0
0.0
0.0
4.48833e8
4.69275e8
-2.0442e7
9
CT
CT_battery
2
0
9
1.08405e8
5.73615e8
0.0
2.70957e7
1.43365e8
0.0
2.1875e6
0.0
2.58447e6
0.0
5.24177e8
0.0
1.31941e9
0.0
0.0
0.0
0.0
0.0
0.0
0.0
1.31941e9
1.38143e9
-6.20165e7
10
ME
ME_battery
3
0
10
3.58043e7
1.03994e8
0.0
8.94925e6
2.59915e7
0.0
7.35552e5
0.0
8.69036e5
0.0
3.81057e7
0.0
2.03732e8
0.0
0.0
0.0
0.0
0.0
0.0
0.0
2.03732e8
2.14449e8
-1.0717e7
```
-
-
```julia
xnames = netrevenue[!,2]
names1 = ["Investment cost" "Fixed OM cost" "Variable OM cost" "Fuel cost" "Start Cost" "Battery charge cost" "CO2 Sequestration Cost" "Revenue"]
@@ -491,8 +478,6 @@ StatsPlots.scatter!(xnames,netrevenue[!,"Revenue"],label="Revenue",color="black"
![svg](./files/t8_cost.svg)
-
-
### Emissions
The file `emmissions.csv` gives the total CO2 emmissions per zone for each hour GenX runs. The first three rows give the marginal CO2 abatement cost in $/ton CO2.
@@ -502,14 +487,10 @@ The file `emmissions.csv` gives the total CO2 emmissions per zone for each hour
emm1 = CSV.read(joinpath(case,"results/emissions.csv"),DataFrame)
```
-
-
``` @raw html
1852×5 DataFrame
1827 rows omitted
Row
Zone
1
2
3
Total
String15
Float64
Float64
Float64
Float64
1
CO2_Price_1
444.921
0.0
0.0
0.0
2
CO2_Price_2
0.0
468.668
0.0
0.0
3
CO2_Price_3
0.0
0.0
240.86
0.0
4
AnnualSum
4.14279e6
1.30236e6
6.03017e5
6.04816e6
5
t1
0.0
0.0
0.0
0.0
6
t2
0.0
0.0
0.0
0.0
7
t3
0.0
0.0
0.0
0.0
8
t4
0.0
0.0
0.0
0.0
9
t5
0.0
0.0
0.0
0.0
10
t6
0.0
0.0
0.0
0.0
11
t7
0.0
0.0
0.0
0.0
12
t8
0.0
0.0
0.0
0.0
13
t9
0.0
0.0
0.0
0.0
⋮
⋮
⋮
⋮
⋮
⋮
1841
t1837
0.0
0.0
0.0
0.0
1842
t1838
0.0
0.0
0.0
0.0
1843
t1839
0.0
0.0
0.0
0.0
1844
t1840
0.0
0.0
0.0
0.0
1845
t1841
0.0
0.0
0.0
0.0
1846
t1842
0.0
0.0
0.0
0.0
1847
t1843
0.0
0.0
0.0
0.0
1848
t1844
0.0
0.0
0.0
0.0
1849
t1845
0.0
0.0
0.0
0.0
1850
t1846
0.0
0.0
0.0
0.0
1851
t1847
0.0
0.0
0.0
0.0
1852
t1848
0.0
0.0
0.0
0.0
```
-
-
```julia
# Pre-processing
tstart = 470
@@ -519,7 +500,6 @@ names_emm = ["Zone 1","Zone 2","Zone 3"]
emm_tot = DataFrame([emm1[3:end,2] emm1[3:end,3] emm1[3:end,4]],
["Zone 1","Zone 2","Zone 3"])
-
emm_plot = DataFrame([collect((tstart-3):(tend-3)) emm_tot[tstart:tend,1] repeat([names_emm[1]],(tend-tstart+1))],
["Hour","MW","Zone"]);
@@ -530,7 +510,6 @@ end
```
-
```julia
emm_plot |>
@vlplot(mark={:line},
@@ -541,11 +520,8 @@ emm_plot |>
![svg](./files/t8_emm1.svg)
-
-
Let's try changing the CO2 cap, as in Tutorial 7, and plotting the resulting emmissions.
-
```julia
genx_settings_TZ = YAML.load(open((joinpath(case,"settings/genx_settings.yml"))))
genx_settings_TZ["CO2Cap"] = 0
@@ -555,7 +531,6 @@ include("example_systems/1_three_zones/Run.jl")
# run outside of notebook
```
-
Configuring Settings
Time Series Data Already Clustered.
Configuring Solver
@@ -576,15 +551,12 @@ include("example_systems/1_three_zones/Run.jl")
Total number of resources: 10
-------------------------------------------------------
-
Thermal.csv Successfully Read.
Vre.csv Successfully Read.
Storage.csv Successfully Read.
Resource_energy_share_requirement.csv Successfully Read.
Resource_capacity_reserve_margin.csv Successfully Read.
Resource_minimum_capacity_requirement.csv Successfully Read.
-
-
Generators_variability.csv Successfully Read!
Validating time basis
Minimum_capacity_requirement.csv Successfully Read!
@@ -607,6 +579,7 @@ include("example_systems/1_three_zones/Run.jl")
Minimum Capacity Requirement Module
Time elapsed for model building is
0.531860834
+
Solving Model
Running HiGHS 1.6.0: Copyright (c) 2023 HiGHS under MIT licence terms
Presolving model
@@ -740,6 +713,7 @@ include("example_systems/1_three_zones/Run.jl")
Objective value : 5.5855435982e+03
HiGHS run time : 66.51
LP solved for primal
+
Writing Output
Time elapsed for writing costs is
0.099885792
@@ -799,20 +773,13 @@ include("example_systems/1_three_zones/Run.jl")
Time elapsed for writing is
0.530491792
-
-
```julia
emm2 = CSV.read(joinpath(case,"results_1/emissions.csv"),DataFrame)
```
-
-
``` @raw html
1849×5 DataFrame
1824 rows omitted
Row
Zone
1
2
3
Total
String15
Float64
Float64
Float64
Float64
1
AnnualSum
1.68155e7
1.41088e7
4310.21
3.09286e7
2
t1
997.169
0.0
0.0
997.169
3
t2
997.169
0.0
0.0
997.169
4
t3
997.169
0.0
0.0
997.169
5
t4
997.169
0.0
0.0
997.169
6
t5
997.169
0.0
0.0
997.169
7
t6
997.169
0.0
0.0
997.169
8
t7
997.169
0.0
0.0
997.169
9
t8
997.169
0.0
0.0
997.169
10
t9
997.169
0.0
0.0
997.169
11
t10
1471.46
0.0
0.0
1471.46
12
t11
997.169
0.0
0.0
997.169
13
t12
1115.81
0.0
0.0
1115.81
⋮
⋮
⋮
⋮
⋮
⋮
1838
t1837
2789.35
1012.99
0.0
3802.34
1839
t1838
2835.21
1012.99
0.0
3848.2
1840
t1839
2520.57
1012.99
0.0
3533.56
1841
t1840
1496.47
445.85
0.0
1942.32
1842
t1841
2571.26
1012.99
0.0
3584.25
1843
t1842
2835.21
1012.99
0.0
3848.2
1844
t1843
2835.21
1012.99
0.0
3848.2
1845
t1844
2625.42
960.184
0.0
3585.6
1846
t1845
2506.32
342.391
0.0
2848.71
1847
t1846
2277.59
342.391
0.0
2619.98
1848
t1847
1960.08
524.526
0.0
2484.6
1849
t1848
1566.77
342.391
0.0
1909.16
```
-
-
-
```julia
# Pre-processing
tstart = 470
@@ -822,7 +789,6 @@ names_emm = ["Zone 1","Zone 2","Zone 3"]
emm_tot2 = DataFrame([emm2[3:end,2] emm2[3:end,3] emm2[3:end,4]],
["Zone 1","Zone 2","Zone 3"])
-
emm_plot2 = DataFrame([collect((tstart-3):(tend-3)) emm_tot2[tstart:tend,1] repeat([names_emm[1]],(tend-tstart+1))],
["Hour","MW","Zone"]);
@@ -858,12 +824,9 @@ Plots.plot(collect((tstart-3):(tend-3)),emm1sum[tstart:tend],size=(800,400),labe
Plots.plot!(collect((tstart-3):(tend-3)),emm2sum[tstart:tend],label="No CO2 Cap",linewidth = 1.5)
```
![svg](./files/t8_emm_comp.svg)
-
-
Finally, set the CO2 Cap back to 2:
-
```julia
genx_settings_TZ["CO2Cap"] = 2
YAML.write_file((joinpath(case,"settings/genx_settings.yml")), genx_settings_TZ)
diff --git a/docs/src/Tutorials/files/t8_cap.svg b/docs/src/Tutorials/files/t8_cap.svg
index c40ef607c6..a8747901c7 100644
--- a/docs/src/Tutorials/files/t8_cap.svg
+++ b/docs/src/Tutorials/files/t8_cap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
From 165836da9c2fff190220c8d90447bbe218da310d Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Wed, 4 Dec 2024 11:44:52 -0500
Subject: [PATCH 08/17] Add `add_similar_to_expression!` for arrays of
`Number`s (#798)
---
CHANGELOG.md | 1 +
Project.toml | 2 +-
src/model/core/operational_reserves.jl | 2 +-
src/model/expression_manipulation.jl | 9 +++++++++
test/expression_manipulation_test.jl | 6 ++++++
5 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 809a969555..b192f55b74 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
number of concurrent Gurobi uses is limited (#783).
- Additional long-duration storage constraints to bound state of charge in
non-representative periods (#781).
+- New version of `add_similar_to_expression!` to support arrays of `Number`s. (#798)
### Changed
- The `charge.csv` and `storage.csv` files now include only resources with
diff --git a/Project.toml b/Project.toml
index a6d2b1048d..15ac369587 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.16"
+version = "0.4.1-dev.17"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/src/model/core/operational_reserves.jl b/src/model/core/operational_reserves.jl
index 54d6dab073..1790db3bb1 100644
--- a/src/model/core/operational_reserves.jl
+++ b/src/model/core/operational_reserves.jl
@@ -270,7 +270,7 @@ function operational_reserves_core!(EP::Model, inputs::Dict, setup::Dict)
# N-1 contingency requirement is considered only if Unit Commitment is being modeled
if UCommit >= 1 &&
(inputs["pDynamic_Contingency"] >= 1 || inputs["pStatic_Contingency"] > 0)
- add_to_expression!(EP[:eRsvReq], EP[:eContingencyReq])
+ add_similar_to_expression!(EP[:eRsvReq], EP[:eContingencyReq])
end
## Objective Function Expressions ##
diff --git a/src/model/expression_manipulation.jl b/src/model/expression_manipulation.jl
index 33b0b8e8e2..7d0dd4e566 100644
--- a/src/model/expression_manipulation.jl
+++ b/src/model/expression_manipulation.jl
@@ -138,6 +138,15 @@ function add_similar_to_expression!(expr1::AbstractArray{GenericAffExpr{C, T}, d
return nothing
end
+# If the expressions are vectors of numbers, use the += operator
+function add_similar_to_expression!(arr1::AbstractArray{T, dims},
+ arr2::AbstractArray{T, dims}) where {T <: Number, dims}
+ for i in eachindex(arr1)
+ arr1[i] += arr2[i]
+ end
+ return nothing
+end
+
###### ###### ###### ###### ###### ######
# Element-wise addition of one term into an expression
# Both arrays must have the same dimensions
diff --git a/test/expression_manipulation_test.jl b/test/expression_manipulation_test.jl
index 71891d80ac..6c403284d0 100644
--- a/test/expression_manipulation_test.jl
+++ b/test/expression_manipulation_test.jl
@@ -92,6 +92,12 @@ let
GenX.add_similar_to_expression!(EP[:large_expr], EP[:large_const_expr])
@test all(EP[:large_expr][:] .== 18.0)
+ # Test add_similar_to_expression! with AbstractArray{Number}
+ @expression(EP, eArr1[i = 1:100, j = 1:50], i * 10.0+j * 10.0)
+ @expression(EP, eArr2[i = 1:100, j = 1:50], -(i * 10.0 + j * 10.0))
+ GenX.add_similar_to_expression!(EP[:eArr1], EP[:eArr2])
+ @test all(EP[:eArr1][:] .== 0.0)
+
# Test add_similar_to_expression! returns an error if the dimensions don't match
GenX.create_empty_expression!(EP, :small_expr, (2, 3))
@test_throws ErrorException GenX.add_similar_to_expression!(EP[:large_expr],
From faaf58af7a6becffd60468abdf652ff39d3f2f18 Mon Sep 17 00:00:00 2001
From: "Chakrabarti, Sambuddha (Sam)"
Date: Wed, 4 Dec 2024 12:04:10 -0500
Subject: [PATCH 09/17] Update README.md and doc with development lead from
Binghamton Univ (#779)
---
Project.toml | 2 +-
README.md | 2 +-
docs/src/index.md | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Project.toml b/Project.toml
index 15ac369587..116fd5ba18 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.17"
+version = "0.4.1-dev.18"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/README.md b/README.md
index e6ce3b87af..725f696499 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ that incorporates several state-of-the-art practices in electricity system plann
The model was [originally developed](https://energy.mit.edu/publication/enhanced-decision-support-changing-electricity-landscape/) by
[Jesse D. Jenkins](https://mae.princeton.edu/people/faculty/jenkins) and
[Nestor A. Sepulveda](https://energy.mit.edu/profile/nestor-sepulveda/) at the Massachusetts Institute of Technology and is now jointly maintained by
-[a team of contributors](https://github.com/GenXProject/GenX#genx-team) at the Princeton University ZERO Lab (led by Jenkins), MIT (led by [Ruaridh MacDonald](https://energy.mit.edu/profile/ruaridh-macdonald/)), and NYU (led by [Dharik Mallapragada](https://engineering.nyu.edu/faculty/dharik-mallapragada)).
+[a team of contributors](https://github.com/GenXProject/GenX#genx-team) at the Princeton University ZERO Lab (led by Jenkins), MIT (led by [Ruaridh MacDonald](https://energy.mit.edu/profile/ruaridh-macdonald/)), NYU (led by [Dharik Mallapragada](https://engineering.nyu.edu/faculty/dharik-mallapragada)), and Binghamton University (led by [Neha Patankar](https://www.binghamton.edu/ssie/people/profile.html?id=npatankar)).
GenX is a constrained linear or mixed integer linear optimization model that determines the portfolio of electricity generation,
storage, transmission, and demand-side resource investments and operational decisions to meet electricity demand in one or more future planning years at lowest cost,
diff --git a/docs/src/index.md b/docs/src/index.md
index 3a87b5b5fb..91dfc7bafe 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -9,7 +9,7 @@
GenX is a highly-configurable, [open source](https://github.com/GenXProject/GenX/blob/main/LICENSE) electricity resource capacity expansion model that incorporates several state-of-the-art practices in electricity system planning to offer improved decision support for a changing electricity landscape.
-The model was [originally developed](https://energy.mit.edu/publication/enhanced-decision-support-changing-electricity-landscape/) by [Jesse D. Jenkins](https://mae.princeton.edu/people/faculty/jenkins) and [Nestor A. Sepulveda](https://energy.mit.edu/profile/nestor-sepulveda/) at the Massachusetts Institute of Technology and is now jointly maintained by [a team of contributors](https://energy.mit.edu/genx/#team) at the Princeton University ZERO Lab (led by Jenkins), MIT (led by [Ruaridh MacDonald](https://energy.mit.edu/profile/ruaridh-macdonald/)), and NYU (led by [Dharik Mallapragada](https://engineering.nyu.edu/faculty/dharik-mallapragada)).
+The model was [originally developed](https://energy.mit.edu/publication/enhanced-decision-support-changing-electricity-landscape/) by [Jesse D. Jenkins](https://mae.princeton.edu/people/faculty/jenkins) and [Nestor A. Sepulveda](https://energy.mit.edu/profile/nestor-sepulveda/) at the Massachusetts Institute of Technology and is now jointly maintained by [a team of contributors](https://energy.mit.edu/genx/#team) at the Princeton University ZERO Lab (led by Jenkins), MIT (led by [Ruaridh MacDonald](https://energy.mit.edu/profile/ruaridh-macdonald/)), NYU (led by [Dharik Mallapragada](https://engineering.nyu.edu/faculty/dharik-mallapragada)), and Binghamton University (led by [Neha Patankar](https://www.binghamton.edu/ssie/people/profile.html?id=npatankar)).
GenX is a constrained linear or mixed integer linear optimization model that determines the portfolio of electricity generation, storage, transmission, and demand-side resource investments and operational decisions to meet electricity demand in one or more future planning years at lowest cost, while subject to a variety of power system operational constraints, resource availability limits, and other imposed environmental, market design, and policy constraints.
From 012a28d7a7a7b46407f157a7f4119f47d6adbff3 Mon Sep 17 00:00:00 2001
From: "Chakrabarti, Sambuddha (Sam)"
Date: Wed, 4 Dec 2024 15:22:34 -0500
Subject: [PATCH 10/17] Fix typos in documentation (#768)
Co-authored-by: lbonaldo
---
Project.toml | 2 +-
docs/src/Model_Concept_Overview/model_notation.md | 2 +-
src/model/core/transmission/dcopf_transmission.jl | 4 ++--
src/model/core/transmission/investment_transmission.jl | 6 ++++--
src/model/core/transmission/transmission.jl | 2 +-
5 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/Project.toml b/Project.toml
index 116fd5ba18..5b2952361c 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.18"
+version = "0.4.1-dev.19"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/docs/src/Model_Concept_Overview/model_notation.md b/docs/src/Model_Concept_Overview/model_notation.md
index 46369433ea..a805fd96cd 100644
--- a/docs/src/Model_Concept_Overview/model_notation.md
+++ b/docs/src/Model_Concept_Overview/model_notation.md
@@ -135,7 +135,7 @@ $\mathcal{W} \subseteq \mathcal{G}$ | where $\mathcal{W}$ set of hydroelectric g
|$r^{ac,cha}_{y,z,t} \in \mathbb{R}_+$ | Upward spinning reserves contribution \[MW\] for the storage AC charge component from technology $y$ in zone $z$ at time $t$ - only applicable for co-located VRE and storage resources with a storage AC charge component, $y \in \mathcal{VS}^{sym,ac} \cup y \in \mathcal{VS}^{asym,ac,cha}$ |
|$\alpha^{Contingency,Aux}_{y,z} \in \{0,1\}$ | Binary variable that is set to be 1 if the total installed capacity $\Delta^{\text{total}}_{y,z} > 0$ for any generator $y \in \mathcal{UC}$ and zone $z$, and can be 0 otherwise |
|$\Phi_{l,t} \in \mathbb{R}_+$ | Power flow in line $l$ at time step $t$ \[MWh\]|
-|$\theta_{z,t} \in \mathbb{R}$ | Volta phase angle in zone $z$ at time step $t$ \[radian\]|
+|$\theta_{z,t} \in \mathbb{R}$ | Voltage phase angle in zone $z$ at time step $t$ \[radian\]|
|$\nu_{y,z,t}$ | Commitment state of the generation cluster $y$ in zone $z$ at time $t$|
|$\chi_{y,z,t}$ | Number of startup decisions, of the generation cluster $y$ in zone $z$ at time $t$|
|$\zeta_{y,z,t}$ | Number of shutdown decisions, of the generation cluster $y$ in zone $z$ at time $t$|
diff --git a/src/model/core/transmission/dcopf_transmission.jl b/src/model/core/transmission/dcopf_transmission.jl
index de2dcfd5bf..ab2537ca01 100644
--- a/src/model/core/transmission/dcopf_transmission.jl
+++ b/src/model/core/transmission/dcopf_transmission.jl
@@ -1,5 +1,5 @@
@doc raw"""
- function dcopf_transmission!(EP::Model, inputs::Dict, setup::Dict)
+ dcopf_transmission!(EP::Model, inputs::Dict, setup::Dict)
The addtional constraints imposed upon the line flows in the case of DC-OPF are as follows:
For the definition of the line flows, in terms of the voltage phase angles:
```math
@@ -14,7 +14,7 @@ For imposing the constraint of maximum allowed voltage phase angle difference ac
& \sum_{z\in \mathcal{Z}}{(\varphi^{map}_{l,z} \times \theta_{z,t})} \geq -\Delta \theta^{\max}_{l} \quad \forall l \in \mathcal{L}, \forall t \in \mathcal{T}\\
\end{aligned}
```
-Finally, we enforce the reference voltage phase angle constraint:
+Finally, we enforce the reference voltage phase angle constraint (for the slack bus/reference bus):
```math
\begin{aligned}
\theta_{1,t} = 0 \quad \forall t \in \mathcal{T}
diff --git a/src/model/core/transmission/investment_transmission.jl b/src/model/core/transmission/investment_transmission.jl
index cb67e708eb..f80b898e9a 100644
--- a/src/model/core/transmission/investment_transmission.jl
+++ b/src/model/core/transmission/investment_transmission.jl
@@ -1,13 +1,15 @@
@doc raw"""
- function investment_transmission!(EP::Model, inputs::Dict, setup::Dict)
-This function model transmission expansion and adds transmission reinforcement or construction costs to the objective function. Transmission reinforcement costs are equal to the sum across all lines of the product between the transmission reinforcement/construction cost, $pi^{TCAP}_{l}$, times the additional transmission capacity variable, $\bigtriangleup\varphi^{cap}_{l}$.
+ investment_transmission!(EP::Model, inputs::Dict, setup::Dict)
+This function model transmission expansion and adds transmission reinforcement or construction costs to the objective function. Transmission reinforcement costs are equal to the sum across all lines of the product between the transmission reinforcement/construction cost, $\pi^{TCAP}_{l}$, times the additional transmission capacity variable, $\bigtriangleup\varphi^{cap}_{l}$.
```math
\begin{aligned}
& \sum_{l \in \mathcal{L}}\left(\pi^{TCAP}_{l} \times \bigtriangleup\varphi^{cap}_{l}\right)
\end{aligned}
```
Note that fixed O\&M and replacement capital costs (depreciation) for existing transmission capacity is treated as a sunk cost and not included explicitly in the GenX objective function.
+
**Accounting for Transmission Between Zones**
+
Available transmission capacity between zones is set equal to the existing line's maximum power transfer capacity, $\overline{\varphi^{cap}_{l}}$, plus any transmission capacity added on that line (for lines eligible for expansion in the set $\mathcal{E}$).
```math
\begin{aligned}
diff --git a/src/model/core/transmission/transmission.jl b/src/model/core/transmission/transmission.jl
index ca5c637159..4020c8ea2e 100644
--- a/src/model/core/transmission/transmission.jl
+++ b/src/model/core/transmission/transmission.jl
@@ -25,7 +25,7 @@ Transmission losses due to power flows can be accounted for in three different w
& \beta_{l,t}(\cdot) = \begin{cases} 0 & \text{if~} \text{losses.~0} \\ \\ \varphi^{loss}_{l}\times \mid \Phi_{l,t} \mid & \text{if~} \text{losses.~1} \\ \\ \ell_{l,t} &\text{if~} \text{losses.~2} \end{cases}, &\quad \forall l \in \mathcal{L},\forall t \in \mathcal{T}
\end{aligned}
```
-For the second option, an absolute value approximation is utilized to calculate the magnitude of the power flow on each line (reflecting the fact that negative power flows for a line linking nodes $i$ and $j$ represents flows from node $j$ to $i$ and causes the same magnitude of losses as an equal power flow from $i$ to $j$). This absolute value function is linearized such that the flow in the line must be equal to the subtraction of the auxiliary variable for flow in the positive direction, $\Phi^{+}_{l,t}$, and the auxiliary variable for flow in the negative direction, $\Phi^{+}_{l,t}$, of the line. Then, the magnitude of the flow is calculated as the sum of the two auxiliary variables. The sum of positive and negative directional flows are also constrained by the line flow capacity.
+For the second option, an absolute value approximation is utilized to calculate the magnitude of the power flow on each line (reflecting the fact that negative power flows for a line linking nodes $i$ and $j$ represents flows from node $j$ to $i$ and causes the same magnitude of losses as an equal power flow from $i$ to $j$). This absolute value function is linearized such that the flow in the line must be equal to the subtraction of the auxiliary variable for flow in the positive direction, $\Phi^{+}_{l,t}$, and the auxiliary variable for flow in the negative direction, $\Phi^{-}_{l,t}$, of the line. Then, the magnitude of the flow is calculated as the sum of the two auxiliary variables. The sum of positive and negative directional flows are also constrained by the line flow capacity.
```math
\begin{aligned}
% trasmission losses simple
From cccab41aec52a1d762302fbfbe48bb8cc289ff1f Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Mon, 23 Dec 2024 09:28:33 -0500
Subject: [PATCH 11/17] Settings flag for new LDS constraints (#801)
---
CHANGELOG.md | 3 +
Project.toml | 2 +-
.../Tutorials/Tutorial_4_model_generation.md | 2 +-
docs/src/User_Guide/model_configuration.md | 3 +
.../resources/Storage.csv | 8 +--
.../settings/genx_settings.yml | 1 +
src/configure_settings/configure_settings.jl | 1 +
src/model/generate_model.jl | 2 +-
.../hydro/hydro_inter_period_linkage.jl | 57 ++++++++++---------
.../storage/long_duration_storage.jl | 45 ++++++++-------
10 files changed, 72 insertions(+), 52 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b192f55b74..4484607652 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,9 @@ number of concurrent Gurobi uses is limited (#783).
- Additional long-duration storage constraints to bound state of charge in
non-representative periods (#781).
- New version of `add_similar_to_expression!` to support arrays of `Number`s. (#798)
+- New settings flag `LDSAdditionalConstraints` to provide flexibility in
+activating new long-duration storage constraints (#781). Can be set in the GenX
+settings file (PR #801).
### Changed
- The `charge.csv` and `storage.csv` files now include only resources with
diff --git a/Project.toml b/Project.toml
index 5b2952361c..ccbd22f3f9 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.19"
+version = "0.4.1-dev.20"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/docs/src/Tutorials/Tutorial_4_model_generation.md b/docs/src/Tutorials/Tutorial_4_model_generation.md
index 4ef5f566a1..0d50bab766 100644
--- a/docs/src/Tutorials/Tutorial_4_model_generation.md
+++ b/docs/src/Tutorials/Tutorial_4_model_generation.md
@@ -474,7 +474,7 @@ end
# Model constraints, variables, expression related to reservoir hydropower resources with long duration storage
if inputs["REP_PERIOD"] > 1 && !isempty(inputs["STOR_HYDRO_LONG_DURATION"])
- GenX.hydro_inter_period_linkage!(EP, inputs)
+ GenX.hydro_inter_period_linkage!(EP, inputs, setup)
end
# Model constraints, variables, expression related to demand flexibility resources
diff --git a/docs/src/User_Guide/model_configuration.md b/docs/src/User_Guide/model_configuration.md
index 0bb8b1dc13..a1526b3eab 100644
--- a/docs/src/User_Guide/model_configuration.md
+++ b/docs/src/User_Guide/model_configuration.md
@@ -31,6 +31,9 @@ The following tables summarize the model settings parameters and their default/p
|StorageVirtualDischarge | Flag to enable contributions that a storage device makes to the capacity reserve margin without generating power.|
||1 = activate the virtual discharge of storage resources.|
||0 = do not activate the virtual discharge of storage resources.|
+|LDSAdditionalConstraints | Flag to activate additional constraints for long duration storage resources to prevent violation of SoC limits in non-representative periods.|
+||1 = activate additional constraints.|
+||0 = do not activate additional constraints.|
|HourlyMatching| Constraint to match generation from clean sources with hourly consumption.|
||1 = Constraint is active.|
||0 = Constraint is not active.|
diff --git a/example_systems/3_three_zones_w_co2_capture/resources/Storage.csv b/example_systems/3_three_zones_w_co2_capture/resources/Storage.csv
index 238c5acd03..c2fcbd3628 100644
--- a/example_systems/3_three_zones_w_co2_capture/resources/Storage.csv
+++ b/example_systems/3_three_zones_w_co2_capture/resources/Storage.csv
@@ -1,4 +1,4 @@
-Resource,Zone,Model,New_Build,Can_Retire,Existing_Cap_MW,Existing_Cap_MWh,Max_Cap_MW,Max_Cap_MWh,Min_Cap_MW,Min_Cap_MWh,Inv_Cost_per_MWyr,Inv_Cost_per_MWhyr,Fixed_OM_Cost_per_MWyr,Fixed_OM_Cost_per_MWhyr,Var_OM_Cost_per_MWh,Var_OM_Cost_per_MWh_In,Self_Disch,Eff_Up,Eff_Down,Min_Duration,Max_Duration,Reg_Max,Rsv_Max,Reg_Cost,Rsv_Cost,region,cluster
-MA_battery,1,1,1,0,0,0,-1,-1,0,0,19584,22494,4895,5622,0.15,0.15,0,0.92,0.92,1,10,0,0,0,0,MA,0
-CT_battery,2,1,1,0,0,0,-1,-1,0,0,19584,22494,4895,5622,0.15,0.15,0,0.92,0.92,1,10,0,0,0,0,CT,0
-ME_battery,3,1,1,0,0,0,-1,-1,0,0,19584,22494,4895,5622,0.15,0.15,0,0.92,0.92,1,10,0,0,0,0,ME,0
\ No newline at end of file
+Resource,Zone,LDS,Model,New_Build,Can_Retire,Existing_Cap_MW,Existing_Cap_MWh,Max_Cap_MW,Max_Cap_MWh,Min_Cap_MW,Min_Cap_MWh,Inv_Cost_per_MWyr,Inv_Cost_per_MWhyr,Fixed_OM_Cost_per_MWyr,Fixed_OM_Cost_per_MWhyr,Var_OM_Cost_per_MWh,Var_OM_Cost_per_MWh_In,Self_Disch,Eff_Up,Eff_Down,Min_Duration,Max_Duration,Reg_Max,Rsv_Max,Reg_Cost,Rsv_Cost,region,cluster
+MA_battery,1,0,1,1,0,0,0,-1,-1,0,0,19584,22494,4895,5622,0.15,0.15,0,0.92,0.92,1,10,0,0,0,0,MA,0
+CT_battery,2,0,1,1,0,0,0,-1,-1,0,0,19584,22494,4895,5622,0.15,0.15,0,0.92,0.92,1,10,0,0,0,0,CT,0
+ME_battery,3,0,1,1,0,0,0,-1,-1,0,0,19584,22494,4895,5622,0.15,0.15,0,0.92,0.92,1,10,0,0,0,0,ME,0
\ No newline at end of file
diff --git a/example_systems/3_three_zones_w_co2_capture/settings/genx_settings.yml b/example_systems/3_three_zones_w_co2_capture/settings/genx_settings.yml
index ac456b6fcb..27f3eef52f 100644
--- a/example_systems/3_three_zones_w_co2_capture/settings/genx_settings.yml
+++ b/example_systems/3_three_zones_w_co2_capture/settings/genx_settings.yml
@@ -10,3 +10,4 @@ ParameterScale: 1 # Turn on parameter scaling wherein demand, capacity and power
WriteShadowPrices: 1 # Write shadow prices of LP or relaxed MILP; 0 = not active; 1 = active
UCommit: 2 # Unit committment of thermal power plants; 0 = not active; 1 = active using integer clestering; 2 = active using linearized clustering
TimeDomainReduction: 1 # Time domain reduce (i.e. cluster) inputs based on Demand_data.csv, Generators_variability.csv, and Fuels_data.csv; 0 = not active (use input data as provided); 0 = active (cluster input data, or use data that has already been clustered)
+LDSAdditionalConstraints: 1 # Activate additional constraints to prevent violation of SoC limits in non-representative periods; 0 = not active; 1 = active
\ No newline at end of file
diff --git a/src/configure_settings/configure_settings.jl b/src/configure_settings/configure_settings.jl
index 01d4603179..9df418e334 100644
--- a/src/configure_settings/configure_settings.jl
+++ b/src/configure_settings/configure_settings.jl
@@ -8,6 +8,7 @@ function default_settings()
"CapacityReserveMargin" => 0,
"CO2Cap" => 0,
"StorageLosses" => 1,
+ "LDSAdditionalConstraints" => 1,
"VirtualChargeDischargeCost" => 1, # $/MWh
"MinCapReq" => 0,
"MaxCapReq" => 0,
diff --git a/src/model/generate_model.jl b/src/model/generate_model.jl
index d047fcbe11..0067b0c553 100644
--- a/src/model/generate_model.jl
+++ b/src/model/generate_model.jl
@@ -171,7 +171,7 @@ function generate_model(setup::Dict, inputs::Dict, OPTIMIZER::MOI.OptimizerWithA
# Model constraints, variables, expression related to reservoir hydropower resources with long duration storage
if inputs["REP_PERIOD"] > 1 && !isempty(inputs["STOR_HYDRO_LONG_DURATION"])
- hydro_inter_period_linkage!(EP, inputs)
+ hydro_inter_period_linkage!(EP, inputs, setup)
end
# Model constraints, variables, expression related to demand flexibility resources
diff --git a/src/model/resources/hydro/hydro_inter_period_linkage.jl b/src/model/resources/hydro/hydro_inter_period_linkage.jl
index e1ca04e975..19761d07ea 100644
--- a/src/model/resources/hydro/hydro_inter_period_linkage.jl
+++ b/src/model/resources/hydro/hydro_inter_period_linkage.jl
@@ -1,5 +1,5 @@
@doc raw"""
- hydro_inter_period_linkage!(EP::Model, inputs::Dict)
+ hydro_inter_period_linkage!(EP::Model, inputs::Dict, setup::Dict)
This function creates variables and constraints enabling modeling of long duration storage resources when modeling representative time periods.
**Storage inventory balance at beginning of each representative period**
@@ -80,7 +80,7 @@ Similarly, the minimum storage content is imposed to be positive in every period
Additional details on this approach are available in [Parolin et al., 2024](https://doi.org/10.48550/arXiv.2409.19079).
"""
-function hydro_inter_period_linkage!(EP::Model, inputs::Dict)
+function hydro_inter_period_linkage!(EP::Model, inputs::Dict, setup::Dict)
println("Long Duration Storage Module for Hydro Reservoir")
gen = inputs["RESOURCES"]
@@ -96,6 +96,7 @@ function hydro_inter_period_linkage!(EP::Model, inputs::Dict)
MODELED_PERIODS_INDEX = 1:NPeriods
REP_PERIODS_INDEX = MODELED_PERIODS_INDEX[dfPeriodMap[!, :Rep_Period] .== MODELED_PERIODS_INDEX]
+ NON_REP_PERIODS_INDEX = setdiff(MODELED_PERIODS_INDEX, REP_PERIODS_INDEX)
### Variables ###
@@ -108,11 +109,14 @@ function hydro_inter_period_linkage!(EP::Model, inputs::Dict)
# Build up inventory can be positive or negative
@variable(EP, vdSOC_HYDRO[y in STOR_HYDRO_LONG_DURATION, w = 1:REP_PERIOD])
- # Maximum positive storage inventory change within subperiod
- @variable(EP, vdSOC_maxPos_HYDRO[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD] >= 0)
+ # Additional constraints to prevent violation of SoC limits in non-representative periods
+ if setup["LDSAdditionalConstraints"] == 1 && !isempty(NON_REP_PERIODS_INDEX)
+ # Maximum positive storage inventory change within subperiod
+ @variable(EP, vdSOC_maxPos_HYDRO[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD] >= 0)
- # Maximum negative storage inventory change within subperiod
- @variable(EP, vdSOC_maxNeg_HYDRO[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD] <= 0)
+ # Maximum negative storage inventory change within subperiod
+ @variable(EP, vdSOC_maxNeg_HYDRO[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD] <= 0)
+ end
### Constraints ###
@@ -155,26 +159,27 @@ function hydro_inter_period_linkage!(EP::Model, inputs::Dict)
vSOC_HYDROw[y,r]==EP[:vS_HYDRO][y, hours_per_subperiod * dfPeriodMap[r, :Rep_Period_Index]] -
vdSOC_HYDRO[y, dfPeriodMap[r, :Rep_Period_Index]])
- # Extract maximum storage level variation (positive) within subperiod
- @constraint(EP, cMaxSoCVarPos_H[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
+ if setup["LDSAdditionalConstraints"] == 1 && !isempty(NON_REP_PERIODS_INDEX)
+ # Extract maximum storage level variation (positive) within subperiod
+ @constraint(EP, cMaxSoCVarPos_H[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
vdSOC_maxPos_HYDRO[y,w] >= EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+t] - EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+1])
- # Extract maximum storage level variation (negative) within subperiod
- @constraint(EP, cMaxSoCVarNeg_H[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
- vdSOC_maxNeg_HYDRO[y,w] <= EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+t] - EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+1])
-
- # Max storage content within each modeled period cannot exceed installed energy capacity
- @constraint(EP, cSoCLongDurationStorageMaxInt_H[y in STOR_HYDRO_LONG_DURATION, r in MODELED_PERIODS_INDEX],
- vSOC_HYDROw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
- -EP[:vSPILL][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]
- +inputs["pP_Max"][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]*EP[:eTotalCap][y]
- +vdSOC_maxPos_HYDRO[y,dfPeriodMap[r,:Rep_Period_Index]] <= hydro_energy_to_power_ratio(gen[y])*EP[:eTotalCap][y])
-
- # Min storage content within each modeled period cannot be negative
- @constraint(EP, cSoCLongDurationStorageMinInt_H[y in STOR_HYDRO_LONG_DURATION, r in MODELED_PERIODS_INDEX],
- vSOC_HYDROw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
- -EP[:vSPILL][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]
- +inputs["pP_Max"][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]*EP[:eTotalCap][y]
- +vdSOC_maxPos_HYDRO[y,dfPeriodMap[r,:Rep_Period_Index]] >= 0)
-
+ # Extract maximum storage level variation (negative) within subperiod
+ @constraint(EP, cMaxSoCVarNeg_H[y in STOR_HYDRO_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
+ vdSOC_maxNeg_HYDRO[y,w] <= EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+t] - EP[:vS_HYDRO][y,hours_per_subperiod*(w-1)+1])
+
+ # Max storage content within each modeled period cannot exceed installed energy capacity
+ @constraint(EP, cSoCLongDurationStorageMaxInt_H[y in STOR_HYDRO_LONG_DURATION, r in NON_REP_PERIODS_INDEX],
+ vSOC_HYDROw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ -EP[:vSPILL][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]
+ +inputs["pP_Max"][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]*EP[:eTotalCap][y]
+ +vdSOC_maxPos_HYDRO[y,dfPeriodMap[r,:Rep_Period_Index]] <= hydro_energy_to_power_ratio(gen[y])*EP[:eTotalCap][y])
+
+ # Min storage content within each modeled period cannot be negative
+ @constraint(EP, cSoCLongDurationStorageMinInt_H[y in STOR_HYDRO_LONG_DURATION, r in NON_REP_PERIODS_INDEX],
+ vSOC_HYDROw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ -EP[:vSPILL][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]
+ +inputs["pP_Max"][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1]*EP[:eTotalCap][y]
+ +vdSOC_maxPos_HYDRO[y,dfPeriodMap[r,:Rep_Period_Index]] >= 0)
+ end
end
diff --git a/src/model/resources/storage/long_duration_storage.jl b/src/model/resources/storage/long_duration_storage.jl
index 05c2e56b1e..189381f895 100644
--- a/src/model/resources/storage/long_duration_storage.jl
+++ b/src/model/resources/storage/long_duration_storage.jl
@@ -112,6 +112,7 @@ function long_duration_storage!(EP::Model, inputs::Dict, setup::Dict)
MODELED_PERIODS_INDEX = 1:NPeriods
REP_PERIODS_INDEX = MODELED_PERIODS_INDEX[dfPeriodMap[!, :Rep_Period] .== MODELED_PERIODS_INDEX]
+ NON_REP_PERIODS_INDEX = setdiff(MODELED_PERIODS_INDEX, REP_PERIODS_INDEX)
### Variables ###
@@ -133,11 +134,14 @@ function long_duration_storage!(EP::Model, inputs::Dict, setup::Dict)
@variable(EP, vCAPRES_dsoc[y in STOR_LONG_DURATION, w = 1:REP_PERIOD])
end
- # Maximum positive storage inventory change within subperiod
- @variable(EP, vdSOC_maxPos[y in STOR_LONG_DURATION, w=1:REP_PERIOD] >= 0)
+ # Additional constraints to prevent violation of SoC limits in non-representative periods
+ if setup["LDSAdditionalConstraints"] == 1 && !isempty(NON_REP_PERIODS_INDEX)
+ # Maximum positive storage inventory change within subperiod
+ @variable(EP, vdSOC_maxPos[y in STOR_LONG_DURATION, w=1:REP_PERIOD] >= 0)
- # Maximum negative storage inventory change within subperiod
- @variable(EP, vdSOC_maxNeg[y in STOR_LONG_DURATION, w=1:REP_PERIOD] <= 0)
+ # Maximum negative storage inventory change within subperiod
+ @variable(EP, vdSOC_maxNeg[y in STOR_LONG_DURATION, w=1:REP_PERIOD] <= 0)
+ end
### Constraints ###
@@ -225,23 +229,26 @@ function long_duration_storage!(EP::Model, inputs::Dict, setup::Dict)
vSOCw[y, r]>=vCAPRES_socw[y, r])
end
- # Extract maximum storage level variation (positive) within subperiod
- @constraint(EP, cMaxSoCVarPos[y in STOR_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
+ if setup["LDSAdditionalConstraints"] == 1 && !isempty(NON_REP_PERIODS_INDEX)
+ # Extract maximum storage level variation (positive) within subperiod
+ @constraint(EP, cMaxSoCVarPos[y in STOR_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
vdSOC_maxPos[y,w] >= EP[:vS][y,hours_per_subperiod*(w-1)+t] - EP[:vS][y,hours_per_subperiod*(w-1)+1])
- # Extract maximum storage level variation (negative) within subperiod
- @constraint(EP, cMaxSoCVarNeg[y in STOR_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
+ # Extract maximum storage level variation (negative) within subperiod
+ @constraint(EP, cMaxSoCVarNeg[y in STOR_LONG_DURATION, w=1:REP_PERIOD, t=2:hours_per_subperiod],
vdSOC_maxNeg[y,w] <= EP[:vS][y,hours_per_subperiod*(w-1)+t] - EP[:vS][y,hours_per_subperiod*(w-1)+1])
- # Max storage content within each modeled period cannot exceed installed energy capacity
- @constraint(EP, cSoCLongDurationStorageMaxInt[y in STOR_LONG_DURATION, r in MODELED_PERIODS_INDEX],
- (1-self_discharge(gen[y]))*vSOCw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
- +(efficiency_up(gen[y])*EP[:vCHARGE][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
- +vdSOC_maxPos[y,dfPeriodMap[r,:Rep_Period_Index]] <= EP[:eTotalCapEnergy][y])
-
- # Min storage content within each modeled period cannot be negative
- @constraint(EP, cSoCLongDurationStorageMinInt[y in STOR_LONG_DURATION, r in MODELED_PERIODS_INDEX],
- (1-self_discharge(gen[y]))*vSOCw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
- +(efficiency_up(gen[y])*EP[:vCHARGE][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
- +vdSOC_maxNeg[y,dfPeriodMap[r,:Rep_Period_Index]] >= 0)
+ # Max storage content within each modeled period cannot exceed installed energy capacity
+ @constraint(EP, cSoCLongDurationStorageMaxInt[y in STOR_LONG_DURATION, r in NON_REP_PERIODS_INDEX],
+ (1-self_discharge(gen[y]))*vSOCw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ +(efficiency_up(gen[y])*EP[:vCHARGE][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ +vdSOC_maxPos[y,dfPeriodMap[r,:Rep_Period_Index]] <= EP[:eTotalCapEnergy][y])
+
+ # Min storage content within each modeled period cannot be negative
+ @constraint(EP, cSoCLongDurationStorageMinInt[y in STOR_LONG_DURATION, r in NON_REP_PERIODS_INDEX],
+ (1-self_discharge(gen[y]))*vSOCw[y,r]-(1/efficiency_down(gen[y])*EP[:vP][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ +(efficiency_up(gen[y])*EP[:vCHARGE][y,hours_per_subperiod*(dfPeriodMap[r,:Rep_Period_Index]-1)+1])
+ +vdSOC_maxNeg[y,dfPeriodMap[r,:Rep_Period_Index]] >= 0)
+ end
end
+
From 74650ddd358c094f8be8c890175af4d9ac0a405d Mon Sep 17 00:00:00 2001
From: lbonaldo
Date: Mon, 23 Dec 2024 16:12:54 -0500
Subject: [PATCH 12/17] Add instructions for skipping precompilation
---
src/startup/genx_startup.jl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/startup/genx_startup.jl b/src/startup/genx_startup.jl
index 847af0b8b1..76ff8fdbc2 100644
--- a/src/startup/genx_startup.jl
+++ b/src/startup/genx_startup.jl
@@ -50,6 +50,9 @@ Returns `nothing`.
"""
function _precompile()
@info "Running precompile script for GenX. This may take a few minutes."
+ @info "If you want to skip this step, please set the environment variable " *
+ "`GENX_PRECOMPILE` to `false` before running `using GenX`. \n" *
+ "Example: `ENV[\"GENX_PRECOMPILE\"] = \"false\"`."
redirect_stdout(devnull) do
warnerror_logger = ConsoleLogger(stderr, Logging.Warn)
with_logger(warnerror_logger) do
From e008d1f8fd3acbffe14536300142121ddfa5768a Mon Sep 17 00:00:00 2001
From: lbonaldo
Date: Thu, 7 Nov 2024 18:07:11 -0500
Subject: [PATCH 13/17] Add GH action to automatically update version number
---
.github/workflows/version_update.yml | 139 +++++++++++++++++++++++++++
1 file changed, 139 insertions(+)
create mode 100644 .github/workflows/version_update.yml
diff --git a/.github/workflows/version_update.yml b/.github/workflows/version_update.yml
new file mode 100644
index 0000000000..0f5236ef07
--- /dev/null
+++ b/.github/workflows/version_update.yml
@@ -0,0 +1,139 @@
+name: Version Check and Update
+
+on:
+ pull_request_review:
+ branches:
+ - develop
+ types: [submitted]
+
+jobs:
+ check-version:
+ if: github.event.review.state == 'approved' && github.event.pull_request.base.ref == 'develop'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pull-requests: write
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.ref }}
+
+ - name: Set up Julia
+ uses: julia-actions/setup-julia@latest
+ with:
+ version: '1.x'
+
+ - uses: julia-actions/cache@v2
+
+ - name: Configure Git
+ run: |
+ git config user.name "GitHub Actions Bot"
+ git config user.email "actions@github.com"
+
+ - name: Check and Update Version
+ env:
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REPO: ${{ github.repository }}
+ run: |
+ # Get PR base branch and export it
+ export BASE_BRANCH=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
+ "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER" | \
+ jq -r .base.ref)
+
+ julia -e '
+
+ # Get base branch directly from environment variable
+ base_branch = ENV["BASE_BRANCH"]
+
+ # First ensure we have the base branch
+ run(`git fetch origin $(base_branch)`)
+
+ function parse_version(version_str)
+ # Extract M.m.p and optional dev number
+ base_pattern = r"^(\d+\.\d+\.\d+)(?:-dev\.(\d+))?$"
+ m = match(base_pattern, version_str)
+ if isnothing(m)
+ error("Invalid version format: $version_str")
+ end
+
+ version = m.captures[1]
+ dev_num = isnothing(m.captures[2]) ? nothing : parse(Int, m.captures[2])
+ return (version, dev_num)
+ end
+
+ function should_update_version(base_ver_str, current_ver_str)
+ base_ver, base_dev = parse_version(base_ver_str)
+ current_ver, current_dev = parse_version(current_ver_str)
+
+ # If versions differ, no update needed
+ if base_ver != current_ver
+ return false
+ end
+
+ # If base has dev number, we should increment from the base dev number
+ if !isnothing(base_dev)
+ return true
+ end
+
+ # If base has no dev number and current has none, add dev.1
+ if isnothing(base_dev) && isnothing(current_dev)
+ return true
+ end
+
+ return false
+ end
+
+ function get_new_version(base_ver_str, current_ver_str)
+ base_ver, base_dev = parse_version(base_ver_str)
+ current_ver, current_dev = parse_version(current_ver_str)
+
+ # If base has a dev number, increment from that
+ if !isnothing(base_dev)
+ return "$(base_ver)-dev.$(base_dev + 1)"
+ end
+
+ # If no dev numbers exist, start with dev.1
+ return "$(current_ver)-dev.1"
+ end
+
+ function check_and_update_version()
+ # Get the base branch version
+ base_content = read(pipeline(`git show origin/$(base_branch):Project.toml`), String)
+ # Get current branch version
+ current_content = read(joinpath(pwd(), "Project.toml"), String)
+
+ # Extract versions using regex
+ version_pattern = r"version = \"(.*?)\""
+ base_version = match(version_pattern, base_content).captures[1]
+ current_version = match(version_pattern, current_content).captures[1]
+
+ println("Base version: $base_version")
+ println("Current version: $current_version")
+
+ if should_update_version(base_version, current_version)
+ println("Version needs updating")
+
+ new_version = get_new_version(base_version, current_version)
+
+ # Update the file
+ new_content = replace(current_content,
+ "version = \"$current_version\"" =>
+ "version = \"$new_version\"")
+
+ write(joinpath(pwd(), "Project.toml"), new_content)
+
+ # Commit and push the change
+ run(`git add $(joinpath(pwd(), "Project.toml"))`)
+ run(`git commit -m "Bump version to $new_version"`)
+ run(`git push`)
+
+ println("Version updated to $new_version")
+ else
+ println("Version already updated")
+ end
+ end
+
+ check_and_update_version()'
\ No newline at end of file
From c2ff87172a8a2e7377a9428eb85e3dcf32f9e4e2 Mon Sep 17 00:00:00 2001
From: lbonaldo
Date: Thu, 7 Nov 2024 18:07:20 -0500
Subject: [PATCH 14/17] Update GH workflows
---
.github/workflows/PR_checks.yml | 42 -------------------
.github/workflows/changelog.yml | 22 ++++++++++
...sion_update.yml => dev_version_update.yml} | 4 +-
.github/workflows/format_suggestions.yml | 14 -------
4 files changed, 23 insertions(+), 59 deletions(-)
delete mode 100644 .github/workflows/PR_checks.yml
create mode 100644 .github/workflows/changelog.yml
rename .github/workflows/{version_update.yml => dev_version_update.yml} (98%)
delete mode 100644 .github/workflows/format_suggestions.yml
diff --git a/.github/workflows/PR_checks.yml b/.github/workflows/PR_checks.yml
deleted file mode 100644
index 6f02a6690b..0000000000
--- a/.github/workflows/PR_checks.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: PR Check
-on:
- pull_request:
- types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
-
-jobs:
- # Enforces update of changelog file on every pull request
- Changelog:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: dangoslen/changelog-enforcer@v3
- with:
- changeLogPath: 'CHANGELOG.md'
- skipLabels: 'Skip-Changelog'
- token: ${{ secrets.GITHUB_TOKEN }}
- missingUpdateErrorMessage: >
- No update to CHANGELOG.md found! Please add an entry describing
- your change and include the pull request tag. Note that we use
- the keepachangelog format (https://keepachangelog.com). If your
- change doesn’t require a changelog entry, please add the
- 'Skip-Changelog' label to the pull request.
-
- # Check if the version number in the Project.toml file is updated
- Version:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - name: Check version number
- if: github.event.pull_request.base.ref == 'develop'
- shell: bash {0}
- run: |
- git fetch origin develop &> /dev/null
- vdiff=$(git diff -U0 origin/develop -- Project.toml | grep -E "^\+" | grep "version =")
- if [ -z "$vdiff" ];
- then
- echo "::error::Error: version number in Project.toml has not been updated." && exit 1
-
- else
- echo "Version number in Project.toml has been updated."
- echo "New" ${vdiff:1}
- fi
\ No newline at end of file
diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml
new file mode 100644
index 0000000000..3bb6ebed9c
--- /dev/null
+++ b/.github/workflows/changelog.yml
@@ -0,0 +1,22 @@
+name: Changelog Enforcer
+on:
+ pull_request:
+ types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
+
+jobs:
+ # Enforces update of changelog file on every pull request
+ Changelog:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dangoslen/changelog-enforcer@v3
+ with:
+ changeLogPath: 'CHANGELOG.md'
+ skipLabels: 'Skip-Changelog, skip changelog'
+ token: ${{ secrets.GITHUB_TOKEN }}
+ missingUpdateErrorMessage: >
+ No update to CHANGELOG.md found! Please add an entry describing
+ your change and include the pull request tag. Note that we use
+ the keepachangelog format (https://keepachangelog.com). If your
+ change doesn’t require a changelog entry, please add the
+ 'Skip-Changelog' or 'skip changelog' label to the pull request.
diff --git a/.github/workflows/version_update.yml b/.github/workflows/dev_version_update.yml
similarity index 98%
rename from .github/workflows/version_update.yml
rename to .github/workflows/dev_version_update.yml
index 0f5236ef07..cadaca0d70 100644
--- a/.github/workflows/version_update.yml
+++ b/.github/workflows/dev_version_update.yml
@@ -1,9 +1,7 @@
-name: Version Check and Update
+name: Dev Version Check and Update
on:
pull_request_review:
- branches:
- - develop
types: [submitted]
jobs:
diff --git a/.github/workflows/format_suggestions.yml b/.github/workflows/format_suggestions.yml
deleted file mode 100644
index dbd307846d..0000000000
--- a/.github/workflows/format_suggestions.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: Format suggestions
-on:
- pull_request:
-
-jobs:
- code-style:
- runs-on: ubuntu-latest
- steps:
- - uses: julia-actions/julia-format@v2
- continue-on-error: true
- - name: Check on failures
- if: steps.julia-format.outcome != 'success'
- run: echo "There are formatting errors. Please check the logs above."
- shell: bash
\ No newline at end of file
From 5e3d0becd1b730183e1b4685fc013b452b831c9e Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Mon, 6 Jan 2025 03:43:02 -0500
Subject: [PATCH 15/17] Add `eTotalCMaxCapSlack` to `cUnmetPolicyPenalty` in
`write_costs.jl` (#806)
---
CHANGELOG.md | 2 ++
src/write_outputs/write_costs.jl | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4484607652..1f415f557a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,8 @@ the charging capacity of the storage component in VRE_STOR (#770).
with Julia v1.11 (#785).
- Fixed cost calculation in `write_costs.jl` when no resources are present in
a zone. (#796)
+- Added `eTotalCMaxCapSlack` to calculation of `cUnmetPolicyPenalty` in
+`write_costs.jl` (#806).
## [0.4.1] - 2024-08-20
diff --git a/src/write_outputs/write_costs.jl b/src/write_outputs/write_costs.jl
index 70bda8516e..9193865911 100644
--- a/src/write_outputs/write_costs.jl
+++ b/src/write_outputs/write_costs.jl
@@ -127,6 +127,10 @@ function write_costs(path::AbstractString, inputs::Dict, setup::Dict, EP::Model)
dfCost[9, 2] += value(EP[:eTotalCMinCapSlack])
end
+ if haskey(inputs, "MaxCapPriceCap")
+ dfCost[9, 2] += value(EP[:eTotalCMaxCapSlack])
+ end
+
if haskey(inputs, "H2DemandPriceCap")
dfCost[9, 2] += value(EP[:eTotalCH2DemandSlack])
end
From 9f82165dde2b160b7ac9017f53cb45ae6181978b Mon Sep 17 00:00:00 2001
From: lbonaldo
Date: Mon, 23 Dec 2024 16:24:57 -0500
Subject: [PATCH 16/17] Prep for v0.4.2
---
CHANGELOG.md | 2 ++
CITATION.cff | 2 +-
Project.toml | 2 +-
README.md | 2 +-
docs/src/installation.md | 2 +-
5 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f415f557a..909f8a14cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+## [0.4.2] - 2024-12-23
+
### Added
- Fusion plant optional features for thermal plants (#743).
- Support for reusing the same Gurobi environment for multiple solves when
diff --git a/CITATION.cff b/CITATION.cff
index 3519a1b267..a0f0146e67 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -54,7 +54,7 @@ authors:
given-names: "Qingyu"
orcid: "https://orcid.org/0000-0003-2692-5135"
title: "GenX"
-version: 0.4.1
+version: 0.4.2
doi: 10.5281/zenodo.10846070
date-released: 2024-04-26
url: "https://github.com/GenXProject/GenX.jl"
diff --git a/Project.toml b/Project.toml
index ccbd22f3f9..60f5a42e21 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.1-dev.20"
+version = "0.4.2"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/README.md b/README.md
index 725f696499..dbbf8a71d2 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,7 @@ The 'main' branch is the current master branch of GenX. The various subdirectori
## Requirements
-GenX (v0.4.1) runs on Julia v1.6 through v1.9, with a minimum version of the package JuMP v1.1.1. Julia v1.10 is also supported. However, we recently noticed a decline in performance with Julia v1.10, which is currently under investigation. Therefore, **we recommend using Julia v1.9**, particularly for very large cases.
+GenX (v0.4.2) runs on Julia v1.6 through v1.9, with a minimum version of the package JuMP v1.1.1. Julia v1.10 and v1.11 are also supported. However, we recently noticed a decline in performance with Julia v1.10, which is currently under investigation. Therefore, **we recommend using Julia v1.9**, particularly for very large cases.
We recommend the users to either stick to a particular version of Julia to run GenX. If however, the users decide to switch between versions, it's very important to delete the old `Manifest.toml` file and do a fresh build of GenX when switching between Julia versions.
There is also an older version of GenX, which is also currently maintained and runs on Julia 1.3.x and 1.4.x series.
diff --git a/docs/src/installation.md b/docs/src/installation.md
index 3fb13ee363..e6801a1b60 100644
--- a/docs/src/installation.md
+++ b/docs/src/installation.md
@@ -2,7 +2,7 @@
This guide will walk you through the steps to install Julia, the GenX package, and the required dependencies to run GenX.
## Installing Julia
-GenX (v0.4.1) runs on Julia v1.6 through v1.9, with a minimum version of the package [JuMP](https://jump.dev/JuMP.jl/stable/) v1.1.1. Julia v1.10 is also supported. However, we recently noticed a decline in performance with Julia v1.10, which is currently under investigation. Therefore, we recommend using Julia v1.9, particularly for very large cases. To install Julia, please follow the instructions on the [Julia website](https://julialang.org/downloads/).
+GenX (v0.4.2) runs on Julia v1.6 through v1.9, with a minimum version of the package [JuMP](https://jump.dev/JuMP.jl/stable/) v1.1.1. Julia v1.10 and v1.11 are also supported. However, we recently noticed a decline in performance with Julia v1.10, which is currently under investigation. Therefore, we recommend using Julia v1.9, particularly for very large cases. To install Julia, please follow the instructions on the [Julia website](https://julialang.org/downloads/).
!!! note "Note"
We recommend the users to stick to a particular version of Julia to run GenX. If however, the users decide to switch between versions, it's very important to delete the old `Manifest.toml` file and do a fresh build of GenX.
From e0e3dcaf225395d969b198233a20083001aeca2a Mon Sep 17 00:00:00 2001
From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com>
Date: Fri, 17 Jan 2025 09:31:55 -0500
Subject: [PATCH 17/17] Fix path to MGA reference in documentation (#813)
---
Project.toml | 2 +-
docs/src/User_Guide/generate_alternatives.md | 2 +-
docs/src/User_Guide/model_configuration.md | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Project.toml b/Project.toml
index 60f5a42e21..64ae1fc30f 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "GenX"
uuid = "5d317b1e-30ec-4ed6-a8ce-8d2d88d7cfac"
authors = ["Bonaldo, Luca", "Chakrabarti, Sambuddha", "Cheng, Fangwei", "Ding, Yifu", "Jenkins, Jesse D.", "Luo, Qian", "Macdonald, Ruaridh", "Mallapragada, Dharik", "Manocha, Aneesha", "Mantegna, Gabe ", "Morris, Jack", "Patankar, Neha", "Pecci, Filippo", "Schwartz, Aaron", "Schwartz, Jacob", "Schivley, Greg", "Sepulveda, Nestor", "Xu, Qingyu", "Zhou, Justin"]
-version = "0.4.2"
+version = "0.4.2-dev.1"
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
diff --git a/docs/src/User_Guide/generate_alternatives.md b/docs/src/User_Guide/generate_alternatives.md
index f0fe836362..4278c4ffe7 100644
--- a/docs/src/User_Guide/generate_alternatives.md
+++ b/docs/src/User_Guide/generate_alternatives.md
@@ -10,4 +10,4 @@ GenX includes a modeling to generate alternatives (MGA) package that can be used
6. Set the `MGAAnnualGeneration` flag in the `genx_settings.yml` file to the desired MGA formulation.
7. Solve the model using `Run.jl` file.
-Results from the MGA algorithm would be saved in MGA_max and MGA_min folders in the case folder.
\ No newline at end of file
+Results from the MGA algorithm would be saved in MGA\_max and MGA\_min folders in the case folder.
\ No newline at end of file
diff --git a/docs/src/User_Guide/model_configuration.md b/docs/src/User_Guide/model_configuration.md
index a1526b3eab..2c8ccdcded 100644
--- a/docs/src/User_Guide/model_configuration.md
+++ b/docs/src/User_Guide/model_configuration.md
@@ -52,7 +52,7 @@ The following tables summarize the model settings parameters and their default/p
|MultiStage | Model multiple planning stages |
||1 = Model multiple planning stages as specified in `multi_stage_settings.yml` |
||0 = Model single planning stage |
-|ModelingToGenerateAlternatives | Modeling to Generate Alternative Algorithm. For details, see [here](https://genxproject.github.io/GenX/dev/additional_features/#Modeling-to-Generate-Alternatives)|
+|ModelingToGenerateAlternatives | Modeling to Generate Alternatives Algorithm. For more details, see the [Modeling to Generate Alternatives](@ref) section in the **Model Reference**.|
||1 = Use the algorithm. |
||0 = Do not use the algorithm. |
|ModelingtoGenerateAlternativeSlack | value used to define the maximum deviation from the least-cost solution as a part of Modeling to Generate Alternative Algorithm. Can take any real value between 0 and 1. |