adjeff 1.2.0__tar.gz → 1.2.2__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 (130) hide show
  1. {adjeff-1.2.0 → adjeff-1.2.2}/CHANGELOG.md +20 -3
  2. {adjeff-1.2.0 → adjeff-1.2.2}/PKG-INFO +1 -1
  3. {adjeff-1.2.0 → adjeff-1.2.2}/pyproject.toml +1 -1
  4. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/sweep_sampler.py +30 -8
  5. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_atmo_samplers.py +63 -3
  6. {adjeff-1.2.0 → adjeff-1.2.2}/.gitattributes +0 -0
  7. {adjeff-1.2.0 → adjeff-1.2.2}/.github/workflows/ci.yml +0 -0
  8. {adjeff-1.2.0 → adjeff-1.2.2}/.github/workflows/publish.yml +0 -0
  9. {adjeff-1.2.0 → adjeff-1.2.2}/.gitignore +0 -0
  10. {adjeff-1.2.0 → adjeff-1.2.2}/.pre-commit-config.yaml +0 -0
  11. {adjeff-1.2.0 → adjeff-1.2.2}/LICENSE +0 -0
  12. {adjeff-1.2.0 → adjeff-1.2.2}/README.md +0 -0
  13. {adjeff-1.2.0 → adjeff-1.2.2}/docs/guide.md +0 -0
  14. {adjeff-1.2.0 → adjeff-1.2.2}/docs/plan-observabilite-0.13.0.md +0 -0
  15. {adjeff-1.2.0 → adjeff-1.2.2}/docs/plan-smartg-2.0-0.15.0.md +0 -0
  16. {adjeff-1.2.0 → adjeff-1.2.2}/docs/revue-simplification-2026-08.md +0 -0
  17. {adjeff-1.2.0 → adjeff-1.2.2}/docs/roadmap.md +0 -0
  18. {adjeff-1.2.0 → adjeff-1.2.2}/notebooks/01-create-and-display-image.ipynb +0 -0
  19. {adjeff-1.2.0 → adjeff-1.2.2}/notebooks/02-atmospheric-configuration.ipynb +0 -0
  20. {adjeff-1.2.0 → adjeff-1.2.2}/notebooks/03-compute-radiative-quantities.ipynb +0 -0
  21. {adjeff-1.2.0 → adjeff-1.2.2}/notebooks/04-simulate-rho-toa.ipynb +0 -0
  22. {adjeff-1.2.0 → adjeff-1.2.2}/notebooks/05-learn-psf.ipynb +0 -0
  23. {adjeff-1.2.0 → adjeff-1.2.2}/notebooks/06-evaluate-a-psf.ipynb +0 -0
  24. {adjeff-1.2.0 → adjeff-1.2.2}/pixi.lock +0 -0
  25. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/__init__.py +0 -0
  26. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/_logging.py +0 -0
  27. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/accessor.py +0 -0
  28. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/analysis/__init__.py +0 -0
  29. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/analysis/_energy.py +0 -0
  30. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/analysis/_error.py +0 -0
  31. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/analysis/_plane.py +0 -0
  32. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/analysis/_radial.py +0 -0
  33. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/analysis/_vectorise.py +0 -0
  34. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/api/__init__.py +0 -0
  35. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/api/_config.py +0 -0
  36. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/api/_load.py +0 -0
  37. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/api/_pipeline.py +0 -0
  38. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/api/_psf.py +0 -0
  39. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/atmosphere/__init__.py +0 -0
  40. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/atmosphere/atmo_config.py +0 -0
  41. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/atmosphere/atmo_factory.py +0 -0
  42. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/atmosphere/geo_config.py +0 -0
  43. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/atmosphere/spectral_config.py +0 -0
  44. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/atmosphere/surface.py +0 -0
  45. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/core/__init__.py +0 -0
  46. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/core/_psf.py +0 -0
  47. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/core/analytical_psf.py +0 -0
  48. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/core/bands.py +0 -0
  49. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/core/image_dict.py +0 -0
  50. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/core/image_generator.py +0 -0
  51. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/core/non_analytical_psf.py +0 -0
  52. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/core/psf_tree.py +0 -0
  53. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/exceptions.py +0 -0
  54. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/__init__.py +0 -0
  55. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/classic/__init__.py +0 -0
  56. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/classic/toa_to_unif.py +0 -0
  57. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/classic/unif_to_toa.py +0 -0
  58. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/loaders/__init__.py +0 -0
  59. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/loaders/maja_loader.py +0 -0
  60. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/loaders/product_loader.py +0 -0
  61. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/models/__init__.py +0 -0
  62. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/models/psf_conv_module.py +0 -0
  63. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/models/unif2surface.py +0 -0
  64. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/pipeline.py +0 -0
  65. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/__init__.py +0 -0
  66. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/_atmo_sampler.py +0 -0
  67. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/_brdf_sampler.py +0 -0
  68. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/_ensure.py +0 -0
  69. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/_smartg.py +0 -0
  70. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/radiatives.py +0 -0
  71. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/rho_atm.py +0 -0
  72. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/rho_toa.py +0 -0
  73. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/rho_toa_sym.py +0 -0
  74. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/sph_alb.py +0 -0
  75. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/sph_alb_brdf.py +0 -0
  76. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/tdif_down.py +0 -0
  77. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/tdif_up.py +0 -0
  78. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/tdif_up_brdf.py +0 -0
  79. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/tdir_down.py +0 -0
  80. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/samplers/tdir_up.py +0 -0
  81. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/scene_module.py +0 -0
  82. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/modules/scene_source.py +0 -0
  83. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/__init__.py +0 -0
  84. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/_combo_stage.py +0 -0
  85. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/_config.py +0 -0
  86. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/adam_optimizer.py +0 -0
  87. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/fit.py +0 -0
  88. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/landscape.py +0 -0
  89. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/lbfgs_optimizer.py +0 -0
  90. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/loss.py +0 -0
  91. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/metrics.py +0 -0
  92. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/optim/training_set.py +0 -0
  93. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/py.typed +0 -0
  94. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/reference/__init__.py +0 -0
  95. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/reference/wu2024.py +0 -0
  96. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/utils/__init__.py +0 -0
  97. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/utils/_config.py +0 -0
  98. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/utils/cache_store.py +0 -0
  99. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/utils/convolve.py +0 -0
  100. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/utils/radial.py +0 -0
  101. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/utils/smartgutils.py +0 -0
  102. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/utils/torchutils.py +0 -0
  103. {adjeff-1.2.0 → adjeff-1.2.2}/src/adjeff/utils/xrutils.py +0 -0
  104. {adjeff-1.2.0 → adjeff-1.2.2}/tests/_test_module.py +0 -0
  105. {adjeff-1.2.0 → adjeff-1.2.2}/tests/conftest.py +0 -0
  106. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_accessor.py +0 -0
  107. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_analysis.py +0 -0
  108. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_api_docs.py +0 -0
  109. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_api_psf.py +0 -0
  110. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_atmosphere_config.py +0 -0
  111. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_brdf_samplers.py +0 -0
  112. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_classic_modules.py +0 -0
  113. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_config.py +0 -0
  114. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_convolve.py +0 -0
  115. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_image_dict.py +0 -0
  116. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_image_generator.py +0 -0
  117. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_import.py +0 -0
  118. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_integration.py +0 -0
  119. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_landscape.py +0 -0
  120. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_loaders.py +0 -0
  121. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_logging.py +0 -0
  122. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_optim.py +0 -0
  123. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_psf.py +0 -0
  124. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_psf_tree.py +0 -0
  125. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_radial.py +0 -0
  126. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_scene_module.py +0 -0
  127. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_smartg_api.py +0 -0
  128. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_smartg_defaults.py +0 -0
  129. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_smartgutils.py +0 -0
  130. {adjeff-1.2.0 → adjeff-1.2.2}/tests/test_xrutils.py +0 -0
