Skip to content
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
8930af0
add numpy 2 from conda-forge dev
valeriupredoi Apr 12, 2024
a3e0632
temp remove PyPI numpy - add numpy 2 from conda-forge dev
valeriupredoi Apr 12, 2024
233eb67
run a GA
valeriupredoi Apr 12, 2024
3687ea0
remove numpy from env
valeriupredoi Apr 12, 2024
bf8dff5
manually install numpy 2.0
valeriupredoi Apr 12, 2024
29284bc
correct conda forge channel
valeriupredoi Apr 15, 2024
23429bc
correct conda forge channel
valeriupredoi Apr 15, 2024
50910b4
do not install manually numpy, like a peasant
valeriupredoi Apr 15, 2024
4d5d1eb
revert to manual installation
valeriupredoi Apr 15, 2024
9fb584a
comment out numpy gubbins i environment yml
valeriupredoi Apr 15, 2024
91a87ed
Merge branch 'main' into numpy2-dev
valeriupredoi May 7, 2024
6eb3d00
Merge branch 'main' into numpy2-dev
valeriupredoi May 10, 2024
ade96ca
Merge branch 'main' into numpy2-dev
valeriupredoi May 13, 2024
ac75b84
Merge branch 'main' into numpy2-dev
valeriupredoi May 27, 2024
d36350a
deactivate direct download
valeriupredoi May 27, 2024
5dfa45a
use direct download again
valeriupredoi May 27, 2024
baedee8
use direct download again
valeriupredoi May 27, 2024
5f91615
Merge branch 'main' into numpy2-dev
valeriupredoi Jun 6, 2024
038099d
Merge branch 'main' into numpy2-dev
valeriupredoi Jun 17, 2024
ccb8f2f
use conda forge package
valeriupredoi Jun 17, 2024
882c1cd
dont install from conda package
valeriupredoi Jun 17, 2024
6d9cda5
restore numpy dep
valeriupredoi Jun 17, 2024
e3e2e25
completeley remove line that installed numpy2 from anaconda package
valeriupredoi Jun 17, 2024
30c99e5
unpin pandas
valeriupredoi Jun 19, 2024
98ee116
unpin pandas
valeriupredoi Jun 19, 2024
0e2799b
release numpy to choose its path
valeriupredoi Jun 19, 2024
03a46fa
replace np.NaN with np.nan
valeriupredoi Jun 19, 2024
af8944e
replace np.NaN with np.nan
valeriupredoi Jun 19, 2024
6a8fe23
fix for new output of np.ogrid
valeriupredoi Jun 19, 2024
e849a73
add printout to test
valeriupredoi Jun 19, 2024
3ec3fe5
explicitly cast Python int
valeriupredoi Jun 20, 2024
26a5228
use vstack instead of simple array
valeriupredoi Jun 20, 2024
ecf68f8
make list out of now tuple
valeriupredoi Jun 20, 2024
c10ce96
explicit cast to numpy strings ffs
valeriupredoi Jun 20, 2024
f822bb8
reqwite the distance stat with sqrt account for masks
valeriupredoi Jun 21, 2024
061a4fb
account for variations in rstol for numpy 2.0
valeriupredoi Jun 21, 2024
a4707ef
add note on sqrt bug
valeriupredoi Jun 21, 2024
266781a
temprary pin numpy<2
valeriupredoi Jun 21, 2024
73d455b
temprary pin numpy<2
valeriupredoi Jun 21, 2024
3007575
unpin and let numpy be two
valeriupredoi Jun 21, 2024
7386b7e
unpin and let numpy be two
valeriupredoi Jun 21, 2024
bf01de0
Merge branch 'main' into numpy2-dev
valeriupredoi Jun 24, 2024
043407a
use the force and xfail the icon test instead
valeriupredoi Jun 24, 2024
a3ccff6
remove commented out pandas pins
valeriupredoi Jun 24, 2024
aefb52c
remove commented out pandas pins
valeriupredoi Jun 24, 2024
1741727
unrun GA
valeriupredoi Jun 24, 2024
4afc351
Update esmvalcore/preprocessor/_compare_with_refs.py
valeriupredoi Jul 3, 2024
6464ecf
Merge branch 'main' into numpy2-dev
valeriupredoi Jul 3, 2024
85b2aa4
remove numpy2 related comment
valeriupredoi Jul 3, 2024
05dea66
remove numpy2 related comment
valeriupredoi Jul 3, 2024
64da79d
Merge branch 'main' into numpy2-dev
valeriupredoi Jul 8, 2024
ea3c702
Merge branch 'main' into numpy2-dev
valeriupredoi Jul 9, 2024
0b2e975
Merge branch 'main' into numpy2-dev
valeriupredoi Aug 5, 2024
b345843
Merge branch 'main' into numpy2-dev
valeriupredoi Sep 13, 2024
55edd79
Merge branch 'main' into numpy2-dev
valeriupredoi Sep 19, 2024
400ccc4
Merge branch 'main' into numpy2-dev
valeriupredoi Sep 27, 2024
e6073eb
run precommit twice
valeriupredoi Sep 27, 2024
803017d
run precommit twice
valeriupredoi Sep 27, 2024
0ee7dcd
Merge branch 'main' into numpy2-dev
valeriupredoi Oct 24, 2024
c4cb2f1
unpin numpy
valeriupredoi Oct 24, 2024
9c5eace
well I have just found one of those rare occasions when merge resulte…
valeriupredoi Oct 24, 2024
519769c
Merge branch 'main' into numpy2-dev
valeriupredoi Oct 30, 2024
fb6cdc3
Merge branch 'main' into numpy2-dev
valeriupredoi Nov 5, 2024
ef6c154
Merge branch 'main' into numpy2-dev
valeriupredoi Nov 11, 2024
dddc083
pin iris
valeriupredoi Nov 11, 2024
858ae11
pin iris
valeriupredoi Nov 11, 2024
e636c7b
rerun GA tests see all is fine
valeriupredoi Nov 11, 2024
acf7838
unrun GA
valeriupredoi Nov 11, 2024
801b7d0
change inline comment
valeriupredoi Nov 11, 2024
bab038d
change inline comment
valeriupredoi Nov 11, 2024
d59d606
cleanup
valeriupredoi Nov 11, 2024
b81db5f
cleanup
valeriupredoi Nov 11, 2024
3bd0468
Update esmvalcore/preprocessor/_regrid_esmpy.py
valeriupredoi Nov 11, 2024
19d7700
Update pyproject.toml
valeriupredoi Nov 11, 2024
8adcd1b
Update environment.yml
valeriupredoi Nov 11, 2024
f11ad18
Update tests/unit/preprocessor/_compare_with_refs/test_compare_with_r…
valeriupredoi Nov 11, 2024
e81c026
revert to old syntax to recheck
valeriupredoi Nov 11, 2024
c99f262
Don't use int8
schlunma Nov 11, 2024
280e692
Restore nice error string when invalid season(s) are given
schlunma Nov 11, 2024
24f2549
Cast 360 to original dtype
schlunma Nov 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ dependencies:
- nc-time-axis
- nested-lookup
- netcdf4
- numpy !=1.24.3,<2.0.0 # avoid pulling 2.0.0rcX
- numpy !=1.24.3
- packaging
- pandas !=2.2.0,!=2.2.1,!=2.2.2 # github.com/ESMValGroup/ESMValCore/pull/2305 and #2349
- pandas
- pillow
- pip !=21.3
- prov
Expand Down
16 changes: 15 additions & 1 deletion esmvalcore/preprocessor/_compare_with_refs.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,21 @@ def _calculate_rmse(
weights = get_weights(cube, coords) if weighted else None
squared_error = (cube.core_data() - reference.core_data())**2
npx = get_array_module(squared_error)
rmse = npx.sqrt(npx.ma.average(squared_error, axis=axis, weights=weights))

# need masked sqrt for numpy >=2.0
# and consequently dask.array.reductions.safe_sqrt for Dask
# otherwise results will be computed ignoring masks
# see https://github.com/numpy/numpy/issues/25635
# and https://docs.dask.org/en/stable/_modules/dask/array/reductions.html
if npx.__name__ == "dask.array":
da_squared_error = npx.ma.average(squared_error,
axis=axis,
weights=weights)
rmse = npx.reductions.safe_sqrt(da_squared_error)
else:
rmse = npx.ma.sqrt(
npx.ma.average(squared_error, axis=axis, weights=weights)
)

# Metadata
metadata = CubeMetadata(
Expand Down
2 changes: 1 addition & 1 deletion esmvalcore/preprocessor/_derive/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def _create_pressure_array(cube, ps_cube, top_limit):
ps_4d_array = iris.util.broadcast_to_shape(ps_cube.data, shape, [0, 2, 3])

# Set pressure levels below the surface pressure to NaN
pressure_4d = np.where((ps_4d_array - p_4d_array) < 0, np.NaN, p_4d_array)
pressure_4d = np.where((ps_4d_array - p_4d_array) < 0, np.nan, p_4d_array)

# Make top_limit last pressure level
top_limit_array = np.full(ps_cube.shape, top_limit, dtype=np.float32)
Expand Down
2 changes: 2 additions & 0 deletions esmvalcore/preprocessor/_derive/co2s.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def _get_first_unmasked_data(array, axis):
indices = da.meshgrid(
*[da.arange(array.shape[i]) for i in range(array.ndim) if i != axis],
indexing='ij')
# numpy>=2.0 indices becomes a tuple
indices = list(indices)
indices.insert(axis, indices_first_positive)
first_unmasked_data = np.array(array)[tuple(indices)]
return first_unmasked_data
Expand Down
5 changes: 4 additions & 1 deletion esmvalcore/preprocessor/_regrid_esmpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,10 @@ def get_grid(esmpy_lat, esmpy_lon,
num_peri_dims = 1
else:
num_peri_dims = 0
grid = esmpy.Grid(np.array(esmpy_lat.shape),

# previous to numpy 2.0, a np.array(esmpy_lat.shape) was used
# numpy>=2.0 throws ValueError: matrix transpose with ndim<2 is undefined
grid = esmpy.Grid(np.vstack(esmpy_lat.shape),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this backwards-compatible? If yes, do we need this comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it is, functionally and it returns the same sort of structure, only typed differently

num_peri_dims=num_peri_dims,
staggerloc=[esmpy.StaggerLoc.CENTER])
grid.get_coords(ESMF_LON)[...] = esmpy_lon
Expand Down
7 changes: 5 additions & 2 deletions esmvalcore/preprocessor/_regrid_unstructured.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,11 @@ def _get_weights_and_idx(
src_points_with_convex_hull = self._add_convex_hull_twice(
src_points, hull.vertices
)
src_points_with_convex_hull[-2 * n_hull:-n_hull, 1] -= 360
src_points_with_convex_hull[-n_hull:, 1] += 360
# explicitly casting to int32 since without it, in Numpy 2.0
# one gets OverflowError: Python integer 360 out of bounds for int8
# see notes https://numpy.org/devdocs/numpy_2_0_migration_guide.html
src_points_with_convex_hull[-2 * n_hull:-n_hull, 1] -= np.int32(360)
src_points_with_convex_hull[-n_hull:, 1] += np.int32(360)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work with 360.0 instead of np.int32(360)? The points should be floats, not integers, so to me this makes much more sense. If this works, you can also remove the comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure, will test - but I agree about points' types indeed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nopers, a float there throws this:

>       src_points_with_convex_hull[-2 * n_hull:-n_hull, 1] -= 360.
E       numpy._core._exceptions._UFuncOutputCastingError: Cannot cast ufunc 'subtract' output from dtype('float64') to dtype('int8') with casting rule 'same_kind'

so we either np.int32 the 360 or we np.float the left member

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I still don't understand this. When I run the tests (numpy < 2), src_points_with_convex_hull is an array with dtype float64 as expected by the CMOR standards. Numpy >= 2 should NOT change that. It would be wrong if this array is of dtype int.

I just tested this locally with dummy arrays and it works with 360 and 360.0 (the latter would be "more" correct). Maybe this was just a problem of an early numpy version. Would it be able to test this again?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, the test still fails - it's looking at it as int8 by default, hence my workaround to cast it to int32

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@schlunma directly referencing you since I can't see my comment on the main PR page for some reason (and maybe you can't either, thanks, GitHub)

Copy link
Contributor

@schlunma schlunma Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I found the problem, was looking at the wrong test before. It looks like the default dtype for test cubes and their coords is int8...I think it would make sense to change this to something more realistic like int32 or int (float might break other tests). There are other tests to that use that, let me try to fix them too. Can I push to this branch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolutely, please do! 🍺


# Actual weights calculation
(weights, indices) = self._calculate_weights(
Expand Down
4 changes: 3 additions & 1 deletion esmvalcore/preprocessor/_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -1559,7 +1559,9 @@ def _transform_to_lst_eager(
"""
# Apart from the time index, all other dimensions will stay the same; this
# is ensured with np.ogrid
idx = np.ogrid[tuple(slice(0, d) for d in data.shape)]
# NOTE: in numpy 2.0 np.ogrid returns ndarray or tuple of ndarrays
# so item assignment can only be done after converting to a list
idx = list(np.ogrid[tuple(slice(0, d) for d in data.shape)])
time_index = broadcast_to_shape(
time_index, data.shape, (time_dim, lon_dim)
)
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@
'nc-time-axis', # needed by iris.plot
'nested-lookup',
'netCDF4',
'numpy!=1.24.3,<2.0.0', # avoid pulling 2.0.0rc1
'numpy!=1.24.3',
'packaging',
'pandas!=2.2.0,!=2.2.1,!=2.2.2', # GH #2305 #2349 etc
'pandas',
'pillow',
'prov',
'psutil',
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/cmor/_fixes/icon/test_icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,9 @@ def test_invalid_time_units(cubes_2d):
# Test fix with (sub-)hourly data


# remove xfail when https://github.com/pandas-dev/pandas/issues/57002
# gets fixed; pinning pandas not a viable solution due deps issues
@pytest.mark.xfail(reason='Bug in pandas needs be fixed.')
def test_hourly_data(cubes_2d):
"""Test fix."""
fix = get_allvars_fix('Amon', 'tas')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
from tests import PreprocessorFile


def assert_allclose(array_1, array_2):
def assert_allclose(array_1, array_2, rtol=1e-7):
"""Assert that (masked) array 1 is close to (masked) array 2."""
if np.ma.is_masked(array_1) or np.ma.is_masked(array_2):
mask_1 = np.ma.getmaskarray(array_1)
mask_2 = np.ma.getmaskarray(array_2)
np.testing.assert_equal(mask_1, mask_2)
np.testing.assert_allclose(array_1[~mask_1], array_2[~mask_2])
np.testing.assert_allclose(array_1[~mask_1], array_2[~mask_2], rtol)
else:
np.testing.assert_allclose(array_1, array_2)
np.testing.assert_allclose(array_1, array_2, rtol)


def products_set_to_dict(products):
Expand Down Expand Up @@ -445,7 +445,10 @@ def test_distance_metric(
out_cube = product_ref.cubes[0]
assert out_cube.shape == ()
assert out_cube.dtype == np.float32
assert_allclose(out_cube.data, ref_data)
# an rtol=1e-6 is needed for numpy >=2.0
assert_allclose(out_cube.data,
np.array(ref_data, dtype=np.float32),
rtol=1e-6)
assert out_cube.var_name == var_name
assert out_cube.long_name == long_name
assert out_cube.standard_name is None
Expand Down Expand Up @@ -620,7 +623,8 @@ def test_distance_metric_masked_data(
expected_data = np.ma.masked_invalid(data)
else:
expected_data = np.array(data, dtype=np.float32)
assert_allclose(out_cube.data, expected_data)
# an rtol=1e-6 is needed for numpy >=2.0
assert_allclose(out_cube.data, expected_data, rtol=1e-6)
assert out_cube.var_name == var_name
assert out_cube.long_name == long_name
assert out_cube.standard_name is None
Expand Down Expand Up @@ -661,6 +665,7 @@ def test_distance_metric_fully_masked_data(
assert out_cube.dtype == np.float64

expected_data = np.ma.masked_all(())
print("out/in/metric", out_cube.data, expected_data, metric)
assert_allclose(out_cube.data, expected_data)
assert out_cube.var_name == var_name
assert out_cube.long_name == long_name
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/preprocessor/_derive/test_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def test_low_lev_below_surf_press():
"""Test for lowest level below surface pressure."""
plev = 970
top_limit = 5
col = np.array([np.NaN, 900, 800])
col = np.array([np.nan, 900, 800])
col = np.insert(col, 0, plev)
col = np.append(col, top_limit)
result = np.array([0, 120, 845])
Expand All @@ -148,7 +148,7 @@ def test_low_lev_below_surf_press():
assert np.array_equal(_get_pressure_level_widths(col, air_pressure_axis=1),
np.atleast_3d(result))

col = np.array([np.NaN, np.NaN, 900, 800])
col = np.array([np.nan, np.nan, 900, 800])
col = np.insert(col, 0, plev)
col = np.append(col, top_limit)
result = np.array([0, 0, 120, 845])
Expand Down
5 changes: 4 additions & 1 deletion tests/unit/preprocessor/_time/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -920,9 +920,12 @@ def test_season_not_available(self):
name='clim_season',
seasons=['JFMAMJ', 'JASOND'],
)

# numpy>=2.0 these need to be explicitly cast to numpy strings
two_seasons = [np.str_('JASOND'), np.str_('JFMAMJ')]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is not backwards-compatible? How does the error look like? It might make sense to adapt this in the actual code so the error message still looks nice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it is, as it is now, ie the test does what it says on the lid for both numpy<2 and numpy>=2, here's the failed test for numpy>=2 if we don't change to forced typing:

>       with pytest.raises(ValueError, match=re.escape(msg)):
E       AssertionError: Regex pattern did not match.
E        Regex: "Seasons\\ \\('DJF',\\ 'MAM',\\ 'JJA',\\ 'SON'\\)\\ do\\ not\\ match\\ prior\\ season\\ extraction\\ \\['JASOND',\\ 'JFMAMJ'\\]\\."
E        Input: "Seasons ('DJF', 'MAM', 'JJA', 'SON') do not match prior season extraction [np.str_('JASOND'), np.str_('JFMAMJ')]."

tests/unit/preprocessor/_time/test_time.py:930: AssertionError

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new error message in Numpy 2 will be

Seasons ('DJF', 'MAM', 'JJA', 'SON') do not match prior season extraction [np.str_('JASOND'), np.str_('JFMAMJ')].

This is not really nice. I think it would be much better to change the code here to

old_seasons = sorted({str(s) for s in cube.coord("clim_season").points})

to get the old nice-looking error message back.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you want to push this too, together with the other int8-related changes? Don't want to push while you're also pushing, things may get hairy in no time 😁

msg = (
"Seasons ('DJF', 'MAM', 'JJA', 'SON') do not match prior season "
"extraction ['JASOND', 'JFMAMJ']."
f"extraction {two_seasons}."
)
with pytest.raises(ValueError, match=re.escape(msg)):
seasonal_statistics(cube, 'mean')
Expand Down