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
104 changes: 104 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Agent notes for working on GAT

This file is for coding agents (and humans) *contributing to* GAT itself.
If you're looking for how to *use* GAT as a library, see the
[README](README.md) and the docs site instead — this file is repo-internal.

## What this is

GAT (`nlr-gat` on PyPI) is a format-agnostic API over grid-model
simulation outputs (Sienna, PLEXOS, ReEDS), with a thin core and
per-format extras so consumers only install the native-code dependencies
(h5py, duckdb, geopandas, polars, matplotlib) their format actually needs.

## Layout

- `src/gat/` — the package (src layout; import as `gat`)
- `src/gat/scenariohandlers/`, `src/gat/simulations/`, `src/gat/systems/`,
`src/gat/datahelpers/` — package boundaries that use a lazy
`__getattr__` pattern (PEP 562) instead of eager `from .x import *`.
**If you add a new format module here, wire it into the `__getattr__`
dispatch, don't add a blanket import** — that's what keeps
`pip install nlr-gat[reeds]` from pulling in h5py/geopandas/duckdb.
- `docs/source/` — Sphinx docs (MyST markdown + autodoc); built and
deployed to GitHub Pages on push to `main`.
- `tests/` — pytest; regression baselines under `tests/handlers/` use
`pytest-regressions` snapshots.

## Setup

```bash
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
```

