roms-tools 1.4.2__py3-none-any.whl → 1.6.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 (278) hide show
  1. roms_tools/_version.py +1 -1
  2. roms_tools/setup/boundary_forcing.py +448 -130
  3. roms_tools/setup/datasets.py +186 -52
  4. roms_tools/setup/fill.py +2 -2
  5. roms_tools/setup/initial_conditions.py +217 -70
  6. roms_tools/setup/regrid.py +143 -0
  7. roms_tools/setup/surface_forcing.py +159 -73
  8. roms_tools/setup/tides.py +141 -54
  9. roms_tools/setup/utils.py +229 -62
  10. roms_tools/tests/test_setup/test_boundary_forcing.py +42 -32
  11. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zattrs +1 -1
  12. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zmetadata +1 -1
  13. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/0.0.0 +0 -0
  14. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_north/0.0.0 +0 -0
  15. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/0.0.0 +0 -0
  16. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/0.0.0 +0 -0
  17. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/0.0.0 +0 -0
  18. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_north/0.0.0 +0 -0
  19. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/0.0.0 +0 -0
  20. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
  21. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
  22. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_north/0.0.0 +0 -0
  23. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
  25. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_north/0.0.0 +0 -0
  27. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_north/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_north/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_north/0.0.0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_north/0.0.0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_north/0.0.0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_north/0.0.0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_north/0.0.0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_north/0.0.0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/0.0.0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_north/0.0.0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_north/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_north/0.0.0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_north/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/0.0.0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/0.0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/0.0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/0.0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/0.0.0 +0 -0
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/0.0.0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
  125. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
  126. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/0.0.0 +0 -0
  127. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
  128. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
  129. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
  130. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/0.0.0 +0 -0
  131. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
  132. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
  133. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
  134. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/0.0.0 +0 -0
  135. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
  136. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
  137. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
  138. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/0.0.0 +0 -0
  139. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
  140. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
  141. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zattrs +1 -1
  142. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zmetadata +1 -1
  143. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust/0.0.0 +0 -0
  144. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron/0.0.0 +0 -0
  145. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy/0.0.0 +0 -0
  146. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox/0.0.0 +0 -0
  147. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air/0.0.0 +0 -0
  148. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air_alt/0.0.0 +0 -0
  149. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zattrs +1 -1
  150. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zmetadata +1 -1
  151. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust/0.0.0 +0 -0
  152. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron/0.0.0 +0 -0
  153. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy/0.0.0 +0 -0
  154. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox/0.0.0 +0 -0
  155. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air/0.0.0 +0 -0
  156. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air_alt/0.0.0 +0 -0
  157. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zattrs +1 -1
  158. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +8 -1
  159. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +3 -0
  160. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
  161. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/0.0.0 +0 -0
  162. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
  163. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/0.0.0 +0 -0
  164. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
  165. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/0.0.0 +0 -0
  166. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
  167. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/0.0.0 +0 -0
  168. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
  169. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/0.0.0 +0 -0
  170. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
  171. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
  172. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/0.0 +0 -0
  173. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
  174. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
  175. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
  176. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
  177. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
  178. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
  179. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
  180. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
  181. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
  182. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +1 -0
  183. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
  184. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +1 -0
  185. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +1 -0
  186. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
  187. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +1 -0
  188. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/.zattrs +1 -1
  189. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/.zmetadata +1 -1
  190. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/Tair/0.0.0 +0 -0
  191. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/lwrad/0.0.0 +0 -0
  192. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/qair/0.0.0 +0 -0
  193. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/rain/0.0.0 +0 -0
  194. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/swrad/0.0.0 +0 -0
  195. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/uwnd/0.0.0 +0 -0
  196. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/vwnd/0.0.0 +0 -0
  197. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/.zattrs +1 -1
  198. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/.zmetadata +1 -1
  199. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/Tair/0.0.0 +0 -0
  200. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/lwrad/0.0.0 +0 -0
  201. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/qair/0.0.0 +0 -0
  202. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/rain/0.0.0 +0 -0
  203. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/swrad/0.0.0 +0 -0
  204. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/uwnd/0.0.0 +0 -0
  205. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/vwnd/0.0.0 +0 -0
  206. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +1 -1
  207. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +1 -1
  208. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK/0.0.0.0 +0 -0
  209. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK_ALT_CO2/0.0.0.0 +0 -0
  210. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC/0.0.0.0 +0 -0
  211. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC_ALT_CO2/0.0.0.0 +0 -0
  212. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOC/0.0.0.0 +0 -0
  213. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOCr/0.0.0.0 +0 -0
  214. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DON/0.0.0.0 +0 -0
  215. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DONr/0.0.0.0 +0 -0
  216. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOP/0.0.0.0 +0 -0
  217. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOPr/0.0.0.0 +0 -0
  218. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Fe/0.0.0.0 +0 -0
  219. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Lig/0.0.0.0 +0 -0
  220. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/0.0.0.0 +0 -0
  221. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/0.0.0.0 +0 -0
  222. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/O2/0.0.0.0 +0 -0
  223. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/0.0.0.0 +0 -0
  224. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/SiO3/0.0.0.0 +0 -0
  225. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatC/0.0.0.0 +0 -0
  226. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatChl/0.0.0.0 +0 -0
  227. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatFe/0.0.0.0 +0 -0
  228. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatP/0.0.0.0 +0 -0
  229. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/0.0.0.0 +0 -0
  230. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazC/0.0.0.0 +0 -0
  231. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazChl/0.0.0.0 +0 -0
  232. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazFe/0.0.0.0 +0 -0
  233. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazP/0.0.0.0 +0 -0
  234. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/0.0.0.0 +0 -0
  235. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/0.0.0.0 +0 -0
  236. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/0.0.0.0 +0 -0
  237. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spChl/0.0.0.0 +0 -0
  238. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/0.0.0.0 +0 -0
  239. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spP/0.0.0.0 +0 -0
  240. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  241. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  242. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  243. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  244. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  245. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zeta/0.0.0 +0 -0
  246. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zooC/0.0.0.0 +0 -0
  247. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/.zattrs +1 -1
  248. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/.zmetadata +1 -1
  249. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/Tair/0.0.0 +0 -0
  250. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/lwrad/0.0.0 +0 -0
  251. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/qair/0.0.0 +0 -0
  252. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/rain/0.0.0 +0 -0
  253. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/swrad/0.0.0 +0 -0
  254. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/uwnd/0.0.0 +0 -0
  255. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/vwnd/0.0.0 +0 -0
  256. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/.zattrs +1 -1
  257. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/.zmetadata +4 -2
  258. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/omega/.zattrs +3 -1
  259. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/pot_Im/0.0.0 +0 -0
  260. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/pot_Re/0.0.0 +0 -0
  261. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/ssh_Im/0.0.0 +0 -0
  262. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/ssh_Re/0.0.0 +0 -0
  263. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Im/0.0.0 +0 -0
  264. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Re/0.0.0 +0 -0
  265. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Im/0.0.0 +0 -0
  266. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Re/0.0.0 +0 -0
  267. roms_tools/tests/test_setup/test_datasets.py +79 -21
  268. roms_tools/tests/test_setup/test_fill.py +55 -113
  269. roms_tools/tests/test_setup/test_initial_conditions.py +21 -21
  270. roms_tools/tests/test_setup/test_regrid.py +53 -0
  271. roms_tools/tests/test_setup/test_surface_forcing.py +21 -3
  272. roms_tools/tests/test_setup/test_tides.py +1 -1
  273. {roms_tools-1.4.2.dist-info → roms_tools-1.6.0.dist-info}/METADATA +13 -4
  274. {roms_tools-1.4.2.dist-info → roms_tools-1.6.0.dist-info}/RECORD +277 -276
  275. {roms_tools-1.4.2.dist-info → roms_tools-1.6.0.dist-info}/WHEEL +1 -1
  276. roms_tools/setup/mixins.py +0 -227
  277. {roms_tools-1.4.2.dist-info → roms_tools-1.6.0.dist-info}/LICENSE +0 -0
  278. {roms_tools-1.4.2.dist-info → roms_tools-1.6.0.dist-info}/top_level.txt +0 -0
