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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. ci/environment.yml +1 -0
  2. roms_tools/__init__.py +2 -1
  3. roms_tools/analysis/roms_output.py +81 -98
  4. roms_tools/plot.py +4 -2
  5. roms_tools/setup/boundary_forcing.py +207 -208
  6. roms_tools/setup/datasets.py +149 -33
  7. roms_tools/setup/grid.py +35 -102
  8. roms_tools/setup/initial_conditions.py +179 -132
  9. roms_tools/setup/nesting.py +239 -86
  10. roms_tools/setup/river_forcing.py +266 -128
  11. roms_tools/setup/surface_forcing.py +137 -76
  12. roms_tools/setup/tides.py +10 -36
  13. roms_tools/setup/topography.py +25 -2
  14. roms_tools/setup/utils.py +52 -82
  15. roms_tools/tests/test_analysis/test_roms_output.py +233 -70
  16. roms_tools/tests/test_setup/test_boundary_forcing.py +283 -57
  17. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zattrs +3 -1
  18. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zmetadata +3 -1
  19. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/0.0.0 +0 -0
  20. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/0.0.0 +0 -0
  21. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/0.0.0 +0 -0
  22. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/0.0.0 +0 -0
  23. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
  25. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
  27. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/0.0.0 +0 -0
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/0.0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/0.0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/0.0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/0.0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/0.0.0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/0.0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/0.0.0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/0.0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/0.0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/0.0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/0.0.0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/0.0.0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/0.0.0 +0 -0
  125. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
  126. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
  127. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
  128. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/0.0.0 +0 -0
  129. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
  130. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
  131. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zattrs +2 -2
  132. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zmetadata +8 -7
  133. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/abs_time/.zattrs +1 -0
  134. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust/0.0.0 +0 -0
  135. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust_time/.zattrs +1 -1
  136. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron/0.0.0 +0 -0
  137. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron_time/.zattrs +1 -1
  138. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy/0.0.0 +0 -0
  139. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy_time/.zattrs +1 -1
  140. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox/0.0.0 +0 -0
  141. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox_time/.zattrs +1 -1
  142. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air/0.0.0 +0 -0
  143. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air_alt/0.0.0 +0 -0
  144. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_time/.zattrs +1 -1
  145. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zattrs +2 -2
  146. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zmetadata +2 -2
  147. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust/0.0.0 +0 -0
  148. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron/0.0.0 +0 -0
  149. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy/0.0.0 +0 -0
  150. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox/0.0.0 +0 -0
  151. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air/0.0.0 +0 -0
  152. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air_alt/0.0.0 +0 -0
  153. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zattrs +5 -3
  154. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +156 -121
  155. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zarray +2 -2
  156. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +2 -1
  157. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/0 +0 -0
  158. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zarray +2 -2
  159. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zattrs +1 -1
  160. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/0 +0 -0
  161. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/.zarray +4 -4
  162. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
  163. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/.zarray +4 -4
  164. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/0.0.0 +0 -0
  165. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/.zarray +4 -4
  166. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
  167. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/.zarray +4 -4
  168. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/0.0.0 +0 -0
  169. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/.zarray +4 -4
  170. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
  171. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/.zarray +4 -4
  172. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/0.0.0 +0 -0
  173. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/.zarray +4 -4
  174. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
  175. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/.zarray +4 -4
  176. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/0.0.0 +0 -0
  177. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/.zarray +4 -4
  178. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
  179. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/.zarray +4 -4
  180. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/0.0.0 +0 -0
  181. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/.zarray +4 -4
  182. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
  183. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/.zarray +4 -4
  184. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
  185. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/.zarray +4 -4
  186. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
  187. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/.zarray +4 -4
  188. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/0.0 +0 -0
  189. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/.zarray +4 -4
  190. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
  191. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/.zarray +4 -4
  192. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
  193. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/.zarray +4 -4
  194. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
  195. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/.zarray +4 -4
  196. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
  197. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/.zarray +4 -4
  198. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
  199. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/.zarray +4 -4
  200. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
  201. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/.zarray +4 -4
  202. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
  203. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/.zarray +4 -4
  204. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
  205. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/.zarray +4 -4
  206. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
  207. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/.zarray +4 -4
  208. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
  209. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zarray +4 -4
  210. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +8 -0
  211. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
  212. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zarray +4 -4
  213. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +8 -0
  214. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/0.0 +0 -0
  215. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zarray +4 -4
  216. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +8 -0
  217. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
  218. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zarray +4 -4
  219. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +8 -0
  220. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/0.0 +0 -0
  221. roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +1 -1
  222. roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +2 -2
  223. roms_tools/tests/test_setup/test_data/grid.zarr/angle/0.0 +0 -0
  224. roms_tools/tests/test_setup/test_data/grid.zarr/angle_coarse/0.0 +0 -0
  225. roms_tools/tests/test_setup/test_data/grid.zarr/f/0.0 +0 -0
  226. roms_tools/tests/test_setup/test_data/grid.zarr/h/.zattrs +1 -1
  227. roms_tools/tests/test_setup/test_data/grid.zarr/h/0.0 +0 -0
  228. roms_tools/tests/test_setup/test_data/grid.zarr/lat_coarse/0.0 +0 -0
  229. roms_tools/tests/test_setup/test_data/grid.zarr/lat_rho/0.0 +0 -0
  230. roms_tools/tests/test_setup/test_data/grid.zarr/lat_u/0.0 +0 -0
  231. roms_tools/tests/test_setup/test_data/grid.zarr/lat_v/0.0 +0 -0
  232. roms_tools/tests/test_setup/test_data/grid.zarr/pm/0.0 +0 -0
  233. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +4 -4
  234. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +4 -4
  235. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
  236. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
  237. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
  238. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  239. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
  240. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
  241. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
  242. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
  243. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
  244. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
  245. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
  246. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
  247. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
  248. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
  249. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
  250. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
  251. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
  252. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
  253. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +2 -1
  254. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +6 -4
  255. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_r/.zattrs +1 -1
  256. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_w/.zattrs +1 -1
  257. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/0.0.0.0 +0 -0
  258. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/0.0.0.0 +0 -0
  259. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/0.0.0.0 +0 -0
  260. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/abs_time/.zattrs +1 -0
  261. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/0.0.0.0 +0 -0
  262. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ocean_time/.zattrs +1 -1
  263. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/0.0.0.0 +0 -0
  264. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/0.0.0.0 +0 -0
  265. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/0.0.0.0 +0 -0
  266. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/0.0.0.0 +0 -0
  267. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  268. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  269. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  270. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  271. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  272. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zeta/0.0.0 +0 -0
  273. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +56 -0
  274. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zarray +20 -0
  275. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zattrs +6 -0
  276. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/0 +0 -0
  277. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zarray +22 -0
  278. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +8 -0
  279. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/0.0 +0 -0
  280. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +56 -0
  281. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zarray +20 -0
  282. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zattrs +6 -0
  283. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/0 +0 -0
  284. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zarray +22 -0
  285. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +8 -0
  286. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/0.0 +0 -0
  287. roms_tools/tests/test_setup/test_grid.py +0 -13
  288. roms_tools/tests/test_setup/test_initial_conditions.py +220 -66
  289. roms_tools/tests/test_setup/test_nesting.py +139 -118
  290. roms_tools/tests/test_setup/test_river_forcing.py +583 -293
  291. roms_tools/tests/test_setup/test_surface_forcing.py +149 -73
  292. roms_tools/tests/test_setup/test_tides.py +4 -16
  293. roms_tools/tests/test_setup/test_utils.py +1 -0
  294. roms_tools/tests/test_setup/test_validation.py +34 -2
  295. roms_tools/tests/{test_utils.py → test_tiling/test_partition.py} +1 -1
  296. roms_tools/tiling/partition.py +338 -0
  297. roms_tools/utils.py +66 -333
  298. roms_tools/vertical_coordinate.py +54 -133
  299. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/METADATA +1 -1
  300. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/RECORD +303 -290
  301. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/WHEEL +1 -1
  302. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/LICENSE +0 -0
  303. {roms_tools-2.3.0.dist-info → roms_tools-2.5.0.dist-info}/top_level.txt +0 -0
