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
@@ -4,9 +4,10 @@ import logging
4
4
  from dataclasses import dataclass, field
5
5
  import cartopy.crs as ccrs
6
6
  from datetime import datetime
7
- from typing import Dict, Union, List
7
+ from typing import Dict, Union, List, Optional
8
8
  from pathlib import Path
9
9
  import matplotlib.pyplot as plt
10
+ import matplotlib.cm as cm
10
11
  from roms_tools import Grid
11
12
  from roms_tools.plot import _get_projection, _add_field_to_ax
12
13
  from roms_tools.utils import save_datasets
@@ -57,6 +58,24 @@ class RiverForcing:
57
58
  Whether to include BGC tracers. Defaults to `False`.
58
59
  model_reference_date : datetime, optional
59
60
  Reference date for the model. Default is January 1, 2000.
61
+ indices : dict[str, list[tuple]], optional
62
+ A dictionary specifying the river indices for each river to be included in the river forcing. This parameter is optional. If not provided,
63
+ the river indices will be automatically determined based on the grid and the source dataset. If provided, it allows for explicit specification
64
+ of river locations. The dictionary structure consists of river names as keys, and each value is a list of tuples. Each tuple represents
65
+ a pair of indices corresponding to the `eta_rho` and `xi_rho` grid coordinates of the river.
66
+
67
+ Example:
68
+ indices = {
69
+ 'Hvita(Olfusa)': [(8, 6), (7, 6)],
70
+ 'Thjorsa': [(8, 6)],
71
+ 'JkulsFjll': [(11, 12)],
72
+ 'Lagarfljot': [(9, 13), (8, 13), (10, 13)],
73
+ 'Bruara': [(8, 6)],
74
+ 'Svarta': [(12, 9)]
75
+ }
76
+
77
+ In the example, the dictionary provides the river names as keys, and the values are lists of tuples, where each tuple represents the
78
+ `(eta_rho, xi_rho)` indices for a river location.
60
79
 
61
80
  Attributes
62
81
  ----------
@@ -64,6 +83,9 @@ class RiverForcing:
64
83
  The xarray Dataset containing the river forcing data.
65
84
  climatology : bool
66
85
  Indicates whether the final river forcing is climatological.