@@ -3,19 +3,24 @@ import numpy as np
3
3
  import pandas as pd
4
4
  import yaml
5
5
  import importlib.metadata
6
+ import warnings
6
7
  from typing import Dict, Union, List
7
8
  from dataclasses import dataclass, field, asdict
8
9
  from roms_tools.setup.grid import Grid
9
- from roms_tools.setup.mixins import ROMSToolsMixins
10
+ from roms_tools.setup.regrid import LateralRegrid, VerticalRegrid
10
11
  from datetime import datetime
11
12
  from roms_tools.setup.datasets import GLORYSDataset, CESMBGCDataset
12
13
  from roms_tools.setup.utils import (
13
- nan_check,
14
- substitute_nans_by_fillvalue,
15
14
  get_variable_metadata,
16
- get_boundary_info,
17
15
  group_dataset,
18
16
  save_datasets,
17
+ get_target_coords,
18
+ rotate_velocities,
19
+ compute_barotropic_velocity,
20
+ transpose_dimensions,
21
+ one_dim_fill,
22
+ nan_check,
23
+ substitute_nans_by_fillvalue,
19
24
  )
20
25
  from roms_tools.setup.plot import _section_plot, _line_plot
21
26
  import matplotlib.pyplot as plt
@@ -23,7 +28,7 @@ from pathlib import Path
23
28
 
