Skip to content

Commit

Permalink
Merge branch 'main' into regrid2_performance
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonb5 committed Dec 7, 2023
2 parents 62fe454 + 6238148 commit d5d7f49
Show file tree
Hide file tree
Showing 17 changed files with 78 additions and 103 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

env:
CANCEL_OTHERS: true
PATHS_IGNORE: '["**/README.rst", "**/docs/**", "**/ISSUE_TEMPLATE/**", "**/pull_request_template.md", "**/.vscode/**"]'
PATHS_IGNORE: '["**/README.rst", "**/AUTHORS.rst", "**/CODE_OF_CONDUCT.rst", "**/HISTORY.rst", "**/CONTRIBUTING.rst", "**/docs/**", "**/ISSUE_TEMPLATE/**", "**/pull_request_template.md", "**/.vscode/**"]'

jobs:
skip-duplicate-actions:
Expand Down
36 changes: 35 additions & 1 deletion HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,40 @@
History
=======

v0.6.1 (29 November 2023)
-------------------------

This patch version adds a default value to the ``axes`` argument in
``ds.bounds.add_missing_bounds()`` (``axes=["X", "Y", "T"]``). The ``axes``
argument was added in v0.6.0 and did not have a default value, which
inadvertently introduced a breaking change to the API.

``xesmf`` is now a required dependency because its core library, ESMF,
supports Windows as of Feb/2023. More information can be found
`here <https://github.com/conda-forge/esmf-feedstock/pull/65>`_.

Bug Fixes
~~~~~~~~~

- Add defaults to add_missing_bounds by `Ana Ordonez`_ in
https://github.com/xCDAT/xcdat/pull/569

DevOps
~~~~~~

- Make xESMF a required dependency by `Tom Vo`_ in
https://github.com/xCDAT/xcdat/pull/566

Documentation
~~~~~~~~~~~~~

- Update doc: Add link to the ESFG seminar xCDAT introduction video by `Jiwoo Lee`_ in
https://github.com/xCDAT/xcdat/pull/571
- Fix v0.6.0 changelog headers for proper nesting by `Tom Vo`_ in
https://github.com/xCDAT/xcdat/pull/559

**Full Changelog**: https://github.com/xCDAT/xcdat/compare/v0.6.0...v0.6.1

v0.6.0 (10 October 2023)
------------------------

Expand Down Expand Up @@ -92,7 +126,6 @@ DevOps

**Full Changelog**: https://github.com/xCDAT/xcdat/compare/v0.5.0...v0.6.0


v0.5.0 (27 March 2023)
--------------------------

Expand Down Expand Up @@ -634,3 +667,4 @@ DevOps
.. _Jiwoo Lee: https://github.com/lee1043
.. _Jill Chengzhu Zhang: https://github.com/chengzhuzhang
.. _Paul Durack: https://github.com/durack1
.. _Ana Ordonez: https://github.com/acordonez
4 changes: 1 addition & 3 deletions conda-env/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ dependencies:
- pandas
- python-dateutil
- xarray >=2022.02.0 # This version of Xarray drops support for Python 3.8.
- xgcm
# Optional - enables additional features.
# =========================================
- xesmf >=0.7.0 # Constrained because https://github.com/pangeo-data/xESMF/issues/212.
- xgcm
# Quality Assurance
# ==================
- types-python-dateutil
Expand Down
2 changes: 1 addition & 1 deletion conda-env/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ dependencies:
- pandas
- python-dateutil
- xarray >=2022.02.0 # This version of Xarray drops support for Python 3.8.
- xesmf >=0.7.0 # Constrained because https://github.com/pangeo-data/xESMF/issues/212.
- xgcm
# Optional - enables additional features.
# =========================================
- xesmf >=0.7.0 # Constrained because https://github.com/pangeo-data/xESMF/issues/212.
- matplotlib-base >=3.7.0
- nc-time-axis=1.4.1
# Documentation
Expand Down
8 changes: 8 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,18 @@
#
import os
import sys
from pathlib import Path
from typing import Dict

import sphinx_autosummary_accessors

