roms-tools 3.2.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.py → datasets/lat_lon_datasets.py} +85 -854
- roms_tools/datasets/river_datasets.py +532 -0
- 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 +42 -34
- roms_tools/setup/boundary_forcing.py +52 -44
- roms_tools/setup/cdr_release.py +2 -4
- roms_tools/setup/grid.py +42 -17
- roms_tools/setup/initial_conditions.py +23 -23
- roms_tools/setup/nesting.py +270 -94
- roms_tools/setup/river_forcing.py +5 -5
- roms_tools/setup/surface_forcing.py +17 -12
- roms_tools/setup/tides.py +1 -1
- roms_tools/setup/topography.py +13 -7
- roms_tools/setup/utils.py +103 -294
- 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.py → test_datasets/test_lat_lon_datasets.py} +25 -64
- roms_tools/tests/test_datasets/test_river_datasets.py +48 -0
- 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/ALK_ALT_CO2_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zarr.json +289 -2017
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zarr.json +294 -2022
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zarr.json +182 -182
- roms_tools/tests/test_setup/test_data/grid.zarr/h/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/zarr.json +191 -191
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/zarr.json +210 -210
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK_ALT_CO2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC_ALT_CO2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOCr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DON/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DONr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOPr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Fe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Lig/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/O2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/SiO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zarr.json +182 -182
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zooC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/salt/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/temp/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/u/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/ubar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/v/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/vbar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/zarr.json +187 -187
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Im/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Re/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Im/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Re/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/zarr.json +66 -66
- roms_tools/tests/test_setup/test_grid.py +56 -1
- roms_tools/tests/test_setup/test_initial_conditions.py +3 -94
- roms_tools/tests/test_setup/test_nesting.py +119 -30
- roms_tools/tests/test_setup/test_surface_forcing.py +2 -2
- 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.2.0.dist-info → roms_tools-3.4.0.dist-info}/METADATA +1 -1
- {roms_tools-3.2.0.dist-info → roms_tools-3.4.0.dist-info}/RECORD +190 -312
- {roms_tools-3.2.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.2.0.dist-info → roms_tools-3.4.0.dist-info}/licenses/LICENSE +0 -0
- {roms_tools-3.2.0.dist-info → roms_tools-3.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
|
+
|
|
4
|
+
import cftime
|
|
5
|
+
import numpy as np
|
|
6
|
+
import xarray as xr
|
|
7
|
+
|
|
8
|
+
from roms_tools.fill import one_dim_fill
|
|
9
|
+
from roms_tools.utils import interpolate_from_climatology
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def extrapolate_deepest_to_bottom(ds: xr.Dataset, depth_dim: str) -> xr.Dataset:
|
|
13
|
+
"""Extrapolate the deepest non-NaN values downward along a depth dimension.
|
|
14
|
+
|
|
15
|
+
For each variable in the dataset that includes the specified depth dimension,
|
|
16
|
+
missing values at the bottom are filled by propagating the deepest available
|
|
17
|
+
data downward.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
ds : xr.Dataset
|
|
22
|
+
Dataset containing variables with a depth dimension.
|
|
23
|
+
depth_dim : str
|
|
24
|
+
Name of the depth dimension (e.g., 's_rho') along which to extrapolate.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
xr.Dataset
|
|
29
|
+
Dataset with bottom NaNs filled along the specified depth dimension.
|
|
30
|
+
"""
|
|
31
|
+
for var_name in ds.data_vars:
|
|
32
|
+
if depth_dim in ds[var_name].dims:
|
|
33
|
+
ds[var_name] = one_dim_fill(ds[var_name], depth_dim, direction="forward")
|
|
34
|
+
|
|
35
|
+
return ds
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def convert_to_float64(ds: xr.Dataset) -> xr.Dataset:
|
|
39
|
+
"""Convert all non-mask data variables to float64.
|
|
40
|
+
|
|
41
|
+
Variables whose names start with ``"mask_"`` are left unchanged.
|
|
42
|
+
"""
|
|
43
|
+
dtype_map = {
|
|
44
|
+
name: ("float64" if not name.startswith("mask_") else var.dtype)
|
|
45
|
+
for name, var in ds.data_vars.items()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return ds.astype(dtype_map)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def check_dataset(
|
|
52
|
+
ds: xr.Dataset,
|
|
53
|
+
dim_names: dict[str, str] | None = None,
|
|
54
|
+
var_names: dict[str, str] | None = None,
|
|
55
|
+
opt_var_names: dict[str, str] | None = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Check if the dataset contains the specified variables and dimensions.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
ds : xr.Dataset
|
|
62
|
+
The xarray Dataset to check.
|
|
63
|
+
dim_names: dict[str, str], optional
|
|
64
|
+
Dictionary specifying the names of dimensions in the dataset.
|
|
65
|
+
var_names: dict[str, str], optional
|
|
66
|
+
Dictionary of variable names that are required in the dataset.
|
|
67
|
+
opt_var_names : dict[str, str], optional
|
|
68
|
+
Dictionary of optional variable names.
|
|
69
|
+
These variables are not strictly required, and the function will not raise an error if they are missing.
|
|
70
|
+
Default is None, meaning no optional variables are considered.
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
Raises
|
|
74
|
+
------
|
|
75
|
+
ValueError
|
|
76
|
+
If the dataset does not contain the specified variables or dimensions.
|
|
77
|
+
"""
|
|
78
|
+
if dim_names:
|
|
79
|
+
missing_dims = [dim for dim in dim_names.values() if dim not in ds.dims]
|
|
80
|
+
if missing_dims:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"Dataset does not contain all required dimensions. The following dimensions are missing: {missing_dims}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if var_names:
|
|
86
|
+
missing_vars = [var for var in var_names.values() if var not in ds.data_vars]
|
|
87
|
+
if missing_vars:
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"Dataset does not contain all required variables. The following variables are missing: {missing_vars}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if opt_var_names:
|
|
93
|
+
missing_optional_vars = [
|
|
94
|
+
var for var in opt_var_names.values() if var not in ds.data_vars
|
|
95
|
+
]
|
|
96
|
+
if missing_optional_vars:
|
|
97
|
+
logging.warning(
|
|
98
|
+
f"Optional variables missing (but not critical): {missing_optional_vars}"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def validate_start_end_time(
|
|
103
|
+
start_time: datetime | None = None, end_time: datetime | None = None
|
|
104
|
+
) -> None:
|
|
105
|
+
"""
|
|
106
|
+
Validate the provided start and end times.
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
start_time : datetime or None
|
|
111
|
+
Start of the time interval. Must be a `datetime` object if provided.
|
|
112
|
+
end_time : datetime or None
|
|
113
|
+
End of the time interval. Must be a `datetime` object if provided.
|
|
114
|
+
|
|
115
|
+
Raises
|
|
116
|
+
------
|
|
117
|
+
TypeError
|
|
118
|
+
If `start_time` or `end_time` is provided but is not a `datetime`.
|
|
119
|
+
ValueError
|
|
120
|
+
If both `start_time` and `end_time` are provided and
|
|
121
|
+
`end_time` occurs before `start_time`.
|
|
122
|
+
"""
|
|
123
|
+
if start_time is not None and not isinstance(start_time, datetime):
|
|
124
|
+
raise TypeError(
|
|
125
|
+
f"`start_time` must be a datetime object or None, "
|
|
126
|
+
f"but got {type(start_time).__name__}."
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if end_time is not None and not isinstance(end_time, datetime):
|
|
130
|
+
raise TypeError(
|
|
131
|
+
f"`end_time` must be a datetime object or None, "
|
|
132
|
+
f"but got {type(end_time).__name__}."
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if start_time is not None and end_time is not None:
|
|
136
|
+
if end_time < start_time:
|
|
137
|
+
raise ValueError(
|
|
138
|
+
f"`end_time` ({end_time}) cannot be earlier than "
|
|
139
|
+
f"`start_time` ({start_time})."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def select_relevant_fields(ds: xr.Dataset, var_names: list[str]) -> xr.Dataset:
|
|
144
|
+
"""
|
|
145
|
+
Return a subset of the dataset containing only the specified variables.
|
|
146
|
+
|
|
147
|
+
All data variables not listed in ``var_names`` are removed, except for the
|
|
148
|
+
special variable ``"mask"``, which is always retained if present.
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
ds : xr.Dataset
|
|
153
|
+
The input dataset from which variables will be selected.
|
|
154
|
+
var_names : list of str
|
|
155
|
+
Names of variables that should be kept in the resulting dataset.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
xr.Dataset
|
|
160
|
+
A new dataset containing only the variables in ``var_names`` and
|
|
161
|
+
``"mask"`` (if it exists in the input dataset).
|
|
162
|
+
"""
|
|
163
|
+
vars_to_keep = set(var_names)
|
|
164
|
+
vars_to_drop = [
|
|
165
|
+
var for var in ds.data_vars if var not in vars_to_keep and var != "mask"
|
|
166
|
+
]
|
|
167
|
+
|
|
168
|
+
if vars_to_drop:
|
|
169
|
+
ds = ds.drop_vars(vars_to_drop)
|
|
170
|
+
|
|
171
|
+
return ds
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def select_relevant_times(
|
|
175
|
+
ds: xr.Dataset,
|
|
176
|
+
time_dim: str,
|
|
177
|
+
time_coord: str,
|
|
178
|
+
start_time: datetime,
|
|
179
|
+
end_time: datetime | None = None,
|
|
180
|
+
climatology: bool = False,
|
|
181
|
+
allow_flex_time: bool = False,
|
|
182
|
+
) -> xr.Dataset:
|
|
183
|
+
"""
|
|
184
|
+
Select a subset of the dataset based on time constraints.
|
|
185
|
+
|
|
186
|
+
This function supports two main use cases:
|
|
187
|
+
|
|
188
|
+
1. **Time range selection (start_time + end_time provided):**
|
|
189
|
+
- Returns all records strictly between `start_time` and `end_time`.
|
|
190
|
+
- Ensures at least one record at or before `start_time` and one record at or
|
|
191
|
+
after `end_time` are included, even if they fall outside the strict range.
|
|
192
|
+
|
|
193
|
+
2. **Initial condition selection (start_time provided, end_time=None):**
|
|
194
|
+
- Delegates to `_select_initial_time`, which reduces the dataset to exactly one
|
|
195
|
+
time entry.
|
|
196
|
+
- If `allow_flex_time=True`, a +24-hour buffer around `start_time` is allowed,
|
|
197
|
+
and the closest timestamp is chosen.
|
|
198
|
+
- If `allow_flex_time=False`, requires an exact timestamp match.
|
|
199
|
+
|
|
200
|
+
Additional behavior:
|
|
201
|
+
- If `climatology=True`, the dataset must contain exactly 12 time steps. If valid,
|
|
202
|
+
the climatology dataset is returned without further filtering.
|
|
203
|
+
- If the dataset uses `cftime` datetime objects, these are converted to
|
|
204
|
+
`np.datetime64` before filtering.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
ds : xr.Dataset
|
|
209
|
+
The dataset to filter. Must contain a valid time dimension.
|
|
210
|
+
time_dim : str
|
|
211
|
+
Name of the time dimension in `ds`.
|
|
212
|
+
time_coord : str
|
|
213
|
+
Name of the time coordinate in `ds`.
|
|
214
|
+
start_time : datetime
|
|
215
|
+
Start time for filtering.
|
|
216
|
+
end_time : datetime or None
|
|
217
|
+
End time for filtering. If `None`, the function assumes an initial condition
|
|
218
|
+
use case and selects exactly one timestamp.
|
|
219
|
+
climatology : bool, optional
|
|
220
|
+
If True, requires exactly 12 time steps and bypasses normal filtering.
|
|
221
|
+
Defaults to False.
|
|
222
|
+
allow_flex_time : bool, optional
|
|
223
|
+
Whether to allow a +24h search window after `start_time` when `end_time`
|
|
224
|
+
is None. If False (default), requires an exact match.
|
|
225
|
+
|
|
226
|
+
Returns
|
|
227
|
+
-------
|
|
228
|
+
xr.Dataset
|
|
229
|
+
A filtered dataset containing only the selected time entries.
|
|
230
|
+
|
|
231
|
+
Raises
|
|
232
|
+
------
|
|
233
|
+
ValueError
|
|
234
|
+
- If `climatology=True` but the dataset does not contain exactly 12 time steps.
|
|
235
|
+
- If `climatology=False` and the dataset contains integer time values.
|
|
236
|
+
- If no valid records are found within the requested range or window.
|
|
237
|
+
|
|
238
|
+
Warns
|
|
239
|
+
-----
|
|
240
|
+
UserWarning
|
|
241
|
+
- If no records exist at or before `start_time` or at or after `end_time`.
|
|
242
|
+
- If the specified time dimension does not exist in the dataset.
|
|
243
|
+
|
|
244
|
+
Notes
|
|
245
|
+
-----
|
|
246
|
+
- For initial conditions (end_time=None), see `_select_initial_time` for details
|
|
247
|
+
on strict vs. flexible selection behavior.
|
|
248
|
+
- Logs warnings instead of failing hard when boundary records are missing, and
|
|
249
|
+
defaults to using the earliest or latest available time in such cases.
|
|
250
|
+
"""
|
|
251
|
+
if time_dim not in ds.dims:
|
|
252
|
+
logging.warning(
|
|
253
|
+
f"Dataset does not contain time dimension '{time_dim}'. "
|
|
254
|
+
"Please check variable naming or dataset structure."
|
|
255
|
+
)
|
|
256
|
+
return ds
|
|
257
|
+
|
|
258
|
+
if time_coord not in ds.variables:
|
|
259
|
+
logging.warning(
|
|
260
|
+
f"Dataset does not contain time coordinate '{time_coord}'. "
|
|
261
|
+
"Please check variable naming or dataset structure."
|
|
262
|
+
)
|
|
263
|
+
return ds
|
|
264
|
+
|
|
265
|
+
time_type = get_time_type(ds[time_coord])
|
|
266
|
+
|
|
267
|
+
if climatology:
|
|
268
|
+
if len(ds[time_coord]) != 12:
|
|
269
|
+
raise ValueError(
|
|
270
|
+
f"The dataset contains {len(ds[time_coord])} time steps, but the climatology flag is set to True, which requires exactly 12 time steps."
|
|
271
|
+
)
|
|
272
|
+
else:
|
|
273
|
+
if time_type == "int":
|
|
274
|
+
raise ValueError(
|
|
275
|
+
"The dataset contains integer time values, which are only supported when the climatology flag is set to True. However, your climatology flag is set to False."
|
|
276
|
+
)
|
|
277
|
+
if time_type == "cftime":
|
|
278
|
+
ds = ds.assign_coords({time_dim: convert_cftime_to_datetime(ds[time_coord])})
|
|
279
|
+
|
|
280
|
+
if not end_time:
|
|
281
|
+
# Assume we are looking for exactly one time record for initial conditions
|
|
282
|
+
return _select_initial_time(
|
|
283
|
+
ds, time_dim, time_coord, start_time, climatology, allow_flex_time
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
if climatology:
|
|
287
|
+
return ds
|
|
288
|
+
|
|
289
|
+
# Identify records before or at start_time
|
|
290
|
+
before_start = ds[time_coord] <= np.datetime64(start_time)
|
|
291
|
+
if before_start.any():
|
|
292
|
+
closest_before_start = ds[time_coord].where(before_start, drop=True)[-1]
|
|
293
|
+
else:
|
|
294
|
+
logging.warning(f"No records found at or before the start_time: {start_time}.")
|
|
295
|
+
closest_before_start = ds[time_coord][0]
|
|
296
|
+
|
|
297
|
+
# Identify records after or at end_time
|
|
298
|
+
after_end = ds[time_coord] >= np.datetime64(end_time)
|
|
299
|
+
if after_end.any():
|
|
300
|
+
closest_after_end = ds[time_coord].where(after_end, drop=True).min()
|
|
301
|
+
else:
|
|
302
|
+
logging.warning(f"No records found at or after the end_time: {end_time}.")
|
|
303
|
+
closest_after_end = ds[time_coord].max()
|
|
304
|
+
|
|
305
|
+
# Select records within the time range and add the closest before/after
|
|
306
|
+
within_range = (ds[time_coord] > np.datetime64(start_time)) & (
|
|
307
|
+
ds[time_coord] < np.datetime64(end_time)
|
|
308
|
+
)
|
|
309
|
+
selected_times = ds[time_coord].where(
|
|
310
|
+
within_range
|
|
311
|
+
| (ds[time_coord] == closest_before_start)
|
|
312
|
+
| (ds[time_coord] == closest_after_end),
|
|
313
|
+
drop=True,
|
|
314
|
+
)
|
|
315
|
+
ds = ds.sel({time_dim: selected_times})
|
|
316
|
+
|
|
317
|
+
return ds
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _select_initial_time(
|
|
321
|
+
ds: xr.Dataset,
|
|
322
|
+
time_dim: str,
|
|
323
|
+
time_coord: str,
|
|
324
|
+
ini_time: datetime,
|
|
325
|
+
climatology: bool,
|
|
326
|
+
allow_flex_time: bool = False,
|
|
327
|
+
) -> xr.Dataset:
|
|
328
|
+
"""Select exactly one initial time from dataset.
|
|
329
|
+
|
|
330
|
+
Parameters
|
|
331
|
+
----------
|
|
332
|
+
ds : xr.Dataset
|
|
333
|
+
The input dataset with a time dimension.
|
|
334
|
+
time_dim : str
|
|
335
|
+
Name of the time dimension.
|
|
336
|
+
time_coord : str
|
|
337
|
+
Name of the time coordinate.
|
|
338
|
+
ini_time : datetime
|
|
339
|
+
The desired initial time.
|
|
340
|
+
allow_flex_time : bool
|
|
341
|
+
- If True: allow a +24h window and pick the closest available timestamp.
|
|
342
|
+
- If False (default): require an exact match, otherwise raise ValueError.
|
|
343
|
+
|
|
344
|
+
Returns
|
|
345
|
+
-------
|
|
346
|
+
xr.Dataset
|
|
347
|
+
Dataset reduced to exactly one timestamp.
|
|
348
|
+
|
|
349
|
+
Raises
|
|
350
|
+
------
|
|
351
|
+
ValueError
|
|
352
|
+
If no matching time is found (when `allow_flex_time=False`), or no entries are
|
|
353
|
+
available within the +24h window (when `allow_flex_time=True`).
|
|
354
|
+
"""
|
|
355
|
+
if climatology:
|
|
356
|
+
# Convert from timedelta64[ns] to fractional days
|
|
357
|
+
ds["time"] = ds["time"] / np.timedelta64(1, "D")
|
|
358
|
+
# Interpolate from climatology for initial conditions
|
|
359
|
+
return interpolate_from_climatology(ds, time_dim, time_coord, ini_time)
|
|
360
|
+
|
|
361
|
+
if allow_flex_time:
|
|
362
|
+
# Look in time range [ini_time, ini_time + 24h)
|
|
363
|
+
end_time = ini_time + timedelta(days=1)
|
|
364
|
+
times = (np.datetime64(ini_time) <= ds[time_coord]) & (
|
|
365
|
+
ds[time_coord] < np.datetime64(end_time)
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
if np.all(~times):
|
|
369
|
+
raise ValueError(
|
|
370
|
+
f"No time entries found between {ini_time} and {end_time}."
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
ds = ds.where(times, drop=True)
|
|
374
|
+
if ds.sizes[time_dim] > 1:
|
|
375
|
+
# Pick the time closest to start_time
|
|
376
|
+
ds = ds.isel({time_dim: 0})
|
|
377
|
+
|
|
378
|
+
logging.warning(
|
|
379
|
+
f"Selected time entry closest to the specified start_time in +24 hour range: {ds[time_coord].values}"
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
else:
|
|
383
|
+
# Strict match required
|
|
384
|
+
if not (ds[time_coord].values == np.datetime64(ini_time)).any():
|
|
385
|
+
raise ValueError(
|
|
386
|
+
f"No exact match found for initial time {ini_time}. Consider setting allow_flex_time to True."
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
ds = ds.sel({time_coord: np.datetime64(ini_time)})
|
|
390
|
+
|
|
391
|
+
if time_dim not in ds.dims:
|
|
392
|
+
ds = ds.expand_dims(time_dim)
|
|
393
|
+
|
|
394
|
+
return ds
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def get_time_type(data_array: xr.DataArray) -> str:
|
|
398
|
+
"""Determines the type of time values in the xarray DataArray.
|
|
399
|
+
|
|
400
|
+
Parameters
|
|
401
|
+
----------
|
|
402
|
+
data_array : xr.DataArray
|
|
403
|
+
The xarray DataArray to be checked for time data types.
|
|
404
|
+
|
|
405
|
+
Returns
|
|
406
|
+
-------
|
|
407
|
+
str
|
|
408
|
+
A string indicating the type of the time data: 'cftime', 'datetime', or 'int'.
|
|
409
|
+
|
|
410
|
+
Raises
|
|
411
|
+
------
|
|
412
|
+
TypeError
|
|
413
|
+
If the values in the DataArray are not of type numpy.ndarray or list.
|
|
414
|
+
"""
|
|
415
|
+
values = data_array.values
|
|
416
|
+
|
|
417
|
+
# 1. numpy datetime64 (any precision)
|
|
418
|
+
if np.issubdtype(values.dtype, np.datetime64):
|
|
419
|
+
return "datetime"
|
|
420
|
+
|
|
421
|
+
# 2. cftime objects
|
|
422
|
+
cftime_types = (
|
|
423
|
+
cftime.DatetimeNoLeap,
|
|
424
|
+
cftime.DatetimeJulian,
|
|
425
|
+
cftime.DatetimeGregorian,
|
|
426
|
+
cftime.Datetime360Day,
|
|
427
|
+
cftime.DatetimeProlepticGregorian,
|
|
428
|
+
)
|
|
429
|
+
if values.dtype == object and any(isinstance(v, cftime_types) for v in values):
|
|
430
|
+
return "cftime"
|
|
431
|
+
|
|
432
|
+
# 3. integer axis
|
|
433
|
+
if np.issubdtype(values.dtype, np.integer):
|
|
434
|
+
return "int"
|
|
435
|
+
|
|
436
|
+
raise ValueError(
|
|
437
|
+
f"Unsupported data type for time values: {values.dtype}. "
|
|
438
|
+
"Expected datetime64, cftime objects, or integer."
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def convert_cftime_to_datetime(data_array: np.ndarray) -> np.ndarray:
|
|
443
|
+
"""Converts cftime datetime objects to numpy datetime64 objects in a numpy ndarray.
|
|
444
|
+
|
|
445
|
+
Parameters
|
|
446
|
+
----------
|
|
447
|
+
data_array : np.ndarray
|
|
448
|
+
The numpy ndarray containing cftime datetime objects to be converted.
|
|
449
|
+
|
|
450
|
+
Returns
|
|
451
|
+
-------
|
|
452
|
+
np.ndarray
|
|
453
|
+
The ndarray with cftime datetimes converted to numpy datetime64 objects.
|
|
454
|
+
|
|
455
|
+
Notes
|
|
456
|
+
-----
|
|
457
|
+
This function is intended to be used with numpy ndarrays. If you need to convert
|
|
458
|
+
cftime datetime objects in an xarray.DataArray, please use the appropriate function
|
|
459
|
+
to handle xarray.DataArray conversions.
|
|
460
|
+
"""
|
|
461
|
+
# List of cftime datetime types
|
|
462
|
+
cftime_types = (
|
|
463
|
+
cftime.DatetimeNoLeap,
|
|
464
|
+
cftime.DatetimeJulian,
|
|
465
|
+
cftime.DatetimeGregorian,
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
# Define a conversion function for cftime to numpy datetime64
|
|
469
|
+
def convert_datetime(dt):
|
|
470
|
+
if isinstance(dt, cftime_types):
|
|
471
|
+
# Convert to ISO format and then to nanosecond precision
|
|
472
|
+
return np.datetime64(dt.isoformat(), "ns")
|
|
473
|
+
return np.datetime64(dt, "ns")
|
|
474
|
+
|
|
475
|
+
return np.vectorize(convert_datetime)(data_array)
|
|
@@ -5,28 +5,53 @@ from scipy import sparse
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class LateralFill:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
"""
|
|
9
|
+
Fill NaN values in a 2D field using an iterative lateral diffusion (Poisson) solver.
|
|
10
|
+
|
|
11
|
+
The fill is performed along two horizontal dimensions. The **order of these
|
|
12
|
+
dimensions is significant** and must match the order of the dimensions of the
|
|
13
|
+
input data passed to :meth:`apply`.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, mask: xr.DataArray, dims: tuple[str, str], tol: float = 1.0e-4):
|
|
17
|
+
"""
|
|
18
|
+
Initialize a lateral fill operator.
|
|
11
19
|
|
|
12
20
|
Parameters
|
|
13
21
|
----------
|
|
14
|
-
mask : xarray.DataArray
|
|
15
|
-
A 2D boolean mask
|
|
16
|
-
|
|
17
|
-
dims :
|
|
18
|
-
|
|
22
|
+
mask : xarray.DataArray
|
|
23
|
+
A 2D boolean mask defining valid points (True) and masked points (False).
|
|
24
|
+
The mask dimensions **must be ordered consistently with `dims`**.
|
|
25
|
+
dims : tuple of str
|
|
26
|
+
The two horizontal dimensions along which the fill is applied.
|
|
27
|
+
**Order matters** and must match the dimension order of both `mask`
|
|
28
|
+
and the data passed to :meth:`apply` (e.g., ``("eta_rho", "xi_rho")``).
|
|
19
29
|
tol : float, optional
|
|
20
|
-
|
|
30
|
+
Convergence tolerance for the iterative solver. Default is ``1.0e-4``.
|
|
21
31
|
|
|
22
32
|
Raises
|
|
23
33
|
------
|
|
34
|
+
ValueError
|
|
35
|
+
If the mask dimensionality or dimension order is inconsistent with `dims`.
|
|
24
36
|
NotImplementedError
|
|
25
|
-
If the
|
|
37
|
+
If the mask is not two-dimensional.
|
|
26
38
|
"""
|
|
27
|
-
|
|
39
|
+
# Type check
|
|
40
|
+
if not isinstance(dims, tuple):
|
|
41
|
+
raise TypeError(
|
|
42
|
+
f"LateralFill error: 'dims' must be a tuple of two strings, got {type(dims).__name__}."
|
|
43
|
+
)
|
|
44
|
+
if len(dims) != 2:
|
|
45
|
+
raise ValueError(
|
|
46
|
+
f"LateralFill error: 'dims' must contain exactly two dimension names, got {len(dims)}: {dims}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if len(mask.shape) != 2:
|
|
28
50
|
raise NotImplementedError("LateralFill currently supports only 2D masks.")
|
|
29
51
|
|
|
52
|
+
_check_dimension_match(dims, mask)
|
|
53
|
+
|
|
54
|
+
self.dims = dims
|
|
30
55
|
self.mask = mask
|
|
31
56
|
|
|
32
57
|
# Ensure the mask is 2D, copy it and set boundary values to True
|
|
@@ -35,7 +60,6 @@ class LateralFill:
|
|
|
35
60
|
mask[-1, :] = True
|
|
36
61
|
mask[:, 0] = True
|
|
37
62
|
mask[:, -1] = True
|
|
38
|
-
|
|
39
63
|
# Flatten the mask for use in the sparse matrix solver
|
|
40
64
|
mask_flat = mask.values.flatten()
|
|
41
65
|
|
|
@@ -45,7 +69,6 @@ class LateralFill:
|
|
|
45
69
|
# Use algebraic multigrid solver for solving the Poisson equation with set seed to ensure reproducibility
|
|
46
70
|
np.random.seed(123089)
|
|
47
71
|
self.ml = pyamg.smoothed_aggregation_solver(A, max_coarse=10)
|
|
48
|
-
self.dims = dims
|
|
49
72
|
self.tol = tol
|
|
50
73
|
|
|
51
74
|
def apply(self, var):
|
|
@@ -63,6 +86,8 @@ class LateralFill:
|
|
|
63
86
|
A DataArray with NaN values filled by iterative smoothing, while preserving
|
|
64
87
|
non-NaN values.
|
|
65
88
|
"""
|
|
89
|
+
_check_dimension_match(self.dims, var)
|
|
90
|
+
|
|
66
91
|
# Apply fill to anomaly field
|
|
67
92
|
mean = var.where(self.mask).mean(dim=self.dims, skipna=True)
|
|
68
93
|
var = var - mean
|
|
@@ -73,6 +98,11 @@ class LateralFill:
|
|
|
73
98
|
# Initial guess: ocean points take their original values, land points are set to 0
|
|
74
99
|
x0 = xr.where(self.mask, var, 0)
|
|
75
100
|
|
|
101
|
+
if x0.isnull().any():
|
|
102
|
+
raise ValueError(
|
|
103
|
+
"LateralFill error: The fill operation cannot proceed because the input field contains NaNs at grid points marked as valid by the mask."
|
|
104
|
+
)
|
|
105
|
+
|
|
76
106
|
# Apply the iterative solver using a custom NumPy function
|
|
77
107
|
var_filled = xr.apply_ufunc(
|
|
78
108
|
_lateral_fill_np_array,
|
|
@@ -298,3 +328,70 @@ def stencil_grid_mod(S, grid, msk, dtype=None, format=None):
|
|
|
298
328
|
data[4, i + diags[4]] = 0
|
|
299
329
|
|
|
300
330
|
return sparse.dia_matrix((data, diags), shape=(N_v, N_v)).asformat(format)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _check_dimension_match(dims: tuple[str, str], da: xr.DataArray):
|
|
334
|
+
"""
|
|
335
|
+
Validate that a DataArray contains the required horizontal dimensions
|
|
336
|
+
in the correct order.
|
|
337
|
+
|
|
338
|
+
Parameters
|
|
339
|
+
----------
|
|
340
|
+
dims : tuple[str, str]
|
|
341
|
+
Names of the two horizontal dimensions, in the required order
|
|
342
|
+
(e.g., ``("eta_rho", "xi_rho")``).
|
|
343
|
+
da : xarray.DataArray
|
|
344
|
+
The DataArray to validate.
|
|
345
|
+
|
|
346
|
+
Raises
|
|
347
|
+
------
|
|
348
|
+
ValueError
|
|
349
|
+
If the required dimensions are missing, extra, or their order in ``da``
|
|
350
|
+
does not exactly match ``dims``. The error message includes guidance
|
|
351
|
+
on how to reorder the DataArray using ``transpose``.
|
|
352
|
+
"""
|
|
353
|
+
# Extract the horizontal dims from the DataArray
|
|
354
|
+
var_horiz_dims = tuple(d for d in da.dims if d in dims)
|
|
355
|
+
|
|
356
|
+
if set(var_horiz_dims) != set(dims):
|
|
357
|
+
raise ValueError(
|
|
358
|
+
"LateralFill error: DataArray does not contain the required horizontal dimensions.\n"
|
|
359
|
+
f" expected dims = {dims}\n"
|
|
360
|
+
f" found dims = {tuple(da.dims)}\n"
|
|
361
|
+
"Ensure the DataArray includes all required dimensions."
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if var_horiz_dims != dims:
|
|
365
|
+
raise ValueError(
|
|
366
|
+
"LateralFill error: DataArray horizontal dimension order is incorrect.\n"
|
|
367
|
+
f" expected order = {dims}\n"
|
|
368
|
+
f" found order = {var_horiz_dims}\n"
|
|
369
|
+
"Reorder the DataArray before applying LateralFill, e.g.:\n"
|
|
370
|
+
f" var = var.transpose(..., {', '.join(dims)})"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def one_dim_fill(da: xr.DataArray, dim: str, direction="forward") -> xr.DataArray:
|
|
375
|
+
"""Fill NaN values in a DataArray along a specified dimension.
|
|
376
|
+
|
|
377
|
+
Parameters
|
|
378
|
+
----------
|
|
379
|
+
da : xr.DataArray
|
|
380
|
+
The input DataArray with NaN values to be filled, which must include the specified dimension.
|
|
381
|
+
dim : str
|
|
382
|
+
The name of the dimension along which to fill NaN values (e.g., 'depth' or 'time').
|
|
383
|
+
direction : str, optional
|
|
384
|
+
The filling direction; either "forward" to propagate non-NaN values downward or "backward" to propagate them upward.
|
|
385
|
+
Defaults to "forward".
|
|
386
|
+
|
|
387
|
+
Returns
|
|
388
|
+
-------
|
|
389
|
+
xr.DataArray
|
|
390
|
+
A new DataArray with NaN values filled in the specified direction, leaving the original data unchanged.
|
|
391
|
+
"""
|
|
392
|
+
if dim in da.dims:
|
|
393
|
+
if direction == "forward":
|
|
394
|
+
return da.ffill(dim=dim)
|
|
395
|
+
elif direction == "backward":
|
|
396
|
+
return da.bfill(dim=dim)
|
|
397
|
+
return da
|