roms-tools 1.3.0__py3-none-any.whl → 1.4.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 (308) hide show
  1. roms_tools/_version.py +1 -1
  2. roms_tools/setup/boundary_forcing.py +1 -1
  3. roms_tools/setup/datasets.py +278 -105
  4. roms_tools/setup/download.py +5 -0
  5. roms_tools/setup/fill.py +266 -331
  6. roms_tools/setup/initial_conditions.py +10 -11
  7. roms_tools/setup/mixins.py +29 -21
  8. roms_tools/setup/surface_forcing.py +3 -5
  9. roms_tools/setup/tides.py +44 -46
  10. roms_tools/setup/utils.py +41 -23
  11. roms_tools/tests/test_setup/test_boundary_forcing.py +1 -1
  12. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zattrs +1 -1
  13. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zmetadata +1 -1
  14. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/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_south/0.0.0 +0 -0
  19. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
  20. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
  21. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
  22. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
  23. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
  25. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
  27. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/0.0.0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/0.0.0 +0 -0
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/0.0.0 +0 -0
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/0.0.0 +0 -0
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zattrs +1 -1
  111. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zmetadata +1 -1
  112. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust/0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron/0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy/0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox/0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air/0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air_alt/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zattrs +1 -1
  119. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zmetadata +1 -1
  120. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust/0.0.0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron/0.0.0 +0 -0
  122. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy/0.0.0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox/0.0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air/0.0.0 +0 -0
  125. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air_alt/0.0.0 +0 -0
  126. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zattrs +1 -1
  127. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +1 -1
  128. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
  129. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
  130. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/0.0.0 +0 -0
  131. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
  132. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
  133. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/0.0.0 +0 -0
  134. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
  135. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
  136. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
  137. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
  138. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
  139. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
  140. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
  141. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
  142. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
  143. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
  144. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
  145. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
  146. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
  147. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
  148. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
  149. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
  150. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/.zattrs +1 -1
  151. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/.zmetadata +8 -8
  152. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/Tair/.zarray +1 -1
  153. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/Tair/0.0.0 +0 -0
  154. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/lwrad/.zarray +1 -1
  155. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/lwrad/0.0.0 +0 -0
  156. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/qair/.zarray +1 -1
  157. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/qair/0.0.0 +0 -0
  158. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/rain/.zarray +1 -1
  159. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/rain/0.0.0 +0 -0
  160. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/swrad/.zarray +1 -1
  161. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/swrad/0.0.0 +0 -0
  162. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/uwnd/.zarray +1 -1
  163. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/uwnd/0.0.0 +0 -0
  164. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/vwnd/.zarray +1 -1
  165. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/vwnd/0.0.0 +0 -0
  166. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/.zattrs +1 -1
  167. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/.zmetadata +8 -8
  168. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/Tair/.zarray +1 -1
  169. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/Tair/0.0.0 +0 -0
  170. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/lwrad/.zarray +1 -1
  171. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/lwrad/0.0.0 +0 -0
  172. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/qair/.zarray +1 -1
  173. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/qair/0.0.0 +0 -0
  174. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/rain/.zarray +1 -1
  175. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/rain/0.0.0 +0 -0
  176. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/swrad/.zarray +1 -1
  177. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/swrad/0.0.0 +0 -0
  178. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/uwnd/.zarray +1 -1
  179. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/uwnd/0.0.0 +0 -0
  180. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/vwnd/.zarray +1 -1
  181. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/vwnd/0.0.0 +0 -0
  182. roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +1 -1
  183. roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +1 -1
  184. roms_tools/tests/test_setup/test_data/grid.zarr/angle/0.0 +0 -0
  185. roms_tools/tests/test_setup/test_data/grid.zarr/angle_coarse/0.0 +0 -0
  186. roms_tools/tests/test_setup/test_data/grid.zarr/f/0.0 +0 -0
  187. roms_tools/tests/test_setup/test_data/grid.zarr/h/0.0 +0 -0
  188. roms_tools/tests/test_setup/test_data/grid.zarr/lat_coarse/0.0 +0 -0
  189. roms_tools/tests/test_setup/test_data/grid.zarr/lat_rho/0.0 +0 -0
  190. roms_tools/tests/test_setup/test_data/grid.zarr/lat_u/0.0 +0 -0
  191. roms_tools/tests/test_setup/test_data/grid.zarr/lat_v/0.0 +0 -0
  192. roms_tools/tests/test_setup/test_data/grid.zarr/pm/0.0 +0 -0
  193. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +1 -1
  194. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +1 -1
  195. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
  196. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
  197. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
  198. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  199. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
  200. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
  201. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
  202. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
  203. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
  204. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
  205. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
  206. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
  207. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
  208. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
  209. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +1 -1
  210. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +1 -1
  211. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK/0.0.0.0 +0 -0
  212. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK_ALT_CO2/0.0.0.0 +0 -0
  213. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC/0.0.0.0 +0 -0
  214. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC_ALT_CO2/0.0.0.0 +0 -0
  215. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOC/0.0.0.0 +0 -0
  216. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOCr/0.0.0.0 +0 -0
  217. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DON/0.0.0.0 +0 -0
  218. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DONr/0.0.0.0 +0 -0
  219. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOP/0.0.0.0 +0 -0
  220. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOPr/0.0.0.0 +0 -0
  221. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Fe/0.0.0.0 +0 -0
  222. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Lig/0.0.0.0 +0 -0
  223. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/0.0.0.0 +0 -0
  224. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/0.0.0.0 +0 -0
  225. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/O2/0.0.0.0 +0 -0
  226. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/0.0.0.0 +0 -0
  227. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/SiO3/0.0.0.0 +0 -0
  228. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatC/0.0.0.0 +0 -0
  229. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatChl/0.0.0.0 +0 -0
  230. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatFe/0.0.0.0 +0 -0
  231. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatP/0.0.0.0 +0 -0
  232. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/0.0.0.0 +0 -0
  233. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazC/0.0.0.0 +0 -0
  234. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazChl/0.0.0.0 +0 -0
  235. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazFe/0.0.0.0 +0 -0
  236. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazP/0.0.0.0 +0 -0
  237. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/0.0.0.0 +0 -0
  238. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/0.0.0.0 +0 -0
  239. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/0.0.0.0 +0 -0
  240. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spChl/0.0.0.0 +0 -0
  241. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/0.0.0.0 +0 -0
  242. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spP/0.0.0.0 +0 -0
  243. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  244. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  245. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  246. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  247. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  248. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zeta/0.0.0 +0 -0
  249. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zooC/0.0.0.0 +0 -0
  250. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/.zattrs +1 -1
  251. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/.zmetadata +8 -8
  252. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/Tair/.zarray +1 -1
  253. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/Tair/0.0.0 +0 -0
  254. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/lwrad/.zarray +1 -1
  255. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/lwrad/0.0.0 +0 -0
  256. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/qair/.zarray +1 -1
  257. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/qair/0.0.0 +0 -0
  258. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/rain/.zarray +1 -1
  259. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/rain/0.0.0 +0 -0
  260. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/swrad/.zarray +1 -1
  261. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/swrad/0.0.0 +0 -0
  262. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/uwnd/.zarray +1 -1
  263. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/uwnd/0.0.0 +0 -0
  264. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/vwnd/.zarray +1 -1
  265. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/vwnd/0.0.0 +0 -0
  266. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/.zattrs +1 -1
  267. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/.zmetadata +2 -4
  268. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/omega/.zattrs +1 -3
  269. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/pot_Im/0.0.0 +0 -0
  270. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/pot_Re/0.0.0 +0 -0
  271. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/ssh_Im/0.0.0 +0 -0
  272. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/ssh_Re/0.0.0 +0 -0
  273. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Im/0.0.0 +0 -0
  274. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Re/0.0.0 +0 -0
  275. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Im/0.0.0 +0 -0
  276. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Re/0.0.0 +0 -0
  277. roms_tools/tests/test_setup/test_datasets.py +280 -211
  278. roms_tools/tests/test_setup/test_fill.py +177 -0
  279. roms_tools/tests/test_setup/test_initial_conditions.py +5 -3
  280. roms_tools/tests/test_setup/test_surface_forcing.py +2 -2
  281. roms_tools/tests/test_setup/test_tides.py +5 -2
  282. roms_tools/tests/test_setup/test_validation.py +1 -1
  283. {roms_tools-1.3.0.dist-info → roms_tools-1.4.0.dist-info}/METADATA +11 -3
  284. {roms_tools-1.3.0.dist-info → roms_tools-1.4.0.dist-info}/RECORD +287 -307
  285. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/Tair/1.0.0 +0 -0
  286. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/lwrad/1.0.0 +0 -0
  287. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/qair/1.0.0 +0 -0
  288. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/rain/1.0.0 +0 -0
  289. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/swrad/1.0.0 +0 -0
  290. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/uwnd/1.0.0 +0 -0
  291. roms_tools/tests/test_setup/test_data/coarse_surface_forcing.zarr/vwnd/1.0.0 +0 -0
  292. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/Tair/1.0.0 +0 -0
  293. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/lwrad/1.0.0 +0 -0
  294. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/qair/1.0.0 +0 -0
  295. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/rain/1.0.0 +0 -0
  296. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/swrad/1.0.0 +0 -0
  297. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/uwnd/1.0.0 +0 -0
  298. roms_tools/tests/test_setup/test_data/corrected_surface_forcing.zarr/vwnd/1.0.0 +0 -0
  299. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/Tair/1.0.0 +0 -0
  300. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/lwrad/1.0.0 +0 -0
  301. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/qair/1.0.0 +0 -0
  302. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/rain/1.0.0 +0 -0
  303. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/swrad/1.0.0 +0 -0
  304. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/uwnd/1.0.0 +0 -0
  305. roms_tools/tests/test_setup/test_data/surface_forcing.zarr/vwnd/1.0.0 +0 -0
  306. {roms_tools-1.3.0.dist-info → roms_tools-1.4.0.dist-info}/LICENSE +0 -0
  307. {roms_tools-1.3.0.dist-info → roms_tools-1.4.0.dist-info}/WHEEL +0 -0
  308. {roms_tools-1.3.0.dist-info → roms_tools-1.4.0.dist-info}/top_level.txt +0 -0