# A workaround that sets the "ESMFMKFILE" env variable for the Read The Docs
# build to work. Read The Docs does not activate the conda environment which
# causes "ESMFMKFILE" to not be set (required by `esmpy` and `xesmf`).
# Source: https://github.com/conda-forge/esmf-feedstock/issues/91
if os.environ.get("READTHEDOCS") and "ESMFMKFILE" not in os.environ:
os.environ["ESMFMKFILE"] = str(Path(os.__file__).parent.parent / "esmf.mk")

sys.path.insert(0, os.path.abspath("..")) # noqa: I001, I003
import xcdat # noqa: I001, E402

Expand Down
1 change: 1 addition & 0 deletions docs/demos.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ This page includes relevant xCDAT presentations, demos, and papers.

AMS 2023 Abstract <https://ams.confex.com/ams/103ANNUAL/meetingapp.cgi/Paper/412648>
demos/1-25-23-cwss-seminar/introduction-to-xcdat.ipynb
A Gentle Introduction to xCDAT (Xarray Climate Data Analysis Tools) - Video introduction <https://youtu.be/sJpQ9vKDKa8?feature=shared>
16 changes: 3 additions & 13 deletions docs/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,14 @@ Installation
The advantage with following this approach is that Mamba will attempt to resolve
dependencies (e.g. ``python >= 3.8``) for compatibility.

To create an ``xcdat`` Mamba environment with ``xesmf`` (a recommended dependency),
To create an ``xcdat`` Mamba environment,
run:

.. code-block:: bash
>>> mamba create -n <ENV_NAME> -c conda-forge xcdat xesmf
>>> mamba create -n <ENV_NAME> -c conda-forge xcdat
>>> mamba activate <ENV_NAME>
Note that ``xesmf`` is an optional dependency, which is required for using ``xesmf``
based horizontal regridding APIs in ``xcdat``. ``xesmf`` is not currently supported
on `Windows`_ because it depends on ``esmpy``, which also does not support Windows.
Windows users can try `WSL2`_ as a workaround.

.. _Windows: https://github.com/conda-forge/esmf-feedstock/issues/64
.. _WSL2: https://docs.microsoft.com/en-us/windows/wsl/install

2. Install ``xcdat`` in an existing Mamba environment (`mamba install`_)

You can also install ``xcdat`` in an existing Mamba environment, granted that Mamba
Expand All @@ -72,9 +64,7 @@ Installation
.. code-block:: bash
>>> mamba activate <ENV_NAME>
>>> mamba install -c conda-forge xcdat xesmf
Note: As above, ``xesmf`` is an optional dependency.
>>> mamba install -c conda-forge xcdat
3. [Optional] Some packages that are commonly used with ``xcdat`` can be installed
either in step 1 or step 2 above:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@
test_suite="tests",
tests_require=test_requires,
url="https://github.com/xCDAT/xcdat",
version="0.6.0",
version="0.6.1",
zip_safe=False,
)
2 changes: 1 addition & 1 deletion tbump.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
github_url = "https://github.com/xCDAT/xcdat"

[version]
current = "0.6.0"
current = "0.6.1"

# Example of a semver regexp.
# Make sure this matches current_version before
Expand Down
4 changes: 1 addition & 3 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"""Unit test package for xcdat."""
from xarray.core.options import set_options
from xarray.tests import _importorskip, requires_dask # noqa: F401
from xarray.tests import requires_dask # noqa: F401

set_options(warn_for_unclosed_files=False)

has_xesmf, requires_xesmf = _importorskip("xesmf")
7 changes: 7 additions & 0 deletions tests/test_bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ def test_adds_x_y_and_z_bounds_to_the_dataset_using_midpoints(self):
result = ds_no_bnds.bounds.add_missing_bounds(axes=["X", "Y", "Z"])
assert result.identical(ds)

def test_adds_default_bounds_to_the_dataset_using_time_frequency(self):
ds = generate_dataset_by_frequency(freq="month")
ds_no_bnds = ds.drop_vars(["time_bnds", "lat_bnds", "lon_bnds"])

result = ds_no_bnds.bounds.add_missing_bounds()
assert result.identical(ds)