24
29
 
25
30
  @dataclass(frozen=True, kw_only=True)
26
- class BoundaryForcing(ROMSToolsMixins):
31
+ class BoundaryForcing:
27
32
  """Represents boundary forcing input data for ROMS.
28
33
 
29
34
  Parameters
@@ -55,6 +60,10 @@ class BoundaryForcing(ROMSToolsMixins):
55
60
 
56
61
  model_reference_date : datetime, optional
57
62
  Reference date for the model. Default is January 1, 2000.
63
+ apply_2d_horizontal_fill: bool, optional
64
+ Indicates whether to perform a two-dimensional horizontal fill on the source data prior to regridding to boundaries.
65
+ If `False`, a one-dimensional horizontal fill is performed separately on each of the four regridded boundaries.
66
+ Defaults to `False`.
58
67
  use_dask: bool, optional
59
68
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
60
69
 
@@ -84,6 +93,7 @@ class BoundaryForcing(ROMSToolsMixins):
84
93
  source: Dict[str, Union[str, Path, List[Union[str, Path]]]]
85
94
  type: str = "physics"
86
95
  model_reference_date: datetime = datetime(2000, 1, 1)
96
+ apply_2d_horizontal_fill: bool = False
87
97
  use_dask: bool = False
88
98
 
89
99
  ds: xr.Dataset = field(init=False, repr=False)
@@ -91,52 +101,153 @@ class BoundaryForcing(ROMSToolsMixins):
91
101
  def __post_init__(self):
92
102
 
93
103
  self._input_checks()
94
- lon, lat, angle, straddle = super()._get_target_lon_lat()
104
+ target_coords = get_target_coords(self.grid)
95
105
 
96
106
  data = self._get_data()
97
- data.choose_subdomain(
98
- latitude_range=[lat.min().values, lat.max().values],
99
- longitude_range=[lon.min().values, lon.max().values],
100
- margin=2,
101
- straddle=straddle,
102
- )
103
-
104
- if self.type == "physics":
105
- vars_2d = ["zeta"]
106
- vars_3d = ["temp", "salt", "u", "v"]
107
- elif self.type == "bgc":
108
- vars_2d = []
109
- vars_3d = data.var_names.keys()
110
-
111
- data_vars = super()._regrid_data(data, vars_2d, vars_3d, lon, lat)
112
107
 
113
- if self.type == "physics":
114
- data_vars = super()._process_velocities(data_vars, angle, "u", "v")
115
- object.__setattr__(data, "data_vars", data_vars)
108
+ if self.apply_2d_horizontal_fill:
109
+ data.choose_subdomain(
110
+ target_coords,
111
+ buffer_points=20, # lateral fill needs good buffer from data margin
112
+ )
113
+ data.extrapolate_deepest_to_bottom()
114
+ data.apply_lateral_fill()
116
115
 
117
- d_meta = get_variable_metadata()
116
+ variable_info = self._set_variable_info(data)
118
117
  bdry_coords = get_boundary_info()
118
+ ds = xr.Dataset()
119
119
 
120
- ds = self._write_into_dataset(data, d_meta, bdry_coords)
121
-
122
- # NaN values at wet points indicate that the raw data did not cover the domain, and the following will raise a ValueError
123
- # this check works only for 2D fields because for 3D I extrapolate to bottom which eliminates NaNs
124
120
  for direction in ["south", "east", "north", "west"]:
125
121
  if self.boundaries[direction]:
126
- if type == "physics":
127
- nan_check(
128
- ds[f"zeta_{direction}"].isel(bry_time=0),
129
- self.grid.ds.mask_rho.isel(**bdry_coords["rho"][direction]),
122
+
123
+ bdry_target_coords = {
124
+ "lat": target_coords["lat"].isel(
125
+ **bdry_coords["vector"][direction]
126
+ ),
127
+ "lon": target_coords["lon"].isel(
128
+ **bdry_coords["vector"][direction]
129
+ ),
130
+ "straddle": target_coords["straddle"],
131
+ }
132
+
133
+ bdry_data = data.choose_subdomain(
134
+ bdry_target_coords,
135
+ buffer_points=3,
136
+ return_copy=True,
137
+ )
138
+
139
+ if not self.apply_2d_horizontal_fill:
140
+ bdry_data.extrapolate_deepest_to_bottom()
141
+
142
+ processed_fields = {}
143
+
144
+ # lateral regridding of vector fields
145
+ vector_var_names = [
146
+ name for name, info in variable_info.items() if info["is_vector"]
147
+ ]
148
+ if len(vector_var_names) > 0:
149
+ lon = target_coords["lon"].isel(**bdry_coords["vector"][direction])
150
+ lat = target_coords["lat"].isel(**bdry_coords["vector"][direction])
151
+ lateral_regrid = LateralRegrid(
152
+ {"lat": lat, "lon": lon}, bdry_data.dim_names
153
+ )
154
+ for var_name in vector_var_names:
155
+ if var_name in bdry_data.var_names.keys():
156
+ processed_fields[var_name] = lateral_regrid.apply(
157
+ bdry_data.ds[bdry_data.var_names[var_name]]
158
+ )
159
+
160
+ # lateral regridding of tracer fields
161
+ tracer_var_names = [
162
+ name
163
+ for name, info in variable_info.items()
164
+ if not info["is_vector"]
165
+ ]
166
+ if len(tracer_var_names) > 0:
167
+ lon = target_coords["lon"].isel(**bdry_coords["rho"][direction])
168
+ lat = target_coords["lat"].isel(**bdry_coords["rho"][direction])
169
+ lateral_regrid = LateralRegrid(
170
+ {"lat": lat, "lon": lon}, bdry_data.dim_names
171
+ )
172
+ for var_name in tracer_var_names:
173
+ if var_name in bdry_data.var_names.keys():
174
+ processed_fields[var_name] = lateral_regrid.apply(
175
+ bdry_data.ds[bdry_data.var_names[var_name]]
176
+ )
177
+
178
+ # rotation of velocities and interpolation to u/v points
179
+ if "u" in variable_info and "v" in variable_info:
180
+ angle = target_coords["angle"].isel(
181
+ **bdry_coords["vector"][direction]
130
182
  )
131
- elif type == "bgc":
132
- nan_check(
133
- ds[f"ALK_{direction}"].isel(bry_time=0, s_rho=-1),
134
- self.grid.ds.mask_rho.isel(**bdry_coords["rho"][direction]),
183
+ (processed_fields["u"], processed_fields["v"],) = rotate_velocities(
184
+ processed_fields["u"],
185
+ processed_fields["v"],
186
+ angle,
187
+ interpolate=True,
135
188
  )
136
189
 
190
+ # selection of outermost margin for u/v variables
191
+ for var_name in variable_info.keys():
192
+ if var_name in processed_fields:
193
+ location = variable_info[var_name]["location"]
194
+ if location in ["u", "v"]:
195
+ processed_fields[var_name] = processed_fields[
196
+ var_name
197
+ ].isel(**bdry_coords[location][direction])
198
+
199
+ if not self.apply_2d_horizontal_fill:
200
+ processed_fields = apply_1d_horizontal_fill(processed_fields)
201
+
202
+ # vertical regridding
203
+ for location in ["rho", "u", "v"]:
204
+ var_names = [
205
+ name
206
+ for name, info in variable_info.items()
207
+ if info["location"] == location and info["is_3d"]
208
+ ]
209
+ if len(var_names) > 0:
210
+ vertical_regrid = VerticalRegrid(
211
+ self.grid.ds[f"layer_depth_{location}"].isel(
212
+ **bdry_coords[location][direction]
213
+ ),
214
+ bdry_data.ds[bdry_data.dim_names["depth"]],
215
+ )
216
+ for var_name in var_names:
217
+ if var_name in processed_fields:
218
+ processed_fields[var_name] = vertical_regrid.apply(
219
+ processed_fields[var_name]
220
+ )
221
+
222
+ # compute barotropic velocities
223
+ if "u" in variable_info and "v" in variable_info:
224
+ for var_name in ["u", "v"]:
225
+ processed_fields[
226
+ f"{var_name}bar"
227
+ ] = compute_barotropic_velocity(
228
+ processed_fields[var_name],
229
+ self.grid.ds[f"interface_depth_{var_name}"].isel(
230
+ **bdry_coords[var_name][direction]
231
+ ),
232
+ )
233
+
234
+ # Reorder dimensions
235
+ for var_name in processed_fields.keys():
236
+ processed_fields[var_name] = transpose_dimensions(
237
+ processed_fields[var_name]
238
+ )
239
+
240
+ # Write the boundary data into dataset
241
+ ds = self._write_into_dataset(direction, processed_fields, ds)
242
+
243
+ # Add global information
244
+ ds = self._add_global_metadata(data, ds)
245
+
246
+ self._validate(ds, variable_info, bdry_coords)
247
+
137
248
  # substitute NaNs over land by a fill value to avoid blow-up of ROMS
138
- for var in ds.data_vars:
139
- ds[var] = substitute_nans_by_fillvalue(ds[var])
249
+ for var_name in ds.data_vars:
250
+ ds[var_name] = substitute_nans_by_fillvalue(ds[var_name])
140
251
 
141
252
  object.__setattr__(self, "ds", ds)
142
253
 
@@ -187,37 +298,88 @@ class BoundaryForcing(ROMSToolsMixins):
187
298
 
188
299
  return data
189
300
 
190
- def _write_into_dataset(self, data, d_meta, bdry_coords):
301
+ def _set_variable_info(self, data):
302
+ """Sets up a dictionary with metadata for variables based on the type of data
303
+ (physics or BGC).
191
304
 
