xcdat icon indicating copy to clipboard operation
xcdat copied to clipboard

[Bug]: xESMF related issue "AttributeError: `np.NaN` was removed in the NumPy 2.0 release. Use `np.nan` instead."

Open tomvothecoder opened this issue 1 year ago • 4 comments

What happened?

xESMF is conflicting with numpy>=2.0, which is breaking the GitHub Actions build (here).

I opened an issue in the xESMF repo here: https://github.com/pangeo-data/xESMF/issues/370

What did you expect to happen? Are there are possible answers you came across?

Related code in xESMF: https://github.com/pangeo-data/xESMF/blob/530b804c28a9b4f64dd360384897c4c2b34ab8c3/xesmf/smm.py#L229

  • [x] Update conda envs to constrain numpy <2.0
  • [x] Update conda-forge feedstock recipe with same constraint (link)

Minimal Complete Verifiable Example (MVCE)

No response

Relevant log output

=================================== FAILURES ===================================
________________________ TestXESMFRegridder.test_regrid ________________________

self = <tests.test_regrid.TestXESMFRegridder object at 0x7f78dd5a7190>

    def test_regrid(self):
        ds = self.ds.copy()
    
        regridder = xesmf.XESMFRegridder(ds, self.new_grid, "bilinear")
    
>       output = regridder.horizontal("ts", ds)

