roms-tools 1.6.2__py3-none-any.whl → 2.0.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.
Files changed (283) hide show
  1. ci/environment.yml +1 -1
  2. roms_tools/__init__.py +1 -0
  3. roms_tools/_version.py +1 -1
  4. roms_tools/setup/boundary_forcing.py +266 -256
  5. roms_tools/setup/datasets.py +986 -231
  6. roms_tools/setup/download.py +41 -15
  7. roms_tools/setup/grid.py +561 -512
  8. roms_tools/setup/initial_conditions.py +162 -106
  9. roms_tools/setup/mask.py +69 -0
  10. roms_tools/setup/plot.py +81 -23
  11. roms_tools/setup/regrid.py +4 -2
  12. roms_tools/setup/river_forcing.py +589 -0
  13. roms_tools/setup/surface_forcing.py +21 -130
  14. roms_tools/setup/tides.py +15 -79
  15. roms_tools/setup/topography.py +92 -128
  16. roms_tools/setup/utils.py +307 -25
  17. roms_tools/setup/vertical_coordinate.py +5 -16
  18. roms_tools/tests/test_setup/test_boundary_forcing.py +10 -7
  19. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zattrs +1 -1
  20. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zmetadata +157 -130
  21. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/.zattrs +1 -1
  22. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_north/.zattrs +1 -1
  23. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/.zattrs +1 -1
  24. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/.zattrs +1 -1
  25. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/.zattrs +1 -1
  26. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_north/.zattrs +1 -1
  27. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/.zattrs +1 -1
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/.zattrs +1 -1
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/.zattrs +1 -1
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_north/.zattrs +1 -1
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/.zattrs +1 -1
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/.zattrs +1 -1
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/.zattrs +1 -1
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_north/.zattrs +1 -1
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/.zattrs +1 -1
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/.zattrs +1 -1
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/.zattrs +1 -1
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_north/.zattrs +1 -1
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/.zattrs +1 -1
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/.zattrs +1 -1
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/.zattrs +1 -1
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_north/.zattrs +1 -1
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/.zattrs +1 -1
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/.zattrs +1 -1
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/.zattrs +1 -1
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_north/.zattrs +1 -1
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/.zattrs +1 -1
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/.zattrs +1 -1
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/.zattrs +1 -1
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_north/.zattrs +1 -1
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/.zattrs +1 -1
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/.zattrs +1 -1
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/.zattrs +1 -1
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_north/.zattrs +1 -1
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/.zattrs +1 -1
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/.zattrs +1 -1
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/.zattrs +1 -1
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_north/.zattrs +1 -1
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/.zattrs +1 -1
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/.zattrs +1 -1
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/.zattrs +1 -1
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_north/.zattrs +1 -1
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/.zattrs +1 -1
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/.zattrs +1 -1
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/.zattrs +1 -1
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_north/.zattrs +1 -1
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/.zattrs +1 -1
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/.zattrs +1 -1
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/.zattrs +1 -1
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/.zattrs +1 -1
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/.zattrs +1 -1
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/.zattrs +1 -1
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/.zattrs +1 -1
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_north/.zattrs +1 -1
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/.zattrs +1 -1
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/.zattrs +1 -1
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/.zattrs +1 -1
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_north/.zattrs +1 -1
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/.zattrs +1 -1
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/.zattrs +1 -1
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/.zattrs +1 -1
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_north/.zattrs +1 -1
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/.zattrs +1 -1
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/.zattrs +1 -1
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/.zattrs +1 -1
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_north/.zattrs +1 -1
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/.zattrs +1 -1
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/.zattrs +1 -1
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/abs_time/.zattrs +1 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/bry_time/.zattrs +1 -1
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/.zattrs +1 -1
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/.zattrs +1 -1
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/.zattrs +1 -1
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/.zattrs +1 -1
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/.zattrs +1 -1
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/.zattrs +1 -1
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/.zattrs +1 -1
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/.zattrs +1 -1
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/.zattrs +1 -1
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/.zattrs +1 -1
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/.zattrs +1 -1
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/.zattrs +1 -1
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/.zattrs +1 -1
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/.zattrs +1 -1
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/.zattrs +1 -1
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/.zattrs +1 -1
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/.zattrs +1 -1
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/.zattrs +1 -1
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/.zattrs +1 -1
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/.zattrs +1 -1
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/.zattrs +1 -1
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/.zattrs +1 -1
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/.zattrs +1 -1
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/.zattrs +1 -1
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/.zattrs +1 -1
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/.zattrs +1 -1
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/.zattrs +1 -1
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/.zattrs +1 -1
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/.zattrs +1 -1
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/.zattrs +1 -1
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/.zattrs +1 -1
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/.zattrs +1 -1
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/.zattrs +1 -1
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/.zattrs +1 -1
  125. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/.zattrs +1 -1
  126. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/.zattrs +1 -1
  127. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/month/.zarray +20 -0
  128. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/month/.zattrs +6 -0
  129. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/month/0 +0 -0
  130. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/.zattrs +1 -1
  131. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/.zattrs +1 -1
  132. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/.zattrs +1 -1
  133. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/.zattrs +1 -1
  134. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/.zattrs +1 -1
  135. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/.zattrs +1 -1
  136. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/.zattrs +1 -1
  137. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/.zattrs +1 -1
  138. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/.zattrs +1 -1
  139. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/.zattrs +1 -1
  140. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/.zattrs +1 -1
  141. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/.zattrs +1 -1
  142. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/.zattrs +1 -1
  143. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/.zattrs +1 -1
  144. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/.zattrs +1 -1
  145. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/.zattrs +1 -1
  146. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/.zattrs +1 -1
  147. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/.zattrs +1 -1
  148. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/.zattrs +1 -1
  149. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/.zattrs +1 -1
  150. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/.zattrs +1 -1
  151. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/.zattrs +1 -1
  152. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/.zattrs +1 -1
  153. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/.zattrs +1 -1
  154. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zattrs +1 -1
  155. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zmetadata +39 -12
  156. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/abs_time/.zattrs +1 -0
  157. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust/.zattrs +1 -1
  158. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust_time/.zattrs +1 -1
  159. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron/.zattrs +1 -1
  160. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron_time/.zattrs +1 -1
  161. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/month/.zarray +20 -0
  162. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/month/.zattrs +6 -0
  163. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/month/0 +0 -0
  164. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy/.zattrs +1 -1
  165. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy_time/.zattrs +1 -1
  166. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox/.zattrs +1 -1
  167. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox_time/.zattrs +1 -1
  168. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air/.zattrs +1 -1
  169. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air_alt/.zattrs +1 -1
  170. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_time/.zattrs +1 -1
  171. roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +0 -1
  172. roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +56 -201
  173. roms_tools/tests/test_setup/test_data/grid.zarr/Cs_r/.zattrs +1 -1
  174. roms_tools/tests/test_setup/test_data/grid.zarr/Cs_w/.zattrs +1 -1
  175. roms_tools/tests/test_setup/test_data/grid.zarr/{interface_depth_rho → sigma_r}/.zarray +2 -6
  176. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_r/.zattrs +7 -0
  177. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_r/0 +0 -0
  178. roms_tools/tests/test_setup/test_data/grid.zarr/{interface_depth_u → sigma_w}/.zarray +2 -6
  179. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/.zattrs +7 -0
  180. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/0 +0 -0
  181. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +1 -2
  182. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +58 -203
  183. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/Cs_r/.zattrs +1 -1
  184. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/Cs_w/.zattrs +1 -1
  185. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/.zattrs +1 -1
  186. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  187. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
  188. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
  189. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
  190. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
  191. roms_tools/tests/test_setup/test_data/{grid.zarr/interface_depth_v → grid_that_straddles_dateline.zarr/sigma_r}/.zarray +2 -6
  192. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/.zattrs +7 -0
  193. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/0 +0 -0
  194. roms_tools/tests/test_setup/test_data/{grid.zarr/layer_depth_rho → grid_that_straddles_dateline.zarr/sigma_w}/.zarray +2 -6
  195. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/.zattrs +7 -0
  196. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/0 +0 -0
  197. roms_tools/tests/test_setup/test_data/river_forcing.zarr/.zattrs +3 -0
  198. roms_tools/tests/test_setup/test_data/river_forcing.zarr/.zgroup +3 -0
  199. roms_tools/tests/test_setup/test_data/river_forcing.zarr/.zmetadata +214 -0
  200. roms_tools/tests/test_setup/test_data/river_forcing.zarr/abs_time/.zarray +20 -0
  201. roms_tools/tests/test_setup/test_data/river_forcing.zarr/abs_time/.zattrs +8 -0
  202. roms_tools/tests/test_setup/test_data/river_forcing.zarr/abs_time/0 +0 -0
  203. roms_tools/tests/test_setup/test_data/river_forcing.zarr/month/.zarray +20 -0
  204. roms_tools/tests/test_setup/test_data/river_forcing.zarr/month/.zattrs +6 -0
  205. roms_tools/tests/test_setup/test_data/river_forcing.zarr/month/0 +0 -0
  206. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_name/.zarray +24 -0
  207. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_name/.zattrs +6 -0
  208. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_name/0 +0 -0
  209. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_time/.zarray +20 -0
  210. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_time/.zattrs +8 -0
  211. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_time/0 +0 -0
  212. roms_tools/tests/test_setup/test_data/{grid.zarr/layer_depth_v → river_forcing.zarr/river_tracer}/.zarray +4 -4
  213. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_tracer/.zattrs +10 -0
  214. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_tracer/0.0.0 +0 -0
  215. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_volume/.zarray +22 -0
  216. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_volume/.zattrs +9 -0
  217. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_volume/0.0 +0 -0
  218. roms_tools/tests/test_setup/test_data/{grid.zarr/layer_depth_u → river_forcing.zarr/tracer_name}/.zarray +2 -6
  219. roms_tools/tests/test_setup/test_data/river_forcing.zarr/tracer_name/.zattrs +6 -0
  220. roms_tools/tests/test_setup/test_data/river_forcing.zarr/tracer_name/0 +0 -0
  221. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zattrs +1 -0
  222. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zgroup +3 -0
  223. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +185 -0
  224. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/abs_time/.zarray +20 -0
  225. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/abs_time/.zattrs +8 -0
  226. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/abs_time/0 +0 -0
  227. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_name/.zarray +24 -0
  228. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_name/.zattrs +6 -0
  229. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_name/0 +0 -0
  230. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_time/.zarray +20 -0
  231. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_time/.zattrs +7 -0
  232. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_time/0 +0 -0
  233. roms_tools/tests/test_setup/test_data/{grid_that_straddles_dateline.zarr/interface_depth_v → river_forcing_no_climatology.zarr/river_tracer}/.zarray +4 -4
  234. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_tracer/.zattrs +10 -0
  235. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_tracer/0.0.0 +0 -0
  236. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_volume/.zarray +22 -0
  237. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_volume/.zattrs +9 -0
  238. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_volume/0.0 +0 -0
  239. roms_tools/tests/test_setup/test_data/{grid_that_straddles_dateline.zarr/interface_depth_u → river_forcing_no_climatology.zarr/tracer_name}/.zarray +2 -6
  240. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/.zattrs +6 -0
  241. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/0 +0 -0
  242. roms_tools/tests/test_setup/test_grid.py +110 -12
  243. roms_tools/tests/test_setup/test_initial_conditions.py +2 -3
  244. roms_tools/tests/test_setup/test_river_forcing.py +367 -0
  245. roms_tools/tests/test_setup/test_surface_forcing.py +2 -24
  246. roms_tools/tests/test_setup/test_tides.py +2 -3
  247. roms_tools/tests/test_setup/test_topography.py +106 -1
  248. roms_tools/tests/test_setup/test_validation.py +4 -0
  249. roms_tools/utils.py +12 -10
  250. {roms_tools-1.6.2.dist-info → roms_tools-2.0.0.dist-info}/LICENSE +1 -1
  251. {roms_tools-1.6.2.dist-info → roms_tools-2.0.0.dist-info}/METADATA +6 -5
  252. {roms_tools-1.6.2.dist-info → roms_tools-2.0.0.dist-info}/RECORD +254 -225
  253. {roms_tools-1.6.2.dist-info → roms_tools-2.0.0.dist-info}/WHEEL +1 -1
  254. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/.zattrs +0 -9
  255. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/0.0.0 +0 -0
  256. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/.zattrs +0 -9
  257. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/0.0.0 +0 -0
  258. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/.zattrs +0 -9
  259. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/0.0.0 +0 -0
  260. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_rho/.zattrs +0 -9
  261. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_rho/0.0.0 +0 -0
  262. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/.zattrs +0 -9
  263. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/0.0.0 +0 -0
  264. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/.zattrs +0 -9
  265. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/0.0.0 +0 -0
  266. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/.zarray +0 -24
  267. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/.zattrs +0 -9
  268. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/0.0.0 +0 -0
  269. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/.zattrs +0 -9
  270. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/0.0.0 +0 -0
  271. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/.zattrs +0 -9
  272. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/0.0.0 +0 -0
  273. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/.zarray +0 -24
  274. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/.zattrs +0 -9
  275. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/0.0.0 +0 -0
  276. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/.zarray +0 -24
  277. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/.zattrs +0 -9
  278. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/0.0.0 +0 -0
  279. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/.zarray +0 -24
  280. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/.zattrs +0 -9
  281. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/0.0.0 +0 -0
  282. roms_tools/tests/test_setup/test_vertical_coordinate.py +0 -91
  283. {roms_tools-1.6.2.dist-info → roms_tools-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,589 @@
1
+ import xarray as xr
2
+ import numpy as np
3
+ import logging
4
+ from dataclasses import dataclass, field
5
+ from roms_tools.setup.grid import Grid
6
+ from datetime import datetime
7
+ from typing import Dict, Union, List
8
+ from roms_tools.setup.datasets import DaiRiverDataset
9
+ from pathlib import Path
10
+ import matplotlib.pyplot as plt
11
+ from roms_tools.setup.utils import (
12
+ get_target_coords,
13
+ gc_dist,
14
+ substitute_nans_by_fillvalue,
15
+ convert_to_roms_time,
16
+ save_datasets,
17
+ _to_yaml,
18
+ _from_yaml,
19
+ )
20
+ from roms_tools.setup.plot import _get_projection, _add_plot_to_ax
21
+ import cartopy.crs as ccrs
22
+
23
+
24
+ @dataclass(frozen=True, kw_only=True)
25
+ class RiverForcing:
26
+ """Represents river forcing input data for ROMS.
27
+
28
+ Parameters
29
+ ----------
30
+ grid : Grid
31
+ Object representing the grid information.
32
+ start_time : datetime
33
+ Start time of the desired river forcing data.
34
+ end_time : datetime
35
+ End time of the desired river forcing data.
36
+ source : Dict[str, Union[str, Path, List[Union[str, Path]]], bool], optional
37
+ Dictionary specifying the source of the river forcing data. Keys include:
38
+
39
+ - "name" (str): Name of the data source (e.g., "DAI").
40
+ - "path" (Union[str, Path, List[Union[str, Path]]]): The path to the raw data file(s). This can be:
41
+
42
+ - A single string (with or without wildcards).
43
+ - A single Path object.
44
+ - A list of strings or Path objects containing multiple files.
45
+ - "climatology" (bool): Indicates if the data is climatology data. Defaults to False.
46
+
47
+ The default is the Dai and Trenberth global river dataset (updated in May 2019), which does not require a path.
48
+
49
+ convert_to_climatology : str, optional
50
+ Determines when to compute climatology for river forcing. Options are:
51
+ - "if_any_missing" (default): Compute climatology for all rivers if any river has missing values.
52
+ - "never": Do not compute climatology.
53
+ - "always": Compute climatology for all rivers, regardless of missing data.
54
+
55
+ model_reference_date : datetime, optional
56
+ Reference date for the model. Default is January 1, 2000.
57
+
58
+ Attributes
59
+ ----------
60
+ ds : xr.Dataset
61
+ The xarray Dataset containing the river forcing data.
62
+ climatology : bool
63
+ Indicates whether the final river forcing is climatological.
64
+ """
65
+
66
+ grid: Grid
67
+ start_time: datetime
68
+ end_time: datetime
69
+ source: Dict[str, Union[str, Path, List[Union[str, Path]]]] = None
70
+ convert_to_climatology: str = "if_any_missing"
71
+ model_reference_date: datetime = datetime(2000, 1, 1)
72
+
73
+ ds: xr.Dataset = field(init=False, repr=False)
74
+ climatology: xr.Dataset = field(init=False, repr=False)
75
+
76
+ def __post_init__(self):
77
+ self._input_checks()
78
+ target_coords = get_target_coords(self.grid)
79
+ # maximum dx in grid
80
+ dx = (
81
+ np.sqrt((1 / self.grid.ds.pm) ** 2 + (1 / self.grid.ds.pn) ** 2) / 2
82
+ ).max()
83
+
84
+ data = self._get_data()
85
+
86
+ original_indices = data.extract_relevant_rivers(target_coords, dx)
87
+ object.__setattr__(self, "original_indices", original_indices)
88
+
89
+ if len(original_indices["station"]) > 0:
90
+ self.move_rivers_to_closest_coast(target_coords, data)
91
+ ds = self._create_river_forcing(data)
92
+ self._validate(ds)
93
+
94
+ for var_name in ds.data_vars:
95
+ ds[var_name] = substitute_nans_by_fillvalue(
96
+ ds[var_name], fill_value=0.0
97
+ )
98
+
99
+ object.__setattr__(self, "ds", ds)
100
+
101
+ else:
102
+ raise ValueError(
103
+ "No relevant rivers found. Consider increasing domain size or using a different river dataset."
104
+ )
105
+
106
+ def _input_checks(self):
107
+ if self.source is None:
108
+ object.__setattr__(self, "source", {"name": "DAI"})
109
+
110
+ if "name" not in self.source:
111
+ raise ValueError("`source` must include a 'name'.")
112
+ if "path" not in self.source:
113
+ if self.source["name"] != "DAI":
114
+ raise ValueError("`source` must include a 'path'.")
115
+
116
+ # Set 'climatology' to False if not provided in 'source'
117
+ object.__setattr__(
118
+ self,
119
+ "source",
120
+ {**self.source, "climatology": self.source.get("climatology", False)},
121
+ )
122
+
123
+ def _get_data(self):
124
+
125
+ data_dict = {
126
+ "start_time": self.start_time,
127
+ "end_time": self.end_time,
128
+ "climatology": self.source["climatology"],
129
+ }
130
+
131
+ if self.source["name"] == "DAI":
132
+ if "path" in self.source.keys():
133
+ data_dict["filename"] = self.source["path"]
134
+ data = DaiRiverDataset(**data_dict)
135
+ else:
136
+ raise ValueError('Only "DAI" is a valid option for source["name"].')
137
+
138
+ return data
139
+
140
+ def _create_river_forcing(self, data):
141
+ """Create river forcing data for volume flux and tracers (temperature and
142
+ salinity).
143
+
144
+ This method computes the river volume flux and associated tracers (temperature and salinity)
145
+ based on the provided input data. It generates a new `xarray.Dataset` that contains:
146
+ - `river_volume`: The river volume flux, calculated as the product of river flux and a specified ratio, with units of m³/s.
147
+ - `river_tracer`: A tracer array containing temperature and salinity values at each river station over time.
148
+
149
+ The method also handles climatological adjustments for missing or incomplete data, depending on the `convert_to_climatology` setting.
150
+
151
+ Parameters
152
+ ----------
153
+ data : object
154
+ An object containing the necessary dataset and variables for river forcing creation. The object must have the following attributes:
155
+ - `ds`: The dataset containing the river flux, ratio, and other related variables.
156
+ - `var_names`: A dictionary mapping variable names (e.g., `"flux"`, `"ratio"`, `"name"`) to the corresponding variable names in the dataset.
157
+ - `dim_names`: A dictionary mapping dimension names (e.g., `"time"`, `"station"`) to the corresponding dimension names in the dataset.
158
+
159
+ Returns
160
+ -------
161
+ xr.Dataset
162
+ A new `xarray.Dataset` containing the computed river forcing data. The dataset includes:
163
+ - `river_volume`: A `DataArray` representing the river volume flux (m³/s).
164
+ - `river_tracer`: A `DataArray` representing tracer data for temperature and salinity at each river station over time.
165
+ """
166
+ if self.source["climatology"]:
167
+ object.__setattr__(self, "climatology", True)
168
+ else:
169
+ if self.convert_to_climatology in ["never", "if_any_missing"]:
170
+ data_ds = data.select_relevant_times(data.ds)
171
+ if self.convert_to_climatology == "if_any_missing":
172
+ if data_ds[data.var_names["flux"]].isnull().any():
173
+ data.compute_climatology()
174
+ object.__setattr__(self, "climatology", True)
175
+ else:
176
+ object.__setattr__(data, "ds", data_ds)
177
+ object.__setattr__(self, "climatology", False)
178
+ else:
179
+ object.__setattr__(data, "ds", data_ds)
180
+ object.__setattr__(self, "climatology", False)
181
+ elif self.convert_to_climatology == "always":
182
+ data.compute_climatology()
183
+ object.__setattr__(self, "climatology", True)
184
+
185
+ ds = xr.Dataset()
186
+
187
+ river_volume = (
188
+ data.ds[data.var_names["flux"]] * data.ds[data.var_names["ratio"]]
189
+ ).astype(np.float32)
190
+ river_volume.attrs["long_name"] = "River volume flux"
191
+ river_volume.attrs["units"] = "m^3/s"
192
+ river_volume = river_volume.rename(
193
+ {data.dim_names["time"]: "river_time", data.dim_names["station"]: "nriver"}
194
+ )
195
+ name = data.ds[data.var_names["name"]].rename(
196
+ {data.dim_names["station"]: "nriver"}
197
+ )
198
+ name.attrs["long_name"] = "River name"
199
+ river_volume.coords["river_name"] = name
200
+ ds["river_volume"] = river_volume
201
+
202
+ tracer_data = np.zeros(
203
+ (len(ds.river_time), 2, len(ds.nriver)), dtype=np.float32
204
+ )
205
+ tracer_data[:, 0, :] = 17.0
206
+ tracer_data[:, 1, :] = 1.0
207
+
208
+ river_tracer = xr.DataArray(
209
+ tracer_data, dims=("river_time", "ntracers", "nriver")
210
+ )
211
+ river_tracer.attrs["long_name"] = "River tracer data"
212
+ river_tracer.attrs["units"] = "degrees C [temperature]; psu [salinity]"
213
+ tracer_names = xr.DataArray(["temperature", "salinity"], dims="ntracers")
214
+ tracer_names.attrs["long_name"] = "Tracer name"
215
+ river_tracer.coords["tracer_name"] = tracer_names
216
+ ds["river_tracer"] = river_tracer
217
+
218
+ ds, time = convert_to_roms_time(
219
+ ds, self.model_reference_date, self.climatology, time_name="river_time"
220
+ )
221
+
222
+ ds = ds.assign_coords({"river_time": time})
223
+
224
+ ds = ds.drop_vars("nriver")
225
+
226
+ return ds
227
+
228
+ def move_rivers_to_closest_coast(self, target_coords, data):
229
+ """Move river mouths to the closest coastal grid cell.
230
+
231
+ This method computes the closest coastal grid point to each river mouth
232
+ based on geographical distance.
233
+
234
+ Parameters:
235
+ -----------
236
+ target_coords : dict
237
+ A dictionary containing the following keys:
238
+ - "lon" (xarray.DataArray): Longitude coordinates of the target grid points.
239
+ - "lat" (xarray.DataArray): Latitude coordinates of the target grid points.
240
+ - "straddle" (bool): A flag indicating whether the river mouth crosses the International Date Line.
241
+
242
+ data : object
243
+ An object that contains the dataset and related variables. It must have the following attributes:
244
+ - `ds`: The dataset containing river information.
245
+ - `var_names`: A dictionary of variable names in the dataset (e.g., longitude, latitude, station names).
246
+ - `dim_names`: A dictionary containing dimension names for the dataset (e.g., "station", "eta_rho", "xi_rho").
247
+
248
+ Returns:
249
+ --------
250
+ None
251
+ This method modifies the `self.updated_indices` attribute and writes the updated indices
252
+ of the river mouths to the grid file using `write_indices_into_grid_file`.
253
+ """
254
+
255
+ # Retrieve longitude and latitude of river mouths
256
+ river_lon = data.ds[data.var_names["longitude"]]
257
+ river_lat = data.ds[data.var_names["latitude"]]
258
+
259
+ # Adjust longitude based on whether it crosses the International Date Line (straddle case)
260
+ if target_coords["straddle"]:
261
+ river_lon = xr.where(river_lon > 180, river_lon - 360, river_lon)
262
+ else:
263
+ river_lon = xr.where(river_lon < 0, river_lon + 360, river_lon)
264
+
265
+ mask = self.grid.ds.mask_rho
266
+ faces = (
267
+ mask.shift(eta_rho=1)
268
+ + mask.shift(eta_rho=-1)
269
+ + mask.shift(xi_rho=1)
270
+ + mask.shift(xi_rho=-1)
271
+ )
272
+
273
+ # We want all grid points on land that are adjacent to the ocean
274
+ coast = (1 - mask) * (faces > 0)
275
+ dist_coast = gc_dist(
276
+ target_coords["lon"].where(coast),
277
+ target_coords["lat"].where(coast),
278
+ river_lon,
279
+ river_lat,
280
+ ).transpose(data.dim_names["station"], "eta_rho", "xi_rho")
281
+ dist_coast_min = dist_coast.min(dim=["eta_rho", "xi_rho"])
282
+
283
+ # Find the indices of the closest coastal grid cell to the river mouth
284
+ indices = np.where(dist_coast == dist_coast_min)
285
+ names = (
286
+ data.ds[data.var_names["name"]]
287
+ .isel({data.dim_names["station"]: indices[0]})
288
+ .values
289
+ )
290
+
291
+ # Return the indices in a dictionary format
292
+ indices = {
293
+ "station": indices[0],
294
+ "eta_rho": indices[1],
295
+ "xi_rho": indices[2],
296
+ "name": names,
297
+ }
298
+ self.write_indices_into_grid_file(indices)
299
+ object.__setattr__(self, "updated_indices", indices)
300
+
301
+ def write_indices_into_grid_file(self, indices):
302
+ """Writes river location indices into the grid dataset as the "river_flux"
303
+ variable.
304
+
305
+ This method checks if the variable "river_flux" already exists in the grid dataset.
306
+ If it does, the method removes it. Then, it creates a new variable called "river_flux"
307
+ based on the provided `indices` and assigns it to the dataset. The `indices` dictionary
308
+ provides information about the river station locations and their corresponding grid
309
+ cell indices (eta_rho and xi_rho).
310
+
311
+ Parameters:
312
+ -----------
313
+ indices : dict
314
+ A dictionary containing information about river station locations.
315
+ It must contain the following keys:
316
+ - "name" (list or array): Names of the river stations.
317
+ - "station" (list or array): River station identifiers.
318
+ - "eta_rho" (list or array): The eta (row) index for each river location.
319
+ - "xi_rho" (list or array): The xi (column) index for each river location.
320
+
321
+ Returns:
322
+ --------
323
+ None
324
+ This method modifies the grid's dataset in-place by adding the "river_flux" variable.
325
+ """
326
+
327
+ if "river_flux" in self.grid.ds:
328
+ ds = self.grid.ds.drop_vars("river_flux")
329
+ object.__setattr__(self.grid, "ds", ds)
330
+
331
+ river_locations = xr.zeros_like(self.grid.ds.mask_rho)
332
+ for i in range(len(indices["name"])):
333
+ station = indices["station"][i]
334
+ eta_index = indices["eta_rho"][i]
335
+ xi_index = indices["xi_rho"][i]
336
+ river_locations[eta_index, xi_index] = station + 2
337
+
338
+ river_locations.attrs["long_name"] = "River volume flux partition"
339
+ river_locations.attrs["units"] = "none"
340
+ self.grid.ds["river_flux"] = river_locations
341
+
342
+ def _validate(self, ds):
343
+ """Validates the dataset by checking for NaN values in river forcing data.
344
+
345
+ Parameters
346
+ ----------
347
+ ds : xarray.Dataset
348
+ The dataset to validate.
349
+
350
+ Raises
351
+ ------
352
+ Warning
353
+ If NaN values are found in any of the dataset variables, a warning message is logged.
354
+ """
355
+
356
+ for var_name in ds.data_vars:
357
+ da = ds[var_name]
358
+ if da.isnull().any().values:
359
+ logging.warning(
360
+ f"NaN values detected in the '{var_name}' field. These values are being set to zero. "
361
+ "This may indicate missing river data, which could affect model accuracy. Consider setting "
362
+ "`convert_to_climatology = 'if_any_missing'` to automatically fill missing values with climatological data."
363
+ )
364
+
365
+ def plot_locations(self):
366
+ """Plots the original and updated river locations on a map projection."""
367
+
368
+ field = self.grid.ds.mask_rho
369
+ field = field.assign_coords(
370
+ {"lon": self.grid.ds.lon_rho, "lat": self.grid.ds.lat_rho}
371
+ )
372
+ vmax = 3
373
+ vmin = 0
374
+ cmap = plt.colormaps.get_cmap("Blues")
375
+ kwargs = {"vmax": vmax, "vmin": vmin, "cmap": cmap}
376
+
377
+ lon_deg = field.lon
378
+ lat_deg = field.lat
379
+
380
+ # check if North or South pole are in domain
381
+ if lat_deg.max().values > 89 or lat_deg.min().values < -89:
382
+ raise NotImplementedError(
383
+ "Plotting is not implemented for the case that the domain contains the North or South pole."
384
+ )
385
+
386
+ if self.grid.straddle:
387
+ lon_deg = xr.where(lon_deg > 180, lon_deg - 360, lon_deg)
388
+
389
+ trans = _get_projection(lon_deg, lat_deg)
390
+
391
+ lon_deg = lon_deg.values
392
+ lat_deg = lat_deg.values
393
+
394
+ fig, axs = plt.subplots(
395
+ 1, 2, figsize=(13, 13), subplot_kw={"projection": trans}
396
+ )
397
+
398
+ for ax in axs:
399
+ _add_plot_to_ax(
400
+ ax,
401
+ lon_deg,
402
+ lat_deg,
403
+ trans,
404
+ field,
405
+ c=None,
406
+ add_colorbar=False,
407
+ kwargs=kwargs,
408
+ )
409
+
410
+ for ax, indices in zip(axs, [self.original_indices, self.updated_indices]):
411
+ for i in range(len(indices["name"])):
412
+ name = indices["name"][i]
413
+ xi_index = indices["xi_rho"][i]
414
+ eta_index = indices["eta_rho"][i]
415
+ # transform coordinates to projected space
416
+ proj = ccrs.PlateCarree()
417
+ transformed_lon, transformed_lat = trans.transform_point(
418
+ self.grid.ds.lon_rho[eta_index, xi_index],
419
+ self.grid.ds.lat_rho[eta_index, xi_index],
420
+ proj,
421
+ )
422
+ ax.plot(
423
+ transformed_lon,
424
+ transformed_lat,
425
+ marker="x",
426
+ markersize=8,
427
+ markeredgewidth=2,
428
+ label=name,
429
+ )
430
+
431
+ axs[0].set_title("Original river locations")
432
+ axs[1].set_title("Updated river locations")
433
+
434
+ axs[1].legend(loc="center left", bbox_to_anchor=(1.1, 0.5))
435
+
436
+ def plot(self, var_name="river_volume"):
437
+ """Plots the river flux (e.g., volume, temperature, or salinity) over time for
438
+ all rivers.
439
+
440
+ This method generates a time-series plot for a specified river-related variable (such as river volume,
441
+ river temperature, or river salinity) for each river in the dataset. It can handle climatology data
442
+ as well as time series data, and customizes the x-axis and labels accordingly.
443
+
444
+ Parameters
445
+ ----------
446
+ var_name : str, optional
447
+ The variable to plot. It can be one of the following:
448
+ - 'river_volume' : Plot the river volume flux.
449
+ - 'river_temperature' : Plot the river temperature (from the river_tracer).
450
+ - 'river_salinity' : Plot the river salinity (from the river_tracer).
451
+ The default is 'river_volume'.
452
+ """
453
+ fig, ax = plt.subplots(1, 1, figsize=(9, 5))
454
+
455
+ if self.climatology:
456
+ xticks = self.ds.month.values
457
+ xlabel = "month"
458
+ else:
459
+ xticks = self.ds.abs_time.values
460
+ xlabel = "time"
461
+
462
+ if var_name == "river_volume":
463
+ field = self.ds[var_name]
464
+ units = f"${self.ds.river_volume.units}$"
465
+ long_name = self.ds[var_name].long_name
466
+ elif var_name == "river_temperature":
467
+ field = self.ds["river_tracer"].isel(ntracers=0)
468
+ units = "degrees C"
469
+ long_name = "River temperature"
470
+ elif var_name == "river_salinity":
471
+ field = self.ds["river_tracer"].isel(ntracers=1)
472
+ units = "psu"
473
+ long_name = "River salinity"
474
+
475
+ for i in range(len(self.ds.nriver)):
476
+
477
+ ax.plot(
478
+ xticks,
479
+ field.isel(nriver=i),
480
+ marker="x",
481
+ markersize=8,
482
+ markeredgewidth=2,
483
+ lw=2,
484
+ label=self.ds.isel(nriver=i).river_name.values,
485
+ )
486
+
487
+ ax.set_xticks(xticks)
488
+ ax.set_xlabel(xlabel)
489
+ if not self.climatology:
490
+ n = len(self.ds.river_time)
491
+ ticks = self.ds.abs_time.values[:: n // 6 + 1]
492
+ ax.set_xticks(ticks)
493
+ ax.set_ylabel(units)
494
+ ax.set_title(long_name)
495
+ ax.grid()
496
+ ax.legend(loc="center left", bbox_to_anchor=(1.1, 0.5))
497
+
498
+ def save(
499
+ self,
500
+ filepath: Union[str, Path],
501
+ filepath_grid: Union[str, Path],
502
+ np_eta: int = None,
503
+ np_xi: int = None,
504
+ ) -> None:
505
+ """Save the river forcing and grid file to netCDF4 files. The grid file is
506
+ required because a new field `river_flux` has been added.
507
+
508
+ This method allows saving the river forcing and grid data either each as a single file or each partitioned into multiple files, based on the provided options. The dataset can be saved in two modes:
509
+
510
+ 1. **Single File Mode (default)**:
511
+ - If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
512
+ - The file is named based on the provided `filepath`, with `.nc` automatically appended to the filename.
513
+
514
+ 2. **Partitioned Mode**:
515
+ - If either `np_eta` or `np_xi` is specified, the dataset is partitioned spatially along the `eta` and `xi` axes into tiles.
516
+ - Each tile is saved as a separate netCDF4 file. Filenames will be modified with an index to represent each partition, e.g., `"filepath_YYYYMM.0.nc"`, `"filepath_YYYYMM.1.nc"`, etc.
517
+
518
+ Parameters
519
+ ----------
520
+ filepath : Union[str, Path]
521
+ The base path and filename for the output files. The filenames will include the specified path and the `.nc` extension.
522
+ If partitioning is used, additional indices will be appended to the filenames, e.g., `"filepath_YYYYMM.0.nc"`, `"filepath_YYYYMM.1.nc"`, etc.
523
+
524
+ filepath_grid : Union[str, Path]
525
+ The base path and filename for saving the grid file. This file is essential for including the `river_flux` field.
526
+
527
+ np_eta : int, optional
528
+ The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed along the `eta` axis.
529
+
530
+ np_xi : int, optional
531
+ The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed along the `xi` axis.
532
+
533
+ Returns
534
+ -------
535
+ List[Path]
536
+ A list of `Path` objects for the saved files. Each element in the list corresponds to a file that was saved.
537
+ """
538
+
539
+ # Ensure filepath is a Path object
540
+ filepath = Path(filepath)
541
+ filepath_grid = Path(filepath_grid)
542
+
543
+ # Remove ".nc" suffix if present
544
+ if filepath.suffix == ".nc":
545
+ filepath = filepath.with_suffix("")
546
+ if filepath_grid.suffix == ".nc":
547
+ filepath_grid = filepath_grid.with_suffix("")
548
+
549
+ dataset_list = [self.ds, self.grid.ds]
550
+ output_filenames = [str(filepath), str(filepath_grid)]
551
+
552
+ saved_filenames = save_datasets(
553
+ dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
554
+ )
555
+
556
+ return saved_filenames
557
+
558
+ def to_yaml(self, filepath: Union[str, Path]) -> None:
559
+ """Export the parameters of the class to a YAML file, including the version of
560
+ roms-tools.
561
+
562
+ Parameters
563
+ ----------
564
+ filepath : Union[str, Path]
565
+ The path to the YAML file where the parameters will be saved.
566
+ """
567
+
568
+ _to_yaml(self, filepath)
569
+
570
+ @classmethod
571
+ def from_yaml(cls, filepath: Union[str, Path]) -> "RiverForcing":
572
+ """Create an instance of the RiverForcing class from a YAML file.
573
+
574
+ Parameters
575
+ ----------
576
+ filepath : Union[str, Path]
577
+ The path to the YAML file from which the parameters will be read.
578
+
579
+ Returns
580
+ -------
581
+ RiverForcing
582
+ An instance of the RiverForcing class.
583
+ """
584
+ filepath = Path(filepath)
585
+
586
+ grid = Grid.from_yaml(filepath)
587
+ params = _from_yaml(cls, filepath)
588
+
589
+ return cls(grid=grid, **params)