Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e5ce797
Initial commit of Benders implementation mid debugging
dlcole3 Jun 3, 2026
96c1d5f
Removed extra Gurobi compat line
dlcole3 Jun 3, 2026
157caf1
Fixed bug in allam cycle
dlcole3 Jun 3, 2026
c22049e
Added support for VRE STOR pre-debugging
dlcole3 Jun 9, 2026
46e5101
Added initial documentation
dlcole3 Jun 10, 2026
b2b7ebb
Debugged VRE Stor with LDES and non LDES
dlcole3 Jun 12, 2026
9a46408
Debugging LDS CAPRES with VRE STOR
dlcole3 Jun 15, 2026
9e539ff
updated changelog, updated examples
dlcole3 Jun 16, 2026
d7fc974
updated examples, removed old Benders code
dlcole3 Jun 16, 2026
8f6bda1
Fixed missing calls in docs and test
dlcole3 Jun 16, 2026
d131115
Fixed bug in docs, updated Benders tests
dlcole3 Jun 17, 2026
7dd87cd
Made benders tests faster via TDR, added doc strings
dlcole3 Jun 17, 2026
37ce62e
removed extra yaml file, added line for debugging tests
dlcole3 Jun 18, 2026
e592fab
Added code to call doc strings
dlcole3 Jun 18, 2026
60fbba3
built benders test cases to use prerun TDR files
dlcole3 Jun 18, 2026
b508308
Updated API for accessing more workers
dlcole3 Jun 18, 2026
4c22931
Fixed @everywhere call in case runner for Benders
dlcole3 Jun 18, 2026
6e5400a
Updated tests to avoid numerical issues with HiGHS
dlcole3 Jun 22, 2026
a2e7558
Switched to simplex to avoid hanging in Julia 1.9 and increased toler…
dlcole3 Jun 22, 2026
b1c0f20
attempt to fix error in writing outputs tests workflow
dlcole3 Jun 22, 2026
517c174
Tightened multistage convergence tolerance
dlcole3 Jun 22, 2026
149e90a
added fallback on multistage testing for degenerate solutions in DDP
dlcole3 Jun 22, 2026
08e540a
Updated highs settings to try to fix numerical issue
dlcole3 Jun 22, 2026
0792dd1
Reverted to IPM for writing outputs
dlcole3 Jun 22, 2026
988f6cb
Tried new optimality tolerance for writing outputs error
dlcole3 Jun 22, 2026
1b1e8f5
Trying parameter scale = 1
dlcole3 Jun 22, 2026
3b3a1ab
added fallback
dlcole3 Jun 22, 2026
fafa6d7
Removed extra rm call
dlcole3 Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased

