adjeff 1.2.2__tar.gz → 1.2.3__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.
- {adjeff-1.2.2 → adjeff-1.2.3}/CHANGELOG.md +13 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/PKG-INFO +1 -1
- {adjeff-1.2.2 → adjeff-1.2.3}/docs/guide.md +61 -16
- {adjeff-1.2.2 → adjeff-1.2.3}/pyproject.toml +1 -1
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/loaders/maja_loader.py +1 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/_brdf_sampler.py +1 -5
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/rho_toa.py +1 -1
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/rho_toa_sym.py +1 -1
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/scene_module.py +4 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/sweep_sampler.py +23 -10
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/reference/wu2024.py +0 -1
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_atmo_samplers.py +29 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/.gitattributes +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/.github/workflows/ci.yml +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/.github/workflows/publish.yml +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/.gitignore +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/.pre-commit-config.yaml +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/LICENSE +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/README.md +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/docs/plan-observabilite-0.13.0.md +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/docs/plan-smartg-2.0-0.15.0.md +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/docs/revue-simplification-2026-08.md +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/docs/roadmap.md +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/notebooks/01-create-and-display-image.ipynb +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/notebooks/02-atmospheric-configuration.ipynb +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/notebooks/03-compute-radiative-quantities.ipynb +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/notebooks/04-simulate-rho-toa.ipynb +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/notebooks/05-learn-psf.ipynb +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/notebooks/06-evaluate-a-psf.ipynb +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/pixi.lock +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/_logging.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/accessor.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/analysis/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/analysis/_energy.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/analysis/_error.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/analysis/_plane.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/analysis/_radial.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/analysis/_vectorise.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/api/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/api/_config.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/api/_load.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/api/_pipeline.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/api/_psf.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/atmosphere/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/atmosphere/atmo_config.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/atmosphere/atmo_factory.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/atmosphere/geo_config.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/atmosphere/spectral_config.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/atmosphere/surface.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/core/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/core/_psf.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/core/analytical_psf.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/core/bands.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/core/image_dict.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/core/image_generator.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/core/non_analytical_psf.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/core/psf_tree.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/exceptions.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/classic/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/classic/toa_to_unif.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/classic/unif_to_toa.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/loaders/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/loaders/product_loader.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/models/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/models/psf_conv_module.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/models/unif2surface.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/pipeline.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/_atmo_sampler.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/_ensure.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/_smartg.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/radiatives.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/rho_atm.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/sph_alb.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/sph_alb_brdf.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/tdif_down.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/tdif_up.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/tdif_up_brdf.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/tdir_down.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/samplers/tdir_up.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/modules/scene_source.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/_combo_stage.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/_config.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/adam_optimizer.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/fit.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/landscape.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/lbfgs_optimizer.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/loss.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/metrics.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/optim/training_set.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/py.typed +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/reference/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/utils/__init__.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/utils/_config.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/utils/cache_store.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/utils/convolve.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/utils/radial.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/utils/smartgutils.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/utils/torchutils.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/src/adjeff/utils/xrutils.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/_test_module.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/conftest.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_accessor.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_analysis.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_api_docs.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_api_psf.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_atmosphere_config.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_brdf_samplers.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_classic_modules.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_config.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_convolve.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_image_dict.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_image_generator.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_import.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_integration.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_landscape.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_loaders.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_logging.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_optim.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_psf.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_psf_tree.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_radial.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_scene_module.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_smartg_api.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_smartg_defaults.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_smartgutils.py +0 -0
- {adjeff-1.2.2 → adjeff-1.2.3}/tests/test_xrutils.py +0 -0
|
@@ -5,6 +5,19 @@ All notable changes to this project are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [1.2.3]
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **A radiative sweep over maps keeps the scene's coordinates.** xsweep
|
|
13
|
+
labels by position the dims it does not sweep, so a scene whose `aot`
|
|
14
|
+
and `h` are maps got its terms aligned on those labels: every pixel
|
|
15
|
+
came back NaN but the handful whose index matched one. `load_maja(...,
|
|
16
|
+
as_map=True, compute_radiatives=True)` could not produce anything.
|
|
17
|
+
- **A sweep restores its own coordinates.** A sampler used to do it
|
|
18
|
+
itself, in its own `_compute`, which is what the fix above forgot in
|
|
19
|
+
one of five places. There is now one place.
|
|
20
|
+
|
|
8
21
|
## [1.2.2]
|
|
9
22
|
|
|
10
23
|
### Fixed
|
|
@@ -5,14 +5,15 @@
|
|
|
5
5
|
3. [Atmosphere and geometry](#3-atmosphere-and-geometry)
|
|
6
6
|
4. [Scene modules and pipelines](#4-scene-modules-and-pipelines)
|
|
7
7
|
5. [Radiative samplers](#5-radiative-samplers)
|
|
8
|
-
6. [
|
|
9
|
-
7. [
|
|
10
|
-
8. [
|
|
11
|
-
9. [
|
|
12
|
-
10. [
|
|
13
|
-
11. [
|
|
14
|
-
12. [
|
|
15
|
-
13. [
|
|
8
|
+
6. [Writing a sampler](#6-writing-a-sampler)
|
|
9
|
+
7. [PSF models](#7-psf-models)
|
|
10
|
+
8. [Correction](#8-correction)
|
|
11
|
+
9. [Fitting a PSF](#9-fitting-a-psf)
|
|
12
|
+
10. [High-level API](#10-high-level-api)
|
|
13
|
+
11. [Analysis and the `.adjeff` accessor](#11-analysis-and-the-adjeff-accessor)
|
|
14
|
+
12. [Cache](#12-cache)
|
|
15
|
+
13. [Logging](#13-logging)
|
|
16
|
+
14. [Installation details](#14-installation-details)
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
@@ -180,7 +181,51 @@ data, or Smart-G returns NaN, which the modules turn into an error.
|
|
|
180
181
|
|
|
181
182
|
> Worked through in [notebook 03](../notebooks/03-compute-radiative-quantities.ipynb).
|
|
182
183
|
|
|
183
|
-
## 6.
|
|
184
|
+
## 6. Writing a sampler
|
|
185
|
+
|
|
186
|
+
A sampler is a combination of a physics function and a sentence saying how to call it.
|
|
187
|
+
Four declarations are required, and then the base class does the rest: cache, batching,
|
|
188
|
+
deduplication, provenance, and putting the coordinates back.
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
class SphAlbSampler(AtmoSampler):
|
|
192
|
+
_output_vars = ["sph_alb"]
|
|
193
|
+
contract = "batch(aot, rh, h, href) vec(wl) -> sph_alb(wl)"
|
|
194
|
+
point_fn = staticmethod(sph_alb)
|
|
195
|
+
default_n_ph = 20000000
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`point_fn` is the physics: it takes one state and returns one result.
|
|
199
|
+
The contract says what a state is.
|
|
200
|
+
|
|
201
|
+
### The contract
|
|
202
|
+
|
|
203
|
+
| Word | Meaning |
|
|
204
|
+
|---|---|
|
|
205
|
+
| `vec(...)` | these parameters travel together in one call, the physics gets arrays |
|
|
206
|
+
| `batch(...)` | these parameters are grouped into calls of `batch_size` states |
|
|
207
|
+
| `loop(...)` | one call per value, nothing else is possible |
|
|
208
|
+
| `-> name(dims)` | the variable written, and the dims it comes back with |
|
|
209
|
+
|
|
210
|
+
### Writing the output
|
|
211
|
+
|
|
212
|
+
A sampler whose contract carries `wl` is swept once for every band. One
|
|
213
|
+
that reads the scene, like `RhoToaSymSampler`, sweeps per band instead
|
|
214
|
+
and passes the scene to the physics:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
def _compute(self, scene):
|
|
218
|
+
for band in scene.bands:
|
|
219
|
+
scene[band][self._slot("rho_toa")] = self._sweep(
|
|
220
|
+
rho_s=scene[band], band=band
|
|
221
|
+
)
|
|
222
|
+
return scene
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
What is bound this way stays out of the sweep space, so the physics can
|
|
226
|
+
read a map the sweep knows nothing about.
|
|
227
|
+
|
|
228
|
+
## 7. PSF models
|
|
184
229
|
|
|
185
230
|
Analytical PSFs are `torch.nn.Module`s with bounded parameters, sampled
|
|
186
231
|
on an odd, square `PSFGrid`.
|
|
@@ -213,7 +258,7 @@ psf_params(tree, S2Band.B02) # {"sigma": DataArray}
|
|
|
213
258
|
tree.to_zarr("psf.zarr", mode="w")
|
|
214
259
|
```
|
|
215
260
|
|
|
216
|
-
##
|
|
261
|
+
## 8. Correction
|
|
217
262
|
|
|
218
263
|
For a uniform surface, `Unif2Toa` applies the 5S formula and `Toa2Unif`
|
|
219
264
|
inverts it, giving `rho_unif`, the reflectance a uniform surface would
|
|
@@ -234,7 +279,7 @@ from adjeff.modules.models import Unif2Surface
|
|
|
234
279
|
scene = Unif2Surface(kernels=tree)(scene)
|
|
235
280
|
```
|
|
236
281
|
|
|
237
|
-
##
|
|
282
|
+
## 9. Fitting a PSF
|
|
238
283
|
|
|
239
284
|
`fit` optimises the PSF of a model over reference scenes, once per
|
|
240
285
|
band and atmospheric state, and returns the frozen tree.
|
|
@@ -275,7 +320,7 @@ encircled-energy radii over a list of PSFs, e.g. a parameter grid.
|
|
|
275
320
|
|
|
276
321
|
> Worked through in [notebook 05](../notebooks/05-learn-psf.ipynb).
|
|
277
322
|
|
|
278
|
-
##
|
|
323
|
+
## 10. High-level API
|
|
279
324
|
|
|
280
325
|
`adjeff` exports one-call functions for the common paths:
|
|
281
326
|
|
|
@@ -294,7 +339,7 @@ encircled-energy radii over a list of PSFs, e.g. a parameter grid.
|
|
|
294
339
|
|
|
295
340
|
> Worked through in [notebook 04](../notebooks/04-simulate-rho-toa.ipynb).
|
|
296
341
|
|
|
297
|
-
##
|
|
342
|
+
## 11. Analysis and the `.adjeff` accessor
|
|
298
343
|
|
|
299
344
|
`adjeff.analysis` quantifies a PSF or a 2-D field:
|
|
300
345
|
|
|
@@ -322,7 +367,7 @@ da.adjeff.untidy() # and back, before xr.merge
|
|
|
322
367
|
|
|
323
368
|
> Worked through in [notebook 06](../notebooks/06-evaluate-a-psf.ipynb).
|
|
324
369
|
|
|
325
|
-
##
|
|
370
|
+
## 12. Cache
|
|
326
371
|
|
|
327
372
|
Every module takes a `CacheStore`. Results are stored in Zarr, keyed by
|
|
328
373
|
the module, its configuration and the provenance of its inputs; an
|
|
@@ -340,7 +385,7 @@ scene = pipeline(scene) # read back, no GPU call
|
|
|
340
385
|
|
|
341
386
|
Without a cache, every output stays in RAM.
|
|
342
387
|
|
|
343
|
-
##
|
|
388
|
+
## 13. Logging
|
|
344
389
|
|
|
345
390
|
Adjeff prints nothing until asked:
|
|
346
391
|
|
|
@@ -362,7 +407,7 @@ are capped at `warning`, and Python warnings join the stream. Lines are
|
|
|
362
407
|
Everything is an ordinary `logging` record under `adjeff`, so an
|
|
363
408
|
application's own handlers receive it.
|
|
364
409
|
|
|
365
|
-
##
|
|
410
|
+
## 14. Installation details
|
|
366
411
|
|
|
367
412
|
| Environment | GPU | Contents |
|
|
368
413
|
|---|---|---|
|
|
@@ -126,11 +126,7 @@ class BrdfSampler(AtmoSampler):
|
|
|
126
126
|
name = self._output_vars[0]
|
|
127
127
|
for band in self.spectral_config.bands:
|
|
128
128
|
ds = scene[band]
|
|
129
|
-
|
|
130
|
-
# xsweep numbers a dim it did not sweep from zero, which no
|
|
131
|
-
# longer matches the scene's own labels.
|
|
132
|
-
arr = self._restore_coords(raw.sel(wl=band.wl_nm), ds)
|
|
133
|
-
ds[self._slot(name)] = self._normalise(arr, ds)
|
|
129
|
+
ds[self._slot(name)] = self._normalise(raw.sel(wl=band.wl_nm), ds)
|
|
134
130
|
return scene
|
|
135
131
|
|
|
136
132
|
def _t_sun(self, ds: xr.Dataset) -> xr.DataArray:
|
|
@@ -140,6 +140,6 @@ class RhoToaSampler(SweepSampler):
|
|
|
140
140
|
# the two travel together rather than through the sweep space.
|
|
141
141
|
for band in scene.bands:
|
|
142
142
|
arr = self._sweep(rho_s=scene[band], band=band)
|
|
143
|
-
scene[band]["rho_toa"] =
|
|
143
|
+
scene[band]["rho_toa"] = arr
|
|
144
144
|
|
|
145
145
|
return scene
|
|
@@ -125,6 +125,6 @@ class RhoToaSymSampler(SweepSampler):
|
|
|
125
125
|
# the two travel together rather than through the sweep space.
|
|
126
126
|
for band in scene.bands:
|
|
127
127
|
arr = self._sweep(rho_s=scene[band], band=band)
|
|
128
|
-
scene[band][self._slot("rho_toa")] =
|
|
128
|
+
scene[band][self._slot("rho_toa")] = arr
|
|
129
129
|
|
|
130
130
|
return scene
|
|
@@ -133,6 +133,9 @@ class SceneModule:
|
|
|
133
133
|
|
|
134
134
|
outcome["cached"] = False
|
|
135
135
|
scene = self._compute(scene)
|
|
136
|
+
for band in scene.bands:
|
|
137
|
+
ds = scene[band]
|
|
138
|
+
print(ds)
|
|
136
139
|
self._reject_non_finite(scene)
|
|
137
140
|
self._stamp_provenance(scene, key)
|
|
138
141
|
self._cache.save_vars(key, self._role_view(scene), self._output_vars)
|
|
@@ -162,6 +165,7 @@ class SceneModule:
|
|
|
162
165
|
values = ds[slot]
|
|
163
166
|
if values.dtype.kind not in "fc":
|
|
164
167
|
continue
|
|
168
|
+
print(values)
|
|
165
169
|
# Reduced rather than materialised: a lazy output is read
|
|
166
170
|
# back one chunk at a time instead of all at once.
|
|
167
171
|
bad = int((~np.isfinite(values)).sum())
|
|
@@ -78,14 +78,17 @@ class SweepSampler(SceneModule):
|
|
|
78
78
|
def _space(self) -> xr.Dataset:
|
|
79
79
|
"""Collect the swept parameters from the configs into one Dataset.
|
|
80
80
|
|
|
81
|
-
Coordinates are dropped
|
|
82
|
-
otherwise claim conflicting labels for it
|
|
81
|
+
Coordinates are dropped, two arrays sharing a spatial dim would
|
|
82
|
+
otherwise claim conflicting labels for it, and kept aside for
|
|
83
|
+
:meth:`_sweep` to put back.
|
|
83
84
|
"""
|
|
84
85
|
wanted = set(self._contract.inputs)
|
|
85
86
|
arrays: dict[str, xr.DataArray] = {}
|
|
87
|
+
self._dropped: dict[str, xr.DataArray] = {}
|
|
86
88
|
for config in self._get_configs():
|
|
87
89
|
for name, array in config._arrays.items():
|
|
88
90
|
if name in wanted and name not in arrays:
|
|
91
|
+
self._dropped.update({str(d): c for d, c in array.coords.items()})
|
|
89
92
|
arrays[name] = array.drop_vars(list(array.coords), errors="ignore")
|
|
90
93
|
return xr.Dataset(arrays)
|
|
91
94
|
|
|
@@ -96,19 +99,29 @@ class SweepSampler(SceneModule):
|
|
|
96
99
|
|
|
97
100
|
return coerce(type(self).contract)
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
def _restore_coords(
|
|
103
|
+
self, arr: xr.DataArray, source: xr.Dataset | None = None
|
|
104
|
+
) -> xr.DataArray:
|
|
105
|
+
"""Give *arr* back the coordinates the sweep space was stripped of.
|
|
102
106
|
|
|
103
107
|
xsweep labels the dims it does not sweep (``y``, ``x``) by
|
|
104
108
|
position; assigned as they are, they would misalign with the
|
|
105
|
-
scene and leave NaN.
|
|
109
|
+
scene and leave NaN. *source* names the scene the physics was
|
|
110
|
+
bound to, whose own dims are not in the space.
|
|
106
111
|
"""
|
|
107
112
|
shared = {
|
|
108
|
-
|
|
109
|
-
for
|
|
110
|
-
if
|
|
113
|
+
name: coord
|
|
114
|
+
for name, coord in getattr(self, "_dropped", {}).items()
|
|
115
|
+
if name in arr.dims
|
|
111
116
|
}
|
|
117
|
+
if source is not None:
|
|
118
|
+
shared.update(
|
|
119
|
+
{
|
|
120
|
+
str(dim): source.coords[dim]
|
|
121
|
+
for dim in arr.dims
|
|
122
|
+
if dim in source.coords and dim in source.dims
|
|
123
|
+
}
|
|
124
|
+
)
|
|
112
125
|
return arr.assign_coords(shared) if shared else arr
|
|
113
126
|
|
|
114
127
|
def _sweep(self, **bound: Any) -> xr.DataArray:
|
|
@@ -137,7 +150,7 @@ class SweepSampler(SceneModule):
|
|
|
137
150
|
self._log.info("sweep.plan", **plan)
|
|
138
151
|
result = sweeper(space, policy=self._policy(space, bound), **statics)
|
|
139
152
|
out: xr.DataArray = result[self._contract.outputs[0]]
|
|
140
|
-
return out
|
|
153
|
+
return self._restore_coords(out, bound.get("rho_s"))
|
|
141
154
|
|
|
142
155
|
_SPATIAL: ClassVar[frozenset[str]] = frozenset({"x", "y", "x_psf", "y_psf"})
|
|
143
156
|
|
|
@@ -125,7 +125,6 @@ class WuPsfSampler(SweepSampler):
|
|
|
125
125
|
for band in scene.bands:
|
|
126
126
|
ds = scene[band]
|
|
127
127
|
arr = self._sweep(rho_s=ds, band=band)
|
|
128
|
-
arr = self._restore_coords(arr, ds)
|
|
129
128
|
# The store allocates from the contract and labels a dimension
|
|
130
129
|
# it never swept by position, so the grid is put back here.
|
|
131
130
|
grid = PSFGrid(res=ds["rho_s"].adjeff.res, n=ds["rho_s"].adjeff.n)
|
|
@@ -305,3 +305,32 @@ class TestSweepPolicy:
|
|
|
305
305
|
for ds in scenes
|
|
306
306
|
]
|
|
307
307
|
assert stores[0] != stores[1]
|
|
308
|
+
|
|
309
|
+
@pytest.mark.integration
|
|
310
|
+
def test_a_mapped_sweep_keeps_the_scene_coordinates(self):
|
|
311
|
+
"""Every pixel gets a value, not the few whose index matches a label."""
|
|
312
|
+
import numpy as np
|
|
313
|
+
|
|
314
|
+
from adjeff.api import run_radiatives_from_scene
|
|
315
|
+
from adjeff.core import gaussian_image_dict
|
|
316
|
+
|
|
317
|
+
band, size = S2Band.B02, 5
|
|
318
|
+
scene = gaussian_image_dict(sigma=1.0, res_km=0.5, bands=[band], n=size)
|
|
319
|
+
ds = scene[band]
|
|
320
|
+
rng = np.random.default_rng(0)
|
|
321
|
+
ds["aot"] = (("y", "x"), rng.uniform(0.05, 0.4, (size, size)))
|
|
322
|
+
ds["h"] = (("y", "x"), np.zeros((size, size)))
|
|
323
|
+
for name, value in (
|
|
324
|
+
("rh", 50.0),
|
|
325
|
+
("href", 2.0),
|
|
326
|
+
("sza", 17.0),
|
|
327
|
+
("saa", 117.0),
|
|
328
|
+
("vza", 3.2),
|
|
329
|
+
("vaa", 207.0),
|
|
330
|
+
):
|
|
331
|
+
ds[name] = xr.DataArray([value], dims=[name])
|
|
332
|
+
scene[band] = ds
|
|
333
|
+
|
|
334
|
+
pytest.importorskip("smartg")
|
|
335
|
+
out = run_radiatives_from_scene(scene, n_bins=2)
|
|
336
|
+
assert bool(np.isfinite(out[band]["tdir_down"]).all())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|