tests/test_regrid.py:734: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
xcdat/regridder/xesmf.py:158: in horizontal
    regridder = xe.Regridder(
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/frontend.py:928: in __init__
    super().__init__(
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/frontend.py:385: in __init__
    self.weights = add_nans_to_weights(self.weights)
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/smm.py:229: in add_nans_to_weights
    m.data[krow] = [np.NaN] if m.data[krow] == [] else m.data[krow]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

attr = 'NaN'

    def __getattr__(attr):
        # Warn for expired attributes
        import warnings
    
        if attr == "linalg":
            import numpy.linalg as linalg
            return linalg
        elif attr == "fft":
            import numpy.fft as fft
            return fft
        elif attr == "dtypes":
            import numpy.dtypes as dtypes
            return dtypes
        elif attr == "random":
            import numpy.random as random
            return random
        elif attr == "polynomial":
            import numpy.polynomial as polynomial
            return polynomial
        elif attr == "ma":
            import numpy.ma as ma
            return ma
        elif attr == "ctypeslib":
            import numpy.ctypeslib as ctypeslib
            return ctypeslib
        elif attr == "exceptions":
            import numpy.exceptions as exceptions
            return exceptions
        elif attr == "testing":
            import numpy.testing as testing
            return testing
        elif attr == "matlib":
            import numpy.matlib as matlib
            return matlib
        elif attr == "f2py":
            import numpy.f2py as f2py
            return f2py
        elif attr == "typing":
            import numpy.typing as typing
            return typing
        elif attr == "rec":
            import numpy.rec as rec
            return rec
        elif attr == "char":
            import numpy.char as char
            return char
        elif attr == "array_api":
            raise AttributeError("`numpy.array_api` is not available from "
                                 "numpy 2.0 onwards")
        elif attr == "core":
            import numpy.core as core
            return core
        elif attr == "strings":
            import numpy.strings as strings
            return strings
        elif attr == "distutils":
            if 'distutils' in __numpy_submodules__:
                import numpy.distutils as distutils
                return distutils
            else:
                raise AttributeError("`numpy.distutils` is not available from "
                                     "Python 3.12 onwards")
    
        if attr in __future_scalars__:
            # And future warnings for those that will change, but also give
            # the AttributeError
            warnings.warn(
                f"In the future `np.{attr}` will be defined as the "
                "corresponding NumPy scalar.", FutureWarning, stacklevel=2)
    
        if attr in __former_attrs__:
            raise AttributeError(__former_attrs__[attr])
    
        if attr in __expired_attributes__:
>           raise AttributeError(
                f"`np.{attr}` was removed in the NumPy 2.0 release. "
                f"{__expired_attributes__[attr]}"
            )
E           AttributeError: `np.NaN` was removed in the NumPy 2.0 release. Use `np.nan` instead.

/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/numpy/__init__.py:397: AttributeError
___________________ TestXESMFRegridder.test_preserve_bounds ____________________

self = <tests.test_regrid.TestXESMFRegridder object at 0x7f78dd3af7d0>

    def test_preserve_bounds(self):
        ds = fixtures.generate_dataset(
            decode_times=True, cf_compliant=False, has_bounds=True
        )
    
        ds = ds.drop_vars(["lat_bnds", "lon_bnds"])
    
        regridder = xesmf.XESMFRegridder(ds, self.new_grid, method="bilinear")
    
>       output = regridder.horizontal("ts", ds)

tests/test_regrid.py:793: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
xcdat/regridder/xesmf.py:158: in horizontal
    regridder = xe.Regridder(
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/frontend.py:928: in __init__
    super().__init__(
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/frontend.py:385: in __init__
    self.weights = add_nans_to_weights(self.weights)
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/smm.py:229: in add_nans_to_weights
    m.data[krow] = [np.NaN] if m.data[krow] == [] else m.data[krow]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

attr = 'NaN'

    def __getattr__(attr):
        # Warn for expired attributes
        import warnings
    
        if attr == "linalg":
            import numpy.linalg as linalg
            return linalg
        elif attr == "fft":
            import numpy.fft as fft
            return fft
        elif attr == "dtypes":
            import numpy.dtypes as dtypes
            return dtypes
        elif attr == "random":
            import numpy.random as random
            return random
        elif attr == "polynomial":
            import numpy.polynomial as polynomial
            return polynomial
        elif attr == "ma":
            import numpy.ma as ma
            return ma
        elif attr == "ctypeslib":
            import numpy.ctypeslib as ctypeslib
            return ctypeslib
        elif attr == "exceptions":
            import numpy.exceptions as exceptions
            return exceptions
        elif attr == "testing":
            import numpy.testing as testing
            return testing
        elif attr == "matlib":
            import numpy.matlib as matlib
            return matlib
        elif attr == "f2py":
            import numpy.f2py as f2py
            return f2py
        elif attr == "typing":
            import numpy.typing as typing
            return typing
        elif attr == "rec":
            import numpy.rec as rec
            return rec
        elif attr == "char":
            import numpy.char as char
            return char
        elif attr == "array_api":
            raise AttributeError("`numpy.array_api` is not available from "
                                 "numpy 2.0 onwards")
        elif attr == "core":
            import numpy.core as core
            return core
        elif attr == "strings":
            import numpy.strings as strings
            return strings
        elif attr == "distutils":
            if 'distutils' in __numpy_submodules__:
                import numpy.distutils as distutils
                return distutils
            else:
                raise AttributeError("`numpy.distutils` is not available from "
                                     "Python 3.12 onwards")
    
        if attr in __future_scalars__:
            # And future warnings for those that will change, but also give
            # the AttributeError
            warnings.warn(
                f"In the future `np.{attr}` will be defined as the "
                "corresponding NumPy scalar.", FutureWarning, stacklevel=2)
    
        if attr in __former_attrs__:
            raise AttributeError(__former_attrs__[attr])
    
        if attr in __expired_attributes__:
>           raise AttributeError(
                f"`np.{attr}` was removed in the NumPy 2.0 release. "
                f"{__expired_attributes__[attr]}"
            )
Warning:  yaksa: 10 leaked handle pool objects
E           AttributeError: `np.NaN` was removed in the NumPy 2.0 release. Use `np.nan` instead.

/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/numpy/__init__.py:397: AttributeError
_________________________ TestAccessor.test_horizontal _________________________

self = <tests.test_regrid.TestAccessor object at 0x7f78dd3f4ed0>

    def test_horizontal(self):
        output_grid = grid.create_gaussian_grid(32)
    
>       output_data = self.horizontal_ds.regridder.horizontal(
            "ts", output_grid, tool="xesmf", method="bilinear"
        )

tests/test_regrid.py:1161: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
xcdat/regridder/accessor.py:205: in horizontal
    output_ds = regridder.horizontal(data_var, self._ds)
xcdat/regridder/xesmf.py:158: in horizontal
    regridder = xe.Regridder(
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/frontend.py:928: in __init__
    super().__init__(
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/frontend.py:385: in __init__
    self.weights = add_nans_to_weights(self.weights)
/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/xesmf/smm.py:229: in add_nans_to_weights
    m.data[krow] = [np.NaN] if m.data[krow] == [] else m.data[krow]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

attr = 'NaN'

    def __getattr__(attr):
        # Warn for expired attributes
        import warnings
    
        if attr == "linalg":
            import numpy.linalg as linalg
            return linalg
        elif attr == "fft":
            import numpy.fft as fft
            return fft
        elif attr == "dtypes":
            import numpy.dtypes as dtypes
            return dtypes
        elif attr == "random":
            import numpy.random as random
            return random
        elif attr == "polynomial":
            import numpy.polynomial as polynomial
            return polynomial
        elif attr == "ma":
            import numpy.ma as ma
            return ma
        elif attr == "ctypeslib":
            import numpy.ctypeslib as ctypeslib
            return ctypeslib
        elif attr == "exceptions":
            import numpy.exceptions as exceptions
            return exceptions
        elif attr == "testing":
            import numpy.testing as testing
            return testing
        elif attr == "matlib":
            import numpy.matlib as matlib
            return matlib
        elif attr == "f2py":
            import numpy.f2py as f2py
            return f2py
        elif attr == "typing":
            import numpy.typing as typing
            return typing
        elif attr == "rec":
            import numpy.rec as rec
            return rec
        elif attr == "char":
            import numpy.char as char
            return char
        elif attr == "array_api":
            raise AttributeError("`numpy.array_api` is not available from "
                                 "numpy 2.0 onwards")
        elif attr == "core":
            import numpy.core as core
            return core
        elif attr == "strings":
            import numpy.strings as strings
            return strings
        elif attr == "distutils":
            if 'distutils' in __numpy_submodules__:
                import numpy.distutils as distutils
                return distutils
            else:
                raise AttributeError("`numpy.distutils` is not available from "
                                     "Python 3.12 onwards")
    
        if attr in __future_scalars__:
            # And future warnings for those that will change, but also give
            # the AttributeError
            warnings.warn(
                f"In the future `np.{attr}` will be defined as the "
                "corresponding NumPy scalar.", FutureWarning, stacklevel=2)
    
        if attr in __former_attrs__:
            raise AttributeError(__former_attrs__[attr])
    
        if attr in __expired_attributes__:
>           raise AttributeError(
                f"`np.{attr}` was removed in the NumPy 2.0 release. "
                f"{__expired_attributes__[attr]}"
            )
E           AttributeError: `np.NaN` was removed in the NumPy 2.0 release. Use `np.nan` instead.

/usr/share/miniconda3/envs/xcdat_ci/lib/python3.11/site-packages/numpy/__init__.py:397: AttributeError

Anything else we need to know?

No response

Environment

Latest xCDAT, numpy>=2.0 and xesmf=0.8.5

tomvothecoder avatar Jun 24 '24 18:06 tomvothecoder

This issue was recently fixed in xESMF by https://github.com/pangeo-data/xESMF/pull/373. We need to wait for a new release of xesmf for this to be carried over, although we still might want to constrain numpy <2.0.0 for at least another year maybe?

tomvothecoder avatar Jun 27 '24 18:06 tomvothecoder

Wow, existing xcdat users having to wait an extra year to get a major numpy update seems quite drastic

Worse, it might prevent potential new xcdat users to install xcdat if they try to install it and conda warns them that they have to downgrade numpy (and possibly other packages)

[UPDATE]

I have experimented a bit, and updated two test environments on two different servers. I don't know how dependencies and priorities work, but I ended up in both cases with numpy 2.0.0 and xcdat 0.7.0, and conda tells me All requested packages already installed when I run conda update -n my_env --all again

It's only when I ask conda to update only xcdat that conda warns me about a numpy downgrade. So it seems that numpy users are safe by default, and that the only risk is getting stuck with an old version of xcdat

$ conda update -n my_env xcdat
Channels:
 - conda-forge
 - defaults
Platform: linux-64
[...]
The following packages will be UPDATED:
  xcdat                                  0.7.0-pyhd8ed1ab_0 --> 0.7.1-pyhd8ed1ab_0
The following packages will be DOWNGRADED:
  numpy                               2.0.0-py312h22e1c76_0 --> 1.26.4-py312heda63a1_0
Proceed ([y]/n)? n

jypeter avatar Jul 10 '24 09:07 jypeter

Wow, existing xcdat users having to wait an extra year to get a major numpy update seems quite drastic

I wonder if a year was sufficient because numpy 2.0 is a major release with many breaking changes (link). It usually takes time for software to become stable after a major release and other packages that depend on that software need to migrate over.

In our case, it looks like xESMF might be the only xCDAT dependency that breaks with numpy 2.0 which would mean we don't need to a year to remove the constraint.

tomvothecoder avatar Jul 10 '24 17:07 tomvothecoder

Turns out that one of our post-doc reported today that regridding in xesmf (0.8.6) was not working anymore, and it worked again when I downgraded numpy from 2.0.0 to 1.26.4. This was not an np.NaN error

An intern had the same problem last Friday. I have also downgraded his numpy and asked him if this fixed the problem

jypeter avatar Jul 11 '24 15:07 jypeter