def test_adds_t_bounds_to_the_dataset_using_time_frequency(self):
ds = generate_dataset_by_frequency(freq="month")
ds_no_bnds = ds.drop_vars(["time_bnds"])
Expand Down
29 changes: 2 additions & 27 deletions tests/test_regrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@
import pytest
import xarray as xr

from tests import fixtures, has_xesmf, requires_xesmf
from xcdat.regridder import accessor, base, grid, regrid2, xgcm

if has_xesmf:
from xcdat.regridder import xesmf
from tests import fixtures
from xcdat.regridder import accessor, base, grid, regrid2, xesmf, xgcm

np.set_printoptions(threshold=sys.maxsize, suppress=True)

Expand Down Expand Up @@ -641,7 +638,6 @@ def test_reversed_extract_bounds(self):
assert north[0], north[-1] == (60, 90)


@requires_xesmf
class TestXESMFRegridder:
@pytest.fixture(autouse=True)
def setup(self):
Expand All @@ -650,13 +646,6 @@ def setup(self):
)
self.new_grid = grid.create_uniform_grid(-90, 90, 4.0, -180, 180, 5.0)

@pytest.mark.xfail
def test_raises_error_if_xesmf_is_not_installed(self):
# TODO Find a way to mock the value of `_has_xesmf` to False or
# to remove the `xesmf` module entirely
with pytest.raises(ModuleNotFoundError):
xesmf.XESMFRegridder(self.ds, self.new_grid, "bilinear")

def test_vertical_placeholder(self):
ds = self.ds.copy()

Expand Down Expand Up @@ -1151,7 +1140,6 @@ def test_preserve_mask_from_input(self):

xr.testing.assert_allclose(mask.ts, grid.mask)

@requires_xesmf
def test_horizontal(self):
output_grid = grid.create_gaussian_grid(32)

Expand Down Expand Up @@ -1301,7 +1289,6 @@ def test_vertical_tool_check(self, _get_input_grid):
):
self.ac.vertical("ts", mock_data, tool="dummy", target_data=None) # type: ignore

@requires_xesmf
@pytest.mark.filterwarnings("ignore:.*invalid value.*divide.*:RuntimeWarning")
def test_convenience_methods(self):
ds = fixtures.generate_dataset(
Expand All @@ -1318,18 +1305,6 @@ def test_convenience_methods(self):

assert output_regrid2.ts.shape == (15, 32, 65)

@pytest.mark.xfail
def test_raises_error_if_xesmf_is_not_installed(self):
# TODO Find a way to mock the value of `_has_xesmf` to False or
# to remove the `xesmf` module entirely
ds = fixtures.generate_dataset(
decode_times=True, cf_compliant=False, has_bounds=True
)

out_grid = grid.create_gaussian_grid(32)
with pytest.raises(ModuleNotFoundError):
ds.regridder.horizontal_xesmf("ts", out_grid, method="bilinear")


class TestBase:
def test_preserve_bounds(self):
Expand Down
2 changes: 1 addition & 1 deletion xcdat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
from xcdat.temporal import TemporalAccessor # noqa: F401
from xcdat.utils import compare_datasets # noqa: F401

__version__ = "0.6.0"
__version__ = "0.6.1"
10 changes: 6 additions & 4 deletions xcdat/bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ def keys(self) -> List[str]:
)
)

