roms-tools 2.4.0__py3-none-any.whl → 2.5.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/roms_output.py +77 -98
- roms_tools/plot.py +4 -2
- roms_tools/setup/boundary_forcing.py +29 -15
- roms_tools/setup/datasets.py +91 -32
- roms_tools/setup/grid.py +4 -5
- roms_tools/setup/initial_conditions.py +7 -6
- roms_tools/setup/nesting.py +237 -63
- roms_tools/setup/river_forcing.py +243 -72
- roms_tools/setup/surface_forcing.py +26 -15
- roms_tools/setup/tides.py +3 -6
- roms_tools/setup/topography.py +25 -2
- roms_tools/setup/utils.py +28 -12
- roms_tools/tests/test_analysis/test_roms_output.py +233 -70
- roms_tools/tests/test_setup/test_boundary_forcing.py +63 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zattrs +3 -1
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zmetadata +3 -1
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zattrs +2 -2
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zmetadata +8 -7
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/abs_time/.zattrs +1 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust_time/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron_time/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy_time/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox_time/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air_alt/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_time/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zattrs +2 -2
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zmetadata +2 -2
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air_alt/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +2 -2
- roms_tools/tests/test_setup/test_data/grid.zarr/angle/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/angle_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/f/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/h/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/grid.zarr/h/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/lat_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/lat_rho/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/lat_u/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/lat_v/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/pm/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +1 -1
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +1 -1
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zeta/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +27 -1
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zarray +20 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zattrs +6 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/0 +0 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +27 -1
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zarray +20 -0
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zattrs +6 -0
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/0 +0 -0
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +1 -1
- roms_tools/tests/test_setup/test_initial_conditions.py +16 -0
- roms_tools/tests/test_setup/test_nesting.py +141 -104
- roms_tools/tests/test_setup/test_river_forcing.py +587 -269
- roms_tools/tests/test_setup/test_surface_forcing.py +47 -0
- roms_tools/tests/test_setup/test_validation.py +34 -2
- {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/METADATA +1 -1
- {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/RECORD +208 -202
- {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/WHEEL +1 -1
- {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/LICENSE +0 -0
- {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/top_level.txt +0 -0
roms_tools/__init__.py
CHANGED
|
@@ -14,7 +14,7 @@ from roms_tools.setup.surface_forcing import SurfaceForcing # noqa: F401
|
|
|
14
14
|
from roms_tools.setup.initial_conditions import InitialConditions # noqa: F401
|
|
15
15
|
from roms_tools.setup.boundary_forcing import BoundaryForcing # noqa: F401
|
|
16
16
|
from roms_tools.setup.river_forcing import RiverForcing # noqa: F401
|
|
17
|
-
from roms_tools.setup.nesting import
|
|
17
|
+
from roms_tools.setup.nesting import ChildGrid # noqa: F401
|
|
18
18
|
from roms_tools.tiling.partition import partition_netcdf # noqa: F401
|
|
19
19
|
from roms_tools.analysis.roms_output import ROMSOutput # noqa: F401
|
|
20
20
|
|
|
@@ -5,7 +5,6 @@ from roms_tools.utils import _load_data
|
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
6
|
from typing import Union, Optional
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
import os
|
|
9
8
|
import re
|
|
10
9
|
import logging
|
|
11
10
|
from datetime import datetime, timedelta
|
|
@@ -25,34 +24,25 @@ class ROMSOutput:
|
|
|
25
24
|
grid : Grid
|
|
26
25
|
Object representing the grid information.
|
|
27
26
|
path : Union[str, Path, List[Union[str, Path]]]
|
|
28
|
-
|
|
29
|
-
type : str
|
|
30
|
-
Specifies the type of model output. Options are:
|
|
31
|
-
|
|
32
|
-
- "restart": for restart files.
|
|
33
|
-
- "average": for time-averaged files.
|
|
34
|
-
- "snapshot": for snapshot files.
|
|
35
|
-
|
|
27
|
+
Filename, or list of filenames with model output.
|
|
36
28
|
model_reference_date : datetime, optional
|
|
37
29
|
If not specified, this is inferred from metadata of the model output
|
|
38
30
|
If specified and does not coincide with metadata, a warning is raised.
|
|
31
|
+
adjust_depth_for_sea_surface_height : bool, optional
|
|
32
|
+
Whether to account for sea surface height variations when computing depth coordinates.
|
|
33
|
+
Defaults to `False`.
|
|
39
34
|
use_dask: bool, optional
|
|
40
35
|
Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
|
|
41
36
|
"""
|
|
42
37
|
|
|
43
38
|
grid: Grid
|
|
44
39
|
path: Union[str, Path]
|
|
45
|
-
type: Union[str, Path]
|
|
46
40
|
use_dask: bool = False
|
|
47
41
|
model_reference_date: Optional[datetime] = None
|
|
42
|
+
adjust_depth_for_sea_surface_height: Optional[bool] = False
|
|
48
43
|
ds: xr.Dataset = field(init=False, repr=False)
|
|
49
44
|
|
|
50
45
|
def __post_init__(self):
|
|
51
|
-
# Validate `type`
|
|
52
|
-
if self.type not in {"restart", "average", "snapshot"}:
|
|
53
|
-
raise ValueError(
|
|
54
|
-
f"Invalid type '{self.type}'. Must be one of 'restart', 'average', or 'snapshot'."
|
|
55
|
-
)
|
|
56
46
|
|
|
57
47
|
ds = self._load_model_output()
|
|
58
48
|
self._infer_model_reference_date_from_metadata(ds)
|
|
@@ -61,6 +51,9 @@ class ROMSOutput:
|
|
|
61
51
|
ds = self._add_lat_lon_coords(ds)
|
|
62
52
|
object.__setattr__(self, "ds", ds)
|
|
63
53
|
|
|
54
|
+
# Dataset for depth coordinates
|
|
55
|
+
object.__setattr__(self, "ds_depth_coords", xr.Dataset())
|
|
56
|
+
|
|
64
57
|
def plot(
|
|
65
58
|
self,
|
|
66
59
|
var_name,
|
|
@@ -68,6 +61,7 @@ class ROMSOutput:
|
|
|
68
61
|
s=None,
|
|
69
62
|
eta=None,
|
|
70
63
|
xi=None,
|
|
64
|
+
include_boundary=False,
|
|
71
65
|
depth_contours=False,
|
|
72
66
|
layer_contours=False,
|
|
73
67
|
ax=None,
|
|
@@ -94,6 +88,11 @@ class ROMSOutput:
|
|
|
94
88
|
xi : int, optional
|
|
95
89
|
The xi-index to plot. Used for vertical sections or horizontal slices.
|
|
96
90
|
Default is None.
|
|
91
|
+
include_boundary : bool, optional
|
|
92
|
+
Whether to include the outermost grid cells along the `eta`- and `xi`-boundaries in the plot.
|
|
93
|
+
In diagnostic ROMS output fields, these boundary cells are set to zero, so excluding them can improve visualization.
|
|
94
|
+
This option is only relevant for 2D horizontal plots (`eta=None`, `xi=None`).
|
|
95
|
+
Default is False.
|
|
97
96
|
depth_contours : bool, optional
|
|
98
97
|
If True, depth contours will be overlaid on the plot, showing lines of constant
|
|
99
98
|
depth. This is typically used for plots that show a single vertical layer.
|
|
@@ -183,10 +182,18 @@ class ROMSOutput:
|
|
|
183
182
|
compute_layer_depth = (depth_contours or s is None) and len(field.dims) > 2
|
|
184
183
|
compute_interface_depth = layer_contours and s is None
|
|
185
184
|
|
|
185
|
+
# Compute depth coordinates directly instead of using .ds_depth_coords.
|
|
186
|
+
# Many cases below require only a 1D or 2D slice, making direct computation
|
|
187
|
+
# more efficient than triggering a full 3D depth computation just to extract a subset.
|
|
188
|
+
# This is especially beneficial when using Dask or if .ds_depth_coords is precomputed.
|
|
189
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
190
|
+
zeta = self.ds.zeta.isel(time=time)
|
|
191
|
+
else:
|
|
192
|
+
zeta = 0
|
|
186
193
|
if compute_layer_depth:
|
|
187
194
|
layer_depth = compute_depth_coordinates(
|
|
188
195
|
self.grid.ds,
|
|
189
|
-
|
|
196
|
+
zeta,
|
|
190
197
|
depth_type="layer",
|
|
191
198
|
location=loc,
|
|
192
199
|
eta=eta,
|
|
@@ -197,7 +204,7 @@ class ROMSOutput:
|
|
|
197
204
|
if compute_interface_depth:
|
|
198
205
|
interface_depth = compute_depth_coordinates(
|
|
199
206
|
self.grid.ds,
|
|
200
|
-
|
|
207
|
+
zeta,
|
|
201
208
|
depth_type="interface",
|
|
202
209
|
location=loc,
|
|
203
210
|
eta=eta,
|
|
@@ -252,6 +259,32 @@ class ROMSOutput:
|
|
|
252
259
|
if compute_layer_depth:
|
|
253
260
|
field = field.assign_coords({"layer_depth": layer_depth})
|
|
254
261
|
|
|
262
|
+
if not include_boundary:
|
|
263
|
+
slice_dict = None
|
|
264
|
+
|
|
265
|
+
if eta is None and xi is None:
|
|
266
|
+
slice_dict = {
|
|
267
|
+
"rho": {"eta_rho": slice(1, -1), "xi_rho": slice(1, -1)},
|
|
268
|
+
"u": {"eta_rho": slice(1, -1), "xi_u": slice(1, -1)},
|
|
269
|
+
"v": {"eta_v": slice(1, -1), "xi_rho": slice(1, -1)},
|
|
270
|
+
}
|
|
271
|
+
elif eta is None:
|
|
272
|
+
slice_dict = {
|
|
273
|
+
"rho": {"eta_rho": slice(1, -1)},
|
|
274
|
+
"u": {"eta_rho": slice(1, -1)},
|
|
275
|
+
"v": {"eta_v": slice(1, -1)},
|
|
276
|
+
}
|
|
277
|
+
elif xi is None:
|
|
278
|
+
slice_dict = {
|
|
279
|
+
"rho": {"xi_rho": slice(1, -1)},
|
|
280
|
+
"u": {"xi_u": slice(1, -1)},
|
|
281
|
+
"v": {"xi_rho": slice(1, -1)},
|
|
282
|
+
}
|
|
283
|
+
if slice_dict is not None:
|
|
284
|
+
if loc in slice_dict:
|
|
285
|
+
field = field.isel(**slice_dict[loc])
|
|
286
|
+
mask = mask.isel(**slice_dict[loc])
|
|
287
|
+
|
|
255
288
|
# Choose colorbar
|
|
256
289
|
if var_name in ["u", "v", "w", "ubar", "vbar", "zeta"]:
|
|
257
290
|
vmax = max(field.where(mask).max().values, -field.where(mask).min().values)
|
|
@@ -300,8 +333,8 @@ class ROMSOutput:
|
|
|
300
333
|
else:
|
|
301
334
|
_line_plot(field.where(mask), title=title, ax=ax)
|
|
302
335
|
|
|
303
|
-
def
|
|
304
|
-
"""
|
|
336
|
+
def _get_depth_coordinates(self, depth_type="layer", locations=["rho"]):
|
|
337
|
+
"""Ensure depth coordinates are stored for a given location and depth type.
|
|
305
338
|
|
|
306
339
|
Calculates vertical depth coordinates (layer or interface) for specified locations (e.g., rho, u, v points)
|
|
307
340
|
and updates them in the dataset (`self.ds`).
|
|
@@ -321,61 +354,44 @@ class ROMSOutput:
|
|
|
321
354
|
|
|
322
355
|
Updates
|
|
323
356
|
-------
|
|
324
|
-
self.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
357
|
+
self.ds_depth_coords : xarray.Dataset
|
|
358
|
+
|
|
359
|
+
Raises
|
|
360
|
+
------
|
|
361
|
+
ValueError
|
|
362
|
+
If `adjust_depth_for_sea_surface_height` is enabled but `zeta` is missing from `self.ds`.
|
|
329
363
|
|
|
330
364
|
Notes
|
|
331
365
|
-----
|
|
332
|
-
This method
|
|
366
|
+
- This method relies on the `compute_depth_coordinates` function to perform calculations.
|
|
367
|
+
- If `adjust_depth_for_sea_surface_height` is `True`, the method accounts for variations
|
|
368
|
+
in sea surface height (`zeta`).
|
|
333
369
|
"""
|
|
334
370
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def _load_model_output(self) -> xr.Dataset:
|
|
341
|
-
"""Load the model output based on the type."""
|
|
342
|
-
if isinstance(self.path, list):
|
|
343
|
-
filetype = "list"
|
|
344
|
-
force_combine_nested = True
|
|
345
|
-
# Check if all items in the list are files
|
|
346
|
-
if not all(Path(item).is_file() for item in self.path):
|
|
347
|
-
raise FileNotFoundError(
|
|
348
|
-
"All items in the provided list must be valid files."
|
|
371
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
372
|
+
if "zeta" not in self.ds:
|
|
373
|
+
raise ValueError(
|
|
374
|
+
"`zeta` is required in provided ROMS output when `adjust_depth_for_sea_surface_height` is enabled."
|
|
349
375
|
)
|
|
350
|
-
|
|
351
|
-
filetype = "file"
|
|
352
|
-
force_combine_nested = False
|
|
353
|
-
elif Path(self.path).is_dir():
|
|
354
|
-
filetype = "dir"
|
|
355
|
-
force_combine_nested = True
|
|
376
|
+
zeta = self.ds.zeta
|
|
356
377
|
else:
|
|
357
|
-
|
|
358
|
-
f"The specified path '{self.path}' is neither a file, nor a list of files, nor a directory."
|
|
359
|
-
)
|
|
378
|
+
zeta = 0
|
|
360
379
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
filename = _validate_and_set_filenames(self.path, filetype, "his")
|
|
369
|
-
else:
|
|
370
|
-
raise ValueError(f"Unsupported type '{self.type}'.")
|
|
380
|
+
for location in locations:
|
|
381
|
+
self.ds_depth_coords[
|
|
382
|
+
f"{depth_type}_depth_{location}"
|
|
383
|
+
] = compute_depth_coordinates(self.grid.ds, zeta, depth_type, location)
|
|
384
|
+
|
|
385
|
+
def _load_model_output(self) -> xr.Dataset:
|
|
386
|
+
"""Load the model output."""
|
|
371
387
|
|
|
372
388
|
# Load the dataset
|
|
373
389
|
ds = _load_data(
|
|
374
|
-
|
|
390
|
+
self.path,
|
|
375
391
|
dim_names={"time": "time"},
|
|
376
392
|
use_dask=self.use_dask,
|
|
377
|
-
time_chunking=
|
|
378
|
-
force_combine_nested=
|
|
393
|
+
time_chunking=True,
|
|
394
|
+
force_combine_nested=True,
|
|
379
395
|
)
|
|
380
396
|
|
|
381
397
|
return ds
|
|
@@ -551,40 +567,3 @@ class ROMSOutput:
|
|
|
551
567
|
)
|
|
552
568
|
|
|
553
569
|
return ds
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
def _validate_and_set_filenames(
|
|
557
|
-
filenames: Union[str, list], filetype: str, string: str
|
|
558
|
-
) -> Union[str, list]:
|
|
559
|
-
"""Validates and adjusts the filename or list of filenames based on the specified
|
|
560
|
-
type and checks for the presence of a string in the filename.
|
|
561
|
-
|
|
562
|
-
Parameters
|
|
563
|
-
----------
|
|
564
|
-
filenames : Union[str, list]
|
|
565
|
-
A single filename (str), a list of filenames, or a directory path.
|
|
566
|
-
filetype : str
|
|
567
|
-
The type of input: 'file' for a single file, 'list' for a list of files, or 'dir' for a directory.
|
|
568
|
-
string : str
|
|
569
|
-
The string that should be present in each filename.
|
|
570
|
-
|
|
571
|
-
Returns
|
|
572
|
-
-------
|
|
573
|
-
Union[str, list]
|
|
574
|
-
The validated filename(s). If a directory is provided, the function returns the adjusted file pattern.
|
|
575
|
-
"""
|
|
576
|
-
if filetype == "file":
|
|
577
|
-
if string not in os.path.basename(filenames):
|
|
578
|
-
logging.warning(
|
|
579
|
-
f"The file '{filenames}' does not appear to contain '{string}' in the name."
|
|
580
|
-
)
|
|
581
|
-
elif filetype == "list":
|
|
582
|
-
for file in filenames:
|
|
583
|
-
if string not in os.path.basename(file):
|
|
584
|
-
logging.warning(
|
|
585
|
-
f"The file '{file}' does not appear to contain '{string}' in the name."
|
|
586
|
-
)
|
|
587
|
-
elif filetype == "dir":
|
|
588
|
-
filenames = os.path.join(filenames, f"*{string}.*.nc")
|
|
589
|
-
|
|
590
|
-
return filenames
|
roms_tools/plot.py
CHANGED
|
@@ -19,8 +19,8 @@ def _plot(
|
|
|
19
19
|
|
|
20
20
|
Parameters
|
|
21
21
|
----------
|
|
22
|
-
field : xarray.DataArray
|
|
23
|
-
The field to plot.
|
|
22
|
+
field : xarray.DataArray
|
|
23
|
+
The field to plot.
|
|
24
24
|
depth_contours : bool, optional
|
|
25
25
|
If True, adds depth contours to the plot.
|
|
26
26
|
c : str, optional
|
|
@@ -284,6 +284,7 @@ def _section_plot(field, interface_depth=None, title="", kwargs={}, ax=None):
|
|
|
284
284
|
)
|
|
285
285
|
|
|
286
286
|
ax.set_title(title)
|
|
287
|
+
ax.set_ylabel("Depth [m]")
|
|
287
288
|
|
|
288
289
|
|
|
289
290
|
def _profile_plot(field, title="", ax=None):
|
|
@@ -324,6 +325,7 @@ def _profile_plot(field, title="", ax=None):
|
|
|
324
325
|
kwargs = {"y": depth_label, "yincrease": False}
|
|
325
326
|
field.plot(**kwargs)
|
|
326
327
|
ax.set_title(title)
|
|
328
|
+
ax.set_ylabel("Depth [m]")
|
|
327
329
|
ax.grid()
|
|
328
330
|
|
|
329
331
|
|
|
@@ -3,7 +3,7 @@ import numpy as np
|
|
|
3
3
|
from scipy.ndimage import label
|
|
4
4
|
import logging
|
|
5
5
|
import importlib.metadata
|
|
6
|
-
from typing import Dict, Union, List
|
|
6
|
+
from typing import Dict, Union, List, Optional
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
import matplotlib.pyplot as plt
|
|
@@ -43,10 +43,14 @@ class BoundaryForcing:
|
|
|
43
43
|
----------
|
|
44
44
|
grid : Grid
|
|
45
45
|
Object representing the grid information.
|
|
46
|
-
start_time : datetime
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
start_time : datetime, optional
|
|
47
|
+
The start time of the desired surface forcing data. This time is used to filter the dataset
|
|
48
|
+
to include only records on or after this time, with a single record at or before this time.
|
|
49
|
+
If no time filtering is desired, set it to None. Default is None.
|
|
50
|
+
end_time : datetime, optional
|
|
51
|
+
The end time of the desired surface forcing data. This time is used to filter the dataset
|
|
52
|
+
to include only records on or before this time, with a single record at or after this time.
|
|
53
|
+
If no time filtering is desired, set it to None. Default is None.
|
|
50
54
|
boundaries : Dict[str, bool], optional
|
|
51
55
|
Dictionary specifying which boundaries are forced (south, east, north, west). Default is all True.
|
|
52
56
|
source : Dict[str, Union[str, Path, List[Union[str, Path]]], bool]
|
|
@@ -96,8 +100,8 @@ class BoundaryForcing:
|
|
|
96
100
|
"""
|
|
97
101
|
|
|
98
102
|
grid: Grid
|
|
99
|
-
start_time: datetime
|
|
100
|
-
end_time: datetime
|
|
103
|
+
start_time: Optional[datetime] = None
|
|
104
|
+
end_time: Optional[datetime] = None
|
|
101
105
|
boundaries: Dict[str, bool] = field(
|
|
102
106
|
default_factory=lambda: {
|
|
103
107
|
"south": True,
|
|
@@ -127,10 +131,13 @@ class BoundaryForcing:
|
|
|
127
131
|
data = self._get_data()
|
|
128
132
|
|
|
129
133
|
if self.apply_2d_horizontal_fill:
|
|
134
|
+
|
|
130
135
|
data.choose_subdomain(
|
|
131
136
|
target_coords,
|
|
132
137
|
buffer_points=20, # lateral fill needs good buffer from data margin
|
|
133
138
|
)
|
|
139
|
+
# Enforce double precision to ensure reproducibility
|
|
140
|
+
data.convert_to_float64()
|
|
134
141
|
data.extrapolate_deepest_to_bottom()
|
|
135
142
|
data.apply_lateral_fill()
|
|
136
143
|
|
|
@@ -158,6 +165,8 @@ class BoundaryForcing:
|
|
|
158
165
|
)
|
|
159
166
|
|
|
160
167
|
if not self.apply_2d_horizontal_fill:
|
|
168
|
+
# Enforce double precision to ensure reproducibility
|
|
169
|
+
bdry_data.convert_to_float64()
|
|
161
170
|
bdry_data.extrapolate_deepest_to_bottom()
|
|
162
171
|
|
|
163
172
|
processed_fields = {}
|
|
@@ -329,6 +338,18 @@ class BoundaryForcing:
|
|
|
329
338
|
object.__setattr__(self, "ds", ds)
|
|
330
339
|
|
|
331
340
|
def _input_checks(self):
|
|
341
|
+
# Check that start_time and end_time are both None or none of them is
|
|
342
|
+
if (self.start_time is None) != (self.end_time is None):
|
|
343
|
+
raise ValueError(
|
|
344
|
+
"Both `start_time` and `end_time` must be provided together as datetime objects or both should be None."
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# Trigger a warning if both are None
|
|
348
|
+
if self.start_time is None and self.end_time is None:
|
|
349
|
+
logging.warning(
|
|
350
|
+
"Both `start_time` and `end_time` are None. No time filtering will be applied to the source data."
|
|
351
|
+
)
|
|
352
|
+
|
|
332
353
|
# Validate the 'type' parameter
|
|
333
354
|
if self.type not in ["physics", "bgc"]:
|
|
334
355
|
raise ValueError("`type` must be either 'physics' or 'bgc'.")
|
|
@@ -967,7 +988,6 @@ class BoundaryForcing:
|
|
|
967
988
|
cls,
|
|
968
989
|
filepath: Union[str, Path],
|
|
969
990
|
use_dask: bool = False,
|
|
970
|
-
bypass_validation: bool = False,
|
|
971
991
|
) -> "BoundaryForcing":
|
|
972
992
|
"""Create an instance of the BoundaryForcing class from a YAML file.
|
|
973
993
|
|
|
@@ -977,10 +997,6 @@ class BoundaryForcing:
|
|
|
977
997
|
The path to the YAML file from which the parameters will be read.
|
|
978
998
|
use_dask: bool, optional
|
|
979
999
|
Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
|
|
980
|
-
bypass_validation: bool, optional
|
|
981
|
-
Indicates whether to skip validation checks in the processed data. When set to True,
|
|
982
|
-
the validation process that ensures no NaN values exist at wet points
|
|
983
|
-
in the processed dataset is bypassed. Defaults to False.
|
|
984
1000
|
|
|
985
1001
|
Returns
|
|
986
1002
|
-------
|
|
@@ -993,9 +1009,7 @@ class BoundaryForcing:
|
|
|
993
1009
|
params = _from_yaml(cls, filepath)
|
|
994
1010
|
|
|
995
1011
|
# Create and return an instance of InitialConditions
|
|
996
|
-
return cls(
|
|
997
|
-
grid=grid, **params, use_dask=use_dask, bypass_validation=bypass_validation
|
|
998
|
-
)
|
|
1012
|
+
return cls(grid=grid, **params, use_dask=use_dask)
|
|
999
1013
|
|
|
1000
1014
|
|
|
1001
1015
|
def apply_1d_horizontal_fill(data_array: xr.DataArray) -> xr.DataArray:
|
roms_tools/setup/datasets.py
CHANGED
|
@@ -35,10 +35,11 @@ class Dataset:
|
|
|
35
35
|
The path to the data file(s). Can be a single string (with or without wildcards), a single Path object,
|
|
36
36
|
or a list of strings or Path objects containing multiple files.
|
|
37
37
|
start_time : Optional[datetime], optional
|
|
38
|
-
|
|
38
|
+
Start time for selecting relevant data. If not provided, no time-based filtering is applied.
|
|
39
39
|
end_time : Optional[datetime], optional
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
End time for selecting relevant data. If not provided, the dataset selects the time entry
|
|
41
|
+
closest to `start_time` within the range `[start_time, start_time + 24 hours]`.
|
|
42
|
+
If `start_time` is also not provided, no time-based filtering is applied.
|
|
42
43
|
dim_names: Dict[str, str], optional
|
|
43
44
|
Dictionary specifying the names of dimensions in the dataset.
|
|
44
45
|
var_names: Dict[str, str]
|
|
@@ -510,6 +511,25 @@ class Dataset:
|
|
|
510
511
|
"""
|
|
511
512
|
pass
|
|
512
513
|
|
|
514
|
+
def convert_to_float64(self) -> None:
|
|
515
|
+
"""Convert all data variables in the dataset to float64.
|
|
516
|
+
|
|
517
|
+
This method updates the dataset by converting all of its data variables to the
|
|
518
|
+
`float64` data type, ensuring consistency for numerical operations that require
|
|
519
|
+
high precision. The dataset is modified in place.
|
|
520
|
+
|
|
521
|
+
Parameters
|
|
522
|
+
----------
|
|
523
|
+
None
|
|
524
|
+
|
|
525
|
+
Returns
|
|
526
|
+
-------
|
|
527
|
+
None
|
|
528
|
+
This method modifies the dataset in place and does not return anything.
|
|
529
|
+
"""
|
|
530
|
+
ds = self.ds.astype({var: "float64" for var in self.ds.data_vars})
|
|
531
|
+
object.__setattr__(self, "ds", ds)
|
|
532
|
+
|
|
513
533
|
def choose_subdomain(
|
|
514
534
|
self,
|
|
515
535
|
target_coords,
|
|
@@ -1843,15 +1863,10 @@ class RiverDataset:
|
|
|
1843
1863
|
|
|
1844
1864
|
Returns
|
|
1845
1865
|
-------
|
|
1846
|
-
indices : dict
|
|
1866
|
+
indices : dict[str, list[tuple]]
|
|
1847
1867
|
A dictionary containing the indices of the rivers that are within the threshold distance from
|
|
1848
|
-
the target coordinates. The dictionary keys
|
|
1849
|
-
|
|
1850
|
-
The indices of the rivers that satisfy the distance threshold.
|
|
1851
|
-
- "eta_rho" : numpy.ndarray
|
|
1852
|
-
The indices of the `eta_rho` dimension corresponding to the selected stations.
|
|
1853
|
-
- "xi_rho" : numpy.ndarray
|
|
1854
|
-
The indices of the `xi_rho` dimension corresponding to the selected stations.
|
|
1868
|
+
the target coordinates. The dictionary structure consists of river names as keys, and each value is a list of tuples. Each tuple represents
|
|
1869
|
+
a pair of indices corresponding to the `eta_rho` and `xi_rho` grid coordinates of the river.
|
|
1855
1870
|
"""
|
|
1856
1871
|
|
|
1857
1872
|
# Retrieve longitude and latitude of river mouths
|
|
@@ -1878,30 +1893,75 @@ class RiverDataset:
|
|
|
1878
1893
|
|
|
1879
1894
|
# Find the indices of the closest grid cell to the river mouth
|
|
1880
1895
|
indices = np.where(dist == dist_min)
|
|
1896
|
+
stations = indices[0]
|
|
1897
|
+
eta_rho_values = indices[1]
|
|
1898
|
+
xi_rho_values = indices[2]
|
|
1881
1899
|
names = (
|
|
1882
|
-
|
|
1883
|
-
.isel({self.dim_names["station"]:
|
|
1900
|
+
ds[self.var_names["name"]]
|
|
1901
|
+
.isel({self.dim_names["station"]: stations})
|
|
1884
1902
|
.values
|
|
1885
1903
|
)
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
}
|
|
1904
|
+
river_indices = {}
|
|
1905
|
+
for i in range(len(stations)):
|
|
1906
|
+
river_name = names[i]
|
|
1907
|
+
river_indices[river_name] = [
|
|
1908
|
+
(int(eta_rho_values[i]), int(xi_rho_values[i]))
|
|
1909
|
+
] # list of tuples
|
|
1893
1910
|
else:
|
|
1894
1911
|
ds = xr.Dataset()
|
|
1895
|
-
|
|
1896
|
-
"station": [],
|
|
1897
|
-
"eta_rho": [],
|
|
1898
|
-
"xi_rho": [],
|
|
1899
|
-
"name": [],
|
|
1900
|
-
}
|
|
1912
|
+
river_indices = {}
|
|
1901
1913
|
|
|
1902
1914
|
object.__setattr__(self, "ds", ds)
|
|
1903
1915
|
|
|
1904
|
-
return
|
|
1916
|
+
return river_indices
|
|
1917
|
+
|
|
1918
|
+
def extract_named_rivers(self, indices):
|
|
1919
|
+
"""Extracts a subset of the dataset based on the provided river names in the
|
|
1920
|
+
indices dictionary.
|
|
1921
|
+
|
|
1922
|
+
This method filters the dataset to include only the rivers specified in the `indices` dictionary.
|
|
1923
|
+
The resulting subset is stored in the `ds` attribute of the class.
|
|
1924
|
+
|
|
1925
|
+
Parameters
|
|
1926
|
+
----------
|
|
1927
|
+
indices : dict
|
|
1928
|
+
A dictionary where the keys are river names (strings) and the values are dictionaries
|
|
1929
|
+
containing river-related data (e.g., river indices, coordinates).
|
|
1930
|
+
|
|
1931
|
+
Returns
|
|
1932
|
+
-------
|
|
1933
|
+
None
|
|
1934
|
+
The method modifies the `self.ds` attribute in place, setting it to the filtered dataset
|
|
1935
|
+
containing only the data related to the specified rivers.
|
|
1936
|
+
|
|
1937
|
+
Raises
|
|
1938
|
+
------
|
|
1939
|
+
ValueError
|
|
1940
|
+
- If `indices` is not a dictionary.
|
|
1941
|
+
- If any of the requested river names are not found in the dataset.
|
|
1942
|
+
"""
|
|
1943
|
+
|
|
1944
|
+
if not isinstance(indices, dict):
|
|
1945
|
+
raise ValueError("`indices` must be a dictionary.")
|
|
1946
|
+
|
|
1947
|
+
river_names = list(indices.keys())
|
|
1948
|
+
|
|
1949
|
+
# Ensure the dataset is filtered based on the provided river names
|
|
1950
|
+
ds_filtered = self.ds.where(
|
|
1951
|
+
self.ds[self.var_names["name"]].isin(river_names), drop=True
|
|
1952
|
+
)
|
|
1953
|
+
|
|
1954
|
+
# Check that all requested rivers exist in the dataset
|
|
1955
|
+
filtered_river_names = set(ds_filtered[self.var_names["name"]].values)
|
|
1956
|
+
missing_rivers = set(river_names) - filtered_river_names
|
|
1957
|
+
|
|
1958
|
+
if missing_rivers:
|
|
1959
|
+
raise ValueError(
|
|
1960
|
+
f"The following rivers were not found in the dataset: {missing_rivers}"
|
|
1961
|
+
)
|
|
1962
|
+
|
|
1963
|
+
# Set the filtered dataset as the new `ds`
|
|
1964
|
+
object.__setattr__(self, "ds", ds_filtered)
|
|
1905
1965
|
|
|
1906
1966
|
|
|
1907
1967
|
@dataclass(frozen=True, kw_only=True)
|
|
@@ -2050,7 +2110,7 @@ def _check_dataset(
|
|
|
2050
2110
|
|
|
2051
2111
|
|
|
2052
2112
|
def _select_relevant_times(
|
|
2053
|
-
ds, time_dim, start_time
|
|
2113
|
+
ds, time_dim, start_time, end_time=None, climatology=False
|
|
2054
2114
|
) -> xr.Dataset:
|
|
2055
2115
|
"""Select a subset of the dataset based on the specified time range.
|
|
2056
2116
|
|
|
@@ -2067,11 +2127,10 @@ def _select_relevant_times(
|
|
|
2067
2127
|
The input dataset to be filtered. Must contain a time dimension.
|
|
2068
2128
|
time_dim: str
|
|
2069
2129
|
Name of time dimension.
|
|
2070
|
-
start_time :
|
|
2071
|
-
The start time for selecting relevant data.
|
|
2130
|
+
start_time : datetime
|
|
2131
|
+
The start time for selecting relevant data.
|
|
2072
2132
|
end_time : Optional[datetime], optional
|
|
2073
|
-
The end time for selecting relevant data. If not provided, only data at the start_time is selected if start_time is provided
|
|
2074
|
-
or no filtering is applied if start_time is not provided.
|
|
2133
|
+
The end time for selecting relevant data. If not provided, only data at the start_time is selected if start_time is provided.
|
|
2075
2134
|
climatology : bool
|
|
2076
2135
|
Indicates whether the dataset is climatological. Defaults to False.
|
|
2077
2136
|
|
roms_tools/setup/grid.py
CHANGED
|
@@ -18,6 +18,7 @@ from roms_tools.setup.utils import (
|
|
|
18
18
|
interpolate_from_rho_to_v,
|
|
19
19
|
get_target_coords,
|
|
20
20
|
gc_dist,
|
|
21
|
+
_pop_grid_data,
|
|
21
22
|
)
|
|
22
23
|
from roms_tools.setup.utils import extract_single_value
|
|
23
24
|
from pathlib import Path
|
|
@@ -112,7 +113,7 @@ class Grid:
|
|
|
112
113
|
# Coarsen the dataset if needed
|
|
113
114
|
self._coarsen()
|
|
114
115
|
|
|
115
|
-
# Topography
|
|
116
|
+
# Topography
|
|
116
117
|
self.update_topography(
|
|
117
118
|
topography_source=self.topography_source,
|
|
118
119
|
hmin=self.hmin,
|
|
@@ -205,7 +206,7 @@ class Grid:
|
|
|
205
206
|
f"=== Generating the topography using {topography_source['name']} data and hmin = {hmin} meters ==="
|
|
206
207
|
)
|
|
207
208
|
|
|
208
|
-
# Add topography
|
|
209
|
+
# Add topography to the dataset
|
|
209
210
|
ds = _add_topography(
|
|
210
211
|
ds=self.ds,
|
|
211
212
|
target_coords=target_coords,
|
|
@@ -688,9 +689,7 @@ class Grid:
|
|
|
688
689
|
filepath = Path(filepath)
|
|
689
690
|
|
|
690
691
|
data = asdict(self)
|
|
691
|
-
data
|
|
692
|
-
data.pop("straddle", None)
|
|
693
|
-
data.pop("verbose", None)
|
|
692
|
+
data = _pop_grid_data(data)
|
|
694
693
|
|
|
695
694
|
# Include the version of roms-tools
|
|
696
695
|
try:
|
|
@@ -150,6 +150,8 @@ class InitialConditions:
|
|
|
150
150
|
target_coords,
|
|
151
151
|
buffer_points=20, # lateral fill needs good buffer from data margin
|
|
152
152
|
)
|
|
153
|
+
# Enforce double precision to ensure reproducibility
|
|
154
|
+
data.convert_to_float64()
|
|
153
155
|
data.extrapolate_deepest_to_bottom()
|
|
154
156
|
data.apply_lateral_fill()
|
|
155
157
|
|
|
@@ -234,6 +236,11 @@ class InitialConditions:
|
|
|
234
236
|
return processed_fields
|
|
235
237
|
|
|
236
238
|
def _input_checks(self):
|
|
239
|
+
# Check that ini_time is not None
|
|
240
|
+
if self.ini_time is None:
|
|
241
|
+
raise ValueError(
|
|
242
|
+
"`ini_time` must be a valid datetime object and cannot be None."
|
|
243
|
+
)
|
|
237
244
|
|
|
238
245
|
if "name" not in self.source.keys():
|
|
239
246
|
raise ValueError("`source` must include a 'name'.")
|
|
@@ -898,7 +905,6 @@ class InitialConditions:
|
|
|
898
905
|
cls,
|
|
899
906
|
filepath: Union[str, Path],
|
|
900
907
|
use_dask: bool = False,
|
|
901
|
-
bypass_validation: bool = False,
|
|
902
908
|
) -> "InitialConditions":
|
|
903
909
|
"""Create an instance of the InitialConditions class from a YAML file.
|
|
904
910
|
|
|
@@ -908,10 +914,6 @@ class InitialConditions:
|
|
|
908
914
|
The path to the YAML file from which the parameters will be read.
|
|
909
915
|
use_dask: bool, optional
|
|
910
916
|
Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
|
|
911
|
-
bypass_validation: bool, optional
|
|
912
|
-
Indicates whether to skip validation checks in the processed data. When set to True,
|
|
913
|
-
the validation process that ensures no NaN values exist at wet points
|
|
914
|
-
in the processed dataset is bypassed. Defaults to False.
|
|
915
917
|
|
|
916
918
|
Returns
|
|
917
919
|
-------
|
|
@@ -926,5 +928,4 @@ class InitialConditions:
|
|
|
926
928
|
grid=grid,
|
|
927
929
|
**initial_conditions_params,
|
|
928
930
|
use_dask=use_dask,
|
|
929
|
-
bypass_validation=bypass_validation,
|
|
930
931
|
)
|