@@ -5,6 +5,24 @@ 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.2]
9
+
10
+ ### Fixed
11
+
12
+ - **The sweep store is named after the scene bound to it.** The scene is
13
+ passed to the physics outside xsweep's fingerprint, so two training
14
+ scenes sweeping the same space shared a store and the second read back
15
+ the first one's results. Introduced in 1.1.0, which is where the store
16
+ comes from.
17
+
18
+ ## [1.2.1]
19
+
20
+ ### Fixed
21
+
22
+ - **The sweep store is named after its statics too.** Two aerosol species
23
+ sweep the same axes and differ only by a static, so the second landed on
24
+ the first one's store and xsweep refused it.
25
+
8
26
  ## [1.2.0]
9
27
 
10
28
  ### Added
@@ -26,9 +44,8 @@ PSF fit being the case that motivated it.
26
44
  - **A cached sweep is written through to zarr and read back lazily.** It
27
45
  applies to the samplers whose output carries the image dims, and only
28
46
  when the module has a cache directory; a sweep returning a spectrum per
29
- state is still assembled in memory. Two consequences: the three training
30
- scenes of a fit share one Smart-G run instead of three, and the result a
31
- module hands on is dask-backed rather than numpy.
47
+ state is still assembled in memory. The result a module hands on is then
48
+ dask-backed rather than numpy.
32
49
  - **`batch_size` controls the batching.** It did not before: xsweep took
