roms-tools 3.1.2__py3-none-any.whl → 3.3.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 +3 -0
- roms_tools/analysis/cdr_analysis.py +203 -0
- roms_tools/analysis/cdr_ensemble.py +198 -0
- roms_tools/analysis/roms_output.py +80 -46
- roms_tools/data/grids/GLORYS_global_grid.nc +0 -0
- roms_tools/download.py +4 -0
- roms_tools/plot.py +113 -51
- roms_tools/setup/boundary_forcing.py +45 -20
- roms_tools/setup/cdr_forcing.py +122 -8
- roms_tools/setup/cdr_release.py +161 -8
- roms_tools/setup/grid.py +150 -141
- roms_tools/setup/initial_conditions.py +113 -48
- roms_tools/setup/{datasets.py → lat_lon_datasets.py} +443 -938
- roms_tools/setup/mask.py +63 -7
- roms_tools/setup/nesting.py +314 -117
- roms_tools/setup/river_datasets.py +527 -0
- roms_tools/setup/river_forcing.py +46 -20
- roms_tools/setup/surface_forcing.py +7 -9
- roms_tools/setup/tides.py +2 -3
- roms_tools/setup/topography.py +8 -10
- roms_tools/setup/utils.py +396 -23
- roms_tools/tests/test_analysis/test_cdr_analysis.py +144 -0
- roms_tools/tests/test_analysis/test_cdr_ensemble.py +202 -0
- roms_tools/tests/test_analysis/test_roms_output.py +61 -3
- roms_tools/tests/test_setup/test_boundary_forcing.py +54 -52
- roms_tools/tests/test_setup/test_cdr_forcing.py +54 -0
- roms_tools/tests/test_setup/test_cdr_release.py +118 -1
- 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_ALT_CO2_west/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/ALK_west/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_ALT_CO2_west/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/DIC_west/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/DOC_west/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/DOCr_west/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/DON_west/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/DONr_west/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/DOP_west/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/DOPr_west/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/Fe_west/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/Lig_west/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/NH4_west/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/NO3_west/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/O2_west/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/PO4_west/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/SiO3_west/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/diatC_west/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/diatChl_west/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/diatFe_west/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/diatP_west/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/diatSi_west/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/diazC_west/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/diazChl_west/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/diazFe_west/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/diazP_west/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/spC_west/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/spCaCO3_west/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/spChl_west/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/spFe_west/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/spP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zarr.json +406 -406
- 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_climatology.zarr/zooC_west/c/0/0/0 +0 -0
- 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 +236 -115
- roms_tools/tests/test_setup/test_initial_conditions.py +94 -41
- roms_tools/tests/test_setup/{test_datasets.py → test_lat_lon_datasets.py} +409 -100
- roms_tools/tests/test_setup/test_nesting.py +119 -31
- roms_tools/tests/test_setup/test_river_datasets.py +48 -0
- roms_tools/tests/test_setup/test_surface_forcing.py +2 -1
- roms_tools/tests/test_setup/test_utils.py +92 -2
- roms_tools/tests/test_setup/utils.py +71 -0
- roms_tools/tests/test_tiling/test_join.py +241 -0
- roms_tools/tests/test_utils.py +139 -17
- roms_tools/tiling/join.py +189 -0
- roms_tools/utils.py +131 -99
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/METADATA +12 -2
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/RECORD +221 -211
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/WHEEL +0 -0
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/licenses/LICENSE +0 -0
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/top_level.txt +0 -0
roms_tools/setup/utils.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import importlib.metadata
|
|
2
2
|
import logging
|
|
3
|
+
import time
|
|
4
|
+
import typing
|
|
3
5
|
from collections.abc import Sequence
|
|
4
6
|
from dataclasses import asdict, fields, is_dataclass
|
|
5
|
-
from datetime import datetime
|
|
7
|
+
from datetime import datetime, timedelta
|
|
6
8
|
from enum import StrEnum
|
|
7
9
|
from pathlib import Path
|
|
8
|
-
from typing import Any
|
|
10
|
+
from typing import Any, Literal, TypeAlias
|
|
9
11
|
|
|
10
12
|
import cftime
|
|
11
13
|
import numba as nb
|
|
@@ -18,11 +20,55 @@ from pydantic import BaseModel
|
|
|
18
20
|
from roms_tools.constants import R_EARTH
|
|
19
21
|
from roms_tools.utils import interpolate_from_rho_to_u, interpolate_from_rho_to_v
|
|
20
22
|
|
|
23
|
+
if typing.TYPE_CHECKING:
|
|
24
|
+
from roms_tools.setup.grid import Grid
|
|
25
|
+
|
|
21
26
|
yaml.SafeDumper.add_multi_representer(
|
|
22
27
|
StrEnum,
|
|
23
28
|
yaml.representer.SafeRepresenter.represent_str,
|
|
24
29
|
)
|
|
25
30
|
|
|
31
|
+
HEADER_SZ = 96
|
|
32
|
+
HEADER_CHAR = "="
|
|
33
|
+
|
|
34
|
+
RawDataSource: TypeAlias = dict[str, str | Path | list[str | Path] | bool]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def log_the_separator() -> None:
|
|
38
|
+
"""Log a separator line using HEADER_CHAR repeated HEADER_SZ times."""
|
|
39
|
+
logging.info(HEADER_CHAR * HEADER_SZ)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Timed:
|
|
43
|
+
"""Context manager to time a block and log messages."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, message: str = "", verbose: bool = True) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Initialize the context manager.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
message : str, optional
|
|
52
|
+
A log message printed at the start of the block (default: "").
|
|
53
|
+
verbose : bool, optional
|
|
54
|
+
Whether to log timing information (default: True).
|
|
55
|
+
"""
|
|
56
|
+
self.message = message
|
|
57
|
+
self.verbose = verbose
|
|
58
|
+
self.start: float | None = None
|
|
59
|
+
|
|
60
|
+
def __enter__(self) -> "Timed":
|
|
61
|
+
if self.verbose:
|
|
62
|
+
self.start = time.time()
|
|
63
|
+
if self.message:
|
|
64
|
+
logging.info(self.message)
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
68
|
+
if self.verbose and self.start is not None:
|
|
69
|
+
logging.info(f"Total time: {time.time() - self.start:.3f} seconds")
|
|
70
|
+
log_the_separator()
|
|
71
|
+
|
|
26
72
|
|
|
27
73
|
def nan_check(field, mask, error_message=None) -> None:
|
|
28
74
|
"""Checks for NaN values at wet points in the field.
|
|
@@ -437,157 +483,193 @@ def get_variable_metadata():
|
|
|
437
483
|
"long_name": "dissolved inorganic phosphate",
|
|
438
484
|
"units": "mmol/m^3",
|
|
439
485
|
"flux_units": "mmol/s",
|
|
486
|
+
"integrated_units": "mmol",
|
|
440
487
|
},
|
|
441
488
|
"NO3": {
|
|
442
489
|
"long_name": "dissolved inorganic nitrate",
|
|
443
490
|
"units": "mmol/m^3",
|
|
444
491
|
"flux_units": "mmol/s",
|
|
492
|
+
"integrated_units": "mmol",
|
|
445
493
|
},
|
|
446
494
|
"SiO3": {
|
|
447
495
|
"long_name": "dissolved inorganic silicate",
|
|
448
496
|
"units": "mmol/m^3",
|
|
449
497
|
"flux_units": "mmol/s",
|
|
498
|
+
"integrated_units": "mmol",
|
|
450
499
|
},
|
|
451
500
|
"NH4": {
|
|
452
501
|
"long_name": "dissolved ammonia",
|
|
453
502
|
"units": "mmol/m^3",
|
|
454
503
|
"flux_units": "mmol/s",
|
|
504
|
+
"integrated_units": "mmol",
|
|
455
505
|
},
|
|
456
506
|
"Fe": {
|
|
457
507
|
"long_name": "dissolved inorganic iron",
|
|
458
508
|
"units": "mmol/m^3",
|
|
459
509
|
"flux_units": "mmol/s",
|
|
510
|
+
"integrated_units": "mmol",
|
|
460
511
|
},
|
|
461
512
|
"Lig": {
|
|
462
513
|
"long_name": "iron binding ligand",
|
|
463
514
|
"units": "mmol/m^3",
|
|
464
515
|
"flux_units": "mmol/s",
|
|
516
|
+
"integrated_units": "mmol",
|
|
465
517
|
},
|
|
466
518
|
"O2": {
|
|
467
519
|
"long_name": "dissolved oxygen",
|
|
468
520
|
"units": "mmol/m^3",
|
|
469
521
|
"flux_units": "mmol/s",
|
|
522
|
+
"integrated_units": "mmol",
|
|
470
523
|
},
|
|
471
524
|
"DIC": {
|
|
472
525
|
"long_name": "dissolved inorganic carbon",
|
|
473
526
|
"units": "mmol/m^3",
|
|
474
527
|
"flux_units": "mmol/s",
|
|
528
|
+
"integrated_units": "mmol",
|
|
475
529
|
},
|
|
476
530
|
"DIC_ALT_CO2": {
|
|
477
531
|
"long_name": "dissolved inorganic carbon, alternative CO2",
|
|
478
532
|
"units": "mmol/m^3",
|
|
479
533
|
"flux_units": "meq/s",
|
|
534
|
+
"integrated_units": "meq",
|
|
535
|
+
},
|
|
536
|
+
"ALK": {
|
|
537
|
+
"long_name": "alkalinity",
|
|
538
|
+
"units": "meq/m^3",
|
|
539
|
+
"flux_units": "meq/s",
|
|
540
|
+
"integrated_units": "meq",
|
|
480
541
|
},
|
|
481
|
-
"ALK": {"long_name": "alkalinity", "units": "meq/m^3", "flux_units": "meq/s"},
|
|
482
542
|
"ALK_ALT_CO2": {
|
|
483
543
|
"long_name": "alkalinity, alternative CO2",
|
|
484
544
|
"units": "meq/m^3",
|
|
485
545
|
"flux_units": "meq/s",
|
|
546
|
+
"integrated_units": "meq",
|
|
486
547
|
},
|
|
487
548
|
"DOC": {
|
|
488
549
|
"long_name": "dissolved organic carbon",
|
|
489
550
|
"units": "mmol/m^3",
|
|
490
551
|
"flux_units": "mmol/s",
|
|
552
|
+
"integrated_units": "mmol",
|
|
491
553
|
},
|
|
492
554
|
"DON": {
|
|
493
555
|
"long_name": "dissolved organic nitrogen",
|
|
494
556
|
"units": "mmol/m^3",
|
|
495
557
|
"flux_units": "mmol/s",
|
|
558
|
+
"integrated_units": "mmol",
|
|
496
559
|
},
|
|
497
560
|
"DOP": {
|
|
498
561
|
"long_name": "dissolved organic phosphorus",
|
|
499
562
|
"units": "mmol/m^3",
|
|
500
563
|
"flux_units": "mmol/s",
|
|
564
|
+
"integrated_units": "mmol",
|
|
501
565
|
},
|
|
502
566
|
"DOCr": {
|
|
503
567
|
"long_name": "refractory dissolved organic carbon",
|
|
504
568
|
"units": "mmol/m^3",
|
|
505
569
|
"flux_units": "mmol/s",
|
|
570
|
+
"integrated_units": "mmol",
|
|
506
571
|
},
|
|
507
572
|
"DONr": {
|
|
508
573
|
"long_name": "refractory dissolved organic nitrogen",
|
|
509
574
|
"units": "mmol/m^3",
|
|
510
575
|
"flux_units": "mmol/s",
|
|
576
|
+
"integrated_units": "mmol",
|
|
511
577
|
},
|
|
512
578
|
"DOPr": {
|
|
513
579
|
"long_name": "refractory dissolved organic phosphorus",
|
|
514
580
|
"units": "mmol/m^3",
|
|
515
581
|
"flux_units": "mmol/s",
|
|
582
|
+
"integrated_units": "mmol",
|
|
516
583
|
},
|
|
517
584
|
"zooC": {
|
|
518
585
|
"long_name": "zooplankton carbon",
|
|
519
586
|
"units": "mmol/m^3",
|
|
520
587
|
"flux_units": "mmol/s",
|
|
588
|
+
"integrated_units": "mmol",
|
|
521
589
|
},
|
|
522
590
|
"spChl": {
|
|
523
591
|
"long_name": "small phytoplankton chlorophyll",
|
|
524
592
|
"units": "mg/m^3",
|
|
525
593
|
"flux_units": "mg/s",
|
|
594
|
+
"integrated_units": "mg",
|
|
526
595
|
},
|
|
527
596
|
"spC": {
|
|
528
597
|
"long_name": "small phytoplankton carbon",
|
|
529
598
|
"units": "mmol/m^3",
|
|
530
599
|
"flux_units": "mmol/s",
|
|
600
|
+
"integrated_units": "mmol",
|
|
531
601
|
},
|
|
532
602
|
"spP": {
|
|
533
603
|
"long_name": "small phytoplankton phosphorous",
|
|
534
604
|
"units": "mmol/m^3",
|
|
535
605
|
"flux_units": "mmol/s",
|
|
606
|
+
"integrated_units": "mmol",
|
|
536
607
|
},
|
|
537
608
|
"spFe": {
|
|
538
609
|
"long_name": "small phytoplankton iron",
|
|
539
610
|
"units": "mmol/m^3",
|
|
540
611
|
"flux_units": "mmol/s",
|
|
612
|
+
"integrated_units": "mmol",
|
|
541
613
|
},
|
|
542
614
|
"spCaCO3": {
|
|
543
615
|
"long_name": "small phytoplankton CaCO3",
|
|
544
616
|
"units": "mmol/m^3",
|
|
545
617
|
"flux_units": "mmol/s",
|
|
618
|
+
"integrated_units": "mmol",
|
|
546
619
|
},
|
|
547
620
|
"diatChl": {
|
|
548
621
|
"long_name": "diatom chloropyll",
|
|
549
622
|
"units": "mg/m^3",
|
|
550
623
|
"flux_units": "mg/s",
|
|
624
|
+
"integrated_units": "mg",
|
|
551
625
|
},
|
|
552
626
|
"diatC": {
|
|
553
627
|
"long_name": "diatom carbon",
|
|
554
628
|
"units": "mmol/m^3",
|
|
555
629
|
"flux_units": "mmol/s",
|
|
630
|
+
"integrated_units": "mmol",
|
|
556
631
|
},
|
|
557
632
|
"diatP": {
|
|
558
633
|
"long_name": "diatom phosphorus",
|
|
559
634
|
"units": "mmol/m^3",
|
|
560
635
|
"flux_units": "mmol/s",
|
|
636
|
+
"integrated_units": "mmol",
|
|
561
637
|
},
|
|
562
638
|
"diatFe": {
|
|
563
639
|
"long_name": "diatom iron",
|
|
564
640
|
"units": "mmol/m^3",
|
|
565
641
|
"flux_units": "mmol/s",
|
|
642
|
+
"integrated_units": "mmol",
|
|
566
643
|
},
|
|
567
644
|
"diatSi": {
|
|
568
645
|
"long_name": "diatom silicate",
|
|
569
646
|
"units": "mmol/m^3",
|
|
570
647
|
"flux_units": "mmol/s",
|
|
648
|
+
"integrated_units": "mmol",
|
|
571
649
|
},
|
|
572
650
|
"diazChl": {
|
|
573
651
|
"long_name": "diazotroph chloropyll",
|
|
574
652
|
"units": "mg/m^3",
|
|
575
653
|
"flux_units": "mg/s",
|
|
654
|
+
"integrated_units": "mg",
|
|
576
655
|
},
|
|
577
656
|
"diazC": {
|
|
578
657
|
"long_name": "diazotroph carbon",
|
|
579
658
|
"units": "mmol/m^3",
|
|
580
659
|
"flux_units": "mmol/s",
|
|
660
|
+
"integrated_units": "mmol",
|
|
581
661
|
},
|
|
582
662
|
"diazP": {
|
|
583
663
|
"long_name": "diazotroph phosphorus",
|
|
584
664
|
"units": "mmol/m^3",
|
|
585
665
|
"flux_units": "mmol/s",
|
|
666
|
+
"integrated_units": "mmol",
|
|
586
667
|
},
|
|
587
668
|
"diazFe": {
|
|
588
669
|
"long_name": "diazotroph iron",
|
|
589
670
|
"units": "mmol/m^3",
|
|
590
671
|
"flux_units": "mmol/s",
|
|
672
|
+
"integrated_units": "mmol",
|
|
591
673
|
},
|
|
592
674
|
"pco2_air": {"long_name": "atmospheric pCO2", "units": "ppmv"},
|
|
593
675
|
"pco2_air_alt": {
|
|
@@ -720,7 +802,10 @@ def compute_missing_surface_bgc_variables(bgc_data):
|
|
|
720
802
|
return bgc_data
|
|
721
803
|
|
|
722
804
|
|
|
723
|
-
def get_tracer_metadata_dict(
|
|
805
|
+
def get_tracer_metadata_dict(
|
|
806
|
+
include_bgc: bool = True,
|
|
807
|
+
unit_type: Literal["concentration", "flux", "integrated"] = "concentration",
|
|
808
|
+
):
|
|
724
809
|
"""Generate a dictionary containing metadata for model tracers.
|
|
725
810
|
|
|
726
811
|
The returned dictionary maps tracer names to their associated units and long names.
|
|
@@ -732,9 +817,8 @@ def get_tracer_metadata_dict(include_bgc=True, with_flux_units=False):
|
|
|
732
817
|
If True (default), includes biogeochemical tracers in the output.
|
|
733
818
|
If False, returns only physical tracers (e.g., temperature, salinity).
|
|
734
819
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
If False (default), uses units appropriate for tracer concentrations (e.g., mmol/m³).
|
|
820
|
+
unit_type : str
|
|
821
|
+
One of "concentration" (default), "flux", or "integrated".
|
|
738
822
|
|
|
739
823
|
Returns
|
|
740
824
|
-------
|
|
@@ -784,15 +868,19 @@ def get_tracer_metadata_dict(include_bgc=True, with_flux_units=False):
|
|
|
784
868
|
|
|
785
869
|
metadata = get_variable_metadata()
|
|
786
870
|
|
|
787
|
-
tracer_dict = {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
871
|
+
tracer_dict = {}
|
|
872
|
+
for tracer in tracer_names:
|
|
873
|
+
if unit_type == "flux":
|
|
874
|
+
unit = metadata[tracer]["flux_units"]
|
|
875
|
+
elif unit_type == "integrated":
|
|
876
|
+
unit = metadata[tracer].get("integrated_units", None)
|
|
877
|
+
else: # default to concentration units
|
|
878
|
+
unit = metadata[tracer]["units"]
|
|
879
|
+
|
|
880
|
+
tracer_dict[tracer] = {
|
|
881
|
+
"units": unit,
|
|
792
882
|
"long_name": metadata[tracer]["long_name"],
|
|
793
883
|
}
|
|
794
|
-
for tracer in tracer_names
|
|
795
|
-
}
|
|
796
884
|
|
|
797
885
|
return tracer_dict
|
|
798
886
|
|
|
@@ -819,7 +907,8 @@ def add_tracer_metadata_to_ds(ds, include_bgc=True, with_flux_units=False):
|
|
|
819
907
|
xarray.Dataset
|
|
820
908
|
The dataset with added tracer metadata.
|
|
821
909
|
"""
|
|
822
|
-
|
|
910
|
+
unit_type = "flux" if with_flux_units else "concentration"
|
|
911
|
+
tracer_dict = get_tracer_metadata_dict(include_bgc, unit_type=unit_type)
|
|
823
912
|
|
|
824
913
|
tracer_names = list(tracer_dict.keys())
|
|
825
914
|
tracer_units = [tracer_dict[tracer]["units"] for tracer in tracer_names]
|
|
@@ -1045,22 +1134,47 @@ def group_by_year(ds, filepath):
|
|
|
1045
1134
|
return dataset_list, output_filenames
|
|
1046
1135
|
|
|
1047
1136
|
|
|
1048
|
-
def get_target_coords(
|
|
1049
|
-
""
|
|
1050
|
-
|
|
1137
|
+
def get_target_coords(
|
|
1138
|
+
grid: "Grid", use_coarse_grid: bool = False
|
|
1139
|
+
) -> dict[str, xr.DataArray | bool | None]:
|
|
1140
|
+
"""
|
|
1141
|
+
Retrieve longitude, latitude, and auxiliary grid coordinates, adjusting for
|
|
1142
|
+
longitude ranges and coarse grid usage.
|
|
1051
1143
|
|
|
1052
1144
|
Parameters
|
|
1053
1145
|
----------
|
|
1054
1146
|
grid : Grid
|
|
1055
|
-
|
|
1147
|
+
Grid object.
|
|
1056
1148
|
use_coarse_grid : bool, optional
|
|
1057
|
-
|
|
1149
|
+
If True, use the coarse grid variables (`lat_coarse`, `lon_coarse`, etc.)
|
|
1150
|
+
instead of the native grid. Defaults to False.
|
|
1058
1151
|
|
|
1059
1152
|
Returns
|
|
1060
1153
|
-------
|
|
1061
|
-
dict
|
|
1062
|
-
Dictionary containing the
|
|
1063
|
-
|
|
1154
|
+
dict[str, xr.DataArray | bool | None]
|
|
1155
|
+
Dictionary containing the following keys:
|
|
1156
|
+
|
|
1157
|
+
- `"lat"` : xr.DataArray
|
|
1158
|
+
Latitude at rho points.
|
|
1159
|
+
- `"lon"` : xr.DataArray
|
|
1160
|
+
Longitude at rho points, adjusted to -180 to 180 or 0 to 360 range.
|
|
1161
|
+
- `"lat_psi"` : xr.DataArray | None
|
|
1162
|
+
Latitude at psi points, if available.
|
|
1163
|
+
- `"lon_psi"` : xr.DataArray | None
|
|
1164
|
+
Longitude at psi points, if available.
|
|
1165
|
+
- `"angle"` : xr.DataArray
|
|
1166
|
+
Grid rotation angle.
|
|
1167
|
+
- `"mask"` : xr.DataArray | None
|
|
1168
|
+
Land/sea mask at rho points.
|
|
1169
|
+
- `"straddle"` : bool
|
|
1170
|
+
True if the grid crosses the Greenwich meridian, False otherwise.
|
|
1171
|
+
|
|
1172
|
+
Notes
|
|
1173
|
+
-----
|
|
1174
|
+
- If `grid.straddle` is False and the ROMS domain lies more than 5° from
|
|
1175
|
+
the Greenwich meridian, longitudes are adjusted to 0-360 range.
|
|
1176
|
+
- Renaming of coarse grid dimensions is applied to match the rho-point
|
|
1177
|
+
naming convention (`eta_rho`, `xi_rho`) for compatibility with ROMS-Tools.
|
|
1064
1178
|
"""
|
|
1065
1179
|
# Select grid variables based on whether the coarse grid is used
|
|
1066
1180
|
if use_coarse_grid:
|
|
@@ -1851,3 +1965,262 @@ def validate_names(
|
|
|
1851
1965
|
names = names[:max_to_plot]
|
|
1852
1966
|
|
|
1853
1967
|
return names
|
|
1968
|
+
|
|
1969
|
+
|
|
1970
|
+
def check_dataset(
|
|
1971
|
+
ds: xr.Dataset,
|
|
1972
|
+
dim_names: dict[str, str],
|
|
1973
|
+
var_names: dict[str, str],
|
|
1974
|
+
opt_var_names: dict[str, str] | None = None,
|
|
1975
|
+
) -> None:
|
|
1976
|
+
"""Check if the dataset contains the specified variables and dimensions.
|
|
1977
|
+
|
|
1978
|
+
Parameters
|
|
1979
|
+
----------
|
|
1980
|
+
ds : xr.Dataset
|
|
1981
|
+
The xarray Dataset to check.
|
|
1982
|
+
dim_names: Dict[str, str], optional
|
|
1983
|
+
Dictionary specifying the names of dimensions in the dataset.
|
|
1984
|
+
var_names: Dict[str, str]
|
|
1985
|
+
Dictionary of variable names that are required in the dataset.
|
|
1986
|
+
opt_var_names : Optional[Dict[str, str]], optional
|
|
1987
|
+
Dictionary of optional variable names.
|
|
1988
|
+
These variables are not strictly required, and the function will not raise an error if they are missing.
|
|
1989
|
+
Default is None, meaning no optional variables are considered.
|
|
1990
|
+
|
|
1991
|
+
|
|
1992
|
+
Raises
|
|
1993
|
+
------
|
|
1994
|
+
ValueError
|
|
1995
|
+
If the dataset does not contain the specified variables or dimensions.
|
|
1996
|
+
"""
|
|
1997
|
+
missing_dims = [dim for dim in dim_names.values() if dim not in ds.dims]
|
|
1998
|
+
if missing_dims:
|
|
1999
|
+
raise ValueError(
|
|
2000
|
+
f"Dataset does not contain all required dimensions. The following dimensions are missing: {missing_dims}"
|
|
2001
|
+
)
|
|
2002
|
+
|
|
2003
|
+
missing_vars = [var for var in var_names.values() if var not in ds.data_vars]
|
|
2004
|
+
if missing_vars:
|
|
2005
|
+
raise ValueError(
|
|
2006
|
+
f"Dataset does not contain all required variables. The following variables are missing: {missing_vars}"
|
|
2007
|
+
)
|
|
2008
|
+
|
|
2009
|
+
if opt_var_names:
|
|
2010
|
+
missing_optional_vars = [
|
|
2011
|
+
var for var in opt_var_names.values() if var not in ds.data_vars
|
|
2012
|
+
]
|
|
2013
|
+
if missing_optional_vars:
|
|
2014
|
+
logging.warning(
|
|
2015
|
+
f"Optional variables missing (but not critical): {missing_optional_vars}"
|
|
2016
|
+
)
|
|
2017
|
+
|
|
2018
|
+
|
|
2019
|
+
def select_relevant_times(
|
|
2020
|
+
ds: xr.Dataset,
|
|
2021
|
+
time_dim: str,
|
|
2022
|
+
start_time: datetime,
|
|
2023
|
+
end_time: datetime | None = None,
|
|
2024
|
+
climatology: bool = False,
|
|
2025
|
+
allow_flex_time: bool = False,
|
|
2026
|
+
) -> xr.Dataset:
|
|
2027
|
+
"""
|
|
2028
|
+
Select a subset of the dataset based on time constraints.
|
|
2029
|
+
|
|
2030
|
+
This function supports two main use cases:
|
|
2031
|
+
|
|
2032
|
+
1. **Time range selection (start_time + end_time provided):**
|
|
2033
|
+
- Returns all records strictly between `start_time` and `end_time`.
|
|
2034
|
+
- Ensures at least one record at or before `start_time` and one record at or
|
|
2035
|
+
after `end_time` are included, even if they fall outside the strict range.
|
|
2036
|
+
|
|
2037
|
+
2. **Initial condition selection (start_time provided, end_time=None):**
|
|
2038
|
+
- Delegates to `_select_initial_time`, which reduces the dataset to exactly one
|
|
2039
|
+
time entry.
|
|
2040
|
+
- If `allow_flex_time=True`, a +24-hour buffer around `start_time` is allowed,
|
|
2041
|
+
and the closest timestamp is chosen.
|
|
2042
|
+
- If `allow_flex_time=False`, requires an exact timestamp match.
|
|
2043
|
+
|
|
2044
|
+
Additional behavior:
|
|
2045
|
+
- If `climatology=True`, the dataset must contain exactly 12 time steps. If valid,
|
|
2046
|
+
the climatology dataset is returned without further filtering.
|
|
2047
|
+
- If the dataset uses `cftime` datetime objects, these are converted to
|
|
2048
|
+
`np.datetime64` before filtering.
|
|
2049
|
+
|
|
2050
|
+
Parameters
|
|
2051
|
+
----------
|
|
2052
|
+
ds : xr.Dataset
|
|
2053
|
+
The dataset to filter. Must contain a valid time dimension.
|
|
2054
|
+
time_dim : str
|
|
2055
|
+
Name of the time dimension in `ds`.
|
|
2056
|
+
start_time : datetime
|
|
2057
|
+
Start time for filtering.
|
|
2058
|
+
end_time : datetime or None
|
|
2059
|
+
End time for filtering. If `None`, the function assumes an initial condition
|
|
2060
|
+
use case and selects exactly one timestamp.
|
|
2061
|
+
climatology : bool, optional
|
|
2062
|
+
If True, requires exactly 12 time steps and bypasses normal filtering.
|
|
2063
|
+
Defaults to False.
|
|
2064
|
+
allow_flex_time : bool, optional
|
|
2065
|
+
Whether to allow a +24h search window after `start_time` when `end_time`
|
|
2066
|
+
is None. If False (default), requires an exact match.
|
|
2067
|
+
|
|
2068
|
+
Returns
|
|
2069
|
+
-------
|
|
2070
|
+
xr.Dataset
|
|
2071
|
+
A filtered dataset containing only the selected time entries.
|
|
2072
|
+
|
|
2073
|
+
Raises
|
|
2074
|
+
------
|
|
2075
|
+
ValueError
|
|
2076
|
+
- If `climatology=True` but the dataset does not contain exactly 12 time steps.
|
|
2077
|
+
- If `climatology=False` and the dataset contains integer time values.
|
|
2078
|
+
- If no valid records are found within the requested range or window.
|
|
2079
|
+
|
|
2080
|
+
Warns
|
|
2081
|
+
-----
|
|
2082
|
+
UserWarning
|
|
2083
|
+
- If no records exist at or before `start_time` or at or after `end_time`.
|
|
2084
|
+
- If the specified time dimension does not exist in the dataset.
|
|
2085
|
+
|
|
2086
|
+
Notes
|
|
2087
|
+
-----
|
|
2088
|
+
- For initial conditions (end_time=None), see `_select_initial_time` for details
|
|
2089
|
+
on strict vs. flexible selection behavior.
|
|
2090
|
+
- Logs warnings instead of failing hard when boundary records are missing, and
|
|
2091
|
+
defaults to using the earliest or latest available time in such cases.
|
|
2092
|
+
"""
|
|
2093
|
+
if time_dim not in ds.variables:
|
|
2094
|
+
logging.warning(
|
|
2095
|
+
f"Dataset does not contain time dimension '{time_dim}'. "
|
|
2096
|
+
"Please check variable naming or dataset structure."
|
|
2097
|
+
)
|
|
2098
|
+
return ds
|
|
2099
|
+
|
|
2100
|
+
time_type = get_time_type(ds[time_dim])
|
|
2101
|
+
|
|
2102
|
+
if climatology:
|
|
2103
|
+
if len(ds[time_dim]) != 12:
|
|
2104
|
+
raise ValueError(
|
|
2105
|
+
f"The dataset contains {len(ds[time_dim])} time steps, but the climatology flag is set to True, which requires exactly 12 time steps."
|
|
2106
|
+
)
|
|
2107
|
+
else:
|
|
2108
|
+
if time_type == "int":
|
|
2109
|
+
raise ValueError(
|
|
2110
|
+
"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."
|
|
2111
|
+
)
|
|
2112
|
+
if time_type == "cftime":
|
|
2113
|
+
ds = ds.assign_coords({time_dim: convert_cftime_to_datetime(ds[time_dim])})
|
|
2114
|
+
|
|
2115
|
+
if not end_time:
|
|
2116
|
+
# Assume we are looking for exactly one time record for initial conditions
|
|
2117
|
+
return _select_initial_time(
|
|
2118
|
+
ds, time_dim, start_time, climatology, allow_flex_time
|
|
2119
|
+
)
|
|
2120
|
+
|
|
2121
|
+
if climatology:
|
|
2122
|
+
return ds
|
|
2123
|
+
|
|
2124
|
+
# Identify records before or at start_time
|
|
2125
|
+
before_start = ds[time_dim] <= np.datetime64(start_time)
|
|
2126
|
+
if before_start.any():
|
|
2127
|
+
closest_before_start = ds[time_dim].where(before_start, drop=True)[-1]
|
|
2128
|
+
else:
|
|
2129
|
+
logging.warning(f"No records found at or before the start_time: {start_time}.")
|
|
2130
|
+
closest_before_start = ds[time_dim][0]
|
|
2131
|
+
|
|
2132
|
+
# Identify records after or at end_time
|
|
2133
|
+
after_end = ds[time_dim] >= np.datetime64(end_time)
|
|
2134
|
+
if after_end.any():
|
|
2135
|
+
closest_after_end = ds[time_dim].where(after_end, drop=True).min()
|
|
2136
|
+
else:
|
|
2137
|
+
logging.warning(f"No records found at or after the end_time: {end_time}.")
|
|
2138
|
+
closest_after_end = ds[time_dim].max()
|
|
2139
|
+
|
|
2140
|
+
# Select records within the time range and add the closest before/after
|
|
2141
|
+
within_range = (ds[time_dim] > np.datetime64(start_time)) & (
|
|
2142
|
+
ds[time_dim] < np.datetime64(end_time)
|
|
2143
|
+
)
|
|
2144
|
+
selected_times = ds[time_dim].where(
|
|
2145
|
+
within_range
|
|
2146
|
+
| (ds[time_dim] == closest_before_start)
|
|
2147
|
+
| (ds[time_dim] == closest_after_end),
|
|
2148
|
+
drop=True,
|
|
2149
|
+
)
|
|
2150
|
+
ds = ds.sel({time_dim: selected_times})
|
|
2151
|
+
|
|
2152
|
+
return ds
|
|
2153
|
+
|
|
2154
|
+
|
|
2155
|
+
def _select_initial_time(
|
|
2156
|
+
ds: xr.Dataset,
|
|
2157
|
+
time_dim: str,
|
|
2158
|
+
ini_time: datetime,
|
|
2159
|
+
climatology: bool,
|
|
2160
|
+
allow_flex_time: bool = False,
|
|
2161
|
+
) -> xr.Dataset:
|
|
2162
|
+
"""Select exactly one initial time from dataset.
|
|
2163
|
+
|
|
2164
|
+
Parameters
|
|
2165
|
+
----------
|
|
2166
|
+
ds : xr.Dataset
|
|
2167
|
+
The input dataset with a time dimension.
|
|
2168
|
+
time_dim : str
|
|
2169
|
+
Name of the time dimension.
|
|
2170
|
+
ini_time : datetime
|
|
2171
|
+
The desired initial time.
|
|
2172
|
+
allow_flex_time : bool
|
|
2173
|
+
- If True: allow a +24h window and pick the closest available timestamp.
|
|
2174
|
+
- If False (default): require an exact match, otherwise raise ValueError.
|
|
2175
|
+
|
|
2176
|
+
Returns
|
|
2177
|
+
-------
|
|
2178
|
+
xr.Dataset
|
|
2179
|
+
Dataset reduced to exactly one timestamp.
|
|
2180
|
+
|
|
2181
|
+
Raises
|
|
2182
|
+
------
|
|
2183
|
+
ValueError
|
|
2184
|
+
If no matching time is found (when `allow_flex_time=False`), or no entries are
|
|
2185
|
+
available within the +24h window (when `allow_flex_time=True`).
|
|
2186
|
+
"""
|
|
2187
|
+
if climatology:
|
|
2188
|
+
# Convert from timedelta64[ns] to fractional days
|
|
2189
|
+
ds["time"] = ds["time"] / np.timedelta64(1, "D")
|
|
2190
|
+
# Interpolate from climatology for initial conditions
|
|
2191
|
+
return interpolate_from_climatology(ds, time_dim, ini_time)
|
|
2192
|
+
|
|
2193
|
+
if allow_flex_time:
|
|
2194
|
+
# Look in time range [ini_time, ini_time + 24h)
|
|
2195
|
+
end_time = ini_time + timedelta(days=1)
|
|
2196
|
+
times = (np.datetime64(ini_time) <= ds[time_dim]) & (
|
|
2197
|
+
ds[time_dim] < np.datetime64(end_time)
|
|
2198
|
+
)
|
|
2199
|
+
|
|
2200
|
+
if np.all(~times):
|
|
2201
|
+
raise ValueError(
|
|
2202
|
+
f"No time entries found between {ini_time} and {end_time}."
|
|
2203
|
+
)
|
|
2204
|
+
|
|
2205
|
+
ds = ds.where(times, drop=True)
|
|
2206
|
+
if ds.sizes[time_dim] > 1:
|
|
2207
|
+
# Pick the time closest to start_time
|
|
2208
|
+
ds = ds.isel({time_dim: 0})
|
|
2209
|
+
|
|
2210
|
+
logging.warning(
|
|
2211
|
+
f"Selected time entry closest to the specified start_time in +24 hour range: {ds[time_dim].values}"
|
|
2212
|
+
)
|
|
2213
|
+
|
|
2214
|
+
else:
|
|
2215
|
+
# Strict match required
|
|
2216
|
+
if not (ds[time_dim].values == np.datetime64(ini_time)).any():
|
|
2217
|
+
raise ValueError(
|
|
2218
|
+
f"No exact match found for initial time {ini_time}. Consider setting allow_flex_time to True."
|
|
2219
|
+
)
|
|
2220
|
+
|
|
2221
|
+
ds = ds.sel({time_dim: np.datetime64(ini_time)})
|
|
2222
|
+
|
|
2223
|
+
if time_dim not in ds.dims:
|
|
2224
|
+
ds = ds.expand_dims(time_dim)
|
|
2225
|
+
|
|
2226
|
+
return ds
|