roms_tools/setup/fill.py CHANGED
@@ -1,376 +1,311 @@
1
1
  import numpy as np
2
2
  import xarray as xr
3
- from numba import jit
4
-
5
-
6
- def fill_and_interpolate(
7
- field,
8
- mask,
9
- fill_dims,
10
- coords,
11
- method="linear",
12
- fillvalue_fill=0.0,
13
- fillvalue_interp=np.nan,
14
- ):
15
- """
16
- Propagates ocean values into land areas and interpolates the data to specified coordinates using a given method.
17
-
18
- Parameters
19
- ----------
20
- field : xr.DataArray
21
- The data array to be interpolated, typically containing oceanographic or atmospheric data
22
- with dimensions such as latitude and longitude.
23
-
24
- mask : xr.DataArray
25
- A data array with the same spatial dimensions as `field`, where `1` indicates ocean points
26
- and `0` indicates land points. This mask is used to identify land and ocean areas in the dataset.
27
-
28
- fill_dims : list of str
29
- List specifying the dimensions along which to perform the lateral fill, typically the horizontal
30
- dimensions such as latitude and longitude, e.g., ["latitude", "longitude"].
31
-
32
- coords : dict
33
- Dictionary specifying the target coordinates for interpolation. The keys should match the dimensions
34
- of `field` (e.g., {"longitude": lon_values, "latitude": lat_values, "depth": depth_values}).
35
- This dictionary provides the new coordinates onto which the data array will be interpolated.
36
-
37
- method : str, optional, default='linear'
38
- The interpolation method to use. Valid options are those supported by `xarray.DataArray.interp`,
39
- such as 'linear' or 'nearest'.
40
-
41
- fillvalue_fill : float, optional, default=0.0
42
- Value to use in the fill step if an entire data slice along the fill dimensions contains only NaNs.
43
-
44
- fillvalue_interp : float, optional, default=np.nan
45
- Value to use in the interpolation step. `np.nan` means that no extrapolation is applied.
46
- `None` means that extrapolation is applied, which often makes sense when interpolating in the
47
- vertical direction to avoid NaNs at the surface if the lowest depth is greater than zero.
48
-
49
- Returns
50
- -------
51
- xr.DataArray
52
- The interpolated data array. This array has the same dimensions as the input `field` but with values
53
- interpolated to the new coordinates specified in `coords`.
54
-
55
- Notes
56
- -----
57
- This method performs the following steps:
58
- 1. Sets land values to NaN based on the provided mask to ensure that interpolation does not cross
59
- the land-ocean boundary.
60
- 2. Uses the `lateral_fill` function to propagate ocean values into the land interior, helping to fill
61
- gaps in the dataset.
62
- 3. Interpolates the filled data array over the specified coordinates using the selected interpolation method.
63
-
64
- Example
65
- -------
66
- >>> import xarray as xr
67
- >>> field = xr.DataArray(...)
68
- >>> mask = xr.DataArray(...)
69
- >>> fill_dims = ["latitude", "longitude"]
70
- >>> coords = {"latitude": new_lat_values, "longitude": new_lon_values}
71
- >>> interpolated_field = fill_and_interpolate(
72
- ... field, mask, fill_dims, coords, method="linear"
73
- ... )
74
- >>> print(interpolated_field)
75
- """
76
- if not isinstance(field, xr.DataArray):
77
- raise TypeError("field must be an xarray.DataArray")
78
- if not isinstance(mask, xr.DataArray):
79
- raise TypeError("mask must be an xarray.DataArray")
80
- if not isinstance(coords, dict):
81
- raise TypeError("coords must be a dictionary")
82
- if not all(dim in field.dims for dim in coords.keys()):
83
- raise ValueError("All keys in coords must match dimensions of field")
84
- if method not in ["linear", "nearest"]:
85
- raise ValueError(
86
- "Unsupported interpolation method. Choose from 'linear', 'nearest'"
3
+ import pyamg
4
+ from scipy import sparse
5
+
6
+
7
+ class LateralFill:
8
+ def __init__(self, mask, dims, tol=1.0e-4):
9
+ """
10
+ Initializes the LateralFill class, which fills NaN values in a DataArray
11
+ by iteratively solving a Poisson equation using a lateral diffusion approach.
12
+
13
+ Parameters
14
+ ----------
15
+ mask : xarray.DataArray or ndarray of bool
16
+ A 2D boolean mask indicating valid points (True) and land points (False).
17
+ Boundary points are automatically set to land (True).
18
+ dims : list of str
19
+ Dimensions along which to perform the lateral fill. Defaults to ["latitude", "longitude"].
20
+ tol : float, optional
21
+ Tolerance for the iterative solver, determining convergence. Default is 1.0e-4.
22
+
23
+ Raises
24
+ ------
25
+ NotImplementedError
26
+ If the input mask has more than two dimensions, which is not supported by the current implementation.
27
+ """
28
+
29
+ if len(mask.shape) > 2:
30
+ raise NotImplementedError("LateralFill currently supports only 2D masks.")
31
+
32
+ self.mask = mask
33
+
34
+ # Ensure the mask is 2D, copy it and set boundary values to True
35
+ mask = mask.copy()
36
+ mask[0, :] = True
37
+ mask[-1, :] = True
38
+ mask[:, 0] = True
39
+ mask[:, -1] = True
40
+
41
+ # Flatten the mask for use in the sparse matrix solver
42
+ mask_flat = mask.values.flatten()
43
+
44
+ # Create a sparse matrix representing the Laplacian operator for the diffusion process
45
+ A = laplacian(mask.shape, mask_flat, format="csr")
46
+
47
+ # Use algebraic multigrid solver for solving the Poisson equation with set seed to ensure reproducibility
48
+ np.random.seed(123089)
49
+ self.ml = pyamg.smoothed_aggregation_solver(A, max_coarse=10)
50
+ self.dims = dims
51
+ self.tol = tol
52
+
53
+ def apply(self, var):
54
+ """
55
+ Fills NaN values in an xarray DataArray using iterative lateral diffusion.
56
+
57
+ Parameters
58
+ ----------
59
+ var : xarray.DataArray
60
+ Input DataArray with NaN values to be filled. The fill is performed
61
+ across the dimensions specified by `dims`.
62
+
63
+ Returns
64
+ -------
65
+ var_filled : xarray.DataArray
66
+ A DataArray with NaN values filled by iterative smoothing, while preserving
67
+ non-NaN values.
68
+
69
+ """
70
+ # Apply fill to anomaly field
71
+ mean = var.mean(dim=self.dims, skipna=True)
72
+ var = var - mean
73
+
74
+ # Setup the right-hand side (RHS): ocean points take their original values, land points are set to 0
75
+ b = xr.where(self.mask, var, 0)
76
+
77
+ # Initial guess: ocean points take their original values, land points are set to 0
78
+ x0 = xr.where(self.mask, var, 0)
79
+
80
+ # Apply the iterative solver using a custom NumPy function
81
+ var_filled = xr.apply_ufunc(
82
+ _lateral_fill_np_array,
83
+ x0,
84
+ b,
85
+ input_core_dims=[self.dims, self.dims],
86
+ output_core_dims=[self.dims],
87
+ output_dtypes=[x0.dtype],
88
+ dask="parallelized",
89
+ vectorize=True,
90
+ kwargs={"ml": self.ml, "tol": self.tol},
87
91
  )
88
92
 
89
- # Set land values to NaN
90
- field = field.where(mask)
91
-
92
- # Propagate ocean values into land interior before interpolation
93
- field = lateral_fill(field, 1 - mask, fill_dims, fillvalue_fill)
93
+ var_filled = var_filled + mean
94
94
 
95
- field_interpolated = field.interp(
96
- coords, method=method, kwargs={"fill_value": fillvalue_interp}
97
- ).drop_vars(list(coords.keys()))
95
+ return var_filled
98
96
 
99
- return field_interpolated
100
97
 
101
-
102
- def lateral_fill(var, land_mask, dims=["latitude", "longitude"], fillvalue=0.0):
98
+ def _lateral_fill_np_array(x0, b, ml, tol=1.0e-4):
103
99
  """
104
- Perform lateral fill on an xarray DataArray using a land mask.
100
+ Fills all NaN values in a 2D NumPy array using an iterative solver,
101
+ while preserving the existing non-NaN values.
102
+ The filling process uses an AMG solver to efficiently perform smoothing
103
+ based on the Laplace operator.
105
104
 
106
105
  Parameters
107
106
  ----------
108
- var : xarray.DataArray
109
- DataArray on which to fill NaNs. The fill is performed on the dimensions specified
110
- in `dims`.
111
-
112
- land_mask : xarray.DataArray
113
- Boolean DataArray indicating valid values: `True` where data should be filled. Must have the
114
- same shape as `var` for the specified dimensions.
115
-
116
- dims : list of str, optional, default=['latitude', 'longitude']
117
- Dimensions along which to perform the fill. The default is ['latitude', 'longitude'].
118
-
119
- fillvalue : float, optional, default=0.0
120
- Value to use if an entire data slice along the dims contains only NaNs.
107
+ x0 : numpy.ndarray
108
+ Initial guess for the fill operation.
109
+ b : numpy.ndarray
110
+ Right-hand side (RHS) of the equation representing the data values
111
+ to be used in the fill process. Non-NaN values in `b` correspond to
112
+ valid points, and zeros are used for masked (invalid) points.
113
+ ml : pyamg.MultilevelSolver
114
+ An algebraic multigrid (AMG) solver used to iteratively fill NaNs
115
+ via a smoothing process.
116
+ tol : float, optional, default=1.0e-4
117
+ Convergence tolerance for the iterative solver. The filling process
118
+ stops when the relative residual (change in values) is less than or
119
+ equal to `tol`. Specifically, the process iterates until:
120
+ ``||Ax - b|| / ||Ax0 - b|| < tol``, where `A` is the system matrix,
121
+ `x` is the solution, and `x0` is the initial guess.
121
122
 
122
123
  Returns
123
124
  -------
124
- var_filled : xarray.DataArray
125
- DataArray with NaNs filled by iterative smoothing, except for the regions
126
- specified by `land_mask` where NaNs are preserved.
127
-
125
+ x_2d : numpy.ndarray
126
+ The filled 2D array where NaN values have been replaced with iteratively
127
+ computed values, and non-NaN values remain unchanged.
128
128
  """
129
129
 
130
- var_filled = xr.apply_ufunc(
131
- _lateral_fill_np_array,
132
- var,
133
- land_mask,
134
- input_core_dims=[dims, dims],
135
- output_core_dims=[dims],
136
- output_dtypes=[var.dtype],
137
- dask="parallelized",
138
- vectorize=True,
139
- kwargs={"fillvalue": fillvalue},
140
- )
130
+ b_flat = b.flatten()
131
+ x0_flat = x0.flatten()
132
+ x = ml.solve(b_flat, x0_flat, tol=tol)
133
+ x_2d = x.reshape(b.shape)
141
134
 
142
- return var_filled
135
+ return x_2d
143
136
 
144
137
 
145
- def _lateral_fill_np_array(
146
- var, isvalid_mask, fillvalue=0.0, tol=1.0e-4, rc=1.8, max_iter=10000
147
- ):
138
+ def laplacian(grid, mask, dtype=float, format=None):
148
139
  """
149
- Perform lateral fill on a numpy array.
140
+ Return a sparse matrix for solving a 2-dimensional Poisson problem.
150
141
 
151
- Parameters
152
- ----------
153
- var : numpy.array
154
- Two-dimensional array on which to fill NaNs.Only NaNs where `isvalid_mask` is
155
- True will be filled.
142
+ This function generates a finite difference approximation of the Laplacian operator
143
+ on a 2-dimensional grid with unit grid spacing and Dirichlet boundary conditions.
144
+ The matrix can be used to solve Poisson-like equations in grid-based numerical methods.
156
145
 
157
- isvalid_mask : numpy.array, boolean
158
- Valid values mask: `True` where data should be filled. Must have same shape
159
- as `var`.
146
+ The computation iterates over the last dimension first (z, then y, then x), and
147
+ the output matrix should be compatible with `np.mgrid()` or `np.ndenumerate()`.
160
148
 
161
- fillvalue: float
162
- Value to use if the full field `var` contains only NaNs. Default is 0.0.
163
-
164
- tol : float, optional, default=1.0e-4
165
- Convergence criteria: stop filling when the value change is less than
166
- or equal to `tol * var`, i.e., `delta <= tol * np.abs(var[j, i])`.
167
-
168
- rc : float, optional, default=1.8
169
- Over-relaxation coefficient to use in the Successive Over-Relaxation (SOR)
170
- fill algorithm. Larger arrays (or extent of region to be filled if not global)
171
- typically converge faster with larger coefficients. For completely
172
- land-filling a 1-degree grid (360x180), a coefficient in the range 1.85-1.9
173
- is near optimal. Valid bounds are (1.0, 2.0).
174
-
175
- max_iter : int, optional, default=10000
176
- Maximum number of iterations to perform before giving up if the tolerance
177
- is not reached.
149
+ Parameters
150
+ ----------
151
+ grid : tuple of int
152
+ Dimensions of the grid, e.g., (100, 100).
153
+ mask : 2D array of bool
154
+ A boolean mask of the same size as the grid, indicating valid grid points
155
+ (True for valid points, False for masked points).
156
+ dtype : data-type, optional
157
+ The desired data type of the resulting matrix. Default is `float`.
158
+ format : str, optional
159
+ The format of the sparse matrix to return, such as "csr", "coo", etc.
160
+ Default is None.
178
161
 
179
162
  Returns
180
163
  -------
181
- var : numpy.array
182
- Array with NaNs filled by iterative smoothing, except for the regions
183
- specified by `isvalid_mask` where NaNs are preserved.
184
-
164
+ sparse matrix
165
+ A sparse matrix representing the finite difference Laplacian operator for
166
+ the given grid.
185
167
 
186
- Example
187
- -------
188
- >>> import numpy as np
189
- >>> var = np.array([[1, 2, np.nan], [4, np.nan, 6]])
190
- >>> isvalid_mask = np.array([[True, True, True], [True, True, True]])
191
- >>> filled_var = lateral_fill_np_array(var, isvalid_mask)
192
- >>> print(filled_var)
193
168
  """
194
- nlat, nlon = var.shape[-2:]
195
- var = var.copy()
169
+ grid = tuple(grid)
196
170
 
197
- fillmask = np.isnan(var) # Fill all NaNs
198
- keepNaNs = ~isvalid_mask & np.isnan(var)
199
- var = _iterative_fill_sor(nlat, nlon, var, fillmask, tol, rc, max_iter, fillvalue)
200
- var[keepNaNs] = np.nan # Replace NaNs in areas not designated for filling
171
+ # create 2-dimensional Laplacian stencil
172
+ N = 2
173
+ stencil = np.zeros((3,) * N, dtype=dtype)
174
+ for i in range(N):
175
+ stencil[(1,) * i + (0,) + (1,) * (N - i - 1)] = 1
176
+ stencil[(1,) * i + (2,) + (1,) * (N - i - 1)] = 1
177
+ stencil[(1,) * N] = -2 * N
201
178
 
202
- return var
179
+ return stencil_grid_mod(stencil, grid, mask, format=format)
203
180
 
204
181
 
205
- @jit(nopython=True, parallel=True)
206
- def _iterative_fill_sor(nlat, nlon, var, fillmask, tol, rc, max_iter, fillvalue=0.0):
182
+ def stencil_grid_mod(S, grid, msk, dtype=None, format=None):
207
183
  """
208
- Perform an iterative land fill algorithm using the Successive Over-Relaxation (SOR)
209
- solution of the Laplace Equation.
184
+ Construct a sparse matrix from a local matrix stencil.
185
+
186
+ This function generates a sparse matrix that represents an operator
187
+ by applying the given stencil `S` at each vertex of a regular grid with
188
+ the specified dimensions. The matrix is modified according to the provided
189
+ mask to ensure that masked points are not affected during matrix operations.
210
190
 
211
191
  Parameters
212
192
  ----------
213
- nlat : int
214
- Number of latitude points in the array.
215
-
216
- nlon : int
217
- Number of longitude points in the array.
218
-
219
- var : numpy.array
220
- Two-dimensional array on which to fill NaNs.
221
-
222
- fillmask : numpy.array, boolean
223
- Mask indicating positions to be filled: `True` where data should be filled.
224
-
225
- tol : float
226
- Convergence criterion: the iterative process stops when the maximum residual change
227
- is less than or equal to `tol`.
228
-
229
- rc : float
230
- Over-relaxation coefficient used in the SOR algorithm. Must be between 1.0 and 2.0.
231
-
232
- max_iter : int
233
- Maximum number of iterations allowed before the process is terminated.
234
-
235
- fillvalue: float
236
- Value to use if the full field is NaNs. Default is 0.0.
193
+ S : ndarray
194
+ An N-dimensional array representing the local matrix stencil.
195
+ All dimensions of `S` must be odd.
196
+ grid : tuple of int
197
+ A tuple specifying the dimensions of the grid. The length of the tuple
198
+ should match the number of dimensions of the stencil `S`.
199
+ msk : ndarray of bool
200
+ A 1D boolean array where `True` indicates points that are masked
201
+ (i.e., should not be affected by the matrix).
202
+ dtype : data-type, optional
203
+ The data type of the resulting sparse matrix. Default is `None`, which
204
+ will infer the type from `S`.
205
+ format : str, optional
206
+ The sparse matrix format to return, such as "csr", "coo", etc. If not
207
+ specified, the default is DIA (diagonal) format.
237
208
 
238
209
  Returns
239
210
  -------
240
- None
241
- The input array `var` is modified in-place with the NaN values filled.
211
+ A : sparse matrix
212
+ A sparse matrix representing the operator formed by applying the stencil
213
+ `S` at each grid vertex. The matrix is modified based on the mask so that
214
+ masked points are unaffected by the operator.
242
215
 
243
216
  Notes
244
217
  -----
245
- This function performs the following steps:
246
- 1. Computes a zonal mean to use as an initial guess for the fill.
247
- 2. Replaces missing values in the input array with the computed zonal average.
248
- 3. Iteratively fills the missing values using the SOR algorithm until the specified
249
- tolerance `tol` is reached or the maximum number of iterations `max_iter` is exceeded.
218
+ The grid vertices are enumerated as `arange(prod(grid)).reshape(grid)`.
219
+ This means the last grid dimension cycles fastest, while the first dimension
220
+ cycles slowest. For example, if `grid=(2,3)`, then the grid vertices are ordered
221
+ as (0,0), (0,1), (0,2), (1,0), (1,1), (1,2).
250
222
 
251
- Example
252
- -------
253
- >>> nlat, nlon = 180, 360
254
- >>> var = np.random.rand(nlat, nlon)
255
- >>> fillmask = np.isnan(var)
256
- >>> tol = 1.0e-4
257
- >>> rc = 1.8
258
- >>> max_iter = 10000
259
- >>> _iterative_fill_sor(nlat, nlon, var, fillmask, tol, rc, max_iter)
223
+ This ordering is consistent with the NumPy functions `ndenumerate()` and `mgrid()`.
224
+
225
+ The stencil is applied in all directions, and boundary conditions are
226
+ respected by zeroing out connections to boundary points.
260
227
  """
261
228
 
262
- # If field consists only of zeros, fill NaNs in with zeros and all done
263
- # Note: this will happen for shortwave downward radiation at night time
264
- if np.max(np.fabs(var)) == 0.0:
265
- var = np.zeros_like(var)
266
- return var
267
- # If field consists only of NaNs, fill NaNs with fill value
268
- if np.isnan(var).all():
269
- var = fillvalue * np.ones_like(var)
270
- return var
271
-
272
- # Compute a zonal mean to use as a first guess
273
- zoncnt = np.zeros(nlat)
274
- zonavg = np.zeros(nlat)
275
- for j in range(0, nlat):
276
- zoncnt[j] = np.sum(np.where(fillmask[j, :], 0, 1))
277
- zonavg[j] = np.sum(np.where(fillmask[j, :], 0, var[j, :]))
278
- if zoncnt[j] != 0:
279
- zonavg[j] = zonavg[j] / zoncnt[j]
280
-
281
- # Fill missing zonal averages for rows that are entirely land
282
- for j in range(0, nlat - 1): # northward pass
283
- if zoncnt[j] > 0 and zoncnt[j + 1] == 0:
284
- zoncnt[j + 1] = 1
285
- zonavg[j + 1] = zonavg[j]
286
- for j in range(nlat - 1, 0, -1): # southward pass
287
- if zoncnt[j] > 0 and zoncnt[j - 1] == 0:
288
- zoncnt[j - 1] = 1
289
- zonavg[j - 1] = zonavg[j]
290
-
291
- # Replace the input array missing values with zonal average as first guess
292
- for j in range(0, nlat):
293
- for i in range(0, nlon):
294
- if fillmask[j, i]:
295
- var[j, i] = zonavg[j]
296
-
297
- # Now do the iterative 2D fill
298
- res = np.zeros((nlat, nlon)) # work array hold residuals
299
- res_max = tol
300
- iter_cnt = 0
301
- while iter_cnt < max_iter and res_max >= tol:
302
- res[:] = 0.0 # reset the residual to zero for this iteration
303
-
304
- for j in range(1, nlat - 1):
305
- jm1 = j - 1
306
- jp1 = j + 1
307
-
308
- for i in range(1, nlon - 1):
309
- if fillmask[j, i]:
310
- im1 = i - 1
311
- ip1 = i + 1
312
-
313
- # this is SOR
314
- res[j, i] = (
315
- var[j, ip1]
316
- + var[j, im1]
317
- + var[jm1, i]
318
- + var[jp1, i]
319
- - 4.0 * var[j, i]
320
- )
321
- var[j, i] = var[j, i] + rc * 0.25 * res[j, i]
322
-
323
- # do 1D smooth on top and bottom row if there is some valid data there in the input
324
- # otherwise leave it set to zonal average
325
- for j in [0, nlat - 1]:
326
- if zoncnt[j] > 1:
327
-
328
- for i in range(1, nlon - 1):
329
- if fillmask[j, i]:
330
- im1 = i - 1
331
- ip1 = i + 1
332
-
333
- res[j, i] = var[j, ip1] + var[j, im1] - 2.0 * var[j, i]
334
- var[j, i] = var[j, i] + rc * 0.5 * res[j, i]
335
-
336
- # do 1D smooth in the vertical on left and right column
337
- for i in [0, nlon - 1]:
338
-
339
- for j in range(1, nlat - 1):
340
- if fillmask[j, i]:
341
- jm1 = j - 1
342
- jp1 = j + 1
343
-
344
- res[j, i] = var[jp1, i] + var[jm1, i] - 2.0 * var[j, i]
345
- var[j, i] = var[j, i] + rc * 0.5 * res[j, i]
346
-
347
- # four corners
348
- for j in [0, nlat - 1]:
349
- if j == 0:
350
- jp1 = j + 1
351
- jm1 = j
352
- elif j == nlat - 1:
353
- jp1 = j
354
- jm1 = j - 1
355
-
356
- for i in [0, nlon - 1]:
357
- if i == 0:
358
- ip1 = i + 1
359
- im1 = i
360
- elif i == nlon - 1:
361
- ip1 = i
362
- im1 = i - 1
363
-
364
- res[j, i] = (
365
- var[j, ip1]
366
- + var[j, im1]
367
- + var[jm1, i]
368
- + var[jp1, i]
369
- - 4.0 * var[j, i]
370
- )
371
- var[j, i] = var[j, i] + rc * 0.25 * res[j, i]
372
-
373
- res_max = np.max(np.fabs(res)) / np.max(np.fabs(var))
374
- iter_cnt += 1
375
-
376
- return var
229
+ S = np.asarray(S, dtype=dtype)
230
+ grid = tuple(grid)
231
+
232
+ if not (np.asarray(S.shape) % 2 == 1).all():
233
+ raise ValueError("all stencil dimensions must be odd")
234
+
235
+ if len(grid) != np.ndim(S):
236
+ raise ValueError(
237
+ "stencil dimension must equal number of grid\
238
+ dimensions"
239
+ )
240
+
241
+ if min(grid) < 1:
242
+ raise ValueError("grid dimensions must be positive")
243
+
244
+ N_v = np.prod(grid) # number of vertices in the mesh
245
+ N_s = (S != 0).sum() # number of nonzero stencil entries
246
+
247
+ # diagonal offsets
248
+ diags = np.zeros(N_s, dtype=int)
249
+
250
+ # compute index offset of each dof within the stencil
251
+ strides = np.cumprod([1] + list(reversed(grid)))[:-1] # noqa: RUF005
252
+ indices = tuple(i.copy() for i in S.nonzero())
253
+ for i, s in zip(indices, S.shape):
254
+ i -= s // 2
255
+
256
+ for stride, coords in zip(strides, reversed(indices)):
257
+ diags += stride * coords
258
+
259
+ data = S[S != 0].repeat(N_v).reshape(N_s, N_v)
260
+
261
+ indices = np.vstack(indices).T
262
+
263
+ # zero boundary connections
264
+ for index, diag in zip(indices, data):
265
+ diag = diag.reshape(grid)
266
+ for n, i in enumerate(index):
267
+ if i > 0:
268
+ s = [slice(None)] * len(grid)
269
+ s[n] = slice(0, i)
270
+ s = tuple(s)
271
+ diag[s] = 0
272
+ elif i < 0:
273
+ s = [slice(None)] * len(grid)
274
+ s[n] = slice(i, None)
275
+ s = tuple(s)
276
+ diag[s] = 0
277
+
278
+ # remove diagonals that lie outside matrix
279
+ mask = abs(diags) < N_v
280
+ if not mask.all():
281
+ diags = diags[mask]
282
+ data = data[mask]
283
+
284
+ # sum duplicate diagonals
285
+ if len(np.unique(diags)) != len(diags):
286
+ new_diags = np.unique(diags)
287
+ new_data = np.zeros((len(new_diags), data.shape[1]), dtype=data.dtype)
288
+
289
+ for dia, dat in zip(diags, data):
290
+ n = np.searchsorted(new_diags, dia)
291
+ new_data[n, :] += dat
292
+
293
+ diags = new_diags
294
+ data = new_data
295
+
296
+ # Modify the data vectors so that masked points are not affected by the matrix solve.
297
+ # The modifications to the data vectors are offset by the elements of "diag" because
298
+ # of the way sparse.dia_matrix sets the diagonals
299
+ for i in range(N_v):
300
+ if msk[i]:
301
+ if (i + diags[0]) >= 0:
302
+ data[0, i + diags[0]] = 0
303
+ if (i + diags[1]) >= 0:
304
+ data[1, i + diags[1]] = 0
305
+ data[2, i] = 1
306
+ if (i + diags[3]) < (N_v):
307
+ data[3, i + diags[3]] = 0
308
+ if (i + diags[4]) < (N_v):
309
+ data[4, i + diags[4]] = 0
310
+
311
+ return sparse.dia_matrix((data, diags), shape=(N_v, N_v)).asformat(format)