33
50
  the whole space in one call whatever its value. Measured on a 3999 pixel
34
51
  grid, a batch of 24 states costs five times the time of three batches of
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: adjeff
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Library that provides utilities to simulate adjacency effects.
5
5
  Project-URL: Homepage, https://github.com/walcark/Adjeff
6
6
  Project-URL: Issues, https://github.com/walcark/Adjeff/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "adjeff"
3
- version = "1.2.0"
3
+ version = "1.2.2"
4
4
  authors = [{ name = "walcark", email = "kevin.walcarius@gmail.com" }]
5
5
  readme = "README.md"
6
6
  description = "Library that provides utilities to simulate adjacency effects."
@@ -13,6 +13,7 @@ import functools
13
13
  import hashlib
14
14
  from typing import TYPE_CHECKING, Any, Callable, ClassVar
15
15
 
16
+ import joblib # type: ignore[import-untyped]
16
17
  import numpy as np
17
18
  import xarray as xr
18
19
  from xsweep import Sweeper, SweepPolicy
@@ -134,13 +135,13 @@ class SweepSampler(SceneModule):
134
135
  if spectral is not None:
135
136
  plan["bands"] = len(spectral.bands)
136
137
  self._log.info("sweep.plan", **plan)
137
- result = sweeper(space, policy=self._policy(space), **statics)
138
+ result = sweeper(space, policy=self._policy(space, bound), **statics)
138
139
  out: xr.DataArray = result[self._contract.outputs[0]]
139
140
  return out
140
141
 
141
142
  _SPATIAL: ClassVar[frozenset[str]] = frozenset({"x", "y", "x_psf", "y_psf"})
142
143
 
143
- def _policy(self, space: xr.Dataset) -> SweepPolicy:
144
+ def _policy(self, space: xr.Dataset, bound: dict[str, Any]) -> SweepPolicy:
144
145
  """Return the sweep policy.
145
146
 
146
147
  A sweep whose output carries the image dims is written to zarr as
@@ -154,21 +155,42 @@ class SweepSampler(SceneModule):
154
155
  return SweepPolicy(
155
156
  batch_size=self.batch_size,
156
157
  dedup=self.dedup,
157
- store=str(root / "sweeps" / self._store_name(space)),
158
+ store=str(root / "sweeps" / self._store_name(space, bound)),
158
159
  chunks=self._chunks(space),
159
160
  load=False,
160
161
  )
161
162
 
162
- def _store_name(self, space: xr.Dataset) -> str:
163
- """Return a store directory unique to the sampler and its grid.
163
+ def _store_name(self, space: xr.Dataset, bound: dict[str, Any]) -> str:
164
+ """Return a store directory unique to everything the results depend on.
164
165
 
165
- A store cannot be resumed once an axis has changed, so the axes are
166
- part of its name.
166
+ The axes and the statics, which xsweep fingerprints itself, and the
167
+ scene data bound to the physics, which it does not: two training
168
+ scenes sweep the same space and must not share a store.
167
169
  """
168
170
  axes = {str(d): space[d].values.tolist() for d in space.dims}
169
- digest = hashlib.sha1(repr(sorted(axes.items())).encode()).hexdigest()
171
+ statics = {k: repr(v) for k, v in self._statics().items()}
172
+ digest = hashlib.sha1(
173
+ repr(
174
+ (
175
+ sorted(axes.items()),
176
+ sorted(statics.items()),
177
+ sorted(self._bound_hashes(bound).items()),
178
+ )
179
+ ).encode()
180
+ ).hexdigest()
170
181
  return f"{type(self).__name__}-{digest[:8]}"
171
182
 
183
+ @staticmethod
184
+ def _bound_hashes(bound: dict[str, Any]) -> dict[str, str]:
185
+ """Return a stable hash of each scene object bound to the physics."""
186
+ hashes: dict[str, str] = {}
187
+ for name, value in bound.items():
188
+ if isinstance(value, (xr.Dataset, xr.DataArray)):
189
+ hashes[name] = str(joblib.hash(value))
190
+ else:
191
+ hashes[name] = repr(value)
192
+ return hashes
193
+
172
194
  def _chunks(self, space: xr.Dataset) -> dict[str, int]:
