aquagrid 0.2.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.
Files changed (39) hide show
  1. aquagrid-0.2.0/CHANGELOG.md +33 -0
  2. aquagrid-0.2.0/LICENSE +25 -0
  3. aquagrid-0.2.0/MANIFEST.in +4 -0
  4. aquagrid-0.2.0/PKG-INFO +337 -0
  5. aquagrid-0.2.0/README.md +304 -0
  6. aquagrid-0.2.0/docs/zarr-schema.md +88 -0
  7. aquagrid-0.2.0/examples/config.example.yaml +18 -0
  8. aquagrid-0.2.0/pyproject.toml +55 -0
  9. aquagrid-0.2.0/setup.cfg +4 -0
  10. aquagrid-0.2.0/src/aquagrid/__init__.py +12 -0
  11. aquagrid-0.2.0/src/aquagrid/bench.py +75 -0
  12. aquagrid-0.2.0/src/aquagrid/cli.py +52 -0
  13. aquagrid-0.2.0/src/aquagrid/engine/__init__.py +1 -0
  14. aquagrid-0.2.0/src/aquagrid/engine/cpu.py +45 -0
  15. aquagrid-0.2.0/src/aquagrid/engine/gpu.py +87 -0
  16. aquagrid-0.2.0/src/aquagrid/engine/run.py +119 -0
  17. aquagrid-0.2.0/src/aquagrid/io/__init__.py +36 -0
  18. aquagrid-0.2.0/src/aquagrid/io/schema.py +76 -0
  19. aquagrid-0.2.0/src/aquagrid/io/soil.py +264 -0
  20. aquagrid-0.2.0/src/aquagrid/io/synthetic.py +164 -0
  21. aquagrid-0.2.0/src/aquagrid/kernels/__init__.py +5 -0
  22. aquagrid-0.2.0/src/aquagrid/kernels/constants.py +196 -0
  23. aquagrid-0.2.0/src/aquagrid/kernels/impl.py +2413 -0
  24. aquagrid-0.2.0/src/aquagrid/kernels/loader.py +45 -0
  25. aquagrid-0.2.0/src/aquagrid/params.py +321 -0
  26. aquagrid-0.2.0/src/aquagrid/pipeline.py +251 -0
  27. aquagrid-0.2.0/src/aquagrid/soil_grid.py +295 -0
  28. aquagrid-0.2.0/src/aquagrid.egg-info/PKG-INFO +337 -0
  29. aquagrid-0.2.0/src/aquagrid.egg-info/SOURCES.txt +37 -0
  30. aquagrid-0.2.0/src/aquagrid.egg-info/dependency_links.txt +1 -0
  31. aquagrid-0.2.0/src/aquagrid.egg-info/entry_points.txt +2 -0
  32. aquagrid-0.2.0/src/aquagrid.egg-info/requires.txt +12 -0
  33. aquagrid-0.2.0/src/aquagrid.egg-info/top_level.txt +1 -0
  34. aquagrid-0.2.0/tests/conftest.py +32 -0
  35. aquagrid-0.2.0/tests/test_gpu.py +84 -0
  36. aquagrid-0.2.0/tests/test_grid.py +108 -0
  37. aquagrid-0.2.0/tests/test_io.py +75 -0
  38. aquagrid-0.2.0/tests/test_parity.py +105 -0
  39. aquagrid-0.2.0/tests/test_soil.py +289 -0
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.0] - 2026-09-22
11
+
12
+ ### Changed
13
+
14
+ - Rename the project to AquaGrid. The distribution, import, and command are `aquagrid`.
15
+
16
+ ## [0.1.1] - 2026-09-16
17
+
18
+ ### Changed
19
+
20
+ - PyPI-friendly README hero (HTML only inside the centered block; version badge from PyPI).
21
+ - CI matrix again covers Ubuntu and Windows on Python 3.11 and 3.12.
22
+
23
+ ## [0.1.0] - 2026-09-16
24
+
25
+ ### Added
26
+
27
+ - Initial AquaGrid package: zarr climate/sowing in, Numba CPU and GPU
28
+ kernels, YAML CLI, and pytest suite with bit-exact AquaCrop-OSPy parity.
29
+
30
+ [Unreleased]: https://github.com/Paloschi/aquagrid/compare/v0.2.0...HEAD
31
+ [0.2.0]: https://github.com/Paloschi/aquagrid/compare/v0.1.1...v0.2.0
32
+ [0.1.1]: https://github.com/Paloschi/aquagrid/releases/tag/v0.1.1
33
+ [0.1.0]: https://github.com/Paloschi/aquagrid/releases/tag/v0.1.0
aquagrid-0.2.0/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rennan Andres Paloschi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ The daily-step kernels in src/aquacrop_grid/kernels/impl.py are derived from
24
+ AquaCrop-OSPy (https://github.com/aquacropos/aquacrop), licensed under the
25
+ Apache License 2.0.
@@ -0,0 +1,4 @@
1
+ include LICENSE README.md CHANGELOG.md pyproject.toml
2
+ include examples/*.yaml
3
+ recursive-include docs *.md
4
+ recursive-include tests *.py
@@ -0,0 +1,337 @@
1
+ Metadata-Version: 2.4
2
+ Name: aquagrid
3
+ Version: 0.2.0
4
+ Summary: AquaCrop on rasters: zarr in/out, Numba-compiled kernels (CPU/GPU)
5
+ Author: Rennan Andres Paloschi
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Paloschi/aquagrid
8
+ Project-URL: Issues, https://github.com/Paloschi/aquagrid/issues
9
+ Project-URL: Changelog, https://github.com/Paloschi/aquagrid/blob/main/CHANGELOG.md
10
+ Project-URL: Source, https://github.com/Paloschi/aquagrid
11
+ Keywords: aquacrop,raster,zarr,numba,crop,hydrology
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: numpy>=1.26
22
+ Requires-Dist: numba>=0.59
23
+ Requires-Dist: pandas>=2.0
24
+ Requires-Dist: xarray>=2024.1
25
+ Requires-Dist: zarr<3,>=2.16
26
+ Requires-Dist: dask>=2024.1
27
+ Requires-Dist: typer>=0.12
28
+ Requires-Dist: pyyaml>=6.0
29
+ Requires-Dist: aquacrop>=3.0.11
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=8.0; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ <div align="center">
35
+ <a href="https://github.com/Paloschi/aquagrid"><img alt="AquaGrid" src="https://img.shields.io/badge/AquaGrid-raster%20AquaCrop-0F766E?style=for-the-badge&labelColor=134E4A"></a>
36
+ <h1>AquaGrid</h1>
37
+ <p><strong>Pixel-wise AquaCrop on rasters: zarr in, Numba kernels out — CPU or GPU.</strong></p>
38
+ <p>
39
+ <a href="https://pypi.org/project/aquagrid/"><img alt="PyPI" src="https://img.shields.io/pypi/v/aquagrid.svg?style=flat-square&color=0F766E"></a>
40
+ <a href="https://github.com/Paloschi/aquagrid/actions/workflows/test.yml"><img alt="Tests" src="https://github.com/Paloschi/aquagrid/actions/workflows/test.yml/badge.svg?branch=main"></a>
41
+ <img alt="Python" src="https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white">
42
+ <a href="#license-and-attribution"><img alt="License" src="https://img.shields.io/badge/license-MIT-0F766E?style=flat-square"></a>
43
+ <img alt="Visitors" src="https://api.visitorbadge.io/api/visitors?path=github.com%2FPaloschi%2Faquagrid&label=Visitors&countColor=%230F766E&style=flat">
44
+ </p>
45
+ <p>
46
+ <img alt="Numba" src="https://img.shields.io/badge/Numba-CPU%20%7C%20CUDA-00A3E0?style=flat-square">
47
+ <img alt="NumPy" src="https://img.shields.io/badge/NumPy-1.26%2B-013243?style=flat-square&logo=numpy&logoColor=white">
48
+ <img alt="xarray" src="https://img.shields.io/badge/xarray-2024%2B-4D8BBD?style=flat-square">
49
+ <img alt="zarr" src="https://img.shields.io/badge/zarr-2.16%2B-F9C642?style=flat-square&labelColor=1A1A1A">
50
+ <img alt="Dask" src="https://img.shields.io/badge/Dask-2024%2B-FFC107?style=flat-square&logo=dask&logoColor=black">
51
+ <img alt="pytest" src="https://img.shields.io/badge/pytest-8-0A9EDC?style=flat-square&logo=pytest&logoColor=white">
52
+ <img alt="AquaCrop-OSPy" src="https://img.shields.io/badge/AquaCrop--OSPy-3.x-0F766E?style=flat-square">
53
+ </p>
54
+ <p>
55
+ <img alt="Scope" src="https://img.shields.io/badge/scope-rainfed%20%7C%20one%20season-134E4A?style=flat-square">
56
+ <img alt="Parity" src="https://img.shields.io/badge/parity-1e--12%20vs%20OSPy-0F766E?style=flat-square">
57
+ <a href="https://www.conventionalcommits.org/"><img alt="Conventional Commits" src="https://img.shields.io/badge/commits-conventional-FE5196?style=flat-square&logo=conventionalcommits&logoColor=white"></a>
58
+ </p>
59
+ <p>
60
+ <a href="#quick-start">Quick start</a> ·
61
+ <a href="#yaml-config">Config</a> ·
62
+ <a href="#architecture">Architecture</a> ·
63
+ <a href="#io-schema">Schema</a> ·
64
+ <a href="#testing">Testing</a> ·
65
+ <a href="#citing-this-project">Citing</a> ·
66
+ <a href="#license-and-attribution">License</a>
67
+ </p>
68
+ </div>
69
+
70
+ <p align="center">
71
+ <img src="docs/images/example-field.png" alt="Soybean field at 30 m: MapBiomas Solo collection 3 clay on the left, AquaCrop dry yield on the right" width="920">
72
+ </p>
73
+
74
+ ---
75
+
76
+ Climate as zarr cubes `(time, y, x)`, per-pixel sowing dates `(y, x)`, zarr
77
+ outputs. Daily-step kernels from
78
+ [AquaCrop-OSPy](https://github.com/aquacropos/aquacrop) 3.x are vendored and
79
+ recompiled with Numba — `njit` + `prange` on CPU (one thread per pixel) and
80
+ `numba.cuda` on GPU — with **bit-exact parity** against AquaCrop-OSPy
81
+ (`tests/test_parity.py`).
82
+
83
+ This is **not** official FAO AquaCrop and not an aquacropos extension. It
84
+ started in [CyMP](https://github.com/Paloschi/CyMP) (Unioeste-LEA).
85
+
86
+ | Layer | Stack |
87
+ | ---------- | -------------------------------------------------------------------------- |
88
+ | Language | **Python 3.11+** |
89
+ | Kernels | **Numba** (`njit` + `prange` / `numba.cuda`) |
90
+ | Arrays | **NumPy**, **xarray**, **zarr**, **Dask** |
91
+ | Reference | **AquaCrop-OSPy** ≥ 3.0.11 (crop/soil params + parity tests) |
92
+ | CLI | **Typer** (`aquagrid`) |
93
+ | Tests | **pytest** 8 — Ubuntu & Windows, Python 3.11 / 3.12 |
94
+
95
+ **Current scope:** rainfed (no irrigation, no groundwater), one season per
96
+ pixel. Soil: a single AquaCrop preset **or** a per-pixel zarr raster
97
+ (HiHydroSoil hydraulics or sand/silt/clay texture).
98
+
99
+ ---
100
+
101
+ ## Prerequisites
102
+
103
+ - **Python 3.11+**
104
+ - GPU (optional): NVIDIA GPU with a CUDA **driver** — Numba talks to the
105
+ driver; the full CUDA toolkit is not required
106
+
107
+ ---
108
+
109
+ ## Quick start
110
+
111
+ ```bash
112
+ pip install aquagrid
113
+ ```
114
+
115
+ From Git (no clone, before or besides PyPI):
116
+
117
+ ```bash
118
+ pip install "git+https://github.com/Paloschi/aquagrid.git"
119
+ ```
120
+
121
+ For development, clone and install editable:
122
+
123
+ ```bash
124
+ git clone https://github.com/Paloschi/aquagrid.git
125
+ cd aquagrid
126
+ pip install -e ".[dev]"
127
+ ```
128
+
129
+ ```bash
130
+ # 1. synthetic 10×10 pixels / 540 days
131
+ aquagrid synth --out ./data
132
+
133
+ # 2. config
134
+ cp examples/config.example.yaml ./data/config.yaml
135
+ # (adjust paths if needed)
136
+
137
+ # 3. run
138
+ aquagrid run --config ./data/config.yaml # CPU
139
+ aquagrid run --config ./data/config.yaml -b gpu # GPU
140
+ ```
141
+
142
+ Output: `output.zarr` with final yield/biomass `(y, x)` and, with
143
+ `save_daily: true`, daily series `(time, y, x)` in group `daily`.
144
+ Full schema: [`docs/zarr-schema.md`](docs/zarr-schema.md).
145
+
146
+ ### Programmatic
147
+
148
+ ```python
149
+ from aquagrid.pipeline import run_grid
150
+
151
+ run_grid("climate.zarr", "sowing.zarr", "output.zarr",
152
+ crop_name="Maize", soil_name="SandyLoam",
153
+ backend="cpu", save_daily=False)
154
+ ```
155
+
156
+ ---
157
+
158
+ ## CLI
159
+
160
+ | Command | Purpose |
161
+ | ------- | ------- |
162
+ | `aquagrid synth -o ./data` | Synthetic climate + sowing zarr for tests |
163
+ | `aquagrid run -c config.yaml` | Gridded simulation from YAML (`-b cpu\|gpu`) |
164
+ | `aquagrid bench` | Throughput (pixels/s), excluding JIT compile |
165
+
166
+ ---
167
+
168
+ ## YAML config
169
+
170
+ ```yaml
171
+ climate: data/climate.zarr # cube (time, y, x): tmin, tmax, precip, eto
172
+ sowing: data/sowing.zarr # grid (y, x) int32 YYYYDDD; <=0 = masked
173
+ output: data/output.zarr
174
+ crop:
175
+ name: Maize # any AquaCrop-OSPy crop
176
+ soil:
177
+ name: SandyLoam # AquaCrop preset (xor with zarr below)
178
+ # zarr: data/soil.zarr # ksat/wcsat/wcpf2/wcpf3 or sand/silt/clay
179
+ # ksat_unit: cm/d
180
+ backend: cpu # cpu | gpu
181
+ options:
182
+ save_daily: false
183
+ tile: 128 # spatial tile size (pixels)
184
+ max_season_days: 400
185
+ ```
186
+
187
+ Copy from [`examples/config.example.yaml`](examples/config.example.yaml).
188
+
189
+ ---
190
+
191
+ ## Project structure
192
+
193
+ ```
194
+ aquagrid/
195
+ ├── docs/ # zarr I/O schema
196
+ ├── examples/ # sample YAML config
197
+ ├── src/aquagrid/
198
+ │ ├── cli.py # Typer: synth / run / bench
199
+ │ ├── pipeline.py # zarr in → tiles → zarr out
200
+ │ ├── params.py # Crop / Soil → kernel arrays
201
+ │ ├── soil_grid.py # PTF + HiHydro layers → compartments
202
+ │ ├── bench.py
203
+ │ ├── engine/
204
+ │ │ ├── cpu.py # njit + prange
205
+ │ │ ├── gpu.py # numba.cuda
206
+ │ │ └── run.py # shared runner over flat arrays
207
+ │ ├── kernels/
208
+ │ │ ├── impl.py # daily step (njit / cuda.jit subset)
209
+ │ │ └── loader.py # compile one source for both backends
210
+ │ └── io/ # schema, validation, synthetic data
211
+ └── tests/ # pytest: parity, grid, soil, io, gpu
212
+ ```
213
+
214
+ ### Where to look first
215
+
216
+ | Concern | Location |
217
+ | ------------------ | --------------------------------------------- |
218
+ | Daily AquaCrop step | `src/aquagrid/kernels/impl.py` |
219
+ | CPU / GPU backends | `src/aquagrid/engine/` |
220
+ | Raster pipeline | `src/aquagrid/pipeline.py` |
221
+ | Crop / soil params | `src/aquagrid/params.py` |
222
+ | Soil rasters / PTF | `src/aquagrid/soil_grid.py` |
223
+ | Zarr schema | [`docs/zarr-schema.md`](docs/zarr-schema.md) |
224
+ | Bit-exact parity | `tests/test_parity.py` |
225
+
226
+ ---
227
+
228
+ ## Architecture
229
+
230
+ ```
231
+ climate.zarr (time, y, x) + sowing.zarr (y, x) [+ soil.zarr]
232
+ ↓
233
+ pipeline.py (spatial tiles)
234
+ ↓
235
+ params.py → kernel arrays
236
+ ↓
237
+ engine/run.py → cpu (prange) | gpu (cuda)
238
+ ↓
239
+ kernels/impl.py one source, Numba njit / cuda.jit
240
+ ↓
241
+ output.zarr
242
+ ```
243
+
244
+ - `kernels/impl.py` — AquaCrop daily step ported to a common `njit` /
245
+ `cuda.jit` subset (no allocation in kernels, scalar state per pixel +
246
+ 1-D compartment views). One source compiled for both backends by
247
+ `kernels/loader.py`.
248
+ - `params.py` — flattens aquacrop `Crop` / `Soil` into kernel arrays
249
+ (weather-independent init, including deepening the profile to
250
+ `Zmax + 0.1`).
251
+ - GDD phenology is computed **inside the kernel per pixel** (sowing date
252
+ changes each pixel's thermal accumulation), mirroring aquacrop
253
+ `compute_crop_calendar`.
254
+
255
+ ---
256
+
257
+ ## I/O schema
258
+
259
+ | Store | Shape | Role |
260
+ | -------- | ------------ | ---- |
261
+ | Climate | `(time, y, x)` | `tmin`, `tmax`, `precip`, `eto` (daily, gap-free) |
262
+ | Sowing | `(y, x)` | `int32` YYYYDDD; `<= 0` = mask |
263
+ | Soil | name **or** zarr | AquaCrop preset, or hydraulic / texture raster |
264
+ | Output | `(y, x)` | yield, biomass, status; optional `daily/` group |
265
+
266
+ Grids must already be aligned — AquaGrid does not reproject.
267
+ Details, units, and soil bands: [`docs/zarr-schema.md`](docs/zarr-schema.md).
268
+
269
+ ---
270
+
271
+ ## Benchmark
272
+
273
+ `aquagrid bench --pixels 65536 --days 540` (excluding JIT compile):
274
+
275
+ | Backend | Reference hardware | Throughput |
276
+ | ------- | ------------------ | ---------- |
277
+ | CPU (`njit` + `prange`) | Ryzen (all threads) | ~58k pixels/s |
278
+ | GPU (`numba.cuda`) | RTX 3060 | ~61k pixels/s |
279
+
280
+ One full season (540 days) per pixel. On larger grids the GPU scales better
281
+ (climate transfer dominates on small grids).
282
+
283
+ ---
284
+
285
+ ## Testing
286
+
287
+ [![Tests](https://github.com/Paloschi/aquagrid/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/Paloschi/aquagrid/actions/workflows/test.yml)
288
+
289
+ ```bash
290
+ pytest # parity vs AquaCrop-OSPy, io, grid driver, gpu (if present)
291
+ ```
292
+
293
+ | File | What it covers |
294
+ | ---- | -------------- |
295
+ | `test_parity.py` | Single pixel vs AquaCrop-OSPy, 1e-12 (maize/soybean, calendar and GDD, 3 soils) |
296
+ | `test_grid.py` | Mask, per-pixel sowing, tiles, grid vs single-pixel equality |
297
+ | `test_soil.py` | Hydraulic/texture zarr, Saxton–Rawls PTF, per-pixel soil |
298
+ | `test_io.py` | Synthetic I/O, sowing → plant index |
299
+ | `test_gpu.py` | CPU vs GPU parity (skipped without CUDA) |
300
+
301
+ CI (GitHub Actions) runs on every **pull request** against `main`, and again
302
+ on push to `main`: Ubuntu and Windows × Python 3.11 / 3.12. Hosted runners
303
+ are CPU-only; `test_gpu.py` is skipped.
304
+
305
+ ---
306
+
307
+ ## Contributing
308
+
309
+ 1. Use **conventional commits** (`feat:`, `fix:`, `chore:`, `ci:`, `docs:`).
310
+ 2. Put tests in the same change as the behaviour they cover.
311
+ 3. Open a pull request against `main` — CI must pass before merge.
312
+
313
+ ---
314
+
315
+ ## Citing this project
316
+
317
+ If you use AquaGrid in research or operational work, please cite this repository:
318
+
319
+ ```bibtex
320
+ @software{paloschi_aquagrid,
321
+ author = {Paloschi, Rennan Andres},
322
+ title = {AquaGrid: pixel-wise AquaCrop on rasters (Numba CPU/GPU)},
323
+ year = {2026},
324
+ url = {https://github.com/Paloschi/aquagrid},
325
+ version = {0.1.0}
326
+ }
327
+ ```
328
+
329
+ Also acknowledge [AquaCrop-OSPy](https://github.com/aquacropos/aquacrop) (and FAO AquaCrop) for the underlying crop water-productivity model that the kernels follow.
330
+
331
+
332
+ ---
333
+
334
+ ## License and attribution
335
+
336
+ **MIT.** Kernels in `src/aquagrid/kernels/impl.py` are derived from
337
+ [AquaCrop-OSPy](https://github.com/aquacropos/aquacrop) (**Apache-2.0**).