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
RowResourceMA_natural_gas_combined_cycleCT_natural_gas_combined_cycleME_natural_gas_combined_cycleMA_solar_pvCT_onshore_windCT_solar_pvME_onshore_windMA_batteryCT_batteryME_batteryTotal
String15Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64
1Zone1.02.03.01.02.02.03.01.02.03.00.0
2AnnualSum1.04015e73.42459e68.94975e52.47213e72.90683e72.69884e72.625e75.06354e61.45833e74.90368e61.463e8
3t1-0.0-0.0-0.0-0.08510.78-0.05300.610.02537.45673.3417022.2
4t2-0.0-0.0-0.0-0.08420.78-0.06282.040.02537.450.017240.3
5t3-0.0-0.0-0.0-0.08367.78-0.02409.840.02537.451828.2415143.3
6t4-0.0-0.0-0.0-0.08353.78-0.02762.241591.462537.450.015244.9
7t5-0.0-0.0-0.0-0.07482.39-0.00.01617.462980.641384.6213465.1
8t6-0.0-0.0-0.0-0.02429.93-0.02797.241717.965535.370.012480.5
9t7-0.0-0.0-0.0-0.011868.8-0.01374.731320.78871.4431340.6716776.4
10t8-0.0-0.0-0.0-0.02656.93-0.00.02115.965535.371452.6211760.9
11t9-0.0-0.0-0.03061.280.03110.82982.24868.8175389.440.015412.6
12t10-0.0-0.0-0.06100.227597.995543.690.00.00.01521.1220763.0
13t11-0.0-0.0-0.08314.290.06341.983080.240.02458.820.020195.3
1839t1837-0.0-0.0-0.06712.182541.66736.37305.6081410.33763.7261427.8219897.6
1840t1838-0.0-0.0-0.06514.150.06847.243153.240.03464.220.019978.9
1841t1839-0.0-0.0-0.05582.073848.886280.20.0195.4222048.31571.1219526.0
1842t1840-0.0-0.0-0.03688.139349.984892.73490.611006.020.00.022427.4
1843t1841-0.0-0.0-0.0509.228124.991351.083653.061218.52507.81828.2419192.9
1844t1842-0.0-0.0-0.0-0.02918.2-0.06896.822194.615535.37256.86317801.9
1845t1843-0.0-0.0-0.0-0.06800.37-0.07324.661838.113950.1541.947219955.2
1846t1844-0.0-0.0-0.0-0.09505.82-0.05683.661744.782567.93838.07720340.3
1847t1845-0.0-0.0-0.0-0.03491.93-0.05128.561597.615535.371107.4916861.0
1848t1846-0.0-0.0-0.0-0.012135.6-0.05021.751341.111140.561125.920764.9
1849t1847-0.0-0.0-0.0-0.08875.71-0.03605.98974.612665.481783.7917905.6
1850t1848-0.0-0.0-0.0-0.013549.1-0.04098.0541.61205.311478.2719872.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
RowregionResourcezoneClusterR_IDInv_cost_MWInv_cost_MWhInv_cost_charge_MWFixed_OM_cost_MWFixed_OM_cost_MWhFixed_OM_cost_charge_MWVar_OM_cost_outFuel_costVar_OM_cost_inStartCostCharge_costCO2SequestrationCostEnergyRevenueSubsidyRevenueOperatingReserveRevenueOperatingRegulationRevenueReserveMarginRevenueESRRevenueEmissionsCostRegSubsidyRevenueRevenueCostProfit
String3String31Int64Int64Int64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64
1MAMA_natural_gas_combined_cycle1115.54734e80.00.08.72561e70.00.03.69253e72.10416e80.03.84832e70.00.02.77103e90.00.00.00.00.01.84321e90.02.77103e92.77103e91.43051e-6
2CTCT_natural_gas_combined_cycle2121.42906e80.00.02.11911e70.00.01.22258e74.97792e70.07.75292e60.00.08.4423e80.00.00.00.00.06.10375e80.08.4423e88.4423e81.19209e-7
3MEME_natural_gas_combined_cycle3133.52336e70.00.08.77661e60.00.04.02739e62.26505e70.03.33663e60.00.02.19267e80.00.00.00.00.01.45243e80.02.19267e82.19267e80.0
4MAMA_solar_pv1141.27007e90.00.02.79327e80.00.00.00.00.00.00.00.01.5494e90.00.00.00.00.00.00.01.5494e91.5494e9-2.86102e-6
5CTCT_onshore_wind2151.40748e90.00.06.25617e80.00.02.90683e60.00.00.00.00.02.036e90.00.00.00.00.00.00.02.036e92.036e9-5.00679e-6
6CTCT_solar_pv2161.35108e90.00.02.97142e80.00.00.00.00.00.00.00.01.64822e90.00.00.00.00.00.00.01.64822e91.64822e99.53674e-7
7MEME_onshore_wind3171.03673e90.00.04.60821e80.00.02.625e60.00.00.00.00.01.50017e90.00.00.00.00.00.00.01.50017e91.50017e92.38419e-6
8MAMA_battery1084.29792e72.23673e80.01.07426e75.59033e70.07.59532e50.08.97367e50.01.3432e80.04.48833e80.00.00.00.00.00.00.04.48833e84.69275e8-2.0442e7
9CTCT_battery2091.08405e85.73615e80.02.70957e71.43365e80.02.1875e60.02.58447e60.05.24177e80.01.31941e90.00.00.00.00.00.00.01.31941e91.38143e9-6.20165e7
10MEME_battery30103.58043e71.03994e80.08.94925e62.59915e70.07.35552e50.08.69036e50.03.81057e70.02.03732e80.00.00.00.00.00.00.02.03732e82.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
RowZone123Total
String15Float64Float64Float64Float64
1CO2_Price_1444.9210.00.00.0
2CO2_Price_20.0468.6680.00.0
3CO2_Price_30.00.0240.860.0
4AnnualSum4.14279e61.30236e66.03017e56.04816e6
5t10.00.00.00.0
6t20.00.00.00.0
7t30.00.00.00.0
8t40.00.00.00.0
9t50.00.00.00.0
10t60.00.00.00.0
11t70.00.00.00.0
12t80.00.00.00.0
13t90.00.00.00.0
1841t18370.00.00.00.0
1842t18380.00.00.00.0
1843t18390.00.00.00.0
1844t18400.00.00.00.0
1845t18410.00.00.00.0
1846t18420.00.00.00.0
1847t18430.00.00.00.0
1848t18440.00.00.00.0
1849t18450.00.00.00.0
1850t18460.00.00.00.0
1851t18470.00.00.00.0
1852t18480.00.00.00.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
RowZone123Total
String15Float64Float64Float64Float64
1AnnualSum1.68155e71.41088e74310.213.09286e7
2t1997.1690.00.0997.169
3t2997.1690.00.0997.169
4t3997.1690.00.0997.169
5t4997.1690.00.0997.169
6t5997.1690.00.0997.169
7t6997.1690.00.0997.169
8t7997.1690.00.0997.169
9t8997.1690.00.0997.169
10t9997.1690.00.0997.169
11t101471.460.00.01471.46
12t11997.1690.00.0997.169
13t121115.810.00.01115.81
1838t18372789.351012.990.03802.34
1839t18382835.211012.990.03848.2
1840t18392520.571012.990.03533.56
1841t18401496.47445.850.01942.32
1842t18412571.261012.990.03584.25
1843t18422835.211012.990.03848.2
1844t18432835.211012.990.03848.2
1845t18442625.42960.1840.03585.6
1846t18452506.32342.3910.02848.71
1847t18462277.59342.3910.02619.98
1848t18471960.08524.5260.02484.6
1849t18481566.77342.3910.01909.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 @@ -1224364860728496108120132144156168Time Step (hours)02,0004,0006,0008,00010,00012,00014,00016,00018,00020,00022,00024,00026,000Demand (MW)WindSolarNatural_GasBatteryDemandResource_TypeResource Capacity per Hour with Demand Curve, all Zones \ No newline at end of file +1224364860728496108120132144156168Time Step (hours)02,0004,0006,0008,00010,00012,00014,00016,00018,00020,00022,00024,00026,000Load (MW)WindSolarNatural_GasBatteryDemandResource_TypeResource Capacity per Hour with Load Demand Curve, all Zones \ 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. |