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
23 changes: 0 additions & 23 deletions .github/workflows/Benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ on:
permissions:
contents: read
pull-requests: write
env:
JULIA_PKG_USE_CLI_GIT: true

concurrency:
group: benchmark-${{ github.event.pull_request.number }}
Expand All @@ -17,29 +15,8 @@ concurrency:
jobs:
benchmark:
runs-on: ubuntu-latest
env:
POWERIO_JL_TOKEN: ${{ secrets.POWERIO_JL_REPO_TOKEN }}
steps:
- name: Probe PowerIO.jl access
id: powerio-jl
env:
GIT_TERMINAL_PROMPT: 0
run: |
if [ -n "${POWERIO_JL_TOKEN}" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
elif git ls-remote https://github.com/eigenergy/PowerIO.jl.git >/dev/null 2>&1; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Skip notice (PowerIO.jl unavailable)
if: steps.powerio-jl.outputs.available != 'true'
run: echo "PowerIO.jl is not publicly readable and POWERIO_JL_REPO_TOKEN is not set; skipping benchmarks until PowerIO.jl is public or the token is configured."
- name: Configure PowerIO.jl access
if: env.POWERIO_JL_TOKEN != ''
run: git config --global url."https://x-access-token:${POWERIO_JL_TOKEN}@github.com/eigenergy/".insteadOf "https://github.com/eigenergy/"
- uses: MilesCranmer/AirspeedVelocity.jl@315c11b51ceee8ebd6063d70cff6ae499a040d28
if: steps.powerio-jl.outputs.available == 'true'
with:
julia-version: '1'
mode: 'time,memory'
Expand Down
73 changes: 0 additions & 73 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,18 @@ on:
- cron: '0 4 * * 0'
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
JULIA_PKG_USE_CLI_GIT: true
jobs:
# Runs on every PR and push — single fast job
test:
runs-on: ubuntu-latest
env:
# PowerIO.jl is private until release; surface the secret as env so steps can
# skip cleanly when CI does not have cross-repo read access.
POWERIO_JL_TOKEN: ${{ secrets.POWERIO_JL_REPO_TOKEN }}
steps:
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v3
with:
version: '1'
- name: Probe PowerIO.jl access
id: powerio-jl
env:
GIT_TERMINAL_PROMPT: 0
run: |
if [ -n "${POWERIO_JL_TOKEN}" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
elif git ls-remote https://github.com/eigenergy/PowerIO.jl.git >/dev/null 2>&1; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Skip notice (PowerIO.jl unavailable)
if: steps.powerio-jl.outputs.available != 'true'
run: echo "PowerIO.jl is not publicly readable and POWERIO_JL_REPO_TOKEN is not set; skipping PowerDiff tests until PowerIO.jl is public or the token is configured."
- name: Configure PowerIO.jl access
if: env.POWERIO_JL_TOKEN != ''
run: git config --global url."https://x-access-token:${POWERIO_JL_TOKEN}@github.com/eigenergy/".insteadOf "https://github.com/eigenergy/"
- uses: julia-actions/cache@v3
if: steps.powerio-jl.outputs.available == 'true'
- uses: julia-actions/julia-buildpkg@v1
if: steps.powerio-jl.outputs.available == 'true'
- uses: julia-actions/julia-runtest@v1
if: steps.powerio-jl.outputs.available == 'true'

# Full matrix — only on main push and scheduled runs
test-full:
Expand All @@ -62,37 +36,14 @@ jobs:
- julia-version: 'nightly'
os: ubuntu-latest
continue-on-error: ${{ matrix.julia-version == 'nightly' }}
env:
POWERIO_JL_TOKEN: ${{ secrets.POWERIO_JL_REPO_TOKEN }}
steps:
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v3
with:
version: ${{ matrix.julia-version }}
- name: Probe PowerIO.jl access
id: powerio-jl
env:
GIT_TERMINAL_PROMPT: 0
run: |
if [ -n "${POWERIO_JL_TOKEN}" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
elif git ls-remote https://github.com/eigenergy/PowerIO.jl.git >/dev/null 2>&1; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Skip notice (PowerIO.jl unavailable)
if: steps.powerio-jl.outputs.available != 'true'
run: echo "PowerIO.jl is not publicly readable and POWERIO_JL_REPO_TOKEN is not set; skipping PowerDiff tests until PowerIO.jl is public or the token is configured."
- name: Configure PowerIO.jl access
if: env.POWERIO_JL_TOKEN != ''
run: git config --global url."https://x-access-token:${POWERIO_JL_TOKEN}@github.com/eigenergy/".insteadOf "https://github.com/eigenergy/"
- uses: julia-actions/cache@v3
if: steps.powerio-jl.outputs.available == 'true'
- uses: julia-actions/julia-buildpkg@v1
if: steps.powerio-jl.outputs.available == 'true'
- uses: julia-actions/julia-runtest@v1
if: steps.powerio-jl.outputs.available == 'true'

# APF extension — only on main push and scheduled runs.
# Pinned to Julia 1.12 because upstream AcceleratedDCPowerFlows restricted
Expand All @@ -101,37 +52,13 @@ jobs:
if: github.event_name != 'pull_request'
name: APF Extension (Julia 1.12)
runs-on: ubuntu-latest
env:
POWERIO_JL_TOKEN: ${{ secrets.POWERIO_JL_REPO_TOKEN }}
steps:
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v3
with:
version: '1.12'
- name: Probe PowerIO.jl access
id: powerio-jl
env:
GIT_TERMINAL_PROMPT: 0
run: |
if [ -n "${POWERIO_JL_TOKEN}" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
elif git ls-remote https://github.com/eigenergy/PowerIO.jl.git >/dev/null 2>&1; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Skip notice (PowerIO.jl unavailable)
if: steps.powerio-jl.outputs.available != 'true'
run: echo "PowerIO.jl is not publicly readable and POWERIO_JL_REPO_TOKEN is not set; skipping PowerDiff tests until PowerIO.jl is public or the token is configured."
- name: Configure PowerIO.jl access
if: env.POWERIO_JL_TOKEN != ''
run: git config --global url."https://x-access-token:${POWERIO_JL_TOKEN}@github.com/eigenergy/".insteadOf "https://github.com/eigenergy/"
- uses: julia-actions/cache@v3
if: steps.powerio-jl.outputs.available == 'true'
- name: Install APF (unregistered)
if: steps.powerio-jl.outputs.available == 'true'
run: julia --project=. -e 'using Pkg; Pkg.add(url="https://github.com/mtanneau/AcceleratedDCPowerFlows.jl.git")'
- uses: julia-actions/julia-buildpkg@v1
if: steps.powerio-jl.outputs.available == 'true'
- uses: julia-actions/julia-runtest@v1
if: steps.powerio-jl.outputs.available == 'true'
23 changes: 0 additions & 23 deletions .github/workflows/Documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,21 @@ on:
pull_request:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
JULIA_PKG_USE_CLI_GIT: true
jobs:
build:
permissions:
actions: write
contents: write
statuses: write
runs-on: ubuntu-latest
env:
POWERIO_JL_TOKEN: ${{ secrets.POWERIO_JL_REPO_TOKEN }}
steps:
- uses: actions/checkout@v6
- uses: julia-actions/setup-julia@v3
with:
version: '1'
- name: Probe PowerIO.jl access
id: powerio-jl
env:
GIT_TERMINAL_PROMPT: 0
run: |
if [ -n "${POWERIO_JL_TOKEN}" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
elif git ls-remote https://github.com/eigenergy/PowerIO.jl.git >/dev/null 2>&1; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Skip notice (PowerIO.jl unavailable)
if: steps.powerio-jl.outputs.available != 'true'
run: echo "PowerIO.jl is not publicly readable and POWERIO_JL_REPO_TOKEN is not set; skipping docs until PowerIO.jl is public or the token is configured."
- name: Configure PowerIO.jl access
if: env.POWERIO_JL_TOKEN != ''
run: git config --global url."https://x-access-token:${POWERIO_JL_TOKEN}@github.com/eigenergy/".insteadOf "https://github.com/eigenergy/"
- name: Install dependencies
if: steps.powerio-jl.outputs.available == 'true'
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
- name: Build and deploy
if: steps.powerio-jl.outputs.available == 'true'
run: julia --project=docs/ docs/make.jl
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
8 changes: 1 addition & 7 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ NLPModelsIpopt = "f4238b75-b362-5c4c-b852-0801c9a21d71"
PowerIO = "05ed8b54-f668-4096-9d0d-e8c3dd9dc169"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

# PowerIO is not yet in the General registry, so it is pinned to the PowerIO.jl repo
# here. Once PowerIO registers, drop this [sources] block and add a [compat] entry.
# The PowerIO branch named in `rev` must be pushed before this branch's CI runs.
[sources]
PowerIO = {url = "https://github.com/eigenergy/PowerIO.jl.git", rev = "main"}

[weakdeps]
AcceleratedDCPowerFlows = "c32744f1-403b-4af7-9195-1da907387c09"

Expand All @@ -32,7 +26,7 @@ Ipopt = "1"
JuMP = "1"
LazyArtifacts = "1"
NLPModelsIpopt = "0.11.2"
PowerIO = "0.0.1, 0.1"
PowerIO = "0.1.3"
julia = "1.9"

[extras]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Pkg.add(url="https://github.com/grid-opt-alg-lab/PowerDiff.jl.git")
```julia
using PowerDiff

# Load a MATPOWER v2 case into PowerDiff's typed representation
# Parse a MATPOWER v2 case into a PowerIO.Network
net = parse_file("case14.m")
dc_net = DCNetwork(net)
d = calc_demand_vector(net)
Expand Down
32 changes: 16 additions & 16 deletions docs/powerio-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ PowerIO is PowerDiff's parser and data layer. PowerDiff does not expose a parser
backend switch.

`PowerDiff.parse_file(path)` resolves the path, requires a MATPOWER `.m` file, and
calls `PowerIO.parse_file(path)`. `PowerDiff.parse_file(io; filetype="m")` reads
the stream and calls `PowerIO.parse_str(text, "matpower")`.
returns a `PowerIO.Network` via `PowerIO.parse_file`. `PowerDiff.parse_file(io)`
reads the stream and calls `PowerIO.parse_str(text, "matpower")`. Pass the result to
[`DCNetwork`](@ref) or [`ACNetwork`](@ref).

PowerIO returns a raw, lossless `Network`: MW/MVAr, degrees, original bus ids, raw
bus types, loads and shunts as first class records, and out of service elements
retained. PowerDiff then maps that `Network` into its own `ParsedCase` and keeps
the normalization it already owns:
The network constructors build directly from `PowerIO.to_powerdata(net)`, which
already returns normalized data: per-unit scaling by `base_mva`, degree-to-radian
conversion, out-of-service and isolated-element filtering, bus-type inference,
per-bus load/shunt aggregation, and polynomial cost rescaling. PowerDiff layers on
only the OPF modeling it owns:

- per unit scaling by `base_mva`
- degree to radian conversion
- bus type inference and slack selection
- out of service and isolated element filtering
- tap `0` to `1`
- angle bound normalization
- generator cost rescaling and padding
- `rate_a` fallback
- polynomial cost interpretation: it reads the constant, linear, and quadratic
coefficients straight from `to_powerdata`'s generator rows (already per-unit and
right-aligned). PWL costs are rejected; higher-order polynomials are rejected by
`to_powerdata` itself. A generator with no cost record is treated as cost-free.
- a finite `rate_a` fallback when the source leaves the thermal limit at `0`
- default angle-difference bounds

PowerDiff rejects PowerIO networks carrying storage or HVDC/dcline records because
the current `ParsedCase` model has no fields for them.
PowerDiff rejects networks carrying storage or HVDC/dcline records, which it does
not model.

The parser tests assert path and IO parity through this single PowerIO path.
2 changes: 1 addition & 1 deletion docs/src/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Stores the DC network topology and parameters.
| `tau` | `Float64` | Regularization parameter |
| `id_map` | `IDMapping` | Bidirectional element ID mapping (original ↔ sequential) |

Construct from typed MATPOWER data with `DCNetwork(parse_file("case14.m"))`, or
Construct from a parsed MATPOWER network with `DCNetwork(parse_file("case14.m"))`, or
with explicit parameters: `DCNetwork(n, m, k, A, G_inc, b; ...)`.

### ACNetwork
Expand Down
6 changes: 0 additions & 6 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
## MATPOWER Parser

```@docs
ParsedCase
ParsedBus
ParsedGen
ParsedBranch
ParsedLoad
ParsedShunt
parse_file
parse_matpower
parse_matpower_struct
Expand Down
2 changes: 1 addition & 1 deletion docs/src/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This guide walks through the main workflows: DC power flow, DC OPF with LMP anal
```julia
using PowerDiff

# Load a MATPOWER v2 case into PowerDiff's typed representation
# Parse a MATPOWER v2 case into a PowerIO.Network
net = parse_file("case14.m")
```

Expand Down
2 changes: 1 addition & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Pkg.add(url="https://github.com/grid-opt-alg-lab/PowerDiff.jl.git")
```julia
using PowerDiff

# Load a MATPOWER v2 case into PowerDiff's typed representation
# Parse a MATPOWER v2 case into a PowerIO.Network
net = parse_file("case14.m")
dc_net = DCNetwork(net)
d = calc_demand_vector(net)
Expand Down
22 changes: 8 additions & 14 deletions experiments/ipp_market_planning.jl
Original file line number Diff line number Diff line change
Expand Up @@ -571,16 +571,12 @@ function run_tier2(; outdir::String=@__DIR__,
println("="^65)

case_path = joinpath(dirname(pathof(PM)), "..", "test", "data", "matpower", "case14.m")
raw = PM.parse_file(case_path)
PM.make_basic_network!(raw) # populates rate_a defaults
net = DCNetwork(parse_file(case_path))
# case14 ships with very loose flow limits (max loading ~15% on the binding
# line at default rate_a). Scale by 0.10 to bring 2 lines to the bound and
# line at default rate_a). Scale fmax by 0.10 to bring 2 lines to the bound and
# produce meaningful LMP variation. This is the "stressed" scenario IPPs
# actually care about — peak loading, outages, etc.
for (_, br) in raw["branch"]
br["rate_a"] *= fmax_scale
end
net = DCNetwork(raw)
net.fmax .*= fmax_scale
# Break generator degeneracy. Without this the KKT system is singular at
# the optimum (multiple gens at upper bound), Tikhonov regularization kicks
# in, and the matrix free VJP returns essentially zero gradient.
Expand Down Expand Up @@ -728,9 +724,8 @@ const RTS_LOAD_CSV = expanduser("~/Datasets/RTS-GMLC/RTS_Data/timeseries_data_fi
function load_rts_gmlc()
isfile(RTS_PATH) || error("RTS_GMLC.m not at $RTS_PATH")
raw = PM.parse_file(RTS_PATH)
if !isempty(raw["dcline"])
empty!(raw["dcline"]) # PowerModels DC line workaround
end
# PowerDiff does not model HVDC/dclines; drop them (RTS-GMLC ships DC lines).
isempty(raw["dcline"]) || empty!(raw["dcline"])
PM.make_basic_network!(raw) # populates rate_a defaults & sequential IDs
return raw
end
Expand Down Expand Up @@ -765,11 +760,10 @@ function run_tier4(; outdir::String=@__DIR__,
println("="^65)

raw = load_rts_gmlc()
# Bridge the (dcline-free) PowerModels dict into PowerDiff via PowerIO.
net = DCNetwork(PowerDiff.PowerIO.from_powermodels(raw))
# Tighten flow limits so congestion is meaningful (RTS-GMLC ships generous limits)
for (_, br) in raw["branch"]
br["rate_a"] *= 0.5
end
net = DCNetwork(raw)
net.fmax .*= 0.5
# Break gen degeneracy so the KKT system is non singular at the optimum.
for i in eachindex(net.gmax)
if net.gmax[i] > 0.01
Expand Down
4 changes: 2 additions & 2 deletions src/PowerDiff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ using JuMP
using Ipopt
using ExaModels
using NLPModelsIpopt
using PowerIO

const MOI = JuMP.MOI

Expand All @@ -28,7 +29,7 @@ const MOI = JuMP.MOI
# =============================================================================
const _SILENCE_WARNINGS = Ref(false)

include("parser.jl")
include("artifacts.jl")

"""
silence()
Expand Down Expand Up @@ -102,7 +103,6 @@ export Sensitivity, silence
export operand_symbols, parameter_symbols
export jvp, vjp, jvp!, vjp!, dict_to_vec, vec_to_dict, kkt_dims
export parse_file, parse_matpower, parse_matpower_struct, get_path
export ParsedCase, ParsedBus, ParsedGen, ParsedBranch, ParsedLoad, ParsedShunt

# DC Power Flow Types
export DCNetwork, DCPowerFlowState
Expand Down
Loading