roms-tools 3.3.0__py3-none-any.whl → 3.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. roms_tools/__init__.py +1 -1
  2. roms_tools/analysis/cdr_ensemble.py +10 -13
  3. roms_tools/analysis/roms_output.py +5 -304
  4. roms_tools/{download.py → datasets/download.py} +1 -0
  5. roms_tools/{setup → datasets}/lat_lon_datasets.py +88 -64
  6. roms_tools/{setup → datasets}/river_datasets.py +9 -4
  7. roms_tools/datasets/roms_dataset.py +854 -0
  8. roms_tools/datasets/utils.py +487 -0
  9. roms_tools/{setup/fill.py → fill.py} +110 -13
  10. roms_tools/plot.py +4 -4
  11. roms_tools/regrid.py +76 -0
  12. roms_tools/setup/boundary_forcing.py +53 -45
  13. roms_tools/setup/cdr_release.py +2 -4
  14. roms_tools/setup/grid.py +46 -15
  15. roms_tools/setup/initial_conditions.py +330 -71
  16. roms_tools/setup/mask.py +2 -5
  17. roms_tools/setup/nesting.py +13 -6
  18. roms_tools/setup/river_forcing.py +4 -4
  19. roms_tools/setup/surface_forcing.py +15 -11
  20. roms_tools/setup/tides.py +7 -6
  21. roms_tools/setup/topography.py +10 -2
  22. roms_tools/setup/utils.py +292 -666
  23. roms_tools/tests/test_analysis/test_cdr_ensemble.py +4 -6
  24. roms_tools/tests/test_analysis/test_roms_output.py +1 -220
  25. roms_tools/tests/{test_setup → test_datasets}/test_lat_lon_datasets.py +4 -4
  26. roms_tools/tests/{test_setup → test_datasets}/test_river_datasets.py +1 -1
  27. roms_tools/tests/test_datasets/test_roms_dataset.py +743 -0
  28. roms_tools/tests/test_datasets/test_utils.py +527 -0
  29. roms_tools/tests/{test_setup/test_fill.py → test_fill.py} +72 -9
  30. roms_tools/tests/test_regrid.py +120 -1
  31. roms_tools/tests/test_setup/test_boundary_forcing.py +57 -138
  32. roms_tools/tests/test_setup/test_cdr_release.py +4 -5
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zarr.json +293 -2021
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zarr.json +294 -2022
  35. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ALK/c/0/0/0/0 +0 -0
  36. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/ALK_west → initial_conditions_from_roms.zarr/ALK}/zarr.json +11 -8
  37. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ALK_ALT_CO2/c/0/0/0/0 +0 -0
  38. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west → initial_conditions_from_roms.zarr/ALK_ALT_CO2}/zarr.json +11 -8
  39. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Cs_r/c/0 +0 -0
  40. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_unified_climatology.zarr/diatFe_west → initial_conditions_from_roms.zarr/Cs_r}/zarr.json +5 -12
  41. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Cs_w/c/0 +0 -0
  42. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/diatFe_west → initial_conditions_from_roms.zarr/Cs_w}/zarr.json +3 -10
  43. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DIC/c/0/0/0/0 +0 -0
  44. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/DOCr_west → initial_conditions_from_roms.zarr/DIC}/zarr.json +11 -8
  45. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DIC_ALT_CO2/c/0/0/0/0 +0 -0
  46. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DIC_ALT_CO2/zarr.json +57 -0
  47. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOC/c/0/0/0/0 +0 -0
  48. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOC/zarr.json +57 -0
  49. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOCr/c/0/0/0/0 +0 -0
  50. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west → initial_conditions_from_roms.zarr/DOCr}/zarr.json +11 -8
  51. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DON/c/0/0/0/0 +0 -0
  52. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DON/zarr.json +57 -0
  53. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DONr/c/0/0/0/0 +0 -0
  54. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DONr/zarr.json +57 -0
  55. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOP/c/0/0/0/0 +0 -0
  56. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOP/zarr.json +57 -0
  57. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOPr/c/0/0/0/0 +0 -0
  58. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOPr/zarr.json +57 -0
  59. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Fe/c/0/0/0/0 +0 -0
  60. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Fe/zarr.json +57 -0
  61. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Lig/c/0/0/0/0 +0 -0
  62. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/DOP_west → initial_conditions_from_roms.zarr/Lig}/zarr.json +11 -8
  63. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/NH4/c/0/0/0/0 +0 -0
  64. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/DON_west → initial_conditions_from_roms.zarr/NH4}/zarr.json +11 -8
  65. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/NO3/c/0/0/0/0 +0 -0
  66. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/NO3/zarr.json +57 -0
  67. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/O2/c/0/0/0/0 +0 -0
  68. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/Lig_west → initial_conditions_from_roms.zarr/O2}/zarr.json +11 -8
  69. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/PO4/c/0/0/0/0 +0 -0
  70. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/PO4/zarr.json +57 -0
  71. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/SiO3/c/0/0/0/0 +0 -0
  72. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/SiO3/zarr.json +57 -0
  73. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/abs_time/zarr.json +47 -0
  74. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatC/c/0/0/0/0 +0 -0
  75. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/diatC_west → initial_conditions_from_roms.zarr/diatC}/zarr.json +11 -8
  76. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatChl/c/0/0/0/0 +0 -0
  77. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/diatChl_west → initial_conditions_from_roms.zarr/diatChl}/zarr.json +11 -8
  78. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatFe/c/0/0/0/0 +0 -0
  79. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/O2_west → initial_conditions_from_roms.zarr/diatFe}/zarr.json +11 -8
  80. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatP/c/0/0/0/0 +0 -0
  81. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/DIC_west → initial_conditions_from_roms.zarr/diatP}/zarr.json +11 -8
  82. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatSi/c/0/0/0/0 +0 -0
  83. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/DOC_west → initial_conditions_from_roms.zarr/diatSi}/zarr.json +11 -8
  84. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazC/c/0/0/0/0 +0 -0
  85. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazC/zarr.json +57 -0
  86. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazChl/c/0/0/0/0 +0 -0
  87. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/diazChl_west → initial_conditions_from_roms.zarr/diazChl}/zarr.json +11 -8
  88. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazFe/c/0/0/0/0 +0 -0
  89. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/Fe_west → initial_conditions_from_roms.zarr/diazFe}/zarr.json +11 -8
  90. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazP/c/0/0/0/0 +0 -0
  91. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazP/zarr.json +57 -0
  92. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ocean_time/c/0 +0 -0
  93. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ocean_time/zarr.json +47 -0
  94. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/salt/c/0/0/0/0 +0 -0
  95. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_unified_climatology.zarr/ALK_west → initial_conditions_from_roms.zarr/salt}/zarr.json +12 -9
  96. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spC/c/0/0/0/0 +0 -0
  97. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spC/zarr.json +57 -0
  98. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spCaCO3/c/0/0/0/0 +0 -0
  99. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spCaCO3/zarr.json +57 -0
  100. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spChl/c/0/0/0/0 +0 -0
  101. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/spChl_west → initial_conditions_from_roms.zarr/spChl}/zarr.json +11 -8
  102. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spFe/c/0/0/0/0 +0 -0
  103. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spFe/zarr.json +57 -0
  104. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spP/c/0/0/0/0 +0 -0
  105. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spP/zarr.json +57 -0
  106. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/temp/c/0/0/0/0 +0 -0
  107. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/temp/zarr.json +57 -0
  108. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/u/c/0/0/0/0 +0 -0
  109. roms_tools/tests/test_setup/test_data/{bgc_boundary_forcing_from_climatology.zarr/NH4_west → initial_conditions_from_roms.zarr/u}/zarr.json +12 -9
  110. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ubar/c/0/0/0 +0 -0
  111. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ubar/zarr.json +54 -0
  112. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/v/c/0/0/0/0 +0 -0
  113. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/v/zarr.json +57 -0
  114. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/vbar/c/0/0/0 +0 -0
  115. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/vbar/zarr.json +54 -0
  116. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/w/zarr.json +57 -0
  117. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zarr.json +2481 -0
  118. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zeta/c/0/0/0 +0 -0
  119. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zeta/zarr.json +54 -0
  120. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zooC/c/0/0/0/0 +0 -0
  121. roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zooC/zarr.json +57 -0
  122. roms_tools/tests/test_setup/test_grid.py +66 -1
  123. roms_tools/tests/test_setup/test_initial_conditions.py +130 -104
  124. roms_tools/tests/test_setup/test_nesting.py +2 -1
  125. roms_tools/tests/test_setup/test_surface_forcing.py +1 -1
  126. roms_tools/tests/test_setup/test_tides.py +1 -1
  127. roms_tools/tests/test_setup/test_utils.py +100 -15
  128. roms_tools/tests/test_setup/test_validation.py +15 -0
  129. roms_tools/tests/test_tiling/test_partition.py +63 -15
  130. roms_tools/tests/test_utils.py +365 -0
  131. roms_tools/tiling/partition.py +81 -211
  132. roms_tools/utils.py +360 -62
  133. {roms_tools-3.3.0.dist-info → roms_tools-3.5.0.dist-info}/METADATA +2 -3
  134. {roms_tools-3.3.0.dist-info → roms_tools-3.5.0.dist-info}/RECORD +137 -174
  135. {roms_tools-3.3.0.dist-info → roms_tools-3.5.0.dist-info}/WHEEL +1 -1
  136. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
  137. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/c/0/0/0 +0 -0
  138. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
  139. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/c/0/0/0 +0 -0
  140. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/c/0/0/0 +0 -0
  141. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
  142. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/c/0/0/0 +0 -0
  143. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/c/0/0/0 +0 -0
  144. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/zarr.json +0 -54
  145. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/c/0/0/0 +0 -0
  146. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
  147. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/zarr.json +0 -54
  148. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/c/0/0/0 +0 -0
  149. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/c/0/0/0 +0 -0
  150. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/c/0/0/0 +0 -0
  151. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/c/0/0/0 +0 -0
  152. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/zarr.json +0 -54
  153. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/c/0/0/0 +0 -0
  154. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/c/0/0/0 +0 -0
  155. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/zarr.json +0 -54
  156. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
  157. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/zarr.json +0 -54
  158. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/c/0/0/0 +0 -0
  159. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
  160. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
  161. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/c/0/0/0 +0 -0
  162. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/zarr.json +0 -54
  163. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
  164. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/zarr.json +0 -54
  165. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/c/0/0/0 +0 -0
  166. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/zarr.json +0 -54
  167. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
  168. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
  169. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/zarr.json +0 -54
  170. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/c/0/0/0 +0 -0
  171. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/zarr.json +0 -54
  172. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/c/0/0/0 +0 -0
  173. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/zarr.json +0 -54
  174. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
  175. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/zarr.json +0 -54
  176. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/c/0/0/0 +0 -0
  177. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/c/0/0/0 +0 -0
  178. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/zarr.json +0 -54
  179. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/c/0/0/0 +0 -0
  180. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/zarr.json +0 -54
  181. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/c/0/0/0 +0 -0
  182. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/zarr.json +0 -54
  183. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
  184. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_ALT_CO2_west/zarr.json +0 -54
  185. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_west/c/0/0/0 +0 -0
  186. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
  187. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_ALT_CO2_west/zarr.json +0 -54
  188. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_west/c/0/0/0 +0 -0
  189. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_west/zarr.json +0 -54
  190. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOC_west/c/0/0/0 +0 -0
  191. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOC_west/zarr.json +0 -54
  192. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
  193. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOCr_west/zarr.json +0 -54
  194. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DON_west/c/0/0/0 +0 -0
  195. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DON_west/zarr.json +0 -54
  196. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DONr_west/c/0/0/0 +0 -0
  197. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DONr_west/zarr.json +0 -54
  198. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOP_west/c/0/0/0 +0 -0
  199. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOP_west/zarr.json +0 -54
  200. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
  201. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOPr_west/zarr.json +0 -54
  202. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Fe_west/c/0/0/0 +0 -0
  203. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Fe_west/zarr.json +0 -54
  204. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Lig_west/c/0/0/0 +0 -0
  205. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Lig_west/zarr.json +0 -54
  206. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NH4_west/c/0/0/0 +0 -0
  207. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NH4_west/zarr.json +0 -54
  208. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NO3_west/c/0/0/0 +0 -0
  209. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NO3_west/zarr.json +0 -54
  210. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/O2_west/c/0/0/0 +0 -0
  211. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/O2_west/zarr.json +0 -54
  212. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/PO4_west/c/0/0/0 +0 -0
  213. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/PO4_west/zarr.json +0 -54
  214. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
  215. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/SiO3_west/zarr.json +0 -54
  216. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatC_west/c/0/0/0 +0 -0
  217. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatC_west/zarr.json +0 -54
  218. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
  219. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatChl_west/zarr.json +0 -54
  220. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
  221. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatP_west/c/0/0/0 +0 -0
  222. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatP_west/zarr.json +0 -54
  223. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
  224. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatSi_west/zarr.json +0 -54
  225. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazC_west/c/0/0/0 +0 -0
  226. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazC_west/zarr.json +0 -54
  227. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
  228. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazChl_west/zarr.json +0 -54
  229. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
  230. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazFe_west/zarr.json +0 -54
  231. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazP_west/c/0/0/0 +0 -0
  232. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazP_west/zarr.json +0 -54
  233. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spC_west/c/0/0/0 +0 -0
  234. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spC_west/zarr.json +0 -54
  235. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
  236. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spCaCO3_west/zarr.json +0 -54
  237. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spChl_west/c/0/0/0 +0 -0
  238. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spChl_west/zarr.json +0 -54
  239. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spFe_west/c/0/0/0 +0 -0
  240. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spFe_west/zarr.json +0 -54
  241. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spP_west/c/0/0/0 +0 -0
  242. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spP_west/zarr.json +0 -54
  243. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zooC_west/c/0/0/0 +0 -0
  244. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zooC_west/zarr.json +0 -54
  245. {roms_tools-3.3.0.dist-info → roms_tools-3.5.0.dist-info}/licenses/LICENSE +0 -0
  246. {roms_tools-3.3.0.dist-info → roms_tools-3.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,527 @@
1
+ import logging
2
+ from copy import deepcopy
3
+ from datetime import datetime, timedelta
4
+
5
+ import cftime
6
+ import numpy as np
7
+ import pytest
8
+ import xarray as xr
9
+
10
+ from roms_tools.datasets.utils import (
11
+ _select_initial_time,
12
+ check_dataset,
13
+ convert_cftime_to_datetime,
14
+ convert_to_float64,
15
+ extrapolate_deepest_to_bottom,
16
+ get_time_type,
17
+ select_relevant_fields,
18
+ select_relevant_times,
19
+ validate_start_end_time,
20
+ )
21
+
22
+
23
+ def test_convert_to_float64():
24
+ # Create dataset with mixed dtypes
25
+ ds = xr.Dataset(
26
+ {
27
+ "temp": (("x",), np.array([1, 2, 3], dtype=np.float32)),
28
+ "mask_rho": (("x",), np.array([0, 1, 0], dtype=np.int16)),
29
+ }
30
+ )
31
+
32
+ out = convert_to_float64(ds)
33
+
34
+ # Non-mask variables should be converted to float64
35
+ assert out["temp"].dtype == np.float64
36
+
37
+ # Mask variables should keep their original dtype
38
+ assert out["mask_rho"].dtype == np.int16
39
+
40
+ # Values should be preserved
41
+ xr.testing.assert_equal(out["temp"], ds["temp"].astype("float64"))
42
+ xr.testing.assert_equal(out["mask_rho"], ds["mask_rho"])
43
+
44
+
45
+ def test_extrapolate_deepest_to_bottom():
46
+ data = np.array(
47
+ [
48
+ [1, 2],
49
+ [3, 4],
50
+ [5, np.nan],
51
+ [np.nan, np.nan],
52
+ [np.nan, np.nan],
53
+ ]
54
+ )
55
+ ds = xr.Dataset({"var": (("s_rho", "x"), data)})
56
+
57
+ ds_filled = extrapolate_deepest_to_bottom(ds, "s_rho")
58
+
59
+ expected = np.array(
60
+ [
61
+ [1, 2],
62
+ [3, 4],
63
+ [5, 4],
64
+ [5, 4],
65
+ [5, 4],
66
+ ]
67
+ )
68
+ np.testing.assert_array_equal(ds_filled["var"].values, expected)
69
+
70
+
71
+ # tests for validate_start_end_time
72
+
73
+
74
+ def test_valid_times():
75
+ start = datetime(2024, 1, 1, 12, 0)
76
+ end = start + timedelta(hours=3)
77
+
78
+ # Should not raise
79
+ validate_start_end_time(start, end)
80
+
81
+
82
+ def test_none_times():
83
+ # None for both is allowed
84
+ validate_start_end_time(None, None)
85
+
86
+ # Only start_time provided
87
+ validate_start_end_time(datetime(2024, 1, 1), None)
88
+
89
+ # Only end_time provided
90
+ validate_start_end_time(None, datetime(2024, 1, 2))
91
+
92
+
93
+ def test_equal_times():
94
+ t = datetime(2024, 1, 1, 12, 0)
95
+
96
+ # end_time == start_time is allowed
97
+ validate_start_end_time(t, t)
98
+
99
+
100
+ def test_invalid_start_type():
101
+ with pytest.raises(TypeError):
102
+ validate_start_end_time("2024-01-01", None)
103
+
104
+
105
+ def test_invalid_end_type():
106
+ with pytest.raises(TypeError):
107
+ validate_start_end_time(None, 123)
108
+
109
+
110
+ def test_end_time_before_start_time():
111
+ start = datetime(2024, 1, 2)
112
+ end = datetime(2024, 1, 1)
113
+
114
+ with pytest.raises(ValueError):
115
+ validate_start_end_time(start, end)
116
+
117
+
118
+ # tests for check_dataset
119
+
120
+
121
+ def test_valid_dataset():
122
+ ds = xr.Dataset(
123
+ data_vars={
124
+ "temp": (("x", "y"), [[1, 2], [3, 4]]),
125
+ "salt": (("x", "y"), [[5, 6], [7, 8]]),
126
+ },
127
+ coords={
128
+ "x": [0, 1],
129
+ "y": [0, 1],
130
+ },
131
+ )
132
+
133
+ dim_names = {"xdim": "x", "ydim": "y"}
134
+ var_names = {"temperature": "temp", "salinity": "salt"}
135
+
136
+ # Should NOT raise
137
+ check_dataset(ds, dim_names, var_names)
138
+
139
+
140
+ def test_missing_required_dimension():
141
+ ds = xr.Dataset(data_vars={"temp": (("x",), [1])}, coords={"x": [0]})
142
+
143
+ dim_names = {"xdim": "x", "ydim": "y"}
144
+
145
+ with pytest.raises(ValueError, match="missing"):
146
+ check_dataset(ds, dim_names=dim_names)
147
+
148
+
149
+ def test_missing_required_variable():
150
+ ds = xr.Dataset(data_vars={"temp": (("x",), [1])}, coords={"x": [0]})
151
+
152
+ var_names = {"temperature": "temp", "salinity": "salt"}
153
+
154
+ with pytest.raises(ValueError, match="missing"):
155
+ check_dataset(ds, var_names=var_names)
156
+
157
+
158
+ def test_optional_variables_warning(caplog):
159
+ ds = xr.Dataset(data_vars={"temp": (("x",), [1])}, coords={"x": [0]})
160
+
161
+ opt_var_names = {"some_opt": "opt_var"}
162
+
163
+ with caplog.at_level(logging.WARNING):
164
+ # Should not raise, only warn
165
+ check_dataset(ds, opt_var_names=opt_var_names)
166
+
167
+ assert "Optional variables missing" in caplog.text
168
+ assert "opt_var" in caplog.text
169
+
170
+
171
+ def test_no_checks_pass():
172
+ ds = xr.Dataset(data_vars={"temp": (("x",), [1])}, coords={"x": [0]})
173
+
174
+ # Should not fail because no dim_names/var_names provided
175
+ check_dataset(ds)
176
+
177
+
178
+ # tests for select_relevant_fields
179
+
180
+
181
+ def test_select_relevant_fields_basic():
182
+ """Keep only required + optional variables and drop others."""
183
+ ds = xr.Dataset(
184
+ {
185
+ "temp": (("x",), np.arange(5)),
186
+ "salt": (("x",), np.arange(5) * 2),
187
+ "u": (("x",), np.arange(5) * 3),
188
+ "mask": (("x",), np.ones(5)),
189
+ "extra": (("x",), np.zeros(5)),
190
+ }
191
+ )
192
+
193
+ keep_vars = ["temp", "u"] # required + optional
194
+
195
+ out = select_relevant_fields(ds, keep_vars)
196
+
197
+ assert set(out.data_vars) == {"temp", "u", "mask"}
198
+ assert "salt" not in out
199
+ assert "extra" not in out
200
+
201
+
202
+ def test_select_relevant_fields_does_not_modify_input():
203
+ """Function must not mutate the original dataset."""
204
+ ds = xr.Dataset(
205
+ {
206
+ "temp": (("x",), np.arange(5)),
207
+ "salt": (("x",), np.arange(5)),
208
+ "mask": (("x",), np.ones(5)),
209
+ }
210
+ )
211
+
212
+ ds_copy = deepcopy(ds)
213
+
214
+ _ = select_relevant_fields(ds, ["temp"])
215
+
216
+ # ensure original dataset is unchanged
217
+ assert set(ds.data_vars) == set(ds_copy.data_vars)
218
+ assert "salt" in ds
219
+ assert "mask" in ds
220
+
221
+
222
+ def test_select_relevant_fields_duplicate_keep_names():
223
+ """Duplicate keep names should not cause issues."""
224
+ ds = xr.Dataset(
225
+ {
226
+ "temp": (("x",), np.arange(5)),
227
+ "mask": (("x",), np.ones(5)),
228
+ }
229
+ )
230
+
231
+ keep_vars = ["temp", "temp"] # duplicates
232
+
233
+ out = select_relevant_fields(ds, keep_vars)
234
+
235
+ assert set(out.data_vars) == {"temp", "mask"}
236
+
237
+
238
+ # tests for select_relevant_times
239
+
240
+
241
+ def make_time_dataset(times):
242
+ return xr.Dataset(
243
+ data_vars={"var": ("time", np.arange(len(times)))},
244
+ coords={"time": times},
245
+ )
246
+
247
+
248
+ def test_missing_time_dimension(caplog):
249
+ ds = xr.Dataset({"x": ("x", [1, 2])})
250
+
251
+ with caplog.at_level(logging.WARNING):
252
+ out = select_relevant_times(
253
+ ds, "time", datetime(2024, 1, 1), datetime(2024, 1, 2)
254
+ )
255
+
256
+ assert "does not contain time dimension" in caplog.text
257
+ assert out is ds # unchanged
258
+
259
+
260
+ def test_climatology_must_have_12_steps():
261
+ times = np.arange(10).astype("datetime64[D]")
262
+ ds = make_time_dataset(times)
263
+
264
+ with pytest.raises(ValueError):
265
+ select_relevant_times(
266
+ ds,
267
+ "time",
268
+ "time",
269
+ datetime(2024, 1, 1),
270
+ climatology=True,
271
+ end_time=datetime(2024, 1, 2),
272
+ )
273
+
274
+
275
+ def test_climatology_pass_through():
276
+ times = np.arange(12)
277
+ ds = make_time_dataset(times)
278
+
279
+ out = select_relevant_times(
280
+ ds,
281
+ "time",
282
+ "time",
283
+ datetime(2024, 1, 1),
284
+ end_time=datetime(2024, 1, 10),
285
+ climatology=True,
286
+ )
287
+ assert out.equals(ds)
288
+
289
+
290
+ def test_int_time_rejected():
291
+ ds = xr.Dataset(
292
+ data_vars={"var": ("time", [1, 2, 3])},
293
+ coords={"time": [1, 2, 3]},
294
+ )
295
+
296
+ with pytest.raises(ValueError):
297
+ select_relevant_times(
298
+ ds, "time", "time", datetime(2024, 1, 1), datetime(2024, 1, 2)
299
+ )
300
+
301
+
302
+ def test_cftime_conversion(monkeypatch):
303
+ times = xr.DataArray(
304
+ [cftime.DatetimeGregorian(2024, 1, i + 1) for i in range(3)], dims="time"
305
+ )
306
+ ds = xr.Dataset({"var": ("time", [1, 2, 3])}, coords={"time": times})
307
+
308
+ # Mock conversion function to ensure it is called
309
+ def mock_convert(t):
310
+ return np.array(
311
+ ["2024-01-01", "2024-01-02", "2024-01-03"], dtype="datetime64[ns]"
312
+ )
313
+
314
+ monkeypatch.setattr(
315
+ "roms_tools.datasets.utils.convert_cftime_to_datetime", mock_convert
316
+ )
317
+
318
+ out = select_relevant_times(
319
+ ds, "time", "time", datetime(2024, 1, 1), datetime(2024, 1, 3)
320
+ )
321
+
322
+ assert np.issubdtype(out["time"].dtype, np.datetime64)
323
+
324
+
325
+ def test_time_range_selection():
326
+ times = np.array(
327
+ ["2024-01-01", "2024-01-02", "2024-01-05", "2024-01-10"], dtype="datetime64[ns]"
328
+ )
329
+ ds = make_time_dataset(times)
330
+
331
+ out = select_relevant_times(
332
+ ds,
333
+ "time",
334
+ "time",
335
+ datetime(2024, 1, 2),
336
+ datetime(2024, 1, 7),
337
+ )
338
+
339
+ # Should include:
340
+ # - closest before start: 2024-01-02
341
+ # - records inside strict range: 2024-01-05
342
+ # - closest after end: 2024-01-10
343
+ expected_times = np.array(
344
+ ["2024-01-02", "2024-01-05", "2024-01-10"],
345
+ dtype="datetime64[ns]",
346
+ )
347
+
348
+ assert np.array_equal(out["time"].values, expected_times)
349
+
350
+
351
+ def test_range_selection_missing_before(caplog):
352
+ times = np.array(["2024-01-05", "2024-01-10"], dtype="datetime64[ns]")
353
+ ds = make_time_dataset(times)
354
+
355
+ with caplog.at_level(logging.WARNING):
356
+ out = select_relevant_times(
357
+ ds,
358
+ "time",
359
+ "time",
360
+ datetime(2024, 1, 1),
361
+ datetime(2024, 1, 12),
362
+ )
363
+
364
+ assert "No records found at or before the start_time" in caplog.text
365
+ # Should fallback to first record
366
+ assert out["time"].values[0] == np.datetime64("2024-01-05")
367
+
368
+
369
+ def test_range_selection_missing_after(caplog):
370
+ times = np.array(["2024-01-01", "2024-01-02"], dtype="datetime64[ns]")
371
+ ds = make_time_dataset(times)
372
+
373
+ with caplog.at_level(logging.WARNING):
374
+ out = select_relevant_times(
375
+ ds,
376
+ "time",
377
+ "time",
378
+ datetime(2024, 1, 1),
379
+ datetime(2024, 1, 10),
380
+ )
381
+
382
+ assert "No records found at or after the end_time" in caplog.text
383
+ assert out["time"].values[-1] == np.datetime64("2024-01-02")
384
+
385
+
386
+ # Tests for _select_initial_time
387
+
388
+
389
+ def test_initial_time_exact_match():
390
+ times = np.array(["2024-01-01", "2024-01-02"], dtype="datetime64[ns]")
391
+ ds = make_time_dataset(times)
392
+
393
+ out = _select_initial_time(
394
+ ds,
395
+ "time",
396
+ "time",
397
+ datetime(2024, 1, 2),
398
+ climatology=False,
399
+ allow_flex_time=False,
400
+ )
401
+
402
+ assert out["time"].values == np.datetime64("2024-01-02")
403
+
404
+
405
+ def test_initial_time_no_exact_match():
406
+ times = np.array(["2024-01-01", "2024-01-03"], dtype="datetime64[ns]")
407
+ ds = make_time_dataset(times)
408
+
409
+ with pytest.raises(ValueError):
410
+ _select_initial_time(
411
+ ds,
412
+ "time",
413
+ "time",
414
+ datetime(2024, 1, 2),
415
+ climatology=False,
416
+ allow_flex_time=False,
417
+ )
418
+
419
+
420
+ def test_initial_flexible_time():
421
+ times = np.array(["2024-01-01", "2024-01-02", "2024-01-15"], dtype="datetime64[ns]")
422
+ ds = make_time_dataset(times)
423
+
424
+ out = _select_initial_time(
425
+ ds,
426
+ "time",
427
+ "time",
428
+ datetime(2024, 1, 1),
429
+ climatology=False,
430
+ allow_flex_time=True,
431
+ )
432
+
433
+ assert out["time"].values == np.datetime64("2024-01-01")
434
+
435
+
436
+ def test_initial_flexible_time_out_of_range():
437
+ times = np.array(["2024-01-10", "2024-01-11"], dtype="datetime64[ns]")
438
+ ds = make_time_dataset(times)
439
+
440
+ with pytest.raises(ValueError):
441
+ _select_initial_time(
442
+ ds,
443
+ "time",
444
+ "time",
445
+ datetime(2024, 1, 1),
446
+ climatology=False,
447
+ allow_flex_time=True,
448
+ )
449
+
450
+
451
+ # tests for get_time_type
452
+
453
+
454
+ def test_get_time_type_datetime():
455
+ times = np.array(["2020-01-01", "2020-01-02"], dtype="datetime64[ns]")
456
+ da = xr.DataArray(times)
457
+ assert get_time_type(da) == "datetime"
458
+
459
+
460
+ def test_get_time_type_cftime():
461
+ times = np.array(
462
+ [
463
+ cftime.DatetimeNoLeap(2000, 1, 1),
464
+ cftime.DatetimeNoLeap(2000, 1, 2),
465
+ ],
466
+ dtype=object,
467
+ )
468
+ da = xr.DataArray(times)
469
+ assert get_time_type(da) == "cftime"
470
+
471
+
472
+ def test_get_time_type_integer():
473
+ da = xr.DataArray(np.array([1, 2, 3], dtype=int))
474
+ assert get_time_type(da) == "int"
475
+
476
+
477
+ def test_get_time_type_unsupported_type():
478
+ da = xr.DataArray(np.array(["a", "b"], dtype=object))
479
+ with pytest.raises(ValueError, match="Unsupported data type"):
480
+ get_time_type(da)
481
+
482
+
483
+ def test_get_time_type_invalid_input_type():
484
+ da = xr.DataArray("not-an-array")
485
+ with pytest.raises(ValueError):
486
+ get_time_type(da)
487
+
488
+
489
+ # Tests for convert_cftime_to_datetime
490
+
491
+
492
+ def test_convert_cftime_to_datetime_basic():
493
+ arr = np.array(
494
+ [
495
+ cftime.DatetimeNoLeap(2000, 1, 1),
496
+ cftime.DatetimeNoLeap(2000, 1, 2),
497
+ ]
498
+ )
499
+
500
+ converted = convert_cftime_to_datetime(arr)
501
+
502
+ assert converted.dtype == "datetime64[ns]"
503
+ assert converted[0] == np.datetime64("2000-01-01")
504
+ assert converted[1] == np.datetime64("2000-01-02")
505
+
506
+
507
+ def test_convert_cftime_to_datetime_mixed_inputs():
508
+ arr = np.array(
509
+ [
510
+ cftime.DatetimeNoLeap(2000, 1, 1),
511
+ "2001-01-01",
512
+ np.datetime64("2002-01-01"),
513
+ ],
514
+ dtype=object,
515
+ )
516
+
517
+ converted = convert_cftime_to_datetime(arr)
518
+
519
+ assert converted[0] == np.datetime64("2000-01-01")
520
+ assert converted[1] == np.datetime64("2001-01-01")
521
+ assert converted[2] == np.datetime64("2002-01-01")
522
+
523
+
524
+ def test_convert_cftime_to_datetime_returns_numpy_array():
525
+ arr = np.array([cftime.DatetimeNoLeap(1999, 12, 31)])
526
+ converted = convert_cftime_to_datetime(arr)
527
+ assert isinstance(converted, np.ndarray)
@@ -2,7 +2,7 @@ import numpy as np
2
2
  import pytest
3
3
  import xarray as xr
4
4
 
5
- from roms_tools.setup.fill import LateralFill
5
+ from roms_tools.fill import LateralFill
6
6
 
7
7
 
8
8
  @pytest.mark.parametrize(
@@ -19,12 +19,12 @@ def test_lateral_fill_no_nans(data_fixture, request):
19
19
  data = request.getfixturevalue(data_fixture)
20
20
  lateral_fill = LateralFill(
21
21
  data.ds["mask"],
22
- [data.dim_names["latitude"], data.dim_names["longitude"]],
22
+ (data.dim_names["latitude"], data.dim_names["longitude"]),
23
23
  )
24
24
  if "mask_vel" in data.ds.data_vars:
25
25
  lateral_fill_vel = LateralFill(
26
26
  data.ds["mask_vel"],
27
- [data.dim_names["latitude"], data.dim_names["longitude"]],
27
+ (data.dim_names["latitude"], data.dim_names["longitude"]),
28
28
  )
29
29
 
30
30
  for var in data.var_names:
@@ -40,10 +40,10 @@ def test_lateral_fill_no_nans(data_fixture, request):
40
40
  def test_lateral_fill_correct_order_of_magnitude(coarsened_cesm_bgc_data):
41
41
  lateral_fill = LateralFill(
42
42
  coarsened_cesm_bgc_data.ds["mask"],
43
- [
43
+ (
44
44
  coarsened_cesm_bgc_data.dim_names["latitude"],
45
45
  coarsened_cesm_bgc_data.dim_names["longitude"],
46
- ],
46
+ ),
47
47
  )
48
48
 
49
49
  ALK = coarsened_cesm_bgc_data.ds["ALK"]
@@ -78,21 +78,21 @@ def test_lateral_fill_reproducibility(data_fixture, request):
78
78
  data = request.getfixturevalue(data_fixture)
79
79
  lateral_fill0 = LateralFill(
80
80
  data.ds["mask"],
81
- [data.dim_names["latitude"], data.dim_names["longitude"]],
81
+ (data.dim_names["latitude"], data.dim_names["longitude"]),
82
82
  )
83
83
  if "mask_vel" in data.ds.data_vars:
84
84
  lateral_fill_vel0 = LateralFill(
85
85
  data.ds["mask_vel"],
86
- [data.dim_names["latitude"], data.dim_names["longitude"]],
86
+ (data.dim_names["latitude"], data.dim_names["longitude"]),
87
87
  )
88
88
  lateral_fill1 = LateralFill(
89
89
  data.ds["mask"],
90
- [data.dim_names["latitude"], data.dim_names["longitude"]],
90
+ (data.dim_names["latitude"], data.dim_names["longitude"]),
91
91
  )
92
92
  if "mask_vel" in data.ds.data_vars:
93
93
  lateral_fill_vel1 = LateralFill(
94
94
  data.ds["mask_vel"],
95
- [data.dim_names["latitude"], data.dim_names["longitude"]],
95
+ (data.dim_names["latitude"], data.dim_names["longitude"]),
96
96
  )
97
97
 
98
98
  ds0 = data.ds.copy()
@@ -116,3 +116,66 @@ def test_lateral_fill_reproducibility(data_fixture, request):
116
116
  )
117
117
 
118
118
  assert ds0.equals(ds1)
119
+
120
+
121
+ def test_lateralfill_raises_notimplemented_for_non2d_mask():
122
+ mask_3d = xr.DataArray(np.ones((3, 3, 3)), dims=("eta_rho", "xi_rho", "s_rho"))
123
+ with pytest.raises(
124
+ NotImplementedError, match="LateralFill currently supports only 2D masks"
125
+ ):
126
+ LateralFill(mask_3d, dims=("eta_rho", "xi_rho"))
127
+
128
+
129
+ def test_lateralfill_raises_valueerror_for_list_of_dims():
130
+ mask = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "xi_rho"))
131
+ with pytest.raises(TypeError, match="must be a tuple"):
132
+ LateralFill(mask, dims=["eta_rho", "xi_rho"])
133
+
134
+
135
+ def test_lateralfill_raises_valueerror_for_wrong_number_of_dims():
136
+ mask = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "xi_rho"))
137
+
138
+ with pytest.raises(ValueError, match="must contain exactly two"):
139
+ LateralFill(mask, dims=("eta_rho", "xi_rho", "extra_dim")) # 3 dims
140
+
141
+
142
+ def test_lateralfill_raises_valueerror_for_mask_dim_order_mismatch():
143
+ mask = xr.DataArray(np.ones((3, 3)), dims=("xi_rho", "eta_rho")) # dims reversed
144
+ with pytest.raises(ValueError, match="dimension order is incorrect"):
145
+ LateralFill(mask, dims=("eta_rho", "xi_rho"))
146
+
147
+
148
+ def test_apply_raises_if_var_missing_dims():
149
+ mask = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "xi_rho"))
150
+ lf = LateralFill(mask, dims=("eta_rho", "xi_rho"))
151
+ var = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "eta_u")) # missing xi_rho
152
+ with pytest.raises(
153
+ ValueError, match="does not contain the required horizontal dimensions"
154
+ ):
155
+ lf.apply(var)
156
+
157
+
158
+ def test_apply_raises_if_var_dim_order_mismatch():
159
+ mask = xr.DataArray(np.ones((3, 3)), dims=("eta_rho", "xi_rho"))
160
+ lf = LateralFill(mask, dims=("eta_rho", "xi_rho"))
161
+ var = xr.DataArray(np.ones((3, 3)), dims=("xi_rho", "eta_rho")) # wrong order
162
+ with pytest.raises(ValueError, match="dimension order is incorrect"):
163
+ lf.apply(var)
164
+
165
+
166
+ def test_apply_raises_if_var_has_nans_on_valid_mask():
167
+ # 2D mask
168
+ mask = xr.DataArray(np.ones((3, 3), dtype=bool), dims=("eta_rho", "xi_rho"))
169
+
170
+ lf = LateralFill(mask, dims=("eta_rho", "xi_rho"))
171
+
172
+ # Variable with a NaN at a location where mask is True
173
+ data = np.ones((3, 3))
174
+ data[1, 1] = np.nan
175
+ var = xr.DataArray(data, dims=("eta_rho", "xi_rho"))
176
+
177
+ # Expect ValueError
178
+ with pytest.raises(
179
+ ValueError, match="contains NaNs at grid points marked as valid"
180
+ ):
181
+ lf.apply(var)