86
+ Dict[str, Union[int, List[int]]]
87
+ A dictionary of river indices. If not provided during initialization, it will be automatically determined
88
+ based on the grid and the source dataset. The dictionary structure is the same as described in the `indices` parameter docstring.
67
89
  """
68
90
 
69
91
  grid: Grid
@@ -73,40 +95,47 @@ class RiverForcing:
73
95
  convert_to_climatology: str = "if_any_missing"
74
96
  include_bgc: bool = False
75
97
  model_reference_date: datetime = datetime(2000, 1, 1)
98
+ indices: Optional[Dict[str, Dict[str, Union[int, List[int]]]]] = None
76
99
 
77
100
  ds: xr.Dataset = field(init=False, repr=False)
78
101
  climatology: xr.Dataset = field(init=False, repr=False)
79
102
 
80
103
  def __post_init__(self):
81
104
  self._input_checks()
82
- target_coords = get_target_coords(self.grid)
83
- # maximum dx in grid
84
- dx = (
85
- np.sqrt((1 / self.grid.ds.pm) ** 2 + (1 / self.grid.ds.pn) ** 2) / 2
86
- ).max()
87
-
88
105
  data = self._get_data()
89
106
 
90
- original_indices = data.extract_relevant_rivers(target_coords, dx)
91
- object.__setattr__(self, "original_indices", original_indices)
107
+ if self.indices is None:
108
+ logging.info(
109
+ "No river indices provided. Identify all rivers within the ROMS domain and assign each of them to the nearest coastal point."
110
+ )
111
+ target_coords = get_target_coords(self.grid)
112
+ # maximum dx in grid
113
+ dx = (
114
+ np.sqrt((1 / self.grid.ds.pm) ** 2 + (1 / self.grid.ds.pn) ** 2) / 2
115
+ ).max()
116
+ original_indices = data.extract_relevant_rivers(target_coords, dx)
117
+ if len(original_indices) == 0:
118
+ raise ValueError(
119
+ "No relevant rivers found. Consider increasing domain size or using a different river dataset."
120
+ )
121
+ object.__setattr__(self, "original_indices", original_indices)
122
+ updated_indices = self._move_rivers_to_closest_coast(target_coords, data)
123
+ object.__setattr__(self, "indices", updated_indices)
92
124
 
93
- if len(original_indices["station"]) > 0:
94
- ds = self._create_river_forcing(data)
95
- self._move_rivers_to_closest_coast(target_coords, data)
96
- ds = self._write_indices_into_dataset(ds)
97
- self._validate(ds)
125
+ else:
126
+ logging.info("Use provided river indices.")
127
+ object.__setattr__(self, "original_indices", self.indices)
128
+ check_river_locations_are_along_coast(self.grid.ds.mask_rho, self.indices)
129
+ data.extract_named_rivers(self.indices)
98
130
 
99
- for var_name in ds.data_vars:
100
- ds[var_name] = substitute_nans_by_fillvalue(
101
- ds[var_name], fill_value=0.0
102
- )
131
+ ds = self._create_river_forcing(data)
132
+ ds = self._write_indices_into_dataset(ds)
133
+ self._validate(ds)
103
134
 
104
- object.__setattr__(self, "ds", ds)
135
+ for var_name in ds.data_vars:
136
+ ds[var_name] = substitute_nans_by_fillvalue(ds[var_name], fill_value=0.0)
105
137
 
106
- else:
107
- raise ValueError(
108
- "No relevant rivers found. Consider increasing domain size or using a different river dataset."
109
- )
138
+ object.__setattr__(self, "ds", ds)
110
139
 
111
140
  def _input_checks(self):
112
141
  if self.source is None:
@@ -125,6 +154,63 @@ class RiverForcing:
125
154
  {**self.source, "climatology": self.source.get("climatology", False)},
126
155
  )
127
156
 
157
+ # Check if 'indices' is provided and has the correct format
158
+ if self.indices is not None:
159
+ if not isinstance(self.indices, dict):
160
+ raise ValueError("`indices` must be a dictionary.")
161
+
162
+ # Ensure the dictionary contains at least one river
163
+ if len(self.indices) == 0:
164
+ raise ValueError(
165
+ "The provided 'indices' dictionary must contain at least one river."
166
+ )
167
+
168
+ for river_name, river_data in self.indices.items():
169
+ if not isinstance(river_name, str):
170
+ raise ValueError(f"River name `{river_name}` must be a string.")
171
+
172
+ if not isinstance(river_data, list):
173
+ raise ValueError(
174
+ f"Data for river `{river_name}` must be a list of tuples."
175
+ )
176
+
177
+ # Ensure each element in the list is a tuple of length 2
178
+ seen_tuples = set()
179
+ for idx_pair in river_data:
180
+ if not isinstance(idx_pair, tuple) or len(idx_pair) != 2:
181
+ raise ValueError(
182
+ f"Each item for river `{river_name}` must be a tuple of length 2 representing (eta_rho, xi_rho)."
183
+ )
184
+
185
+ eta_rho, xi_rho = idx_pair
186
+
187
+ # Ensure both eta_rho and xi_rho are integers
188
+ if not isinstance(eta_rho, int):
189
+ raise ValueError(
190
+ f"First element of tuple for river `{river_name}` must be an integer (eta_rho), but got {type(eta_rho)}."
191
+ )
192
+ if not isinstance(xi_rho, int):
193
+ raise ValueError(
194
+ f"Second element of tuple for river `{river_name}` must be an integer (xi_rho), but got {type(xi_rho)}."
195
+ )
196
+
197
+ # Check that eta_rho and xi_rho are within the valid range
198
+ if not (0 <= eta_rho < len(self.grid.ds.eta_rho)):
199
+ raise ValueError(
200
+ f"Value of eta_rho for river `{river_name}` ({eta_rho}) is out of valid range [0, {len(self.grid.ds.eta_rho)-1}]."
201
+ )
202
+ if not (0 <= xi_rho < len(self.grid.ds.xi_rho)):
203
+ raise ValueError(
204
+ f"Value of xi_rho for river `{river_name}` ({xi_rho}) is out of valid range [0, {len(self.grid.ds.xi_rho)-1}]."
205
+ )
206
+
207
+ # Check for duplicate tuples
208
+ if idx_pair in seen_tuples:
209
+ raise ValueError(
210
+ f"Duplicate location {idx_pair} found for river `{river_name}`."
211
+ )
212
+ seen_tuples.add(idx_pair)
213
+
128
214
  def _get_data(self):
129
215
 
130
216
  data_dict = {
@@ -197,13 +283,19 @@ class RiverForcing:
197
283
  river_volume = river_volume.rename(
198
284
  {data.dim_names["time"]: "river_time", data.dim_names["station"]: "nriver"}
199
285
  )
286
+
200
287
  name = data.ds[data.var_names["name"]].rename(
201
288
  {data.dim_names["station"]: "nriver"}
202
289
  )
203
290
  name.attrs["long_name"] = "River name"
204
291
  river_volume.coords["river_name"] = name
292
+
205
293
  ds["river_volume"] = river_volume
206
294
 
295
+ nriver = xr.DataArray(np.arange(1, len(ds.nriver) + 1), dims="nriver")
296
+ nriver.attrs["long_name"] = "River ID (1-based Fortran indexing)"
297
+ ds = ds.assign_coords({"nriver": nriver})
298
+
207
299
  if self.include_bgc:
208
300
  ntracers = 2 + 32
209
301
  else:
@@ -272,15 +364,13 @@ class RiverForcing:
272
364
 
273
365
  ds = ds.assign_coords({"river_time": time})
274
366
 
275
- ds = ds.drop_vars("nriver")
276
-
277
367
  return ds
278
368
 
279
369
  def _move_rivers_to_closest_coast(self, target_coords, data):
280
370
  """Move river mouths to the closest coastal grid cell.