### Added
- A generalized hourly matching policy module (#855).
- Added `MacroEnergySolvers` (v0.2) as a package dependency, providing shared solver infrastructure used by the Benders decomposition framework.
- Added `Gurobi` as a weak dependency with a `GenXGurobiExt` package extension, making Gurobi support optional and loaded only when the `Gurobi` package is available in the environment.
- Core Benders decomposition framework in new `src/benders/` directory: `benders_planning_problem.jl` (master problem / investment stage), `benders_subproblems.jl` (operational subproblems per representative period), `benders_utility.jl` (shared cut/convergence/logging utilities), and `results.jl` (result aggregation from subproblems). Includes `src/configure_solver/configure_benders.jl` for solver configuration.
- VRE-STOR capacity investment support for Benders decomposition via new `src/model/resources/vre_stor/investment_vre_stor.jl`, adding investment decisions and linking constraints for co-located VRE+storage resources in the master problem.
- Benders-specific policy modules for CO2 cap (`co2_cap.jl`), energy share requirement (`energy_share_requirement.jl`), and hydrogen demand (`hydrogen_demand.jl`), ensuring dual prices from each subproblem feed back into optimality cuts.
- Benders-specific resource modules for hydro inter-period linkage (`hydro_inter_period_linkage.jl`) and long-duration storage (`long_duration_storage.jl`), supporting state-of-charge linking across representative periods in the subproblems.
- Benders decomposition documentation: user guide at `docs/src/User_Guide/benders_decomposition.md` and mathematical overview at `docs/src/Model_Concept_Overview/benders_math_overview.md`; updated `docs/make.jl`, `examples_casestudies.md`, `model_configuration.md`, and `developer_guide.md` to cover Benders usage and code structure.
- Benders test suite: `test/test_benders_vs_monolithic.jl` validates that Benders and monolithic solves produce consistent objective values across example systems.
- Added `Run_benders.jl` entry point scripts to example systems 1–5, 7, 10, and 11, enabling Benders decomposition runs for each of those cases.
- A generalized hourly matching policy module.
- Benders decomposition output now writes additional files matching the monolithic solver: `prices.csv`, `reliability.csv`, `storagebal_duals.csv`, `capacityfactor.csv`, `time_weights.csv`, `EnergyRevenue.csv`, `ChargingCost.csv`, `commit.csv`, `start.csv`, `shutdown.csv` (when UCommit≥1), `CO2_prices_and_penalties.csv` (when CO2Cap>0), and `SubsidyRevenue.csv`/`RegSubsidyRevenue.csv` (when MinCap/MinCapReq is active).

### Changed
- `capacity_decisions.jl` refactored to support both monolithic and Benders decomposition modes, cleanly separating capacity (master problem) and operational (subproblem) variables.
- `generate_model.jl` updated to route model construction through Benders subproblem or monolithic paths based on the `BendersDecomposition` settings flag.

### Fixed
- Corrected investment and operational constraints in `allamcyclelox.jl` that caused incorrect capacity accounting for the Allam Cycle with LOX storage resource type.
- Fixed effective capacity calculation in the capacity reserve margin subproblem for co-located VRE+storage resources with long-duration storage.
- Fix writing of net revenue to include all sources of revenue, not just energy revenue (#855).

## [0.4.6] - 2026-01-06
Expand Down
13 changes: 13 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ version = "0.4.6"

[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
ClusterManagers = "34f1f09b-3a8b-5176-ab39-66d58a4d544e"
Clustering = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5"
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
DistributedArrays = "aaf54ef3-cdf8-58ed-94cc-d582ad619b94"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
MacroEnergySolvers = "466a4f75-6c9c-4ef2-97df-f5f43ac00c23"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
Expand All @@ -24,6 +30,12 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"

[weakdeps]
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"

[extensions]
GenXGurobiExt = "Gurobi"

[compat]
CSV = "0.10.4"
Clustering = "0.14.2, 0.15"
Expand All @@ -35,6 +47,7 @@ Distances = "0.10.7"
HiGHS = "1.1.4"
JuMP = "1.1.1"
LinearAlgebra = "1"
MacroEnergySolvers = "0.2"
MathOptInterface = "1.6.1"
NearestNeighbors = "0.4.0 - 0.4.22"
PrecompileTools = "1"
Expand Down
11 changes: 7 additions & 4 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pages = OrderedDict(
"Tutorial 5: Solving the Model" => "Tutorials/Tutorial_5_solve_model.md",
"Tutorial 6: Solver Settings" => "Tutorials/Tutorial_6_solver_settings.md",
"Tutorial 7: Policy Constraints" => "Tutorials/Tutorial_7_setup.md",
"Tutorial 8: Outputs" => "Tutorials/Tutorial_8_outputs.md"
"Tutorial 8: Outputs" => "Tutorials/Tutorial_8_outputs.md",
],
"User Guide" => [
"Overall workflow" => "User_Guide/workflow.md",
Expand All @@ -35,18 +35,20 @@ pages = OrderedDict(
"Model Inputs" => "User_Guide/model_input.md",
"Time-domain Reduction Inputs" => "User_Guide/TDR_input.md",
"Running the Time-domain Reduction" => "User_Guide/running_TDR.md",
"Benders Decompositiion" => "User_Guide/benders_decomposition.md",
"MGA package" => "User_Guide/generate_alternatives.md",
"Multi-stage Model" => "User_Guide/multi_stage_input.md",
"Slack Variables for Policies" => "User_Guide/slack_variables_overview.md",
"Method of Morris Inputs" => "User_Guide/methodofmorris_input.md",
"Running the Model" => "User_Guide/running_model.md",
"Model Outputs" => "User_Guide/model_output.md"
"Model Outputs" => "User_Guide/model_output.md",
],
"Model Concept and Overview" => [
"Model Introduction" => "Model_Concept_Overview/model_introduction.md",
"Notation" => "Model_Concept_Overview/model_notation.md",
"Objective Function" => "Model_Concept_Overview/objective_function.md",
"Power Balance" => "Model_Concept_Overview/power_balance.md"
"Power Balance" => "Model_Concept_Overview/power_balance.md",
"Benders Model Overview" => "Model_Concept_Overview/benders_math_overview.md",
],
"Model Reference" => [
"Core" => "Model_Reference/core.md",
Expand Down Expand Up @@ -95,7 +97,8 @@ pages = OrderedDict(
"Endogenous Retirement" => "Model_Reference/Multi_Stage/endogenous_retirement.md"
],
"Method of Morris" => "Model_Reference/methodofmorris.md",
"Utility Functions" => "Model_Reference/utility_functions.md"
"Utility Functions" => "Model_Reference/utility_functions.md",
"Benders Decomposition" => "Model_Reference/Benders/benders.md"
],
"Public API Reference" => [
"Public API" => "Public_API/public_api.md"],
Expand Down
36 changes: 35 additions & 1 deletion docs/src/Getting_Started/examples_casestudies.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ The available examples are:
- [5\_three\_zones\_w\_piecewise\_fuel](https://github.com/GenXProject/GenX/tree/main/example_systems/5_three_zones_w_piecewise_fuel)
- [6\_three\_zones\_w\_multistage](https://github.com/GenXProject/GenX/tree/main/example_systems/6_three_zones_w_multistage)
- [7\_three\_zones\_w\_colocated\_VRE\_storage](https://github.com/GenXProject/GenX/tree/main/example_systems/7_three_zones_w_colocated_VRE_storage)
- [IEEE\_9\_bus\_DC\_OPF](https://github.com/GenXProject/GenX/tree/main/example_systems/IEEE_9_bus_DC_OPF)
- [8\_three\_zones\_w\_colocated\_VRE\_storage\_electrolyzers](https://github.com/GenXProject/GenX/tree/main/example_systems/8_three_zones_w_colocated_VRE_storage_electrolyzers)
- [9\_three\_zones\_w\_retrofit](https://github.com/GenXProject/GenX/tree/main/example_systems/9_three_zones_w_retrofit)
- [10_IEEE\_9\_bus\_DC\_OPF](https://github.com/GenXProject/GenX/tree/main/example_systems/10_IEEE_9_bus_DC_OPF)

!!! note "Note"
The following instructions assume that you have already installed GenX and its dependencies. If you haven't, please follow the instructions in the [Installation Guide](@ref).
Expand Down Expand Up @@ -163,3 +165,35 @@ Here, `/path/to/env` is the path to the environment where GenX is installed.

!!! note "Note"
The environment variable `GENX_PRECOMPILE` must be set before loading GenX for the first time. However, to force recompilation of GenX, you can delete the `~/.julia/compiled/vZ.Y/GenX/*.ji` binaries (where vZ.Y is the version of Julia being used, e.g., v1.9), set the environment variable `GENX_PRECOMPILE` to the desired value, and then reload the package. If GenX was imported via `Pkg.develop` or `] dev`, modifying any of the package files will also force recompilation.

## Solving a Case with Benders Decomposition

To solve a case using Benders decomposition, you need to enable it in the `genx_settings.yml` file and provide specific settings files for the Benders planning (master) and subproblems.

1. **Enable Benders Decomposition:** In your `genx_settings.yml` file, set the `Benders` flag to `1`.

```yaml
# genx_settings.yml
Benders: 1
```

2. **Provide Benders and Solver Settings:** You must create a benders settings yaml file and two specific solver settings files in the `settings` directory:
* `benders_settings.yml`: contains Benders specific settings, such as the number of iterations or a convergence tolerance. See [Benders Decomposition](@ref) for a list and explanation of settings.
* `[solver_name]_benders_planning_settings.yml`: Contains the solver settings for the master (planning) problem.
* `[solver_name]_benders_subprob_settings.yml`: Contains the solver settings for the operational subproblems.

Replace `[solver_name]` with the name of the solver you are using (e.g., `gurobi`, `cplex`, `highs`).

The directory structure for a case using Benders with Gurobi would look like this:

```
MyCase
├── settings
│ ├── genx_settings.yml # GenX settings
│ ├── gurobi_benders_planning_settings.yml # Benders master problem settings
│ └── gurobi_benders_subprob_settings.yml # Benders subproblem settings
...
```

For more details on Benders Decomposition, see the [Benders Decomposition](@ref) documentation.
42 changes: 42 additions & 0 deletions docs/src/Model_Concept_Overview/benders_math_overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Benders Decomposition Overview

Below we outline how Benders decomposes a capacity expansion model mathematically. For a broader overview of the Benders decomposition algorithm, see [Benders Decomposition](@ref).

## Mathematical Formulation

The standard capacity expansion problem that GenX solves can be formulated as a large, monolithic optimization problem. With Benders decomposition, this problem is restructured. The formulation below represents the full, undecomposed problem:

$$
\begin{aligned}
\min \ & c_p^\top x_p + \sum_{w \in W} c_w^\top x_w & &(1) \\
\textrm{s.t.}\ & A_w x_w + B_w x_p \le b_w, \quad & w \in W &(2) \\
& \sum_{w \in W} q_w \le d, \quad &&(3) \\
& Q_w x_w \le q_w, \quad & w \in W &(4) \\
& Dx_w \le f_w, \quad & w \in W &(5) \\
& x_w \in \mathcal{X}_w, \quad & w \in W \\
& x_p \in \mathcal{X}_p &
\end{aligned}
$$

### Variables

* **$x_p$ (Planning Variables):** These are the master problem variables, often called "first-stage" or "investment" variables. In GenX, they represent long-term investment and retirement decisions for generation, storage, and transmission capacity.
* **$x_w$ (Operational Variables):** These are the subproblem variables, often called "second-stage" or "operational" variables. They represent the operational decisions for a specific time slice $w$ (e.g., an hour, day, or week). Examples include the power output of each generator, charging/discharging of storage, and power flow on transmission lines.
* **$q_w$ (Policy Variables):** These variables link policies across different operational time slices. For example, this could be the allocation of an annual $CO_2$ emissions budget to different weeks or months.

### Constraints

* **(1) Objective Function:** The goal is to minimize the total system cost, which is the sum of planning/investment costs ($c_p^\top x_p$) and the operational costs over all time periods ($\sum_{w \in W} c_w^\top x_w$).
* **(2) Linking Constraints:** These constraints connect the planning decisions to the operational decisions. For example, the maximum power output of a generator in any given hour is limited by the total installed capacity determined by the planning variables.
* **(3) and (4) Policy Constraints:** These constraints enforce system-wide policies that couple the operational subproblems, such as annual emissions caps or renewable portfolio standards.
* **(5) Operational Constraints:** These are constraints that are entirely contained within a single operational subperiod $w$. Examples include nodal power balance (generation = demand), transmission limits, and generator ramping limits.

## Master Problem and Subproblem Split

Benders decomposition splits the problem above into a master problem and multiple independent operational subproblems.

* **The Master Problem:** Contains the planning variables ($x_p$) and policy variables ($q_w$). It approximates the operational costs using "Benders cuts," which are linear constraints derived from the subproblems' dual information. The master problem proposes an investment plan and passes it to the subproblems.

* **The Subproblems:** There is one subproblem for each operational time slice $w \in W$. Each subproblem takes the investment plan from the master problem as fixed input and solves for the optimal operational decisions ($x_w$). If the subproblem is feasible, it returns cost information (dual variables) to the master problem to generate an "optimality cut." If it is infeasible, it returns information about the infeasibility (a dual ray) to generate a "feasibility cut."

This iterative process allows GenX to solve very large problems that would be intractable as a single, monolithic optimization.
18 changes: 18 additions & 0 deletions docs/src/Model_Reference/Benders/benders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Benders Decomposition API

## Planning Problem
```@autodocs
Modules = [GenX]
Pages = ["benders_planning_problem.jl"]
```

## Subproblems
```@autodocs
Modules = [GenX]
Pages = ["benders_subproblems.jl"]
```

## Gurobi Optimizer Helper
```@docs
GenX.benders_gurobi_optimizer
```
6 changes: 6 additions & 0 deletions docs/src/Model_Reference/Resources/long_duration_storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
```@autodocs
Modules = [GenX]
Pages = ["long_duration_storage.jl"]
```

## Long Duration Storage Slack Variables
```@autodocs
Modules = [GenX]
Pages = ["ldes_slack.jl"]
```
1 change: 1 addition & 0 deletions docs/src/Model_Reference/Resources/storage.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Storage
```@docs
GenX.storage!
GenX.investment_storage!
```
6 changes: 6 additions & 0 deletions docs/src/Model_Reference/solver_configuration_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@ Pages = ["configure_cbc.jl"]
```@autodocs
Modules = [GenX]
Pages = ["configure_scip.jl"]
```

## Configuring Benders Settings
```@autodocs
Modules = [GenX]
Pages = ["configure_benders.jl"]
```
6 changes: 6 additions & 0 deletions docs/src/Model_Reference/write_outputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,9 @@ GenX.write_settings_file
GenX.write_allam_capacity
GenX.write_allam_output
```

## Write Benders Decomposition Outputs
```@autodocs
Modules = [GenX]
Pages = ["write_benders_output.jl", "write_planning_problem_costs.jl"]
```
Loading
Loading