192
- # save in new dataset
193
- ds = xr.Dataset()
305
+ The dictionary contains the following information:
306
+ - `location`: Where the variable resides in the grid (e.g., rho, u, or v points).
307
+ - `is_vector`: Whether the variable is part of a vector (True for velocity components like 'u' and 'v').
308
+ - `vector_pair`: For vector variables, this indicates the associated variable that forms the vector (e.g., 'u' and 'v').
309
+ - `is_3d`: Indicates whether the variable is 3D (True for variables like 'temp' and 'salt') or 2D (False for 'zeta').
194
310
 
195
- for direction in ["south", "east", "north", "west"]:
196
- if self.boundaries[direction]:
311
+ Returns
312
+ -------
313
+ dict
314
+ A dictionary where the keys are variable names and the values are dictionaries of metadata
315
+ about each variable, including 'location', 'is_vector', 'vector_pair', and 'is_3d'.
316
+ """
317
+ default_info = {
318
+ "location": "rho",
319
+ "is_vector": False,
320
+ "vector_pair": None,
321
+ "is_3d": True,
322
+ }
197
323
 
198
- for var in data.data_vars.keys():
199
- if var in ["u", "ubar"]:
200
- ds[f"{var}_{direction}"] = (
201
- data.data_vars[var]
202
- .isel(**bdry_coords["u"][direction])
203
- .astype(np.float32)
204
- )
205
- elif var in ["v", "vbar"]:
206
- ds[f"{var}_{direction}"] = (
207
- data.data_vars[var]
208
- .isel(**bdry_coords["v"][direction])
209
- .astype(np.float32)
210
- )
211
- else:
212
- ds[f"{var}_{direction}"] = (
213
- data.data_vars[var]
214
- .isel(**bdry_coords["rho"][direction])
215
- .astype(np.float32)
216
- )
217
- ds[f"{var}_{direction}"].attrs[
218
- "long_name"
219
- ] = f"{direction}ern boundary {d_meta[var]['long_name']}"
220
- ds[f"{var}_{direction}"].attrs["units"] = d_meta[var]["units"]
324
+ # Define a dictionary for variable names and their associated information
325
+ if self.type == "physics":
326
+ variable_info = {
327
+ "zeta": {
328
+ "location": "rho",
329
+ "is_vector": False,
330
+ "vector_pair": None,
331
+ "is_3d": False,
332
+ },
333
+ "temp": default_info,
334
+ "salt": default_info,
335
+ "u": {
336
+ "location": "u",
337
+ "is_vector": True,
338
+ "vector_pair": "v",
339
+ "is_3d": True,
340
+ },
341
+ "v": {
342
+ "location": "v",
343
+ "is_vector": True,
344
+ "vector_pair": "u",
345
+ "is_3d": True,
346
+ },
347
+ "ubar": {
348
+ "location": "u",
349
+ "is_vector": True,
350
+ "vector_pair": "vbar",
351
+ "is_3d": False,
352
+ },
353
+ "vbar": {
354
+ "location": "v",
355
+ "is_vector": True,
356
+ "vector_pair": "ubar",
357
+ "is_3d": False,
358
+ },
359
+ }
360
+ elif self.type == "bgc":
361
+ variable_info = {}
362
+ for var_name in data.var_names.keys():
363
+ variable_info[var_name] = default_info
364
+
365
+ return variable_info
366
+
367
+ def _write_into_dataset(self, direction, processed_fields, ds=None):
368
+ if ds is None:
369
+ ds = xr.Dataset()
370
+
371
+ d_meta = get_variable_metadata()
372
+
373
+ for var_name in processed_fields.keys():
374
+ ds[f"{var_name}_{direction}"] = processed_fields[var_name].astype(
375
+ np.float32
376
+ )
377
+
378
+ ds[f"{var_name}_{direction}"].attrs[
379
+ "long_name"
380
+ ] = f"{direction}ern boundary {d_meta[var_name]['long_name']}"
381
+
382
+ ds[f"{var_name}_{direction}"].attrs["units"] = d_meta[var_name]["units"]
221
383
 
222
384
  # Gracefully handle dropping variables that might not be present
223
385
  variables_to_drop = [
@@ -235,59 +397,9 @@ class BoundaryForcing(ROMSToolsMixins):
235
397
  "lat_v",
236
398
  "lon_v",
237
399
  ]
238
- existing_vars = [var for var in variables_to_drop if var in ds]
400
+ existing_vars = [var_name for var_name in variables_to_drop if var_name in ds]
239
401
  ds = ds.drop_vars(existing_vars)
240
402
 
241
- ds = self._add_global_metadata(ds)
242
-
243
- # Convert the time coordinate to the format expected by ROMS
244
- if data.climatology:
245
- ds.attrs["climatology"] = str(True)
246
- # Preserve absolute time coordinate for readability
247
- ds = ds.assign_coords(
248
- {"abs_time": np.datetime64(self.model_reference_date) + ds["time"]}
249
- )
250
- # Convert to pandas TimedeltaIndex
251
- timedelta_index = pd.to_timedelta(ds["time"].values)
252
-
253
- # Determine the start of the year for the base_datetime
254
- start_of_year = datetime(self.model_reference_date.year, 1, 1)
255
-
256
- # Calculate the offset from midnight of the new year
257
- offset = self.model_reference_date - start_of_year
258
-
259
- # Convert the timedelta to nanoseconds first, then to days
260
- bry_time = xr.DataArray(
261
- (timedelta_index - offset).view("int64") / 3600 / 24 * 1e-9,
262
- dims="time",
263
- )
264
-
265
- else:
266
- # Preserve absolute time coordinate for readability
267
- ds = ds.assign_coords({"abs_time": ds["time"]})
268
- # TODO: Check if we need to convert from 12:00:00 to 00:00:00 as in matlab scripts
269
- bry_time = (
270
- (ds["time"] - np.datetime64(self.model_reference_date)).astype(
271
- "float64"
272
- )
273
- / 3600
274
- / 24
275
- * 1e-9
276
- )
277
-
278
- ds = ds.assign_coords({"bry_time": bry_time})
279
- ds["bry_time"].attrs[
280
- "long_name"
281
- ] = f"days since {str(self.model_reference_date)}"
282
- ds["bry_time"].encoding["units"] = "days"
283
- ds["bry_time"].attrs["units"] = "days"
284
- ds = ds.swap_dims({"time": "bry_time"})
285
- ds = ds.drop_vars("time")
286
- ds.encoding["unlimited_dims"] = "bry_time"
287
-
288
- if data.climatology:
289
- ds["bry_time"].attrs["cycle_length"] = 365.25
290
-
291
403
  return ds
292
404
 
293
405
  def _get_coordinates(self, direction, point):
@@ -325,7 +437,7 @@ class BoundaryForcing(ROMSToolsMixins):
325
437
 
326
438
  return layer_depth, interface_depth
327
439
 
328
- def _add_global_metadata(self, ds=None):
440
+ def _add_global_metadata(self, data, ds=None):
329
441
 
330
442
  if ds is None:
331
443
  ds = xr.Dataset()
@@ -345,11 +457,125 @@ class BoundaryForcing(ROMSToolsMixins):
345
457
  ds.attrs["theta_b"] = self.grid.ds.attrs["theta_b"]
346
458
  ds.attrs["hc"] = self.grid.ds.attrs["hc"]
347
459
 
460
+ # Convert the time coordinate to the format expected by ROMS
461
+ if data.climatology:
462
+ ds.attrs["climatology"] = str(True)
463
+ # Preserve absolute time coordinate for readability
464
+ ds = ds.assign_coords(
465
+ {"abs_time": np.datetime64(self.model_reference_date) + ds["time"]}
466
+ )
467
+ # Convert to pandas TimedeltaIndex
468
+ timedelta_index = pd.to_timedelta(ds["time"].values)
469
+
470
+ # Determine the start of the year for the base_datetime
471
+ start_of_year = datetime(self.model_reference_date.year, 1, 1)
472
+
473
+ # Calculate the offset from midnight of the new year
474
+ offset = self.model_reference_date - start_of_year
475
+
476
+ # Convert the timedelta to nanoseconds first, then to days
477
+ bry_time = xr.DataArray(
478
+ (timedelta_index - offset).view("int64") / 3600 / 24 * 1e-9,
479
+ dims="time",
480
+ )
481
+
482
+ else:
483
+ # Preserve absolute time coordinate for readability
484
+ ds = ds.assign_coords({"abs_time": ds["time"]})
485
+ bry_time = (
486
+ (ds["time"] - np.datetime64(self.model_reference_date)).astype(
487
+ "float64"
488
+ )
489
+ / 3600
490
+ / 24
491
+ * 1e-9
492
+ )
493
+
494
+ ds = ds.assign_coords({"bry_time": bry_time})
495
+ ds["bry_time"].attrs[
496
+ "long_name"
497
+ ] = f"days since {str(self.model_reference_date)}"
498
+ ds["bry_time"].encoding["units"] = "days"
499
+ ds["bry_time"].attrs["units"] = "days"
500
+ ds = ds.swap_dims({"time": "bry_time"})
501
+ ds = ds.drop_vars("time")
502
+ ds.encoding["unlimited_dims"] = "bry_time"
503
+
504
+ if data.climatology:
505
+ ds["bry_time"].attrs["cycle_length"] = 365.25
506
+
348
507
  return ds
349
508
 
509
+ def _validate(self, ds, variable_info, bdry_coords):
510
+ """Validate the dataset for NaN values at the first time step based on the fill
511
+ method used.
512
+
513
+ Parameters
514
+ ----------
515
+ ds : xarray.Dataset
516
+ The dataset to validate.
517
+
518
+ variable_info : dict
519
+ A dictionary containing metadata about the variables, including their locations (e.g., 'rho', 'u', 'v').
520
+
521
+ bdry_coords : dict
522
+ A dictionary containing the boundary coordinates for each variable location.
523
+
524
+ Raises
525
+ ------
526
+ ValueError
527
+ If NaN values are found in any of the specified variables at wet points,
528
+ indicating incomplete data coverage.
529
+
530
+ Notes
531
+ -----
532
+ Validation is performed on the initial boundary time step (`bry_time=0`) for each
533
+ variable in the dataset. If the `apply_2d_horizontal_fill` attribute is set to False,
534
+ a warning is issued instead of a strict NaN check, as the data may not be reliably validated.
535
+ Conversely, if `apply_2d_horizontal_fill` is True, a strict NaN check is performed, raising
536
+ a ValueError if any NaN values are detected.
537
+ """
538
+ if self.apply_2d_horizontal_fill:
539
+ # Strict NaN check with ValueError makes sense to be applied
540
+ for var_name in variable_info:
541
+ location = variable_info[var_name]["location"]
542
+
543
+ # Select the appropriate mask based on variable location
544
+ if location == "rho":
545
+ mask = self.grid.ds.mask_rho
546
+ elif location == "u":
547
+ mask = self.grid.ds.mask_u
548
+ elif location == "v":
549
+ mask = self.grid.ds.mask_v
550
+ else:
551
+ continue # Skip if location is not recognized
552
+
553
+ for direction in ["south", "east", "north", "west"]:
554
+ if self.boundaries[direction]:
555
+ bdry_var_name = f"{var_name}_{direction}"
556
+
557
+ # Check for NaN values at the first time step using the nan_check function
558
+ nan_check(
559
+ ds[bdry_var_name].isel(bry_time=0),
560
+ mask.isel(**bdry_coords[location][direction]),
561
+ )
562
+ else:
563
+ # Can't apply strict NaN check because land values haven't been filled before regridding step; instead warn user
564
+ for direction in ["south", "east", "north", "west"]:
565
+ if self.boundaries[direction]:
566
+ for var_name in variable_info:
567
+ bdry_var_name = f"{var_name}_{direction}"
568
+ if ds[bdry_var_name].isel(bry_time=0).isnull().any().values:
569
+ warnings.warn(
570
+ f"NaN values detected in regridded variables along the {direction}ern boundary. This may indicate that the entire boundary is on land in the source data, or that the source data does not cover this boundary.",
571
+ UserWarning,
572
+ )
573
+ # Break after the first warning for this direction to avoid duplicates
574
+ break
575
+
350
576
  def plot(
351
577
  self,
352
- varname,
578
+ var_name,
353
579
  time=0,
354
580
  layer_contours=False,
355
581
  ) -> None:
@@ -357,7 +583,7 @@ class BoundaryForcing(ROMSToolsMixins):
357
583
 
358
584
  Parameters
359
585
  ----------
360
- varname : str
586
+ var_name : str
361
587
  The name of the boundary forcing field to plot. Options include:
362
588
 
363
589
  - "temp_{direction}": Potential temperature,
@@ -417,37 +643,37 @@ class BoundaryForcing(ROMSToolsMixins):
417
643
  Raises
418
644
  ------
419
645
  ValueError
420
- If the specified varname is not one of the valid options.
646
+ If the specified var_name is not one of the valid options.
421
647
  """
