roms-tools 3.1.2__py3-none-any.whl → 3.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roms_tools/__init__.py +3 -0
- roms_tools/analysis/cdr_analysis.py +203 -0
- roms_tools/analysis/cdr_ensemble.py +198 -0
- roms_tools/analysis/roms_output.py +80 -46
- roms_tools/data/grids/GLORYS_global_grid.nc +0 -0
- roms_tools/download.py +4 -0
- roms_tools/plot.py +113 -51
- roms_tools/setup/boundary_forcing.py +45 -20
- roms_tools/setup/cdr_forcing.py +122 -8
- roms_tools/setup/cdr_release.py +161 -8
- roms_tools/setup/grid.py +150 -141
- roms_tools/setup/initial_conditions.py +113 -48
- roms_tools/setup/{datasets.py → lat_lon_datasets.py} +443 -938
- roms_tools/setup/mask.py +63 -7
- roms_tools/setup/nesting.py +314 -117
- roms_tools/setup/river_datasets.py +527 -0
- roms_tools/setup/river_forcing.py +46 -20
- roms_tools/setup/surface_forcing.py +7 -9
- roms_tools/setup/tides.py +2 -3
- roms_tools/setup/topography.py +8 -10
- roms_tools/setup/utils.py +396 -23
- roms_tools/tests/test_analysis/test_cdr_analysis.py +144 -0
- roms_tools/tests/test_analysis/test_cdr_ensemble.py +202 -0
- roms_tools/tests/test_analysis/test_roms_output.py +61 -3
- roms_tools/tests/test_setup/test_boundary_forcing.py +54 -52
- roms_tools/tests/test_setup/test_cdr_forcing.py +54 -0
- roms_tools/tests/test_setup/test_cdr_release.py +118 -1
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zarr.json +406 -406
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zarr.json +182 -182
- roms_tools/tests/test_setup/test_data/grid.zarr/h/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/zarr.json +191 -191
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/c/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/zarr.json +210 -210
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK_ALT_CO2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC_ALT_CO2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOCr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DON/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DONr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOPr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Fe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Lig/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/O2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/SiO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zarr.json +182 -182
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zooC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/salt/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/temp/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/u/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/ubar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/v/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/vbar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/zarr.json +187 -187
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Im/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Re/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Im/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Re/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/zarr.json +66 -66
- roms_tools/tests/test_setup/test_grid.py +236 -115
- roms_tools/tests/test_setup/test_initial_conditions.py +94 -41
- roms_tools/tests/test_setup/{test_datasets.py → test_lat_lon_datasets.py} +409 -100
- roms_tools/tests/test_setup/test_nesting.py +119 -31
- roms_tools/tests/test_setup/test_river_datasets.py +48 -0
- roms_tools/tests/test_setup/test_surface_forcing.py +2 -1
- roms_tools/tests/test_setup/test_utils.py +92 -2
- roms_tools/tests/test_setup/utils.py +71 -0
- roms_tools/tests/test_tiling/test_join.py +241 -0
- roms_tools/tests/test_utils.py +139 -17
- roms_tools/tiling/join.py +189 -0
- roms_tools/utils.py +131 -99
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/METADATA +12 -2
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/RECORD +221 -211
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/WHEEL +0 -0
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/licenses/LICENSE +0 -0
- {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections import Counter, defaultdict
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import xarray as xr
|
|
9
|
+
|
|
10
|
+
from roms_tools.download import download_river_data
|
|
11
|
+
from roms_tools.setup.utils import (
|
|
12
|
+
assign_dates_to_climatology,
|
|
13
|
+
check_dataset,
|
|
14
|
+
gc_dist,
|
|
15
|
+
select_relevant_times,
|
|
16
|
+
)
|
|
17
|
+
from roms_tools.utils import load_data
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(kw_only=True)
|
|
21
|
+
class RiverDataset:
|
|
22
|
+
"""Represents river data.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
filename : Union[str, Path, List[Union[str, Path]]]
|
|
27
|
+
The path to the data file(s). Can be a single string (with or without wildcards), a single Path object,
|
|
28
|
+
or a list of strings or Path objects containing multiple files.
|
|
29
|
+
start_time : datetime
|
|
30
|
+
The start time for selecting relevant data.
|
|
31
|
+
end_time : datetime
|
|
32
|
+
The end time for selecting relevant data.
|
|
33
|
+
dim_names: Dict[str, str]
|
|
34
|
+
Dictionary specifying the names of dimensions in the dataset.
|
|
35
|
+
Requires "station" and "time" as keys.
|
|
36
|
+
var_names: Dict[str, str]
|
|
37
|
+
Dictionary of variable names that are required in the dataset.
|
|
38
|
+
Requires the keys "latitude", "longitude", "flux", "ratio", and "name".
|
|
39
|
+
opt_var_names: Dict[str, str], optional
|
|
40
|
+
Dictionary of variable names that are optional in the dataset.
|
|
41
|
+
Defaults to an empty dictionary.
|
|
42
|
+
climatology : bool
|
|
43
|
+
Indicates whether the dataset is climatological. Defaults to False.
|
|
44
|
+
|
|
45
|
+
Attributes
|
|
46
|
+
----------
|
|
47
|
+
ds : xr.Dataset
|
|
48
|
+
The xarray Dataset containing the forcing data on its original grid.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
filename: str | Path | list[str | Path]
|
|
52
|
+
start_time: datetime
|
|
53
|
+
end_time: datetime
|
|
54
|
+
dim_names: dict[str, str]
|
|
55
|
+
var_names: dict[str, str]
|
|
56
|
+
opt_var_names: dict[str, str] | None = field(default_factory=dict)
|
|
57
|
+
climatology: bool = False
|
|
58
|
+
ds: xr.Dataset = field(init=False, repr=False)
|
|
59
|
+
|
|
60
|
+
def __post_init__(self):
|
|
61
|
+
# Validate start_time and end_time
|
|
62
|
+
if not isinstance(self.start_time, datetime):
|
|
63
|
+
raise TypeError(
|
|
64
|
+
f"start_time must be a datetime object, but got {type(self.start_time).__name__}."
|
|
65
|
+
)
|
|
66
|
+
if not isinstance(self.end_time, datetime):
|
|
67
|
+
raise TypeError(
|
|
68
|
+
f"end_time must be a datetime object, but got {type(self.end_time).__name__}."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
ds = self.load_data()
|
|
72
|
+
ds = self.clean_up(ds)
|
|
73
|
+
self.check_dataset(ds)
|
|
74
|
+
ds = _deduplicate_river_names(
|
|
75
|
+
ds, self.var_names["name"], self.dim_names["station"]
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Select relevant times
|
|
79
|
+
ds = self.add_time_info(ds)
|
|
80
|
+
self.ds = ds
|
|
81
|
+
|
|
82
|
+
def load_data(self) -> xr.Dataset:
|
|
83
|
+
"""Load dataset from the specified file.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
ds : xr.Dataset
|
|
88
|
+
The loaded xarray Dataset containing the forcing data.
|
|
89
|
+
"""
|
|
90
|
+
ds = load_data(
|
|
91
|
+
self.filename, self.dim_names, use_dask=False, decode_times=False
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return ds
|
|
95
|
+
|
|
96
|
+
def clean_up(self, ds: xr.Dataset) -> xr.Dataset:
|
|
97
|
+
"""Decodes the 'name' variable (if byte-encoded) and updates the dataset.
|
|
98
|
+
|
|
99
|
+
This method checks if the 'name' variable is of dtype 'object' (i.e., byte-encoded),
|
|
100
|
+
and if so, decodes each byte array to a string and updates the dataset.
|
|
101
|
+
It also ensures that the 'station' dimension is of integer type.
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
ds : xr.Dataset
|
|
107
|
+
The dataset containing the 'name' variable to decode.
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
ds : xr.Dataset
|
|
112
|
+
The dataset with the decoded 'name' variable.
|
|
113
|
+
"""
|
|
114
|
+
if ds[self.var_names["name"]].dtype == "object":
|
|
115
|
+
names = []
|
|
116
|
+
for i in range(len(ds[self.dim_names["station"]])):
|
|
117
|
+
byte_array = ds[self.var_names["name"]].isel(
|
|
118
|
+
**{self.dim_names["station"]: i}
|
|
119
|
+
)
|
|
120
|
+
name = _decode_string(byte_array)
|
|
121
|
+
names.append(name)
|
|
122
|
+
ds[self.var_names["name"]] = xr.DataArray(
|
|
123
|
+
data=names, dims=self.dim_names["station"]
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if ds[self.dim_names["station"]].dtype == "float64":
|
|
127
|
+
ds[self.dim_names["station"]] = ds[self.dim_names["station"]].astype(int)
|
|
128
|
+
|
|
129
|
+
# Drop all variables that have chars dim
|
|
130
|
+
vars_to_drop = ["ocn_name", "stn_name", "ct_name", "cn_name", "chars"]
|
|
131
|
+
existing_vars = [var for var in vars_to_drop if var in ds]
|
|
132
|
+
ds = ds.drop_vars(existing_vars)
|
|
133
|
+
|
|
134
|
+
return ds
|
|
135
|
+
|
|
136
|
+
def check_dataset(self, ds: xr.Dataset) -> None:
|
|
137
|
+
"""Validate required variables, dimensions, and uniqueness of river names.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
ds : xr.Dataset
|
|
142
|
+
The xarray Dataset to check.
|
|
143
|
+
|
|
144
|
+
Raises
|
|
145
|
+
------
|
|
146
|
+
ValueError
|
|
147
|
+
If the dataset does not contain the specified variables or dimensions.
|
|
148
|
+
"""
|
|
149
|
+
check_dataset(ds, self.dim_names, self.var_names, self.opt_var_names)
|
|
150
|
+
|
|
151
|
+
def add_time_info(self, ds: xr.Dataset) -> xr.Dataset:
|
|
152
|
+
"""Dummy method to be overridden by child classes to add time information to the
|
|
153
|
+
dataset.
|
|
154
|
+
|
|
155
|
+
This method is intended as a placeholder and should be implemented in subclasses
|
|
156
|
+
to provide specific functionality for adding time-related information to the dataset.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
ds : xr.Dataset
|
|
161
|
+
The xarray Dataset to which time information will be added.
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
xr.Dataset
|
|
166
|
+
The xarray Dataset with time information added (as implemented by child classes).
|
|
167
|
+
"""
|
|
168
|
+
return ds
|
|
169
|
+
|
|
170
|
+
def select_relevant_times(self, ds) -> xr.Dataset:
|
|
171
|
+
"""Select a subset of the dataset based on the specified time range.
|
|
172
|
+
|
|
173
|
+
This method filters the dataset to include all records between `start_time` and `end_time`.
|
|
174
|
+
Additionally, it ensures that one record at or before `start_time` and one record at or
|
|
175
|
+
after `end_time` are included, even if they fall outside the strict time range.
|
|
176
|
+
|
|
177
|
+
If no `end_time` is specified, the method will select the time range of
|
|
178
|
+
[start_time, start_time + 24 hours] and return the closest time entry to `start_time` within that range.
|
|
179
|
+
|
|
180
|
+
Parameters
|
|
181
|
+
----------
|
|
182
|
+
ds : xr.Dataset
|
|
183
|
+
The input dataset to be filtered. Must contain a time dimension.
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
xr.Dataset
|
|
188
|
+
A dataset filtered to the specified time range, including the closest entries
|
|
189
|
+
at or before `start_time` and at or after `end_time` if applicable.
|
|
190
|
+
|
|
191
|
+
Warns
|
|
192
|
+
-----
|
|
193
|
+
UserWarning
|
|
194
|
+
If no records at or before `start_time` or no records at or after `end_time` are found.
|
|
195
|
+
|
|
196
|
+
UserWarning
|
|
197
|
+
If the dataset does not contain any time dimension or the time dimension is incorrectly named.
|
|
198
|
+
"""
|
|
199
|
+
time_dim = self.dim_names["time"]
|
|
200
|
+
|
|
201
|
+
ds = select_relevant_times(ds, time_dim, self.start_time, self.end_time, False)
|
|
202
|
+
|
|
203
|
+
return ds
|
|
204
|
+
|
|
205
|
+
def compute_climatology(self):
|
|
206
|
+
logging.info("Compute climatology for river forcing.")
|
|
207
|
+
|
|
208
|
+
time_dim = self.dim_names["time"]
|
|
209
|
+
|
|
210
|
+
flux = self.ds[self.var_names["flux"]].groupby(f"{time_dim}.month").mean()
|
|
211
|
+
self.ds[self.var_names["flux"]] = flux
|
|
212
|
+
|
|
213
|
+
ds = assign_dates_to_climatology(self.ds, "month")
|
|
214
|
+
ds = ds.swap_dims({"month": "time"})
|
|
215
|
+
self.ds = ds
|
|
216
|
+
|
|
217
|
+
updated_dim_names = {**self.dim_names}
|
|
218
|
+
updated_dim_names["time"] = "time"
|
|
219
|
+
self.dim_names = updated_dim_names
|
|
220
|
+
|
|
221
|
+
self.climatology = True
|
|
222
|
+
|
|
223
|
+
def sort_by_river_volume(self, ds: xr.Dataset) -> xr.Dataset:
|
|
224
|
+
"""Sorts the dataset by river volume in descending order (largest rivers first),
|
|
225
|
+
if the volume variable is available.
|
|
226
|
+
|
|
227
|
+
This method uses the river volume to reorder the dataset such that the rivers with
|
|
228
|
+
the largest volumes come first in the `station` dimension. If the volume variable
|
|
229
|
+
is not present in the dataset, a warning is logged.
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
ds : xr.Dataset
|
|
234
|
+
The xarray Dataset containing the river data to be sorted by volume.
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
xr.Dataset
|
|
239
|
+
The dataset with rivers sorted by their volume in descending order.
|
|
240
|
+
If the volume variable is not available, the original dataset is returned.
|
|
241
|
+
"""
|
|
242
|
+
if self.opt_var_names is not None and "vol" in self.opt_var_names:
|
|
243
|
+
volume_values = ds[self.opt_var_names["vol"]].values
|
|
244
|
+
if isinstance(volume_values, np.ndarray):
|
|
245
|
+
# Check if all volume values are the same
|
|
246
|
+
if np.all(volume_values == volume_values[0]):
|
|
247
|
+
# If all volumes are the same, no need to reverse order
|
|
248
|
+
sorted_indices = np.argsort(
|
|
249
|
+
volume_values
|
|
250
|
+
) # Sort in ascending order
|
|
251
|
+
else:
|
|
252
|
+
# If volumes differ, reverse order for descending sort
|
|
253
|
+
sorted_indices = np.argsort(volume_values)[
|
|
254
|
+
::-1
|
|
255
|
+
] # Reverse for descending order
|
|
256
|
+
|
|
257
|
+
ds = ds.isel(**{self.dim_names["station"]: sorted_indices})
|
|
258
|
+
|
|
259
|
+
else:
|
|
260
|
+
logging.warning("The volume data is not in a valid array format.")
|
|
261
|
+
else:
|
|
262
|
+
logging.warning(
|
|
263
|
+
"Cannot sort rivers by volume. 'vol' is missing in the variable names."
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
return ds
|
|
267
|
+
|
|
268
|
+
def extract_relevant_rivers(self, target_coords, dx):
|
|
269
|
+
"""Extracts a subset of the dataset based on the proximity of river mouths to
|
|
270
|
+
target coordinates.
|
|
271
|
+
|
|
272
|
+
This method calculates the distance between each river mouth and the provided target coordinates
|
|
273
|
+
(latitude and longitude) using the `gc_dist` function. It then filters the dataset to include only those
|
|
274
|
+
river stations whose minimum distance from the target is less than a specified threshold distance (`dx`).
|
|
275
|
+
|
|
276
|
+
Parameters
|
|
277
|
+
----------
|
|
278
|
+
target_coords : dict
|
|
279
|
+
A dictionary containing the target coordinates for the comparison. It should include:
|
|
280
|
+
- "lon" (float): The target longitude in degrees.
|
|
281
|
+
- "lat" (float): The target latitude in degrees.
|
|
282
|
+
- "straddle" (bool): A flag indicating whether to adjust the longitudes for stations that cross the
|
|
283
|
+
International Date Line. If `True`, longitudes greater than 180 degrees are adjusted by subtracting 360,
|
|
284
|
+
otherwise, negative longitudes are adjusted by adding 360.
|
|
285
|
+
|
|
286
|
+
dx : float
|
|
287
|
+
The maximum distance threshold (in meters) for including a river station. Only river mouths that are
|
|
288
|
+
within `dx` meters from the target coordinates will be included in the returned dataset.
|
|
289
|
+
|
|
290
|
+
Returns
|
|
291
|
+
-------
|
|
292
|
+
indices : dict[str, list[tuple]]
|
|
293
|
+
A dictionary containing the indices of the rivers that are within the threshold distance from
|
|
294
|
+
the target coordinates. The dictionary structure consists of river names as keys, and each value is a list of tuples. Each tuple represents
|
|
295
|
+
a pair of indices corresponding to the `eta_rho` and `xi_rho` grid coordinates of the river.
|
|
296
|
+
"""
|
|
297
|
+
# Retrieve longitude and latitude of river mouths
|
|
298
|
+
river_lon = self.ds[self.var_names["longitude"]]
|
|
299
|
+
river_lat = self.ds[self.var_names["latitude"]]
|
|
300
|
+
|
|
301
|
+
# Adjust longitude based on whether it crosses the International Date Line (straddle case)
|
|
302
|
+
if target_coords["straddle"]:
|
|
303
|
+
river_lon = xr.where(river_lon > 180, river_lon - 360, river_lon)
|
|
304
|
+
else:
|
|
305
|
+
river_lon = xr.where(river_lon < 0, river_lon + 360, river_lon)
|
|
306
|
+
|
|
307
|
+
# Calculate the distance between the target coordinates and each river mouth
|
|
308
|
+
dist = gc_dist(target_coords["lon"], target_coords["lat"], river_lon, river_lat)
|
|
309
|
+
dist_min = dist.min(dim=["eta_rho", "xi_rho"])
|
|
310
|
+
# Filter the dataset to include only stations within the distance threshold
|
|
311
|
+
if (dist_min < dx).any():
|
|
312
|
+
ds = self.ds.where(dist_min < dx, drop=True)
|
|
313
|
+
ds = self.sort_by_river_volume(ds)
|
|
314
|
+
dist = dist.where(dist_min < dx, drop=True).transpose(
|
|
315
|
+
self.dim_names["station"], "eta_rho", "xi_rho"
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
river_indices = get_indices_of_nearest_grid_cell_for_rivers(dist, self)
|
|
319
|
+
else:
|
|
320
|
+
ds = xr.Dataset()
|
|
321
|
+
river_indices = {}
|
|
322
|
+
|
|
323
|
+
self.ds = ds
|
|
324
|
+
|
|
325
|
+
return river_indices
|
|
326
|
+
|
|
327
|
+
def extract_named_rivers(self, indices):
|
|
328
|
+
"""Extracts a subset of the dataset based on the provided river names in the
|
|
329
|
+
indices dictionary.
|
|
330
|
+
|
|
331
|
+
This method filters the dataset to include only the rivers specified in the `indices` dictionary.
|
|
332
|
+
The resulting subset is stored in the `ds` attribute of the class.
|
|
333
|
+
|
|
334
|
+
Parameters
|
|
335
|
+
----------
|
|
336
|
+
indices : dict
|
|
337
|
+
A dictionary where the keys are river names (strings) and the values are dictionaries
|
|
338
|
+
containing river-related data (e.g., river indices, coordinates).
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
None
|
|
343
|
+
The method modifies the `self.ds` attribute in place, setting it to the filtered dataset
|
|
344
|
+
containing only the data related to the specified rivers.
|
|
345
|
+
|
|
346
|
+
Raises
|
|
347
|
+
------
|
|
348
|
+
ValueError
|
|
349
|
+
- If `indices` is not a dictionary.
|
|
350
|
+
- If any of the requested river names are not found in the dataset.
|
|
351
|
+
"""
|
|
352
|
+
if not isinstance(indices, dict):
|
|
353
|
+
raise ValueError("`indices` must be a dictionary.")
|
|
354
|
+
|
|
355
|
+
river_names = list(indices.keys())
|
|
356
|
+
|
|
357
|
+
# Ensure the dataset is filtered based on the provided river names
|
|
358
|
+
ds_filtered = self.ds.where(
|
|
359
|
+
self.ds[self.var_names["name"]].isin(river_names), drop=True
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
# Check that all requested rivers exist in the dataset
|
|
363
|
+
filtered_river_names = set(ds_filtered[self.var_names["name"]].values)
|
|
364
|
+
missing_rivers = set(river_names) - filtered_river_names
|
|
365
|
+
|
|
366
|
+
if missing_rivers:
|
|
367
|
+
raise ValueError(
|
|
368
|
+
f"The following rivers were not found in the dataset: {missing_rivers}"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
# Set the filtered dataset as the new `ds`
|
|
372
|
+
self.ds = ds_filtered
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@dataclass(kw_only=True)
|
|
376
|
+
class DaiRiverDataset(RiverDataset):
|
|
377
|
+
"""Represents river data from the Dai river dataset."""
|
|
378
|
+
|
|
379
|
+
filename: str | Path | list[str | Path] = field(
|
|
380
|
+
default_factory=lambda: download_river_data("dai_trenberth_may2019.nc")
|
|
381
|
+
)
|
|
382
|
+
dim_names: dict[str, str] = field(
|
|
383
|
+
default_factory=lambda: {
|
|
384
|
+
"station": "station",
|
|
385
|
+
"time": "time",
|
|
386
|
+
}
|
|
387
|
+
)
|
|
388
|
+
var_names: dict[str, str] = field(
|
|
389
|
+
default_factory=lambda: {
|
|
390
|
+
"latitude": "lat_mou",
|
|
391
|
+
"longitude": "lon_mou",
|
|
392
|
+
"flux": "FLOW",
|
|
393
|
+
"ratio": "ratio_m2s",
|
|
394
|
+
"name": "riv_name",
|
|
395
|
+
}
|
|
396
|
+
)
|
|
397
|
+
opt_var_names: dict[str, str] = field(
|
|
398
|
+
default_factory=lambda: {
|
|
399
|
+
"vol": "vol_stn",
|
|
400
|
+
}
|
|
401
|
+
)
|
|
402
|
+
climatology: bool = False
|
|
403
|
+
|
|
404
|
+
def add_time_info(self, ds: xr.Dataset) -> xr.Dataset:
|
|
405
|
+
"""Adds time information to the dataset based on the climatology flag and
|
|
406
|
+
dimension names.
|
|
407
|
+
|
|
408
|
+
This method processes the dataset to include time information according to the climatology
|
|
409
|
+
setting. If the dataset represents climatology data and the time dimension is labeled as
|
|
410
|
+
"month", it assigns dates to the dataset based on a monthly climatology. Additionally, it
|
|
411
|
+
handles dimension name updates if necessary.
|
|
412
|
+
|
|
413
|
+
Parameters
|
|
414
|
+
----------
|
|
415
|
+
ds : xr.Dataset
|
|
416
|
+
The input dataset to which time information will be added.
|
|
417
|
+
|
|
418
|
+
Returns
|
|
419
|
+
-------
|
|
420
|
+
xr.Dataset
|
|
421
|
+
The dataset with time information added, including adjustments for climatology and
|
|
422
|
+
dimension names.
|
|
423
|
+
"""
|
|
424
|
+
time_dim = self.dim_names["time"]
|
|
425
|
+
|
|
426
|
+
# Extract the 'time' variable as a numpy array
|
|
427
|
+
time_vals = ds[time_dim].values
|
|
428
|
+
|
|
429
|
+
# Handle rounding of the time values
|
|
430
|
+
year = np.round(time_vals * 1e-2).astype(int)
|
|
431
|
+
month = np.round((time_vals * 1e-2 - year) * 1e2).astype(int)
|
|
432
|
+
|
|
433
|
+
# Convert to datetime (assuming the day is always 15th for this example)
|
|
434
|
+
dates = [datetime(year=i, month=m, day=15) for i, m in zip(year, month)]
|
|
435
|
+
|
|
436
|
+
ds[time_dim] = dates
|
|
437
|
+
|
|
438
|
+
return ds
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def _decode_string(byte_array):
|
|
442
|
+
# Decode each byte and handle errors with 'ignore'
|
|
443
|
+
decoded_string = "".join(
|
|
444
|
+
[
|
|
445
|
+
x.decode("utf-8", errors="ignore") # Ignore invalid byte sequences
|
|
446
|
+
for x in byte_array.values
|
|
447
|
+
if isinstance(x, bytes) and x != b" " and x is not np.nan
|
|
448
|
+
]
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
return decoded_string
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def get_indices_of_nearest_grid_cell_for_rivers(
|
|
455
|
+
dist: xr.DataArray, data: RiverDataset
|
|
456
|
+
) -> dict[str, list[tuple[int, int]]]:
|
|
457
|
+
"""Get the indices of the nearest grid cell for each river based on distance.
|
|
458
|
+
|
|
459
|
+
Parameters
|
|
460
|
+
----------
|
|
461
|
+
dist : xr.DataArray
|
|
462
|
+
A 2D or 3D array representing distances from each river to coastal grid cells,
|
|
463
|
+
with dimensions including "eta_rho" and "xi_rho".
|
|
464
|
+
data : RiverDataset
|
|
465
|
+
An instance of RiverDataset containing river names and dimension metadata.
|
|
466
|
+
|
|
467
|
+
Returns
|
|
468
|
+
-------
|
|
469
|
+
dict[str, list[tuple[int, int]]]
|
|
470
|
+
Dictionary mapping each river name to a list containing the (eta_rho, xi_rho) index
|
|
471
|
+
of the closest coastal grid cell.
|
|
472
|
+
"""
|
|
473
|
+
# Find indices of the nearest coastal grid cell for each river
|
|
474
|
+
indices = dist.argmin(dim=["eta_rho", "xi_rho"])
|
|
475
|
+
|
|
476
|
+
eta_rho_values = indices["eta_rho"].values
|
|
477
|
+
xi_rho_values = indices["xi_rho"].values
|
|
478
|
+
|
|
479
|
+
# Get the corresponding station indices and river names
|
|
480
|
+
stations = indices["eta_rho"][data.dim_names["station"]].values
|
|
481
|
+
names = (
|
|
482
|
+
data.ds[data.var_names["name"]]
|
|
483
|
+
.sel({data.dim_names["station"]: stations})
|
|
484
|
+
.values
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
# Build dictionary of river name to grid index
|
|
488
|
+
river_indices = {
|
|
489
|
+
str(names[i]): [(int(eta_rho_values[i]), int(xi_rho_values[i]))]
|
|
490
|
+
for i in range(len(stations))
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return river_indices
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _deduplicate_river_names(
|
|
497
|
+
ds: xr.Dataset, name_var: str, station_dim: str
|
|
498
|
+
) -> xr.Dataset:
|
|
499
|
+
"""Ensure river names are unique by appending _1, _2 to duplicates, excluding non-
|
|
500
|
+
duplicates.
|
|
501
|
+
"""
|
|
502
|
+
original = ds[name_var]
|
|
503
|
+
|
|
504
|
+
# Force cast to plain Python strings
|
|
505
|
+
names = [str(name) for name in original.values]
|
|
506
|
+
|
|
507
|
+
# Count all names
|
|
508
|
+
name_counts = Counter(names)
|
|
509
|
+
seen: defaultdict[str, int] = defaultdict(int)
|
|
510
|
+
|
|
511
|
+
unique_names = []
|
|
512
|
+
for name in names:
|
|
513
|
+
if name_counts[name] > 1:
|
|
514
|
+
seen[name] += 1
|
|
515
|
+
unique_names.append(f"{name}_{seen[name]}")
|
|
516
|
+
else:
|
|
517
|
+
unique_names.append(name)
|
|
518
|
+
|
|
519
|
+
# Replace with updated names while preserving dtype, dims, attrs
|
|
520
|
+
updated_array = xr.DataArray(
|
|
521
|
+
data=np.array(unique_names, dtype=f"<U{max(len(n) for n in unique_names)}"),
|
|
522
|
+
dims=original.dims,
|
|
523
|
+
attrs=original.attrs,
|
|
524
|
+
)
|
|
525
|
+
ds[name_var] = updated_array
|
|
526
|
+
|
|
527
|
+
return ds
|