@@ -3,13 +3,14 @@ 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
10
10
  from pathlib import Path
11
11
  from roms_tools import Grid
12
12
  from roms_tools.regrid import LateralRegrid, VerticalRegrid
13
+ from roms_tools.utils import save_datasets
13
14
  from roms_tools.vertical_coordinate import compute_depth
14
15
  from roms_tools.plot import _section_plot, _line_plot
15
16
  from roms_tools.utils import (
@@ -21,7 +22,6 @@ from roms_tools.setup.datasets import GLORYSDataset, CESMBGCDataset
21
22
  from roms_tools.setup.utils import (
22
23
  get_variable_metadata,
23
24
  group_dataset,
24
- save_datasets,
25
25
  get_target_coords,
26
26
  rotate_velocities,
27
27
  compute_barotropic_velocity,
@@ -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]
@@ -66,10 +70,14 @@ class BoundaryForcing:
66
70
  - "physics": for physical atmospheric forcing.
67
71
  - "bgc": for biogeochemical forcing.
68
72
 
69
- apply_2d_horizontal_fill: bool, optional
73
+ apply_2d_horizontal_fill : bool, optional
70
74
  Indicates whether to perform a two-dimensional horizontal fill on the source data prior to regridding to boundaries.
71
75
  If `False`, a one-dimensional horizontal fill is performed separately on each of the four regridded boundaries.
72
76
  Defaults to `False`.
77
+ adjust_depth_for_sea_surface_height : bool, optional
78
+ Whether to account for sea surface height (`zeta`) variations when computing depth coordinates.
79
+ This adjustment is only applicable for `type="physics"`, as for biogeochemical fields usually `zeta` is not available.
80
+ Defaults to `False`.
73
81
  model_reference_date : datetime, optional
74
82
  Reference date for the model. Default is January 1, 2000.
75
83
  use_dask: bool, optional
@@ -92,8 +100,8 @@ class BoundaryForcing:
92
100
  """
93
101
 
94
102
  grid: Grid
95
- start_time: datetime
96
- end_time: datetime
103
+ start_time: Optional[datetime] = None
104
+ end_time: Optional[datetime] = None
97
105
  boundaries: Dict[str, bool] = field(
98
106
  default_factory=lambda: {
99
107
  "south": True,
@@ -105,6 +113,7 @@ class BoundaryForcing:
105
113
  source: Dict[str, Union[str, Path, List[Union[str, Path]]]]
106
114
  type: str = "physics"
107
115
  apply_2d_horizontal_fill: bool = False
116
+ adjust_depth_for_sea_surface_height: bool = False
108
117
  model_reference_date: datetime = datetime(2000, 1, 1)
109
118
  use_dask: bool = False
110
119
  bypass_validation: bool = False
@@ -114,15 +123,21 @@ class BoundaryForcing:
114
123
  def __post_init__(self):
115
124
 
116
125
  self._input_checks()
126
+ # Dataset for depth coordinates
127
+ object.__setattr__(self, "ds_depth_coords", xr.Dataset())
128
+
117
129
  target_coords = get_target_coords(self.grid)
118
130
 
119
131
  data = self._get_data()
120
132
 
121
133
  if self.apply_2d_horizontal_fill:
134
+
122
135
  data.choose_subdomain(
123
136
  target_coords,
124
137
  buffer_points=20, # lateral fill needs good buffer from data margin
125
138
  )
139
+ # Enforce double precision to ensure reproducibility
140
+ data.convert_to_float64()
126
141
  data.extrapolate_deepest_to_bottom()
127
142
  data.apply_lateral_fill()
128
143
 
@@ -150,17 +165,20 @@ class BoundaryForcing:
150
165
  )
151
166
 
152
167
  if not self.apply_2d_horizontal_fill:
168
+ # Enforce double precision to ensure reproducibility
169
+ bdry_data.convert_to_float64()
153
170
  bdry_data.extrapolate_deepest_to_bottom()
154
171
 
155
172
  processed_fields = {}
156
173
 
157
- # lateral regridding of vector fields
158
- vector_var_names = [
159
- name
160
- for name, info in self.variable_info.items()
161
- if info["is_vector"]
162
- ]
163
- if len(vector_var_names) > 0:
174
+ # vector fields (velocities) are only present in physics datasets
175
+ if self.type == "physics":
176
+ # lateral regridding of vector fields
177
+ vector_var_names = [
178
+ name
179
+ for name, info in self.variable_info.items()
180
+ if info["is_vector"]
181
+ ]
164
182
  lon = target_coords["lon"].isel(
165
183
  **self.bdry_coords["vector"][direction]
166
184
  )
@@ -176,6 +194,13 @@ class BoundaryForcing:
176
194
  bdry_data.ds[bdry_data.var_names[var_name]]
177
195
  )
178
196
 
197
+ if self.adjust_depth_for_sea_surface_height:
198
+ # Regrid sea surface height ('zeta') onto a 2-cell-wide margin.
199
+ # This is needed to correctly infer depth coordinates at u- and v-points along the boundary.
200
+ zeta_vector = lateral_regrid.apply(
201
+ bdry_data.ds[bdry_data.var_names["zeta"]]
202
+ )
203
+
179
204
  # lateral regridding of tracer fields
180
205
  tracer_var_names = [
181
206
  name
@@ -209,6 +234,9 @@ class BoundaryForcing:
209
234
  angle,
210
235
  interpolate=True,
211
236
  )
237
+ if self.adjust_depth_for_sea_surface_height:
238
+ zeta_u = interpolate_from_rho_to_u(zeta_vector)
239
+ zeta_v = interpolate_from_rho_to_v(zeta_vector)
212
240
 
213
241
  # selection of outermost margin for u/v variables
214
242
  for var_name in self.variable_info.keys():
@@ -218,6 +246,9 @@ class BoundaryForcing:
218
246
  processed_fields[var_name] = processed_fields[
219
247
  var_name
220
248
  ].isel(**self.bdry_coords[location][direction])
249
+ if self.adjust_depth_for_sea_surface_height:
250
+ zeta_u = zeta_u.isel(**self.bdry_coords["u"][direction])
251
+ zeta_v = zeta_v.isel(**self.bdry_coords["v"][direction])
221
252
 
222
253
  if not self.apply_2d_horizontal_fill:
223
254
  self._validate_1d_fill(
@@ -225,7 +256,20 @@ class BoundaryForcing:
225
256
  direction,
226
257
  bdry_data.dim_names["depth"],
227
258
  )
228
- processed_fields = apply_1d_horizontal_fill(processed_fields)
259
+ for var_name in processed_fields.keys():
260
+ processed_fields[var_name] = apply_1d_horizontal_fill(
261
+ processed_fields[var_name]
262
+ )
263
+ if self.adjust_depth_for_sea_surface_height:
264
+ zeta_u = apply_1d_horizontal_fill(zeta_u)
265
+ zeta_v = apply_1d_horizontal_fill(zeta_v)
266
+
267
+ if self.adjust_depth_for_sea_surface_height:
268
+ zeta = processed_fields["zeta"]
269
+ else:
270
+ zeta = 0
271
+ zeta_u = 0
272
+ zeta_v = 0
229
273
 
230
274
  var_names_dict = {}
231
275
  for location in ["rho", "u", "v"]:
@@ -234,24 +278,22 @@ class BoundaryForcing:
234
278
  for name, info in self.variable_info.items()
235
279
  if info["location"] == location and info["is_3d"]
236
280
  ]
281
+
237
282
  # compute layer depth coordinates
283
+ if len(var_names_dict["rho"]) > 0:
284
+ self._get_depth_coordinates(zeta, direction, "rho", "layer")
285
+ self._get_depth_coordinates(
286
+ zeta, direction, "rho", "interface"
287
+ ) # only necessary for plotting
238
288
  if len(var_names_dict["u"]) > 0 or len(var_names_dict["v"]) > 0:
239
- self._get_vertical_coordinates(
240
- type="layer",
241
- direction=direction,
242
- additional_locations=["u", "v"],
243
- )
244
- else:
245
- if len(var_names_dict["rho"]) > 0:
246
- self._get_vertical_coordinates(
247
- type="layer", direction=direction, additional_locations=[]
248
- )
289
+ self._get_depth_coordinates(zeta_u, direction, "u", "layer")
290
+ self._get_depth_coordinates(zeta_v, direction, "v", "layer")
249
291
 
250
292
  # vertical regridding
251
293
  for location in ["rho", "u", "v"]:
252
294
  if len(var_names_dict[location]) > 0:
253
295
  vertical_regrid = VerticalRegrid(
254
- self.grid.ds[f"layer_depth_{location}_{direction}"],
296
+ self.ds_depth_coords[f"layer_depth_{location}_{direction}"],
255
297
  bdry_data.ds[bdry_data.dim_names["depth"]],
256
298
  )
257
299
  for var_name in var_names_dict[location]:
@@ -262,17 +304,16 @@ class BoundaryForcing:
262
304
 
263
305
  # compute barotropic velocities
264
306
  if "u" in self.variable_info and "v" in self.variable_info:
265
- self._get_vertical_coordinates(
266
- type="interface",
267
- direction=direction,
268
- additional_locations=["u", "v"],
269
- )
307
+ self._get_depth_coordinates(zeta_u, direction, "u", "interface")
308
+ self._get_depth_coordinates(zeta_v, direction, "v", "interface")
270
309
  for location in ["u", "v"]:
271
310
  processed_fields[
272
311
  f"{location}bar"
273
312
  ] = compute_barotropic_velocity(
274
313
  processed_fields[location],
275
- self.grid.ds[f"interface_depth_{location}_{direction}"],
314
+ self.ds_depth_coords[
315
+ f"interface_depth_{location}_{direction}"
316
+ ],
276
317
  )
277
318
 
278
319
  # Reorder dimensions
@@ -297,6 +338,18 @@ class BoundaryForcing:
297
338
  object.__setattr__(self, "ds", ds)
298
339
 
299
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
+
300
353
  # Validate the 'type' parameter
301
354
  if self.type not in ["physics", "bgc"]:
302
355
  raise ValueError("`type` must be either 'physics' or 'bgc'.")
@@ -314,6 +367,29 @@ class BoundaryForcing:
314
367
  {**self.source, "climatology": self.source.get("climatology", False)},
315
368
  )
316
369
 
370
+ # Ensure adjust_depth_for_sea_surface_height is only used with type="physics"
371
+ if self.type == "bgc" and self.adjust_depth_for_sea_surface_height:
372
+ logging.warning(
373
+ "adjust_depth_for_sea_surface_height is not applicable for BGC fields. "
374
+ "Setting it to False."
375
+ )
376
+ object.__setattr__(self, "adjust_depth_for_sea_surface_height", False)
377
+ elif self.adjust_depth_for_sea_surface_height:
378
+ logging.info("Sea surface height will be used to adjust depth coordinates.")
379
+ else:
380
+ logging.info(
381
+ "Sea surface height will NOT be used to adjust depth coordinates."
382
+ )
383
+
384
+ if self.apply_2d_horizontal_fill:
385
+ logging.info(
386
+ "Applying 2D horizontal fill to the source data before regridding."
387
+ )
388
+ else:
389
+ logging.info(
390
+ "Applying 1D horizontal fill separately to each regridded boundary."
391
+ )
392
+
317
393
  def _get_data(self):
318
394
 
319
395
  data_dict = {
@@ -489,118 +565,80 @@ class BoundaryForcing:
489
565
 
490
566
  object.__setattr__(self, "bdry_coords", bdry_coords)
491
567
 
492
- def _get_vertical_coordinates(
493
- self, type, direction, additional_locations=["u", "v"]
494
- ):
495
- """Retrieve layer and interface depth coordinates for a specified grid boundary.
568
+ def _get_depth_coordinates(
569
+ self,
570
+ zeta: xr.DataArray | float,
571
+ direction: str,
572
+ location: str,
573
+ depth_type: str = "layer",
574
+ ) -> None:
575
+ """Compute and store depth coordinates for a specified boundary direction, grid
576
+ location, and depth type.
496
577
 
497
- This method computes and updates the layer and interface depth coordinates along a specified
498
- boundary (north, south, east, or west). It handles depth calculations for rho points and
499
- additional specified locations (u and v).
578
+ This method efficiently computes depth coordinates along the specified boundary without
579
+ interpolating the entire domain topography. The computed depth values are stored in
580
+ `self.ds_depth_coords`.
500
581
 
501
582
  Parameters
502
583
  ----------
503
- type : str
504
- The type of depth coordinate to retrieve. Valid options are:
505
- - "layer": Retrieves layer depth coordinates.
506
- - "interface": Retrieves interface depth coordinates.
507
-
584
+ zeta : xr.DataArray or float
585
+ Free-surface elevation (`zeta`). Can be:
586
+ - A scalar float value (constant sea surface height).
587
+ - An `xarray.DataArray` with spatial variations. If provided as an array, it may have a
588
+ time dimension, but must be **1D** (varying only in time).
508
589
  direction : str
509
- The direction of the boundary to retrieve coordinates for. Valid options are:
590
+ The boundary direction for which depth coordinates are computed. Must be one of:
510
591
  - "north"
511
592
  - "south"
512
593
  - "east"
513
594
  - "west"
595
+ location : str
596
+ Grid location at which depth is computed. Must be one of:
597
+ - `"rho"`: Depth at scalar grid points.
598
+ - `"u"`: Depth at U-velocity grid points.
599
+ - `"v"`: Depth at V-velocity grid points.
600
+ depth_type : str, optional
601
+ Type of depth coordinate to compute, either:
602
+ - `"layer"` (default): Depth at vertical layer midpoints.
603
+ - `"interface"`: Depth at vertical layer interfaces.
514
604
 
515
- additional_locations : list of str, optional
516
- Specifies additional locations to compute depth coordinates for. Default is ["u", "v"].
517
- Valid options include:
518
- - "u": Computes depth coordinates for u points.
519
- - "v": Computes depth coordinates for v points.
520
-
521
- Updates
522
- -------
523
- self.grid.ds : xarray.Dataset
524
- The dataset is updated with the following vertical depth coordinates:
525
- - f"{type}_depth_rho_{direction}": Depth coordinates at rho points.
526
- - f"{type}_depth_u_{direction}": Depth coordinates at u points (if applicable).
527
- - f"{type}_depth_v_{direction}": Depth coordinates at v points (if applicable).
605
+ Notes
606
+ -----
607
+ - This method is optimized for boundary computations by selecting only the relevant margin
608
+ (2 grid cells) instead of interpolating the entire domain.
528
609
  """
529
-
530
- layer_vars = []
531
- for location in ["rho"] + additional_locations:
532
- layer_vars.append(f"{type}_depth_{location}_{direction}")
533
-
534
- if all(layer_var in self.grid.ds for layer_var in layer_vars):
535
- # Vertical coordinate data already exists
536
- pass
537
-
538
- elif f"{type}_depth_rho" in self.grid.ds:
539
- depth = self.grid.ds[f"{type}_depth_rho"]
540
- depth.attrs["long_name"] = f"{type} depth at rho-points"
541
- depth.attrs["units"] = "m"
542
- self.grid.ds[f"{type}_depth_rho_{direction}"] = depth.isel(
543
- **self.bdry_coords["rho"][direction]
544
- )
545
-
546
- if "u" in additional_locations or "v" in additional_locations:
610
+ key = f"{depth_type}_depth_{location}_{direction}"
611
+ if key not in self.ds_depth_coords:
612
+ if location in ["u", "v"]:
547
613
  # selection of margin consisting of 2 grid cells
548
- depth = depth.isel(**self.bdry_coords["vector"][direction])
549
- # interpolation
550
- if "u" in additional_locations:
551
- depth_u = interpolate_from_rho_to_u(depth)
552
- depth_u.attrs["long_name"] = f"{type} depth at u-points"
553
- depth_u.attrs["units"] = "m"
554
- self.grid.ds[f"{type}_depth_u_{direction}"] = depth_u.isel(
555
- **self.bdry_coords["u"][direction]
556
- )
557
- if "v" in additional_locations:
558
- depth_v = interpolate_from_rho_to_v(depth)
559
- depth_v.attrs["long_name"] = f"{type} depth at v-points"
560
- depth_v.attrs["units"] = "m"
561
- self.grid.ds[f"{type}_depth_v_{direction}"] = depth_v.isel(
562
- **self.bdry_coords["v"][direction]
563
- )
564
- else:
565
- if "u" in additional_locations or "v" in additional_locations:
566
614
  h = self.grid.ds["h"].isel(**self.bdry_coords["vector"][direction])
615
+ if location == "u":
616
+ h = interpolate_from_rho_to_u(h)
617
+ h = h.isel(**self.bdry_coords["u"][direction])
618
+ elif location == "v":
619
+ h = interpolate_from_rho_to_v(h)
620
+ h = h.isel(**self.bdry_coords["v"][direction])
567
621
  else:
568
622
  h = self.grid.ds["h"].isel(**self.bdry_coords["rho"][direction])
569
- if type == "layer":
623
+
624
+ if depth_type == "layer":
570
625
  depth = compute_depth(
571
- 0, h, self.grid.hc, self.grid.ds.Cs_r, self.grid.ds.sigma_r
626
+ zeta, h, self.grid.hc, self.grid.ds.Cs_r, self.grid.ds.sigma_r
572
627
  )
573
628
  else:
574
629
  depth = compute_depth(
575
- 0, h, self.grid.hc, self.grid.ds.Cs_w, self.grid.ds.sigma_w
630
+ zeta, h, self.grid.hc, self.grid.ds.Cs_w, self.grid.ds.sigma_w
576
631
  )
577
632
 
578
- if "u" in additional_locations or "v" in additional_locations:
579
- depth.attrs["long_name"] = f"{type} depth at rho-points"
580
- depth.attrs["units"] = "m"
581
- self.grid.ds[f"{type}_depth_rho_{direction}"] = depth.isel(
582
- **self.bdry_coords["rho"][direction]
583
- )
584
- # selection of margin consisting of 2 grid cells
585
- depth = depth.isel(**self.bdry_coords["vector"][direction])
586
- # interpolation
587
- depth_u = interpolate_from_rho_to_u(depth)
588
- depth_v = interpolate_from_rho_to_v(depth)
589
- # selection of outermost margin
590
- depth_u.attrs["long_name"] = f"{type} depth at u-points"
591
- depth_u.attrs["units"] = "m"
592
- self.grid.ds[f"{type}_depth_u_{direction}"] = depth_u.isel(
593
- **self.bdry_coords["u"][direction]
594
- )
595
- depth_v.attrs["long_name"] = f"{type} depth at v-points"
596
- depth_v.attrs["units"] = "m"
597
- self.grid.ds[f"{type}_depth_v_{direction}"] = depth_v.isel(
598
- **self.bdry_coords["v"][direction]
599
- )
600
- else:
601
- depth.attrs["long_name"] = f"{type} depth at rho-points"
602
- depth.attrs["units"] = "m"
603
- self.grid.ds[f"{type}_depth_rho_{direction}"] = depth
633
+ # Add metadata
634
+ depth.attrs.update(
635
+ {
636
+ "long_name": f"{depth_type} depth at {location}-points along {direction}ern boundary",
637
+ "units": "m",
638
+ }
639
+ )
640
+
641
+ self.ds_depth_coords[key] = depth
604
642
 
605
643
  def _add_global_metadata(self, data, ds=None):
606
644
 
@@ -617,6 +655,10 @@ class BoundaryForcing:
617
655
  ds.attrs["end_time"] = str(self.end_time)
618
656
  ds.attrs["source"] = self.source["name"]
619
657
  ds.attrs["model_reference_date"] = str(self.model_reference_date)
658
+ ds.attrs["apply_2d_horizontal_fill"] = str(self.apply_2d_horizontal_fill)
659
+ ds.attrs["adjust_depth_for_sea_surface_height"] = str(
660
+ self.adjust_depth_for_sea_surface_height
661
+ )
620
662
 
621
663
  ds.attrs["theta_s"] = self.grid.ds.attrs["theta_s"]
622
664
  ds.attrs["theta_b"] = self.grid.ds.attrs["theta_b"]
@@ -840,9 +882,10 @@ class BoundaryForcing:
840
882
  mask = mask.isel(**self.bdry_coords[location][direction])
841
883
 
842
884
  if "s_rho" in field.dims:
843
- field = field.assign_coords(
844
- {"layer_depth": self.grid.ds[f"layer_depth_{location}_{direction}"]}
845
- )
885
+ layer_depth = self.ds_depth_coords[f"layer_depth_{location}_{direction}"]
886
+ if self.adjust_depth_for_sea_surface_height:
887
+ layer_depth = layer_depth.isel(time=time)
888
+ field = field.assign_coords({"layer_depth": layer_depth})
846
889
  if var_name.startswith(("u", "v", "ubar", "vbar", "zeta")):
847
890
  vmax = max(field.max().values, -field.min().values)
848
891
  vmin = -vmax
@@ -859,19 +902,11 @@ class BoundaryForcing:
859
902
 
860
903
  if len(field.dims) == 2:
861
904
  if layer_contours:
862
- if location in ["u", "v"]:
863
- additional_locations = ["u", "v"]
864
- else:
865
- additional_locations = []
866
- self._get_vertical_coordinates(
867
- type="interface",
868
- direction=direction,
869
- additional_locations=additional_locations,
870
- )
871
-
872
- interface_depth = self.grid.ds[
905
+ interface_depth = self.ds_depth_coords[
873
906
  f"interface_depth_{location}_{direction}"
874
907
  ]
908
+ if self.adjust_depth_for_sea_surface_height:
909
+ interface_depth = interface_depth.isel(time=time)
875
910
  # restrict number of layer_contours to 10 for the sake of plot clearity
876
911
  nr_layers = len(interface_depth["s_w"])
877
912
  selected_layers = np.linspace(
@@ -883,7 +918,7 @@ class BoundaryForcing:
883
918
  interface_depth = None
884
919
 
885
920
  _section_plot(
886
- field.where(mask),
921
+ field,
887
922
  interface_depth=interface_depth,
888
923
  title=title,
889
924
  kwargs=kwargs,
@@ -895,37 +930,21 @@ class BoundaryForcing:
895
930
  def save(
896
931
  self,
897
932
  filepath: Union[str, Path],
898
- np_eta: int = None,
899
- np_xi: int = None,
900
- group: bool = False,
933
+ group: bool = True,
901
934
  ) -> None:
902
935
  """Save the boundary forcing fields to one or more netCDF4 files.
903
936
 
904
- This method saves the dataset either as a single file or as multiple files depending on the partitioning and grouping options.
905
- The dataset can be saved in two modes:
906
-
907
- 1. **Single File Mode (default)**:
908
- - If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
909
- - The file is named based on the `filepath`, with `.nc` automatically appended.
910
-
911
- 2. **Partitioned Mode**:
912
- - If either `np_eta` or `np_xi` is specified, the dataset is partitioned into spatial tiles along the `eta` and `xi` axes.
913
- - Each tile is saved as a separate netCDF4 file, and filenames are modified with an index (e.g., `"filepath_YYYYMM.0.nc"`, `"filepath_YYYYMM.1.nc"`).
914
-
915
- Additionally, if `group` is set to `True`, the dataset is first grouped into temporal subsets, resulting in multiple grouped files before partitioning and saving.
937
+ This method saves the dataset to disk as either a single netCDF4 file or multiple files, depending on the `group` parameter.
938
+ If `group` is `True`, the dataset is divided into subsets (e.g., monthly or yearly) based on the temporal frequency
939
+ of the data, and each subset is saved to a separate file.
916
940
 
917
941
  Parameters
918
942
  ----------
919
943
  filepath : Union[str, Path]
920
- The base path and filename for the output files. The format of the filenames depends on whether partitioning is used
921
- and the temporal range of the data. For partitioned datasets, files will be named with an additional index, e.g.,
922
- `"filepath_YYYYMM.0.nc"`, `"filepath_YYYYMM.1.nc"`, etc.
923
- np_eta : int, optional
924
- The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed.
925
- np_xi : int, optional
926
- The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed.
927
- group: bool, optional
928
- If `True`, groups the dataset into multiple files based on temporal data frequency. Defaults to `False`.
944
+ The base path and filename for the output file(s). If `group` is `True`, the filenames will include additional
945
+ time-based information (e.g., year or month) to distinguish the subsets.
946
+ group : bool, optional
947
+ Whether to divide the dataset into multiple files based on temporal frequency. Defaults to `True`.
929
948
 
930
949
  Returns
931
950
  -------
@@ -940,12 +959,6 @@ class BoundaryForcing:
940
959
  if filepath.suffix == ".nc":
941
960
  filepath = filepath.with_suffix("")
942
961
 
943
- if self.use_dask:
944
- from dask.diagnostics import ProgressBar
945
-
946
- with ProgressBar():
947
- self.ds.load()
948
-
949
962
  if group:
950
963
  dataset_list, output_filenames = group_dataset(self.ds, str(filepath))
951
964
  else:
@@ -953,7 +966,7 @@ class BoundaryForcing:
953
966
  output_filenames = [str(filepath)]
954
967
 
955
968
  saved_filenames = save_datasets(
956
- dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
969
+ dataset_list, output_filenames, use_dask=self.use_dask
957
970
  )
958
971
 
959
972
  return saved_filenames
@@ -975,7 +988,6 @@ class BoundaryForcing:
975
988
  cls,
976
989
  filepath: Union[str, Path],
977
990
  use_dask: bool = False,
978
- bypass_validation: bool = False,
979
991
  ) -> "BoundaryForcing":
980
992
  """Create an instance of the BoundaryForcing class from a YAML file.
981
993
 
@@ -985,10 +997,6 @@ class BoundaryForcing:
985
997
  The path to the YAML file from which the parameters will be read.
986
998
  use_dask: bool, optional
987
999
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
988
- bypass_validation: bool, optional
989
- Indicates whether to skip validation checks in the processed data. When set to True,
990
- the validation process that ensures no NaN values exist at wet points
991
- in the processed dataset is bypassed. Defaults to False.
992
1000
 
993
1001
  Returns
994
1002
  -------
@@ -1001,24 +1009,22 @@ class BoundaryForcing:
1001
1009
  params = _from_yaml(cls, filepath)
1002
1010
 
1003
1011
  # Create and return an instance of InitialConditions
1004
- return cls(
1005
- grid=grid, **params, use_dask=use_dask, bypass_validation=bypass_validation
1006
- )
1012
+ return cls(grid=grid, **params, use_dask=use_dask)
1007
1013
 
1008
1014
 
1009
- def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
1010
- """Forward and backward fill NaN values in horizontal direction for open boundaries.
1015
+ def apply_1d_horizontal_fill(data_array: xr.DataArray) -> xr.DataArray:
1016
+ """Forward and backward fill NaN values in a single horizontal dimension for open
1017
+ boundaries.
1011
1018
 
1012
1019
  Parameters
1013
1020
  ----------
1014
- processed_fields : dict
1015
- A dictionary of variables to be updated, where each value is an
1016
- `xarray.DataArray`.
1021
+ data_array : xarray.DataArray
1022
+ The data array to be updated.
1017
1023
 
1018
1024
  Returns
1019
1025
  -------
1020
- dict of str : xarray.DataArray
1021
- The updated dictionary of variables, with NaN values filled.
1026
+ xarray.DataArray
1027
+ The updated data array with NaN values filled.
1022
1028
 
1023
1029
  Raises
1024
1030
  ------
@@ -1027,29 +1033,22 @@ def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
1027
1033
  """
1028
1034
 
1029
1035
  horizontal_dims = ["eta_rho", "eta_v", "xi_rho", "xi_u"]
1036
+ selected_horizontal_dim = None
1030
1037
 
1031
- for var_name in processed_fields.keys():
1032
- selected_horizontal_dim = None
1033
- # Determine the horizontal dimension to fill
1034
- for dim in horizontal_dims:
1035
- if dim in processed_fields[var_name].dims:
1036
- if selected_horizontal_dim is not None:
1037
- raise ValueError(
1038
- f"More than one horizontal dimension found in variable '{var_name}'."
1039
- )
1040
- selected_horizontal_dim = dim
1041
-
1042
- if selected_horizontal_dim is None:
1043
- raise ValueError(
1044
- f"No valid horizontal dimension found for variable '{var_name}'."
1045
- )
1038
+ # Determine the horizontal dimension to fill
1039
+ for dim in horizontal_dims:
1040
+ if dim in data_array.dims:
1041
+ if selected_horizontal_dim is not None:
1042
+ raise ValueError(
1043
+ f"More than one horizontal dimension found in variable '{data_array.name}'."
1044
+ )
1045
+ selected_horizontal_dim = dim
1046
1046
 
1047
- # Forward and backward fill in the horizontal direction
1048
- filled = one_dim_fill(
1049
- processed_fields[var_name], selected_horizontal_dim, direction="forward"
1050
- )
1051
- processed_fields[var_name] = one_dim_fill(
1052
- filled, selected_horizontal_dim, direction="backward"
1047
+ if selected_horizontal_dim is None:
1048
+ raise ValueError(
1049
+ f"No valid horizontal dimension found for variable '{data_array.name}'."
1053
1050
  )
1054
1051
 
1055
- return processed_fields
1052
+ # Forward and backward fill in the horizontal direction
1053
+ filled = one_dim_fill(data_array, selected_horizontal_dim, direction="forward")
1054
+ return one_dim_fill(filled, selected_horizontal_dim, direction="backward")