cugrid 0.0.1.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cugrid-0.0.1.dev1/.clang-format +4 -0
- cugrid-0.0.1.dev1/.github/workflows/ci.yml +55 -0
- cugrid-0.0.1.dev1/.github/workflows/publish.yml +39 -0
- cugrid-0.0.1.dev1/.gitignore +20 -0
- cugrid-0.0.1.dev1/.pre-commit-config.yaml +23 -0
- cugrid-0.0.1.dev1/.python-version +1 -0
- cugrid-0.0.1.dev1/CLAUDE.md +113 -0
- cugrid-0.0.1.dev1/CMakeLists.txt +72 -0
- cugrid-0.0.1.dev1/PKG-INFO +92 -0
- cugrid-0.0.1.dev1/README.md +77 -0
- cugrid-0.0.1.dev1/benchmarks/.gitkeep +0 -0
- cugrid-0.0.1.dev1/docs/benchmarks/.gitkeep +0 -0
- cugrid-0.0.1.dev1/docs/math/.gitkeep +0 -0
- cugrid-0.0.1.dev1/include/cugrid/common/device_vector.hpp +96 -0
- cugrid-0.0.1.dev1/include/cugrid/common/error_check.hpp +38 -0
- cugrid-0.0.1.dev1/include/cugrid/common/timer.hpp +54 -0
- cugrid-0.0.1.dev1/plans/cugrid-implementation-plan.md +316 -0
- cugrid-0.0.1.dev1/pyproject.toml +28 -0
- cugrid-0.0.1.dev1/python/cugrid/__init__.py +4 -0
- cugrid-0.0.1.dev1/python/cugrid/_bindings.cpp +27 -0
- cugrid-0.0.1.dev1/scripts/make_reference.py +80 -0
- cugrid-0.0.1.dev1/src/common/.gitkeep +0 -0
- cugrid-0.0.1.dev1/src/io/.gitkeep +0 -0
- cugrid-0.0.1.dev1/src/linalg/.gitkeep +0 -0
- cugrid-0.0.1.dev1/src/network/.gitkeep +0 -0
- cugrid-0.0.1.dev1/src/opf/cpu/.gitkeep +0 -0
- cugrid-0.0.1.dev1/src/opf/cuda/.gitkeep +0 -0
- cugrid-0.0.1.dev1/src/pf/cpu/.gitkeep +0 -0
- cugrid-0.0.1.dev1/src/pf/cuda/.gitkeep +0 -0
- cugrid-0.0.1.dev1/tests/cases/.gitkeep +0 -0
- cugrid-0.0.1.dev1/tests/unit/CMakeLists.txt +17 -0
- cugrid-0.0.1.dev1/tests/unit/test_timer.cpp +28 -0
- cugrid-0.0.1.dev1/tests/validation/.gitkeep +0 -0
- cugrid-0.0.1.dev1/uv.lock +729 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- uses: pre-commit/action@v3.0.1
|
|
17
|
+
|
|
18
|
+
cpu-tests:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: astral-sh/setup-uv@v5
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
- name: Install build tooling
|
|
26
|
+
# The runner's system Python is externally managed (PEP 668), so `uv pip
|
|
27
|
+
# install --system` is rejected; use a throwaway venv and put it on PATH.
|
|
28
|
+
run: |
|
|
29
|
+
uv venv .venv-tools
|
|
30
|
+
uv pip install --python .venv-tools/bin/python cmake ninja
|
|
31
|
+
echo "$PWD/.venv-tools/bin" >> "$GITHUB_PATH"
|
|
32
|
+
- name: Configure (CPU-only)
|
|
33
|
+
# No CUDA toolchain on GitHub-hosted runners: CUGRID_ENABLE_CUDA auto-detects
|
|
34
|
+
# off. GPU kernels are exercised on a separate self-hosted/manual GPU stage.
|
|
35
|
+
run: cmake -S . -B build -G Ninja -DCUGRID_BUILD_TESTS=ON -DCUGRID_BUILD_PYTHON=OFF
|
|
36
|
+
- name: Build
|
|
37
|
+
run: cmake --build build
|
|
38
|
+
- name: Test
|
|
39
|
+
run: ctest --test-dir build --output-on-failure
|
|
40
|
+
|
|
41
|
+
python-build:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v4
|
|
45
|
+
- uses: astral-sh/setup-uv@v5
|
|
46
|
+
with:
|
|
47
|
+
enable-cache: true
|
|
48
|
+
- name: uv sync
|
|
49
|
+
run: uv sync --extra dev
|
|
50
|
+
- name: Smoke test the extension
|
|
51
|
+
run: uv run python -c "import cugrid; print(cugrid.hello()); print('cuda_available =', cugrid.cuda_available())"
|
|
52
|
+
|
|
53
|
+
# GPU kernel build + tests require a CUDA toolkit and are not available on
|
|
54
|
+
# GitHub-hosted runners. Run manually or wire to a self-hosted GPU runner:
|
|
55
|
+
# cmake -S . -B build -DCUGRID_ENABLE_CUDA=ON && cmake --build build && ctest --test-dir build
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: astral-sh/setup-uv@v5
|
|
13
|
+
with:
|
|
14
|
+
enable-cache: true
|
|
15
|
+
- name: Build sdist and wheel
|
|
16
|
+
# No CUDA toolchain on GitHub-hosted runners, same as the `python-build`
|
|
17
|
+
# CI job: CUGRID_ENABLE_CUDA auto-detects off and this produces a
|
|
18
|
+
# CPU-only wheel. GPU users build from source until a self-hosted GPU
|
|
19
|
+
# release stage exists.
|
|
20
|
+
run: uv build
|
|
21
|
+
- uses: actions/upload-artifact@v4
|
|
22
|
+
with:
|
|
23
|
+
name: dist
|
|
24
|
+
path: dist/
|
|
25
|
+
|
|
26
|
+
publish:
|
|
27
|
+
needs: build
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment: pypi
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/download-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist
|
|
34
|
+
path: dist/
|
|
35
|
+
- uses: astral-sh/setup-uv@v5
|
|
36
|
+
- name: Publish to PyPI
|
|
37
|
+
env:
|
|
38
|
+
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
|
39
|
+
run: uv publish
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
build/
|
|
2
|
+
build-*/
|
|
3
|
+
cmake-build-*/
|
|
4
|
+
_skbuild/
|
|
5
|
+
dist/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.pyc
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
*.so
|
|
12
|
+
*.pyd
|
|
13
|
+
*.parquet
|
|
14
|
+
*.npz
|
|
15
|
+
.cache/
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.idea/
|
|
19
|
+
.DS_Store
|
|
20
|
+
CMakeUserPresets.json
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-merge-conflict
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: check-toml
|
|
11
|
+
- id: mixed-line-ending
|
|
12
|
+
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.15.20
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff
|
|
17
|
+
args: [--exit-non-zero-on-fix]
|
|
18
|
+
|
|
19
|
+
- repo: https://github.com/pre-commit/mirrors-clang-format
|
|
20
|
+
rev: v18.1.8
|
|
21
|
+
hooks:
|
|
22
|
+
- id: clang-format
|
|
23
|
+
types_or: [c++, c]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Instructions:
|
|
6
|
+
|
|
7
|
+
- do not include claude code in commit message and PR message
|
|
8
|
+
|
|
9
|
+
## What this is
|
|
10
|
+
|
|
11
|
+
`cugrid` is a CUDA-accelerated AC power flow / ACOPF solver for large-scale (20k+ bus) transmission
|
|
12
|
+
networks: batched Newton-Raphson PF, N-1/N-1-1 contingency screening, hosting capacity, and
|
|
13
|
+
curtailment optimization. See [`plans/cugrid-implementation-plan.md`](plans/cugrid-implementation-plan.md)
|
|
14
|
+
for the full phased roadmap, workload definitions, and architecture (the "screening funnel").
|
|
15
|
+
|
|
16
|
+
**Current state:** Phase 0 (scaffolding) only — no network model or solver logic exists yet. The
|
|
17
|
+
codebase is a build skeleton: common C++ utilities, a pybind11 hello-world extension, and CI.
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
|
|
21
|
+
Python tooling uses [uv](https://docs.astral.sh/uv/), pinned to the version in `.python-version` (3.12).
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Python extension: build + install editable, then run
|
|
25
|
+
uv sync --extra dev
|
|
26
|
+
uv run python -c "import cugrid; print(cugrid.hello()); print(cugrid.cuda_available())"
|
|
27
|
+
|
|
28
|
+
# Re-run after touching any C++ source under python/cugrid/ or include/ — `uv run`
|
|
29
|
+
# alone does not rebuild the extension.
|
|
30
|
+
uv sync --extra dev
|
|
31
|
+
|
|
32
|
+
# C++ unit tests (CPU-only, GoogleTest fetched via CMake FetchContent)
|
|
33
|
+
cmake -S . -B build -DCUGRID_BUILD_PYTHON=OFF
|
|
34
|
+
cmake --build build
|
|
35
|
+
ctest --test-dir build --output-on-failure
|
|
36
|
+
|
|
37
|
+
# Run a single test by name
|
|
38
|
+
ctest --test-dir build -R ScopedTimer.LabelIsPreserved --output-on-failure
|
|
39
|
+
|
|
40
|
+
# Force CUDA on/off regardless of auto-detection
|
|
41
|
+
cmake -S . -B build -DCUGRID_ENABLE_CUDA=ON # requires nvcc + CUDA Toolkit on PATH
|
|
42
|
+
cmake -S . -B build -DCUGRID_ENABLE_CUDA=OFF
|
|
43
|
+
|
|
44
|
+
# Lint (ruff + clang-format, run via pre-commit; see .pre-commit-config.yaml)
|
|
45
|
+
uv run pre-commit install # one-time: installs the git commit hook
|
|
46
|
+
uv run pre-commit run --all-files
|
|
47
|
+
|
|
48
|
+
# Generate pandapower PF reference solutions used by validation tests
|
|
49
|
+
uv sync --extra reference
|
|
50
|
+
uv run python scripts/make_reference.py --cases case9 case118 --out tests/cases
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Architecture
|
|
54
|
+
|
|
55
|
+
### CUDA is optional and auto-detected — never assume it's present
|
|
56
|
+
|
|
57
|
+
The top-level `CMakeLists.txt` runs `check_language(CUDA)` *before* `project()` and sets
|
|
58
|
+
`CUGRID_ENABLE_CUDA` from whether a CUDA compiler was actually found, not from the platform. This
|
|
59
|
+
machine (and most dev/CI machines) has no CUDA toolkit, so every change must build and pass tests
|
|
60
|
+
in CPU-only mode. When `CUGRID_ENABLE_CUDA` is on, the `cugrid_common` interface target gets
|
|
61
|
+
`CUGRID_HAVE_CUDA` defined and links `CUDA::cudart`; `CUGRID_HAVE_NVTX` is
|
|
62
|
+
added separately if `CUDA::nvtx3` exists. All CUDA-only code (`include/cugrid/common/device_vector.hpp`,
|
|
63
|
+
anything under `src/*/cuda/`) must be wrapped in `#ifdef CUGRID_HAVE_CUDA` so the file still compiles
|
|
64
|
+
(as a no-op) in CPU-only builds — see `error_check.hpp` and `device_vector.hpp` for the pattern.
|
|
65
|
+
`timer.hpp`'s `ScopedTimer` is the one common utility that works unconditionally (chrono-based),
|
|
66
|
+
with NVTX emission compiled in only under `CUGRID_HAVE_NVTX`.
|
|
67
|
+
|
|
68
|
+
### Build topology
|
|
69
|
+
|
|
70
|
+
- `cugrid_common` (CMakeLists.txt) is a header-only INTERFACE target: include path + `cxx_std_20`
|
|
71
|
+
+ the CUDA/NVTX defines above. Every future target (CPU or GPU, library or test) links against it
|
|
72
|
+
rather than repeating include/define logic.
|
|
73
|
+
- The pybind11 module compiles to `_core` (from `python/cugrid/_bindings.cpp`) and is imported by
|
|
74
|
+
`python/cugrid/__init__.py` as `cugrid._core`. Native code lives under
|
|
75
|
+
`python/cugrid/` only for the bindings shim; the actual C++ implementation goes in `src/` and
|
|
76
|
+
`include/cugrid/`, and the bindings link against it — don't grow business logic inside
|
|
77
|
+
`_bindings.cpp`.
|
|
78
|
+
- `CUGRID_BUILD_TESTS` (GoogleTest, fetched) and `CUGRID_BUILD_PYTHON` (pybind11 module) are
|
|
79
|
+
independent CMake options so CI can build/test the C++ side without touching Python packaging,
|
|
80
|
+
and `pip`/`uv` installs skip the GoogleTest fetch (`pyproject.toml` sets
|
|
81
|
+
`CUGRID_BUILD_TESTS=OFF` for the scikit-build-core path).
|
|
82
|
+
|
|
83
|
+
### Planned source layout (see `plans/cugrid-implementation-plan.md` §3 and §5)
|
|
84
|
+
|
|
85
|
+
`src/{io,network,pf/cpu,pf/cuda,opf/cpu,opf/cuda,linalg,common}` currently hold only `.gitkeep` —
|
|
86
|
+
they exist to match the plan's phase-by-phase build-out:
|
|
87
|
+
- Every CUDA kernel gets a CPU twin in the parallel `cpu/` directory, validated against pandapower
|
|
88
|
+
first (design principle: CPU reference before GPU port).
|
|
89
|
+
- All device data is Structure-of-Arrays (SoA), flat arrays indexed by bus/branch/gen ID — never
|
|
90
|
+
per-object structs on GPU.
|
|
91
|
+
- APIs are batch-first from Phase 1 onward (`n_scenarios` dimension, default 1) — this is a
|
|
92
|
+
deliberate constraint from the plan because retrofitting batching later is expensive, and the
|
|
93
|
+
whole roadmap (N-1, N-1-1, 8760-hour time series, hosting capacity) is one shape of batched
|
|
94
|
+
scenario sweep with different filtering.
|
|
95
|
+
- Symbolic factorization (sparsity pattern) is computed once per topology and reused; only numeric
|
|
96
|
+
refactorization happens per iteration/scenario. Outages are modeled as low-rank Woodbury updates
|
|
97
|
+
to the base-case factorization (Phase 4), not full re-factorization.
|
|
98
|
+
|
|
99
|
+
### CI shape
|
|
100
|
+
|
|
101
|
+
Three independent GitHub Actions jobs (`.github/workflows/ci.yml`): `lint` (runs
|
|
102
|
+
`.pre-commit-config.yaml` via `pre-commit/action` — ruff + clang-format + the standard
|
|
103
|
+
pre-commit-hooks), `cpu-tests` (raw CMake/CTest build with `CUGRID_BUILD_PYTHON=OFF`, uses
|
|
104
|
+
`astral-sh/setup-uv`), `python-build` (`uv sync` + import smoke test, uses `astral-sh/setup-uv`).
|
|
105
|
+
None of these have a CUDA toolchain — GPU kernels are validated on a self-hosted/manual GPU stage,
|
|
106
|
+
not GitHub-hosted runners. `cpu-tests` installs `cmake`/`ninja` into a throwaway `uv venv` and adds
|
|
107
|
+
it to `$GITHUB_PATH`, rather than `uv pip install --system`, because the runner's system Python is
|
|
108
|
+
PEP 668 externally-managed.
|
|
109
|
+
|
|
110
|
+
## Code style
|
|
111
|
+
|
|
112
|
+
- Repo-wide `.clang-format`: Google style, 120-column limit, left-aligned pointers.
|
|
113
|
+
- C++ namespace is `cugrid::common` (mirrors `include/cugrid/`), not `gridcuda`.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.24)
|
|
2
|
+
|
|
3
|
+
include(CheckLanguage)
|
|
4
|
+
check_language(CUDA)
|
|
5
|
+
if(CMAKE_CUDA_COMPILER)
|
|
6
|
+
set(_cugrid_cuda_default ON)
|
|
7
|
+
else()
|
|
8
|
+
set(_cugrid_cuda_default OFF)
|
|
9
|
+
endif()
|
|
10
|
+
|
|
11
|
+
# Auto-detected: ON if a CUDA compiler is on PATH, OFF otherwise (e.g. CPU-only dev
|
|
12
|
+
# machines, cloud CI). Override explicitly with -DCUGRID_ENABLE_CUDA=ON/OFF.
|
|
13
|
+
option(CUGRID_ENABLE_CUDA "Build CUDA kernels" ${_cugrid_cuda_default})
|
|
14
|
+
option(CUGRID_BUILD_TESTS "Build C++ unit tests (GoogleTest, fetched)" ON)
|
|
15
|
+
option(CUGRID_BUILD_PYTHON "Build the pybind11 extension module" ON)
|
|
16
|
+
|
|
17
|
+
if(CUGRID_ENABLE_CUDA AND NOT CMAKE_CUDA_COMPILER)
|
|
18
|
+
message(WARNING "CUGRID_ENABLE_CUDA=ON but no CUDA compiler was found on PATH; building CPU-only.")
|
|
19
|
+
set(CUGRID_ENABLE_CUDA OFF)
|
|
20
|
+
endif()
|
|
21
|
+
|
|
22
|
+
if(CUGRID_ENABLE_CUDA)
|
|
23
|
+
project(cugrid LANGUAGES CXX CUDA)
|
|
24
|
+
else()
|
|
25
|
+
project(cugrid LANGUAGES CXX)
|
|
26
|
+
endif()
|
|
27
|
+
|
|
28
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
29
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
30
|
+
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
31
|
+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
32
|
+
|
|
33
|
+
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|
34
|
+
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE)
|
|
35
|
+
endif()
|
|
36
|
+
|
|
37
|
+
# Header-only interface target: include path + language standard + optional
|
|
38
|
+
# CUDA/NVTX defines. Every cugrid target (CPU or GPU) links against this.
|
|
39
|
+
add_library(cugrid_common INTERFACE)
|
|
40
|
+
target_include_directories(cugrid_common INTERFACE
|
|
41
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
42
|
+
$<INSTALL_INTERFACE:include>)
|
|
43
|
+
target_compile_features(cugrid_common INTERFACE cxx_std_20)
|
|
44
|
+
|
|
45
|
+
if(CUGRID_ENABLE_CUDA)
|
|
46
|
+
set(CMAKE_CUDA_STANDARD 20)
|
|
47
|
+
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
|
|
48
|
+
find_package(CUDAToolkit REQUIRED)
|
|
49
|
+
|
|
50
|
+
target_compile_definitions(cugrid_common INTERFACE CUGRID_HAVE_CUDA)
|
|
51
|
+
target_link_libraries(cugrid_common INTERFACE CUDA::cudart)
|
|
52
|
+
|
|
53
|
+
if(TARGET CUDA::nvtx3)
|
|
54
|
+
target_compile_definitions(cugrid_common INTERFACE CUGRID_HAVE_NVTX)
|
|
55
|
+
target_link_libraries(cugrid_common INTERFACE CUDA::nvtx3)
|
|
56
|
+
endif()
|
|
57
|
+
endif()
|
|
58
|
+
|
|
59
|
+
if(CUGRID_BUILD_PYTHON)
|
|
60
|
+
find_package(Python 3.12 COMPONENTS Interpreter Development.Module REQUIRED)
|
|
61
|
+
find_package(pybind11 CONFIG REQUIRED)
|
|
62
|
+
|
|
63
|
+
pybind11_add_module(_core python/cugrid/_bindings.cpp)
|
|
64
|
+
target_link_libraries(_core PRIVATE cugrid_common)
|
|
65
|
+
|
|
66
|
+
install(TARGETS _core DESTINATION cugrid)
|
|
67
|
+
endif()
|
|
68
|
+
|
|
69
|
+
if(CUGRID_BUILD_TESTS)
|
|
70
|
+
enable_testing()
|
|
71
|
+
add_subdirectory(tests/unit)
|
|
72
|
+
endif()
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: cugrid
|
|
3
|
+
Version: 0.0.1.dev1
|
|
4
|
+
Summary: CUDA-accelerated AC power flow and ACOPF for large-scale transmission networks (20k+ buses)
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Provides-Extra: dev
|
|
8
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
9
|
+
Requires-Dist: pre-commit>=4; extra == "dev"
|
|
10
|
+
Provides-Extra: reference
|
|
11
|
+
Requires-Dist: pandapower; extra == "reference"
|
|
12
|
+
Requires-Dist: pandas; extra == "reference"
|
|
13
|
+
Requires-Dist: pyarrow; extra == "reference"
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# cugrid
|
|
17
|
+
|
|
18
|
+
CUDA-accelerated AC power flow and ACOPF for large-scale transmission networks:
|
|
19
|
+
batched Newton-Raphson PF, contingency screening (N-1 / N-1-1), hosting capacity, and
|
|
20
|
+
curtailment optimization. See [`plans/cugrid-implementation-plan.md`](plans/cugrid-implementation-plan.md)
|
|
21
|
+
for the full phased plan.
|
|
22
|
+
|
|
23
|
+
**Status:** Phase 0 (scaffolding) — no solver logic yet.
|
|
24
|
+
|
|
25
|
+
## Layout
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
include/cugrid/ public C++ headers (common/: DeviceVector, error checks, timers)
|
|
29
|
+
src/ io, network, pf/{cpu,cuda}, opf/{cpu,cuda}, linalg — filled in from Phase 1 onward
|
|
30
|
+
python/cugrid/ pybind11 module + Python package
|
|
31
|
+
tests/unit/ GoogleTest (C++), CPU-only, runs in CI on every PR
|
|
32
|
+
tests/validation/ accuracy checks vs pandapower / PowerModels (from Phase 2 onward)
|
|
33
|
+
tests/cases/ vendored small .m cases + generated reference solutions
|
|
34
|
+
scripts/ case download, pandapower/Ipopt reference generation
|
|
35
|
+
docs/math/ derivations (Jacobian, KKT, per-asset models)
|
|
36
|
+
docs/benchmarks/ performance reports
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Build
|
|
40
|
+
|
|
41
|
+
Requires CMake ≥ 3.24 and a C++20 compiler. CUDA (12.x) is auto-detected; the project
|
|
42
|
+
builds CPU-only when no `nvcc` is on `PATH` (e.g. laptops, cloud CI) and CUDA-enabled
|
|
43
|
+
targets simply aren't compiled.
|
|
44
|
+
|
|
45
|
+
Python tooling uses [uv](https://docs.astral.sh/uv/); the pinned interpreter version
|
|
46
|
+
lives in [`.python-version`](.python-version) (3.12).
|
|
47
|
+
|
|
48
|
+
### Python extension
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv sync --extra dev
|
|
52
|
+
uv run python -c "import cugrid; print(cugrid.hello()); print(cugrid.cuda_available())"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`uv sync` creates `.venv/`, installs the pinned Python if needed, and builds the pybind11
|
|
56
|
+
extension in editable mode via scikit-build-core. Re-run `uv sync` after touching C++
|
|
57
|
+
sources under `python/cugrid/` or `include/`; plain `uv run` does not rebuild automatically.
|
|
58
|
+
|
|
59
|
+
### C++ unit tests (CPU-only, GoogleTest fetched via CMake)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cmake -S . -B build -DCUGRID_BUILD_PYTHON=OFF
|
|
63
|
+
cmake --build build
|
|
64
|
+
ctest --test-dir build --output-on-failure
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Force CUDA on/off
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cmake -S . -B build -DCUGRID_ENABLE_CUDA=ON # requires nvcc + CUDA Toolkit on PATH
|
|
71
|
+
cmake -S . -B build -DCUGRID_ENABLE_CUDA=OFF # CPU-only even if a CUDA compiler is present
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Lint / pre-commit
|
|
75
|
+
|
|
76
|
+
Ruff (Python) and clang-format (C++) run via [pre-commit](https://pre-commit.com/):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
uv sync --extra dev
|
|
80
|
+
uv run pre-commit install # runs the hooks on `git commit`
|
|
81
|
+
uv run pre-commit run --all-files
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Reference solutions
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
uv sync --extra reference
|
|
88
|
+
uv run python scripts/make_reference.py --cases case9 case118 --out tests/cases
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Generates pandapower PF results (`V, θ` per bus; branch flows) that the CPU/GPU solvers
|
|
92
|
+
are validated against (Phase 2+).
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# cugrid
|
|
2
|
+
|
|
3
|
+
CUDA-accelerated AC power flow and ACOPF for large-scale transmission networks:
|
|
4
|
+
batched Newton-Raphson PF, contingency screening (N-1 / N-1-1), hosting capacity, and
|
|
5
|
+
curtailment optimization. See [`plans/cugrid-implementation-plan.md`](plans/cugrid-implementation-plan.md)
|
|
6
|
+
for the full phased plan.
|
|
7
|
+
|
|
8
|
+
**Status:** Phase 0 (scaffolding) — no solver logic yet.
|
|
9
|
+
|
|
10
|
+
## Layout
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
include/cugrid/ public C++ headers (common/: DeviceVector, error checks, timers)
|
|
14
|
+
src/ io, network, pf/{cpu,cuda}, opf/{cpu,cuda}, linalg — filled in from Phase 1 onward
|
|
15
|
+
python/cugrid/ pybind11 module + Python package
|
|
16
|
+
tests/unit/ GoogleTest (C++), CPU-only, runs in CI on every PR
|
|
17
|
+
tests/validation/ accuracy checks vs pandapower / PowerModels (from Phase 2 onward)
|
|
18
|
+
tests/cases/ vendored small .m cases + generated reference solutions
|
|
19
|
+
scripts/ case download, pandapower/Ipopt reference generation
|
|
20
|
+
docs/math/ derivations (Jacobian, KKT, per-asset models)
|
|
21
|
+
docs/benchmarks/ performance reports
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Build
|
|
25
|
+
|
|
26
|
+
Requires CMake ≥ 3.24 and a C++20 compiler. CUDA (12.x) is auto-detected; the project
|
|
27
|
+
builds CPU-only when no `nvcc` is on `PATH` (e.g. laptops, cloud CI) and CUDA-enabled
|
|
28
|
+
targets simply aren't compiled.
|
|
29
|
+
|
|
30
|
+
Python tooling uses [uv](https://docs.astral.sh/uv/); the pinned interpreter version
|
|
31
|
+
lives in [`.python-version`](.python-version) (3.12).
|
|
32
|
+
|
|
33
|
+
### Python extension
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv sync --extra dev
|
|
37
|
+
uv run python -c "import cugrid; print(cugrid.hello()); print(cugrid.cuda_available())"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`uv sync` creates `.venv/`, installs the pinned Python if needed, and builds the pybind11
|
|
41
|
+
extension in editable mode via scikit-build-core. Re-run `uv sync` after touching C++
|
|
42
|
+
sources under `python/cugrid/` or `include/`; plain `uv run` does not rebuild automatically.
|
|
43
|
+
|
|
44
|
+
### C++ unit tests (CPU-only, GoogleTest fetched via CMake)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cmake -S . -B build -DCUGRID_BUILD_PYTHON=OFF
|
|
48
|
+
cmake --build build
|
|
49
|
+
ctest --test-dir build --output-on-failure
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Force CUDA on/off
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
cmake -S . -B build -DCUGRID_ENABLE_CUDA=ON # requires nvcc + CUDA Toolkit on PATH
|
|
56
|
+
cmake -S . -B build -DCUGRID_ENABLE_CUDA=OFF # CPU-only even if a CUDA compiler is present
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Lint / pre-commit
|
|
60
|
+
|
|
61
|
+
Ruff (Python) and clang-format (C++) run via [pre-commit](https://pre-commit.com/):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
uv sync --extra dev
|
|
65
|
+
uv run pre-commit install # runs the hooks on `git commit`
|
|
66
|
+
uv run pre-commit run --all-files
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Reference solutions
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv sync --extra reference
|
|
73
|
+
uv run python scripts/make_reference.py --cases case9 case118 --out tests/cases
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Generates pandapower PF results (`V, θ` per bus; branch flows) that the CPU/GPU solvers
|
|
77
|
+
are validated against (Phase 2+).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstddef>
|
|
4
|
+
#include <utility>
|
|
5
|
+
#include <vector>
|
|
6
|
+
|
|
7
|
+
#include "cugrid/common/error_check.hpp"
|
|
8
|
+
|
|
9
|
+
#ifdef CUGRID_HAVE_CUDA
|
|
10
|
+
#include <cuda_runtime.h>
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
namespace cugrid::common {
|
|
14
|
+
|
|
15
|
+
#ifdef CUGRID_HAVE_CUDA
|
|
16
|
+
|
|
17
|
+
// Owns a device-resident array of `T` allocated with cudaMalloc. Move-only.
|
|
18
|
+
// All device data in cugrid is flat and SoA; this is the single allocation
|
|
19
|
+
// primitive the rest of the codebase builds on.
|
|
20
|
+
template <typename T>
|
|
21
|
+
class DeviceVector {
|
|
22
|
+
public:
|
|
23
|
+
DeviceVector() = default;
|
|
24
|
+
|
|
25
|
+
explicit DeviceVector(std::size_t count) : size_(count) {
|
|
26
|
+
if (size_ > 0) {
|
|
27
|
+
CUGRID_CUDA_CHECK(cudaMalloc(&data_, size_ * sizeof(T)));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
DeviceVector(const T* host_src, std::size_t count) : DeviceVector(count) { copy_from_host(host_src, count); }
|
|
32
|
+
|
|
33
|
+
~DeviceVector() { reset(); }
|
|
34
|
+
|
|
35
|
+
DeviceVector(const DeviceVector&) = delete;
|
|
36
|
+
DeviceVector& operator=(const DeviceVector&) = delete;
|
|
37
|
+
|
|
38
|
+
DeviceVector(DeviceVector&& other) noexcept : data_(other.data_), size_(other.size_) {
|
|
39
|
+
other.data_ = nullptr;
|
|
40
|
+
other.size_ = 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
DeviceVector& operator=(DeviceVector&& other) noexcept {
|
|
44
|
+
if (this != &other) {
|
|
45
|
+
reset();
|
|
46
|
+
data_ = other.data_;
|
|
47
|
+
size_ = other.size_;
|
|
48
|
+
other.data_ = nullptr;
|
|
49
|
+
other.size_ = 0;
|
|
50
|
+
}
|
|
51
|
+
return *this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void copy_from_host(const T* host_src, std::size_t count) {
|
|
55
|
+
CUGRID_CUDA_CHECK(cudaMemcpy(data_, host_src, count * sizeof(T), cudaMemcpyHostToDevice));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void copy_to_host(T* host_dst, std::size_t count) const {
|
|
59
|
+
CUGRID_CUDA_CHECK(cudaMemcpy(host_dst, data_, count * sizeof(T), cudaMemcpyDeviceToHost));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
std::vector<T> to_host() const {
|
|
63
|
+
std::vector<T> out(size_);
|
|
64
|
+
if (size_ > 0) {
|
|
65
|
+
copy_to_host(out.data(), size_);
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void zero() {
|
|
71
|
+
if (size_ > 0) {
|
|
72
|
+
CUGRID_CUDA_CHECK(cudaMemset(data_, 0, size_ * sizeof(T)));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
[[nodiscard]] T* data() noexcept { return data_; }
|
|
77
|
+
[[nodiscard]] const T* data() const noexcept { return data_; }
|
|
78
|
+
[[nodiscard]] std::size_t size() const noexcept { return size_; }
|
|
79
|
+
[[nodiscard]] bool empty() const noexcept { return size_ == 0; }
|
|
80
|
+
|
|
81
|
+
private:
|
|
82
|
+
void reset() {
|
|
83
|
+
if (data_ != nullptr) {
|
|
84
|
+
cudaFree(data_); // noexcept context: log-and-ignore rather than throw from a destructor
|
|
85
|
+
data_ = nullptr;
|
|
86
|
+
}
|
|
87
|
+
size_ = 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
T* data_ = nullptr;
|
|
91
|
+
std::size_t size_ = 0;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
#endif // CUGRID_HAVE_CUDA
|
|
95
|
+
|
|
96
|
+
} // namespace cugrid::common
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <stdexcept>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
#ifdef CUGRID_HAVE_CUDA
|
|
7
|
+
#include <cuda_runtime.h>
|
|
8
|
+
|
|
9
|
+
namespace cugrid::common {
|
|
10
|
+
|
|
11
|
+
class CudaError : public std::runtime_error {
|
|
12
|
+
public:
|
|
13
|
+
CudaError(cudaError_t code, const char* expr, const char* file, int line)
|
|
14
|
+
: std::runtime_error(build_message(code, expr, file, line)), code_(code) {}
|
|
15
|
+
|
|
16
|
+
[[nodiscard]] cudaError_t code() const noexcept { return code_; }
|
|
17
|
+
|
|
18
|
+
private:
|
|
19
|
+
static std::string build_message(cudaError_t code, const char* expr, const char* file, int line) {
|
|
20
|
+
return std::string("CUDA error: ") + cudaGetErrorString(code) + " (" + std::to_string(static_cast<int>(code)) +
|
|
21
|
+
") at " + file + ":" + std::to_string(line) + " in `" + expr + "`";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
cudaError_t code_;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
inline void check_cuda(cudaError_t code, const char* expr, const char* file, int line) {
|
|
28
|
+
if (code != cudaSuccess) {
|
|
29
|
+
throw CudaError(code, expr, file, line);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
} // namespace cugrid::common
|
|
34
|
+
|
|
35
|
+
// Wraps a CUDA runtime call, throwing cugrid::common::CudaError on failure.
|
|
36
|
+
#define CUGRID_CUDA_CHECK(expr) ::cugrid::common::check_cuda((expr), #expr, __FILE__, __LINE__)
|
|
37
|
+
|
|
38
|
+
#endif // CUGRID_HAVE_CUDA
|