roms-tools 3.3.0__py3-none-any.whl → 3.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roms_tools/__init__.py +1 -1
- roms_tools/analysis/cdr_ensemble.py +10 -13
- roms_tools/analysis/roms_output.py +5 -304
- roms_tools/{download.py → datasets/download.py} +1 -0
- roms_tools/{setup → datasets}/lat_lon_datasets.py +76 -64
- roms_tools/{setup → datasets}/river_datasets.py +9 -4
- roms_tools/datasets/roms_dataset.py +767 -0
- roms_tools/datasets/utils.py +475 -0
- roms_tools/{setup/fill.py → fill.py} +110 -13
- roms_tools/plot.py +4 -4
- roms_tools/setup/boundary_forcing.py +51 -43
- roms_tools/setup/cdr_release.py +2 -4
- roms_tools/setup/grid.py +29 -12
- roms_tools/setup/initial_conditions.py +19 -19
- roms_tools/setup/nesting.py +8 -4
- roms_tools/setup/river_forcing.py +4 -4
- roms_tools/setup/surface_forcing.py +14 -9
- roms_tools/setup/tides.py +1 -1
- roms_tools/setup/topography.py +10 -2
- roms_tools/setup/utils.py +72 -524
- roms_tools/tests/test_analysis/test_cdr_ensemble.py +4 -6
- roms_tools/tests/test_analysis/test_roms_output.py +1 -220
- roms_tools/tests/{test_setup → test_datasets}/test_lat_lon_datasets.py +4 -4
- roms_tools/tests/{test_setup → test_datasets}/test_river_datasets.py +1 -1
- roms_tools/tests/test_datasets/test_roms_dataset.py +539 -0
- roms_tools/tests/test_datasets/test_utils.py +527 -0
- roms_tools/tests/{test_setup/test_fill.py → test_fill.py} +72 -9
- roms_tools/tests/test_setup/test_boundary_forcing.py +57 -138
- roms_tools/tests/test_setup/test_cdr_release.py +4 -5
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zarr.json +293 -2021
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zarr.json +294 -2022
- roms_tools/tests/test_setup/test_grid.py +42 -1
- roms_tools/tests/test_setup/test_initial_conditions.py +3 -94
- roms_tools/tests/test_setup/test_nesting.py +2 -1
- roms_tools/tests/test_setup/test_surface_forcing.py +1 -1
- roms_tools/tests/test_setup/test_tides.py +1 -1
- roms_tools/tests/test_setup/test_utils.py +100 -15
- roms_tools/tests/test_tiling/test_partition.py +63 -15
- roms_tools/tests/test_utils.py +78 -0
- roms_tools/tiling/partition.py +81 -211
- roms_tools/utils.py +193 -0
- {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/METADATA +1 -1
- {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/RECORD +46 -170
- {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/WHEEL +1 -1
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_ALT_CO2_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_ALT_CO2_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOCr_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DON_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DON_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DONr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DONr_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOP_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOPr_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Fe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Fe_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Lig_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Lig_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NH4_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NH4_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NO3_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/O2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/O2_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/PO4_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/PO4_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/SiO3_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatChl_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatFe_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatP_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatSi_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazChl_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazFe_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazP_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spC_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spCaCO3_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spChl_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spFe_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spP_west/zarr.json +0 -54
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zooC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zooC_west/zarr.json +0 -54
- {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/licenses/LICENSE +0 -0
- {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
|
|
5
|
+
import cftime
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pytest
|
|
8
|
+
import xarray as xr
|
|
9
|
+
|
|
10
|
+
from roms_tools.datasets.utils import (
|
|
11
|
+
_select_initial_time,
|
|
12
|
+
check_dataset,
|
|
13
|
+
convert_cftime_to_datetime,
|
|
14
|
+
convert_to_float64,
|
|
15
|
+
extrapolate_deepest_to_bottom,
|
|
16
|
+
get_time_type,
|
|
17
|
+
select_relevant_fields,
|
|
18
|
+
select_relevant_times,
|
|
19
|
+
validate_start_end_time,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_convert_to_float64():
|
|
24
|
+
# Create dataset with mixed dtypes
|
|
25
|
+
ds = xr.Dataset(
|
|
26
|
+
{
|
|
27
|
+
"temp": (("x",), np.array([1, 2, 3], dtype=np.float32)),
|
|
28
|
+
"mask_rho": (("x",), np.array([0, 1, 0], dtype=np.int16)),
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
out = convert_to_float64(ds)
|
|
33
|
+
|
|
34
|
+
# Non-mask variables should be converted to float64
|
|
35
|
+
assert out["temp"].dtype == np.float64
|
|
36
|
+
|
|
37
|
+
# Mask variables should keep their original dtype
|
|
38
|
+
assert out["mask_rho"].dtype == np.int16
|
|
39
|
+
|
|
40
|
+
# Values should be preserved
|
|
41
|
+
xr.testing.assert_equal(out["temp"], ds["temp"].astype("float64"))
|
|
42
|
+
xr.testing.assert_equal(out["mask_rho"], ds["mask_rho"])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_extrapolate_deepest_to_bottom():
|
|
46
|
+
data = np.array(
|
|
47
|
+
[
|
|
48
|
+
[1, 2],
|
|
49
|
+
[3, 4],
|
|
50
|
+
[5, np.nan],
|
|
51
|
+
[np.nan, np.nan],
|
|
52
|
+
[np.nan, np.nan],
|
|
53
|
+
]
|
|
54
|
+
)
|
|
55
|
+
ds = xr.Dataset({"var": (("s_rho", "x"), data)})
|
|
56
|
+
|
|
57
|
+
ds_filled = extrapolate_deepest_to_bottom(ds, "s_rho")
|
|
58
|
+
|
|
59
|
+
expected = np.array(
|
|
60
|
+
[
|
|
61
|
+
[1, 2],
|
|
62
|
+
[3, 4],
|
|
63
|
+
[5, 4],
|
|
64
|
+
[5, 4],
|
|
65
|
+
[5, 4],
|
|
66
|
+
]
|
|
67
|
+
)
|
|
68
|
+
np.testing.assert_array_equal(ds_filled["var"].values, expected)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# tests for validate_start_end_time
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_valid_times():
|
|
75
|
+
start = datetime(2024, 1, 1, 12, 0)
|
|
76
|
+
end = start + timedelta(hours=3)
|
|
77
|
+
|
|
78
|
+
# Should not raise
|
|
79
|
+
validate_start_end_time(start, end)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_none_times():
|
|
83
|
+
# None for both is allowed
|
|
84
|
+
validate_start_end_time(None, None)
|
|
85
|
+
|
|
86
|
+
# Only start_time provided
|
|
87
|
+
validate_start_end_time(datetime(2024, 1, 1), None)
|
|
88
|
+
|
|
89
|
+
# Only end_time provided
|
|
90
|
+
validate_start_end_time(None, datetime(2024, 1, 2))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_equal_times():
|
|
94
|
+
t = datetime(2024, 1, 1, 12, 0)
|
|
95
|
+
|
|
96
|
+
# end_time == start_time is allowed
|
|
97
|
+
validate_start_end_time(t, t)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_invalid_start_type():
|
|
101
|
+
with pytest.raises(TypeError):
|
|
102
|
+
validate_start_end_time("2024-01-01", None)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_invalid_end_type():
|
|
106
|
+
with pytest.raises(TypeError):
|
|
107
|
+
validate_start_end_time(None, 123)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_end_time_before_start_time():
|
|
111
|
+
start = datetime(2024, 1, 2)
|
|
112
|
+
end = datetime(2024, 1, 1)
|
|
113
|
+
|
|
114
|
+
with pytest.raises(ValueError):
|
|
115
|
+
validate_start_end_time(start, end)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# tests for check_dataset
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_valid_dataset():
|
|
122
|
+
ds = xr.Dataset(
|
|
123
|
+
data_vars={
|
|
124
|
+
"temp": (("x", "y"), [[1, 2], [3, 4]]),
|
|
125
|
+
"salt": (("x", "y"), [[5, 6], [7, 8]]),
|
|
126
|
+
},
|
|
127
|
+
coords={
|
|
128
|
+
"x": [0, 1],
|
|
129
|
+
"y": [0, 1],
|
|
130
|
+
},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
dim_names = {"xdim": "x", "ydim": "y"}
|
|
134
|
+
var_names = {"temperature": "temp", "salinity": "salt"}
|
|
135
|
+
|
|
136
|
+
# Should NOT raise
|
|
137
|
+
check_dataset(ds, dim_names, var_names)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_missing_required_dimension():
|
|
141
|
+
ds = xr.Dataset(data_vars={"temp": (("x",), [1])}, coords={"x": [0]})
|
|
142
|
+
|
|
143
|
+
dim_names = {"xdim": "x", "ydim": "y"}
|
|
144
|
+
|
|
145
|
+
with pytest.raises(ValueError, match="missing"):
|
|
146
|
+
check_dataset(ds, dim_names=dim_names)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_missing_required_variable():
|
|
150
|
+
ds = xr.Dataset(data_vars={"temp": (("x",), [1])}, coords={"x": [0]})
|
|
151
|
+
|
|
152
|
+
var_names = {"temperature": "temp", "salinity": "salt"}
|
|
153
|
+
|
|
154
|
+
with pytest.raises(ValueError, match="missing"):
|
|
155
|
+
check_dataset(ds, var_names=var_names)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_optional_variables_warning(caplog):
|
|
159
|
+
ds = xr.Dataset(data_vars={"temp": (("x",), [1])}, coords={"x": [0]})
|
|
160
|
+
|
|
161
|
+
opt_var_names = {"some_opt": "opt_var"}
|
|
162
|
+
|
|
163
|
+
with caplog.at_level(logging.WARNING):
|
|
164
|
+
# Should not raise, only warn
|
|
165
|
+
check_dataset(ds, opt_var_names=opt_var_names)
|
|
166
|
+
|
|
167
|
+
assert "Optional variables missing" in caplog.text
|
|
168
|
+
assert "opt_var" in caplog.text
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_no_checks_pass():
|
|
172
|
+
ds = xr.Dataset(data_vars={"temp": (("x",), [1])}, coords={"x": [0]})
|
|
173
|
+
|
|
174
|
+
# Should not fail because no dim_names/var_names provided
|
|
175
|
+
check_dataset(ds)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# tests for select_relevant_fields
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_select_relevant_fields_basic():
|
|
182
|
+
"""Keep only required + optional variables and drop others."""
|
|
183
|
+
ds = xr.Dataset(
|
|
184
|
+
{
|
|
185
|
+
"temp": (("x",), np.arange(5)),
|
|
186
|
+
"salt": (("x",), np.arange(5) * 2),
|
|
187
|
+
"u": (("x",), np.arange(5) * 3),
|
|
188
|
+
"mask": (("x",), np.ones(5)),
|
|
189
|
+
"extra": (("x",), np.zeros(5)),
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
keep_vars = ["temp", "u"] # required + optional
|
|
194
|
+
|
|
195
|
+
out = select_relevant_fields(ds, keep_vars)
|
|
196
|
+
|
|
197
|
+
assert set(out.data_vars) == {"temp", "u", "mask"}
|
|
198
|
+
assert "salt" not in out
|
|
199
|
+
assert "extra" not in out
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_select_relevant_fields_does_not_modify_input():
|
|
203
|
+
"""Function must not mutate the original dataset."""
|
|
204
|
+
ds = xr.Dataset(
|
|
205
|
+
{
|
|
206
|
+
"temp": (("x",), np.arange(5)),
|
|
207
|
+
"salt": (("x",), np.arange(5)),
|
|
208
|
+
"mask": (("x",), np.ones(5)),
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
ds_copy = deepcopy(ds)
|
|
213
|
+
|
|
214
|
+
_ = select_relevant_fields(ds, ["temp"])
|
|
215
|
+
|
|
216
|
+
# ensure original dataset is unchanged
|
|
217
|
+
assert set(ds.data_vars) == set(ds_copy.data_vars)
|
|
218
|
+
assert "salt" in ds
|
|
219
|
+
assert "mask" in ds
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def test_select_relevant_fields_duplicate_keep_names():
|
|
223
|
+
"""Duplicate keep names should not cause issues."""
|
|
224
|
+
ds = xr.Dataset(
|
|
225
|
+
{
|
|
226
|
+
"temp": (("x",), np.arange(5)),
|
|
227
|
+
"mask": (("x",), np.ones(5)),
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
keep_vars = ["temp", "temp"] # duplicates
|
|
232
|
+
|
|
233
|
+
out = select_relevant_fields(ds, keep_vars)
|
|
234
|
+
|
|
235
|
+
assert set(out.data_vars) == {"temp", "mask"}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# tests for select_relevant_times
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def make_time_dataset(times):
|
|
242
|
+
return xr.Dataset(
|
|
243
|
+
data_vars={"var": ("time", np.arange(len(times)))},
|
|
244
|
+
coords={"time": times},
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def test_missing_time_dimension(caplog):
|
|
249
|
+
ds = xr.Dataset({"x": ("x", [1, 2])})
|
|
250
|
+
|
|
251
|
+
with caplog.at_level(logging.WARNING):
|
|
252
|
+
out = select_relevant_times(
|
|
253
|
+
ds, "time", datetime(2024, 1, 1), datetime(2024, 1, 2)
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
assert "does not contain time dimension" in caplog.text
|
|
257
|
+
assert out is ds # unchanged
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_climatology_must_have_12_steps():
|
|
261
|
+
times = np.arange(10).astype("datetime64[D]")
|
|
262
|
+
ds = make_time_dataset(times)
|
|
263
|
+
|
|
264
|
+
with pytest.raises(ValueError):
|
|
265
|
+
select_relevant_times(
|
|
266
|
+
ds,
|
|
267
|
+
"time",
|
|
268
|
+
"time",
|
|
269
|
+
datetime(2024, 1, 1),
|
|
270
|
+
climatology=True,
|
|
271
|
+
end_time=datetime(2024, 1, 2),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_climatology_pass_through():
|
|
276
|
+
times = np.arange(12)
|
|
277
|
+
ds = make_time_dataset(times)
|
|
278
|
+
|
|
279
|
+
out = select_relevant_times(
|
|
280
|
+
ds,
|
|
281
|
+
"time",
|
|
282
|
+
"time",
|
|
283
|
+
datetime(2024, 1, 1),
|
|
284
|
+
end_time=datetime(2024, 1, 10),
|
|
285
|
+
climatology=True,
|
|
286
|
+
)
|
|
287
|
+
assert out.equals(ds)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def test_int_time_rejected():
|
|
291
|
+
ds = xr.Dataset(
|
|
292
|
+
data_vars={"var": ("time", [1, 2, 3])},
|
|
293
|
+
coords={"time": [1, 2, 3]},
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
with pytest.raises(ValueError):
|
|
297
|
+
select_relevant_times(
|
|
298
|
+
ds, "time", "time", datetime(2024, 1, 1), datetime(2024, 1, 2)
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def test_cftime_conversion(monkeypatch):
|
|
303
|
+
times = xr.DataArray(
|
|
304
|
+
[cftime.DatetimeGregorian(2024, 1, i + 1) for i in range(3)], dims="time"
|
|
305
|
+
)
|
|
306
|
+
ds = xr.Dataset({"var": ("time", [1, 2, 3])}, coords={"time": times})
|
|
307
|
+
|
|
308
|
+
# Mock conversion function to ensure it is called
|
|
309
|
+
def mock_convert(t):
|
|
310
|
+
return np.array(
|
|
311
|
+
["2024-01-01", "2024-01-02", "2024-01-03"], dtype="datetime64[ns]"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
monkeypatch.setattr(
|
|
315
|
+
"roms_tools.datasets.utils.convert_cftime_to_datetime", mock_convert
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
out = select_relevant_times(
|
|
319
|
+
ds, "time", "time", datetime(2024, 1, 1), datetime(2024, 1, 3)
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
assert np.issubdtype(out["time"].dtype, np.datetime64)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def test_time_range_selection():
|
|
326
|
+
times = np.array(
|
|
327
|
+
["2024-01-01", "2024-01-02", "2024-01-05", "2024-01-10"], dtype="datetime64[ns]"
|
|
328
|
+
)
|
|
329
|
+
ds = make_time_dataset(times)
|
|
330
|
+
|
|
331
|
+
out = select_relevant_times(
|
|
332
|
+
ds,
|
|
333
|
+
"time",
|
|
334
|
+
"time",
|
|
335
|
+
datetime(2024, 1, 2),
|
|
336
|
+
datetime(2024, 1, 7),
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
# Should include:
|
|
340
|
+
# - closest before start: 2024-01-02
|
|
341
|
+
# - records inside strict range: 2024-01-05
|
|
342
|
+
# - closest after end: 2024-01-10
|
|
343
|
+
expected_times = np.array(
|
|
344
|
+
["2024-01-02", "2024-01-05", "2024-01-10"],
|
|
345
|
+
dtype="datetime64[ns]",
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
assert np.array_equal(out["time"].values, expected_times)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def test_range_selection_missing_before(caplog):
|
|
352
|
+
times = np.array(["2024-01-05", "2024-01-10"], dtype="datetime64[ns]")
|
|
353
|
+
ds = make_time_dataset(times)
|
|
354
|
+
|
|
355
|
+
with caplog.at_level(logging.WARNING):
|
|
356
|
+
out = select_relevant_times(
|
|
357
|
+
ds,
|
|
358
|
+
"time",
|
|
359
|
+
"time",
|
|
360
|
+
datetime(2024, 1, 1),
|
|
361
|
+
datetime(2024, 1, 12),
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
assert "No records found at or before the start_time" in caplog.text
|
|
365
|
+
# Should fallback to first record
|
|
366
|
+
assert out["time"].values[0] == np.datetime64("2024-01-05")
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def test_range_selection_missing_after(caplog):
|
|
370
|
+
times = np.array(["2024-01-01", "2024-01-02"], dtype="datetime64[ns]")
|
|
371
|
+
ds = make_time_dataset(times)
|
|
372
|
+
|
|
373
|
+
with caplog.at_level(logging.WARNING):
|
|
374
|
+
out = select_relevant_times(
|
|
375
|
+
ds,
|
|
376
|
+
"time",
|
|
377
|
+
"time",
|
|
378
|
+
datetime(2024, 1, 1),
|
|
379
|
+
datetime(2024, 1, 10),
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
assert "No records found at or after the end_time" in caplog.text
|
|
383
|
+
assert out["time"].values[-1] == np.datetime64("2024-01-02")
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# Tests for _select_initial_time
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def test_initial_time_exact_match():
|
|
390
|
+
times = np.array(["2024-01-01", "2024-01-02"], dtype="datetime64[ns]")
|
|
391
|
+
ds = make_time_dataset(times)
|
|
392
|
+
|
|
393
|
+
out = _select_initial_time(
|
|
394
|
+
ds,
|
|
395
|
+
"time",
|
|
396
|
+
"time",
|
|
397
|
+
datetime(2024, 1, 2),
|
|
398
|
+
climatology=False,
|
|
399
|
+
allow_flex_time=False,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
assert out["time"].values == np.datetime64("2024-01-02")
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def test_initial_time_no_exact_match():
|
|
406
|
+
times = np.array(["2024-01-01", "2024-01-03"], dtype="datetime64[ns]")
|
|
407
|
+
ds = make_time_dataset(times)
|
|
408
|
+
|
|
409
|
+
with pytest.raises(ValueError):
|
|
410
|
+
_select_initial_time(
|
|
411
|
+
ds,
|
|
412
|
+
"time",
|
|
413
|
+
"time",
|
|
414
|
+
datetime(2024, 1, 2),
|
|
415
|
+
climatology=False,
|
|
416
|
+
allow_flex_time=False,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def test_initial_flexible_time():
|
|
421
|
+
times = np.array(["2024-01-01", "2024-01-02", "2024-01-15"], dtype="datetime64[ns]")
|
|
422
|
+
ds = make_time_dataset(times)
|
|
423
|
+
|
|
424
|
+
out = _select_initial_time(
|
|
425
|
+
ds,
|
|
426
|
+
"time",
|
|
427
|
+
"time",
|
|
428
|
+
datetime(2024, 1, 1),
|
|
429
|
+
climatology=False,
|
|
430
|
+
allow_flex_time=True,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
assert out["time"].values == np.datetime64("2024-01-01")
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def test_initial_flexible_time_out_of_range():
|
|
437
|
+
times = np.array(["2024-01-10", "2024-01-11"], dtype="datetime64[ns]")
|
|
438
|
+
ds = make_time_dataset(times)
|
|
439
|
+
|
|
440
|
+
with pytest.raises(ValueError):
|
|
441
|
+
_select_initial_time(
|
|
442
|
+
ds,
|
|
443
|
+
"time",
|
|
444
|
+
"time",
|
|
445
|
+
datetime(2024, 1, 1),
|
|
446
|
+
climatology=False,
|
|
447
|
+
allow_flex_time=True,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
# tests for get_time_type
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def test_get_time_type_datetime():
|
|
455
|
+
times = np.array(["2020-01-01", "2020-01-02"], dtype="datetime64[ns]")
|
|
456
|
+
da = xr.DataArray(times)
|
|
457
|
+
assert get_time_type(da) == "datetime"
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def test_get_time_type_cftime():
|
|
461
|
+
times = np.array(
|
|
462
|
+
[
|
|
463
|
+
cftime.DatetimeNoLeap(2000, 1, 1),
|
|
464
|
+
cftime.DatetimeNoLeap(2000, 1, 2),
|
|
465
|
+
],
|
|
466
|
+
dtype=object,
|
|
467
|
+
)
|
|
468
|
+
da = xr.DataArray(times)
|
|
469
|
+
assert get_time_type(da) == "cftime"
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def test_get_time_type_integer():
|
|
473
|
+
da = xr.DataArray(np.array([1, 2, 3], dtype=int))
|
|
474
|
+
assert get_time_type(da) == "int"
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def test_get_time_type_unsupported_type():
|
|
478
|
+
da = xr.DataArray(np.array(["a", "b"], dtype=object))
|
|
479
|
+
with pytest.raises(ValueError, match="Unsupported data type"):
|
|
480
|
+
get_time_type(da)
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def test_get_time_type_invalid_input_type():
|
|
484
|
+
da = xr.DataArray("not-an-array")
|
|
485
|
+
with pytest.raises(ValueError):
|
|
486
|
+
get_time_type(da)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
# Tests for convert_cftime_to_datetime
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def test_convert_cftime_to_datetime_basic():
|
|
493
|
+
arr = np.array(
|
|
494
|
+
[
|
|
495
|
+
cftime.DatetimeNoLeap(2000, 1, 1),
|
|
496
|
+
cftime.DatetimeNoLeap(2000, 1, 2),
|
|
497
|
+
]
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
converted = convert_cftime_to_datetime(arr)
|
|
501
|
+
|
|
502
|
+
assert converted.dtype == "datetime64[ns]"
|
|
503
|
+
assert converted[0] == np.datetime64("2000-01-01")
|
|
504
|
+
assert converted[1] == np.datetime64("2000-01-02")
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def test_convert_cftime_to_datetime_mixed_inputs():
|
|
508
|
+
arr = np.array(
|
|
509
|
+
[
|
|
510
|
+
cftime.DatetimeNoLeap(2000, 1, 1),
|
|
511
|
+
"2001-01-01",
|
|
512
|
+
np.datetime64("2002-01-01"),
|
|
513
|
+
],
|
|
514
|
+
dtype=object,
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
converted = convert_cftime_to_datetime(arr)
|
|
518
|
+
|
|
519
|
+
assert converted[0] == np.datetime64("2000-01-01")
|
|
520
|
+
assert converted[1] == np.datetime64("2001-01-01")
|
|
521
|
+
assert converted[2] == np.datetime64("2002-01-01")
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def test_convert_cftime_to_datetime_returns_numpy_array():
|
|
525
|
+
arr = np.array([cftime.DatetimeNoLeap(1999, 12, 31)])
|
|
526
|
+
converted = convert_cftime_to_datetime(arr)
|
|
527
|
+
assert isinstance(converted, np.ndarray)
|
|
@@ -2,7 +2,7 @@ import numpy as np
|
|
|
2
2
|
import pytest
|
|
3
3
|
import xarray as xr
|
|
4
4
|
|
|
5
|
-
from roms_tools.
|
|
5
|
+
from roms_tools.fill import LateralFill
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
@pytest.mark.parametrize(
|
|
@@ -19,12 +19,12 @@ def test_lateral_fill_no_nans(data_fixture, request):
|
|
|
19
19
|
data = request.getfixturevalue(data_fixture)
|
|
20
20
|
lateral_fill = LateralFill(
|
|
21
21
|
data.ds["mask"],
|
|
22
|
-
|
|
22
|
+
(data.dim_names["latitude"], data.dim_names["longitude"]),
|
|
23
23
|
)
|
|
24
24
|
if "mask_vel" in data.ds.data_vars:
|
|
25
25
|
lateral_fill_vel = LateralFill(
|
|
26
26
|
data.ds["mask_vel"],
|
|
27
|
-
|
|
27
|
+
(data.dim_names["latitude"], data.dim_names["longitude"]),
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
for var in data.var_names:
|
|
@@ -40,10 +40,10 @@ def test_lateral_fill_no_nans(data_fixture, request):
|
|
|
40
40
|
def test_lateral_fill_correct_order_of_magnitude(coarsened_cesm_bgc_data):
|
|
41
41
|
lateral_fill = LateralFill(
|
|
42
42
|
coarsened_cesm_bgc_data.ds["mask"],
|
|
43
|
-
|
|
43
|
+
(
|
|
44
44
|
coarsened_cesm_bgc_data.dim_names["latitude"],
|
|
45
45
|
coarsened_cesm_bgc_data.dim_names["longitude"],
|
|
46
|
-
|
|
46
|
+
),
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
ALK = coarsened_cesm_bgc_data.ds["ALK"]
|
|
@@ -78,21 +78,21 @@ def test_lateral_fill_reproducibility(data_fixture, request):
|
|
|
78
78
|
data = request.getfixturevalue(data_fixture)
|
|
79
79
|
lateral_fill0 = LateralFill(
|
|
80
80
|
data.ds["mask"],
|
|
81
|
-
|
|
81
|
+
(data.dim_names["latitude"], data.dim_names["longitude"]),
|
|
82
82
|
)
|
|
83
83
|
if "mask_vel" in data.ds.data_vars:
|
|
84
84
|
lateral_fill_vel0 = LateralFill(
|
|
85
85
|
data.ds["mask_vel"],
|
|
86
|
-
|
|
86
|
+
(data.dim_names["latitude"], data.dim_names["longitude"]),
|
|
87
87
|
)
|
|
88
88
|
lateral_fill1 = LateralFill(
|
|
89
89
|
data.ds["mask"],
|
|
90
|
-
|
|
90
|
+
(data.dim_names["latitude"], data.dim_names["longitude"]),
|
|
91
91
|
)
|
|
92
92
|
if "mask_vel" in data.ds.data_vars:
|
|
93
93
|
lateral_fill_vel1 = LateralFill(
|
|
94
94
|
data.ds["mask_vel"],
|
|
95
|
-
|
|
95
|
+
(data.dim_names["latitude"], data.dim_names["longitude"]),
|
|
96
96
|
)
|
|
97
97
|
|
|
98
98
|
ds0 = data.ds.copy()
|
|
@@ -116,3 +116,66 @@ def test_lateral_fill_reproducibility(data_fixture, request):
|
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
assert ds0.equals(ds1)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_lateralfill_raises_notimplemented_for_non2d_mask():
|
|
122
|
+
mask_3d = xr.DataArray(np.ones((3, 3, 3)), dims=("eta_rho", "xi_rho", "s_rho"))
|
|
123
|
+
with pytest.raises(
|
|
124
|
+
NotImplementedError, match="LateralFill currently supports only 2D masks"
|
|
125
|
+
):
|
|
126
|
+
LateralFill(mask_3d, dims=("eta_rho", "xi_rho"))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_lateralfill_raises_valueerror_for_list_of_dims():
|
|
130
|
+
mask = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "xi_rho"))
|
|
131
|
+
with pytest.raises(TypeError, match="must be a tuple"):
|
|
132
|
+
LateralFill(mask, dims=["eta_rho", "xi_rho"])
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_lateralfill_raises_valueerror_for_wrong_number_of_dims():
|
|
136
|
+
mask = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "xi_rho"))
|
|
137
|
+
|
|
138
|
+
with pytest.raises(ValueError, match="must contain exactly two"):
|
|
139
|
+
LateralFill(mask, dims=("eta_rho", "xi_rho", "extra_dim")) # 3 dims
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_lateralfill_raises_valueerror_for_mask_dim_order_mismatch():
|
|
143
|
+
mask = xr.DataArray(np.ones((3, 3)), dims=("xi_rho", "eta_rho")) # dims reversed
|
|
144
|
+
with pytest.raises(ValueError, match="dimension order is incorrect"):
|
|
145
|
+
LateralFill(mask, dims=("eta_rho", "xi_rho"))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_apply_raises_if_var_missing_dims():
|
|
149
|
+
mask = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "xi_rho"))
|
|
150
|
+
lf = LateralFill(mask, dims=("eta_rho", "xi_rho"))
|
|
151
|
+
var = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "eta_u")) # missing xi_rho
|
|
152
|
+
with pytest.raises(
|
|
153
|
+
ValueError, match="does not contain the required horizontal dimensions"
|
|
154
|
+
):
|
|
155
|
+
lf.apply(var)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_apply_raises_if_var_dim_order_mismatch():
|
|
159
|
+
mask = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "xi_rho"))
|
|
160
|
+
lf = LateralFill(mask, dims=("eta_rho", "xi_rho"))
|
|
161
|
+
var = xr.DataArray(np.ones((3, 3)), dims=("xi_rho", "eta_rho")) # wrong order
|
|
162
|
+
with pytest.raises(ValueError, match="dimension order is incorrect"):
|
|
163
|
+
lf.apply(var)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_apply_raises_if_var_has_nans_on_valid_mask():
|
|
167
|
+
# 2D mask
|
|
168
|
+
mask = xr.DataArray(np.ones((3, 3), dtype=bool), dims=("eta_rho", "xi_rho"))
|
|
169
|
+
|
|
170
|
+
lf = LateralFill(mask, dims=("eta_rho", "xi_rho"))
|
|
171
|
+
|
|
172
|
+
# Variable with a NaN at a location where mask is True
|
|
173
|
+
data = np.ones((3, 3))
|
|
174
|
+
data[1, 1] = np.nan
|
|
175
|
+
var = xr.DataArray(data, dims=("eta_rho", "xi_rho"))
|
|
176
|
+
|
|
177
|
+
# Expect ValueError
|
|
178
|
+
with pytest.raises(
|
|
179
|
+
ValueError, match="contains NaNs at grid points marked as valid"
|
|
180
|
+
):
|
|
181
|
+
lf.apply(var)
|