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
|
@@ -5,22 +5,26 @@ from datetime import datetime
|
|
|
5
5
|
import textwrap
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
import pytest
|
|
8
|
+
import logging
|
|
8
9
|
from conftest import calculate_file_hash
|
|
9
|
-
from roms_tools.download import download_river_data
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@pytest.fixture
|
|
13
|
-
def
|
|
14
|
-
|
|
15
|
-
grid = Grid(
|
|
13
|
+
def iceland_test_grid():
|
|
14
|
+
return Grid(
|
|
16
15
|
nx=18, ny=18, size_x=800, size_y=800, center_lon=-18, center_lat=65, rot=20, N=3
|
|
17
16
|
)
|
|
18
17
|
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def river_forcing_climatology(iceland_test_grid):
|
|
21
|
+
"""Fixture for creating a RiverForcing object from the global Dai river dataset."""
|
|
22
|
+
|
|
19
23
|
start_time = datetime(1998, 1, 1)
|
|
20
24
|
end_time = datetime(1998, 3, 1)
|
|
21
25
|
|
|
22
26
|
return RiverForcing(
|
|
23
|
-
grid=
|
|
27
|
+
grid=iceland_test_grid,
|
|
24
28
|
start_time=start_time,
|
|
25
29
|
end_time=end_time,
|
|
26
30
|
convert_to_climatology="always",
|
|
@@ -52,325 +56,639 @@ def river_forcing_for_grid_that_straddles_dateline():
|
|
|
52
56
|
)
|
|
53
57
|
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
@pytest.fixture
|
|
60
|
+
def single_cell_indices():
|
|
61
|
+
# These are the indices that the `river_forcing` fixture generates automatically.
|
|
62
|
+
return {
|
|
63
|
+
"Hvita(Olfusa)": [(8, 6)],
|
|
64
|
+
"Thjorsa": [(8, 6)],
|
|
65
|
+
"JkulsFjll": [(11, 12)],
|
|
66
|
+
"Lagarfljot": [(9, 13)],
|
|
67
|
+
"Bruara": [(8, 6)],
|
|
68
|
+
"Svarta": [(12, 9)],
|
|
69
|
+
}
|
|
57
70
|
|
|
58
|
-
|
|
59
|
-
|
|
71
|
+
|
|
72
|
+
@pytest.fixture
|
|
73
|
+
def multi_cell_indices():
|
|
74
|
+
# These are the indices that the `river_forcing` fixture generates automatically.
|
|
75
|
+
return {
|
|
76
|
+
"Hvita(Olfusa)": [(8, 6)],
|
|
77
|
+
"Thjorsa": [(8, 6)],
|
|
78
|
+
"JkulsFjll": [(11, 12)],
|
|
79
|
+
"Lagarfljot": [(9, 13), (10, 13)],
|
|
80
|
+
"Bruara": [(8, 6)],
|
|
81
|
+
"Svarta": [(12, 8), (12, 9), (12, 10)],
|
|
82
|
+
}
|
|
60
83
|
|
|
61
84
|
|
|
62
|
-
@pytest.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"river_forcing",
|
|
66
|
-
"river_forcing_for_grid_that_straddles_dateline",
|
|
67
|
-
"river_forcing_with_bgc",
|
|
68
|
-
],
|
|
69
|
-
)
|
|
70
|
-
def test_successful_initialization_with_climatological_dai_data(
|
|
71
|
-
river_forcing_fixture, request
|
|
85
|
+
@pytest.fixture
|
|
86
|
+
def river_forcing_with_prescribed_single_cell_indices(
|
|
87
|
+
single_cell_indices, iceland_test_grid
|
|
72
88
|
):
|
|
89
|
+
"""Fixture for creating a RiverForcing object based on the global Dai river dataset,
|
|
90
|
+
using manually specified single-cell river indices instead of relying on automatic
|
|
91
|
+
detection."""
|
|
73
92
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
assert "river_time" in river_forcing.ds
|
|
83
|
-
assert hasattr(
|
|
84
|
-
river_forcing.ds.river_time,
|
|
85
|
-
"cycle_length",
|
|
93
|
+
start_time = datetime(1998, 1, 1)
|
|
94
|
+
end_time = datetime(1998, 3, 1)
|
|
95
|
+
|
|
96
|
+
return RiverForcing(
|
|
97
|
+
grid=iceland_test_grid,
|
|
98
|
+
start_time=start_time,
|
|
99
|
+
end_time=end_time,
|
|
100
|
+
indices=single_cell_indices,
|
|
86
101
|
)
|
|
87
|
-
assert hasattr(river_forcing.ds, "climatology")
|
|
88
102
|
|
|
89
103
|
|
|
90
|
-
|
|
104
|
+
@pytest.fixture
|
|
105
|
+
def river_forcing_with_prescribed_multi_cell_indices(
|
|
106
|
+
multi_cell_indices, iceland_test_grid
|
|
107
|
+
):
|
|
108
|
+
"""Fixture for creating a RiverForcing object based on the global Dai river dataset,
|
|
109
|
+
using manually specified multi-cell river indices instead of relying on automatic
|
|
110
|
+
detection."""
|
|
111
|
+
|
|
112
|
+
start_time = datetime(1998, 1, 1)
|
|
113
|
+
end_time = datetime(1998, 3, 1)
|
|
91
114
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
assert not hasattr(
|
|
98
|
-
river_forcing_no_climatology.ds.river_time,
|
|
99
|
-
"cycle_length",
|
|
115
|
+
return RiverForcing(
|
|
116
|
+
grid=iceland_test_grid,
|
|
117
|
+
start_time=start_time,
|
|
118
|
+
end_time=end_time,
|
|
119
|
+
indices=multi_cell_indices,
|
|
100
120
|
)
|
|
101
|
-
assert not hasattr(river_forcing_no_climatology.ds, "climatology")
|
|
102
121
|
|
|
103
122
|
|
|
104
|
-
def
|
|
123
|
+
def compare_dictionaries(dict1, dict2):
|
|
124
|
+
assert dict1.keys() == dict2.keys()
|
|
105
125
|
|
|
106
|
-
|
|
126
|
+
for key in dict1.keys():
|
|
127
|
+
assert np.array_equal(dict1[key], dict2[key])
|
|
107
128
|
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
|
|
130
|
+
class TestRiverForcingGeneral:
|
|
131
|
+
@pytest.mark.parametrize(
|
|
132
|
+
"river_forcing_fixture",
|
|
133
|
+
[
|
|
134
|
+
"river_forcing",
|
|
135
|
+
"river_forcing_climatology",
|
|
136
|
+
"river_forcing_with_bgc",
|
|
137
|
+
"river_forcing_for_grid_that_straddles_dateline",
|
|
138
|
+
"river_forcing_with_prescribed_single_cell_indices",
|
|
139
|
+
"river_forcing_with_prescribed_multi_cell_indices",
|
|
140
|
+
],
|
|
110
141
|
)
|
|
111
|
-
|
|
112
|
-
river_forcing
|
|
142
|
+
def test_successful_initialization(self, river_forcing_fixture, request):
|
|
143
|
+
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
144
|
+
assert isinstance(river_forcing.ds, xr.Dataset)
|
|
145
|
+
assert len(river_forcing.ds.nriver) > 0
|
|
146
|
+
assert len(river_forcing.original_indices) > 0
|
|
147
|
+
assert len(river_forcing.indices) > 0
|
|
148
|
+
assert "river_volume" in river_forcing.ds
|
|
149
|
+
assert "river_tracer" in river_forcing.ds
|
|
150
|
+
assert "river_time" in river_forcing.ds
|
|
151
|
+
|
|
152
|
+
@pytest.mark.parametrize(
|
|
153
|
+
"river_forcing_fixture",
|
|
154
|
+
[
|
|
155
|
+
"river_forcing",
|
|
156
|
+
"river_forcing_climatology",
|
|
157
|
+
"river_forcing_with_bgc",
|
|
158
|
+
"river_forcing_with_prescribed_single_cell_indices",
|
|
159
|
+
"river_forcing_with_prescribed_multi_cell_indices",
|
|
160
|
+
],
|
|
113
161
|
)
|
|
162
|
+
def test_climatology_attributes(self, river_forcing_fixture, request):
|
|
163
|
+
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
164
|
+
assert river_forcing.climatology
|
|
165
|
+
assert hasattr(
|
|
166
|
+
river_forcing.ds.river_time,
|
|
167
|
+
"cycle_length",
|
|
168
|
+
)
|
|
169
|
+
assert hasattr(river_forcing.ds, "climatology")
|
|
114
170
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
171
|
+
def test_no_climatology_attributes(self, river_forcing_no_climatology, request):
|
|
172
|
+
assert not river_forcing_no_climatology.climatology
|
|
173
|
+
assert not hasattr(
|
|
174
|
+
river_forcing_no_climatology.ds.river_time,
|
|
175
|
+
"cycle_length",
|
|
176
|
+
)
|
|
177
|
+
assert not hasattr(river_forcing_no_climatology.ds, "climatology")
|
|
178
|
+
|
|
179
|
+
@pytest.mark.parametrize(
|
|
180
|
+
"river_forcing_fixture",
|
|
181
|
+
[
|
|
182
|
+
"river_forcing_climatology",
|
|
183
|
+
"river_forcing_no_climatology",
|
|
184
|
+
"river_forcing_with_bgc",
|
|
185
|
+
"river_forcing_with_prescribed_single_cell_indices",
|
|
186
|
+
"river_forcing_with_prescribed_multi_cell_indices",
|
|
187
|
+
],
|
|
123
188
|
)
|
|
189
|
+
def test_constant_tracers(self, river_forcing_fixture, request):
|
|
190
|
+
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
191
|
+
np.testing.assert_allclose(
|
|
192
|
+
river_forcing.ds.river_tracer.isel(ntracers=0).values, 17.0, atol=0
|
|
193
|
+
)
|
|
194
|
+
np.testing.assert_allclose(
|
|
195
|
+
river_forcing.ds.river_tracer.isel(ntracers=1).values, 1.0, atol=0
|
|
196
|
+
)
|
|
197
|
+
np.testing.assert_allclose(
|
|
198
|
+
river_forcing.ds.river_tracer.isel(ntracers=slice(2, None)).values,
|
|
199
|
+
0.0,
|
|
200
|
+
atol=0,
|
|
201
|
+
)
|
|
124
202
|
|
|
203
|
+
def test_reproducibility_same_grid(self, river_forcing):
|
|
125
204
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"river_forcing_with_bgc",
|
|
132
|
-
],
|
|
133
|
-
)
|
|
134
|
-
def test_constant_tracers(river_forcing_fixture, request):
|
|
135
|
-
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
205
|
+
the_same_river_forcing = RiverForcing(
|
|
206
|
+
grid=river_forcing.grid,
|
|
207
|
+
start_time=datetime(1998, 1, 1),
|
|
208
|
+
end_time=datetime(1998, 3, 1),
|
|
209
|
+
)
|
|
136
210
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
211
|
+
assert river_forcing == the_same_river_forcing
|
|
212
|
+
|
|
213
|
+
@pytest.mark.parametrize(
|
|
214
|
+
"river_forcing_fixture",
|
|
215
|
+
[
|
|
216
|
+
"river_forcing_climatology",
|
|
217
|
+
"river_forcing_no_climatology",
|
|
218
|
+
"river_forcing_with_bgc",
|
|
219
|
+
"river_forcing_with_prescribed_single_cell_indices",
|
|
220
|
+
"river_forcing_with_prescribed_multi_cell_indices",
|
|
221
|
+
],
|
|
142
222
|
)
|
|
143
|
-
|
|
144
|
-
river_forcing.
|
|
223
|
+
def test_river_locations_are_along_coast(self, river_forcing_fixture, request):
|
|
224
|
+
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
225
|
+
|
|
226
|
+
mask = river_forcing.grid.ds.mask_rho
|
|
227
|
+
faces = (
|
|
228
|
+
mask.shift(eta_rho=1)
|
|
229
|
+
+ mask.shift(eta_rho=-1)
|
|
230
|
+
+ mask.shift(xi_rho=1)
|
|
231
|
+
+ mask.shift(xi_rho=-1)
|
|
232
|
+
)
|
|
233
|
+
coast = (1 - mask) * (faces > 0)
|
|
234
|
+
|
|
235
|
+
indices = river_forcing.indices
|
|
236
|
+
for name in indices.keys():
|
|
237
|
+
for (eta_rho, xi_rho) in indices[name]:
|
|
238
|
+
assert coast[eta_rho, xi_rho]
|
|
239
|
+
assert river_forcing.ds["river_location"][eta_rho, xi_rho] > 0
|
|
240
|
+
|
|
241
|
+
def test_missing_source_name(self, iceland_test_grid):
|
|
242
|
+
with pytest.raises(ValueError, match="`source` must include a 'name'."):
|
|
243
|
+
RiverForcing(
|
|
244
|
+
grid=iceland_test_grid,
|
|
245
|
+
start_time=datetime(1998, 1, 1),
|
|
246
|
+
end_time=datetime(1998, 3, 1),
|
|
247
|
+
source={"path": "river_data.nc"},
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def test_river_forcing_plot(self, river_forcing_with_bgc):
|
|
251
|
+
"""Test plot method."""
|
|
252
|
+
|
|
253
|
+
river_forcing_with_bgc.plot_locations()
|
|
254
|
+
river_forcing_with_bgc.plot("river_volume")
|
|
255
|
+
river_forcing_with_bgc.plot("river_temp")
|
|
256
|
+
river_forcing_with_bgc.plot("river_salt")
|
|
257
|
+
river_forcing_with_bgc.plot("river_ALK")
|
|
258
|
+
river_forcing_with_bgc.plot("river_PO4")
|
|
259
|
+
|
|
260
|
+
@pytest.mark.parametrize(
|
|
261
|
+
"river_forcing_fixture",
|
|
262
|
+
[
|
|
263
|
+
"river_forcing_with_bgc",
|
|
264
|
+
"river_forcing_with_prescribed_multi_cell_indices",
|
|
265
|
+
],
|
|
145
266
|
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
267
|
+
def test_river_forcing_save(self, river_forcing_fixture, tmp_path, request):
|
|
268
|
+
"""Test save method."""
|
|
269
|
+
|
|
270
|
+
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
271
|
+
for file_str in ["test_rivers", "test_rivers.nc"]:
|
|
272
|
+
# Create a temporary filepath using the tmp_path fixture
|
|
273
|
+
for filepath in [tmp_path / file_str, str(tmp_path / file_str)]:
|
|
274
|
+
|
|
275
|
+
saved_filenames = river_forcing.save(filepath)
|
|
276
|
+
# Check if the .nc file was created
|
|
277
|
+
filepath = Path(filepath).with_suffix(".nc")
|
|
278
|
+
assert saved_filenames == [filepath]
|
|
279
|
+
assert filepath.exists()
|
|
280
|
+
# Clean up the .nc file
|
|
281
|
+
filepath.unlink()
|
|
282
|
+
|
|
283
|
+
@pytest.mark.parametrize(
|
|
284
|
+
"river_forcing_fixture",
|
|
285
|
+
[
|
|
286
|
+
"river_forcing_climatology",
|
|
287
|
+
"river_forcing_no_climatology",
|
|
288
|
+
"river_forcing_with_bgc",
|
|
289
|
+
"river_forcing_with_prescribed_single_cell_indices",
|
|
290
|
+
"river_forcing_with_prescribed_multi_cell_indices",
|
|
291
|
+
],
|
|
165
292
|
)
|
|
166
|
-
|
|
293
|
+
def test_roundtrip_yaml(self, river_forcing_fixture, request, tmp_path, caplog):
|
|
294
|
+
"""Test that creating an RiverForcing object, saving its parameters to yaml
|
|
295
|
+
file, and re-opening yaml file creates the same object."""
|
|
167
296
|
|
|
168
|
-
|
|
169
|
-
for i in range(len(indices["station"])):
|
|
170
|
-
eta_rho = indices["eta_rho"][i]
|
|
171
|
-
xi_rho = indices["xi_rho"][i]
|
|
172
|
-
assert coast[eta_rho, xi_rho]
|
|
173
|
-
assert river_forcing.ds["river_location"][eta_rho, xi_rho] > 0
|
|
297
|
+
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
174
298
|
|
|
299
|
+
# Create a temporary filepath using the tmp_path fixture
|
|
300
|
+
file_str = "test_yaml"
|
|
301
|
+
for filepath in [
|
|
302
|
+
tmp_path / file_str,
|
|
303
|
+
str(tmp_path / file_str),
|
|
304
|
+
]: # test for Path object and str
|
|
175
305
|
|
|
176
|
-
|
|
306
|
+
river_forcing.to_yaml(filepath)
|
|
177
307
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
ny=2,
|
|
181
|
-
size_x=500,
|
|
182
|
-
size_y=1000,
|
|
183
|
-
center_lon=0,
|
|
184
|
-
center_lat=55,
|
|
185
|
-
rot=10,
|
|
186
|
-
N=3, # number of vertical levels
|
|
187
|
-
)
|
|
308
|
+
# Clear caplog before running the test
|
|
309
|
+
caplog.clear()
|
|
188
310
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
grid=grid,
|
|
192
|
-
start_time=datetime(1998, 1, 1),
|
|
193
|
-
end_time=datetime(1998, 3, 1),
|
|
194
|
-
source={"path": "river_data.nc"},
|
|
195
|
-
)
|
|
311
|
+
with caplog.at_level(logging.INFO):
|
|
312
|
+
river_forcing_from_file = RiverForcing.from_yaml(filepath)
|
|
196
313
|
|
|
314
|
+
assert "Use provided river indices." in caplog.text
|
|
315
|
+
assert river_forcing == river_forcing_from_file
|
|
197
316
|
|
|
198
|
-
|
|
317
|
+
filepath = Path(filepath)
|
|
318
|
+
filepath.unlink()
|
|
199
319
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
320
|
+
@pytest.mark.parametrize(
|
|
321
|
+
"river_forcing_fixture",
|
|
322
|
+
[
|
|
323
|
+
"river_forcing_climatology",
|
|
324
|
+
"river_forcing_no_climatology",
|
|
325
|
+
"river_forcing_with_bgc",
|
|
326
|
+
"river_forcing_with_prescribed_single_cell_indices",
|
|
327
|
+
"river_forcing_with_prescribed_multi_cell_indices",
|
|
328
|
+
],
|
|
203
329
|
)
|
|
204
|
-
|
|
205
|
-
ValueError,
|
|
206
|
-
match="No relevant rivers found.",
|
|
207
|
-
):
|
|
330
|
+
def test_files_have_same_hash(self, river_forcing_fixture, request, tmp_path):
|
|
208
331
|
|
|
209
|
-
|
|
210
|
-
grid=grid,
|
|
211
|
-
start_time=datetime(1998, 1, 1),
|
|
212
|
-
end_time=datetime(1998, 3, 1),
|
|
213
|
-
)
|
|
332
|
+
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
214
333
|
|
|
334
|
+
yaml_filepath = tmp_path / "test_yaml.yaml"
|
|
335
|
+
filepath1 = tmp_path / "test1.nc"
|
|
336
|
+
filepath2 = tmp_path / "test2.nc"
|
|
215
337
|
|
|
216
|
-
|
|
338
|
+
river_forcing.to_yaml(yaml_filepath)
|
|
339
|
+
river_forcing.save(filepath1)
|
|
340
|
+
rf_from_file = RiverForcing.from_yaml(yaml_filepath)
|
|
341
|
+
rf_from_file.save(filepath2)
|
|
217
342
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
start_time=datetime(1998, 1, 1),
|
|
221
|
-
end_time=datetime(1998, 3, 1),
|
|
222
|
-
)
|
|
343
|
+
hash1 = calculate_file_hash(filepath1)
|
|
344
|
+
hash2 = calculate_file_hash(filepath2)
|
|
223
345
|
|
|
224
|
-
|
|
346
|
+
assert hash1 == hash2, f"Hashes do not match: {hash1} != {hash2}"
|
|
225
347
|
|
|
348
|
+
yaml_filepath.unlink()
|
|
349
|
+
filepath1.unlink()
|
|
350
|
+
filepath2.unlink()
|
|
351
|
+
|
|
352
|
+
def test_from_yaml_missing_river_forcing(self, tmp_path):
|
|
353
|
+
yaml_content = textwrap.dedent(
|
|
354
|
+
"""\
|
|
355
|
+
---
|
|
356
|
+
roms_tools_version: 0.0.0
|
|
357
|
+
---
|
|
358
|
+
Grid:
|
|
359
|
+
nx: 100
|
|
360
|
+
ny: 100
|
|
361
|
+
size_x: 1800
|
|
362
|
+
size_y: 2400
|
|
363
|
+
center_lon: -10
|
|
364
|
+
center_lat: 61
|
|
365
|
+
rot: -20
|
|
366
|
+
topography_source:
|
|
367
|
+
name: ETOPO5
|
|
368
|
+
hmin: 5.0
|
|
369
|
+
"""
|
|
370
|
+
)
|
|
226
371
|
|
|
227
|
-
|
|
372
|
+
# Create a temporary filepath using the tmp_path fixture
|
|
373
|
+
file_str = "test_yaml"
|
|
374
|
+
for yaml_filepath in [
|
|
375
|
+
tmp_path / file_str,
|
|
376
|
+
str(tmp_path / file_str),
|
|
377
|
+
]: # test for Path object and str
|
|
378
|
+
|
|
379
|
+
# Write YAML content to file
|
|
380
|
+
if isinstance(yaml_filepath, Path):
|
|
381
|
+
yaml_filepath.write_text(yaml_content)
|
|
382
|
+
else:
|
|
383
|
+
with open(yaml_filepath, "w") as f:
|
|
384
|
+
f.write(yaml_content)
|
|
385
|
+
|
|
386
|
+
with pytest.raises(
|
|
387
|
+
ValueError,
|
|
388
|
+
match="No RiverForcing configuration found in the YAML file.",
|
|
389
|
+
):
|
|
390
|
+
RiverForcing.from_yaml(yaml_filepath)
|
|
391
|
+
|
|
392
|
+
yaml_filepath = Path(yaml_filepath)
|
|
393
|
+
yaml_filepath.unlink()
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class TestRiverForcingWithoutPrescribedIndices:
|
|
397
|
+
def test_logging_message(self, iceland_test_grid, caplog):
|
|
398
|
+
|
|
399
|
+
with caplog.at_level(logging.INFO):
|
|
400
|
+
RiverForcing(
|
|
401
|
+
grid=iceland_test_grid,
|
|
402
|
+
start_time=datetime(1998, 1, 1),
|
|
403
|
+
end_time=datetime(1998, 3, 1),
|
|
404
|
+
)
|
|
405
|
+
# Verify the info message in the log
|
|
406
|
+
assert "No river indices provided." in caplog.text
|
|
407
|
+
|
|
408
|
+
def test_reproducibility(self, river_forcing, river_forcing_climatology):
|
|
409
|
+
"""Verify that `river_forcing` and `river_forcing_climatology` produce identical
|
|
410
|
+
outputs.
|
|
411
|
+
|
|
412
|
+
`river_forcing` is initialized with `convert_to_climatology="if_any_missing"`, meaning
|
|
413
|
+
it fell back to climatology. This test ensures that the resulting datasets
|
|
414
|
+
and river index mappings are the same between the two cases.
|
|
415
|
+
"""
|
|
416
|
+
xr.testing.assert_allclose(river_forcing.ds, river_forcing_climatology.ds)
|
|
417
|
+
compare_dictionaries(
|
|
418
|
+
river_forcing.original_indices, river_forcing_climatology.original_indices
|
|
419
|
+
)
|
|
420
|
+
compare_dictionaries(river_forcing.indices, river_forcing_climatology.indices)
|
|
228
421
|
|
|
229
|
-
|
|
230
|
-
ds = xr.open_dataset(fname, decode_times=False)
|
|
231
|
-
# only keep the 300 biggest rivers, which will lower the total relevant river number
|
|
232
|
-
ds = ds.isel(station=slice(None, 300))
|
|
233
|
-
filepath = tmp_path / "test.nc"
|
|
234
|
-
ds.to_netcdf(filepath)
|
|
422
|
+
def test_no_rivers_found(self):
|
|
235
423
|
|
|
236
|
-
|
|
237
|
-
grid=
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
source={"name": "DAI", "path": filepath},
|
|
241
|
-
)
|
|
424
|
+
# Create a grid over open ocean
|
|
425
|
+
grid = Grid(
|
|
426
|
+
nx=2, ny=2, size_x=50, size_y=50, center_lon=0, center_lat=55, rot=10, N=3
|
|
427
|
+
)
|
|
242
428
|
|
|
243
|
-
|
|
429
|
+
with pytest.raises(ValueError, match="No relevant rivers found."):
|
|
430
|
+
RiverForcing(
|
|
431
|
+
grid=grid,
|
|
432
|
+
start_time=datetime(1998, 1, 1),
|
|
433
|
+
end_time=datetime(1998, 3, 1),
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class TestRiverForcingWithPrescribedIndices:
|
|
438
|
+
def test_logging_message(self, iceland_test_grid, single_cell_indices, caplog):
|
|
439
|
+
|
|
440
|
+
with caplog.at_level(logging.INFO):
|
|
441
|
+
RiverForcing(
|
|
442
|
+
grid=iceland_test_grid,
|
|
443
|
+
start_time=datetime(1998, 1, 1),
|
|
444
|
+
end_time=datetime(1998, 3, 1),
|
|
445
|
+
indices=single_cell_indices,
|
|
446
|
+
)
|
|
447
|
+
# Verify the info message in the log
|
|
448
|
+
assert "Use provided river indices." in caplog.text
|
|
449
|
+
|
|
450
|
+
@pytest.mark.parametrize(
|
|
451
|
+
"indices_fixture", ["single_cell_indices", "multi_cell_indices"]
|
|
452
|
+
)
|
|
453
|
+
def test_indices_stay_untouched(self, iceland_test_grid, indices_fixture, request):
|
|
454
|
+
indices = request.getfixturevalue(indices_fixture)
|
|
244
455
|
|
|
456
|
+
start_time = datetime(1998, 1, 1)
|
|
457
|
+
end_time = datetime(1998, 3, 1)
|
|
245
458
|
|
|
246
|
-
|
|
247
|
-
|
|
459
|
+
river_forcing = RiverForcing(
|
|
460
|
+
grid=iceland_test_grid,
|
|
461
|
+
start_time=start_time,
|
|
462
|
+
end_time=end_time,
|
|
463
|
+
indices=indices,
|
|
464
|
+
)
|
|
465
|
+
river_forcing.original_indices == indices
|
|
466
|
+
river_forcing.indices == indices
|
|
248
467
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
468
|
+
def test_fraction(
|
|
469
|
+
self,
|
|
470
|
+
river_forcing_with_prescribed_single_cell_indices,
|
|
471
|
+
river_forcing_with_prescribed_multi_cell_indices,
|
|
472
|
+
):
|
|
473
|
+
def list_non_zero_values(data_array):
|
|
474
|
+
non_zero_values = data_array.values
|
|
475
|
+
return non_zero_values[non_zero_values != 0].tolist()
|
|
476
|
+
|
|
477
|
+
# check that all values are integers for single cell rivers
|
|
478
|
+
non_zero_values = river_forcing_with_prescribed_single_cell_indices.ds[
|
|
479
|
+
"river_location"
|
|
480
|
+
]
|
|
481
|
+
is_integer = non_zero_values == np.floor(non_zero_values)
|
|
482
|
+
assert (is_integer).all()
|
|
483
|
+
|
|
484
|
+
# check that not all values are integers for multi cell rivers
|
|
485
|
+
non_zero_values = river_forcing_with_prescribed_multi_cell_indices.ds[
|
|
486
|
+
"river_location"
|
|
487
|
+
]
|
|
488
|
+
is_integer = non_zero_values == np.floor(non_zero_values)
|
|
489
|
+
assert not (is_integer).all()
|
|
490
|
+
|
|
491
|
+
def test_reproducibility(
|
|
492
|
+
self, river_forcing, river_forcing_with_prescribed_single_cell_indices
|
|
493
|
+
):
|
|
494
|
+
"""river_forcing_with_prescribed_single_cell_indices was created with the
|
|
495
|
+
indices that were automatically inferred for river_forcing.
|
|
496
|
+
|
|
497
|
+
Test that these two are identical.
|
|
498
|
+
"""
|
|
499
|
+
assert (
|
|
500
|
+
river_forcing.indices
|
|
501
|
+
== river_forcing_with_prescribed_single_cell_indices.indices
|
|
502
|
+
)
|
|
503
|
+
assert river_forcing.ds.identical(
|
|
504
|
+
river_forcing_with_prescribed_single_cell_indices.ds
|
|
505
|
+
)
|
|
506
|
+
assert river_forcing == river_forcing_with_prescribed_single_cell_indices
|
|
255
507
|
|
|
508
|
+
def test_reproducibility_with_flipped_dictionary_entries(
|
|
509
|
+
self, iceland_test_grid, tmp_path
|
|
510
|
+
):
|
|
511
|
+
indices = {
|
|
512
|
+
"Hvita(Olfusa)": [(8, 6)],
|
|
513
|
+
"Thjorsa": [(8, 6)],
|
|
514
|
+
"JkulsFjll": [(11, 12)],
|
|
515
|
+
"Lagarfljot": [(9, 13), (10, 13)],
|
|
516
|
+
"Bruara": [(8, 6)],
|
|
517
|
+
"Svarta": [(12, 8), (12, 9), (12, 10)],
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
flipped_indices = {
|
|
521
|
+
"Thjorsa": [(8, 6)],
|
|
522
|
+
"Hvita(Olfusa)": [(8, 6)],
|
|
523
|
+
"JkulsFjll": [(11, 12)],
|
|
524
|
+
"Svarta": [(12, 10), (12, 9), (12, 8)], # also flip order of tuples here
|
|
525
|
+
"Lagarfljot": [(9, 13), (10, 13)],
|
|
526
|
+
"Bruara": [(8, 6)],
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
start_time = datetime(1998, 1, 1)
|
|
530
|
+
end_time = datetime(1998, 3, 1)
|
|
531
|
+
|
|
532
|
+
river_forcing = RiverForcing(
|
|
533
|
+
grid=iceland_test_grid,
|
|
534
|
+
start_time=start_time,
|
|
535
|
+
end_time=end_time,
|
|
536
|
+
indices=indices,
|
|
537
|
+
)
|
|
256
538
|
|
|
257
|
-
|
|
258
|
-
|
|
539
|
+
river_forcing_from_flipped_indices = RiverForcing(
|
|
540
|
+
grid=iceland_test_grid,
|
|
541
|
+
start_time=start_time,
|
|
542
|
+
end_time=end_time,
|
|
543
|
+
indices=flipped_indices,
|
|
544
|
+
)
|
|
259
545
|
|
|
260
|
-
for file_str in ["test_rivers", "test_rivers.nc"]:
|
|
261
546
|
# Create a temporary filepath using the tmp_path fixture
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
saved_filenames = river_forcing_with_bgc.save(filepath)
|
|
265
|
-
# Check if the .nc file was created
|
|
266
|
-
filepath = Path(filepath).with_suffix(".nc")
|
|
267
|
-
assert saved_filenames == [filepath]
|
|
268
|
-
assert filepath.exists()
|
|
269
|
-
# Clean up the .nc file
|
|
270
|
-
filepath.unlink()
|
|
547
|
+
file1 = Path(tmp_path / "test1.nc")
|
|
548
|
+
file2 = Path(tmp_path / "test2.nc")
|
|
271
549
|
|
|
550
|
+
river_forcing.save(file1)
|
|
551
|
+
river_forcing_from_flipped_indices.save(file2)
|
|
272
552
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
"
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
)
|
|
281
|
-
def test_roundtrip_yaml(river_forcing_fixture, request, tmp_path):
|
|
282
|
-
"""Test that creating an RiverForcing object, saving its parameters to yaml file,
|
|
283
|
-
and re-opening yaml file creates the same object."""
|
|
284
|
-
|
|
285
|
-
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
286
|
-
|
|
287
|
-
# Create a temporary filepath using the tmp_path fixture
|
|
288
|
-
file_str = "test_yaml"
|
|
289
|
-
for filepath in [
|
|
290
|
-
tmp_path / file_str,
|
|
291
|
-
str(tmp_path / file_str),
|
|
292
|
-
]: # test for Path object and str
|
|
293
|
-
|
|
294
|
-
river_forcing.to_yaml(filepath)
|
|
295
|
-
|
|
296
|
-
river_forcing_from_file = RiverForcing.from_yaml(filepath)
|
|
297
|
-
|
|
298
|
-
assert river_forcing == river_forcing_from_file
|
|
299
|
-
|
|
300
|
-
filepath = Path(filepath)
|
|
301
|
-
filepath.unlink()
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
@pytest.mark.parametrize(
|
|
305
|
-
"river_forcing_fixture",
|
|
306
|
-
[
|
|
307
|
-
"river_forcing_climatology",
|
|
308
|
-
"river_forcing_no_climatology",
|
|
309
|
-
"river_forcing_with_bgc",
|
|
310
|
-
],
|
|
311
|
-
)
|
|
312
|
-
def test_files_have_same_hash(river_forcing_fixture, request, tmp_path):
|
|
313
|
-
|
|
314
|
-
river_forcing = request.getfixturevalue(river_forcing_fixture)
|
|
315
|
-
|
|
316
|
-
yaml_filepath = tmp_path / "test_yaml.yaml"
|
|
317
|
-
filepath1 = tmp_path / "test1.nc"
|
|
318
|
-
filepath2 = tmp_path / "test2.nc"
|
|
319
|
-
|
|
320
|
-
river_forcing.to_yaml(yaml_filepath)
|
|
321
|
-
river_forcing.save(filepath1)
|
|
322
|
-
rf_from_file = RiverForcing.from_yaml(yaml_filepath)
|
|
323
|
-
rf_from_file.save(filepath2)
|
|
324
|
-
|
|
325
|
-
hash1 = calculate_file_hash(filepath1)
|
|
326
|
-
hash2 = calculate_file_hash(filepath2)
|
|
327
|
-
|
|
328
|
-
assert hash1 == hash2, f"Hashes do not match: {hash1} != {hash2}"
|
|
329
|
-
|
|
330
|
-
yaml_filepath.unlink()
|
|
331
|
-
filepath1.unlink()
|
|
332
|
-
filepath2.unlink()
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
def test_from_yaml_missing_river_forcing(tmp_path):
|
|
336
|
-
yaml_content = textwrap.dedent(
|
|
337
|
-
"""\
|
|
338
|
-
---
|
|
339
|
-
roms_tools_version: 0.0.0
|
|
340
|
-
---
|
|
341
|
-
Grid:
|
|
342
|
-
nx: 100
|
|
343
|
-
ny: 100
|
|
344
|
-
size_x: 1800
|
|
345
|
-
size_y: 2400
|
|
346
|
-
center_lon: -10
|
|
347
|
-
center_lat: 61
|
|
348
|
-
rot: -20
|
|
349
|
-
topography_source:
|
|
350
|
-
name: ETOPO5
|
|
351
|
-
hmin: 5.0
|
|
352
|
-
"""
|
|
353
|
-
)
|
|
553
|
+
hash1 = calculate_file_hash(file1)
|
|
554
|
+
hash2 = calculate_file_hash(file2)
|
|
555
|
+
|
|
556
|
+
assert hash1 == hash2, f"Hashes do not match: {hash1} != {hash2}"
|
|
557
|
+
|
|
558
|
+
file1.unlink()
|
|
559
|
+
file2.unlink()
|
|
354
560
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
tmp_path / file_str,
|
|
359
|
-
str(tmp_path / file_str),
|
|
360
|
-
]: # test for Path object and str
|
|
561
|
+
def test_invalid_indices(self, iceland_test_grid):
|
|
562
|
+
invalid_single_cell_indices = {"Hvita(Olfusa)": [(0, 6)]}
|
|
563
|
+
invalid_multi_cell_indices = {"Hvita(Olfusa)": [(8, 6), (0, 6)]}
|
|
361
564
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
565
|
+
for indices in [invalid_single_cell_indices, invalid_multi_cell_indices]:
|
|
566
|
+
with pytest.raises(
|
|
567
|
+
ValueError, match="is not located on the coast at grid cell"
|
|
568
|
+
):
|
|
569
|
+
RiverForcing(
|
|
570
|
+
grid=iceland_test_grid,
|
|
571
|
+
start_time=datetime(1998, 1, 1),
|
|
572
|
+
end_time=datetime(1998, 3, 1),
|
|
573
|
+
indices=indices,
|
|
574
|
+
)
|
|
368
575
|
|
|
576
|
+
def test_raise_missing_rivers(self, iceland_test_grid):
|
|
577
|
+
fake_indices = {"Hvita(Olfusa)": [(8, 6)], "fake": [(11, 12)]}
|
|
578
|
+
|
|
579
|
+
with pytest.raises(
|
|
580
|
+
ValueError, match="The following rivers were not found in the dataset"
|
|
581
|
+
):
|
|
582
|
+
RiverForcing(
|
|
583
|
+
grid=iceland_test_grid,
|
|
584
|
+
start_time=datetime(1998, 1, 1),
|
|
585
|
+
end_time=datetime(1998, 3, 1),
|
|
586
|
+
indices=fake_indices,
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
def test_indices_is_dict(self, iceland_test_grid):
|
|
590
|
+
with pytest.raises(ValueError, match="`indices` must be a dictionary."):
|
|
591
|
+
RiverForcing(
|
|
592
|
+
grid=iceland_test_grid,
|
|
593
|
+
start_time=datetime(1998, 1, 1),
|
|
594
|
+
end_time=datetime(1998, 3, 1),
|
|
595
|
+
indices="invalid",
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
def test_indices_empty(self, iceland_test_grid):
|
|
369
599
|
with pytest.raises(
|
|
370
600
|
ValueError,
|
|
371
|
-
match="
|
|
601
|
+
match="The provided 'indices' dictionary must contain at least one river.",
|
|
372
602
|
):
|
|
373
|
-
RiverForcing
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
603
|
+
RiverForcing(
|
|
604
|
+
grid=iceland_test_grid,
|
|
605
|
+
start_time=datetime(1998, 1, 1),
|
|
606
|
+
end_time=datetime(1998, 3, 1),
|
|
607
|
+
indices={},
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
def test_invalid_river_name_type(self, iceland_test_grid):
|
|
611
|
+
indices = {123: [(8, 6)]} # Invalid river name (should be a string)
|
|
612
|
+
with pytest.raises(ValueError, match="River name `123` must be a string."):
|
|
613
|
+
RiverForcing(
|
|
614
|
+
grid=iceland_test_grid,
|
|
615
|
+
start_time=datetime(1998, 1, 1),
|
|
616
|
+
end_time=datetime(1998, 3, 1),
|
|
617
|
+
indices=indices,
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
def test_invalid_river_data_type(self, iceland_test_grid):
|
|
621
|
+
indices = {
|
|
622
|
+
"Hvita(Olfusa)": "8, 6" # Invalid river data (should be a list of tuples)
|
|
623
|
+
}
|
|
624
|
+
with pytest.raises(ValueError, match="must be a list of tuples."):
|
|
625
|
+
RiverForcing(
|
|
626
|
+
grid=iceland_test_grid,
|
|
627
|
+
start_time=datetime(1998, 1, 1),
|
|
628
|
+
end_time=datetime(1998, 3, 1),
|
|
629
|
+
indices=indices,
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
def test_invalid_tuple_length(self, iceland_test_grid):
|
|
633
|
+
indices = {
|
|
634
|
+
"Hvita(Olfusa)": [(8, 6, 7)] # Invalid tuple length (should be length 2)
|
|
635
|
+
}
|
|
636
|
+
with pytest.raises(ValueError, match="must be a tuple of length 2"):
|
|
637
|
+
RiverForcing(
|
|
638
|
+
grid=iceland_test_grid,
|
|
639
|
+
start_time=datetime(1998, 1, 1),
|
|
640
|
+
end_time=datetime(1998, 3, 1),
|
|
641
|
+
indices=indices,
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
def test_invalid_eta_rho_type(self, iceland_test_grid):
|
|
645
|
+
indices = {
|
|
646
|
+
"Hvita(Olfusa)": [("a", 6)] # Invalid eta_rho (should be an integer)
|
|
647
|
+
}
|
|
648
|
+
with pytest.raises(ValueError, match="First element of tuple for river"):
|
|
649
|
+
RiverForcing(
|
|
650
|
+
grid=iceland_test_grid,
|
|
651
|
+
start_time=datetime(1998, 1, 1),
|
|
652
|
+
end_time=datetime(1998, 3, 1),
|
|
653
|
+
indices=indices,
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
def test_invalid_xi_rho_type(self, iceland_test_grid):
|
|
657
|
+
indices = {"Hvita(Olfusa)": [(8, "b")]} # Invalid xi_rho (should be an integer)
|
|
658
|
+
with pytest.raises(ValueError, match="Second element of tuple for river"):
|
|
659
|
+
RiverForcing(
|
|
660
|
+
grid=iceland_test_grid,
|
|
661
|
+
start_time=datetime(1998, 1, 1),
|
|
662
|
+
end_time=datetime(1998, 3, 1),
|
|
663
|
+
indices=indices,
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
def test_eta_rho_out_of_range(self, iceland_test_grid):
|
|
667
|
+
indices = {"Hvita(Olfusa)": [(20, 6)]} # eta_rho out of valid range [0, 17]
|
|
668
|
+
with pytest.raises(ValueError, match="Value of eta_rho for river"):
|
|
669
|
+
RiverForcing(
|
|
670
|
+
grid=iceland_test_grid,
|
|
671
|
+
start_time=datetime(1998, 1, 1),
|
|
672
|
+
end_time=datetime(1998, 3, 1),
|
|
673
|
+
indices=indices,
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
def test_xi_rho_out_of_range(self, iceland_test_grid):
|
|
677
|
+
indices = {"Hvita(Olfusa)": [(8, 20)]} # xi_rho out of valid range [0, 17]
|
|
678
|
+
with pytest.raises(ValueError, match="Value of xi_rho for river"):
|
|
679
|
+
RiverForcing(
|
|
680
|
+
grid=iceland_test_grid,
|
|
681
|
+
start_time=datetime(1998, 1, 1),
|
|
682
|
+
end_time=datetime(1998, 3, 1),
|
|
683
|
+
indices=indices,
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
def test_duplicate_location(self, iceland_test_grid):
|
|
687
|
+
indices = {"Hvita(Olfusa)": [(8, 6), (8, 6)]} # Duplicate location
|
|
688
|
+
with pytest.raises(ValueError, match="Duplicate location"):
|
|
689
|
+
RiverForcing(
|
|
690
|
+
grid=iceland_test_grid,
|
|
691
|
+
start_time=datetime(1998, 1, 1),
|
|
692
|
+
end_time=datetime(1998, 3, 1),
|
|
693
|
+
indices=indices,
|
|
694
|
+
)
|