173
195
  """Return the chunk widths that give batches of ``batch_size`` states.
174
196
 
@@ -197,7 +197,7 @@ class TestSweepPolicy:
197
197
  remove_rayleigh=False,
198
198
  cache=CacheStore(tmp_path),
199
199
  )
200
- policy = sampler._policy(sampler._space())
200
+ policy = sampler._policy(sampler._space(), {})
201
201
  vec = {var.name for var in sampler._contract.vec}
202
202
  assert "RhoToaSymSampler" in str(policy.store)
203
203
  assert set(policy.chunks) <= vec
@@ -225,7 +225,7 @@ class TestSweepPolicy:
225
225
  batch_size=4,
226
226
  cache=CacheStore(tmp_path),
227
227
  )
228
- chunks = sampler._policy(sampler._space()).chunks
228
+ chunks = sampler._policy(sampler._space(), {}).chunks
229
229
  assert np.prod(list(chunks.values())) == 4
230
230
 
231
231
  def test_a_spectral_sweep_is_left_in_memory(self, tmp_path):
@@ -244,4 +244,64 @@ class TestSweepPolicy:
244
244
  remove_rayleigh=False,
245
245
  cache=CacheStore(tmp_path),
246
246
  )
247
- assert sampler._policy(sampler._space()).store is UNSET
247
+ assert sampler._policy(sampler._space(), {}).store is UNSET
248
+
249
+ def test_two_species_do_not_share_a_store(self, tmp_path):
250
+ from adjeff.modules.samplers import RhoToaSymSampler
251
+ from adjeff.utils import CacheStore
252
+
253
+ def name(species: str) -> str:
254
+ sampler = RhoToaSymSampler(
255
+ atmo_config=atmo.AtmoConfig(
256
+ aot=xr.DataArray([0.1, 0.2], dims=["aot"]),
257
+ rh=xr.DataArray([50.0], dims=["rh"]),
258
+ h=xr.DataArray([0.0], dims=["h"]),
259
+ href=xr.DataArray([2.0], dims=["href"]),
260
+ species={species: 1.0},
261
+ ),
262
+ geo_config=atmo.GeoConfig(
263
+ sza=xr.DataArray([40.0], dims=["sza"]),
264
+ vza=xr.DataArray([0.0], dims=["vza"]),
265
+ saa=xr.DataArray([0.0], dims=["saa"]),
266
+ vaa=xr.DataArray([0.0], dims=["vaa"]),
267
+ ),
268
+ remove_rayleigh=False,
269
+ cache=CacheStore(tmp_path),
270
+ )
271
+ return str(sampler._policy(sampler._space(), {}).store)
272
+
273
+ assert name("ammonium") != name("blackcar")
274
+
275
+ def test_two_scenes_do_not_share_a_store(self, tmp_path):
276
+ from adjeff.core import S2Band, gaussian_image_dict
277
+ from adjeff.modules.samplers import RhoToaSymSampler
278
+ from adjeff.utils import CacheStore
279
+
280
+ sampler = RhoToaSymSampler(
281
+ atmo_config=atmo.AtmoConfig(
282
+ aot=xr.DataArray([0.1, 0.2], dims=["aot"]),
283
+ rh=xr.DataArray([50.0], dims=["rh"]),
284
+ h=xr.DataArray([0.0], dims=["h"]),
285
+ href=xr.DataArray([2.0], dims=["href"]),
286
+ species={"sulphate": 1.0},
287
+ ),
288
+ geo_config=atmo.GeoConfig(
289
+ sza=xr.DataArray([40.0], dims=["sza"]),
290
+ vza=xr.DataArray([0.0], dims=["vza"]),
291
+ saa=xr.DataArray([0.0], dims=["saa"]),
292
+ vaa=xr.DataArray([0.0], dims=["vaa"]),
293
+ ),
294
+ remove_rayleigh=False,
295
+ cache=CacheStore(tmp_path),
296
+ )
297
+ band = S2Band.B02
298
+ scenes = [
299
+ gaussian_image_dict(sigma=s, res_km=0.5, bands=[band], n=9)[band]
300
+ for s in (1.0, 5.0)
301
+ ]
302
+ space = sampler._space()
303
+ stores = [
304
+ str(sampler._policy(space, {"rho_s": ds, "band": band}).store)
305
+ for ds in scenes
306
+ ]
307
+ assert stores[0] != stores[1]
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