marEx 4.1__tar.gz → 4.1.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.
- {marex-4.1 → marex-4.1.2}/PKG-INFO +2 -2
- {marex-4.1 → marex-4.1.2}/marEx/detect.py +8 -3
- {marex-4.1 → marex-4.1.2}/marEx/track.py +5 -1
- {marex-4.1 → marex-4.1.2}/marEx.egg-info/PKG-INFO +2 -2
- {marex-4.1 → marex-4.1.2}/marEx.egg-info/requires.txt +1 -1
- {marex-4.1 → marex-4.1.2}/pyproject.toml +1 -1
- {marex-4.1 → marex-4.1.2}/tests/test_gridded_preprocessing.py +58 -0
- {marex-4.1 → marex-4.1.2}/.codecov.yml +0 -0
- {marex-4.1 → marex-4.1.2}/.github/workflows/README.md +0 -0
- {marex-4.1 → marex-4.1.2}/.github/workflows/ci.yml +0 -0
- {marex-4.1 → marex-4.1.2}/.github/workflows/release.yml +0 -0
- {marex-4.1 → marex-4.1.2}/.github/workflows/validate.py +0 -0
- {marex-4.1 → marex-4.1.2}/.readthedocs.yaml +0 -0
- {marex-4.1 → marex-4.1.2}/CITATION.cff +0 -0
- {marex-4.1 → marex-4.1.2}/CONTRIBUTING.md +0 -0
- {marex-4.1 → marex-4.1.2}/MANIFEST.in +0 -0
- {marex-4.1 → marex-4.1.2}/README.md +0 -0
- {marex-4.1 → marex-4.1.2}/examples/batch jobs/run_detect.py +0 -0
- {marex-4.1 → marex-4.1.2}/examples/batch jobs/run_track.py +0 -0
- {marex-4.1 → marex-4.1.2}/examples/batch jobs/submit_track.sh +0 -0
- {marex-4.1 → marex-4.1.2}/examples/gridded data/01_preprocess_extremes.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/examples/gridded data/02_id_track_events.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/examples/gridded data/03_visualise_events.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/examples/regional data/01_preprocess_extremes.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/examples/regional data/02_id_track_events.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/examples/regional data/03_visualise_events.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/examples/unstructured data/01_preprocess_extremes.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/examples/unstructured data/02_id_track_events.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/examples/unstructured data/03_visualise_events.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/__init__.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/_coverage_init.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/_dependencies.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/exceptions.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/helper.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/logging_config.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/plotX/__init__.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/plotX/base.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/plotX/gridded.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx/plotX/unstructured.py +0 -0
- {marex-4.1 → marex-4.1.2}/marEx.egg-info/SOURCES.txt +0 -0
- {marex-4.1 → marex-4.1.2}/marEx.egg-info/dependency_links.txt +0 -0
- {marex-4.1 → marex-4.1.2}/marEx.egg-info/top_level.txt +0 -0
- {marex-4.1 → marex-4.1.2}/setup.cfg +0 -0
- {marex-4.1 → marex-4.1.2}/tests/__init__.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/conftest.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/coverage_config.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/make_test_data.ipynb +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_detect_detrending.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_detect_helpers.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_error_handling.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_exceptions.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_gridded_tracking.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_integration.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_logging_system.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_plotx.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_track_edge_cases.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_track_helpers.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_unstructured_preprocessing.py +0 -0
- {marex-4.1 → marex-4.1.2}/tests/test_unstructured_tracking.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: marEx
|
|
3
|
-
Version: 4.1
|
|
3
|
+
Version: 4.1.2
|
|
4
4
|
Summary: Marine Extremes Detection and Tracking
|
|
5
5
|
Author-email: Aaron Wienkers <aaron@wienkers.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/wienkers/marEx
|
|
@@ -21,7 +21,7 @@ Requires-Python: >=3.10
|
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
Requires-Dist: numpy
|
|
23
23
|
Requires-Dist: xarray[complete]
|
|
24
|
-
Requires-Dist: dask[complete]
|
|
24
|
+
Requires-Dist: dask[complete]>=2025.9
|
|
25
25
|
Requires-Dist: netcdf4
|
|
26
26
|
Requires-Dist: scipy
|
|
27
27
|
Requires-Dist: scikit-image
|
|
@@ -819,9 +819,14 @@ def preprocess_data(
|
|
|
819
819
|
ds["mask"] = ds.mask.compute()
|
|
820
820
|
ds["dat_anomaly"] = fix_dask_tuple_array(ds.dat_anomaly)
|
|
821
821
|
|
|
822
|
-
# Patch for same dask-Zarr bug:
|
|
823
|
-
|
|
824
|
-
|
|
822
|
+
# Patch for same dask-Zarr bug: materialise *any* remaining Dask-backed coordinates.
|
|
823
|
+
# Auxiliary (non-index) coordinates (e.g. gridded `x`/`y` or unstructured `lon`/`lat`)
|
|
824
|
+
# become Dask-backed via `.chunk()` and otherwise retain tuple chunk references that
|
|
825
|
+
# break distributed serialisation on save. This is seen as an
|
|
826
|
+
# "AttributeError: 'tuple' object has no attribute 'size'" from dask.array.store.
|
|
827
|
+
for coord_name in list(ds.coords):
|
|
828
|
+
if is_dask_collection(ds[coord_name].data):
|
|
829
|
+
ds[coord_name] = ds[coord_name].compute()
|
|
825
830
|
if "neighbours" in ds.data_vars:
|
|
826
831
|
ds["neighbours"] = ds.neighbours.compute()
|
|
827
832
|
if "cell_areas" in ds.data_vars:
|
|
@@ -1612,7 +1612,11 @@ class tracker:
|
|
|
1612
1612
|
# Generate structuring element (disk-shaped)
|
|
1613
1613
|
y, x = np.ogrid[-R_fill : R_fill + 1, -R_fill : R_fill + 1]
|
|
1614
1614
|
r = x**2 + y**2
|
|
1615
|
-
|
|
1615
|
+
# Pad/trim width for the closing+opening morphology. Closing has reach 2*R_fill and the
|
|
1616
|
+
# subsequent opening another 2*R_fill, so the total reach is 4*R_fill. Padding by only
|
|
1617
|
+
# 2*R_fill (the old value) leaves an under-padded artifact in the 2R-4R band around the
|
|
1618
|
+
# periodic-longitude seam. (Matches the clean_up-branch EDT fill_holes, which pads 4*R_fill.)
|
|
1619
|
+
diameter = 4 * R_fill
|
|
1616
1620
|
se_kernel = r < (R_fill**2) + 1
|
|
1617
1621
|
mode = "wrap" if not self.regional_mode else "edge"
|
|
1618
1622
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: marEx
|
|
3
|
-
Version: 4.1
|
|
3
|
+
Version: 4.1.2
|
|
4
4
|
Summary: Marine Extremes Detection and Tracking
|
|
5
5
|
Author-email: Aaron Wienkers <aaron@wienkers.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/wienkers/marEx
|
|
@@ -21,7 +21,7 @@ Requires-Python: >=3.10
|
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
Requires-Dist: numpy
|
|
23
23
|
Requires-Dist: xarray[complete]
|
|
24
|
-
Requires-Dist: dask[complete]
|
|
24
|
+
Requires-Dist: dask[complete]>=2025.9
|
|
25
25
|
Requires-Dist: netcdf4
|
|
26
26
|
Requires-Dist: scipy
|
|
27
27
|
Requires-Dist: scikit-image
|
|
@@ -846,3 +846,61 @@ class TestGriddedPreprocessing:
|
|
|
846
846
|
dimensions=self.dimensions,
|
|
847
847
|
dask_chunks=self.dask_chunks,
|
|
848
848
|
)
|
|
849
|
+
|
|
850
|
+
def test_auxiliary_coords_are_materialised(self, tmp_path):
|
|
851
|
+
"""Regression: auxiliary (non-index) coordinates must not remain Dask-backed.
|
|
852
|
+
|
|
853
|
+
Datasets often carry auxiliary spatial coordinates (e.g. integer grid
|
|
854
|
+
indices ``x``/``y`` alongside ``lon``/``lat``). ``.chunk()`` turns these
|
|
855
|
+
into Dask arrays, and prior to the fix they survived ``preprocess_data``
|
|
856
|
+
un-materialised. Their lazy graphs retain tuple chunk references that
|
|
857
|
+
raise ``AttributeError: 'tuple' object has no attribute 'size'`` when the
|
|
858
|
+
result is written under a distributed scheduler (e.g. ``to_netcdf`` on a
|
|
859
|
+
``LocalCluster``). ``preprocess_data`` must therefore compute every
|
|
860
|
+
remaining Dask-backed coordinate. This invariant is scheduler- and
|
|
861
|
+
Dask-version-independent, so it is checked directly here.
|
|
862
|
+
"""
|
|
863
|
+
import dask
|
|
864
|
+
import pandas as pd
|
|
865
|
+
|
|
866
|
+
times = pd.date_range("2000-01-01", "2002-12-31", freq="D")
|
|
867
|
+
nlat, nlon = 4, 4
|
|
868
|
+
da = xr.DataArray(
|
|
869
|
+
np.random.default_rng(7).standard_normal((len(times), nlat, nlon)).astype(np.float32),
|
|
870
|
+
dims=["time", "lat", "lon"],
|
|
871
|
+
coords={
|
|
872
|
+
"time": times,
|
|
873
|
+
"lat": np.arange(nlat, dtype=np.float32),
|
|
874
|
+
"lon": np.arange(nlon, dtype=np.float32),
|
|
875
|
+
# Auxiliary, non-index coordinates that become Dask-backed via .chunk()
|
|
876
|
+
"x": ("lon", np.arange(nlon, dtype=np.int64)),
|
|
877
|
+
"y": ("lat", np.arange(nlat, dtype=np.int64)),
|
|
878
|
+
},
|
|
879
|
+
).chunk({"time": -1})
|
|
880
|
+
|
|
881
|
+
# Sanity check: the aux coords are genuinely Dask-backed on the input
|
|
882
|
+
assert dask.base.is_dask_collection(da.coords["x"].data)
|
|
883
|
+
assert dask.base.is_dask_collection(da.coords["y"].data)
|
|
884
|
+
|
|
885
|
+
result = marEx.preprocess_data(
|
|
886
|
+
da,
|
|
887
|
+
method_anomaly="fixed_baseline",
|
|
888
|
+
method_extreme="global_extreme",
|
|
889
|
+
threshold_percentile=95,
|
|
890
|
+
dimensions={"time": "time", "x": "lon", "y": "lat"},
|
|
891
|
+
dask_chunks={"time": 25},
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
# No coordinate in the output may remain Dask-backed (the actual fix)
|
|
895
|
+
dask_coords = [name for name in result.coords if dask.base.is_dask_collection(result[name].data)]
|
|
896
|
+
assert dask_coords == [], f"Output coordinates still Dask-backed: {dask_coords}"
|
|
897
|
+
|
|
898
|
+
# The aux coords must still be present and carry the right values
|
|
899
|
+
assert "x" in result.coords and "y" in result.coords
|
|
900
|
+
np.testing.assert_array_equal(result["x"].values, np.arange(nlon, dtype=np.int64))
|
|
901
|
+
np.testing.assert_array_equal(result["y"].values, np.arange(nlat, dtype=np.int64))
|
|
902
|
+
|
|
903
|
+
# End-to-end: the result must be writable to NetCDF
|
|
904
|
+
out_path = tmp_path / "aux_coords_roundtrip.nc"
|
|
905
|
+
result.to_netcdf(out_path)
|
|
906
|
+
assert out_path.exists()
|
|
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
|