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,767 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import xarray as xr
|
|
10
|
+
|
|
11
|
+
from roms_tools import Grid
|
|
12
|
+
from roms_tools.datasets.utils import (
|
|
13
|
+
check_dataset,
|
|
14
|
+
convert_to_float64,
|
|
15
|
+
extrapolate_deepest_to_bottom,
|
|
16
|
+
select_relevant_fields,
|
|
17
|
+
select_relevant_times,
|
|
18
|
+
validate_start_end_time,
|
|
19
|
+
)
|
|
20
|
+
from roms_tools.fill import LateralFill
|
|
21
|
+
from roms_tools.utils import load_data, wrap_longitudes
|
|
22
|
+
from roms_tools.vertical_coordinate import (
|
|
23
|
+
compute_depth_coordinates,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
DEFAULT_NR_BUFFER_POINTS = (
|
|
27
|
+
20 # Default number of buffer points for subdomain selection.
|
|
28
|
+
)
|
|
29
|
+
# Balances performance and accuracy:
|
|
30
|
+
# - Too many points → more expensive computations
|
|
31
|
+
# - Too few points → potential boundary artifacts when lateral refill is performed
|
|
32
|
+
# See discussion: https://github.com/CWorthy-ocean/roms-tools/issues/153
|
|
33
|
+
# This default will be applied consistently across all datasets requiring lateral fill.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(kw_only=True)
|
|
37
|
+
class ROMSDataset:
|
|
38
|
+
"""Represents ROMS model output.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
path: str | Path | list[str | Path]
|
|
43
|
+
Filename, or list of filenames with model output.
|
|
44
|
+
grid : Grid
|
|
45
|
+
Object representing the grid information.
|
|
46
|
+
start_time : Optional[datetime], optional
|
|
47
|
+
Start time for selecting relevant data. If not provided, no time-based filtering is applied.
|
|
48
|
+
end_time : Optional[datetime], optional
|
|
49
|
+
End time for selecting relevant data. If not provided, the dataset selects the time entry
|
|
50
|
+
closest to `start_time` within the range `[start_time, start_time + 24 hours)`.
|
|
51
|
+
If `start_time` is also not provided, no time-based filtering is applied.
|
|
52
|
+
allow_flex_time: bool, optional
|
|
53
|
+
Controls how strictly the dataset selects a time entry when `end_time` is not provided (relevant for initial conditions):
|
|
54
|
+
|
|
55
|
+
- If False (default): requires an exact match to `start_time`. Raises a ValueError if no match exists.
|
|
56
|
+
- If True: allows a +24h search window after `start_time` and selects the closest available
|
|
57
|
+
time entry within that window. Raises a ValueError if none are found.
|
|
58
|
+
|
|
59
|
+
Only used when `end_time` is None. Has no effect otherwise.
|
|
60
|
+
dim_names: dict[str, str], optional
|
|
61
|
+
Dictionary specifying the names of dimensions in the dataset.
|
|
62
|
+
var_names: dict[str, str], optional
|
|
63
|
+
Dictionary of variable names that are required in the dataset.
|
|
64
|
+
opt_var_names: dict[str, str], optional
|
|
65
|
+
Dictionary of variable names that are optional in the dataset.
|
|
66
|
+
Defaults to an empty dictionary.
|
|
67
|
+
model_reference_date : datetime, optional
|
|
68
|
+
Reference date of ROMS simulation.
|
|
69
|
+
If not specified, this is inferred from metadata of the model output
|
|
70
|
+
If specified and does not coincide with metadata, a warning is raised.
|
|
71
|
+
adjust_depth_for_sea_surface_height : bool, optional
|
|
72
|
+
Whether to account for sea surface height variations when computing depth coordinates.
|
|
73
|
+
Defaults to `False`.
|
|
74
|
+
use_dask: bool, optional
|
|
75
|
+
Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
path: str | Path | list[str | Path]
|
|
79
|
+
"""Filename, or list of filenames with model output."""
|
|
80
|
+
grid: Grid
|
|
81
|
+
"""Object representing the grid information."""
|
|
82
|
+
start_time: datetime | None = None
|
|
83
|
+
"""Start time for selecting relevant data."""
|
|
84
|
+
end_time: datetime | None = None
|
|
85
|
+
"""End time for selecting relevant data."""
|
|
86
|
+
allow_flex_time: bool = False
|
|
87
|
+
"""Controls how strictly the dataset selects a time entry when `end_time` is not provided."""
|
|
88
|
+
dim_names: dict[str, str] = field(
|
|
89
|
+
default_factory=lambda: {
|
|
90
|
+
"eta_rho": "eta_rho",
|
|
91
|
+
"xi_rho": "xi_rho",
|
|
92
|
+
"time": "time",
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
"""Dictionary specifying the names of dimensions in the dataset."""
|
|
96
|
+
var_names: dict[str, str] | None = None
|
|
97
|
+
"""Dictionary of variable names that are required in the dataset."""
|
|
98
|
+
opt_var_names: dict[str, str] = field(default_factory=dict)
|
|
99
|
+
"""Dictionary of variable names that are optional in the dataset."""
|
|
100
|
+
use_dask: bool = False
|
|
101
|
+
"""Whether to use dask for processing."""
|
|
102
|
+
model_reference_date: datetime | None = None
|
|
103
|
+
"""Reference date of ROMS simulation."""
|
|
104
|
+
adjust_depth_for_sea_surface_height: bool | None = False
|
|
105
|
+
"""Whether to account for sea surface height variations when computing depth
|
|
106
|
+
coordinates."""
|
|
107
|
+
|
|
108
|
+
ds: xr.Dataset = field(init=False, repr=False)
|
|
109
|
+
"""An xarray Dataset containing the ROMS output."""
|
|
110
|
+
|
|
111
|
+
def __post_init__(self):
|
|
112
|
+
validate_start_end_time(self.start_time, self.end_time)
|
|
113
|
+
ds = self.load_data()
|
|
114
|
+
self._check_consistency_data_grid(ds)
|
|
115
|
+
|
|
116
|
+
self._set_default_var_names(ds)
|
|
117
|
+
|
|
118
|
+
check_dataset(ds, self.dim_names, self.var_names)
|
|
119
|
+
self._check_vertical_coordinate(ds)
|
|
120
|
+
self._infer_model_reference_date_from_metadata(ds)
|
|
121
|
+
ds = self._add_absolute_time(ds)
|
|
122
|
+
|
|
123
|
+
ds = self.select_relevant_fields(ds)
|
|
124
|
+
if self.start_time is not None:
|
|
125
|
+
ds = self.select_relevant_times(ds)
|
|
126
|
+
|
|
127
|
+
ds = self._add_lat_lon_coords_and_masks(ds)
|
|
128
|
+
self.ds = ds
|
|
129
|
+
|
|
130
|
+
# Dataset for depth coordinates
|
|
131
|
+
self.ds_depth_coords = xr.Dataset()
|
|
132
|
+
|
|
133
|
+
def _check_consistency_data_grid(self, ds: xr.Dataset) -> None:
|
|
134
|
+
"""
|
|
135
|
+
Ensure that the input dataset `ds` is consistent with the grid dataset.
|
|
136
|
+
|
|
137
|
+
Specifically, checks that the dimensions of the dataset match the grid's
|
|
138
|
+
`eta_rho` and `xi_rho` dimensions.
|
|
139
|
+
|
|
140
|
+
Parameters
|
|
141
|
+
----------
|
|
142
|
+
ds : xr.Dataset
|
|
143
|
+
The dataset to check against the grid.
|
|
144
|
+
|
|
145
|
+
Raises
|
|
146
|
+
------
|
|
147
|
+
ValueError
|
|
148
|
+
If the `eta_rho` or `xi_rho` dimensions of `ds` do not match those of `self.grid.ds`.
|
|
149
|
+
"""
|
|
150
|
+
eta = self.dim_names["eta_rho"]
|
|
151
|
+
xi = self.dim_names["xi_rho"]
|
|
152
|
+
grid_eta = self.grid.ds.sizes.get(eta)
|
|
153
|
+
grid_xi = self.grid.ds.sizes.get(xi)
|
|
154
|
+
ds_eta = ds.sizes.get(eta)
|
|
155
|
+
ds_xi = ds.sizes.get(xi)
|
|
156
|
+
|
|
157
|
+
if grid_eta != ds_eta or grid_xi != ds_xi:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"Inconsistent dataset dimensions: "
|
|
160
|
+
f"grid ({eta}={grid_eta}, {xi}={grid_xi}), "
|
|
161
|
+
f"dataset ({eta}={ds_eta}, {xi}={ds_xi})."
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def _set_default_var_names(self, ds: xr.Dataset) -> None:
|
|
165
|
+
"""
|
|
166
|
+
Ensure ``self.var_names`` is a valid mapping.
|
|
167
|
+
|
|
168
|
+
If ``self.var_names`` is ``None``, it is initialized as an identity
|
|
169
|
+
mapping for all variables in ``ds``.
|
|
170
|
+
|
|
171
|
+
Parameters
|
|
172
|
+
----------
|
|
173
|
+
ds : xr.Dataset
|
|
174
|
+
Dataset whose variable names are used when creating defaults.
|
|
175
|
+
"""
|
|
176
|
+
if self.var_names is None:
|
|
177
|
+
self.var_names = {name: name for name in ds.data_vars}
|
|
178
|
+
|
|
179
|
+
def _get_depth_coordinates(self, depth_type="layer", locations=["rho"]):
|
|
180
|
+
"""Ensure depth coordinates are stored for a given location and depth type.
|
|
181
|
+
|
|
182
|
+
Calculates vertical depth coordinates (layer or interface) for specified locations (e.g., rho, u, v points)
|
|
183
|
+
and updates them in the dataset (`self.ds`).
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
depth_type : str
|
|
188
|
+
The type of depth coordinate to compute. Valid options:
|
|
189
|
+
- "layer": Compute layer depth coordinates.
|
|
190
|
+
- "interface": Compute interface depth coordinates.
|
|
191
|
+
locations : list[str], optional
|
|
192
|
+
Locations for which to compute depth coordinates. Default is ["rho", "u", "v"].
|
|
193
|
+
Valid options include:
|
|
194
|
+
- "rho": Depth coordinates at rho points.
|
|
195
|
+
- "u": Depth coordinates at u points.
|
|
196
|
+
- "v": Depth coordinates at v points.
|
|
197
|
+
|
|
198
|
+
Updates
|
|
199
|
+
-------
|
|
200
|
+
self.ds_depth_coords : xarray.Dataset
|
|
201
|
+
|
|
202
|
+
Raises
|
|
203
|
+
------
|
|
204
|
+
ValueError
|
|
205
|
+
If `adjust_depth_for_sea_surface_height` is enabled but `zeta` is missing from `self.ds`.
|
|
206
|
+
|
|
207
|
+
Notes
|
|
208
|
+
-----
|
|
209
|
+
- This method relies on the `compute_depth_coordinates` function to perform calculations.
|
|
210
|
+
- If `adjust_depth_for_sea_surface_height` is `True`, the method accounts for variations
|
|
211
|
+
in sea surface height (`zeta`).
|
|
212
|
+
"""
|
|
213
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
214
|
+
if "zeta" not in self.ds:
|
|
215
|
+
raise ValueError(
|
|
216
|
+
"`zeta` is required in provided ROMS output when `adjust_depth_for_sea_surface_height` is enabled."
|
|
217
|
+
)
|
|
218
|
+
zeta = self.ds.zeta
|
|
219
|
+
else:
|
|
220
|
+
zeta = 0
|
|
221
|
+
|
|
222
|
+
for location in locations:
|
|
223
|
+
var_name = f"{depth_type}_depth_{location}"
|
|
224
|
+
if var_name not in self.ds_depth_coords:
|
|
225
|
+
depth_da = compute_depth_coordinates(
|
|
226
|
+
self.grid.ds, zeta, depth_type, location
|
|
227
|
+
)
|
|
228
|
+
depth_da = depth_da.assign_coords(
|
|
229
|
+
{
|
|
230
|
+
f"lat_{location}": self.grid.ds[f"lat_{location}"],
|
|
231
|
+
f"lon_{location}": self.grid.ds[f"lon_{location}"],
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
self.ds_depth_coords[var_name] = depth_da
|
|
235
|
+
|
|
236
|
+
def load_data(self) -> xr.Dataset:
|
|
237
|
+
"""Load the ROMS data."""
|
|
238
|
+
ds = load_data(
|
|
239
|
+
filename=self.path,
|
|
240
|
+
dim_names={"time": "time"},
|
|
241
|
+
use_dask=self.use_dask,
|
|
242
|
+
decode_times=False,
|
|
243
|
+
decode_timedelta=False,
|
|
244
|
+
time_chunking=True,
|
|
245
|
+
force_combine_nested=True,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
return ds
|
|
249
|
+
|
|
250
|
+
def select_relevant_fields(self, ds: xr.Dataset) -> xr.Dataset:
|
|
251
|
+
"""
|
|
252
|
+
Return a subset of the dataset containing only the required and optional
|
|
253
|
+
variables defined for this object.
|
|
254
|
+
|
|
255
|
+
Variables retained are those listed in ``self.var_names`` and
|
|
256
|
+
``self.opt_var_names``. Any other data variables are removed, except for
|
|
257
|
+
the special variable ``"mask"``, which is always preserved if present.
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
ds : xr.Dataset
|
|
262
|
+
The input dataset from which relevant variables will be selected.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
xr.Dataset
|
|
267
|
+
A new dataset containing only the required variables specified in
|
|
268
|
+
``self.var_names`` and the optional variables specified in
|
|
269
|
+
``self.opt_var_names``, along with ``"mask"`` if present.
|
|
270
|
+
"""
|
|
271
|
+
return select_relevant_fields(
|
|
272
|
+
ds,
|
|
273
|
+
[*self.var_names.values(), *self.opt_var_names.values()], # type: ignore
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def select_relevant_times(self, ds: xr.Dataset) -> xr.Dataset:
|
|
277
|
+
"""Select a subset of the dataset based on the specified time range.
|
|
278
|
+
|
|
279
|
+
This method filters the dataset to include all records between `start_time` and `end_time`.
|
|
280
|
+
Additionally, it ensures that one record at or before `start_time` and one record at or
|
|
281
|
+
after `end_time` are included, even if they fall outside the strict time range.
|
|
282
|
+
|
|
283
|
+
If no `end_time` is specified, the method will select the time range of
|
|
284
|
+
[start_time, start_time + 24 hours) and return the closest time entry to `start_time` within that range.
|
|
285
|
+
|
|
286
|
+
Parameters
|
|
287
|
+
----------
|
|
288
|
+
ds : xr.Dataset
|
|
289
|
+
The input dataset to be filtered. Must contain a time dimension.
|
|
290
|
+
|
|
291
|
+
Returns
|
|
292
|
+
-------
|
|
293
|
+
xr.Dataset
|
|
294
|
+
A dataset filtered to the specified time range, including the closest entries
|
|
295
|
+
at or before `start_time` and at or after `end_time` if applicable.
|
|
296
|
+
|
|
297
|
+
Raises
|
|
298
|
+
------
|
|
299
|
+
ValueError
|
|
300
|
+
If no matching times are found between `start_time` and `start_time + 24 hours`.
|
|
301
|
+
|
|
302
|
+
Warns
|
|
303
|
+
-----
|
|
304
|
+
UserWarning
|
|
305
|
+
If the dataset contains exactly 12 time steps but the climatology flag is not set.
|
|
306
|
+
This may indicate that the dataset represents climatology data.
|
|
307
|
+
|
|
308
|
+
UserWarning
|
|
309
|
+
If no records at or before `start_time` or no records at or after `end_time` are found.
|
|
310
|
+
|
|
311
|
+
UserWarning
|
|
312
|
+
If the dataset does not contain any time dimension or the time dimension is incorrectly named.
|
|
313
|
+
|
|
314
|
+
Notes
|
|
315
|
+
-----
|
|
316
|
+
- If the `climatology` flag is set and `end_time` is not provided, the method will
|
|
317
|
+
interpolate initial conditions from climatology data.
|
|
318
|
+
- If the dataset uses `cftime` datetime objects, these will be converted to standard
|
|
319
|
+
`np.datetime64` objects before filtering.
|
|
320
|
+
"""
|
|
321
|
+
time_dim = self.dim_names["time"]
|
|
322
|
+
|
|
323
|
+
# Ensure start_time is not None for type safety
|
|
324
|
+
if self.start_time is None:
|
|
325
|
+
raise ValueError("select_relevant_times called but start_time is None.")
|
|
326
|
+
|
|
327
|
+
ds = select_relevant_times(
|
|
328
|
+
ds=ds,
|
|
329
|
+
time_dim=time_dim,
|
|
330
|
+
time_coord="time",
|
|
331
|
+
start_time=self.start_time,
|
|
332
|
+
end_time=self.end_time,
|
|
333
|
+
allow_flex_time=self.allow_flex_time,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
return ds
|
|
337
|
+
|
|
338
|
+
def _infer_model_reference_date_from_metadata(self, ds: xr.Dataset) -> None:
|
|
339
|
+
"""Infer and validate the model reference date from `ocean_time` metadata.
|
|
340
|
+
|
|
341
|
+
Parameters
|
|
342
|
+
----------
|
|
343
|
+
ds : xr.Dataset
|
|
344
|
+
Dataset with an `ocean_time` variable and a `long_name` attribute
|
|
345
|
+
in the format `Time since YYYY/MM/DD`.
|
|
346
|
+
|
|
347
|
+
Raises
|
|
348
|
+
------
|
|
349
|
+
ValueError
|
|
350
|
+
If `self.model_reference_date` is not set and the reference date cannot
|
|
351
|
+
be inferred, or if the inferred date does not match `self.model_reference_date`.
|
|
352
|
+
|
|
353
|
+
Warns
|
|
354
|
+
-----
|
|
355
|
+
UserWarning
|
|
356
|
+
If `self.model_reference_date` is set but the reference date cannot be inferred.
|
|
357
|
+
"""
|
|
358
|
+
# Check if 'long_name' exists in the attributes of 'ocean_time'
|
|
359
|
+
if "long_name" in ds.ocean_time.attrs:
|
|
360
|
+
input_string = ds.ocean_time.attrs["long_name"]
|
|
361
|
+
match = re.search(r"(\d{4})/(\d{2})/(\d{2})", input_string)
|
|
362
|
+
|
|
363
|
+
if match:
|
|
364
|
+
# If a match is found, extract year, month, day and create the inferred date
|
|
365
|
+
year, month, day = map(int, match.groups())
|
|
366
|
+
inferred_date = datetime(year, month, day)
|
|
367
|
+
|
|
368
|
+
if hasattr(self, "model_reference_date") and self.model_reference_date:
|
|
369
|
+
# Check if the inferred date matches the provided model reference date
|
|
370
|
+
if self.model_reference_date != inferred_date:
|
|
371
|
+
raise ValueError(
|
|
372
|
+
f"Mismatch between `self.model_reference_date` ({self.model_reference_date}) "
|
|
373
|
+
f"and inferred reference date ({inferred_date})."
|
|
374
|
+
)
|
|
375
|
+
else:
|
|
376
|
+
# Set the model reference date if not already set
|
|
377
|
+
self.model_reference_date = inferred_date
|
|
378
|
+
else:
|
|
379
|
+
# Handle case where no match is found
|
|
380
|
+
if hasattr(self, "model_reference_date") and self.model_reference_date:
|
|
381
|
+
logging.warning(
|
|
382
|
+
"Could not infer the model reference date from the metadata. "
|
|
383
|
+
"`self.model_reference_date` will be used.",
|
|
384
|
+
)
|
|
385
|
+
else:
|
|
386
|
+
raise ValueError(
|
|
387
|
+
"Model reference date could not be inferred from the metadata, "
|
|
388
|
+
"and `self.model_reference_date` is not set."
|
|
389
|
+
)
|
|
390
|
+
else:
|
|
391
|
+
# Handle case where 'long_name' attribute doesn't exist
|
|
392
|
+
if hasattr(self, "model_reference_date") and self.model_reference_date:
|
|
393
|
+
logging.warning(
|
|
394
|
+
"`long_name` attribute not found in ocean_time. "
|
|
395
|
+
"`self.model_reference_date` will be used instead.",
|
|
396
|
+
)
|
|
397
|
+
else:
|
|
398
|
+
raise ValueError(
|
|
399
|
+
"Model reference date could not be inferred from the metadata, "
|
|
400
|
+
"and `self.model_reference_date` is not set."
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
def _check_vertical_coordinate(self, ds: xr.Dataset) -> None:
|
|
404
|
+
"""Check that the vertical coordinate parameters in the dataset are consistent
|
|
405
|
+
with the model grid.
|
|
406
|
+
|
|
407
|
+
This method compares the vertical coordinate parameters (`theta_s`, `theta_b`, `hc`, `Cs_r`, `Cs_w`) in
|
|
408
|
+
the provided dataset (`ds`) with those in the model grid (`self.grid`). The first three parameters are
|
|
409
|
+
checked for exact equality, while the last two are checked for numerical closeness.
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
ds : xarray.Dataset
|
|
414
|
+
The dataset containing vertical coordinate parameters in its attributes, such as `theta_s`, `theta_b`,
|
|
415
|
+
`hc`, `Cs_r`, and `Cs_w`.
|
|
416
|
+
|
|
417
|
+
Raises
|
|
418
|
+
------
|
|
419
|
+
ValueError
|
|
420
|
+
If the vertical coordinate parameters do not match the expected values (based on exact or approximate equality).
|
|
421
|
+
|
|
422
|
+
Notes
|
|
423
|
+
-----
|
|
424
|
+
- Missing attributes trigger a warning instead of an exception.
|
|
425
|
+
- `theta_s`, `theta_b`, and `hc` are checked for exact equality using `np.array_equal`.
|
|
426
|
+
- `Cs_r` and `Cs_w` are checked for numerical closeness using `np.allclose`.
|
|
427
|
+
"""
|
|
428
|
+
required_exact = ["theta_s", "theta_b", "hc"]
|
|
429
|
+
required_close = ["Cs_r", "Cs_w"]
|
|
430
|
+
|
|
431
|
+
# Check exact equality
|
|
432
|
+
for param in required_exact:
|
|
433
|
+
value = ds.attrs.get(param, None)
|
|
434
|
+
if value is None:
|
|
435
|
+
logging.warning(
|
|
436
|
+
f"Dataset is missing attribute '{param}'. Skipping this check."
|
|
437
|
+
)
|
|
438
|
+
continue
|
|
439
|
+
if not np.array_equal(getattr(self.grid, param), value):
|
|
440
|
+
raise ValueError(
|
|
441
|
+
f"{param} from grid ({getattr(self.grid, param)}) does not match dataset ({value})."
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
# Check numerical closeness
|
|
445
|
+
for param in required_close:
|
|
446
|
+
value = ds.attrs.get(param, None)
|
|
447
|
+
if value is None:
|
|
448
|
+
logging.warning(
|
|
449
|
+
f"Dataset is missing attribute '{param}'. Skipping this check."
|
|
450
|
+
)
|
|
451
|
+
continue
|
|
452
|
+
grid_value = getattr(self.grid.ds, param)
|
|
453
|
+
if not np.allclose(grid_value, value):
|
|
454
|
+
raise ValueError(
|
|
455
|
+
f"{param} from grid ({grid_value}) is not close to dataset ({value})."
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
def _add_absolute_time(self, ds: xr.Dataset) -> xr.Dataset:
|
|
459
|
+
"""Add absolute time as a coordinate to the dataset.
|
|
460
|
+
|
|
461
|
+
Parameters
|
|
462
|
+
----------
|
|
463
|
+
ds : xarray.Dataset
|
|
464
|
+
Dataset containing "ocean_time" in seconds since the model reference date.
|
|
465
|
+
|
|
466
|
+
Returns
|
|
467
|
+
-------
|
|
468
|
+
xarray.Dataset
|
|
469
|
+
Dataset with absolute time added.
|
|
470
|
+
"""
|
|
471
|
+
if self.model_reference_date is None:
|
|
472
|
+
raise ValueError(
|
|
473
|
+
"`model_reference_date` must be set before computing absolute time."
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
ocean_time_seconds = ds["ocean_time"].values
|
|
477
|
+
|
|
478
|
+
abs_time = np.array(
|
|
479
|
+
[
|
|
480
|
+
self.model_reference_date + timedelta(seconds=seconds)
|
|
481
|
+
for seconds in ocean_time_seconds
|
|
482
|
+
]
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
abs_time = xr.DataArray(
|
|
486
|
+
abs_time, dims=["time"], coords={"time": ds["ocean_time"]}
|
|
487
|
+
)
|
|
488
|
+
abs_time.attrs["long_name"] = "absolute time"
|
|
489
|
+
ds = ds.assign_coords({"abs_time": abs_time})
|
|
490
|
+
ds = ds.drop_vars("time")
|
|
491
|
+
ds = ds.set_index(time="abs_time")
|
|
492
|
+
|
|
493
|
+
return ds
|
|
494
|
+
|
|
495
|
+
def _add_lat_lon_coords_and_masks(self, ds: xr.Dataset) -> xr.Dataset:
|
|
496
|
+
"""
|
|
497
|
+
Attach horizontal coordinate fields (lat/lon) and grid masks to a dataset.
|
|
498
|
+
|
|
499
|
+
This method augments the input dataset with the appropriate geographic
|
|
500
|
+
coordinates taken from the grid object. It *always* adds `lat_rho` and
|
|
501
|
+
`lon_rho`. If the dataset contains staggered horizontal dimensions
|
|
502
|
+
(`xi_u` or `eta_v`), the corresponding `u`- or `v`-point coordinates are
|
|
503
|
+
added as well (`lat_u`, `lon_u`, `lat_v`, `lon_v`).
|
|
504
|
+
|
|
505
|
+
In addition, the grid masks (`mask_rho`, `mask_u`, `mask_v`) are copied
|
|
506
|
+
into the dataset for later use in operations such as lateral filling.
|
|
507
|
+
|
|
508
|
+
Parameters
|
|
509
|
+
----------
|
|
510
|
+
ds : xarray.Dataset
|
|
511
|
+
Dataset to be augmented with horizontal coordinates and masks.
|
|
512
|
+
|
|
513
|
+
Returns
|
|
514
|
+
-------
|
|
515
|
+
xarray.Dataset
|
|
516
|
+
A new dataset with the appropriate latitude/longitude coordinates
|
|
517
|
+
and grid masks assigned based on the dataset's horizontal staggering.
|
|
518
|
+
|
|
519
|
+
Notes
|
|
520
|
+
-----
|
|
521
|
+
This routine does not modify the input dataset in place; a new dataset
|
|
522
|
+
with added coordinates and mask variables is returned.
|
|
523
|
+
"""
|
|
524
|
+
coords_to_add = {
|
|
525
|
+
"lat_rho": self.grid.ds["lat_rho"],
|
|
526
|
+
"lon_rho": self.grid.ds["lon_rho"],
|
|
527
|
+
}
|
|
528
|
+
vars_to_add = {"mask_rho": self.grid.ds["mask_rho"]}
|
|
529
|
+
|
|
530
|
+
if "xi_u" in ds.dims:
|
|
531
|
+
coords_to_add.update(
|
|
532
|
+
{"lat_u": self.grid.ds["lat_u"], "lon_u": self.grid.ds["lon_u"]}
|
|
533
|
+
)
|
|
534
|
+
vars_to_add.update({"mask_u": self.grid.ds["mask_u"]})
|
|
535
|
+
if "eta_v" in ds.dims:
|
|
536
|
+
coords_to_add.update(
|
|
537
|
+
{"lat_v": self.grid.ds["lat_v"], "lon_v": self.grid.ds["lon_v"]}
|
|
538
|
+
)
|
|
539
|
+
vars_to_add.update({"mask_v": self.grid.ds["mask_v"]})
|
|
540
|
+
|
|
541
|
+
ds = ds.assign_coords(coords_to_add)
|
|
542
|
+
for mask_name, mask_data in vars_to_add.items():
|
|
543
|
+
ds[mask_name] = mask_data
|
|
544
|
+
|
|
545
|
+
return ds
|
|
546
|
+
|
|
547
|
+
def choose_subdomain(
|
|
548
|
+
self,
|
|
549
|
+
target_coords: dict[str, Any],
|
|
550
|
+
buffer_points: int = DEFAULT_NR_BUFFER_POINTS,
|
|
551
|
+
) -> None:
|
|
552
|
+
"""Selects a subdomain from the xarray Dataset based on specified target
|
|
553
|
+
coordinates, extending the selection by a defined buffer. Adjusts longitude
|
|
554
|
+
ranges as necessary to accommodate the dataset's expected range and handles
|
|
555
|
+
potential discontinuities.
|
|
556
|
+
|
|
557
|
+
Parameters
|
|
558
|
+
----------
|
|
559
|
+
target_coords : dict
|
|
560
|
+
A dictionary containing the target latitude and longitude coordinates, typically
|
|
561
|
+
with keys "lat", "lon", and "straddle".
|
|
562
|
+
buffer_points : int
|
|
563
|
+
The number of grid points to extend beyond the specified latitude and longitude
|
|
564
|
+
ranges when selecting the subdomain. Defaults to 20.
|
|
565
|
+
|
|
566
|
+
Returns
|
|
567
|
+
-------
|
|
568
|
+
None
|
|
569
|
+
The subdomain of the xarray Dataset is assigned to `self.ds`.
|
|
570
|
+
|
|
571
|
+
Raises
|
|
572
|
+
------
|
|
573
|
+
ValueError
|
|
574
|
+
If the selected latitude or longitude range does not intersect with the dataset.
|
|
575
|
+
"""
|
|
576
|
+
subdomain = choose_subdomain(
|
|
577
|
+
self.ds, self.grid.ds, target_coords, buffer_points
|
|
578
|
+
)
|
|
579
|
+
self.ds = subdomain
|
|
580
|
+
|
|
581
|
+
def convert_to_float64(self) -> None:
|
|
582
|
+
"""Convert all data variables in the dataset to float64.
|
|
583
|
+
|
|
584
|
+
This method updates the dataset by converting all of its data variables to the
|
|
585
|
+
`float64` data type, ensuring consistency for numerical operations that require
|
|
586
|
+
high precision. The dataset is modified in place.
|
|
587
|
+
|
|
588
|
+
Parameters
|
|
589
|
+
----------
|
|
590
|
+
None
|
|
591
|
+
|
|
592
|
+
Returns
|
|
593
|
+
-------
|
|
594
|
+
None
|
|
595
|
+
This method modifies the dataset in place and does not return anything.
|
|
596
|
+
"""
|
|
597
|
+
ds = convert_to_float64(self.ds)
|
|
598
|
+
self.ds = ds
|
|
599
|
+
|
|
600
|
+
return None
|
|
601
|
+
|
|
602
|
+
def extrapolate_deepest_to_bottom(self):
|
|
603
|
+
"""Extrapolate deepest non-NaN values to fill bottom NaNs along the s (depth)
|
|
604
|
+
dimension.
|
|
605
|
+
|
|
606
|
+
For each variable with a depth dimension, fills missing values at the bottom by
|
|
607
|
+
propagating the deepest available data downward.
|
|
608
|
+
"""
|
|
609
|
+
self.ds = extrapolate_deepest_to_bottom(self.ds, "s_rho")
|
|
610
|
+
|
|
611
|
+
def apply_lateral_fill(self) -> None:
|
|
612
|
+
"""Apply lateral fill to variables using available masks and grid dimensions.
|
|
613
|
+
|
|
614
|
+
Lateral fill is applied only when:
|
|
615
|
+
- A corresponding mask exists in the dataset, and
|
|
616
|
+
- At least one variable is defined on the associated horizontal grid.
|
|
617
|
+
|
|
618
|
+
Raises
|
|
619
|
+
------
|
|
620
|
+
ValueError
|
|
621
|
+
If variables exist on a horizontal grid (rho, u, or v) but the
|
|
622
|
+
corresponding mask is missing.
|
|
623
|
+
"""
|
|
624
|
+
# Mapping of horizontal dims to required mask name
|
|
625
|
+
dim_to_mask: dict[tuple[str, str], str] = {
|
|
626
|
+
("eta_rho", "xi_rho"): "mask_rho",
|
|
627
|
+
("eta_rho", "xi_u"): "mask_u",
|
|
628
|
+
("eta_v", "xi_rho"): "mask_v",
|
|
629
|
+
}
|
|
630
|
+
horiz_dim_order = ("eta_rho", "eta_v", "xi_rho", "xi_u")
|
|
631
|
+
|
|
632
|
+
# Identify which horizontal dim sets are actually used
|
|
633
|
+
used_dim_sets: set[tuple[str, str]] = set()
|
|
634
|
+
|
|
635
|
+
for var in self.ds.data_vars.values():
|
|
636
|
+
horiz_dims = tuple(d for d in horiz_dim_order if d in var.dims)
|
|
637
|
+
if len(horiz_dims) == 2:
|
|
638
|
+
used_dim_sets.add(horiz_dims)
|
|
639
|
+
|
|
640
|
+
# Enforce required masks for all grids (rho, u, v)
|
|
641
|
+
for dims, mask_name in dim_to_mask.items():
|
|
642
|
+
if dims in used_dim_sets and mask_name not in self.ds:
|
|
643
|
+
raise ValueError(
|
|
644
|
+
f"Variable(s) found on grid {tuple(dims)}, but required mask "
|
|
645
|
+
f"'{mask_name}' is missing from the dataset."
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
# Build lateral fillers
|
|
649
|
+
lateral_fillers: dict[tuple[str, str], LateralFill] = {
|
|
650
|
+
dims: LateralFill(xr.where(self.ds[mask_name] == 1, True, False), dims)
|
|
651
|
+
for dims, mask_name in dim_to_mask.items()
|
|
652
|
+
if dims in used_dim_sets
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
# Apply lateral fill
|
|
656
|
+
for var_name, var in self.ds.data_vars.items():
|
|
657
|
+
if var_name.startswith("mask"):
|
|
658
|
+
continue
|
|
659
|
+
|
|
660
|
+
# Keep dims in canonical order
|
|
661
|
+
var_horiz_dims = tuple(d for d in horiz_dim_order if d in var.dims)
|
|
662
|
+
if len(var_horiz_dims) == 2:
|
|
663
|
+
filler = lateral_fillers.get(var_horiz_dims)
|
|
664
|
+
if filler is not None:
|
|
665
|
+
self.ds[var_name] = filler.apply(var)
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def choose_subdomain(
|
|
669
|
+
ds: xr.Dataset,
|
|
670
|
+
ds_grid: xr.Dataset,
|
|
671
|
+
target_coords: dict[str, Any],
|
|
672
|
+
buffer_points: int = DEFAULT_NR_BUFFER_POINTS,
|
|
673
|
+
):
|
|
674
|
+
"""Selects a subdomain from the xarray Dataset based on specified target
|
|
675
|
+
coordinates, extending the selection by a defined buffer. Adjusts longitude
|
|
676
|
+
ranges as necessary to accommodate the dataset's expected range and handles
|
|
677
|
+
potential discontinuities.
|
|
678
|
+
|
|
679
|
+
Parameters
|
|
680
|
+
----------
|
|
681
|
+
ds : xr.Dataset
|
|
682
|
+
The full ROMS xarray Dataset to subset.
|
|
683
|
+
ds_grid: xr.Dataset
|
|
684
|
+
Dataset containing the grid coordinates, in particular `pm` and `pn`.
|
|
685
|
+
target_coords : dict
|
|
686
|
+
A dictionary containing the target latitude and longitude coordinates, typically
|
|
687
|
+
with keys "lat", "lon", and "straddle".
|
|
688
|
+
buffer_points : int
|
|
689
|
+
The number of grid points to extend beyond the specified latitude and longitude
|
|
690
|
+
ranges when selecting the subdomain. Defaults to 20.
|
|
691
|
+
|
|
692
|
+
Returns
|
|
693
|
+
-------
|
|
694
|
+
xr.Dataset
|
|
695
|
+
Returns the subset of the original dataset.
|
|
696
|
+
|
|
697
|
+
Raises
|
|
698
|
+
------
|
|
699
|
+
ValueError
|
|
700
|
+
If the selected latitude or longitude range does not intersect with the dataset.
|
|
701
|
+
"""
|
|
702
|
+
# Adjust longitude range if needed to match the expected range
|
|
703
|
+
ds = wrap_longitudes(ds, target_coords["straddle"])
|
|
704
|
+
|
|
705
|
+
lat_min = target_coords["lat"].min().values
|
|
706
|
+
lat_max = target_coords["lat"].max().values
|
|
707
|
+
lon_min = target_coords["lon"].min().values
|
|
708
|
+
lon_max = target_coords["lon"].max().values
|
|
709
|
+
|
|
710
|
+
# Extract grid spacing (in meters)
|
|
711
|
+
dx = 0.5 * ((1 / ds_grid.pm).mean() + (1 / ds_grid.pn).mean()) # meters
|
|
712
|
+
buffer = dx * buffer_points # buffer distance in meters
|
|
713
|
+
|
|
714
|
+
lat = np.deg2rad(0.5 * (lat_min + lat_max))
|
|
715
|
+
|
|
716
|
+
deg_per_meter_lat = 1 / 111_320.0
|
|
717
|
+
margin_lat = buffer * deg_per_meter_lat
|
|
718
|
+
|
|
719
|
+
deg_per_meter_lon = 1 / (111_320.0 * np.cos(lat))
|
|
720
|
+
margin_lon = buffer * deg_per_meter_lon
|
|
721
|
+
|
|
722
|
+
mapping = {
|
|
723
|
+
"rho": ("eta_rho", "xi_rho"),
|
|
724
|
+
"u": ("eta_rho", "xi_u"),
|
|
725
|
+
"v": ("eta_v", "xi_rho"),
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
for location in ["rho", "u", "v"]:
|
|
729
|
+
eta_dim, xi_dim = mapping[location]
|
|
730
|
+
|
|
731
|
+
if eta_dim in ds.dims and xi_dim in ds.dims:
|
|
732
|
+
# Check that lat/lon coordinates exist
|
|
733
|
+
lat_coord = f"lat_{location}"
|
|
734
|
+
lon_coord = f"lon_{location}"
|
|
735
|
+
if lat_coord not in ds.coords or lon_coord not in ds.coords:
|
|
736
|
+
raise ValueError(
|
|
737
|
+
f"Dataset is missing expected coordinates for location '{location}': "
|
|
738
|
+
f"expected '{lat_coord}' and '{lon_coord}'"
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
# Build subset mask
|
|
742
|
+
subset_mask = (
|
|
743
|
+
(ds[lat_coord] > lat_min - margin_lat)
|
|
744
|
+
& (ds[lat_coord] < lat_max + margin_lat)
|
|
745
|
+
& (ds[lon_coord] > lon_min - margin_lon)
|
|
746
|
+
& (ds[lon_coord] < lon_max + margin_lon)
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
# Reduce along xi_dim
|
|
750
|
+
eta_mask = subset_mask.any(dim=xi_dim)
|
|
751
|
+
eta_indices = np.where(eta_mask)[0]
|
|
752
|
+
first_eta, last_eta = eta_indices[0], eta_indices[-1]
|
|
753
|
+
|
|
754
|
+
# Reduce along eta_dim
|
|
755
|
+
xi_mask = subset_mask.any(dim=eta_dim)
|
|
756
|
+
xi_indices = np.where(xi_mask)[0]
|
|
757
|
+
first_xi, last_xi = xi_indices[0], xi_indices[-1]
|
|
758
|
+
|
|
759
|
+
# Subset the dataset
|
|
760
|
+
ds = ds.isel(
|
|
761
|
+
**{
|
|
762
|
+
eta_dim: slice(first_eta, last_eta + 1),
|
|
763
|
+
xi_dim: slice(first_xi, last_xi + 1),
|
|
764
|
+
}
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
return ds
|