roms-tools 2.3.0__py3-none-any.whl → 2.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. ci/environment.yml +1 -0
  2. roms_tools/__init__.py +2 -1
  3. roms_tools/analysis/roms_output.py +81 -98
  4. roms_tools/plot.py +4 -2
  5. roms_tools/setup/boundary_forcing.py +207 -208
  6. roms_tools/setup/datasets.py +149 -33
  7. roms_tools/setup/grid.py +35 -102
  8. roms_tools/setup/initial_conditions.py +179 -132
  9. roms_tools/setup/nesting.py +239 -86
  10. roms_tools/setup/river_forcing.py +266 -128
  11. roms_tools/setup/surface_forcing.py +137 -76
  12. roms_tools/setup/tides.py +10 -36
  13. roms_tools/setup/topography.py +25 -2
  14. roms_tools/setup/utils.py +52 -82
  15. roms_tools/tests/test_analysis/test_roms_output.py +233 -70
  16. roms_tools/tests/test_setup/test_boundary_forcing.py +283 -57
  17. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zattrs +3 -1
  18. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zmetadata +3 -1
  19. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/0.0.0 +0 -0
  20. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/0.0.0 +0 -0
  21. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/0.0.0 +0 -0
  22. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/0.0.0 +0 -0
  23. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
  25. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
  27. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/0.0.0 +0 -0
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/0.0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/0.0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/0.0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/0.0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/0.0.0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/0.0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/0.0.0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/0.0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/0.0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/0.0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/0.0.0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/0.0.0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/0.0.0 +0 -0
  125. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
  126. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
  127. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
  128. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/0.0.0 +0 -0
  129. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
  130. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
  131. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zattrs +2 -2
  132. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zmetadata +8 -7
  133. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/abs_time/.zattrs +1 -0
  134. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust/0.0.0 +0 -0
  135. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust_time/.zattrs +1 -1
  136. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron/0.0.0 +0 -0
  137. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron_time/.zattrs +1 -1
  138. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy/0.0.0 +0 -0
  139. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy_time/.zattrs +1 -1
  140. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox/0.0.0 +0 -0
  141. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox_time/.zattrs +1 -1
  142. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air/0.0.0 +0 -0
  143. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air_alt/0.0.0 +0 -0
  144. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_time/.zattrs +1 -1
  145. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zattrs +2 -2
  146. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zmetadata +2 -2
  147. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust/0.0.0 +0 -0
  148. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron/0.0.0 +0 -0
  149. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy/0.0.0 +0 -0
  150. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox/0.0.0 +0 -0
  151. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air/0.0.0 +0 -0
  152. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air_alt/0.0.0 +0 -0
  153. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zattrs +5 -3
  154. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +156 -121
  155. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zarray +2 -2
  156. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +2 -1
  157. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/0 +0 -0
  158. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zarray +2 -2
  159. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zattrs +1 -1
  160. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/0 +0 -0
  161. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/.zarray +4 -4
  162. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
  163. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/.zarray +4 -4
  164. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/0.0.0 +0 -0
  165. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/.zarray +4 -4
  166. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
  167. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/.zarray +4 -4
  168. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/0.0.0 +0 -0
  169. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/.zarray +4 -4
  170. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
  171. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/.zarray +4 -4
  172. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/0.0.0 +0 -0
  173. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/.zarray +4 -4
  174. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
  175. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/.zarray +4 -4
  176. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/0.0.0 +0 -0
  177. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/.zarray +4 -4
  178. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
  179. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/.zarray +4 -4
  180. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/0.0.0 +0 -0
  181. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/.zarray +4 -4
  182. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
  183. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/.zarray +4 -4
  184. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
  185. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/.zarray +4 -4
  186. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
  187. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/.zarray +4 -4
  188. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/0.0 +0 -0
  189. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/.zarray +4 -4
  190. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
  191. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/.zarray +4 -4
  192. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
  193. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/.zarray +4 -4
  194. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
  195. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/.zarray +4 -4
  196. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
  197. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/.zarray +4 -4
  198. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
  199. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/.zarray +4 -4
  200. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
  201. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/.zarray +4 -4
  202. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
  203. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/.zarray +4 -4
  204. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
  205. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/.zarray +4 -4
  206. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
  207. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/.zarray +4 -4
  208. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
  209. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zarray +4 -4
  210. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +8 -0
  211. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
  212. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zarray +4 -4
  213. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +8 -0
  214. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/0.0 +0 -0
  215. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zarray +4 -4
  216. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +8 -0
  217. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
  218. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zarray +4 -4
  219. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +8 -0
  220. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/0.0 +0 -0
  221. roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +1 -1
  222. roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +2 -2
  223. roms_tools/tests/test_setup/test_data/grid.zarr/angle/0.0 +0 -0
  224. roms_tools/tests/test_setup/test_data/grid.zarr/angle_coarse/0.0 +0 -0
  225. roms_tools/tests/test_setup/test_data/grid.zarr/f/0.0 +0 -0
  226. roms_tools/tests/test_setup/test_data/grid.zarr/h/.zattrs +1 -1
  227. roms_tools/tests/test_setup/test_data/grid.zarr/h/0.0 +0 -0
  228. roms_tools/tests/test_setup/test_data/grid.zarr/lat_coarse/0.0 +0 -0
  229. roms_tools/tests/test_setup/test_data/grid.zarr/lat_rho/0.0 +0 -0
  230. roms_tools/tests/test_setup/test_data/grid.zarr/lat_u/0.0 +0 -0
  231. roms_tools/tests/test_setup/test_data/grid.zarr/lat_v/0.0 +0 -0
  232. roms_tools/tests/test_setup/test_data/grid.zarr/pm/0.0 +0 -0
  233. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +4 -4
  234. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +4 -4
  235. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
  236. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
  237. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
  238. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  239. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
  240. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
  241. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
  242. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
  243. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
  244. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
  245. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
  246. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
  247. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
  248. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
  249. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
  250. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
  251. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
  252. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
  253. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +2 -1
  254. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +6 -4
  255. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_r/.zattrs +1 -1
  256. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_w/.zattrs +1 -1
  257. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/0.0.0.0 +0 -0
  258. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/0.0.0.0 +0 -0
  259. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/0.0.0.0 +0 -0
  260. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/abs_time/.zattrs +1 -0
  261. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/0.0.0.0 +0 -0
  262. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ocean_time/.zattrs +1 -1
  263. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/0.0.0.0 +0 -0
  264. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/0.0.0.0 +0 -0
  265. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/0.0.0.0 +0 -0
  266. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/0.0.0.0 +0 -0
  267. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  268. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  269. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  270. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  271. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  272. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zeta/0.0.0 +0 -0
  273. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +56 -0
  274. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zarray +20 -0
  275. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zattrs +6 -0
  276. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/0 +0 -0
  277. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zarray +22 -0
  278. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +8 -0
  279. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/0.0 +0 -0
  280. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +56 -0
  281. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zarray +20 -0
  282. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zattrs +6 -0
  283. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/0 +0 -0
  284. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zarray +22 -0
  285. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +8 -0
  286. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/0.0 +0 -0
  287. roms_tools/tests/test_setup/test_grid.py +0 -13
  288. roms_tools/tests/test_setup/test_initial_conditions.py +220 -66
  289. roms_tools/tests/test_setup/test_nesting.py +139 -118
  290. roms_tools/tests/test_setup/test_river_forcing.py +583 -293
  291. roms_tools/tests/test_setup/test_surface_forcing.py +149 -73
  292. roms_tools/tests/test_setup/test_tides.py +4 -16
  293. roms_tools/tests/test_setup/test_utils.py +1 -0
  294. roms_tools/tests/test_setup/test_validation.py +34 -2
  295. roms_tools/tests/{test_utils.py → test_tiling/test_partition.py} +1 -1
  296. roms_tools/tiling/partition.py +338 -0
  297. roms_tools/utils.py +66 -333
  298. roms_tools/vertical_coordinate.py +54 -133
  299. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/METADATA +1 -1
  300. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/RECORD +303 -290
  301. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/WHEEL +1 -1
  302. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/LICENSE +0 -0
  303. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/top_level.txt +0 -0
