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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. roms_tools/__init__.py +1 -1
  2. roms_tools/analysis/roms_output.py +77 -98
  3. roms_tools/plot.py +4 -2
  4. roms_tools/setup/boundary_forcing.py +29 -15
  5. roms_tools/setup/datasets.py +91 -32
  6. roms_tools/setup/grid.py +4 -5
  7. roms_tools/setup/initial_conditions.py +7 -6
  8. roms_tools/setup/nesting.py +237 -63
  9. roms_tools/setup/river_forcing.py +243 -72
  10. roms_tools/setup/surface_forcing.py +26 -15
  11. roms_tools/setup/tides.py +3 -6
  12. roms_tools/setup/topography.py +25 -2
  13. roms_tools/setup/utils.py +28 -12
  14. roms_tools/tests/test_analysis/test_roms_output.py +233 -70
  15. roms_tools/tests/test_setup/test_boundary_forcing.py +63 -0
  16. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zattrs +3 -1
  17. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zmetadata +3 -1
  18. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/0.0.0 +0 -0
  19. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/0.0.0 +0 -0
  20. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/0.0.0 +0 -0
  21. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/0.0.0 +0 -0
  22. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/0.0.0 +0 -0
  23. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
  25. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
  27. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/0.0.0 +0 -0
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/0.0.0 +0 -0
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/0.0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/0.0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/0.0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/0.0.0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/0.0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/0.0.0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/0.0.0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/0.0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/0.0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/0.0.0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/0.0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
  125. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
  126. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
  127. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/0.0.0 +0 -0
  128. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
  129. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
  130. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zattrs +2 -2
  131. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zmetadata +8 -7
  132. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/abs_time/.zattrs +1 -0
  133. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust/0.0.0 +0 -0
  134. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust_time/.zattrs +1 -1
  135. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron/0.0.0 +0 -0
  136. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron_time/.zattrs +1 -1
  137. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy/0.0.0 +0 -0
  138. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy_time/.zattrs +1 -1
  139. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox/0.0.0 +0 -0
  140. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox_time/.zattrs +1 -1
  141. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air/0.0.0 +0 -0
  142. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air_alt/0.0.0 +0 -0
  143. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_time/.zattrs +1 -1
  144. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zattrs +2 -2
  145. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zmetadata +2 -2
  146. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust/0.0.0 +0 -0
  147. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron/0.0.0 +0 -0
  148. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy/0.0.0 +0 -0
  149. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox/0.0.0 +0 -0
  150. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air/0.0.0 +0 -0
  151. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air_alt/0.0.0 +0 -0
  152. roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +1 -1
  153. roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +2 -2
  154. roms_tools/tests/test_setup/test_data/grid.zarr/angle/0.0 +0 -0
  155. roms_tools/tests/test_setup/test_data/grid.zarr/angle_coarse/0.0 +0 -0
  156. roms_tools/tests/test_setup/test_data/grid.zarr/f/0.0 +0 -0
  157. roms_tools/tests/test_setup/test_data/grid.zarr/h/.zattrs +1 -1
  158. roms_tools/tests/test_setup/test_data/grid.zarr/h/0.0 +0 -0
  159. roms_tools/tests/test_setup/test_data/grid.zarr/lat_coarse/0.0 +0 -0
  160. roms_tools/tests/test_setup/test_data/grid.zarr/lat_rho/0.0 +0 -0
  161. roms_tools/tests/test_setup/test_data/grid.zarr/lat_u/0.0 +0 -0
  162. roms_tools/tests/test_setup/test_data/grid.zarr/lat_v/0.0 +0 -0
  163. roms_tools/tests/test_setup/test_data/grid.zarr/pm/0.0 +0 -0
  164. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +1 -1
  165. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +1 -1
  166. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
  167. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
  168. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
  169. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  170. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
  171. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
  172. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
  173. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
  174. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
  175. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
  176. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
  177. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
  178. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
  179. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
  180. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +1 -1
  181. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +1 -1
  182. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/0.0.0.0 +0 -0
  183. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  184. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  185. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  186. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  187. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  188. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zeta/0.0.0 +0 -0
  189. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +27 -1
  190. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zarray +20 -0
  191. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zattrs +6 -0
  192. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/0 +0 -0
  193. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +1 -1
  194. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +27 -1
  195. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zarray +20 -0
  196. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zattrs +6 -0
  197. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/0 +0 -0
  198. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +1 -1
  199. roms_tools/tests/test_setup/test_initial_conditions.py +16 -0
  200. roms_tools/tests/test_setup/test_nesting.py +141 -104
  201. roms_tools/tests/test_setup/test_river_forcing.py +587 -269
  202. roms_tools/tests/test_setup/test_surface_forcing.py +47 -0
  203. roms_tools/tests/test_setup/test_validation.py +34 -2
  204. {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/METADATA +1 -1
  205. {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/RECORD +208 -202
  206. {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/WHEEL +1 -1
  207. {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/LICENSE +0 -0
  208. {roms_tools-2.4.0.dist-info → roms_tools-2.5.0.dist-info}/top_level.txt +0 -0
roms_tools/__init__.py CHANGED
@@ -14,7 +14,7 @@ from roms_tools.setup.surface_forcing import SurfaceForcing # noqa: F401
14
14
  from roms_tools.setup.initial_conditions import InitialConditions # noqa: F401
15
15
  from roms_tools.setup.boundary_forcing import BoundaryForcing # noqa: F401
16
16
  from roms_tools.setup.river_forcing import RiverForcing # noqa: F401
17
- from roms_tools.setup.nesting import Nesting # noqa: F401
17
+ from roms_tools.setup.nesting import ChildGrid # noqa: F401
18
18
  from roms_tools.tiling.partition import partition_netcdf # noqa: F401
19
19
  from roms_tools.analysis.roms_output import ROMSOutput # noqa: F401
20
20
 
@@ -5,7 +5,6 @@ from roms_tools.utils import _load_data
5
5
  from dataclasses import dataclass, field
6
6
  from typing import Union, Optional
7
7
  from pathlib import Path
8
- import os
9
8
  import re
10
9
  import logging
11
10
  from datetime import datetime, timedelta
@@ -25,34 +24,25 @@ class ROMSOutput:
25
24
  grid : Grid
26
25
  Object representing the grid information.
27
26
  path : Union[str, Path, List[Union[str, Path]]]
28
- Directory, filename, or list of filenames with model output.
29
- type : str
30
- Specifies the type of model output. Options are:
31
-
32
- - "restart": for restart files.
33
- - "average": for time-averaged files.
34
- - "snapshot": for snapshot files.
35
-
27
+ Filename, or list of filenames with model output.
36
28
  model_reference_date : datetime, optional
37
29
  If not specified, this is inferred from metadata of the model output
38
30
  If specified and does not coincide with metadata, a warning is raised.
31
+ adjust_depth_for_sea_surface_height : bool, optional
32
+ Whether to account for sea surface height variations when computing depth coordinates.
33
+ Defaults to `False`.
39
34
  use_dask: bool, optional
40
35
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
41
36
  """
42
37
 
43
38
  grid: Grid
44
39
  path: Union[str, Path]
45
- type: Union[str, Path]
46
40
  use_dask: bool = False
47
41
  model_reference_date: Optional[datetime] = None
42
+ adjust_depth_for_sea_surface_height: Optional[bool] = False
48
43
  ds: xr.Dataset = field(init=False, repr=False)
49
44
 
50
45
  def __post_init__(self):
51
- # Validate `type`
52
- if self.type not in {"restart", "average", "snapshot"}:
53
- raise ValueError(
54
- f"Invalid type '{self.type}'. Must be one of 'restart', 'average', or 'snapshot'."
55
- )
56
46
 
57
47
  ds = self._load_model_output()
58
48
  self._infer_model_reference_date_from_metadata(ds)
@@ -61,6 +51,9 @@ class ROMSOutput:
61
51
  ds = self._add_lat_lon_coords(ds)
62
52
  object.__setattr__(self, "ds", ds)
63
53
 
54
+ # Dataset for depth coordinates
55
+ object.__setattr__(self, "ds_depth_coords", xr.Dataset())
56
+
64
57
  def plot(
65
58
  self,
66
59
  var_name,
@@ -68,6 +61,7 @@ class ROMSOutput:
68
61
  s=None,
69
62
  eta=None,
70
63
  xi=None,
64
+ include_boundary=False,
71
65
  depth_contours=False,
72
66
  layer_contours=False,
73
67
  ax=None,
@@ -94,6 +88,11 @@ class ROMSOutput:
94
88
  xi : int, optional
95
89
  The xi-index to plot. Used for vertical sections or horizontal slices.
96
90
  Default is None.
91
+ include_boundary : bool, optional
92
+ Whether to include the outermost grid cells along the `eta`- and `xi`-boundaries in the plot.
93
+ In diagnostic ROMS output fields, these boundary cells are set to zero, so excluding them can improve visualization.
94
+ This option is only relevant for 2D horizontal plots (`eta=None`, `xi=None`).
95
+ Default is False.
97
96
  depth_contours : bool, optional
98
97
  If True, depth contours will be overlaid on the plot, showing lines of constant
99
98
  depth. This is typically used for plots that show a single vertical layer.
@@ -183,10 +182,18 @@ class ROMSOutput:
183
182
  compute_layer_depth = (depth_contours or s is None) and len(field.dims) > 2
184
183
  compute_interface_depth = layer_contours and s is None
185
184
 
185
+ # Compute depth coordinates directly instead of using .ds_depth_coords.
186
+ # Many cases below require only a 1D or 2D slice, making direct computation
187
+ # more efficient than triggering a full 3D depth computation just to extract a subset.
188
+ # This is especially beneficial when using Dask or if .ds_depth_coords is precomputed.
189
+ if self.adjust_depth_for_sea_surface_height:
190
+ zeta = self.ds.zeta.isel(time=time)
191
+ else:
192
+ zeta = 0
186
193
  if compute_layer_depth:
187
194
  layer_depth = compute_depth_coordinates(
188
195
  self.grid.ds,
189
- self.ds.zeta.isel(time=time),
196
+ zeta,
190
197
  depth_type="layer",
191
198
  location=loc,
192
199
  eta=eta,
@@ -197,7 +204,7 @@ class ROMSOutput:
197
204
  if compute_interface_depth:
198
205
  interface_depth = compute_depth_coordinates(
199
206
  self.grid.ds,
200
- self.ds.zeta.isel(time=time),
207
+ zeta,
201
208
  depth_type="interface",
202
209
  location=loc,
203
210
  eta=eta,
@@ -252,6 +259,32 @@ class ROMSOutput:
252
259
  if compute_layer_depth:
253
260
  field = field.assign_coords({"layer_depth": layer_depth})
254
261
 
262
+ if not include_boundary:
263
+ slice_dict = None
264
+
265
+ if eta is None and xi is None:
266
+ slice_dict = {
267
+ "rho": {"eta_rho": slice(1, -1), "xi_rho": slice(1, -1)},
268
+ "u": {"eta_rho": slice(1, -1), "xi_u": slice(1, -1)},
269
+ "v": {"eta_v": slice(1, -1), "xi_rho": slice(1, -1)},
270
+ }
271
+ elif eta is None:
272
+ slice_dict = {
273
+ "rho": {"eta_rho": slice(1, -1)},
274
+ "u": {"eta_rho": slice(1, -1)},
275
+ "v": {"eta_v": slice(1, -1)},
276
+ }
277
+ elif xi is None:
278
+ slice_dict = {
279
+ "rho": {"xi_rho": slice(1, -1)},
280
+ "u": {"xi_u": slice(1, -1)},
281
+ "v": {"xi_rho": slice(1, -1)},
282
+ }
283
+ if slice_dict is not None:
284
+ if loc in slice_dict:
285
+ field = field.isel(**slice_dict[loc])
286
+ mask = mask.isel(**slice_dict[loc])
287
+
255
288
  # Choose colorbar
256
289
  if var_name in ["u", "v", "w", "ubar", "vbar", "zeta"]:
257
290
  vmax = max(field.where(mask).max().values, -field.where(mask).min().values)
@@ -300,8 +333,8 @@ class ROMSOutput:
300
333
  else:
301
334
  _line_plot(field.where(mask), title=title, ax=ax)
302
335
 
303
- def compute_depth_coordinates(self, depth_type="layer", locations=["rho"]):
304
- """Compute and update vertical depth coordinates.
336
+ def _get_depth_coordinates(self, depth_type="layer", locations=["rho"]):
337
+ """Ensure depth coordinates are stored for a given location and depth type.
305
338
 
306
339
  Calculates vertical depth coordinates (layer or interface) for specified locations (e.g., rho, u, v points)
307
340
  and updates them in the dataset (`self.ds`).
@@ -321,61 +354,44 @@ class ROMSOutput:
321
354
 
322
355
  Updates
323
356
  -------
324
- self.ds : xarray.Dataset
325
- The dataset (`self.ds`) is updated with the following depth coordinate variables:
326
- - f"{depth_type}_depth_rho": Depth coordinates at rho points.
327
- - f"{depth_type}_depth_u": Depth coordinates at u points (if included in `locations`).
328
- - f"{depth_type}_depth_v": Depth coordinates at v points (if included in `locations`).
357
+ self.ds_depth_coords : xarray.Dataset
358
+
359
+ Raises
360
+ ------
361
+ ValueError
362
+ If `adjust_depth_for_sea_surface_height` is enabled but `zeta` is missing from `self.ds`.
329
363
 
330
364
  Notes
331
365
  -----
332
- This method uses the `compute_and_update_depth_coordinates` function to perform calculations and updates.
366
+ - This method relies on the `compute_depth_coordinates` function to perform calculations.
367
+ - If `adjust_depth_for_sea_surface_height` is `True`, the method accounts for variations
368
+ in sea surface height (`zeta`).
333
369
  """
334
370
 
335
- for location in locations:
336
- self.ds[f"{depth_type}_depth_{location}"] = compute_depth_coordinates(
337
- self.grid.ds, self.ds.zeta, depth_type, location
338
- )
339
-
340
- def _load_model_output(self) -> xr.Dataset:
341
- """Load the model output based on the type."""
342
- if isinstance(self.path, list):
343
- filetype = "list"
344
- force_combine_nested = True
345
- # Check if all items in the list are files
346
- if not all(Path(item).is_file() for item in self.path):
347
- raise FileNotFoundError(
348
- "All items in the provided list must be valid files."
371
+ if self.adjust_depth_for_sea_surface_height:
372
+ if "zeta" not in self.ds:
373
+ raise ValueError(
374
+ "`zeta` is required in provided ROMS output when `adjust_depth_for_sea_surface_height` is enabled."
349
375
  )
350
- elif Path(self.path).is_file():
351
- filetype = "file"
352
- force_combine_nested = False
353
- elif Path(self.path).is_dir():
354
- filetype = "dir"
355
- force_combine_nested = True
376
+ zeta = self.ds.zeta
356
377
  else:
357
- raise FileNotFoundError(
358
- f"The specified path '{self.path}' is neither a file, nor a list of files, nor a directory."
359
- )
378
+ zeta = 0
360
379
 
361
- time_chunking = True
362
- if self.type == "restart":
363
- time_chunking = False
364
- filename = _validate_and_set_filenames(self.path, filetype, "rst")
365
- elif self.type == "average":
366
- filename = _validate_and_set_filenames(self.path, filetype, "avg")
367
- elif self.type == "snapshot":
368
- filename = _validate_and_set_filenames(self.path, filetype, "his")
369
- else:
370
- raise ValueError(f"Unsupported type '{self.type}'.")
380
+ for location in locations:
381
+ self.ds_depth_coords[
382
+ f"{depth_type}_depth_{location}"
383
+ ] = compute_depth_coordinates(self.grid.ds, zeta, depth_type, location)
384
+
385
+ def _load_model_output(self) -> xr.Dataset:
386
+ """Load the model output."""
371
387
 
372
388
  # Load the dataset
373
389
  ds = _load_data(
374
- filename,
390
+ self.path,
375
391
  dim_names={"time": "time"},
376
392
  use_dask=self.use_dask,
377
- time_chunking=time_chunking,
378
- force_combine_nested=force_combine_nested,
393
+ time_chunking=True,
394
+ force_combine_nested=True,
379
395
  )
380
396
 
381
397
  return ds
@@ -551,40 +567,3 @@ class ROMSOutput:
551
567
  )
552
568
 
553
569
  return ds
554
-
555
-
556
- def _validate_and_set_filenames(
557
- filenames: Union[str, list], filetype: str, string: str
558
- ) -> Union[str, list]:
559
- """Validates and adjusts the filename or list of filenames based on the specified
560
- type and checks for the presence of a string in the filename.
561
-
562
- Parameters
563
- ----------
564
- filenames : Union[str, list]
565
- A single filename (str), a list of filenames, or a directory path.
566
- filetype : str
567
- The type of input: 'file' for a single file, 'list' for a list of files, or 'dir' for a directory.
568
- string : str
569
- The string that should be present in each filename.
570
-
571
- Returns
572
- -------
573
- Union[str, list]
574
- The validated filename(s). If a directory is provided, the function returns the adjusted file pattern.
575
- """
576
- if filetype == "file":
577
- if string not in os.path.basename(filenames):
578
- logging.warning(
579
- f"The file '{filenames}' does not appear to contain '{string}' in the name."
580
- )
581
- elif filetype == "list":
582
- for file in filenames:
583
- if string not in os.path.basename(file):
584
- logging.warning(
585
- f"The file '{file}' does not appear to contain '{string}' in the name."
586
- )
587
- elif filetype == "dir":
588
- filenames = os.path.join(filenames, f"*{string}.*.nc")
589
-
590
- return filenames
roms_tools/plot.py CHANGED
@@ -19,8 +19,8 @@ def _plot(
19
19
 
20
20
  Parameters
21
21
  ----------
22
- field : xarray.DataArray, optional
23
- The field to plot. If None, only the grid is plotted.
22
+ field : xarray.DataArray
23
+ The field to plot.
24
24
  depth_contours : bool, optional
25
25
  If True, adds depth contours to the plot.
26
26
  c : str, optional
@@ -284,6 +284,7 @@ def _section_plot(field, interface_depth=None, title="", kwargs={}, ax=None):
284
284
  )
285
285
 
286
286
  ax.set_title(title)
287
+ ax.set_ylabel("Depth [m]")
287
288
 
288
289
 
289
290
  def _profile_plot(field, title="", ax=None):
@@ -324,6 +325,7 @@ def _profile_plot(field, title="", ax=None):
324
325
  kwargs = {"y": depth_label, "yincrease": False}
325
326
  field.plot(**kwargs)
326
327
  ax.set_title(title)
328
+ ax.set_ylabel("Depth [m]")
327
329
  ax.grid()
328
330
 
329
331
 
@@ -3,7 +3,7 @@ import numpy as np
3
3
  from scipy.ndimage import label
4
4
  import logging
5
5
  import importlib.metadata
6
- from typing import Dict, Union, List
6
+ from typing import Dict, Union, List, Optional
7
7
  from dataclasses import dataclass, field
8
8
  from datetime import datetime
9
9
  import matplotlib.pyplot as plt
@@ -43,10 +43,14 @@ class BoundaryForcing:
43
43
  ----------
44
44
  grid : Grid
45
45
  Object representing the grid information.
46
- start_time : datetime
47
- Start time of the desired boundary forcing data.
48
- end_time : datetime
49
- End time of the desired boundary forcing data.
46
+ start_time : datetime, optional
47
+ The start time of the desired surface forcing data. This time is used to filter the dataset
48
+ to include only records on or after this time, with a single record at or before this time.
49
+ If no time filtering is desired, set it to None. Default is None.
50
+ end_time : datetime, optional
51
+ The end time of the desired surface forcing data. This time is used to filter the dataset
52
+ to include only records on or before this time, with a single record at or after this time.
53
+ If no time filtering is desired, set it to None. Default is None.
50
54
  boundaries : Dict[str, bool], optional
51
55
  Dictionary specifying which boundaries are forced (south, east, north, west). Default is all True.
52
56
  source : Dict[str, Union[str, Path, List[Union[str, Path]]], bool]
@@ -96,8 +100,8 @@ class BoundaryForcing:
96
100
  """
97
101
 
98
102
  grid: Grid
99
- start_time: datetime
100
- end_time: datetime
103
+ start_time: Optional[datetime] = None
104
+ end_time: Optional[datetime] = None
101
105
  boundaries: Dict[str, bool] = field(
102
106
  default_factory=lambda: {
103
107
  "south": True,
@@ -127,10 +131,13 @@ class BoundaryForcing:
127
131
  data = self._get_data()
128
132
 
129
133
  if self.apply_2d_horizontal_fill:
134
+
130
135
  data.choose_subdomain(
131
136
  target_coords,
132
137
  buffer_points=20, # lateral fill needs good buffer from data margin
133
138
  )
139
+ # Enforce double precision to ensure reproducibility
140
+ data.convert_to_float64()
134
141
  data.extrapolate_deepest_to_bottom()
135
142
  data.apply_lateral_fill()
136
143
 
@@ -158,6 +165,8 @@ class BoundaryForcing:
158
165
  )
159
166
 
160
167
  if not self.apply_2d_horizontal_fill:
168
+ # Enforce double precision to ensure reproducibility
169
+ bdry_data.convert_to_float64()
161
170
  bdry_data.extrapolate_deepest_to_bottom()
162
171
 
163
172
  processed_fields = {}
@@ -329,6 +338,18 @@ class BoundaryForcing:
329
338
  object.__setattr__(self, "ds", ds)
330
339
 
331
340
  def _input_checks(self):
341
+ # Check that start_time and end_time are both None or none of them is
342
+ if (self.start_time is None) != (self.end_time is None):
343
+ raise ValueError(
344
+ "Both `start_time` and `end_time` must be provided together as datetime objects or both should be None."
345
+ )
346
+
347
+ # Trigger a warning if both are None
348
+ if self.start_time is None and self.end_time is None:
349
+ logging.warning(
350
+ "Both `start_time` and `end_time` are None. No time filtering will be applied to the source data."
351
+ )
352
+
332
353
  # Validate the 'type' parameter
333
354
  if self.type not in ["physics", "bgc"]:
334
355
  raise ValueError("`type` must be either 'physics' or 'bgc'.")
@@ -967,7 +988,6 @@ class BoundaryForcing:
967
988
  cls,
968
989
  filepath: Union[str, Path],
969
990
  use_dask: bool = False,
970
- bypass_validation: bool = False,
971
991
  ) -> "BoundaryForcing":
972
992
  """Create an instance of the BoundaryForcing class from a YAML file.
973
993
 
@@ -977,10 +997,6 @@ class BoundaryForcing:
977
997
  The path to the YAML file from which the parameters will be read.
978
998
  use_dask: bool, optional
979
999
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
980
- bypass_validation: bool, optional
981
- Indicates whether to skip validation checks in the processed data. When set to True,
982
- the validation process that ensures no NaN values exist at wet points
983
- in the processed dataset is bypassed. Defaults to False.
984
1000
 
985
1001
  Returns
986
1002
  -------
@@ -993,9 +1009,7 @@ class BoundaryForcing:
993
1009
  params = _from_yaml(cls, filepath)
994
1010
 
995
1011
  # Create and return an instance of InitialConditions
996
- return cls(
997
- grid=grid, **params, use_dask=use_dask, bypass_validation=bypass_validation
998
- )
1012
+ return cls(grid=grid, **params, use_dask=use_dask)
999
1013
 
1000
1014
 
1001
1015
  def apply_1d_horizontal_fill(data_array: xr.DataArray) -> xr.DataArray:
@@ -35,10 +35,11 @@ class Dataset:
35
35
  The path to the data file(s). Can be a single string (with or without wildcards), a single Path object,
36
36
  or a list of strings or Path objects containing multiple files.
37
37
  start_time : Optional[datetime], optional
38
- The start time for selecting relevant data. If not provided, the data is not filtered by start time.
38
+ Start time for selecting relevant data. If not provided, no time-based filtering is applied.
39
39
  end_time : Optional[datetime], optional
40
- The end time for selecting relevant data. If not provided, only data at the start_time is selected if start_time is provided,
41
- or no filtering is applied if start_time is not provided.
40
+ End time for selecting relevant data. If not provided, the dataset selects the time entry
41
+ closest to `start_time` within the range `[start_time, start_time + 24 hours]`.
42
+ If `start_time` is also not provided, no time-based filtering is applied.
42
43
  dim_names: Dict[str, str], optional
43
44
  Dictionary specifying the names of dimensions in the dataset.
44
45
  var_names: Dict[str, str]
@@ -510,6 +511,25 @@ class Dataset:
510
511
  """
511
512
  pass
512
513
 
514
+ def convert_to_float64(self) -> None:
515
+ """Convert all data variables in the dataset to float64.
516
+
517
+ This method updates the dataset by converting all of its data variables to the
518
+ `float64` data type, ensuring consistency for numerical operations that require
519
+ high precision. The dataset is modified in place.
520
+
521
+ Parameters
522
+ ----------
523
+ None
524
+
525
+ Returns
526
+ -------
527
+ None
528
+ This method modifies the dataset in place and does not return anything.
529
+ """
530
+ ds = self.ds.astype({var: "float64" for var in self.ds.data_vars})
531
+ object.__setattr__(self, "ds", ds)
532
+
513
533
  def choose_subdomain(
514
534
  self,
515
535
  target_coords,
@@ -1843,15 +1863,10 @@ class RiverDataset:
1843
1863
 
1844
1864
  Returns
1845
1865
  -------
1846
- indices : dict
1866
+ indices : dict[str, list[tuple]]
1847
1867
  A dictionary containing the indices of the rivers that are within the threshold distance from
1848
- the target coordinates. The dictionary keys are:
1849
- - "station" : numpy.ndarray
1850
- The indices of the rivers that satisfy the distance threshold.
1851
- - "eta_rho" : numpy.ndarray
1852
- The indices of the `eta_rho` dimension corresponding to the selected stations.
1853
- - "xi_rho" : numpy.ndarray
1854
- The indices of the `xi_rho` dimension corresponding to the selected stations.
1868
+ the target coordinates. The dictionary structure consists of river names as keys, and each value is a list of tuples. Each tuple represents
1869
+ a pair of indices corresponding to the `eta_rho` and `xi_rho` grid coordinates of the river.
1855
1870
  """
1856
1871
 
1857
1872
  # Retrieve longitude and latitude of river mouths
@@ -1878,30 +1893,75 @@ class RiverDataset:
1878
1893
 
1879
1894
  # Find the indices of the closest grid cell to the river mouth
1880
1895
  indices = np.where(dist == dist_min)
1896
+ stations = indices[0]
1897
+ eta_rho_values = indices[1]
1898
+ xi_rho_values = indices[2]
1881
1899
  names = (
1882
- self.ds[self.var_names["name"]]
1883
- .isel({self.dim_names["station"]: indices[0]})
1900
+ ds[self.var_names["name"]]
1901
+ .isel({self.dim_names["station"]: stations})
1884
1902
  .values
1885
1903
  )
1886
- # Return the indices in a dictionary format
1887
- indices = {
1888
- "station": indices[0],
1889
- "eta_rho": indices[1],
1890
- "xi_rho": indices[2],
1891
- "name": names,
1892
- }
1904
+ river_indices = {}
1905
+ for i in range(len(stations)):
1906
+ river_name = names[i]
1907
+ river_indices[river_name] = [
1908
+ (int(eta_rho_values[i]), int(xi_rho_values[i]))
1909
+ ] # list of tuples
1893
1910
  else:
1894
1911
  ds = xr.Dataset()
1895
- indices = {
1896
- "station": [],
1897
- "eta_rho": [],
1898
- "xi_rho": [],
1899
- "name": [],
1900
- }
1912
+ river_indices = {}
1901
1913
 
1902
1914
  object.__setattr__(self, "ds", ds)
1903
1915
 
1904
- return indices
1916
+ return river_indices
1917
+
1918
+ def extract_named_rivers(self, indices):
1919
+ """Extracts a subset of the dataset based on the provided river names in the
1920
+ indices dictionary.
1921
+
1922
+ This method filters the dataset to include only the rivers specified in the `indices` dictionary.
1923
+ The resulting subset is stored in the `ds` attribute of the class.
1924
+
1925
+ Parameters
1926
+ ----------
1927
+ indices : dict
1928
+ A dictionary where the keys are river names (strings) and the values are dictionaries
1929
+ containing river-related data (e.g., river indices, coordinates).
1930
+
1931
+ Returns
1932
+ -------
1933
+ None
1934
+ The method modifies the `self.ds` attribute in place, setting it to the filtered dataset
1935
+ containing only the data related to the specified rivers.
1936
+
1937
+ Raises
1938
+ ------
1939
+ ValueError
1940
+ - If `indices` is not a dictionary.
1941
+ - If any of the requested river names are not found in the dataset.
1942
+ """
1943
+
1944
+ if not isinstance(indices, dict):
1945
+ raise ValueError("`indices` must be a dictionary.")
1946
+
1947
+ river_names = list(indices.keys())
1948
+
1949
+ # Ensure the dataset is filtered based on the provided river names
1950
+ ds_filtered = self.ds.where(
1951
+ self.ds[self.var_names["name"]].isin(river_names), drop=True
1952
+ )
1953
+
1954
+ # Check that all requested rivers exist in the dataset
1955
+ filtered_river_names = set(ds_filtered[self.var_names["name"]].values)
1956
+ missing_rivers = set(river_names) - filtered_river_names
1957
+
1958
+ if missing_rivers:
1959
+ raise ValueError(
1960
+ f"The following rivers were not found in the dataset: {missing_rivers}"
1961
+ )
1962
+
1963
+ # Set the filtered dataset as the new `ds`
1964
+ object.__setattr__(self, "ds", ds_filtered)
1905
1965
 
1906
1966
 
1907
1967
  @dataclass(frozen=True, kw_only=True)
@@ -2050,7 +2110,7 @@ def _check_dataset(
2050
2110
 
2051
2111
 
2052
2112
  def _select_relevant_times(
2053
- ds, time_dim, start_time=None, end_time=None, climatology=False
2113
+ ds, time_dim, start_time, end_time=None, climatology=False
2054
2114
  ) -> xr.Dataset:
2055
2115
  """Select a subset of the dataset based on the specified time range.
2056
2116
 
@@ -2067,11 +2127,10 @@ def _select_relevant_times(
2067
2127
  The input dataset to be filtered. Must contain a time dimension.
2068
2128
  time_dim: str
2069
2129
  Name of time dimension.
2070
- start_time : Optional[datetime], optional
2071
- The start time for selecting relevant data. If not provided, the data is not filtered by start time.
2130
+ start_time : datetime
2131
+ The start time for selecting relevant data.
2072
2132
  end_time : Optional[datetime], optional
2073
- The end time for selecting relevant data. If not provided, only data at the start_time is selected if start_time is provided,
2074
- or no filtering is applied if start_time is not provided.
2133
+ The end time for selecting relevant data. If not provided, only data at the start_time is selected if start_time is provided.
2075
2134
  climatology : bool
2076
2135
  Indicates whether the dataset is climatological. Defaults to False.
2077
2136
 
roms_tools/setup/grid.py CHANGED
@@ -18,6 +18,7 @@ from roms_tools.setup.utils import (
18
18
  interpolate_from_rho_to_v,
19
19
  get_target_coords,
20
20
  gc_dist,
21
+ _pop_grid_data,
21
22
  )
22
23
  from roms_tools.setup.utils import extract_single_value
23
24
  from pathlib import Path
@@ -112,7 +113,7 @@ class Grid:
112
113
  # Coarsen the dataset if needed
113
114
  self._coarsen()
114
115
 
115
- # Topography and mask
116
+ # Topography
116
117
  self.update_topography(
117
118
  topography_source=self.topography_source,
118
119
  hmin=self.hmin,
@@ -205,7 +206,7 @@ class Grid:
205
206
  f"=== Generating the topography using {topography_source['name']} data and hmin = {hmin} meters ==="
206
207
  )
207
208
 
208
- # Add topography and mask to the dataset
209
+ # Add topography to the dataset
209
210
  ds = _add_topography(
210
211
  ds=self.ds,
211
212
  target_coords=target_coords,
@@ -688,9 +689,7 @@ class Grid:
688
689
  filepath = Path(filepath)
689
690
 
690
691
  data = asdict(self)
691
- data.pop("ds", None)
692
- data.pop("straddle", None)
693
- data.pop("verbose", None)
692
+ data = _pop_grid_data(data)
694
693
 
695
694
  # Include the version of roms-tools
696
695
  try:
@@ -150,6 +150,8 @@ class InitialConditions:
150
150
  target_coords,
151
151
  buffer_points=20, # lateral fill needs good buffer from data margin
152
152
  )
153
+ # Enforce double precision to ensure reproducibility
154
+ data.convert_to_float64()
153
155
  data.extrapolate_deepest_to_bottom()
154
156
  data.apply_lateral_fill()
155
157
 
@@ -234,6 +236,11 @@ class InitialConditions:
234
236
  return processed_fields
235
237
 
236
238
  def _input_checks(self):
239
+ # Check that ini_time is not None
240
+ if self.ini_time is None:
241
+ raise ValueError(
242
+ "`ini_time` must be a valid datetime object and cannot be None."
243
+ )
237
244
 
238
245
  if "name" not in self.source.keys():
239
246
  raise ValueError("`source` must include a 'name'.")
@@ -898,7 +905,6 @@ class InitialConditions:
898
905
  cls,
899
906
  filepath: Union[str, Path],
900
907
  use_dask: bool = False,
901
- bypass_validation: bool = False,
902
908
  ) -> "InitialConditions":
903
909
  """Create an instance of the InitialConditions class from a YAML file.
904
910
 
@@ -908,10 +914,6 @@ class InitialConditions:
908
914
  The path to the YAML file from which the parameters will be read.
909
915
  use_dask: bool, optional
910
916
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
911
- bypass_validation: bool, optional
912
- Indicates whether to skip validation checks in the processed data. When set to True,
913
- the validation process that ensures no NaN values exist at wet points
914
- in the processed dataset is bypassed. Defaults to False.
915
917
 
916
918
  Returns
917
919
  -------
@@ -926,5 +928,4 @@ class InitialConditions:
926
928
  grid=grid,
927
929
  **initial_conditions_params,
928
930
  use_dask=use_dask,
929
- bypass_validation=bypass_validation,
930
931
  )