forcingkit 0.11.0__tar.gz → 0.12.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.11.0 → forcingkit-0.12.0}/.github/workflows/tests.yml +15 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/AGENTS.md +6 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/PKG-INFO +6 -2
- {forcingkit-0.11.0 → forcingkit-0.12.0}/README.md +2 -1
- forcingkit-0.12.0/client/README.md +27 -0
- forcingkit-0.12.0/client/pyproject.toml +53 -0
- forcingkit-0.12.0/client/src/forcingkit_client/__init__.py +18 -0
- forcingkit-0.12.0/client/src/forcingkit_client/client.py +512 -0
- forcingkit-0.12.0/client/src/forcingkit_client/schema.py +56 -0
- forcingkit-0.12.0/client/src/forcingkit_client/store.py +73 -0
- forcingkit-0.12.0/client/src/forcingkit_client/trajectory.py +264 -0
- forcingkit-0.12.0/client/tests/conftest.py +73 -0
- forcingkit-0.12.0/client/tests/test_client.py +293 -0
- forcingkit-0.12.0/client/tests/test_schema.py +44 -0
- forcingkit-0.12.0/client/tests/test_trajectory.py +95 -0
- forcingkit-0.12.0/client/uv.lock +944 -0
- forcingkit-0.12.0/docs/source/client.rst +133 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/conf.py +1 -1
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/fetchers.rst +19 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/index.rst +16 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/roadmap.rst +15 -0
- forcingkit-0.12.0/docs/source/waves.rst +124 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/pyproject.toml +7 -1
- forcingkit-0.12.0/service/forcingkit_serve/jobs.py +119 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/service/forcingkit_serve/main.py +308 -70
- forcingkit-0.12.0/service/forcingkit_serve/routers/stores.py +43 -0
- forcingkit-0.12.0/service/forcingkit_serve/stac.py +100 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/dispatcher.py +68 -0
- forcingkit-0.12.0/src/forcingkit/fetchers/nwps.py +517 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/settings.py +11 -0
- forcingkit-0.12.0/tests/integration/test_nwps_nomads.py +83 -0
- forcingkit-0.12.0/tests/unit/test_jobs.py +237 -0
- forcingkit-0.12.0/tests/unit/test_nwps.py +370 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/uv.lock +466 -1
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.agents/forcingkit.md +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.claude/CLAUDE.md +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.env.template +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.github/copilot-instructions.md +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.github/workflows/docs.yml +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.github/workflows/publish.yml +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.gitignore +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.markdownlint.yaml +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.pre-commit-config.yaml +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/.python-version +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/CONTRIBUTING.md +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/Dockerfile +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/LICENSE +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docker-compose.yml +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/Makefile +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/make.bat +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/requirements-docs.txt +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/_extra/CNAME +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/_static/custom.css +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/_static/forcingkit-noreaster-wind.gif +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/atmospheric_forcing.rst +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/hycom.rst +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/layered_mesh.rst +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/mesh_frames.rst +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/necofs.rst +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/nyofs.rst +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/rivers.rst +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/docs/source/sources.rst +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/main.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/service/__init__.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/service/forcingkit_serve/__init__.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/service/forcingkit_serve/routers/bathymetry.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/service/forcingkit_serve/routers/plotly_api.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/service/forcingkit_serve/routers/removed.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/service/forcingkit_serve/routers/viewer.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/service/run_server.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/__init__.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/data/rivers/northeast_us.json +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/coastwatch.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/dbofs.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/erddap.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/hrrr.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/hrrr_atmosphere.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/hrrr_levels.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/hrrr_points.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/hycom.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/hydrography.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/ndbc.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/necofs.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/noaa.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/nyofs.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/usgs.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/fetchers/usgs_rivers.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/layered_mesh.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/mesh_frames.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/ramps/currents_lis.csv +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/ramps/currents_lis_dark.csv +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/ramps/currents_lis_deep.csv +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/ramps/currents_lis_haline.csv +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/render.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/src/forcingkit/zarr_stream.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/static/app.js +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/static/favicon.ico +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/static/favicon.svg +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/static/index.html +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/static/logo.svg +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/static/preview3d.js +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/static/styles.css +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/__init__.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/integration/test_auth.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/integration/test_erddap_fetch.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/integration/test_mesh_frames_necofs.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/integration/test_necofs_volume_live.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/integration/test_nyofs_obc_fetch.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/integration/test_obc_offshore_nj.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/integration/test_ofs_archive_paths.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/integration/test_s3_roms_fetchers.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/scripts/inspect_dem.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/scripts/inspect_grib.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/scripts/inspect_zarr.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_coastwatch.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_dbofs.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_dispatcher.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_erddap.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_hrrr_atm.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_hrrr_idx.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_hrrr_levels.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_hrrr_points.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_hycom.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_layered_mesh.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_main.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_mesh_frames.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_ndbc.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_necofs_parent.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_necofs_volume.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_noaa_currents.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_noaa_datum.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_nyofs.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_obc_donor_report.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_obc_pipeline.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_removed_routes.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_settings.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_usgs.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_usgs_rivers.py +0 -0
- {forcingkit-0.11.0 → forcingkit-0.12.0}/tests/unit/test_zarr_stream.py +0 -0
|
@@ -33,3 +33,18 @@ jobs:
|
|
|
33
33
|
# Run them locally or via workflow_dispatch.
|
|
34
34
|
- name: Unit tests
|
|
35
35
|
run: uv run python -m pytest tests/unit -m "not integration" --durations=10
|
|
36
|
+
|
|
37
|
+
client:
|
|
38
|
+
name: Client tests
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@v4
|
|
42
|
+
|
|
43
|
+
- name: Install uv
|
|
44
|
+
uses: astral-sh/setup-uv@v5
|
|
45
|
+
with:
|
|
46
|
+
enable-cache: true
|
|
47
|
+
|
|
48
|
+
# Its own environment, so a client import of anything from the service stack fails here.
|
|
49
|
+
- name: Client tests
|
|
50
|
+
run: uv run --project client python -m pytest client/tests --durations=10
|
|
@@ -37,6 +37,12 @@ observations for validation.
|
|
|
37
37
|
Use `python -m pytest` rather than the bare `pytest` entry point: the latter can
|
|
38
38
|
resolve to a system interpreter outside the venv. `tests/conftest.py` puts
|
|
39
39
|
`src/` and `service/` on `sys.path`, so no `PYTHONPATH` export is needed.
|
|
40
|
+
- **Client**: `client/` is a second distribution, `forcingkit-client` (import
|
|
41
|
+
`forcingkit_client`), with its own `pyproject.toml` and lock. It talks to the service over
|
|
42
|
+
HTTP only and must never import `forcingkit` or take on the service's dependencies. Test it
|
|
43
|
+
with `uv run --project client python -m pytest client/tests`. A change to the job routes
|
|
44
|
+
(`/api/v1/jobs`), the store routes (`/api/v1/stores`) or the STAC Item shape
|
|
45
|
+
(`service/forcingkit_serve/stac.py`) needs the client and its tests updated with it.
|
|
40
46
|
- **Commits**: Conventional commits (`feat:`, `fix:`, `docs:`, `chore:`).
|
|
41
47
|
Author and committer are always the human contributor (`dfry-lhzn <dfry@lhzn.io>`,
|
|
42
48
|
from `git config`), so the log shows who was behind each change. No `Co-Authored-By:`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: forcingkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: Spatiotemporal forcing for computational Earth-system models: selects, regrids and serves model-ready time series with provenance.
|
|
5
5
|
Project-URL: Documentation, https://forcingkit.docs.lhzn.io
|
|
6
6
|
Project-URL: Issues, https://github.com/lhzn-io/forcingkit/issues
|
|
@@ -220,6 +220,7 @@ Requires-Dist: cfgrib>=0.9.15.1
|
|
|
220
220
|
Requires-Dist: cftime>=1.6.5
|
|
221
221
|
Requires-Dist: dask[array]>=2026.3.0
|
|
222
222
|
Requires-Dist: eccodes>=2.46.0
|
|
223
|
+
Requires-Dist: eccodeslib>=2.46.0
|
|
223
224
|
Requires-Dist: fastapi>=0.135.1
|
|
224
225
|
Requires-Dist: h5netcdf>=1.8.1
|
|
225
226
|
Requires-Dist: h5py>=3.16.0
|
|
@@ -238,6 +239,8 @@ Requires-Dist: utide>=0.3.1
|
|
|
238
239
|
Requires-Dist: uvicorn>=0.41.0
|
|
239
240
|
Requires-Dist: xarray>=2026.2.0
|
|
240
241
|
Requires-Dist: zarr>=3.1.5
|
|
242
|
+
Provides-Extra: gcs
|
|
243
|
+
Requires-Dist: gcsfs>=2026.2.0; extra == 'gcs'
|
|
241
244
|
Description-Content-Type: text/markdown
|
|
242
245
|
|
|
243
246
|
# <img src="https://raw.githubusercontent.com/lhzn-io/forcingkit/main/static/logo.svg" height="36" valign="middle" alt="Logo" /> forcingkit
|
|
@@ -254,7 +257,7 @@ Description-Content-Type: text/markdown
|
|
|
254
257
|
|
|
255
258
|
Spatiotemporal forcing for computational Earth-system models: selects, regrids and serves model-ready time series with provenance.
|
|
256
259
|
|
|
257
|
-
`forcingkit` sits between operational and archive data providers (NOAA HRRR, NECOFS, NOAA OFS, HYCOM, NDBC, CO-OPS) and model codes such as `Oceananigans.jl`. It delivers a model's parent ocean on true z levels (`/api/v1/obc`)
|
|
260
|
+
`forcingkit` sits between operational and archive data providers (NOAA HRRR, NWPS, NECOFS, NOAA OFS, HYCOM, NDBC, CO-OPS) and model codes such as `Oceananigans.jl`. It delivers a model's parent ocean on true z levels (`/api/v1/obc`), its HRRR surface atmosphere on a regular grid (`/api/v1/atmosphere`) and NWPS surface waves on the forecast office's own grid (`/api/v1/waves`), plus station observations for validation, as schema-versioned Zarr stores that record their sources.
|
|
258
261
|
|
|
259
262
|
Documentation: <https://forcingkit.docs.lhzn.io>.
|
|
260
263
|
|
|
@@ -276,6 +279,7 @@ The primary goal of forcingkit is to provide clean, standardized ocean and atmos
|
|
|
276
279
|
|
|
277
280
|
- **Oceanic Forcing**: NYOFS, NECOFS, DBOFS, HYCOM (global, 1994 to present).
|
|
278
281
|
- **Atmospheric Forcing**: HRRR (3 km, hourly, 2014-07-30 to present), regridded to 0.03 degrees.
|
|
282
|
+
- **Surface Waves**: NWPS (one forecast office's grid per request, for example OKX CG1 at about 1.8 km, hourly, not regridded). NOMADS keeps about five days; older windows need an NWPS archive (`FORCINGKIT_NWPS_ARCHIVE`).
|
|
279
283
|
|
|
280
284
|
## Roadmap
|
|
281
285
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
Spatiotemporal forcing for computational Earth-system models: selects, regrids and serves model-ready time series with provenance.
|
|
14
14
|
|
|
15
|
-
`forcingkit` sits between operational and archive data providers (NOAA HRRR, NECOFS, NOAA OFS, HYCOM, NDBC, CO-OPS) and model codes such as `Oceananigans.jl`. It delivers a model's parent ocean on true z levels (`/api/v1/obc`)
|
|
15
|
+
`forcingkit` sits between operational and archive data providers (NOAA HRRR, NWPS, NECOFS, NOAA OFS, HYCOM, NDBC, CO-OPS) and model codes such as `Oceananigans.jl`. It delivers a model's parent ocean on true z levels (`/api/v1/obc`), its HRRR surface atmosphere on a regular grid (`/api/v1/atmosphere`) and NWPS surface waves on the forecast office's own grid (`/api/v1/waves`), plus station observations for validation, as schema-versioned Zarr stores that record their sources.
|
|
16
16
|
|
|
17
17
|
Documentation: <https://forcingkit.docs.lhzn.io>.
|
|
18
18
|
|
|
@@ -34,6 +34,7 @@ The primary goal of forcingkit is to provide clean, standardized ocean and atmos
|
|
|
34
34
|
|
|
35
35
|
- **Oceanic Forcing**: NYOFS, NECOFS, DBOFS, HYCOM (global, 1994 to present).
|
|
36
36
|
- **Atmospheric Forcing**: HRRR (3 km, hourly, 2014-07-30 to present), regridded to 0.03 degrees.
|
|
37
|
+
- **Surface Waves**: NWPS (one forecast office's grid per request, for example OKX CG1 at about 1.8 km, hourly, not regridded). NOMADS keeps about five days; older windows need an NWPS archive (`FORCINGKIT_NWPS_ARCHIVE`).
|
|
37
38
|
|
|
38
39
|
## Roadmap
|
|
39
40
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# forcingkit-client
|
|
2
|
+
|
|
3
|
+
A lightweight Python client for a [forcingkit](https://github.com/lhzn-io/forcingkit) service. It
|
|
4
|
+
returns ocean, atmosphere and station forcing as xarray objects, and interpolates forcing along a
|
|
5
|
+
trajectory (a tagged animal, a vessel, a drifter). It depends on `httpx`, `numpy`, `pandas`,
|
|
6
|
+
`scipy`, `xarray` and `zarr` only, none of the service's data-access stack, so it installs into a
|
|
7
|
+
simulator or an application's embedded Python (3.11 or newer).
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from forcingkit_client import Client
|
|
11
|
+
|
|
12
|
+
fk = Client("http://localhost:9598")
|
|
13
|
+
box = (-70.9, 41.3, -70.6, 41.6) # west, south, east, north
|
|
14
|
+
ocean = fk.ocean(box, "2025-07-01", 24) # u, v, temp, salt, zeta
|
|
15
|
+
air = fk.atmosphere(box, "2025-07-01", 24) # HRRR u10, v10, t2m, ...
|
|
16
|
+
|
|
17
|
+
# lon, lat, time, z: one value per point; z in metres, positive up
|
|
18
|
+
track = fk.interpolate_trajectory(
|
|
19
|
+
lon, lat, time, z, variables=("u", "v"), atmosphere=("u10", "v10")
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Store builds run as jobs on the service; the client polls them and opens the finished store over
|
|
24
|
+
HTTP through a local cache (`~/.cache/forcingkit-client`), so only the chunks read are fetched,
|
|
25
|
+
once. `AsyncClient` has the same methods for callers that must not block an event loop.
|
|
26
|
+
|
|
27
|
+
Units are SI; z is metres, positive up. Documentation: <https://forcingkit.docs.lhzn.io/client.html>.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "forcingkit-client"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Lightweight Python client for a forcingkit service: ocean, atmosphere and station forcing as xarray, and forcing interpolated along a trajectory."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Daniel Fry", email = "dfry@lhzn.io" },
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Atmospheric Science",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Hydrology",
|
|
21
|
+
]
|
|
22
|
+
# Deliberately small: nothing from the service's stack (GRIB, OPeNDAP, S3), so the client
|
|
23
|
+
# installs into a simulator or an application's embedded Python.
|
|
24
|
+
dependencies = [
|
|
25
|
+
"httpx>=0.27",
|
|
26
|
+
"numpy>=1.26",
|
|
27
|
+
"pandas>=2.2",
|
|
28
|
+
"scipy>=1.11",
|
|
29
|
+
"xarray>=2024.10",
|
|
30
|
+
"zarr>=3.0.8",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Documentation = "https://forcingkit.docs.lhzn.io/client.html"
|
|
35
|
+
Issues = "https://github.com/lhzn-io/forcingkit/issues"
|
|
36
|
+
Source = "https://github.com/lhzn-io/forcingkit"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/forcingkit_client"]
|
|
40
|
+
|
|
41
|
+
[tool.ruff.lint]
|
|
42
|
+
select = ["E4", "E7", "E9", "F"]
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
pythonpath = ["src"]
|
|
46
|
+
asyncio_mode = "auto"
|
|
47
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
48
|
+
|
|
49
|
+
[dependency-groups]
|
|
50
|
+
dev = [
|
|
51
|
+
"pytest>=9.0.2",
|
|
52
|
+
"pytest-asyncio>=1.3.0",
|
|
53
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Lightweight client for a forcingkit service."""
|
|
2
|
+
|
|
3
|
+
from .client import AsyncClient, Client, ForcingkitError
|
|
4
|
+
from .schema import SchemaError, normalise
|
|
5
|
+
from .trajectory import interpolate_points, interpolate_trajectory, trajectory_window
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AsyncClient",
|
|
9
|
+
"Client",
|
|
10
|
+
"ForcingkitError",
|
|
11
|
+
"SchemaError",
|
|
12
|
+
"interpolate_points",
|
|
13
|
+
"interpolate_trajectory",
|
|
14
|
+
"normalise",
|
|
15
|
+
"trajectory_window",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
__version__ = "0.1.0"
|