`dev` is deliberately self-referential (`nlr-gat[sienna,plexos,reeds,plots]`)
so the full test suite is runnable out of the box — see the comment above
it in `pyproject.toml` (issue #23) for why that matters.

## Running tests

```bash
make test # pytest tests/ -v
```

Some tests need fixture data that isn't checked in:
- **Sienna**: `make sienna-fixture-v4` (needs Docker; see
`docker/sienna/README.md`). Point `GAT_SIENNA_FIXTURE` or use the
default resolution order in `tests/visual/generate_fixture_deck.py` if
working with fixtures interactively.
- **PLEXOS**: set `GAT_PLEXOS_FIXTURE` (H5PLEXOS.jl `.h5` directory) or
`GAT_PLEXOS_ZIP_FIXTURE` (native `Solution.zip`) to point at your own
data. Tests needing fixtures that aren't present skip rather than fail.

After a change that should shift regression baselines intentionally:

```bash
make regen-snapshots
```

## Conventions

- **Formatting/lint**: `black` (line length 88) and a minimal `ruff`
rule set (bug-catching only, not style). CI runs
`black --check src/gat tests` and `ruff check src/gat tests`.
- **Commit messages and PR titles must follow [Conventional
Commits](https://www.conventionalcommits.org/)** (`feat:`, `fix:`,
`docs:`, `refactor:`, `perf:`, `ci:`, `build:`, `test:`, `chore:`,
with `!` or a `BREAKING CHANGE:` footer for breaking changes). This is
enforced (informationally, not yet a required check) by
`.github/workflows/commit-lint.yml`, and it's not just style —
**release-please parses these prefixes to compute version bumps and
generate `docs/source/CHANGELOG.md`.** An unprefixed commit is
invisible to it, not an error.
- **No static version string.** GAT uses `setuptools_scm` — version is
derived from git tags at build time (`dynamic = ["version"]` in
`pyproject.toml`). Never hand-edit a version number anywhere.

## Release process

Releases are automated via [release-please](https://github.com/googleapis/release-please)
(`.github/workflows/release.yml`), modeled on NatLabRockies/R2X:

1. Every push to `main` (i.e., every merged PR) updates a standing
`chore(main): release nlr-gat X.Y.Z` PR with an auto-generated
changelog, computed from Conventional Commits since the last release.
2. Merging that PR creates the git tag + GitHub Release.
3. That triggers build + publish to PyPI via Trusted Publishing (OIDC,
no stored token). The `pypi` GitHub Environment has a required
reviewer gate — publishing pauses for manual approval regardless of
who merged the release PR.

You will not need to run `git tag` or upload to PyPI by hand.

## Known gotchas worth knowing before you dig for them

- `EGRETScenario` was removed entirely (issue #11) — if you find a
reference to EGRET in an old branch or issue, it's gone, not moved.
- Support for PLEXOS `.h5` files (from H5PLEXOS.jl) is deprecated as of
v0.1.1 and will be removed in v0.2.0 — new PLEXOS work should target
the DuckDB-backed engine (`nlr-gat[plexos]`, native `Solution.zip`).
- Path arguments (`PlexosScenario(...)`, etc.) go through
`os.path.expanduser()` before resolution — `~` is expanded, but shell
globs and environment variables written literally in a Python string
are not.
40 changes: 40 additions & 0 deletions docs/source/_llms_root/llms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# GAT (Grid Analysis Toolkit)

> A format-agnostic Python API for reading Production Cost Model (PCM)
> and Capacity Expansion Model (CEM) simulation outputs — Sienna
> (PowerSimulations.jl), PLEXOS, and ReEDS today — into pandas
> DataFrames, without needing to know each format's native file layout.
> Distributed on PyPI as `nlr-gat` (import as `gat`); source at
> https://github.com/NatLabRockies/GridAnalysisToolkit.

GAT's public API is still evolving pre-1.0 — pin an exact version if you
need stability. The base package installs almost nothing; each simulation
format is an opt-in extra so you only pull in the native-code
dependencies (h5py, duckdb, geopandas, polars) that format needs, e.g.
`pip install "nlr-gat[sienna]"` or `pip install "nlr-gat[plexos,plots]"`.

## Getting started

- [Installation and extras table](https://github.com/NatLabRockies/GridAnalysisToolkit#installation): which `pip install "nlr-gat[...]"` extra to use for Sienna, PLEXOS, ReEDS, plotting, server/client, or all of the above
- [Quickstart: scenarios and palettes](scenario_quickstart.html): load a scenario and pull your first dispatch/capacity results
- [Python API: gat.load(...)](python_api_load.html): the top-level entry point for constructing a scenario from a path

## Format-specific guides

- [Sienna scaling and raw dataset access](sienna_scaling.html): base-power scaling rules and how to reach unscaled h5 datasets directly
- [Legacy scenario handler deprecation](legacy_handler_deprecation.html): what changed, what's deprecated (H5PLEXOS.jl `.h5` support, removed in v0.2.0), and migration paths

## Extending GAT

- [Custom plots and the reporting framework](extending_plots.html): the `@plot_function` decorator and how quickplots/reports are assembled
- [v1 architecture & migration pattern](architecture/v1_migration_pattern.html): the `BaseSystem`/`BaseSimulation` interfaces new format backends implement

## Reference

- [Changelog](https://github.com/NatLabRockies/GridAnalysisToolkit/blob/main/docs/source/CHANGELOG.md): version history, generated from Conventional Commits
- [PyPI package](https://pypi.org/project/nlr-gat/)
- [Issue tracker](https://github.com/NatLabRockies/GridAnalysisToolkit/issues)

## Optional

- [Migrating from v0.x to v1.0](migration_v1.html): only relevant if you're upgrading an existing integration
4 changes: 4 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@

html_theme = "sphinx_book_theme"
html_static_path = ["_static"]
# Copied verbatim to the site root (unlike html_static_path, which nests
# under /_static/) so llms.txt is served at the conventional
# https://<docs-site>/llms.txt location — see llmstxt.org.
html_extra_path = ["_llms_root"]

source_suffix = {
".rst": "restructuredtext",
Expand Down
18 changes: 18 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,28 @@ license = {file = "LICENSE"}
authors = [
{name = "Micah Webb", email = "micah.webb@nlr.gov"},
]
keywords = [
"power systems",
"grid analysis",
"production cost model",
"capacity expansion model",
"PCM",
"CEM",
"PLEXOS",
"Sienna",
"PowerSimulations.jl",
"ReEDS",
"energy modeling",
]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering",
]
[project.scripts]
gat="gat.cli:cli"
Expand Down
Loading