422
648
 
423
- if varname not in self.ds:
424
- raise ValueError(f"Variable '{varname}' is not found in dataset.")
649
+ if var_name not in self.ds:
650
+ raise ValueError(f"Variable '{var_name}' is not found in dataset.")
425
651
 
426
- field = self.ds[varname].isel(bry_time=time).load()
652
+ field = self.ds[var_name].isel(bry_time=time).load()
427
653
  title = field.long_name
428
654
 
429
655
  if "s_rho" in field.dims:
430
- if varname.startswith(("u_", "ubar_")):
656
+ if var_name.startswith(("u_", "ubar_")):
431
657
  point = "u"
432
- elif varname.startswith(("v_", "vbar_")):
658
+ elif var_name.startswith(("v_", "vbar_")):
433
659
  point = "v"
434
660
  else:
435
661
  point = "rho"
436
- direction = varname.split("_")[-1]
662
+ direction = var_name.split("_")[-1]
437
663
 
438
664
  layer_depth, interface_depth = self._get_coordinates(direction, point)
439
665
 
440
666
  field = field.assign_coords({"layer_depth": layer_depth})
441
667
 
442
668
  # chose colorbar
443
- if varname.startswith(("u", "v", "ubar", "vbar", "zeta")):
669
+ if var_name.startswith(("u", "v", "ubar", "vbar", "zeta")):
444
670
  vmax = max(field.max().values, -field.min().values)
