Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/TestOnPRs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ concurrency:

jobs:
test:
strategy:
fail-fast: false
matrix:
version:
- "1.9"
- "1"
uses: ./.github/workflows/ReusableTest.yml
with:
os: ubuntu-latest
version: "1"
version: ${{ matrix.version }}
arch: x64
allow_failure: false
run_codecov: true
run_codecov: ${{ matrix.version == '1' }}
secrets:
codecov_token: ${{ secrets.CODECOV_TOKEN }}
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Lightweight in-memory DART SCUC, SCED, generator N-1, rolling, and settlement
functionality.

### Changed

- Add `clean_energy_policy = 2` for local annual RPS compliance without
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6"
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Expand All @@ -24,6 +23,7 @@ YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"

[weakdeps]
CPLEX = "a076750e-1247-5638-91d2-ce28b192dca0"
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
SCIP = "82193955-e24f-5292-bf16-6f2c5261a85f"

[extensions]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The **Holistic Optimization Program for Electricity (HOPE)** model is a transpar

1. `GTEP` mode: a generation & transmission expansion planning model
2. `PCM` mode: a production cost model
3. `DART` mode: a SCUC/SCED market model for simulating day-ahead and real-time markets
3. `DART` module: an individual-generator SCUC/SCED model for day-ahead and real-time markets
4. `OPF` mode: (under development): an optimal power flow model
5. `HOPE-AI` mode: an AI agent helps connect all HOPE modules and enables complex modeling workflows. The current HOPE-AI framework is powered by [PowerAgent](https://github.com/Power-Agent), while more specialized agents are under development.

Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pages = OrderedDict(
"Notation" => "notation.md",
"GTEP" => "GTEP.md",
"PCM" => "PCM.md",
"DART" => "DART.md",
],
"Input Data Explanation" =>
["GTEP Inputs" => "GTEP_inputs.md", "PCM Inputs" => "PCM_inputs.md"],
Expand Down
6 changes: 6 additions & 0 deletions docs/src/95-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ Pages = ["95-reference.md"]
```@autodocs
Modules = [HOPE]
```

## DART module

```@autodocs
Modules = [HOPE.DART]
```
106 changes: 106 additions & 0 deletions docs/src/DART.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
```@meta
CurrentModule = HOPE.DART
```

# DART

DART is HOPE's lightweight day-ahead and real-time operations module. It is an
in-memory API: callers construct typed Julia inputs and receive typed Julia
results. It does not introduce another case-file schema.

The V1 model contains:

- hourly individual-generator day-ahead SCUC;
- rolling real-time SCED, normally at five-minute resolution;
- lossless nodal PTDF transmission constraints;
- regulation, spinning, and quick-start non-spinning reserves;
- strict generator N-1 corrective redispatch by default;
- chronological storage with mutually exclusive charge/discharge operation; and
- day-ahead/real-time energy, reserve, and uplift settlements.

The implementation is contained in `src/DART.jl`. The main entry points are
`solve_dart_scuc`, `solve_dart_sced`, `run_dart_rolling`, and
`calculate_dart_settlements`.

## Formulation mapping

| Formulation group | Implementation |
|:--|:--|
| SCUC-OBJ, UC-1--UC-4 | Commitment, startup/shutdown, minimum up/down time, and commitment costs |
| GEN-1--GEN-7, RAMP-1--RAMP-2 | Generator bounds, transition limits, hourly ramps, and reserve headroom |
| REN-1--REN-2 | Time-varying availability; zero-cost curtailment is eliminated algebraically |
| RES-1--RES-5 | Product capability, response time, requirements, and quick-start eligibility |
| STO-1--STO-6 | Exclusive charge/discharge modes, SOC chronology, and reserve-energy deliverability |
| NET-1--NET-4 | Nodal injection, balance, PTDF flow, and line limits |
| SEC-1--SEC-5 | One corrective scenario per eligible generator |
| RT-1--RT-3 | Fixed DA commitment and energy-only real-time ramping |
| COUP-1 | Binding RT dispatch and SOC are carried into the next solve |

V1 deliberately uses system-wide reserve requirements, nodal load shedding,
generator contingencies, and the lossless PTDF network option. Line outages,
zonal transport, demand response, and policy constraints remain outside this
small operational core.

Contingency load shedding is disabled by default. Set
`allow_emergency_contingency_shed = true` in `DARTConfig` only when a penalized
soft-security solve is preferable to infeasibility.

Quick-start reserve is available only while a unit is offline, in service,
within its response-time qualification, beyond any remaining minimum-down
obligation, and within its availability- and forced-outage-derated capacity.

Storage charge and discharge are separated by a binary operating-mode variable
in both SCUC and SCED. A horizon-end SOC can be imposed by setting
`terminal_storage_soc_mwh` in `DARTForecast`. It is optional because forcing a
terminal target at the end of every short rolling RT look-ahead can distort the
binding dispatch. During rolling simulation, a supplied target applies only to
a slice that reaches the end of the corresponding input forecast.

## Operational safeguards

`DARTDispatchResult` records the solver termination status, primal status,
solve time, and relative optimality gap. An optimal result is always accepted.
By default, a time-limited award is also accepted when it has a feasible primal
solution and a relative gap no greater than one percent. Configure this with
`accept_feasible_time_limit` and `maximum_relative_gap`. The fixed-integer LP
used to produce settlement prices must still solve to optimality.

Explicit generator-contingency variables scale with the number of generators,
intervals, and monitored outages. DART estimates that block before creating it
and rejects a solve above `maximum_security_variables` (two million by
default). Reduce the look-ahead or the set of generators marked
`contingency_eligible`, or deliberately raise the limit after confirming the
available memory and solver capacity.

Inputs are checked before model construction for dimensions, finite values,
physical limits, known reserve products, valid state values, and configuration
consistency.

## Settlements

Energy settles at fixed-integer nodal prices. Because contingency deliverability
can make reserve value resource-specific, V1 pays reserve awards at each
resource's submitted reserve offer instead of reporting a misleading uniform
security-reserve price. The result field
`reserve_requirement_shadow_price_per_mw_hour` is the informational dual of the
system reserve-requirement constraint; settlements do not use it. The
settlement output identifies this rule as `:pay_as_bid`.

Settlement input must contain one binding column per result, a valid
chronological RT-to-DA mapping, and exactly one DA interval of RT duration for
each DA award. Reserve and uplift charges are allocated in proportion to
real-time served energy. If there is no served energy, the amounts are reported
explicitly as `unallocated_reserve_charge` and
`unallocated_uplift_charge`; they are included in the settlement balance rather
than silently disappearing.

## Minimal example

The ModelCases repository contains `DART_two_bus_example.jl`, a programmatic
example with no generated or committed data files.

From the HOPE repository root, run it with:

```bash
julia --project=. ModelCases/DART_two_bus_example.jl
```
6 changes: 6 additions & 0 deletions docs/src/model_introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,9 @@ Both `GTEP` and `PCM` support:
- `2`: REG + SPIN + NSPIN
- `summary_table`:
- `1`: write summary analytics to `output/Analysis/Summary_*.csv`

## DART Module

The lightweight in-memory DART API provides individual-generator day-ahead
SCUC, rolling real-time SCED, generator N-1 security, and two-settlement
accounting. See [DART](@ref).
Loading