281
371
 
282
372
  This method computes the closest coastal grid point to each river mouth
283
- based on geographical distance.
373
+ based on geographical distance. It identifies the nearest grid point on the coast and returns the updated river mouth indices.
284
374
 
285
375
  Parameters:
286
376
  -----------
@@ -296,11 +386,11 @@ class RiverForcing:
296
386
  - `var_names`: A dictionary of variable names in the dataset (e.g., longitude, latitude, station names).
297
387
  - `dim_names`: A dictionary containing dimension names for the dataset (e.g., "station", "eta_rho", "xi_rho").
298
388
 
299
- Returns:
300
- --------
301
- None
302
- This method modifies the `self.updated_indices` attribute and writes the updated indices
303
- of the river mouths to the grid file using `write_indices_into_grid_file`.
389
+ Returns
390
+ -------
391
+ indices : dict[str, list[tuple]]
392
+ A dictionary consisting of river names as keys, and each value is a list of tuples. Each tuple represents
393
+ a pair of indices corresponding to the `eta_rho` and `xi_rho` grid coordinates of the river.
304
394
  """
305
395
 
306
396
  # Retrieve longitude and latitude of river mouths
@@ -333,27 +423,29 @@ class RiverForcing:
333
423
 
334
424
  # Find the indices of the closest coastal grid cell to the river mouth
335
425
  indices = np.where(dist_coast == dist_coast_min)
426
+ stations = indices[0]
427
+ eta_rho_values = indices[1]
428
+ xi_rho_values = indices[2]
336
429
  names = (
337
430
  data.ds[data.var_names["name"]]
338
- .isel({data.dim_names["station"]: indices[0]})
431
+ .isel({data.dim_names["station"]: stations})
339
432
  .values
340
433
  )
341
-
342
434
  # Return the indices in a dictionary format
343
- indices = {
344
- "station": indices[0],
345
- "eta_rho": indices[1],
346
- "xi_rho": indices[2],
347
- "name": names,
348
- }
349
- object.__setattr__(self, "updated_indices", indices)
435
+ river_indices = {}
436
+ for i in range(len(stations)):
437
+ river_name = names[i]
438
+ river_indices[river_name] = [
439
+ (int(eta_rho_values[i]), int(xi_rho_values[i]))
440
+ ] # list of tuples
441
+
442
+ return river_indices
350
443
 
351
444
  def _write_indices_into_dataset(self, ds):
352
445
  """Adds river location indices to the dataset as the "river_location" variable.