@@ -4,18 +4,19 @@ import logging
4
4
  from dataclasses import dataclass, field
5
5
  import cartopy.crs as ccrs
6
6
  from datetime import datetime
7
- from typing import Dict, Union, List
7
+ from typing import Dict, Union, List, Optional
8
8
  from pathlib import Path
9
9
  import matplotlib.pyplot as plt
10
+ import matplotlib.cm as cm
10
11
  from roms_tools import Grid
11
12
  from roms_tools.plot import _get_projection, _add_field_to_ax
13
+ from roms_tools.utils import save_datasets
12
14
  from roms_tools.setup.datasets import DaiRiverDataset
13
15
  from roms_tools.setup.utils import (
14
16
  get_target_coords,
15
17
  gc_dist,
16
18
  substitute_nans_by_fillvalue,
17
19
  convert_to_roms_time,
18
- save_datasets,
19
20
  _to_yaml,
20
21
  _from_yaml,
21
22
  get_variable_metadata,
@@ -57,6 +58,24 @@ class RiverForcing:
57
58
  Whether to include BGC tracers. Defaults to `False`.
58
59
  model_reference_date : datetime, optional
59
60
  Reference date for the model. Default is January 1, 2000.
61
+ indices : dict[str, list[tuple]], optional
62
+ A dictionary specifying the river indices for each river to be included in the river forcing. This parameter is optional. If not provided,
63
+ the river indices will be automatically determined based on the grid and the source dataset. If provided, it allows for explicit specification
64
+ of river locations. The dictionary structure consists of river names as keys, and each value is a list of tuples. Each tuple represents
65
+ a pair of indices corresponding to the `eta_rho` and `xi_rho` grid coordinates of the river.
66
+
67
+ Example:
68
+ indices = {
69
+ 'Hvita(Olfusa)': [(8, 6), (7, 6)],
70
+ 'Thjorsa': [(8, 6)],
71
+ 'JkulsFjll': [(11, 12)],
72
+ 'Lagarfljot': [(9, 13), (8, 13), (10, 13)],
73
+ 'Bruara': [(8, 6)],
74
+ 'Svarta': [(12, 9)]
75
+ }
76
+
77
+ In the example, the dictionary provides the river names as keys, and the values are lists of tuples, where each tuple represents the
78
+ `(eta_rho, xi_rho)` indices for a river location.
60
79
 
61
80
  Attributes
62
81
  ----------
@@ -64,6 +83,9 @@ class RiverForcing:
64
83
  The xarray Dataset containing the river forcing data.
65
84
  climatology : bool
66
85
  Indicates whether the final river forcing is climatological.
86
+ Dict[str, Union[int, List[int]]]
87
+ A dictionary of river indices. If not provided during initialization, it will be automatically determined
88
+ based on the grid and the source dataset. The dictionary structure is the same as described in the `indices` parameter docstring.
67
89
  """
68
90
 
69
91
  grid: Grid
@@ -73,39 +95,47 @@ class RiverForcing:
73
95
  convert_to_climatology: str = "if_any_missing"
74
96
  include_bgc: bool = False
75
97
  model_reference_date: datetime = datetime(2000, 1, 1)
98
+ indices: Optional[Dict[str, Dict[str, Union[int, List[int]]]]] = None
76
99
 
77
100
  ds: xr.Dataset = field(init=False, repr=False)
78
101
  climatology: xr.Dataset = field(init=False, repr=False)
79
102
 
80
103
  def __post_init__(self):
81
104
  self._input_checks()
82
- target_coords = get_target_coords(self.grid)
83
- # maximum dx in grid
84
- dx = (
85
- np.sqrt((1 / self.grid.ds.pm) ** 2 + (1 / self.grid.ds.pn) ** 2) / 2
86
- ).max()
87
-
88
105
  data = self._get_data()
89
106
 
90
- original_indices = data.extract_relevant_rivers(target_coords, dx)
91
- object.__setattr__(self, "original_indices", original_indices)
107
+ if self.indices is None:
108
+ logging.info(
109
+ "No river indices provided. Identify all rivers within the ROMS domain and assign each of them to the nearest coastal point."
110
+ )
111
+ target_coords = get_target_coords(self.grid)
112
+ # maximum dx in grid
113
+ dx = (
114
+ np.sqrt((1 / self.grid.ds.pm) ** 2 + (1 / self.grid.ds.pn) ** 2) / 2
115
+ ).max()
116
+ original_indices = data.extract_relevant_rivers(target_coords, dx)
117
+ if len(original_indices) == 0:
118
+ raise ValueError(
119
+ "No relevant rivers found. Consider increasing domain size or using a different river dataset."
120
+ )
121
+ object.__setattr__(self, "original_indices", original_indices)
122
+ updated_indices = self._move_rivers_to_closest_coast(target_coords, data)
123
+ object.__setattr__(self, "indices", updated_indices)
92
124
 
93
- if len(original_indices["station"]) > 0:
94
- self._move_rivers_to_closest_coast(target_coords, data)
95
- ds = self._create_river_forcing(data)
96
- self._validate(ds)
125
+ else:
126
+ logging.info("Use provided river indices.")
127
+ object.__setattr__(self, "original_indices", self.indices)
128
+ check_river_locations_are_along_coast(self.grid.ds.mask_rho, self.indices)
129
+ data.extract_named_rivers(self.indices)
97
130
 
98
- for var_name in ds.data_vars:
99
- ds[var_name] = substitute_nans_by_fillvalue(
100
- ds[var_name], fill_value=0.0
101
- )
131
+ ds = self._create_river_forcing(data)
132
+ ds = self._write_indices_into_dataset(ds)
133
+ self._validate(ds)
102
134
 
103
- object.__setattr__(self, "ds", ds)
135
+ for var_name in ds.data_vars:
136
+ ds[var_name] = substitute_nans_by_fillvalue(ds[var_name], fill_value=0.0)
104
137
 
105
- else:
106
- raise ValueError(
107
- "No relevant rivers found. Consider increasing domain size or using a different river dataset."
108
- )
138
+ object.__setattr__(self, "ds", ds)
109
139
 
110
140
  def _input_checks(self):
111
141
  if self.source is None:
@@ -124,6 +154,63 @@ class RiverForcing:
124
154
  {**self.source, "climatology": self.source.get("climatology", False)},
125
155
  )
126
156
 
157
+ # Check if 'indices' is provided and has the correct format
158
+ if self.indices is not None:
159
+ if not isinstance(self.indices, dict):
160
+ raise ValueError("`indices` must be a dictionary.")
161
+
162
+ # Ensure the dictionary contains at least one river
163
+ if len(self.indices) == 0:
164
+ raise ValueError(
165
+ "The provided 'indices' dictionary must contain at least one river."
166
+ )
167
+
168
+ for river_name, river_data in self.indices.items():
169
+ if not isinstance(river_name, str):
170
+ raise ValueError(f"River name `{river_name}` must be a string.")
171
+
172
+ if not isinstance(river_data, list):
173
+ raise ValueError(
174
+ f"Data for river `{river_name}` must be a list of tuples."
175
+ )
176
+
177
+ # Ensure each element in the list is a tuple of length 2
178
+ seen_tuples = set()
179
+ for idx_pair in river_data:
180
+ if not isinstance(idx_pair, tuple) or len(idx_pair) != 2:
181
+ raise ValueError(
182
+ f"Each item for river `{river_name}` must be a tuple of length 2 representing (eta_rho, xi_rho)."
183
+ )
184
+
185
+ eta_rho, xi_rho = idx_pair
186
+
187
+ # Ensure both eta_rho and xi_rho are integers
188
+ if not isinstance(eta_rho, int):
189
+ raise ValueError(
190
+ f"First element of tuple for river `{river_name}` must be an integer (eta_rho), but got {type(eta_rho)}."
191
+ )
192
+ if not isinstance(xi_rho, int):
193
+ raise ValueError(
194
+ f"Second element of tuple for river `{river_name}` must be an integer (xi_rho), but got {type(xi_rho)}."
195
+ )
196
+
197
+ # Check that eta_rho and xi_rho are within the valid range
198
+ if not (0 <= eta_rho < len(self.grid.ds.eta_rho)):
199
+ raise ValueError(
200
+ f"Value of eta_rho for river `{river_name}` ({eta_rho}) is out of valid range [0, {len(self.grid.ds.eta_rho)-1}]."
201
+ )
202
+ if not (0 <= xi_rho < len(self.grid.ds.xi_rho)):
203
+ raise ValueError(
204
+ f"Value of xi_rho for river `{river_name}` ({xi_rho}) is out of valid range [0, {len(self.grid.ds.xi_rho)-1}]."
205
+ )
206
+
207
+ # Check for duplicate tuples
208
+ if idx_pair in seen_tuples:
209
+ raise ValueError(
210
+ f"Duplicate location {idx_pair} found for river `{river_name}`."
211
+ )
212
+ seen_tuples.add(idx_pair)
213
+
127
214
  def _get_data(self):
128
215
 
129
216
  data_dict = {
@@ -196,13 +283,19 @@ class RiverForcing:
196
283
  river_volume = river_volume.rename(
197
284
  {data.dim_names["time"]: "river_time", data.dim_names["station"]: "nriver"}
198
285
  )
286
+
199
287
  name = data.ds[data.var_names["name"]].rename(
200
288
  {data.dim_names["station"]: "nriver"}
201
289
  )
202
290
  name.attrs["long_name"] = "River name"
203
291
  river_volume.coords["river_name"] = name
292
+
204
293
  ds["river_volume"] = river_volume
205
294
 
295
+ nriver = xr.DataArray(np.arange(1, len(ds.nriver) + 1), dims="nriver")
296
+ nriver.attrs["long_name"] = "River ID (1-based Fortran indexing)"
297
+ ds = ds.assign_coords({"nriver": nriver})
298
+
206
299
  if self.include_bgc:
207
300
  ntracers = 2 + 32
208
301
  else:
@@ -271,15 +364,13 @@ class RiverForcing:
271
364
 
272
365
  ds = ds.assign_coords({"river_time": time})
273
366
 
274
- ds = ds.drop_vars("nriver")
275
-
276
367
  return ds
277
368
 
278
369
  def _move_rivers_to_closest_coast(self, target_coords, data):
279
370
  """Move river mouths to the closest coastal grid cell.
280
371
 
281
372
  This method computes the closest coastal grid point to each river mouth
282
- based on geographical distance.
373
+ based on geographical distance. It identifies the nearest grid point on the coast and returns the updated river mouth indices.
283
374
 
284
375
  Parameters:
285
376
  -----------
@@ -295,11 +386,11 @@ class RiverForcing:
295
386
  - `var_names`: A dictionary of variable names in the dataset (e.g., longitude, latitude, station names).
296
387
  - `dim_names`: A dictionary containing dimension names for the dataset (e.g., "station", "eta_rho", "xi_rho").
297
388
 
298
- Returns:
299
- --------
300
- None
301
- This method modifies the `self.updated_indices` attribute and writes the updated indices
302
- of the river mouths to the grid file using `write_indices_into_grid_file`.
389
+ Returns
390
+ -------
391
+ indices : dict[str, list[tuple]]
392
+ A dictionary consisting of river names as keys, and each value is a list of tuples. Each tuple represents
393
+ a pair of indices corresponding to the `eta_rho` and `xi_rho` grid coordinates of the river.
303
394
  """
304
395
 
305
396
  # Retrieve longitude and latitude of river mouths
@@ -332,62 +423,63 @@ class RiverForcing:
332
423
 
333
424
  # Find the indices of the closest coastal grid cell to the river mouth
334
425
  indices = np.where(dist_coast == dist_coast_min)
426
+ stations = indices[0]
427
+ eta_rho_values = indices[1]
428
+ xi_rho_values = indices[2]
335
429
  names = (
336
430
  data.ds[data.var_names["name"]]
337
- .isel({data.dim_names["station"]: indices[0]})
431
+ .isel({data.dim_names["station"]: stations})
338
432
  .values
339
433
  )
340
-
341
434
  # Return the indices in a dictionary format
342
- indices = {
343
- "station": indices[0],
344
- "eta_rho": indices[1],
345
- "xi_rho": indices[2],
346
- "name": names,
347
- }
348
- self._write_indices_into_grid_file(indices)
349
- object.__setattr__(self, "updated_indices", indices)
435
+ river_indices = {}
436
+ for i in range(len(stations)):
437
+ river_name = names[i]
438
+ river_indices[river_name] = [
439
+ (int(eta_rho_values[i]), int(xi_rho_values[i]))
440
+ ] # list of tuples
350
441
 
351
- def _write_indices_into_grid_file(self, indices):
352
- """Writes river location indices into the grid dataset as the "river_flux"
353
- variable.
442
+ return river_indices
354
443
 
355
- This method checks if the variable "river_flux" already exists in the grid dataset.
356
- If it does, the method removes it. Then, it creates a new variable called "river_flux"
357
- based on the provided `indices` and assigns it to the dataset. The `indices` dictionary
358
- provides information about the river station locations and their corresponding grid
359
- cell indices (eta_rho and xi_rho).
444
+ def _write_indices_into_dataset(self, ds):
445
+ """Adds river location indices to the dataset as the "river_location" variable.
360
446
 
361
- Parameters:
362
- -----------
363
- indices : dict
364
- A dictionary containing information about river station locations.
365
- It must contain the following keys:
366
- - "name" (list or array): Names of the river stations.
367
- - "station" (list or array): River station identifiers.
368
- - "eta_rho" (list or array): The eta (row) index for each river location.
369
- - "xi_rho" (list or array): The xi (column) index for each river location.
370
-
371
- Returns:
372
- --------
373
- None
374
- This method modifies the grid's dataset in-place by adding the "river_flux" variable.
447
+ This method creates a new "river_location" variable
448
+ using river station indices from `self.indices` and assigns it to the dataset.
449
+ The indices specify the river station locations in terms of eta_rho and xi_rho grid cell indices.
450
+
451
+ Parameters
452
+ ----------
453
+ ds : xarray.Dataset
454
+ The dataset to which the "river_location" variable will be added.
455
+
456
+ Returns
457
+ -------
458
+ xarray.Dataset
459
+ The modified dataset with the "river_location" variable added.
375
460
  """
376
461
 
377
- if "river_flux" in self.grid.ds:
378
- ds = self.grid.ds.drop_vars("river_flux")
379
- object.__setattr__(self.grid, "ds", ds)
462
+ river_locations = xr.zeros_like(self.grid.ds.h)
463
+
464
+ for nriver in ds.nriver:
465
+ river_name = str(ds.river_name.sel(nriver=nriver).values)
466
+ indices = self.indices[river_name]
467
+ fraction = 1.0 / len(indices)
468
+
469
+ for eta_index, xi_index in indices:
380
470
 
381
- river_locations = xr.zeros_like(self.grid.ds.mask_rho)
382
- for i in range(len(indices["name"])):
383
- station = indices["station"][i]
384
- eta_index = indices["eta_rho"][i]
385
- xi_index = indices["xi_rho"][i]
386
- river_locations[eta_index, xi_index] = station + 2
471
+ river_locations[eta_index, xi_index] = (
472
+ nriver # assign unique nriver ID (Fortran-based indexing)
473
+ + fraction # Fractional contribution for multiple grid points
474
+ )
387
475
 
388
- river_locations.attrs["long_name"] = "River volume flux partition"
476
+ river_locations.attrs["long_name"] = "River ID plus local volume fraction"
389
477
  river_locations.attrs["units"] = "none"
390
- self.grid.ds["river_flux"] = river_locations
478
+ ds["river_location"] = river_locations
479
+
480
+ ds = ds.drop_vars(["lat_rho", "lon_rho"])
481
+
482
+ return ds
391
483
 
392
484
  def _validate(self, ds):
393
485
  """Validates the dataset by checking for NaN values in river forcing data.
@@ -467,26 +559,46 @@ class RiverForcing:
467
559
  "color": "black",
468
560
  } # Customize latitude label style
469
561
 
470
- for ax, indices in zip(axs, [self.original_indices, self.updated_indices]):
471
- for i in range(len(indices["name"])):
472
- name = indices["name"][i]
473
- xi_index = indices["xi_rho"][i]
474
- eta_index = indices["eta_rho"][i]
475
- # transform coordinates to projected space
476
- proj = ccrs.PlateCarree()
477
- transformed_lon, transformed_lat = trans.transform_point(
478
- self.grid.ds.lon_rho[eta_index, xi_index],
479
- self.grid.ds.lat_rho[eta_index, xi_index],
480
- proj,
481
- )
482
- ax.plot(
483
- transformed_lon,
484
- transformed_lat,
485
- marker="x",
486
- markersize=8,
487
- markeredgewidth=2,
488
- label=name,
489
- )
562
+ proj = ccrs.PlateCarree()
563
+
564
+ if len(self.indices) <= 10:
565
+ color_map = cm.get_cmap("tab10")
566
+ elif len(self.indices) <= 20:
567
+ color_map = cm.get_cmap("tab20")
568
+ else:
569
+ color_map = cm.get_cmap("tab20b")
570
+ # Create a dictionary of colors
571
+ colors = {name: color_map(i) for i, name in enumerate(self.indices.keys())}
572
+
573
+ for ax, indices in zip(axs, [self.original_indices, self.indices]):
574
+ added_labels = set()
575
+ for name in indices.keys():
576
+ for tuple in indices[name]:
577
+ eta_index = tuple[0]
578
+ xi_index = tuple[1]
579
+
580
+ # transform coordinates to projected space
581
+ transformed_lon, transformed_lat = trans.transform_point(
582
+ self.grid.ds.lon_rho[eta_index, xi_index],
583
+ self.grid.ds.lat_rho[eta_index, xi_index],
584
+ proj,
585
+ )
586
+
587
+ if name not in added_labels:
588
+ added_labels.add(name)
589
+ label = name
590
+ else:
591
+ label = "_None"
592
+
593
+ ax.plot(
594
+ transformed_lon,
595
+ transformed_lat,
596
+ marker="x",
597
+ markersize=8,
598
+ markeredgewidth=2,
599
+ label=label,
600
+ color=colors[name],
601
+ )
490
602
 
491
603
  axs[0].set_title("Original river locations")
492
604
  axs[1].set_title("Updated river locations")
@@ -592,37 +704,13 @@ class RiverForcing:
592
704
  def save(
593
705
  self,
594
706
  filepath: Union[str, Path],
595
- filepath_grid: Union[str, Path],
596
- np_eta: int = None,
597
- np_xi: int = None,
598
707
  ) -> None:
599
- """Save the river forcing and grid file to netCDF4 files. The grid file is
600
- required because a new field `river_flux` has been added.
601
-
602
- 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:
603
-
604
- 1. **Single File Mode (default)**:
605
- - If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
606
- - The file is named based on the provided `filepath`, with `.nc` automatically appended to the filename.
607
-
608
- 2. **Partitioned Mode**:
609
- - If either `np_eta` or `np_xi` is specified, the dataset is partitioned spatially along the `eta` and `xi` axes into tiles.
610
- - 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.
708
+ """Save the river forcing to netCDF4 file.
611
709
 
612
710
  Parameters
613
711
  ----------
614
712
  filepath : Union[str, Path]
615
- The base path and filename for the output files. The filenames will include the specified path and the `.nc` extension.
616
- If partitioning is used, additional indices will be appended to the filenames, e.g., `"filepath.0.nc"`, `"filepath.1.nc"`, etc.
617
-
618
- filepath_grid : Union[str, Path]
619
- The base path and filename for saving the grid file.
620
-
621
- np_eta : int, optional
622
- The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed along the `eta` axis.
623
-
624
- np_xi : int, optional
625
- The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed along the `xi` axis.
713
+ The base path and filename for the output files.
626
714
 
627
715
  Returns
628
716
  -------
@@ -632,20 +720,15 @@ class RiverForcing:
632
720
 
633
721
  # Ensure filepath is a Path object
634
722
  filepath = Path(filepath)
635
- filepath_grid = Path(filepath_grid)
636
723
 
637
724
  # Remove ".nc" suffix if present
638
725
  if filepath.suffix == ".nc":
639
726
  filepath = filepath.with_suffix("")
640
- if filepath_grid.suffix == ".nc":
641
- filepath_grid = filepath_grid.with_suffix("")
642
727
 
643
- dataset_list = [self.ds, self.grid.ds]
644
- output_filenames = [str(filepath), str(filepath_grid)]
728
+ dataset_list = [self.ds]
729
+ output_filenames = [str(filepath)]
645
730
 
646
- saved_filenames = save_datasets(
647
- dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
648
- )
731
+ saved_filenames = save_datasets(dataset_list, output_filenames)
649
732
 
650
733
  return saved_filenames
651
734
 
@@ -680,4 +763,59 @@ class RiverForcing:
680
763
  grid = Grid.from_yaml(filepath)
681
764
  params = _from_yaml(cls, filepath)
682
765
 
766
+ def convert_indices_format(indices):
767
+ # Remove the '_convention' key from the dictionary if present
768
+ indices = {
769
+ key: value for key, value in indices.items() if key != "_convention"
770
+ }
771
+
772
+ # Convert the string of indices into tuples
773
+ for river, index_list in indices.items():
774
+ # Split the string by ',' and convert to tuples of integers
775
+ indices[river] = [tuple(map(int, idx.split(","))) for idx in index_list]
776
+
777
+ return indices
778
+
779
+ params["indices"] = convert_indices_format(params["indices"])
780
+
683
781
  return cls(grid=grid, **params)
782
+
783
+
784
+ def check_river_locations_are_along_coast(mask, indices):
785
+ """Check if the river locations are along the coast.
786
+
787
+ This function checks if the river locations specified in the `indices` dictionary are located on coastal grid cells.
788
+ A coastal grid cell is defined as a land grid cell adjacent to an ocean grid cell.
789
+
790
+ Parameters
791
+ ----------
792
+ mask : xarray.DataArray
793
+ A mask representing the land and ocean cells in the grid, where 1 represents ocean and 0 represents land.
794
+
795
+ indices : dict
796
+ A dictionary where the keys are river names, and the values are dictionaries containing the river's grid locations (`eta_rho` and `xi_rho`).
797
+ Each entry should have keys `"eta_rho"` and `"xi_rho"`, which are lists of grid cell indices representing river mouth locations.
798
+
799
+ Raises
800
+ ------
801
+ ValueError
802
+ If any river is not located on the coast.
803
+ """
804
+
805
+ faces = (
806
+ mask.shift(eta_rho=1)
807
+ + mask.shift(eta_rho=-1)
808
+ + mask.shift(xi_rho=1)
809
+ + mask.shift(xi_rho=-1)
810
+ )
811
+ coast = (1 - mask) * (faces > 0)
812
+
813
+ for key, river_data in indices.items():
814
+ for idx_pair in river_data:
815
+ eta_rho, xi_rho = idx_pair
816
+
817
+ # Check if the river location is along the coast
818
+ if not coast[eta_rho, xi_rho]:
819
+ raise ValueError(
820
+ f"River `{key}` is not located on the coast at grid cell ({eta_rho}, {xi_rho})."
821
+ )