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.
Files changed (34) hide show
  1. cugrid-0.0.1.dev1/.clang-format +4 -0
  2. cugrid-0.0.1.dev1/.github/workflows/ci.yml +55 -0
  3. cugrid-0.0.1.dev1/.github/workflows/publish.yml +39 -0
  4. cugrid-0.0.1.dev1/.gitignore +20 -0
  5. cugrid-0.0.1.dev1/.pre-commit-config.yaml +23 -0
  6. cugrid-0.0.1.dev1/.python-version +1 -0
  7. cugrid-0.0.1.dev1/CLAUDE.md +113 -0
  8. cugrid-0.0.1.dev1/CMakeLists.txt +72 -0
  9. cugrid-0.0.1.dev1/PKG-INFO +92 -0
  10. cugrid-0.0.1.dev1/README.md +77 -0
  11. cugrid-0.0.1.dev1/benchmarks/.gitkeep +0 -0
  12. cugrid-0.0.1.dev1/docs/benchmarks/.gitkeep +0 -0
  13. cugrid-0.0.1.dev1/docs/math/.gitkeep +0 -0
  14. cugrid-0.0.1.dev1/include/cugrid/common/device_vector.hpp +96 -0
  15. cugrid-0.0.1.dev1/include/cugrid/common/error_check.hpp +38 -0
  16. cugrid-0.0.1.dev1/include/cugrid/common/timer.hpp +54 -0
  17. cugrid-0.0.1.dev1/plans/cugrid-implementation-plan.md +316 -0
  18. cugrid-0.0.1.dev1/pyproject.toml +28 -0
  19. cugrid-0.0.1.dev1/python/cugrid/__init__.py +4 -0
  20. cugrid-0.0.1.dev1/python/cugrid/_bindings.cpp +27 -0
  21. cugrid-0.0.1.dev1/scripts/make_reference.py +80 -0
  22. cugrid-0.0.1.dev1/src/common/.gitkeep +0 -0
  23. cugrid-0.0.1.dev1/src/io/.gitkeep +0 -0
  24. cugrid-0.0.1.dev1/src/linalg/.gitkeep +0 -0
  25. cugrid-0.0.1.dev1/src/network/.gitkeep +0 -0
  26. cugrid-0.0.1.dev1/src/opf/cpu/.gitkeep +0 -0
  27. cugrid-0.0.1.dev1/src/opf/cuda/.gitkeep +0 -0
  28. cugrid-0.0.1.dev1/src/pf/cpu/.gitkeep +0 -0
  29. cugrid-0.0.1.dev1/src/pf/cuda/.gitkeep +0 -0
  30. cugrid-0.0.1.dev1/tests/cases/.gitkeep +0 -0
  31. cugrid-0.0.1.dev1/tests/unit/CMakeLists.txt +17 -0
  32. cugrid-0.0.1.dev1/tests/unit/test_timer.cpp +28 -0
  33. cugrid-0.0.1.dev1/tests/validation/.gitkeep +0 -0
  34. cugrid-0.0.1.dev1/uv.lock +729 -0
@@ -0,0 +1,4 @@
1
+ BasedOnStyle: Google
2
+ ColumnLimit: 120
3
+ DerivePointerAlignment: false
4
+ PointerAlignment: Left
@@ -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