353
446
 
354
- This method checks if the "river_location" variable already exists in the dataset.
355
- If it does, the method removes it. Then, it creates a new "river_location" variable
356
- using river station indices from `self.updated_indices` and assigns it to the dataset.
447
+ This method creates a new "river_location" variable
448
+ using river station indices from `self.indices` and assigns it to the dataset.
357
449
  The indices specify the river station locations in terms of eta_rho and xi_rho grid cell indices.
358
450
 
359
451
  Parameters
@@ -367,17 +459,21 @@ class RiverForcing:
367
459
  The modified dataset with the "river_location" variable added.
368
460
  """
369
461
 
370
- if "river_location" in ds:
371
- ds = ds.drop_vars("river_location")
462
+ river_locations = xr.zeros_like(self.grid.ds.h)
463
+
464
+ for nriver in ds.nriver:
465
+ river_name = str(ds.river_name.sel(nriver=nriver).values)
466
+ indices = self.indices[river_name]
467
+ fraction = 1.0 / len(indices)
372
468
 
373
- river_locations = xr.zeros_like(self.grid.ds.mask_rho)
374
- for i in range(len(self.updated_indices["name"])):
375
- station = self.updated_indices["station"][i]
376
- eta_index = self.updated_indices["eta_rho"][i]
377
- xi_index = self.updated_indices["xi_rho"][i]
378
- river_locations[eta_index, xi_index] = station + 2
469
+ for eta_index, xi_index in indices:
379
470
 
380
- river_locations.attrs["long_name"] = "River index plus local volume fraction"
471
+ river_locations[eta_index, xi_index] = (
472
+ nriver # assign unique nriver ID (Fortran-based indexing)
473
+ + fraction # Fractional contribution for multiple grid points
474
+ )
475
+
476
+ river_locations.attrs["long_name"] = "River ID plus local volume fraction"
381
477
  river_locations.attrs["units"] = "none"
382
478
  ds["river_location"] = river_locations
383
479
 
@@ -463,26 +559,46 @@ class RiverForcing:
463
559
  "color": "black",
464
560
  } # Customize latitude label style
465
561
 
466
- for ax, indices in zip(axs, [self.original_indices, self.updated_indices]):
467
- for i in range(len(indices["name"])):
468
- name = indices["name"][i]
469
- xi_index = indices["xi_rho"][i]
470
- eta_index = indices["eta_rho"][i]
471
- # transform coordinates to projected space
472
- proj = ccrs.PlateCarree()
473
- transformed_lon, transformed_lat = trans.transform_point(
474
- self.grid.ds.lon_rho[eta_index, xi_index],
475
- self.grid.ds.lat_rho[eta_index, xi_index],
476
- proj,
477
- )
478
- ax.plot(
479
- transformed_lon,
480
- transformed_lat,
481
- marker="x",
482
- markersize=8,
483
- markeredgewidth=2,
484
- label=name,
485
- )
562
+ proj = ccrs.PlateCarree()
563
+
564
+ if len(self.indices) <= 10:
565
+ color_map = cm.get_cmap("tab10")
566
+ elif len(self.indices) <= 20:
567
+ color_map = cm.get_cmap("tab20")
568
+ else:
569
+ color_map = cm.get_cmap("tab20b")
570
+ # Create a dictionary of colors
571
+ colors = {name: color_map(i) for i, name in enumerate(self.indices.keys())}
572
+
573
+ for ax, indices in zip(axs, [self.original_indices, self.indices]):
574
+ added_labels = set()
575
+ for name in indices.keys():
576
+ for tuple in indices[name]:
577
+ eta_index = tuple[0]
578
+ xi_index = tuple[1]
579
+
580
+ # transform coordinates to projected space
581
+ transformed_lon, transformed_lat = trans.transform_point(
582
+ self.grid.ds.lon_rho[eta_index, xi_index],
583
+ self.grid.ds.lat_rho[eta_index, xi_index],
584
+ proj,
585
+ )
586
+
587
+ if name not in added_labels:
588
+ added_labels.add(name)
589
+ label = name
590
+ else:
591
+ label = "_None"
592
+
593
+ ax.plot(
594
+ transformed_lon,
595
+ transformed_lat,
596
+ marker="x",
597
+ markersize=8,
598
+ markeredgewidth=2,
599
+ label=label,
600
+ color=colors[name],
601
+ )
486
602
 
487
603
  axs[0].set_title("Original river locations")
488
604
  axs[1].set_title("Updated river locations")
@@ -647,4 +763,59 @@ class RiverForcing:
647
763
  grid = Grid.from_yaml(filepath)
648
764
  params = _from_yaml(cls, filepath)
649
765
 
766
+ def convert_indices_format(indices):
767
+ # Remove the '_convention' key from the dictionary if present
768
+ indices = {
769
+ key: value for key, value in indices.items() if key != "_convention"
770
+ }
771
+
772
+ # Convert the string of indices into tuples
773
+ for river, index_list in indices.items():
774
+ # Split the string by ',' and convert to tuples of integers
775
+ indices[river] = [tuple(map(int, idx.split(","))) for idx in index_list]
776
+
777
+ return indices
778
+
779
+ params["indices"] = convert_indices_format(params["indices"])
780
+
650
781
  return cls(grid=grid, **params)
782
+
783
+
784
+ def check_river_locations_are_along_coast(mask, indices):
785
+ """Check if the river locations are along the coast.
786
+
787
+ This function checks if the river locations specified in the `indices` dictionary are located on coastal grid cells.
788
+ A coastal grid cell is defined as a land grid cell adjacent to an ocean grid cell.
789
+
790
+ Parameters
791
+ ----------
792
+ mask : xarray.DataArray
793
+ A mask representing the land and ocean cells in the grid, where 1 represents ocean and 0 represents land.
794
+
795
+ indices : dict
796
+ A dictionary where the keys are river names, and the values are dictionaries containing the river's grid locations (`eta_rho` and `xi_rho`).
797
+ Each entry should have keys `"eta_rho"` and `"xi_rho"`, which are lists of grid cell indices representing river mouth locations.
798
+
799
+ Raises
800
+ ------
801
+ ValueError
802
+ If any river is not located on the coast.
803
+ """
804
+
805
+ faces = (
806
+ mask.shift(eta_rho=1)
807
+ + mask.shift(eta_rho=-1)
808
+ + mask.shift(xi_rho=1)
809
+ + mask.shift(xi_rho=-1)
810
+ )
811
+ coast = (1 - mask) * (faces > 0)
812
+
813
+ for key, river_data in indices.items():
814
+ for idx_pair in river_data:
815
+ eta_rho, xi_rho = idx_pair
816
+
817
+ # Check if the river location is along the coast
818
+ if not coast[eta_rho, xi_rho]:
819
+ raise ValueError(
820
+ f"River `{key}` is not located on the coast at grid cell ({eta_rho}, {xi_rho})."
821
+ )
@@ -6,7 +6,7 @@ import numpy as np
6
6
  import matplotlib.pyplot as plt
7
7
  from pathlib import Path
8
8
  import logging
9
- from typing import Dict, Union, List
9
+ from typing import Dict, Union, List, Optional
10
10
  from roms_tools import Grid
11
11
  from roms_tools.utils import save_datasets
12
12
  from roms_tools.regrid import LateralRegrid
@@ -38,10 +38,14 @@ class SurfaceForcing:
38
38
  ----------
39
39
  grid : Grid
40
40
  Object representing the grid information.
41
- start_time : datetime
42
- Start time of the desired surface forcing data.
43
- end_time : datetime
44
- End time of the desired surface forcing data.
41
+ start_time : datetime, optional
42
+ The start time of the desired surface forcing data. This time is used to filter the dataset
43
+ to include only records on or after this time, with a single record at or before this time.
44
+ If no time filtering is desired, set it to None. Default is None.
45
+ end_time : datetime, optional
46
+ The end time of the desired surface forcing data. This time is used to filter the dataset
47
+ to include only records on or before this time, with a single record at or after this time.
48
+ If no time filtering is desired, set it to None. Default is None.
45
49
  source : Dict[str, Union[str, Path, List[Union[str, Path]]], bool]
46
50
  Dictionary specifying the source of the surface forcing data. Keys include:
47
51
 
@@ -90,8 +94,8 @@ class SurfaceForcing:
90
94
  """
