forcingkit 0.1.0__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.
- forcingkit-0.1.0/.agents/forcingkit.md +98 -0
- forcingkit-0.1.0/.claude/CLAUDE.md +10 -0
- forcingkit-0.1.0/.env.template +17 -0
- forcingkit-0.1.0/.github/copilot-instructions.md +16 -0
- forcingkit-0.1.0/.github/workflows/docs.yml +49 -0
- forcingkit-0.1.0/.github/workflows/publish.yml +51 -0
- forcingkit-0.1.0/.github/workflows/tests.yml +35 -0
- forcingkit-0.1.0/.gitignore +19 -0
- forcingkit-0.1.0/.markdownlint.yaml +5 -0
- forcingkit-0.1.0/.pre-commit-config.yaml +48 -0
- forcingkit-0.1.0/.python-version +1 -0
- forcingkit-0.1.0/AGENTS.md +71 -0
- forcingkit-0.1.0/CONTRIBUTING.md +30 -0
- forcingkit-0.1.0/Dockerfile +48 -0
- forcingkit-0.1.0/LICENSE +201 -0
- forcingkit-0.1.0/PKG-INFO +329 -0
- forcingkit-0.1.0/README.md +88 -0
- forcingkit-0.1.0/docker-compose.yml +10 -0
- forcingkit-0.1.0/docs/Makefile +20 -0
- forcingkit-0.1.0/docs/make.bat +35 -0
- forcingkit-0.1.0/docs/requirements-docs.txt +3 -0
- forcingkit-0.1.0/docs/source/_extra/CNAME +1 -0
- forcingkit-0.1.0/docs/source/_static/forcingkit-inventory-screenshot.png +0 -0
- forcingkit-0.1.0/docs/source/atmospheric_forcing.rst +196 -0
- forcingkit-0.1.0/docs/source/conf.py +30 -0
- forcingkit-0.1.0/docs/source/fetchers.rst +29 -0
- forcingkit-0.1.0/docs/source/index.rst +16 -0
- forcingkit-0.1.0/docs/source/nyofs.rst +188 -0
- forcingkit-0.1.0/docs/source/removed_endpoints.rst +44 -0
- forcingkit-0.1.0/main.py +6 -0
- forcingkit-0.1.0/pyproject.toml +89 -0
- forcingkit-0.1.0/service/__init__.py +0 -0
- forcingkit-0.1.0/service/forcingkit_serve/__init__.py +0 -0
- forcingkit-0.1.0/service/forcingkit_serve/main.py +544 -0
- forcingkit-0.1.0/service/forcingkit_serve/routers/bathymetry.py +241 -0
- forcingkit-0.1.0/service/forcingkit_serve/routers/plotly_api.py +150 -0
- forcingkit-0.1.0/service/forcingkit_serve/routers/removed.py +86 -0
- forcingkit-0.1.0/service/forcingkit_serve/routers/viewer.py +1199 -0
- forcingkit-0.1.0/service/run_server.py +36 -0
- forcingkit-0.1.0/src/forcingkit/__init__.py +0 -0
- forcingkit-0.1.0/src/forcingkit/dispatcher.py +448 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/dbofs.py +442 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/erddap.py +142 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/hrrr.py +72 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/hrrr_atmosphere.py +289 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/hycom.py +159 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/hydrography.py +117 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/ndbc.py +231 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/necofs.py +369 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/noaa.py +87 -0
- forcingkit-0.1.0/src/forcingkit/fetchers/nyofs.py +458 -0
- forcingkit-0.1.0/src/forcingkit/settings.py +72 -0
- forcingkit-0.1.0/src/forcingkit/zarr_stream.py +146 -0
- forcingkit-0.1.0/static/app.js +590 -0
- forcingkit-0.1.0/static/favicon.ico +0 -0
- forcingkit-0.1.0/static/favicon.svg +258 -0
- forcingkit-0.1.0/static/index.html +142 -0
- forcingkit-0.1.0/static/logo.svg +29 -0
- forcingkit-0.1.0/static/preview3d.js +272 -0
- forcingkit-0.1.0/static/styles.css +701 -0
- forcingkit-0.1.0/tests/__init__.py +0 -0
- forcingkit-0.1.0/tests/integration/test_auth.py +24 -0
- forcingkit-0.1.0/tests/integration/test_erddap_fetch.py +16 -0
- forcingkit-0.1.0/tests/integration/test_nyofs_obc_fetch.py +36 -0
- forcingkit-0.1.0/tests/integration/test_obc_mab.py +47 -0
- forcingkit-0.1.0/tests/integration/test_s3_roms_fetchers.py +87 -0
- forcingkit-0.1.0/tests/scripts/inspect_dem.py +11 -0
- forcingkit-0.1.0/tests/scripts/inspect_grib.py +11 -0
- forcingkit-0.1.0/tests/scripts/inspect_zarr.py +6 -0
- forcingkit-0.1.0/tests/unit/test_dbofs.py +280 -0
- forcingkit-0.1.0/tests/unit/test_dispatcher.py +40 -0
- forcingkit-0.1.0/tests/unit/test_hrrr_atm.py +153 -0
- forcingkit-0.1.0/tests/unit/test_hrrr_idx.py +72 -0
- forcingkit-0.1.0/tests/unit/test_hycom.py +32 -0
- forcingkit-0.1.0/tests/unit/test_main.py +17 -0
- forcingkit-0.1.0/tests/unit/test_ndbc.py +97 -0
- forcingkit-0.1.0/tests/unit/test_necofs_parent.py +140 -0
- forcingkit-0.1.0/tests/unit/test_nyofs.py +294 -0
- forcingkit-0.1.0/tests/unit/test_obc_pipeline.py +249 -0
- forcingkit-0.1.0/tests/unit/test_removed_routes.py +32 -0
- forcingkit-0.1.0/tests/unit/test_settings.py +79 -0
- forcingkit-0.1.0/tests/unit/test_zarr_stream.py +170 -0
- forcingkit-0.1.0/uv.lock +3269 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# forcingkit Reference
|
|
2
|
+
|
|
3
|
+
## Mission
|
|
4
|
+
|
|
5
|
+
Python microservice providing real-time and historical forcing (the parent ocean and
|
|
6
|
+
the HRRR atmosphere) and validation observations to the `coastal-sim` Julia physics
|
|
7
|
+
engine.
|
|
8
|
+
|
|
9
|
+
## Environment
|
|
10
|
+
|
|
11
|
+
- **Python 3.11+** via `uv`. Always use `uv run` — never `pip install`.
|
|
12
|
+
- **FastAPI** microservice in `service/forcingkit_serve/`.
|
|
13
|
+
- Tests run with `uv run pytest`.
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
### Core Pipeline
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
HTTP Request → service/forcingkit_serve/main.py
|
|
21
|
+
→ src/forcingkit/dispatcher.py
|
|
22
|
+
→ fetchers/*.py
|
|
23
|
+
→ Zarr (~/.cache/forcingkit/)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **Entry point**: `service/forcingkit_serve/main.py` — FastAPI application.
|
|
27
|
+
- **Dispatcher**: `src/forcingkit/dispatcher.py` — interprets
|
|
28
|
+
requests and routes to the appropriate fetcher via tiered fallback.
|
|
29
|
+
Uses `_rank_obc_candidates(bbox)` to select the best parent by
|
|
30
|
+
resolution and domain overlap.
|
|
31
|
+
- **Fetchers**: `src/forcingkit/fetchers/` — isolated modules per data source.
|
|
32
|
+
|
|
33
|
+
## Fetcher Tier (Parent Ocean Priority Order)
|
|
34
|
+
|
|
35
|
+
| Fetcher | Resolution | Domain | Notes |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| `nyofs.py` | ~100 m | NY/NJ Harbor | POM curvilinear grid, C-grid stagger |
|
|
38
|
+
| `necofs.py` | ~200 m | New England | FVCOM unstructured |
|
|
39
|
+
| `hycom.py` | ~9 km | Global | Final fallback (Operational + Historical) |
|
|
40
|
+
|
|
41
|
+
*Note: Because the architecture is modular, a future roadmap item includes the integration of TPXO (via `tpxo.py`) given usage rights.*
|
|
42
|
+
|
|
43
|
+
Atmospheric forcing: HRRR from 2014-07-30 (`hrrr_atmosphere.py`, `/api/v1/atmosphere`).
|
|
44
|
+
Earlier runs use ERA5 through NumericalEarth in coastal-sim, not this service.
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Unit tests (no network I/O, fast)
|
|
50
|
+
uv run pytest tests/unit/ -v
|
|
51
|
+
|
|
52
|
+
# Single test file
|
|
53
|
+
uv run pytest tests/unit/test_nyofs.py -v
|
|
54
|
+
|
|
55
|
+
# Integration tests (live APIs, slower)
|
|
56
|
+
uv run pytest tests/integration/ -v
|
|
57
|
+
|
|
58
|
+
# Pre-commit checks (ruff + mypy) on modified files
|
|
59
|
+
uv run pre-commit run --files <file1> <file2> ...
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Commands
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Sync dependencies
|
|
66
|
+
uv sync
|
|
67
|
+
|
|
68
|
+
# Start data service
|
|
69
|
+
uv run python service/run_server.py
|
|
70
|
+
|
|
71
|
+
# Run one-off fetch
|
|
72
|
+
uv run python -c "from forcingkit.fetchers import nyofs; print(nyofs.get_metadata())"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Gotchas
|
|
76
|
+
|
|
77
|
+
- **Telemetry performance**: Use `.csvp` endpoints for ERDDAP
|
|
78
|
+
tabular time-series to avoid heavy NetCDF binary parsing overhead.
|
|
79
|
+
- **OPeNDAP engine**: Always pass `engine="pydap"` to `xr.open_dataset` for THREDDS
|
|
80
|
+
endpoints. The default netcdf4 engine fails on remote DAP URLs.
|
|
81
|
+
- **NYOFS FMRC vs NCEI tiering**: FMRC aggregation covers rolling 7-day window
|
|
82
|
+
(< 31 days). Older data requires per-hour NCEI file enumeration with naming
|
|
83
|
+
conventions that changed on 2024-09-09.
|
|
84
|
+
- **C-grid interpolation**: NYOFS u/v are on Arakawa C-grid face
|
|
85
|
+
points. After `.where(mask, drop=True)`, the subset is already
|
|
86
|
+
zero-indexed — use the `_c_grid_to_rho()` helper that averages
|
|
87
|
+
adjacent cells on the subset array, not the old absolute-index
|
|
88
|
+
approach.
|
|
89
|
+
- **Mocking strategy**: Dispatcher unit tests check tuple/dict return
|
|
90
|
+
boundaries carefully. When modifying mocked fetchers, track
|
|
91
|
+
keyword-argument vs positional argument boundaries.
|
|
92
|
+
- **Grid normalization**: `coastal-sim` expects elevations positive
|
|
93
|
+
up (LMSL/NAVD88). Normalize any inverted datasets in the fetcher
|
|
94
|
+
tier before the dispatcher sees them.
|
|
95
|
+
- **Cache keys**: Cache Zarr keys are deterministic hashes of bbox
|
|
96
|
+
- time window. Changing fetcher output schema (variable names, dims)
|
|
97
|
+
will miss existing cache entries — purge
|
|
98
|
+
`~/.cache/forcingkit/` when making breaking schema changes.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# forcingkit environment variables
|
|
2
|
+
# Copy this file to .env and fill in your credentials.
|
|
3
|
+
# This file is loaded by coastal-sim/docker-compose.yml via env_file.
|
|
4
|
+
|
|
5
|
+
# No credentials are required at present: HRRR, NOAA OFS, NECOFS, HYCOM, NDBC and
|
|
6
|
+
# CO-OPS are all served without authentication.
|
|
7
|
+
|
|
8
|
+
# Cache root for every store (default ~/.cache/forcingkit). The old names
|
|
9
|
+
# ECODATA_CACHE_CACHE_DIR and COASTAL_SIM_DATA_CACHE_DIR are still read, with a
|
|
10
|
+
# warning, until the next release.
|
|
11
|
+
# FORCINGKIT_CACHE_DIR=~/.cache/forcingkit
|
|
12
|
+
|
|
13
|
+
# Threads for parallel OPeNDAP reads (default 4; was ECODATA_CACHE_MAX_WORKERS).
|
|
14
|
+
# FORCINGKIT_MAX_WORKERS=4
|
|
15
|
+
|
|
16
|
+
# The topobathykit elevation service (default http://localhost:9595; was TOPOBATHYSIM_URL).
|
|
17
|
+
# TOPOBATHYKIT_URL=http://localhost:9595
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# forcingkit — Copilot Compatibility
|
|
2
|
+
|
|
3
|
+
This file exists for tools that specifically load `.github/copilot-instructions.md`.
|
|
4
|
+
|
|
5
|
+
## Canonical Guidance
|
|
6
|
+
|
|
7
|
+
- Treat `/AGENTS.md` as the primary instruction entry point.
|
|
8
|
+
- Treat `/.agents/` as the vendor-neutral location for deeper project guidance.
|
|
9
|
+
|
|
10
|
+
## Copilot-Specific Compatibility Notes
|
|
11
|
+
|
|
12
|
+
- Always use `uv run` for Python commands and `uv run pytest` for tests.
|
|
13
|
+
- Run `uv run pre-commit run --files <modified-files>` before staging any changes.
|
|
14
|
+
- Never commit without explicit user approval: present the change set and commit
|
|
15
|
+
message first, then wait for positive confirmation.
|
|
16
|
+
- Stage files explicitly (`git add <file>`). Never `git add .`.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Deploy Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "src/forcingkit/**"
|
|
9
|
+
- ".github/workflows/docs.yml"
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pages: write
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: pages
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
|
|
31
|
+
- name: Install Sphinx dependencies
|
|
32
|
+
run: pip install -r docs/requirements-docs.txt
|
|
33
|
+
|
|
34
|
+
- name: Build HTML docs
|
|
35
|
+
run: sphinx-build -W --keep-going -b html docs/source docs/build/html
|
|
36
|
+
|
|
37
|
+
- uses: actions/upload-pages-artifact@v3
|
|
38
|
+
with:
|
|
39
|
+
path: docs/build/html
|
|
40
|
+
|
|
41
|
+
deploy:
|
|
42
|
+
needs: build
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
environment:
|
|
45
|
+
name: github-pages
|
|
46
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
47
|
+
steps:
|
|
48
|
+
- id: deployment
|
|
49
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Pushing a tag vX.Y.Z builds the sdist and wheel and publishes them to PyPI through trusted
|
|
4
|
+
# publishing (OIDC): no API token is stored anywhere. PyPI must list this repository, this
|
|
5
|
+
# workflow file and the `pypi` environment as a trusted publisher for forcingkit.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ["v*"]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- uses: astral-sh/setup-uv@v6
|
|
17
|
+
|
|
18
|
+
- name: Check the tag matches the package version
|
|
19
|
+
run: |
|
|
20
|
+
version=$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
|
|
21
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
22
|
+
echo "Tag $GITHUB_REF_NAME does not match pyproject.toml version $version" >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
- name: Build
|
|
27
|
+
run: uv build
|
|
28
|
+
|
|
29
|
+
- name: Check the distributions
|
|
30
|
+
run: uvx twine check --strict dist/*
|
|
31
|
+
|
|
32
|
+
- uses: actions/upload-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: dist
|
|
35
|
+
path: dist/
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
needs: build
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
environment:
|
|
41
|
+
name: pypi
|
|
42
|
+
url: https://pypi.org/p/forcingkit
|
|
43
|
+
permissions:
|
|
44
|
+
id-token: write
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/download-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: dist
|
|
49
|
+
path: dist/
|
|
50
|
+
|
|
51
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
unit:
|
|
11
|
+
name: Unit tests
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v5
|
|
18
|
+
with:
|
|
19
|
+
enable-cache: true
|
|
20
|
+
|
|
21
|
+
- name: Sync dependencies
|
|
22
|
+
run: uv sync --dev
|
|
23
|
+
|
|
24
|
+
# Gating. Runs the pinned hooks from .pre-commit-config.yaml rather than
|
|
25
|
+
# whatever ruff the venv resolves, so CI and a local commit apply the same
|
|
26
|
+
# version and the same rule selection. Grading against ambient-latest is
|
|
27
|
+
# what made the first run of this workflow fail on untouched code.
|
|
28
|
+
- name: Lint
|
|
29
|
+
run: uv run pre-commit run --all-files --show-diff-on-failure
|
|
30
|
+
|
|
31
|
+
# Integration tests are excluded: they call live NOAA, NECOFS and HYCOM
|
|
32
|
+
# endpoints and take several minutes.
|
|
33
|
+
# Run them locally or via workflow_dispatch.
|
|
34
|
+
- name: Unit tests
|
|
35
|
+
run: uv run python -m pytest tests/unit -m "not integration" --durations=10
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
args: ["--maxkb=500000"]
|
|
10
|
+
- id: check-toml
|
|
11
|
+
- id: detect-private-key
|
|
12
|
+
|
|
13
|
+
# Pinned deliberately. This rev is the authoritative linter version for the
|
|
14
|
+
# repo; CI runs these same hooks so there is one standard everywhere. Bump it
|
|
15
|
+
# as its own reviewed change, never implicitly. The rule selection is pinned
|
|
16
|
+
# alongside it in pyproject.toml [tool.ruff.lint], because ruff's built-in
|
|
17
|
+
# defaults are not stable across versions.
|
|
18
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
19
|
+
rev: v0.16.9
|
|
20
|
+
hooks:
|
|
21
|
+
- id: ruff-check
|
|
22
|
+
args: [--fix]
|
|
23
|
+
- id: ruff-format
|
|
24
|
+
|
|
25
|
+
- repo: local
|
|
26
|
+
hooks:
|
|
27
|
+
- id: mypy
|
|
28
|
+
name: mypy
|
|
29
|
+
entry: mypy
|
|
30
|
+
language: system
|
|
31
|
+
types: [python]
|
|
32
|
+
require_serial: true
|
|
33
|
+
args: ["--ignore-missing-imports", "--explicit-package-bases"]
|
|
34
|
+
|
|
35
|
+
- repo: https://github.com/igorshubovych/markdownlint-cli
|
|
36
|
+
rev: v0.39.0
|
|
37
|
+
hooks:
|
|
38
|
+
- id: markdownlint
|
|
39
|
+
language_version: 20.10.0
|
|
40
|
+
args: ["--fix"]
|
|
41
|
+
|
|
42
|
+
- repo: local
|
|
43
|
+
hooks:
|
|
44
|
+
- id: block-cds-secrets
|
|
45
|
+
name: Block Copernicus Secrets
|
|
46
|
+
entry: 'url: https://cds.climate.copernicus.eu'
|
|
47
|
+
language: pygrep
|
|
48
|
+
files: \.jl$|\.py$|\.env$|\.cdsapirc$
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# forcingkit Agent Guide
|
|
2
|
+
|
|
3
|
+
This file is the canonical, vendor-neutral entry point for agent
|
|
4
|
+
guidance in this repository.
|
|
5
|
+
|
|
6
|
+
## Scope
|
|
7
|
+
|
|
8
|
+
- Prefer standards and shared conventions over vendor-specific agent layouts.
|
|
9
|
+
- Treat `AGENTS.md` and `.agents/` as the primary instruction surface.
|
|
10
|
+
- Keep agent-specific files as thin compatibility shims when needed by a tool.
|
|
11
|
+
|
|
12
|
+
## Tool Discovery Map
|
|
13
|
+
|
|
14
|
+
- Canonical source of truth: `AGENTS.md` and `/.agents/`.
|
|
15
|
+
- Claude Code:
|
|
16
|
+
- Compatibility shim: `/.claude/CLAUDE.md`.
|
|
17
|
+
- VS Code GitHub Copilot Agent:
|
|
18
|
+
- Compatibility shim: `/.github/copilot-instructions.md`.
|
|
19
|
+
- Rule for all tool-specific files: redirect to this file and
|
|
20
|
+
`/.agents/`, and only add minimal tool-local behavior.
|
|
21
|
+
|
|
22
|
+
## Repository Focus
|
|
23
|
+
|
|
24
|
+
forcingkit is a Python microservice that fetches, harmonizes, regrids, and serves
|
|
25
|
+
real-time and historical forcing to the `coastal-sim` Julia physics engine: the parent
|
|
26
|
+
ocean (`/api/v1/obc`, schema z-v2), the HRRR atmosphere (`/api/v1/atmosphere`), tides,
|
|
27
|
+
and station telemetry and NDBC observations for validation.
|
|
28
|
+
|
|
29
|
+
## Core Rules
|
|
30
|
+
|
|
31
|
+
- **Dependency management**: Always use `uv run` - never `pip install` directly.
|
|
32
|
+
- **Linting/formatting**: Run `uv run pre-commit run --files <files>` (ruff + mypy)
|
|
33
|
+
before staging any changes.
|
|
34
|
+
- **Testing**: Run tests with `uv run python -m pytest tests/unit/` (unit) or
|
|
35
|
+
`uv run python -m pytest tests/integration/` (live API, roughly 10 minutes).
|
|
36
|
+
Use `python -m pytest` rather than the bare `pytest` entry point: the latter can
|
|
37
|
+
resolve to a system interpreter outside the venv. `tests/conftest.py` puts
|
|
38
|
+
`src/` and `service/` on `sys.path`, so no `PYTHONPATH` export is needed.
|
|
39
|
+
- **Commits**: Conventional commits (`feat:`, `fix:`, `docs:`, `chore:`).
|
|
40
|
+
Author and committer are always the human contributor (`dfry-lhzn <dfry@lhzn.io>`,
|
|
41
|
+
from `git config`), so the log shows who was behind each change. No `Co-Authored-By:`
|
|
42
|
+
trailers or non-human identities: GitHub parses those into the Contributors list,
|
|
43
|
+
which is reserved for people. Noting the agent harness or model that collaborated
|
|
44
|
+
is welcome as plain text in the commit body. Verify with `git var GIT_AUTHOR_IDENT`
|
|
45
|
+
before the first commit in a shell.
|
|
46
|
+
- **Git staging**: Stage files explicitly (`git add <file>`). Never `git add .`.
|
|
47
|
+
- **Never commit without approval**: Present the proposed change set and commit message
|
|
48
|
+
to the user and wait for explicit confirmation before running any `git commit`.
|
|
49
|
+
|
|
50
|
+
## Domain Constraints
|
|
51
|
+
|
|
52
|
+
- **Grid normalization**: Elevations and water levels are positive up. Normalize
|
|
53
|
+
external dataset quirks at the fetcher tier - never let them propagate into the
|
|
54
|
+
dispatcher.
|
|
55
|
+
- **OPeNDAP access**: Use the `pydap` engine (`engine="pydap"`) for all THREDDS/OPeNDAP
|
|
56
|
+
endpoints. Prefer `.csvp` endpoints for tabular time-series to avoid NetCDF overhead.
|
|
57
|
+
- **C-grid stagger**: NYOFS (POM) uses an Arakawa C-grid. Interpolation to rho-points
|
|
58
|
+
must be done in the fetcher before returning data to the dispatcher.
|
|
59
|
+
- **Precision**: Output arrays should default to `float32` (`<f4`) and use Little-Endian
|
|
60
|
+
endianness for compatibility with `Zarr.jl` and `Oceananigans.jl`.
|
|
61
|
+
|
|
62
|
+
## Where To Read Next
|
|
63
|
+
|
|
64
|
+
- `.agents/forcingkit.md`: architecture pipeline, fetcher tier, testing strategy
|
|
65
|
+
and gotchas
|
|
66
|
+
|
|
67
|
+
## Compatibility
|
|
68
|
+
|
|
69
|
+
If a tool only reads `.github/copilot-instructions.md`, `CLAUDE.md`, or another
|
|
70
|
+
vendor-specific file, that file redirects here and adds only the minimum compatibility
|
|
71
|
+
details required by that tool.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Contributing to forcingkit
|
|
2
|
+
|
|
3
|
+
Thank you for considering contributing to `forcingkit`. This repository is a community resource for providing clean, standardized ocean and atmospheric forcing boundaries for high-fidelity coastal hydrodynamic models. Whether you're fixing a bug, improving documentation, or adding a new data node, contributions are welcome.
|
|
4
|
+
|
|
5
|
+
## Development Workflow
|
|
6
|
+
|
|
7
|
+
### 1. Environment Setup
|
|
8
|
+
|
|
9
|
+
We use `uv` for Python package management.
|
|
10
|
+
|
|
11
|
+
1. Install `uv`.
|
|
12
|
+
2. Sync the environment: `uv sync`
|
|
13
|
+
3. We use `pre-commit` to ensure code format standardization. Run `pre-commit install` to set up your git hooks.
|
|
14
|
+
|
|
15
|
+
### 2. Making Changes
|
|
16
|
+
|
|
17
|
+
- All code must pass `ruff` (for formatting and linting) and `mypy` (for typing).
|
|
18
|
+
- Ensure your changes are covered by tests where applicable (we use `pytest`). Tests are located in the `tests/` directory.
|
|
19
|
+
- `forcingkit.fetchers` logic handles API integrations. If adding a new telemetry source (e.g., a new regional IOOS node), follow the patterns established in `ndbc.py` (observations) or `dbofs.py` (a parent ocean).
|
|
20
|
+
- Keep the Sphinx documentation up to date.
|
|
21
|
+
|
|
22
|
+
### 3. Pull Requests
|
|
23
|
+
|
|
24
|
+
1. Create a descriptive branch name: `git checkout -b feature/ioos-integration`
|
|
25
|
+
2. Make your commits clear and logical.
|
|
26
|
+
3. Open a Pull Request.
|
|
27
|
+
|
|
28
|
+
### 4. Code of Conduct
|
|
29
|
+
|
|
30
|
+
We encourage an inclusive, patient, and highly respectful environment for all collaborators.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Tested on: NVIDIA Jetson AGX Orin (JetPack 6.x / l4t r36) and x86_64 (Ubuntu 24.04)
|
|
2
|
+
# Override BASE_IMAGE at build time for your target platform:
|
|
3
|
+
# Jetson Orin: --build-arg BASE_IMAGE=nvcr.io/nvidia/l4t-base:r36.2.0
|
|
4
|
+
# x86_64/WSL2: --build-arg BASE_IMAGE=ubuntu:24.04 (default)
|
|
5
|
+
ARG BASE_IMAGE=ubuntu:24.04
|
|
6
|
+
FROM ${BASE_IMAGE}
|
|
7
|
+
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
|
|
10
|
+
# Install python and dev dependencies
|
|
11
|
+
RUN apt-get update && apt-get install -y \
|
|
12
|
+
python3 \
|
|
13
|
+
python3-pip \
|
|
14
|
+
python3-venv \
|
|
15
|
+
gcc \
|
|
16
|
+
curl \
|
|
17
|
+
libeccodes-dev \
|
|
18
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
19
|
+
|
|
20
|
+
# Alias python to python3
|
|
21
|
+
RUN ln -s /usr/bin/python3 /usr/bin/python
|
|
22
|
+
|
|
23
|
+
# Install UV
|
|
24
|
+
RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_UNMANAGED_INSTALL="/usr/local/bin" sh
|
|
25
|
+
|
|
26
|
+
# Configure environment
|
|
27
|
+
ENV UV_PROJECT_ENVIRONMENT="/app/.venv" \
|
|
28
|
+
PYTHONPATH="/app/src:/app/service"
|
|
29
|
+
|
|
30
|
+
COPY pyproject.toml uv.lock ./
|
|
31
|
+
COPY README.md ./
|
|
32
|
+
COPY src/ /app/src/
|
|
33
|
+
COPY service/ /app/service/
|
|
34
|
+
COPY static/ /app/static/
|
|
35
|
+
|
|
36
|
+
# Downgrade python was done in pyproject.toml.
|
|
37
|
+
# We use --frozen to respect uv.lock
|
|
38
|
+
RUN uv sync --frozen --no-dev --compile-bytecode
|
|
39
|
+
|
|
40
|
+
# Fix ownership so non-root users can write to .venv
|
|
41
|
+
ARG UID=1000
|
|
42
|
+
ARG GID=1000
|
|
43
|
+
RUN chown -R ${UID}:${GID} /app
|
|
44
|
+
|
|
45
|
+
# Default port 9598
|
|
46
|
+
EXPOSE 9598
|
|
47
|
+
|
|
48
|
+
CMD ["uv", "run", "python", "-m", "forcingkit_serve.main", "--host", "0.0.0.0", "--port", "9598"]
|