445
671
  vmin = -vmax
446
672
  cmap = plt.colormaps.get_cmap("RdBu_r")
447
673
  else:
448
674
  vmax = field.max().values
449
675
  vmin = field.min().values
450
- if varname.startswith(("temp", "salt")):
676
+ if var_name.startswith(("temp", "salt")):
451
677
  cmap = plt.colormaps.get_cmap("YlOrRd")
452
678
  else:
453
679
  cmap = plt.colormaps.get_cmap("YlGn")
@@ -621,3 +847,95 @@ class BoundaryForcing(ROMSToolsMixins):
621
847
 
622
848
  # Create and return an instance of InitialConditions
623
849
  return cls(grid=grid, **boundary_forcing_data, use_dask=use_dask)
850
+
851
+
852
+ def get_boundary_info():
853
+ """This function provides information about the boundary points for the rho, u, and
854
+ v variables on the grid, specifying the indices for the south, east, north, and west
855
+ boundaries.
856
+
857
+ Returns
858
+ -------
859
+ dict
860
+ A dictionary where keys are variable types ("rho", "u", "v"), and values
861
+ are nested dictionaries mapping directions ("south", "east", "north", "west")
862
+ to the corresponding boundary coordinates.
863
+ """
864
+
865
+ # Boundary coordinates
866
+ bdry_coords = {
867
+ "rho": {
868
+ "south": {"eta_rho": 0},
869
+ "east": {"xi_rho": -1},
870
+ "north": {"eta_rho": -1},
871
+ "west": {"xi_rho": 0},
872
+ },
873
+ "u": {
874
+ "south": {"eta_rho": 0},
875
+ "east": {"xi_u": -1},
876
+ "north": {"eta_rho": -1},
877
+ "west": {"xi_u": 0},
878
+ },
879
+ "v": {
880
+ "south": {"eta_v": 0},
881
+ "east": {"xi_rho": -1},
882
+ "north": {"eta_v": -1},
883
+ "west": {"xi_rho": 0},
884
+ },
885
+ "vector": {
886
+ "south": {"eta_rho": [0, 1]},
887
+ "east": {"xi_rho": [-2, -1]},
888
+ "north": {"eta_rho": [-2, -1]},
889
+ "west": {"xi_rho": [0, 1]},
890
+ },
891
+ }
892
+
893
+ return bdry_coords
894
+
895
+
896
+ def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
897
+ """Forward and backward fill NaN values in horizontal direction for open boundaries.
898
+
899
+ Parameters
900
+ ----------
901
+ processed_fields : dict
902
+ A dictionary of variables to be updated, where each value is an
903
+ `xarray.DataArray`.
904
+
905
+ Returns
906
+ -------
907
+ dict of str : xarray.DataArray
908
+ The updated dictionary of variables, with NaN values filled.
909
+
910
+ Raises
911
+ ------
912
+ ValueError
913
+ If more than one horizontal dimension is found or none at all.
914
+ """
915
+
916
+ horizontal_dims = ["eta_rho", "eta_v", "xi_rho", "xi_u"]
917
+
918
+ for var_name in processed_fields.keys():
919
+ selected_horizontal_dim = None
920
+ # Determine the horizontal dimension to fill
921
+ for dim in horizontal_dims:
922
+ if dim in processed_fields[var_name].dims:
923
+ if selected_horizontal_dim is not None:
924
+ raise ValueError(
925
+ f"More than one horizontal dimension found in variable '{var_name}'."
926
+ )
927
+ selected_horizontal_dim = dim
928
+
929
+ if selected_horizontal_dim is None:
930
+ raise ValueError(
931
+ f"No valid horizontal dimension found for variable '{var_name}'."
932
+ )
933
+ # Forward and backward fill in the horizontal direction
934
+ filled = one_dim_fill(
935
+ processed_fields[var_name], selected_horizontal_dim, direction="forward"
936
+ )
937
+ processed_fields[var_name] = one_dim_fill(
938
+ filled, selected_horizontal_dim, direction="backward"
939
+ )
940
+
941
+ return processed_fields