91
95
 
92
96
  grid: Grid
93
- start_time: datetime
94
- end_time: datetime
97
+ start_time: Optional[datetime] = None
98
+ end_time: Optional[datetime] = None
95
99
  source: Dict[str, Union[str, Path, List[Union[str, Path]]]]
96
100
  type: str = "physics"
97
101
  correct_radiation: bool = True
@@ -126,6 +130,8 @@ class SurfaceForcing:
126
130
  target_coords,
127
131
  buffer_points=20, # lateral fill needs some buffer from data margin
128
132
  )
133
+ # Enforce double precision to ensure reproducibility
134
+ data.convert_to_float64()
129
135
 
130
136
  data.apply_lateral_fill()
131
137
 
@@ -168,6 +174,18 @@ class SurfaceForcing:
168
174
  object.__setattr__(self, "ds", ds)
169
175
 
170
176
  def _input_checks(self):
177
+ # Check that start_time and end_time are both None or none of them is
178
+ if (self.start_time is None) != (self.end_time is None):
179
+ raise ValueError(
180
+ "Both `start_time` and `end_time` must be provided together as datetime objects or both should be None."
181
+ )
182
+
183
+ # Trigger a warning if both are None
184
+ if self.start_time is None and self.end_time is None:
185
+ logging.warning(
186
+ "Both `start_time` and `end_time` are None. No time filtering will be applied to the source data."
187
+ )
188
+
171
189
  # Validate the 'type' parameter
