Skip to content

Commit 0ca1ab1

Browse files
authored
Fix CI to use correct python versions (#463)
* Fix CI to use correct python versions * Remove broken test - remove pydap from env dev - pin python >= 3.11 * Adapt ci to latest pins * rewrite gen_mask_from_weights without scipy * Fix spatialaverager test - add doc to gen mask - fix coverage conf * update changes * Fix the fix
1 parent 8276035 commit 0ca1ab1

File tree

8 files changed

+36
-58
lines changed

8 files changed

+36
-58
lines changed

.github/workflows/ci.yaml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,12 @@ jobs:
2222
matrix:
2323
include:
2424
# Warning: Unless in quotations, numbers below are read as floats. 3.10 < 3.2
25-
- python-version: "3.9"
26-
esmf-version: 8.3
27-
- python-version: "3.10"
28-
esmf-version: 8.4
2925
- python-version: "3.11"
30-
esmf-version: 8.6
26+
esmf-version: 8.4
3127
- python-version: "3.12"
32-
esmf-version: 8.7
28+
esmf-version: 8.6
3329
- python-version: "3.13"
34-
esmf-version: 8.8
30+
esmf-version: 8.9
3531
steps:
3632
- name: Cancel previous runs
3733
uses: styfle/[email protected]
@@ -45,7 +41,7 @@ jobs:
4541
cache-downloads: true
4642
micromamba-version: "latest"
4743
environment-file: ci/environment.yml
48-
extra-specs: |
44+
create-args: >-
4945
python=${{ matrix.python-version }}
5046
esmpy=${{ matrix.esmf-version }}
5147
- name: Fix env for esmpy 8.4
@@ -66,7 +62,7 @@ jobs:
6662
- name: Upload coverage to Codecov
6763
uses: codecov/[email protected]
6864
with:
69-
file: ./coverage.xml
65+
files: ./coverage.xml
7066
fail_ci_if_error: false
7167

7268
upstream-dev:
@@ -87,8 +83,6 @@ jobs:
8783
cache-downloads: true
8884
micromamba-version: "latest"
8985
environment-file: ci/environment-upstream-dev.yml
90-
extra-specs: |
91-
python=3.10
9286
- name: Install Xesmf (editable)
9387
run: |
9488
python -m pip install -e .

CHANGES.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
What's new
22
==========
33

4+
0.9.2 (2025-11-27)
5+
------------------
6+
This release drops support for Python < 3.11. xESMF aims to preserve support for older python and ESMF version as long as possible with its reduced maintaining team. The most recent windows release of ESMF is currently 8.4.2 and new versions of xESMF will support it as long as it is not updated. All fixes in :pull:`463`, by `Pascal Bourgault <https://github.com/aulemahal>`_.
7+
8+
* Rewrote ``xe.smm.gen_mask_from_weights`` to remove scipy-dependent code.
9+
* Fix the CI reenable testing with previous python versions.
10+
* Avoid a ``SpatialAverager`` bug that happens when polygon segments have a length of exactly 1 on ESMF 8.4.2. The bug is not actually fixed in xESMF, but "segmentizing" the polygons with 0.99 seems to fix the issue.
11+
412
0.9.1 (2025-11-25)
513
------------------
614
* Rewrote ``xe.smm.add_nans_to_weight`` (called when ``unmapped_to_nan`` is True)to remove scipy-dependent code, which also resulted in a significant (>=4x) speedup of that step (:pull:`461`). By `Pascal Bourgault <https://github.com/aulemahal>`_.

ci/environment-upstream-dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ dependencies:
66
- codecov
77
- dask
88
- esmpy
9+
- netcdf4
910
- numba
1011
- numpy
1112
- pip
1213
- pre-commit
13-
- pydap
1414
- pytest
1515
- pytest-cov
1616
- shapely

ci/environment.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ name: xesmf
22
channels:
33
- conda-forge
44
dependencies:
5-
- python>=3.8
5+
- python>=3.11
66
- cf_xarray>=0.5.1
7+
- dask
78
- esmpy
89
- numba >=0.55.2
910
- numpy >=1.16
@@ -13,9 +14,8 @@ dependencies:
1314
# Testing and extras
1415
- cftime
1516
- codecov
16-
- dask
17+
- netcdf4
1718
- pip
1819
- pre-commit
19-
- pydap
2020
- pytest
2121
- pytest-cov

pyproject.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,14 @@ license = { text = "MIT" }
1313
authors = [
1414
{ name = "Jiawei Zhuang", email = "[email protected]" },
1515
]
16-
requires-python = ">=3.8"
16+
requires-python = ">=3.11"
1717
classifiers = [
1818
"Development Status :: 4 - Beta",
1919
"Intended Audience :: Science/Research",
2020
"License :: OSI Approved :: MIT License",
2121
"Operating System :: OS Independent",
2222
"Programming Language :: Python",
2323
"Programming Language :: Python :: 3 :: Only",
24-
"Programming Language :: Python :: 3.8",
25-
"Programming Language :: Python :: 3.9",
26-
"Programming Language :: Python :: 3.10",
2724
"Programming Language :: Python :: 3.11",
2825
"Programming Language :: Python :: 3.12",
2926
"Programming Language :: Python :: 3.13",

xesmf/smm.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -354,30 +354,34 @@ def mask_source_indices(weights, source_indices_to_mask):
354354

355355
def gen_mask_from_weights(weights, nlat, nlon):
356356
"""Generate a 2D mask from the regridding weights sparse matrix.
357+
357358
This function will generate a 2D binary mask out of a regridding weights sparse matrix.
359+
The mask shows which pixels of the output grid are mapped in the input grid.
360+
This is only feasible when the regridder weights have been created with ``unmapped_to_nan=True``.
361+
358362
359363
Parameters
360364
----------
361365
weights : DataArray backed by a sparse.COO array
362-
Sparse weights matrix.
366+
Sparse weights matrix, as computed by the `Regridder`.
367+
nlat, nlon: int
368+
Shape of the final matrix.
369+
nlat * nlon must be equal to the size of the `out_dim` dimension of the weights.
363370
364371
Returns
365372
-------
366373
numpy.ndarray of type numpy.int32 and of shape (nlat, nlon)
367374
Binary mask.
375+
376+
Examples
377+
--------
378+
This function acts on lower level inputs, but the regridder has all information needed to construct a DataArray.
379+
380+
>>> reg = xe.Regridder(ds_in, ds_out, 'bilinear', unmapped_to_nan=True)
381+
>>> mask = xe.smm.gen_mask_from_weights(reg.weights, *reg.shape_out)
382+
>>> mask_da = xr.DataArray(mask, dims=reg.out_horiz_dims, coords=reg.out_coords.coords)
368383
"""
369-
# Taken from @trondkr and adapted by @raphaeldussin to use `lil`.
370-
# lil matrix is better than CSR when changing sparsity
371-
m = weights.data.to_scipy_sparse().tolil()
372-
373-
# Create mask ndarray of ones and fill with 0-elements
374-
mask = np.ones((nlat, nlon), dtype=np.int32).ravel()
375-
for krow in range(len(m.rows)):
376-
if any([np.isnan(x) for x in m.data[krow]]):
377-
mask[krow] = 0
378-
379-
# Reshape and return
380-
return mask.reshape((nlat, nlon))
384+
return 1 * (~weights.isnull().any('in_dim').data.todense().reshape(nlat, nlon))
381385

382386

383387
def _combine_weight_multipoly(weights, areas, indexes):

xesmf/tests/test_frontend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
def _segmentize(p):
101101
if isinstance(p, list):
102102
return list(map(_segmentize, p))
103-
return segmentize(p, 1)
103+
return segmentize(p, 0.99)
104104

105105

106106
polys = list(map(_segmentize, polys_raw))

xesmf/tests/test_oceanmodels.py

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -78,28 +78,3 @@ def test_mom6like_to_5x5():
7878
with pytest.warns(UserWarning, match=r"Using dimensions \('yh', 'xh'\)"):
7979
tos_regridded = regrid_to_5x5(mom6like['tos'])
8080
assert tos_regridded.shape == ((2, 36, 72))
81-
82-
83-
@pytest.mark.testcases
84-
def test_mom6_to_1x1():
85-
"""check regridding of MOM6 to regular lon/lat"""
86-
87-
dataurl = 'http://35.188.34.63:8080/thredds/dodsC/OM4p5/'
88-
mom6 = xr.open_dataset(
89-
f'{dataurl}/ocean_monthly_z.200301-200712.nc4',
90-
chunks={'time': 1, 'z_l': 1},
91-
drop_variables=['average_DT', 'average_T1', 'average_T2'],
92-
decode_times=False,
93-
engine='pydap',
94-
)
95-
96-
grid_1x1 = xr.Dataset()
97-
grid_1x1['lon'] = xr.DataArray(data=0.5 + np.arange(360), dims=('x'))
98-
grid_1x1['lat'] = xr.DataArray(data=0.5 - 90 + np.arange(180), dims=('y'))
99-
100-
regrid_to_1x1 = xesmf.Regridder(
101-
mom6.rename({'geolon': 'lon', 'geolat': 'lat'}), grid_1x1, 'bilinear', periodic=True
102-
)
103-
104-
thetao_1x1 = regrid_to_1x1(mom6['thetao'])
105-
assert np.allclose(thetao_1x1.isel(time=0, z_l=0, x=200, y=100).values, 27.15691922)

0 commit comments

Comments
 (0)