def add_missing_bounds(self, axes: List[CFAxisKey]) -> xr.Dataset: # noqa: C901
def add_missing_bounds( # noqa: C901
self, axes: List[CFAxisKey] = ["X", "Y", "T"]
) -> xr.Dataset:
"""Adds missing coordinate bounds for supported axes in the Dataset.
This function loops through the Dataset's axes and attempts to adds
Expand All @@ -144,13 +146,13 @@ def add_missing_bounds(self, axes: List[CFAxisKey]) -> xr.Dataset: # noqa: C901
``"time_bnds"`` and ``ds.time_bnds`` is present in the dataset.
5. For the "T" axis, its coordinates must be composed of datetime-like
objects (`np.datetime64` or `cftime`).
objects (``np.datetime64`` or ``cftime``).
Parameters
----------
axes : List[str]
List of CF axes that function should operate on. Options include
"X", "Y", "T", or "Z".
List of CF axes that function should operate on, by default
["X", "Y", "T"]. Options include "X", "Y", "T", or "Z".
Returns
-------
Expand Down
5 changes: 1 addition & 4 deletions xcdat/regridder/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from xcdat.regridder.accessor import RegridderAccessor
from xcdat.regridder.regrid2 import Regrid2Regridder
from xcdat.regridder.xesmf import XESMFRegridder
from xcdat.regridder.xgcm import XGCMRegridder
from xcdat.utils import _has_module

_has_xesmf = _has_module("xesmf")
if _has_xesmf:
from xcdat.regridder.xesmf import XESMFRegridder
37 changes: 7 additions & 30 deletions xcdat/regridder/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,14 @@
import xarray as xr

from xcdat.axis import CFAxisKey, get_dim_coords
from xcdat.regridder import regrid2, xgcm
from xcdat.regridder import regrid2, xesmf, xgcm
from xcdat.regridder.grid import _validate_grid_has_single_axis_dim
from xcdat.utils import _has_module

HorizontalRegridTools = Literal["xesmf", "regrid2"]
HORIZONTAL_REGRID_TOOLS = {"regrid2": regrid2.Regrid2Regridder}

# TODO: Test this conditional.
_has_xesmf = _has_module("xesmf")
if _has_xesmf: # pragma: no cover
from xcdat.regridder import xesmf

HORIZONTAL_REGRID_TOOLS["xesmf"] = xesmf.XESMFRegridder # type: ignore
HORIZONTAL_REGRID_TOOLS = {
"regrid2": regrid2.Regrid2Regridder,
"xesmf": xesmf.XESMFRegridder,
}

VerticalRegridTools = Literal["xgcm"]
VERTICAL_REGRID_TOOLS = {"xgcm": xgcm.XGCMRegridder}
Expand Down Expand Up @@ -179,19 +174,9 @@ def horizontal_xesmf(
stacklevel=2,
)

# TODO: Test this conditional.
if _has_xesmf: # pragma: no cover
regridder = HORIZONTAL_REGRID_TOOLS["xesmf"](
self._ds, output_grid, **options
)
regridder = HORIZONTAL_REGRID_TOOLS["xesmf"](self._ds, output_grid, **options)

return regridder.horizontal(data_var, self._ds)
else: # pragma: no cover
raise ModuleNotFoundError(
"The `xesmf` package is required for horizontal regridding with "
"`xesmf`. Make sure your platform supports `xesmf` and it is installed "
"in your conda environment."
)
return regridder.horizontal(data_var, self._ds)

# TODO Either provide generic `horizontal` and `vertical` methods or tool specific
def horizontal_regrid2(
Expand Down Expand Up @@ -327,14 +312,6 @@ def horizontal(
>>> output_data = ds.regridder.horizontal("ts", output_grid, tool="regrid2")
"""
# TODO: Test this conditional.
if tool == "xesmf" and not _has_xesmf: # pragma: no cover
raise ModuleNotFoundError(
"The `xesmf` package is required for horizontal regridding with "
"`xesmf`. Make sure your platform supports `xesmf` and it is installed "
"in your conda environment."
)

try:
regrid_tool = HORIZONTAL_REGRID_TOOLS[tool]
except KeyError as e:
Expand Down
14 changes: 1 addition & 13 deletions xcdat/regridder/xesmf.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
from typing import Any, Optional

import xarray as xr
import xesmf as xe

from xcdat.regridder.base import BaseRegridder, _preserve_bounds
from xcdat.utils import _has_module

# TODO: Test this conditional.
_has_xesmf = _has_module("xesmf")
if _has_xesmf: # pragma: no cover
import xesmf as xe
else: # pragma: no cover
raise ModuleNotFoundError(
"The `xesmf` package is required for horizontal regridding with `xesmf`. Make "
"sure your platform supports `xesmf` and it is installed in your conda "
"environment."
)


VALID_METHODS = [
"bilinear",
Expand Down

0 comments on commit d5d7f49

Please sign in to comment.