172
190
  if self.type not in ["physics", "bgc"]:
173
191
  raise ValueError("`type` must be either 'physics' or 'bgc'.")
@@ -606,7 +624,6 @@ class SurfaceForcing:
606
624
  cls,
607
625
  filepath: Union[str, Path],
608
626
  use_dask: bool = False,
609
- bypass_validation: bool = False,
610
627
  ) -> "SurfaceForcing":
611
628
  """Create an instance of the SurfaceForcing class from a YAML file.
612
629
 
@@ -616,10 +633,6 @@ class SurfaceForcing:
616
633
  The path to the YAML file from which the parameters will be read.
617
634
  use_dask: bool, optional
618
635
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
619
- bypass_validation: bool, optional
620
- Indicates whether to skip validation checks in the processed data. When set to True,
621
- the validation process that ensures no NaN values exist at wet points
622
- in the processed dataset is bypassed. Defaults to False.
623
636
 
624
637
  Returns
625
638
  -------
@@ -631,6 +644,4 @@ class SurfaceForcing:
631
644
  grid = Grid.from_yaml(filepath)
632
645
  params = _from_yaml(cls, filepath)
633
646
 
634
- return cls(
635
- grid=grid, **params, use_dask=use_dask, bypass_validation=bypass_validation
636
- )
647
+ return cls(grid=grid, **params, use_dask=use_dask)
roms_tools/setup/tides.py CHANGED
@@ -84,6 +84,9 @@ class TidalForcing:
84
84
  target_coords,
85
85
  buffer_points=20,
86
86
  )
87
+ # Enforce double precision to ensure reproducibility
88
+ data.convert_to_float64()
89
+
87
90
  # select desired number of constituents
88
91
  object.__setattr__(data, "ds", data.ds.isel(ntides=slice(None, self.ntides)))
89
92
  self._correct_tides(data)
@@ -416,7 +419,6 @@ class TidalForcing:
416
419
  cls,
417
420
  filepath: Union[str, Path],
418
421
  use_dask: bool = False,
419
- bypass_validation: bool = False,
420
422
  ) -> "TidalForcing":
421
423
  """Create an instance of the TidalForcing class from a YAML file.
