From ab20bf95e85e80d8d4ae3f9f396351691b6686dd Mon Sep 17 00:00:00 2001 From: Tom Vo Date: Wed, 26 Apr 2023 15:48:12 -0700 Subject: [PATCH 1/7] Update build workflow to cache conda env --- .github/workflows/build_workflow.yml | 38 ++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build_workflow.yml b/.github/workflows/build_workflow.yml index ddf0baf0..4f9afb21 100644 --- a/.github/workflows/build_workflow.yml +++ b/.github/workflows/build_workflow.yml @@ -62,16 +62,6 @@ jobs: - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} uses: actions/checkout@v3 - - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} - name: Cache Conda - uses: actions/cache@v3 - env: - # Increase this value to reset cache if conda-env/ci.yml has not changed in the workflow - CACHE_NUMBER: 0 - with: - path: ~/conda_pkgs_dir - key: ${{ runner.os }}-${{ matrix.python-version }}-conda-${{ env.CACHE_NUMBER }} - - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} name: Set up Conda Environment uses: conda-incubator/setup-miniconda@v2 @@ -81,13 +71,35 @@ jobs: miniforge-version: latest use-mamba: true mamba-version: "*" - environment-file: conda-env/ci.yml channel-priority: strict auto-update-conda: true - # IMPORTANT: This needs to be set for caching to work properly! - use-only-tar-bz2: true python-version: ${{ matrix.python-version }} + # Refresh the cache every 24 hours to avoid inconsistencies of package versions + # between the CI pipeline and local installations. + - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + name: Get Date + id: get-date + run: echo "today=$(/bin/date -u '+%Y%m%d')" >> $GITHUB_OUTPUT + shell: bash + + - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + name: Cache Conda env + uses: actions/cache@v3 + with: + path: ${{ env.CONDA }}/envs + key: + conda-${{ runner.os }}-${{ runner.arch }}-${{ matrix.python-version }}-${{ + steps.get-date.outputs.today }}-${{hashFiles('conda-env/ci.yml') }}-${{ env.CACHE_NUMBER}} + env: + # Increase this value to reset cache if conda-env/ci.yml has not changed in the workflow + CACHE_NUMBER: 0 + id: cache + + - if: $${{ steps.skip_check.outputs.should_skip != 'true' && steps.cache.outputs.cache-hit != 'true' && }} + name: Update environment + run: mamba env update -n xcdat_ci -f conda-env/ci.yml + - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} name: Install xcdat # Source: https://github.com/conda/conda-build/issues/4251#issuecomment-1053460542 From 03d2b35d6a6cbcbc07dfd29107ed86ba837da847 Mon Sep 17 00:00:00 2001 From: Tom Vo Date: Wed, 26 Apr 2023 15:49:25 -0700 Subject: [PATCH 2/7] Fix build workflow --- .github/workflows/build_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_workflow.yml b/.github/workflows/build_workflow.yml index 4f9afb21..e59e0587 100644 --- a/.github/workflows/build_workflow.yml +++ b/.github/workflows/build_workflow.yml @@ -96,7 +96,7 @@ jobs: CACHE_NUMBER: 0 id: cache - - if: $${{ steps.skip_check.outputs.should_skip != 'true' && steps.cache.outputs.cache-hit != 'true' && }} + - if: $${{ steps.skip_check.outputs.should_skip != 'true' && steps.cache.outputs.cache-hit != 'true' }} name: Update environment run: mamba env update -n xcdat_ci -f conda-env/ci.yml From a117838e157d51aa74ae17366874f07b8c4e32aa Mon Sep 17 00:00:00 2001 From: Tom Vo Date: Wed, 26 Apr 2023 16:55:02 -0700 Subject: [PATCH 3/7] Fix dv attrs getting dropped using `.where()` - Constraint `xesmf` in `ci.yml` --- .github/workflows/build_workflow.yml | 6 +++--- conda-env/ci.yml | 4 +++- xcdat/temporal.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_workflow.yml b/.github/workflows/build_workflow.yml index e59e0587..875b6395 100644 --- a/.github/workflows/build_workflow.yml +++ b/.github/workflows/build_workflow.yml @@ -66,9 +66,9 @@ jobs: name: Set up Conda Environment uses: conda-incubator/setup-miniconda@v2 with: - activate-environment: "xcdat_ci" miniforge-variant: Mambaforge miniforge-version: latest + activate-environment: "xcdat_ci" use-mamba: true mamba-version: "*" channel-priority: strict @@ -78,12 +78,13 @@ jobs: # Refresh the cache every 24 hours to avoid inconsistencies of package versions # between the CI pipeline and local installations. - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} - name: Get Date id: get-date + name: Get Date run: echo "today=$(/bin/date -u '+%Y%m%d')" >> $GITHUB_OUTPUT shell: bash - if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + id: cache name: Cache Conda env uses: actions/cache@v3 with: @@ -94,7 +95,6 @@ jobs: env: # Increase this value to reset cache if conda-env/ci.yml has not changed in the workflow CACHE_NUMBER: 0 - id: cache - if: $${{ steps.skip_check.outputs.should_skip != 'true' && steps.cache.outputs.cache-hit != 'true' }} name: Update environment diff --git a/conda-env/ci.yml b/conda-env/ci.yml index e5a128a6..af73a5df 100644 --- a/conda-env/ci.yml +++ b/conda-env/ci.yml @@ -19,7 +19,9 @@ dependencies: - pandas - python-dateutil - xarray - - xesmf + # Constrained because 0.6.3 breaks with import ESMF + # Source: https://github.com/pangeo-data/xESMF/issues/212 + - xesmf >0.6.3 # Quality Assurance # ================== - types-python-dateutil diff --git a/xcdat/temporal.py b/xcdat/temporal.py index 7711fe17..79efd778 100644 --- a/xcdat/temporal.py +++ b/xcdat/temporal.py @@ -1138,7 +1138,7 @@ def _group_average( # dimension) shape of the `weights` DataArray to the # multi-dimensional shape of its corresponding data variable. weights, _ = xr.broadcast(self._weights, dv) - weights = xr.where(np.isnan(dv), 0.0, weights) + weights = xr.where(dv.copy().isnull(), 0.0, weights) # Perform weighted average using the formula # WA = sum(data*weights) / sum(weights). The denominator must be From d703472f6dbfb9e838d97a029695dbf527c058ab Mon Sep 17 00:00:00 2001 From: Tom Vo Date: Wed, 10 May 2023 16:47:52 -0700 Subject: [PATCH 4/7] Update tests to silence logger warnings - Update `_logger.py` module to avoid propagating logger messages multiple times - Update `TemporalAccessor._set_data_var_attrs` try and except statement to a `.get()` --- spatial-plot.png | Bin 0 -> 43410 bytes tests/test_dataset.py | 33 +++++++--------- tests/test_temporal.py | 66 ++++++++++++-------------------- xcdat/{logger.py => _logger.py} | 28 ++++++++------ xcdat/bounds.py | 4 +- xcdat/dataset.py | 4 +- xcdat/regridder/base.py | 4 +- xcdat/temporal.py | 10 ++--- 8 files changed, 64 insertions(+), 85 deletions(-) create mode 100644 spatial-plot.png rename xcdat/{logger.py => _logger.py} (64%) diff --git a/spatial-plot.png b/spatial-plot.png new file mode 100644 index 0000000000000000000000000000000000000000..a60a68dc45fc4d41e5a4b819a341e5a68e852bd2 GIT binary patch literal 43410 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 %(message)s" +) +logging.basicConfig(format=log_format, filemode="w", level=logging.INFO) -def setup_custom_logger(name: str, propagate: bool = False) -> logging.Logger: +# Console handler setup +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.INFO) +logFormatter = logging.Formatter(log_format) +console_handler.setFormatter(logFormatter) +logging.getLogger().addHandler(console_handler) + + +def _setup_custom_logger(name, propagate=True) -> logging.Logger: """Sets up a custom logger. + Documentation on logging: https://docs.python.org/3/library/logging.html + Parameters ---------- name : str @@ -43,18 +58,7 @@ def setup_custom_logger(name: str, propagate: bool = False) -> logging.Logger: >>> logger.critical("") """ - log_format = "%(asctime)s [%(levelname)s]: %(filename)s(%(funcName)s:%(lineno)s) >> %(message)s" - log_filemode = "w" # w: overwrite; a: append - - # Setup - logging.basicConfig(format=log_format, filemode=log_filemode, level=logging.INFO) logger = logging.getLogger(name) logger.propagate = propagate - # Console output - consoleHandler = logging.StreamHandler() - logFormatter = logging.Formatter(log_format) - consoleHandler.setFormatter(logFormatter) - logger.addHandler(consoleHandler) - return logger diff --git a/xcdat/bounds.py b/xcdat/bounds.py index 942d0251..388053c9 100644 --- a/xcdat/bounds.py +++ b/xcdat/bounds.py @@ -12,16 +12,16 @@ from xarray.coding.cftime_offsets import get_date_type from xarray.core.common import contains_cftime_datetimes +from xcdat._logger import _setup_custom_logger from xcdat.axis import CF_ATTR_MAP, CFAxisKey, get_dim_coords from xcdat.dataset import _get_data_var -from xcdat.logger import setup_custom_logger from xcdat.temporal import ( _contains_datetime_like_objects, _get_datetime_like_type, _infer_freq, ) -logger = setup_custom_logger(__name__) +logger = _setup_custom_logger(__name__) @xr.register_dataset_accessor("bounds") diff --git a/xcdat/dataset.py b/xcdat/dataset.py index ece06c27..5103a68a 100644 --- a/xcdat/dataset.py +++ b/xcdat/dataset.py @@ -16,12 +16,12 @@ from xarray.core.variable import as_variable from xcdat import bounds as bounds_accessor # noqa: F401 +from xcdat._logger import _setup_custom_logger from xcdat.axis import CFAxisKey, _get_all_coord_keys from xcdat.axis import center_times as center_times_func from xcdat.axis import swap_lon_axis -from xcdat.logger import setup_custom_logger -logger = setup_custom_logger(__name__) +logger = _setup_custom_logger(__name__) #: List of non-CF compliant time units. NON_CF_TIME_UNITS: List[str] = ["month", "months", "year", "years"] diff --git a/xcdat/regridder/base.py b/xcdat/regridder/base.py index e3419b09..9fcf7925 100644 --- a/xcdat/regridder/base.py +++ b/xcdat/regridder/base.py @@ -4,9 +4,9 @@ import xarray as xr import xcdat.bounds # noqa: F401 -from xcdat.logger import setup_custom_logger +from xcdat._logger import _setup_custom_logger -logger = setup_custom_logger(__name__) +logger = _setup_custom_logger(__name__) def preserve_bounds( diff --git a/xcdat/temporal.py b/xcdat/temporal.py index 79efd778..33ad8c2a 100644 --- a/xcdat/temporal.py +++ b/xcdat/temporal.py @@ -14,11 +14,11 @@ from xarray.core.groupby import DataArrayGroupBy from xcdat import bounds # noqa: F401 +from xcdat._logger import _setup_custom_logger from xcdat.axis import get_dim_coords from xcdat.dataset import _get_data_var -from xcdat.logger import setup_custom_logger -logger = setup_custom_logger(__name__) +logger = _setup_custom_logger(__name__) # Type alias for supported time averaging modes. Mode = Literal["average", "group_average", "climatology", "departures"] @@ -816,10 +816,10 @@ def _set_data_var_attrs(self, data_var: str): # Get the `cftime` date type based on the CF calendar attribute. # The date type is used to get the correct cftime.datetime sub-class # type for creating new grouped time coordinates for averaging. - try: - self.calendar = dv[self.dim].encoding["calendar"] - except KeyError: + self.calendar = dv[self.dim].encoding.get("calendar", None) + if self.calendar is None: self.calendar = "standard" + logger.warning( f"'{self.dim}' does not have a calendar encoding attribute set, " "which is used to determine the `cftime.datetime` object type for the " From c6a524f97b089f0fb13558cb05126e745ae8334e Mon Sep 17 00:00:00 2001 From: Tom Vo Date: Wed, 10 May 2023 16:56:38 -0700 Subject: [PATCH 5/7] Delete plot --- spatial-plot.png | Bin 43410 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 spatial-plot.png diff --git a/spatial-plot.png b/spatial-plot.png deleted file mode 100644 index a60a68dc45fc4d41e5a4b819a341e5a68e852bd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43410 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 Date: Wed, 10 May 2023 17:06:11 -0700 Subject: [PATCH 6/7] Update tests/test_dataset.py --- tests/test_dataset.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_dataset.py b/tests/test_dataset.py index 0bcfc85f..2fddae0c 100644 --- a/tests/test_dataset.py +++ b/tests/test_dataset.py @@ -46,9 +46,7 @@ def test_raises_warning_if_decode_times_but_no_time_coords_found(self, caplog): assert result.identical(expected) - def test_skip_decoding_time_explicitly( - self, - ): + def test_skip_decoding_time_explicitly(self): ds = generate_dataset(decode_times=False, cf_compliant=True, has_bounds=True) ds.to_netcdf(self.file_path) From ba295b2c62d1df45c4f758dd55eb93f505a7f97f Mon Sep 17 00:00:00 2001 From: Tom Vo Date: Thu, 11 May 2023 11:21:27 -0700 Subject: [PATCH 7/7] Clean up test suite and silence logger warnings - Bump xarray to latest version in conda envs --- conda-env/dev.yml | 2 +- conda-env/readthedocs.yml | 2 +- tests/test_bounds.py | 91 ++-- tests/test_dataset.py | 928 ++++++-------------------------------- tests/test_regrid.py | 4 +- 5 files changed, 195 insertions(+), 832 deletions(-) diff --git a/conda-env/dev.yml b/conda-env/dev.yml index eff2480d..e5bec3b8 100644 --- a/conda-env/dev.yml +++ b/conda-env/dev.yml @@ -18,7 +18,7 @@ dependencies: - numpy=1.23.5 - pandas=1.5.3 - python-dateutil=2.8.2 - - xarray=2023.3.0 + - xarray=2023.4.2 # ================== # Optional # ================== diff --git a/conda-env/readthedocs.yml b/conda-env/readthedocs.yml index 5204854c..a6a2896f 100644 --- a/conda-env/readthedocs.yml +++ b/conda-env/readthedocs.yml @@ -17,7 +17,7 @@ dependencies: - numpy=1.23.5 - pandas=1.5.3 - python-dateutil=2.8.2 - - xarray=2023.3.0 + - xarray=2023.4.2 # ================== # Optional # ================== diff --git a/tests/test_bounds.py b/tests/test_bounds.py index 389e1267..38151642 100644 --- a/tests/test_bounds.py +++ b/tests/test_bounds.py @@ -72,62 +72,89 @@ def setup(self): def test_adds_bounds_to_the_dataset(self): ds = self.ds_with_bnds.copy() - ds = ds.drop_vars(["lat_bnds", "lon_bnds"]) + # Compare the result against the expected. result = ds.bounds.add_missing_bounds(axes=["X", "Y"]) assert result.identical(self.ds_with_bnds) - def test_skips_adding_bounds_for_coords_that_are_1_dim_singleton(self): - # Length <=1 - lon = xr.DataArray( - data=np.array([0]), - dims=["lon"], - attrs={"units": "degrees_east", "axis": "X"}, + def test_skips_adding_bounds_for_coords_that_are_1_dim_singleton(self, caplog): + # NOTE: Suppress logger warning to avoid polluting test suite. + caplog.set_level(logging.CRITICAL) + + # Create the input dataset. + ds = xr.Dataset( + coords={ + "lon": xr.DataArray( + data=np.array([0]), + dims=["lon"], + attrs={"units": "degrees_east", "axis": "X"}, + ) + } ) - ds = xr.Dataset(coords={"lon": lon}) + # Compare the result against the expected. result = ds.bounds.add_missing_bounds(axes=["X"]) - assert result.identical(ds) - def test_skips_adding_bounds_for_coords_that_are_0_dim_singleton(self): - # 0-dimensional array - lon = xr.DataArray( - data=float(0), - attrs={"units": "degrees_east", "axis": "X"}, + def test_skips_adding_bounds_for_coords_that_are_0_dim_singleton(self, caplog): + # NOTE: Suppress logger warning to avoid polluting test suite. + caplog.set_level(logging.CRITICAL) + + # Create the input dataset. + ds = xr.Dataset( + coords={ + "lon": xr.DataArray( + data=float(0), + attrs={"units": "degrees_east", "axis": "X"}, + ) + } ) - ds = xr.Dataset(coords={"lon": lon}) + # Compare the result against the expected. result = ds.bounds.add_missing_bounds(axes=["X"]) - assert result.identical(ds) - def test_skips_adding_time_bounds_for_coords_that_are_1_dim_singleton(self): - # Length <=1 - time = xr.DataArray( - data=np.array(["2000-01-01T12:00:00.000000000"], dtype="datetime64[ns]"), - dims=["time"], - attrs={"calendar": "standard", "units": "days since 1850-01-01"}, + def test_skips_adding_time_bounds_for_coords_that_are_1_dim_singleton(self, caplog): + # NOTE: Suppress logger warning to avoid polluting test suite. + caplog.set_level(logging.CRITICAL) + + # Create the input dataset. + ds = xr.Dataset( + coords={ + "time": xr.DataArray( + data=np.array( + ["2000-01-01T12:00:00.000000000"], dtype="datetime64[ns]" + ), + dims=["time"], + attrs={"calendar": "standard", "units": "days since 1850-01-01"}, + ) + } ) - ds = xr.Dataset(coords={"time": time}) + # Compare the result against the expected. result = ds.bounds.add_missing_bounds(axes=["T"]) - assert result.identical(ds) def test_skips_adding_time_bounds_for_coords_that_are_not_datetime_like_objects( - self, + self, caplog ): - time = xr.DataArray( - data=np.array([0, 1, 2]), - dims=["time"], - attrs={"calendar": "standard", "units": "days since 1850-01-01"}, + # NOTE: Suppress logger warning to avoid polluting test suite. + caplog.set_level(logging.CRITICAL) + + # Create the input dataset. + ds = xr.Dataset( + coords={ + "time": xr.DataArray( + data=np.array([0, 1, 2]), + dims=["time"], + attrs={"calendar": "standard", "units": "days since 1850-01-01"}, + ) + } ) - ds = xr.Dataset(coords={"time": time}) + # Compare the result against the expected. result = ds.bounds.add_missing_bounds(axes=["T"]) - assert result.identical(ds) @@ -332,7 +359,7 @@ def test_raises_error_if_lat_coord_var_units_is_not_in_degrees(self): def test_adds_bounds_and_sets_units_to_degrees_north_if_lat_coord_var_is_missing_units_attr( self, caplog ): - # Suppress the warning + # NOTE: Suppress logger warning to avoid polluting test suite. caplog.set_level(logging.CRITICAL) ds = self.ds.copy() diff --git a/tests/test_dataset.py b/tests/test_dataset.py index 2fddae0c..fd70734f 100644 --- a/tests/test_dataset.py +++ b/tests/test_dataset.py @@ -84,12 +84,12 @@ def test_skips_adding_bounds(self): assert result.identical(ds) def test_decode_time_in_days(self): - ds = generate_dataset(decode_times=False, cf_compliant=True, has_bounds=True) + ds = generate_dataset( + decode_times=False, cf_compliant=True, has_bounds=True + ).isel(time=slice(0, 3)) ds.to_netcdf(self.file_path) - result = open_dataset(self.file_path, data_var="ts", decode_times=True) - - # Generate an expected dataset with decoded CF compliant time units. + # Create the expected dataset. expected = ds.copy() expected["time"] = xr.DataArray( name="time", @@ -104,42 +104,6 @@ def test_decode_time_in_days(self): cftime.DatetimeGregorian( 2000, 1, 3, 0, 0, 0, 0, has_year_zero=False ), - cftime.DatetimeGregorian( - 2000, 1, 4, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 5, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 6, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 7, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 8, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 9, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 10, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 11, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 12, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 13, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 14, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 15, 0, 0, 0, 0, has_year_zero=False - ), ], dtype="object", ), @@ -173,102 +137,6 @@ def test_decode_time_in_days(self): 2000, 1, 3, 0, 0, 0, 0, has_year_zero=False ), ], - [ - cftime.DatetimeGregorian( - 2000, 1, 3, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 4, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 4, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 5, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 5, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 6, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 6, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 7, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 7, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 8, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 8, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 9, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 9, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 10, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 10, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 11, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 11, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 12, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 12, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 13, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 13, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 14, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 14, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 15, 0, 0, 0, 0, has_year_zero=False - ), - ], ], dtype="object", ), @@ -282,9 +150,11 @@ def test_decode_time_in_days(self): "bounds": "time_bnds", } + # Compare the result against the expected. + result = open_dataset(self.file_path, data_var="ts", decode_times=True) assert result.identical(expected) - # Check encoding is preserved. + # Compare time encoding. expected.time.encoding = { "zlib": False, "szip": False, @@ -298,7 +168,7 @@ def test_decode_time_in_days(self): "chunksizes": None, # Set source as result source because it changes every test run. "source": result.time.encoding["source"], - "original_shape": (15,), + "original_shape": expected.time.shape, "dtype": np.dtype("int64"), "units": "days since 2000-01-01", "calendar": "standard", @@ -315,22 +185,21 @@ def test_decode_time_in_days(self): "contiguous": True, "chunksizes": None, "source": result.time.encoding["source"], - "original_shape": (15, 2), + "original_shape": expected.time_bnds.shape, "dtype": np.dtype("int64"), "units": "days since 2000-01-01", "calendar": "standard", } - assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding def test_decode_time_in_months(self): - ds = generate_dataset(decode_times=False, cf_compliant=False, has_bounds=True) + ds = generate_dataset( + decode_times=False, cf_compliant=False, has_bounds=True + ).isel(time=slice(0, 3)) ds.to_netcdf(self.file_path) - result = open_dataset(self.file_path, data_var="ts") - - # Generate an expected dataset with decoded non-CF compliant time units. + # Create the expected dataset. expected = ds.copy() expected["time"] = xr.DataArray( name="time", @@ -345,42 +214,6 @@ def test_decode_time_in_months(self): cftime.DatetimeGregorian( 2000, 3, 1, 0, 0, 0, 0, has_year_zero=False ), - cftime.DatetimeGregorian( - 2000, 4, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 5, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 6, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 7, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 8, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 9, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 10, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 11, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 12, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 1, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 2, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 3, 1, 0, 0, 0, 0, has_year_zero=False - ), ], dtype="object", ), @@ -415,102 +248,6 @@ def test_decode_time_in_months(self): 2000, 3, 1, 0, 0, 0, 0, has_year_zero=False ), ], - [ - cftime.DatetimeGregorian( - 2000, 3, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 4, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 4, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 5, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 5, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 6, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 6, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 7, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 7, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 8, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 8, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 9, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 9, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 10, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 10, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 11, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 11, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 12, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 12, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 1, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2001, 1, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 2, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2001, 2, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 3, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], ], dtype="object", ), @@ -525,9 +262,11 @@ def test_decode_time_in_months(self): "bounds": "time_bnds", } + # Compare the result against the expected. + result = open_dataset(self.file_path, data_var="ts") assert result.identical(expected) - # Check encoding is preserved. + # Compare time encoding. expected.time.encoding = { "zlib": False, "szip": False, @@ -541,7 +280,7 @@ def test_decode_time_in_months(self): "chunksizes": None, # Set source as result source because it changes every test run. "source": result.time.encoding["source"], - "original_shape": (15,), + "original_shape": expected.time.shape, "dtype": np.dtype("int64"), "units": "months since 2000-01-01", "calendar": "standard", @@ -559,24 +298,23 @@ def test_decode_time_in_months(self): "chunksizes": None, # Set source as result source because it changes every test run. "source": result.time.encoding["source"], - "original_shape": (15, 2), + "original_shape": expected.time_bnds.shape, "dtype": np.dtype("int64"), "units": "months since 2000-01-01", "calendar": "standard", } - assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding def test_keeps_specified_var_and_preserves_bounds(self): ds = generate_dataset(decode_times=True, cf_compliant=True, has_bounds=True) - # Create a modified version of the Dataset with a new var + # Create a modified version of the Dataset with a new var. ds_mod = ds.copy() ds_mod["tas"] = ds_mod.ts.copy() - # Suppress UserWarning regarding missing time.encoding "units" because - # it is not relevant to this test. + # NOTE: Suppress UserWarning regarding missing time.encoding "units" + # because it is not relevant to this test. with warnings.catch_warnings(): warnings.simplefilter("ignore") ds_mod.to_netcdf(self.file_path) @@ -738,20 +476,21 @@ def callable(ds): def test_decode_time_in_months(self): # Generate two dataset files with different variables. - ds1 = generate_dataset(decode_times=False, cf_compliant=False, has_bounds=True) + ds1 = generate_dataset( + decode_times=False, cf_compliant=False, has_bounds=True + ).isel(time=slice(0, 3)) ds1.to_netcdf(self.file_path1) - ds2 = generate_dataset(decode_times=False, cf_compliant=False, has_bounds=True) + ds2 = generate_dataset( + decode_times=False, cf_compliant=False, has_bounds=True + ).isel(time=slice(0, 3)) ds2 = ds2.rename_vars({"ts": "tas"}) ds2.to_netcdf(self.file_path2) - # Open both dataset files as a single Dataset object. - result = open_mfdataset([self.file_path1, self.file_path2], data_var="ts") - - # Create an expected Dataset object. + # Create the expected dataset. expected = generate_dataset( decode_times=True, cf_compliant=False, has_bounds=True - ) + ).isel(time=slice(0, 3)) expected["time"] = xr.DataArray( name="time", data=np.array( @@ -765,42 +504,6 @@ def test_decode_time_in_months(self): cftime.DatetimeGregorian( 2000, 3, 1, 0, 0, 0, 0, has_year_zero=False ), - cftime.DatetimeGregorian( - 2000, 4, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 5, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 6, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 7, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 8, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 9, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 10, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 11, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 12, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 1, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 2, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 3, 1, 0, 0, 0, 0, has_year_zero=False - ), ], dtype="object", ), @@ -812,7 +515,6 @@ def test_decode_time_in_months(self): "bounds": "time_bnds", }, ) - expected["time_bnds"] = xr.DataArray( name="time_bnds", data=np.array( @@ -841,114 +543,21 @@ def test_decode_time_in_months(self): 2000, 3, 1, 0, 0, 0, 0, has_year_zero=False ), ], - [ - cftime.DatetimeGregorian( - 2000, 3, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 4, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 4, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 5, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 5, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 6, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 6, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 7, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 7, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 8, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 8, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 9, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 9, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 10, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 10, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 11, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 11, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 12, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 12, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 1, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2001, 1, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 2, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2001, 2, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 3, 1, 0, 0, 0, 0, has_year_zero=False - ), - ], ], dtype="object", ), dims=["time", "bnds"], attrs={"xcdat_bounds": "True"}, ) - # Make sure the expected is chunked. - expected = expected.chunk(chunks={"time": 15, "bnds": 2}) + expected = expected.chunk(chunks={"time": 3, "bnds": 2}) + + # Compare the result against the expected. + result = open_mfdataset([self.file_path1, self.file_path2], data_var="ts") + assert result.identical(expected) - # Check encoding is preserved. The extra metadata like "zlib" are from - # the netCDF4 files. + # Compare the time encoding. + # The extra metadata like "zlib" are from the netCDF4 files. expected.time.encoding = { "zlib": False, "szip": False, @@ -962,7 +571,7 @@ def test_decode_time_in_months(self): "chunksizes": None, # Set source as result source because it changes every test run. "source": result.time.encoding["source"], - "original_shape": (15,), + "original_shape": expected.time.shape, "dtype": np.dtype("int64"), "units": "months since 2000-01-01", "calendar": "standard", @@ -971,8 +580,6 @@ def test_decode_time_in_months(self): "units": "months since 2000-01-01", "calendar": "standard", } - - assert result.identical(expected) assert result.time.encoding == expected.time.encoding # FIXME: For some reason the encoding attributes get dropped only in @@ -1050,14 +657,15 @@ def test_skips_decoding_time_coords_if_units_is_not_supported(self, caplog): # Update logger level to silence the logger warning during test runs. caplog.set_level(logging.ERROR) + # Create the input dataset and update the units. ds = generate_dataset(decode_times=False, cf_compliant=False, has_bounds=True) - ds.time.attrs["units"] = "year AD" result = decode_time(ds) assert ds.identical(result) def test_skips_decoding_time_bounds_if_bounds_dont_exist(self): + # Create the input dataset. ds = xr.Dataset( coords={ "time": xr.DataArray( @@ -1089,7 +697,7 @@ def test_skips_decoding_time_bounds_if_bounds_dont_exist(self): }, ) - result = decode_time(ds) + # Create the expected dataset. expected = xr.Dataset( coords={ "time": xr.DataArray( @@ -1142,10 +750,6 @@ def test_skips_decoding_time_bounds_if_bounds_dont_exist(self): ), }, ) - - assert result.identical(expected) - - # Check encoding is preserved. expected.time.encoding = { "units": "months since 2000-01-01", "calendar": "standard", @@ -1155,10 +759,14 @@ def test_skips_decoding_time_bounds_if_bounds_dont_exist(self): "calendar": "standard", } + # Compare the result agaisnt the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time2.encoding == expected.time.encoding def test_decodes_all_time_coordinates_and_time_bounds(self): + # Create the input dataset. ds = xr.Dataset( coords={ "time": xr.DataArray( @@ -1197,7 +805,7 @@ def test_decodes_all_time_coordinates_and_time_bounds(self): }, ) - result = decode_time(ds) + # Create the expected dataset expected = xr.Dataset( coords={ "time": xr.DataArray( @@ -1286,10 +894,6 @@ def test_decodes_all_time_coordinates_and_time_bounds(self): ), }, ) - - assert result.identical(expected) - - # Check the encoding is preserved. expected.time.encoding = { "units": "months since 2000-01-01", "calendar": "standard", @@ -1303,6 +907,9 @@ def test_decodes_all_time_coordinates_and_time_bounds(self): "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time2.encoding == expected.time2.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding @@ -1335,7 +942,7 @@ def test_decodes_time_coords_and_bounds_without_calendar_attr_set(self, caplog): }, ) - result = decode_time(ds) + # Create the expected dataset expected = xr.Dataset( coords={ "time": xr.DataArray( @@ -1399,10 +1006,6 @@ def test_decodes_time_coords_and_bounds_without_calendar_attr_set(self, caplog): ), }, ) - - assert result.identical(expected) - - # Check the encoding is preserved. expected.time.encoding = { "units": "months since 2000-01-01", "calendar": "standard", @@ -1412,64 +1015,31 @@ def test_decodes_time_coords_and_bounds_without_calendar_attr_set(self, caplog): "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding def test_decode_time_in_days(self): - ds = generate_dataset(decode_times=False, cf_compliant=True, has_bounds=True) + ds = generate_dataset( + decode_times=False, cf_compliant=True, has_bounds=True + ).isel(time=slice(0, 3)) - result = decode_time(ds) - - # Generate an expected dataset with decoded CF compliant time units. + # Create the expected dataset expected = ds.copy() expected["time"] = xr.DataArray( name="time", - data=np.array( - [ - cftime.DatetimeGregorian( - 2000, 1, 1, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 2, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 3, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 4, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 5, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 6, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 7, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 8, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 9, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 10, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 11, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 12, 0, 0, 0, 0, has_year_zero=False - ), + data=np.array( + [ cftime.DatetimeGregorian( - 2000, 1, 13, 0, 0, 0, 0, has_year_zero=False + 2000, 1, 1, 0, 0, 0, 0, has_year_zero=False ), cftime.DatetimeGregorian( - 2000, 1, 14, 0, 0, 0, 0, has_year_zero=False + 2000, 1, 2, 0, 0, 0, 0, has_year_zero=False ), cftime.DatetimeGregorian( - 2000, 1, 15, 0, 0, 0, 0, has_year_zero=False + 2000, 1, 3, 0, 0, 0, 0, has_year_zero=False ), ], dtype="object", @@ -1504,102 +1074,6 @@ def test_decode_time_in_days(self): 2000, 1, 3, 0, 0, 0, 0, has_year_zero=False ), ], - [ - cftime.DatetimeGregorian( - 2000, 1, 3, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 4, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 4, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 5, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 5, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 6, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 6, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 7, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 7, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 8, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 8, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 9, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 9, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 10, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 10, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 11, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 11, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 12, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 12, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 13, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 13, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 14, 0, 0, 0, 0, has_year_zero=False - ), - ], - [ - cftime.DatetimeGregorian( - 2000, 1, 14, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 1, 15, 0, 0, 0, 0, has_year_zero=False - ), - ], ], dtype="object", ), @@ -1612,10 +1086,6 @@ def test_decode_time_in_days(self): "standard_name": "time", "bounds": "time_bnds", } - - assert result.identical(expected) - - # Check encoding is preserved. expected.time.encoding = { "units": "days since 2000-01-01", "calendar": "standard", @@ -1625,6 +1095,9 @@ def test_decode_time_in_days(self): "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding @@ -1636,7 +1109,7 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_s ds.time.attrs["calendar"] = calendar ds.time.attrs["units"] = "months since 2000-01-01" - result = decode_time(ds) + # Create the expected dataset expected = xr.Dataset( { "time": xr.DataArray( @@ -1698,9 +1171,6 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_s ), } ) - assert result.identical(expected) - - # Check the encoding is preserved. expected.time.encoding = { "units": "months since 2000-01-01", "calendar": "standard", @@ -1710,6 +1180,9 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_s "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding @@ -1721,7 +1194,7 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_m ds.time.attrs["calendar"] = calendar ds.time.attrs["units"] = "months since 2000-01-15" - result = decode_time(ds) + # Create the expected dataset expected = xr.Dataset( { "time": xr.DataArray( @@ -1764,9 +1237,6 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_m ), } ) - assert result.identical(expected) - - # Check the encoding is preserved. expected.time.encoding = { "units": "months since 2000-01-15", "calendar": "standard", @@ -1776,6 +1246,9 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_m "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding @@ -1787,7 +1260,7 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_e ds.time.attrs["calendar"] = calendar ds.time.attrs["units"] = "months since 1999-12-31" - result = decode_time(ds) + # Create the expected dataset expected = xr.Dataset( { "time": xr.DataArray( @@ -1830,9 +1303,6 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_e ), } ) - assert result.identical(expected) - - # Check the encoding is preserved. expected.time.encoding = { "units": "months since 1999-12-31", "calendar": "standard", @@ -1842,6 +1312,9 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_at_the_e "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding @@ -1853,8 +1326,7 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_on_a_lea ds.time.attrs["calendar"] = calendar ds.time.attrs["units"] = "months since 2000-02-29" - result = decode_time(ds) - + # Create the expected dataset expected = xr.Dataset( { "time": xr.DataArray( @@ -1897,9 +1369,6 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_on_a_lea ), } ) - assert result.identical(expected) - - # Check the encoding is preserved. expected.time.encoding = { "units": "months since 2000-02-29", "calendar": "standard", @@ -1909,6 +1378,9 @@ def test_decodes_time_coords_and_bounds_in_months_with_a_reference_date_on_a_lea "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding @@ -1921,8 +1393,7 @@ def test_decodes_time_coords_and_bounds_in_years_with_a_reference_date_in_the_mi ds.time.attrs["calendar"] = calendar ds.time.attrs["units"] = "years since 2000-06-01" - result = decode_time(ds) - + # Create the expected dataset expected = xr.Dataset( { "time": xr.DataArray( @@ -1965,9 +1436,6 @@ def test_decodes_time_coords_and_bounds_in_years_with_a_reference_date_in_the_mi ), } ) - assert result.identical(expected) - - # Check the encoding is preserved. expected.time.encoding = { "units": "years since 2000-06-01", "calendar": "standard", @@ -1977,6 +1445,9 @@ def test_decodes_time_coords_and_bounds_in_years_with_a_reference_date_in_the_mi "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding @@ -1989,8 +1460,7 @@ def test_decodes_time_coords_and_bounds_in_years_with_a_reference_date_on_a_leap ds.time.attrs["calendar"] = calendar ds.time.attrs["units"] = "years since 2000-02-29" - result = decode_time(ds) - + # Create the expected dataset expected = xr.Dataset( { "time": xr.DataArray( @@ -2033,9 +1503,6 @@ def test_decodes_time_coords_and_bounds_in_years_with_a_reference_date_on_a_leap ), } ) - assert result.identical(expected) - - # Check the encoding is preserved. expected.time.encoding = { "units": "years since 2000-02-29", "calendar": "standard", @@ -2045,6 +1512,9 @@ def test_decodes_time_coords_and_bounds_in_years_with_a_reference_date_on_a_leap "calendar": "standard", } + # Compare the result against the expected. + result = decode_time(ds) + assert result.identical(expected) assert result.time.encoding == expected.time.encoding assert result.time_bnds.encoding == expected.time_bnds.encoding @@ -2056,159 +1526,30 @@ def setup(self): decode_times=True, cf_compliant=False, has_bounds=True ) - def test_centers_time_coords_and_converts_datetime_dtype_to_cftime_object_type( - self, - ): - ds = generate_dataset(decode_times=True, cf_compliant=False, has_bounds=True) - - # Create a dataset with uncentered time coordinates that are decoded as - # dtype="datetime[ns]" - ds_uncentered = ds.copy() - ds_uncentered["time"] = xr.DataArray( - data=np.array( - [ - "2000-01-31T12:00:00.000000000", - "2000-02-29T12:00:00.000000000", - "2000-03-31T12:00:00.000000000", - "2000-04-30T00:00:00.000000000", - "2000-05-31T12:00:00.000000000", - "2000-06-30T00:00:00.000000000", - "2000-07-31T12:00:00.000000000", - "2000-08-31T12:00:00.000000000", - "2000-09-30T00:00:00.000000000", - "2000-10-16T12:00:00.000000000", - "2000-11-30T00:00:00.000000000", - "2000-12-31T12:00:00.000000000", - "2001-01-31T12:00:00.000000000", - "2001-02-28T00:00:00.000000000", - "2001-12-31T12:00:00.000000000", - ], - dtype="datetime64[ns]", - ), - dims=ds.time.dims, - attrs=ds.time.attrs, - ) - ds_uncentered.time.encoding = { - "source": None, - "original_shape": ds.time.data.shape, - "dtype": np.dtype("float64"), - "units": "days since 2000-01-01", - "calendar": "standard", - "_FillValue": False, - } - - # Compare result of the method against the expected. - result = _postprocess_dataset(ds_uncentered, center_times=True) - expected = ds.copy() - expected["time"] = xr.DataArray( - name="time", - data=np.array( - [ - cftime.DatetimeGregorian( - 2000, 1, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 2, 15, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 3, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 4, 16, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 5, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 6, 16, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 7, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 8, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 9, 16, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 10, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 11, 16, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 12, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 1, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 2, 15, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 12, 16, 12, 0, 0, 0, has_year_zero=False - ), - ], - dtype="object", - ), - dims="time", - attrs={ - "long_name": "time", - "standard_name": "time", - "axis": "T", - "bounds": "time_bnds", - }, - ) - - expected.time.encoding = { - "source": None, - "original_shape": (15,), - "dtype": np.dtype("float64"), - "units": "days since 2000-01-01", - "calendar": "standard", - "_FillValue": False, - } - - # Compare result of the function against the expected. - assert result.identical(expected) - assert result.time.encoding == expected.time.encoding - def test_centers_time_coordinates_and_maintains_cftime_object_type(self): - # Create a dataset with uncentered time coordinates - ds = generate_dataset(decode_times=True, cf_compliant=False, has_bounds=True) + # Create the input dataset with uncentered time coordinates + ds = generate_dataset( + decode_times=True, cf_compliant=False, has_bounds=True + ).isel(time=slice(0, 3)) uncentered_time = np.array( [ cftime.DatetimeGregorian(2000, 1, 31, 12, 0, 0, 0), cftime.DatetimeGregorian(2000, 2, 29, 12, 0, 0, 0), cftime.DatetimeGregorian(2000, 3, 31, 12, 0, 0, 0), - cftime.DatetimeGregorian(2000, 4, 30, 0, 0, 0, 0), - cftime.DatetimeGregorian(2000, 5, 31, 12, 0, 0, 0), - cftime.DatetimeGregorian(2000, 6, 30, 0, 0, 0, 0), - cftime.DatetimeGregorian(2000, 7, 31, 12, 0, 0, 0), - cftime.DatetimeGregorian(2000, 8, 31, 12, 0, 0, 0), - cftime.DatetimeGregorian(2000, 9, 30, 0, 0, 0, 0), - cftime.DatetimeGregorian(2000, 10, 16, 12, 0, 0, 0), - cftime.DatetimeGregorian(2000, 11, 30, 0, 0, 0, 0), - cftime.DatetimeGregorian(2000, 12, 31, 12, 0, 0, 0), - cftime.DatetimeGregorian(2001, 1, 31, 12, 0, 0, 0), - cftime.DatetimeGregorian(2001, 2, 28, 0, 0, 0, 0), - cftime.DatetimeGregorian(2001, 12, 31, 12, 0, 0, 0), ], dtype="object", ) ds.time.data[:] = uncentered_time ds.time.encoding = { "source": None, - "original_shape": ds.time.data.shape, + "original_shape": ds.time.shape, "dtype": np.dtype("float64"), "units": "days since 2000-01-01", "calendar": "standard", "_FillValue": False, } - # Compare result of the method against the expected. - result = _postprocess_dataset(ds, center_times=True) + # Create the expected dataset. expected = ds.copy() expected["time"] = xr.DataArray( name="time", @@ -2223,42 +1564,6 @@ def test_centers_time_coordinates_and_maintains_cftime_object_type(self): cftime.DatetimeGregorian( 2000, 3, 16, 12, 0, 0, 0, has_year_zero=False ), - cftime.DatetimeGregorian( - 2000, 4, 16, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 5, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 6, 16, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 7, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 8, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 9, 16, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 10, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 11, 16, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2000, 12, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 1, 16, 12, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 2, 15, 0, 0, 0, 0, has_year_zero=False - ), - cftime.DatetimeGregorian( - 2001, 12, 16, 12, 0, 0, 0, has_year_zero=False - ), ], dtype="object", ), @@ -2273,18 +1578,49 @@ def test_centers_time_coordinates_and_maintains_cftime_object_type(self): expected.time.encoding = { "source": None, - "original_shape": (15,), + "original_shape": expected.time.shape, "dtype": np.dtype("float64"), "units": "days since 2000-01-01", "calendar": "standard", "_FillValue": False, } + expected["time_bnds"] = xr.DataArray( + name="time_bnds", + data=np.array( + [ + [ + cftime.DatetimeGregorian( + 2000, 1, 1, 0, 0, 0, 0, has_year_zero=False + ), + cftime.DatetimeGregorian( + 2000, 2, 1, 0, 0, 0, 0, has_year_zero=False + ), + ], + [ + cftime.DatetimeGregorian( + 2000, 2, 1, 0, 0, 0, 0, has_year_zero=False + ), + cftime.DatetimeGregorian( + 2000, 3, 1, 0, 0, 0, 0, has_year_zero=False + ), + ], + [ + cftime.DatetimeGregorian( + 2000, 3, 1, 0, 0, 0, 0, has_year_zero=False + ), + cftime.DatetimeGregorian( + 2000, 4, 1, 0, 0, 0, 0, has_year_zero=False + ), + ], + ], + dtype="object", + ), + dims=["time", "bnds"], + attrs=ds.time_bnds.attrs, + ) - # Update time bounds with centered time coordinates. - expected["time_bnds"] = ds.time_bnds.copy() - expected["time_bnds"]["time"] = expected.time - - # Compare result of the function against the expected. + # Compare result of the method against the expected. + result = _postprocess_dataset(ds, center_times=True) assert result.identical(expected) assert result.time.encoding == expected.time.encoding @@ -2328,7 +1664,7 @@ def test_adds_missing_lat_and_lon_and_time_bounds(self): def test_orients_longitude_bounds_from_180_to_360_and_sorts_with_prime_meridian_cell( self, ): - # Chunk the dataset to test method also works with Dask. + # Chunk the input dataset to test method also works with Dask. ds = xr.Dataset( coords={ "lon": xr.DataArray( diff --git a/tests/test_regrid.py b/tests/test_regrid.py index c5219b21..e2ee69ff 100644 --- a/tests/test_regrid.py +++ b/tests/test_regrid.py @@ -139,7 +139,7 @@ def setup(self): } ) - @pytest.mark.filterwarnings("ignore:.*invalid value.*true_divide.*:RuntimeWarning") + @pytest.mark.filterwarnings("ignore:.*invalid value.*divide.*:RuntimeWarning") def test_output_bounds(self): ds = fixtures.generate_dataset( decode_times=True, cf_compliant=False, has_bounds=True @@ -796,7 +796,7 @@ def test_invalid_tool(self): self.ac.horizontal("ts", mock.MagicMock(), "test") # type: ignore @requires_xesmf - @pytest.mark.filterwarnings("ignore:.*invalid value.*true_divide.*:RuntimeWarning") + @pytest.mark.filterwarnings("ignore:.*invalid value.*divide.*:RuntimeWarning") def test_convenience_methods(self): ds = fixtures.generate_dataset( decode_times=True, cf_compliant=False, has_bounds=True