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
@@ -1,34 +1,37 @@
1
+ import time
2
+ import logging
1
3
  import xarray as xr
2
4
  import numpy as np
3
5
  import gcm_filters
4
- from scipy.interpolate import RegularGridInterpolator
5
- from scipy.ndimage import label
6
- from roms_tools.setup.download import fetch_topo
7
- from roms_tools.setup.utils import interpolate_from_rho_to_u, interpolate_from_rho_to_v
6
+ from roms_tools.setup.utils import handle_boundaries
8
7
  import warnings
9
8
  from itertools import count
10
-
11
-
12
- def _add_topography_and_mask(
13
- ds, topography_source, hmin, smooth_factor=8.0, rmax=0.2
9
+ from roms_tools.setup.datasets import ETOPO5Dataset, SRTM15Dataset
10
+ from roms_tools.setup.regrid import LateralRegrid
11
+
12
+
13
+ def _add_topography(
14
+ ds,
15
+ target_coords,
16
+ topography_source,
17
+ hmin,
18
+ smooth_factor=8.0,
19
+ rmax=0.2,
20
+ verbose=False,
14
21
  ) -> xr.Dataset:
15
- """Adds topography and a land/water mask to the dataset based on the provided
16
- topography source.
17
-
18
- This function performs the following operations:
19
- 1. Interpolates topography data onto the desired grid.
20
- 2. Applies a mask based on ocean depth.
21
- 3. Smooths the topography globally to reduce grid-scale instabilities.
22
- 4. Fills enclosed basins with land.
23
- 5. Smooths the topography locally to ensure the steepness ratio satisfies the rmax criterion.
24
- 6. Adds topography metadata.
22
+ """Adds topography to the dataset based on the provided topography source.
25
23
 
26
24
  Parameters
27
25
  ----------
28
26
  ds : xr.Dataset
29
- The dataset to which topography and the land/water mask will be added.
30
- topography_source : str
31
- The source of the topography data.
27
+ The dataset to which topography will be added.
28
+ topography_source : Dict[str, Union[str, Path]], optional
29
+ Dictionary specifying the source of the topography data:
30
+
31
+ - "name" (str): The name of the topography data source (e.g., "SRTM15").
32
+ - "path" (Union[str, Path, List[Union[str, Path]]]): The path to the raw data file. Can be a string or a Path object.
33
+
34
+ The default is "ETOPO5", which does not require a path.
32
35
  hmin : float
33
36
  The minimum allowable depth for the topography.
34
37
  smooth_factor : float, optional
@@ -39,81 +42,93 @@ def _add_topography_and_mask(
39
42
  The maximum allowable steepness ratio for the topography smoothing.
40
43
  This parameter controls the local smoothing of the topography. Smaller values result in
41
44
  smoother topography, while larger values preserve more detail. The default is 0.2.
45
+ verbose: bool, optional
46
+ Indicates whether to print topography generation steps with timing. Defaults to False.
42
47
 
43
48
  Returns
44
49
  -------
45
50
  xr.Dataset
46
- The dataset with added topography, mask, and metadata.
51
+ Updated dataset with added topography and metadata.
47
52
  """
48
53
 
49
- lon = ds.lon_rho.values
50
- lat = ds.lat_rho.values
54
+ if verbose:
55
+ start_time = time.time()
56
+ data = _get_topography_data(topography_source)
57
+ if verbose:
58
+ logging.info(
59
+ f"Reading the topography data: {time.time() - start_time:.3f} seconds"
60
+ )
51
61
 
52
62
  # interpolate topography onto desired grid
53
- hraw = _make_raw_topography(lon, lat, topography_source)
54
- hraw = xr.DataArray(data=hraw, dims=["eta_rho", "xi_rho"])
55
-
56
- # Mask is obtained by finding locations where ocean depth is positive
57
- mask = xr.where(hraw > 0, 1.0, 0.0)
63
+ hraw = _make_raw_topography(data, target_coords, verbose=verbose)
64
+ nan_check(hraw)
58
65
 
59
66
  # smooth topography domain-wide with Gaussian kernel to avoid grid scale instabilities
67
+ if verbose:
68
+ start_time = time.time()
60
69
  hraw = _smooth_topography_globally(hraw, smooth_factor)
61
-
62
- # fill enclosed basins with land
63
- mask = _fill_enclosed_basins(mask.values)
64
-
65
- # adjust mask boundaries by copying values from adjacent cells
66
- mask = _handle_boundaries(mask)
67
-
68
- ds["mask_rho"] = xr.DataArray(mask.astype(np.int32), dims=("eta_rho", "xi_rho"))
69
- ds["mask_rho"].attrs = {
70
- "long_name": "Mask at rho-points",
71
- "units": "land/water (0/1)",
72
- }
73
-
74
- ds = _add_velocity_masks(ds)
70
+ if verbose:
71
+ logging.info(
72
+ f"Smoothing the topography globally: {time.time() - start_time:.3f} seconds"
73
+ )
75
74
 
76
75
  # smooth topography locally to satisfy r < rmax
76
+ if verbose:
77
+ start_time = time.time()
78
+ # inserting hraw * mask_rho into this function eliminates any inconsistencies between
79
+ # the land according to the topography and the land according to the mask; land points
80
+ # will always be set to hmin
77
81
  ds["h"] = _smooth_topography_locally(hraw * ds["mask_rho"], hmin, rmax)
78
82
  ds["h"].attrs = {
79
- "long_name": "Final bathymetry at rho-points",
83
+ "long_name": "Bathymetry at rho-points",
80
84
  "units": "meter",
81
85
  }
86
+ if verbose:
87
+ logging.info(
88
+ f"Smoothing the topography locally: {time.time() - start_time:.3f} seconds"
89
+ )
82
90
 
83
91
  ds = _add_topography_metadata(ds, topography_source, smooth_factor, hmin, rmax)
84
92
 
85
93
  return ds
86
94
 
87
95
 
88
- def _make_raw_topography(lon, lat, topography_source) -> np.ndarray:
89
- """Given a grid of (lon, lat) points, fetch the topography file and interpolate
90
- height values onto the desired grid."""
96
+ def _get_topography_data(source):
91
97
 
92
- topo_ds = fetch_topo(topography_source)
98
+ kwargs = {"use_dask": False}
93
99
 
94
- # the following will depend on the topography source
95
- if topography_source == "ETOPO5":
96
- topo_lon = topo_ds["topo_lon"].copy()
97
- # Modify longitude values where necessary
98
- topo_lon = xr.where(topo_lon < 0, topo_lon + 360, topo_lon)
99
- topo_lon_minus360 = topo_lon - 360
100
- topo_lon_plus360 = topo_lon + 360
101
- # Concatenate along the longitude axis
102
- topo_lon_concatenated = xr.concat(
103
- [topo_lon_minus360, topo_lon, topo_lon_plus360], dim="lon"
104
- )
105
- topo_concatenated = xr.concat(
106
- [-topo_ds["topo"], -topo_ds["topo"], -topo_ds["topo"]], dim="lon"
100
+ if source["name"] == "ETOPO5":
101
+ if "path" in source.keys():
102
+ kwargs["filename"] = source["path"]
103
+ data = ETOPO5Dataset(**kwargs)
104
+ elif source["name"] == "SRTM15":
105
+ kwargs["filename"] = source["path"]
106
+ data = SRTM15Dataset(**kwargs)
107
+ else:
108
+ raise ValueError(
109
+ 'Only "ETOPO5" and "SRTM15" are valid options for topography_source["name"].'
107
110
  )
108
111
 
109
- interp = RegularGridInterpolator(
110
- (topo_ds["topo_lat"].values, topo_lon_concatenated.values),
111
- topo_concatenated.values,
112
- method="linear",
112
+ return data
113
+
114
+
115
+ def _make_raw_topography(
116
+ data, target_coords, method="linear", verbose=False
117
+ ) -> xr.DataArray:
118
+
119
+ data.choose_subdomain(target_coords, buffer_points=3, verbose=verbose)
120
+
121
+ if verbose:
122
+ start_time = time.time()
123
+ lateral_regrid = LateralRegrid(target_coords, data.dim_names)
124
+ hraw = lateral_regrid.apply(data.ds[data.var_names["topo"]], method=method)
125
+ if verbose:
126
+ logging.info(
127
+ f"Regridding the topography: {time.time() - start_time:.3f} seconds"
113
128
  )
114
129
 
115
- # Interpolate onto desired domain grid points
116
- hraw = interp((lat, lon))
130
+ # flip sign so that bathmetry is positive
131
+ hraw = -hraw
117
132
 
118
133
  return hraw
119
134
 
@@ -148,28 +163,6 @@ def _smooth_topography_globally(hraw, factor) -> xr.DataArray:
148
163
  return hsmooth
149
164
 
150
165
 
151
- def _fill_enclosed_basins(mask) -> np.ndarray:
152
- """Fills in enclosed basins with land."""
153
-
154
- # Label connected regions in the mask
155
- reg, nreg = label(mask)
156
- # Find the largest region
157
- lint = 0
158
- lreg = 0
159
- for ireg in range(nreg):
160
- int_ = np.sum(reg == ireg)
161
- if int_ > lint and mask[reg == ireg].sum() > 0:
162
- lreg = ireg
163
- lint = int_
164
-
165
- # Remove regions other than the largest one
166
- for ireg in range(nreg):
167
- if ireg != lreg:
168
- mask[reg == ireg] = 0
169
-
170
- return mask
171
-
172
-
173
166
  def _smooth_topography_locally(h, hmin=5, rmax=0.2):
174
167
  """Smoothes topography locally to satisfy r < rmax."""
175
168
  # Compute rmax_log
@@ -232,7 +225,7 @@ def _smooth_topography_locally(h, hmin=5, rmax=0.2):
232
225
  )
233
226
 
234
227
  # No gradient at the domain boundaries
235
- h_log = _handle_boundaries(h_log)
228
+ h_log = handle_boundaries(h_log)
236
229
 
237
230
  # Update h
238
231
  h = hmin * np.exp(h_log)
@@ -248,29 +241,6 @@ def _smooth_topography_locally(h, hmin=5, rmax=0.2):
248
241
  return h
249
242
 
250
243
 
251
- def _handle_boundaries(field):
252
- """Adjust the boundaries of a 2D field by copying values from adjacent cells.
253
-
254
- Parameters
255
- ----------
256
- field : numpy.ndarray or xarray.DataArray
257
- A 2D array representing a field (e.g., topography or mask) whose boundary values
258
- need to be adjusted.
259
-
260
- Returns
261
- -------
262
- field : numpy.ndarray or xarray.DataArray
263
- The input field with adjusted boundary values.
264
- """
265
-
266
- field[0, :] = field[1, :]
267
- field[-1, :] = field[-2, :]
268
- field[:, 0] = field[:, 1]
269
- field[:, -1] = field[:, -2]
270
-
271
- return field
272
-
273
-
274
244
  def _compute_rfactor(h):
275
245
  """Computes slope parameter (or r-factor) r = |Delta h| / 2h in both horizontal grid
276
246
  directions."""
@@ -286,23 +256,17 @@ def _compute_rfactor(h):
286
256
 
287
257
 
288
258
  def _add_topography_metadata(ds, topography_source, smooth_factor, hmin, rmax):
289
- ds.attrs["topography_source"] = topography_source
259
+ ds.attrs["topography_source"] = topography_source["name"]
290
260
  ds.attrs["hmin"] = hmin
291
261
 
292
262
  return ds
293
263
 
294
264
 
295
- def _add_velocity_masks(ds):
296
-
297
- # add u- and v-masks
298
- ds["mask_u"] = interpolate_from_rho_to_u(
299
- ds["mask_rho"], method="multiplicative"
300
- ).astype(np.int32)
301
- ds["mask_v"] = interpolate_from_rho_to_v(
302
- ds["mask_rho"], method="multiplicative"
303
- ).astype(np.int32)
304
-
305
- ds["mask_u"].attrs = {"long_name": "Mask at u-points", "units": "land/water (0/1)"}
306
- ds["mask_v"].attrs = {"long_name": "Mask at v-points", "units": "land/water (0/1)"}
307
-
308
- return ds
265
+ def nan_check(hraw):
266
+ error_message = (
267
+ "NaN values found in regridded topography. This likely occurs because the ROMS grid, including "
268
+ "a small safety margin for interpolation, is not fully contained within the topography dataset's longitude/latitude range. Please ensure that the "
269
+ "dataset covers the entire area required by the ROMS grid."
270
+ )
271
+ if hraw.isnull().any().values:
272
+ raise ValueError(error_message)
roms_tools/setup/utils.py CHANGED
@@ -1,10 +1,14 @@
1
1
  import xarray as xr
2
2
  import numpy as np
3
- from typing import Union
3
+ from typing import Union, Any, Dict, Type
4
4
  import pandas as pd
5
5
  import cftime
6
6
  from roms_tools.utils import partition
7
7
  from pathlib import Path
8
+ from datetime import datetime
9
+ from dataclasses import fields, asdict
10
+ import importlib.metadata
11
+ import yaml
8
12
 
9
13
 
10
14
  def nan_check(field, mask, error_message=None) -> None:
@@ -37,7 +41,7 @@ def nan_check(field, mask, error_message=None) -> None:
37
41
  da = xr.where(mask == 1, field, 0)
38
42
  if error_message is None:
39
43
  error_message = (
40
- "NaN values found in interpolated field. This likely occurs because the ROMS grid, including "
44
+ "NaN values found in regridded field. This likely occurs because the ROMS grid, including "
41
45
  "a small safety margin for interpolation, is not fully contained within the dataset's longitude/latitude range. Please ensure that the "
42
46
  "dataset covers the entire area required by the ROMS grid."
43
47
  )
@@ -103,10 +107,10 @@ def interpolate_from_rho_to_u(field, method="additive"):
103
107
  else:
104
108
  raise NotImplementedError(f"Unsupported method '{method}' specified.")
105
109
 
106
- if "lat_rho" in field_interpolated.coords:
107
- field_interpolated.drop_vars(["lat_rho"])
108
- if "lon_rho" in field_interpolated.coords:
109
- field_interpolated.drop_vars(["lon_rho"])
110
+ vars_to_drop = ["lat_rho", "lon_rho", "eta_rho", "xi_rho"]
111
+ for var in vars_to_drop:
112
+ if var in field_interpolated.coords:
113
+ field_interpolated = field_interpolated.drop_vars(var)
110
114
 
111
115
  field_interpolated = field_interpolated.swap_dims({"xi_rho": "xi_u"})
112
116
 
@@ -151,10 +155,10 @@ def interpolate_from_rho_to_v(field, method="additive"):
151
155
  else:
152
156
  raise NotImplementedError(f"Unsupported method '{method}' specified.")
153
157
 
154
- if "lat_rho" in field_interpolated.coords:
155
- field_interpolated.drop_vars(["lat_rho"])
156
- if "lon_rho" in field_interpolated.coords:
157
- field_interpolated.drop_vars(["lon_rho"])
158
+ vars_to_drop = ["lat_rho", "lon_rho", "eta_rho", "xi_rho"]
159
+ for var in vars_to_drop:
160
+ if var in field_interpolated.coords:
161
+ field_interpolated = field_interpolated.drop_vars(var)
158
162
 
159
163
  field_interpolated = field_interpolated.swap_dims({"eta_rho": "eta_v"})
160
164
 
@@ -729,27 +733,27 @@ def get_target_coords(grid, use_coarse_grid=False):
729
733
  """
730
734
  # Select grid variables based on whether the coarse grid is used
731
735
  if use_coarse_grid:
732
- lat, lon, angle, mask = (
733
- grid.ds.lat_coarse.rename({"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}),
734
- grid.ds.lon_coarse.rename({"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}),
735
- grid.ds.angle_coarse.rename(
736
- {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
737
- ),
738
- grid.ds.mask_coarse.rename(
739
- {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
740
- ),
736
+ lat = grid.ds.lat_coarse.rename(
737
+ {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
741
738
  )
739
+ lon = grid.ds.lon_coarse.rename(
740
+ {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
741
+ )
742
+ angle = grid.ds.angle_coarse.rename(
743
+ {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
744
+ )
745
+ mask = grid.ds.get("mask_coarse")
746
+ if mask is not None:
747
+ mask = mask.rename({"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"})
742
748
 
743
749
  lat_psi = grid.ds.get("lat_psi_coarse")
744
750
  lon_psi = grid.ds.get("lon_psi_coarse")
745
751
 
746
752
  else:
747
- lat, lon, angle, mask = (
748
- grid.ds.lat_rho,
749
- grid.ds.lon_rho,
750
- grid.ds.angle,
751
- grid.ds.mask_rho,
752
- )
753
+ lat = grid.ds.lat_rho
754
+ lon = grid.ds.lon_rho
755
+ angle = grid.ds.angle
756
+ mask = grid.ds.get("mask_rho")
753
757
  lat_psi = grid.ds.get("lat_psi")
754
758
  lon_psi = grid.ds.get("lon_psi")
755
759
 
@@ -921,3 +925,281 @@ def get_vector_pairs(variable_info):
921
925
  processed.update([var_name, vector_pair])
922
926
 
923
927
  return vector_pairs
928
+
929
+
930
+ def gc_dist(lon1, lat1, lon2, lat2):
931
+ """Calculate the great circle distance between two points on the Earth's surface.
932
+ Latitude and longitude must be provided in degrees (they will be converted to
933
+ radians).
934
+
935
+ The function uses the Haversine formula to compute the shortest distance
936
+ along the surface of a sphere (Earth), assuming the Earth is a perfect sphere.
937
+
938
+ Parameters
939
+ ----------
940
+ lon1, lat1 : float
941
+ Longitude and latitude of the first point in degrees.
942
+ lon2, lat2 : float
943
+ Longitude and latitude of the second point in degrees.
944
+
945
+ Returns
946
+ -------
947
+ dis : float
948
+ The great circle distance between the two points in meters.
949
+ This is the shortest distance along the surface of a sphere (Earth).
950
+
951
+ Notes
952
+ -----
953
+ The radius of the Earth is taken to be 6371315 meters.
954
+ """
955
+ # Convert degrees to radians
956
+ d2r = np.pi / 180
957
+ lon1 = lon1 * d2r
958
+ lat1 = lat1 * d2r
959
+ lon2 = lon2 * d2r
960
+ lat2 = lat2 * d2r
961
+
962
+ # Difference in latitudes and longitudes
963
+ dlat = lat2 - lat1
964
+ dlon = lon2 - lon1
965
+
966
+ # Haversine formula
967
+ dang = 2 * np.arcsin(
968
+ np.sqrt(
969
+ np.sin(dlat / 2) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2) ** 2
970
+ )
971
+ )
972
+
973
+ # Radius of the Earth in meters
974
+ r_earth = 6371315.0
975
+
976
+ # Distance in meters
977
+ dis = r_earth * dang
978
+
979
+ return dis
980
+
981
+
982
+ def convert_to_roms_time(ds, model_reference_date, climatology, time_name="time"):
983
+
984
+ if climatology:
985
+ ds.attrs["climatology"] = str(True)
986
+ month = xr.DataArray(range(1, 13), dims=time_name)
987
+ month.attrs["long_name"] = "Month index (1-12)"
988
+ ds = ds.assign_coords({"month": month})
989
+ # Preserve absolute time coordinate for readability
990
+ abs_time = np.datetime64(model_reference_date) + ds[time_name]
991
+ # Convert to pandas TimedeltaIndex
992
+ timedelta_index = pd.to_timedelta(ds[time_name].values)
993
+
994
+ # Determine the start of the year for the base_datetime
995
+ start_of_year = datetime(model_reference_date.year, 1, 1)
996
+
997
+ # Calculate the offset from midnight of the new year
998
+ offset = model_reference_date - start_of_year
999
+
1000
+ # Convert the timedelta to nanoseconds first, then to days
1001
+ time = xr.DataArray(
1002
+ (timedelta_index - offset).view("int64") / 3600 / 24 * 1e-9,
1003
+ dims=time_name,
1004
+ )
1005
+ time.attrs["cycle_length"] = 365.25
1006
+
1007
+ else:
1008
+ # Preserve absolute time coordinate for readability
1009
+ abs_time = ds[time_name]
1010
+
1011
+ time = (
1012
+ (ds[time_name] - np.datetime64(model_reference_date)).astype("float64")
1013
+ / 3600
1014
+ / 24
1015
+ * 1e-9
1016
+ )
1017
+
1018
+ attrs = [key for key in abs_time.attrs]
1019
+ for attr in attrs:
1020
+ del abs_time.attrs[attr]
1021
+ abs_time.attrs["long_name"] = "absolute time"
1022
+ ds = ds.assign_coords({"abs_time": abs_time})
1023
+
1024
+ time.attrs["long_name"] = f"relative time: days since {str(model_reference_date)}"
1025
+ time.encoding["units"] = "days"
1026
+ time.attrs["units"] = "days"
1027
+ ds.encoding["unlimited_dims"] = "time"
1028
+
1029
+ return ds, time
1030
+
1031
+
1032
+ def _to_yaml(forcing_object, filepath: Union[str, Path]) -> None:
1033
+ """Serialize a forcing object (including its grid) into a YAML file.
1034
+
1035
+ This function serializes a dataclass object (forcing_object) and its associated
1036
+ `grid` attribute into a YAML file. It includes additional metadata, such as
1037
+ the version of the `roms-tools` package, and omits fields like `grid` and `ds`
1038
+ that are not serializable or meant to be excluded.
1039
+
1040
+ The function also converts datetime fields to ISO format strings for proper
1041
+ serialization.
1042
+
1043
+ Parameters
1044
+ ----------
1045
+ forcing_object : object
1046
+ The object that contains the forcing data, typically a dataclass with attributes
1047
+ such as `grid`, `start_time`, `end_time`, etc.
1048
+ filepath : Union[str, Path]
1049
+ The path where the serialized YAML file will be saved.
1050
+
1051
+ Returns
1052
+ -------
1053
+ None
1054
+ The function writes the serialized data directly to a YAML file at the specified path.
1055
+ """
1056
+
1057
+ # Convert the filepath to a Path object
1058
+ filepath = Path(filepath)
1059
+
1060
+ # Step 1: Serialize Grid data
1061
+ # Convert the grid attribute to a dictionary and remove non-serializable fields
1062
+ grid_data = asdict(forcing_object.grid)
1063
+ grid_data.pop("ds", None) # Remove 'ds' attribute (non-serializable)
1064
+ grid_data.pop("straddle", None)
1065
+ grid_data.pop("verbose", None)
1066
+ grid_yaml_data = {"Grid": grid_data}
1067
+
1068
+ # Step 2: Get ROMS Tools version
1069
+ # Fetch the version of the 'roms-tools' package for inclusion in the YAML header
1070
+ try:
1071
+ roms_tools_version = importlib.metadata.version("roms-tools")
1072
+ except importlib.metadata.PackageNotFoundError:
1073
+ roms_tools_version = "unknown"
1074
+
1075
+ # Create YAML header with version information
1076
+ header = f"---\nroms_tools_version: {roms_tools_version}\n---\n"
1077
+
1078
+ # Step 3: Prepare Forcing Data
1079
+ # Prepare the forcing object fields, excluding 'grid' and 'ds'
1080
+ forcing_data = {}
1081
+ field_names = [field.name for field in fields(forcing_object)]
1082
+ filtered_field_names = [
1083
+ param
1084
+ for param in field_names
1085
+ if param not in ("grid", "ds", "use_dask", "climatology")
1086
+ ]
1087
+
1088
+ for field_name in filtered_field_names:
1089
+ # Retrieve the value of each field using getattr
1090
+ value = getattr(forcing_object, field_name)
1091
+
1092
+ # If the field is a datetime object, convert it to ISO format
1093
+ if isinstance(value, datetime):
1094
+ value = value.isoformat()
1095
+
1096
+ # Add the field and its value to the forcing_data dictionary
1097
+ forcing_data[field_name] = value
1098
+
1099
+ # Step 4: Combine Grid and Forcing Data
1100
+ # Combine grid and forcing data into a single dictionary for the final YAML content
1101
+ yaml_data = {
1102
+ **grid_yaml_data, # Add the grid data to the final YAML structure
1103
+ forcing_object.__class__.__name__: forcing_data, # Include the serialized forcing object data
1104
+ }
1105
+
1106
+ # Step 5: Write to YAML file
1107
+ with filepath.open("w") as file:
1108
+ # Write the header first
1109
+ file.write(header)
1110
+ # Write the serialized YAML data
1111
+ yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
1112
+
1113
+
1114
+ def _from_yaml(forcing_object: Type, filepath: Union[str, Path]) -> Dict[str, Any]:
1115
+ """Extract the configuration data for a given forcing object from a YAML file.
1116
+
1117
+ This function reads a YAML file, searches for the configuration data associated
1118
+ with the class name of the forcing object, and returns the configuration data
1119
+ as a dictionary. The dictionary contains the forcing parameters extracted from
1120
+ the YAML file, with any date fields converted from ISO format.
1121
+
1122
+ Parameters
1123
+ ----------
1124
+ filepath : Union[str, Path]
1125
+ The path to the YAML file from which the parameters will be read.
1126
+ forcing_object : Type
1127
+ The class type (e.g., TidalForcing) whose configuration data is to be loaded
1128
+ from the YAML file. The class name is used to locate the relevant data in
1129
+ the YAML structure.
1130
+
1131
+ Returns
1132
+ -------
1133
+ dict
1134
+ A dictionary containing the forcing parameters extracted from the YAML file.
1135
+ This dictionary contains key-value pairs where the keys are the parameter
1136
+ names, and the values are the corresponding values from the YAML file.
1137
+ Any date fields are converted from ISO format if necessary.
1138
+
1139
+ Raises
1140
+ ------
1141
+ ValueError
1142
+ If no configuration for the specified class name is found in the YAML file.
1143
+ """
1144
+
1145
+ # Read the entire file content
1146
+ with filepath.open("r") as file:
1147
+ file_content = file.read()
1148
+
1149
+ # Split the content into YAML documents
1150
+ documents = list(yaml.safe_load_all(file_content))
1151
+
1152
+ forcing_data = None
1153
+ forcing_object_name = forcing_object.__name__
1154
+
1155
+ # Process the YAML documents to find the forcing data for the given object
1156
+ for doc in documents:
1157
+ if doc is None:
1158
+ continue
1159
+ if forcing_object_name in doc:
1160
+ forcing_data = doc[forcing_object_name]
1161
+ break
1162
+
1163
+ if forcing_data is None:
1164
+ raise ValueError(
1165
+ f"No {forcing_object_name} configuration found in the YAML file."
1166
+ )
1167
+
1168
+ # Convert any date fields from ISO format if necessary
1169
+ for key, value in forcing_data.items():
1170
+ forcing_data[key] = _convert_from_iso_format(value)
1171
+
1172
+ # Return the forcing data as a dictionary
1173
+ return forcing_data
1174
+
1175
+
1176
+ def _convert_from_iso_format(value):
1177
+ try:
1178
+ # Return the parsed datetime object if successful
1179
+ return datetime.fromisoformat(str(value))
1180
+ except ValueError:
1181
+ # Return None or raise an exception if parsing fails
1182
+ return value
1183
+
1184
+
1185
+ def handle_boundaries(field):
1186
+ """Adjust the boundaries of a 2D field by copying values from adjacent cells.
1187
+
1188
+ Parameters
1189
+ ----------
1190
+ field : numpy.ndarray or xarray.DataArray
1191
+ A 2D array representing a field (e.g., topography or mask) whose boundary values
1192
+ need to be adjusted.
1193
+
1194
+ Returns
1195
+ -------
1196
+ field : numpy.ndarray or xarray.DataArray
1197
+ The input field with adjusted boundary values.
1198
+ """
1199
+
1200
+ field[0, :] = field[1, :]
1201
+ field[-1, :] = field[-2, :]
1202
+ field[:, 0] = field[:, 1]
1203
+ field[:, -1] = field[:, -2]
1204
+
1205
+ return field