422
424
 
@@ -426,10 +428,6 @@ class TidalForcing:
426
428
  The path to the YAML file from which the parameters will be read.
427
429
  use_dask: bool, optional
428
430
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
429
- bypass_validation: bool, optional
430
- Indicates whether to skip validation checks in the processed data. When set to True,
431
- the validation process that ensures no NaN values exist at wet points
432
- in the processed dataset is bypassed. Defaults to False.
433
431
 
434
432
  Returns
435
433
  -------
@@ -444,7 +442,6 @@ class TidalForcing:
444
442
  grid=grid,
445
443
  **tidal_forcing_params,
446
444
  use_dask=use_dask,
447
- bypass_validation=bypass_validation,
448
445
  )
449
446
 
450
447
  def _correct_tides(self, data):
@@ -145,6 +145,8 @@ def _make_raw_topography(
145
145
  The regridded topography data with the sign flipped (bathymetry positive).
146
146
  """
147
147
  data.choose_subdomain(target_coords, buffer_points=3, verbose=verbose)
148
+ # Enforce double precision to ensure reproducibility
149
+ data.convert_to_float64()
148
150
 
149
151
  if verbose:
150
152
  start_time = time.time()
@@ -233,7 +235,7 @@ def _smooth_topography_locally(h, hmin=5, rmax=0.2):
233
235
  rmax_log = 0.0
234
236
 
235
237
  # Apply hmin threshold
236
- h = xr.where(h < hmin, hmin, h)
238
+ h = _clip_depth(h, hmin)
237
239
 
238
240
  # Perform logarithmic transformation of the height field
239
241
  h_log = np.log(h / hmin)
@@ -316,7 +318,7 @@ def _smooth_topography_locally(h, hmin=5, rmax=0.2):
316
318
  h = hmin * np.exp(h_log)
317
319
 
318
320
  # Apply hmin threshold again
319
- h = xr.where(h < hmin, hmin, h)
321
+ h = _clip_depth(h, hmin)
320
322
 
321
323
  # Compute maximum slope parameter r
322
324
  r_eta, r_xi = _compute_rfactor(h)
@@ -327,6 +329,27 @@ def _smooth_topography_locally(h, hmin=5, rmax=0.2):
327
329
  return h
328
330
 
329
331
 
332
+ def _clip_depth(h: xr.DataArray, hmin: float) -> xr.DataArray:
333
+ """Ensures that depth values do not fall below a minimum threshold.
334
+
335
+ This function replaces all depth values in `h` that are less than `hmin` with `hmin`,
336
+ ensuring a minimum depth constraint.
337
+
338
+ Parameters
339
+ ----------
340
+ h : xr.DataArray
341
+ The depth (bathymetry) array.
342
+ hmin : float
343
+ The minimum allowable depth value.
344
+
345
+ Returns
346
+ -------
347
+ xr.DataArray
348
+ The modified depth array with values clipped at `hmin`.
349
+ """
350
+ return xr.where(h < hmin, hmin, h)
351
+
352
+
330
353
  def _compute_rfactor(h):
331
354
  """Computes the slope parameter (r-factor) in both horizontal directions.
332
355