roms-tools 3.1.2__py3-none-any.whl → 3.3.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 (221) hide show
  1. roms_tools/__init__.py +3 -0
  2. roms_tools/analysis/cdr_analysis.py +203 -0
  3. roms_tools/analysis/cdr_ensemble.py +198 -0
  4. roms_tools/analysis/roms_output.py +80 -46
  5. roms_tools/data/grids/GLORYS_global_grid.nc +0 -0
  6. roms_tools/download.py +4 -0
  7. roms_tools/plot.py +113 -51
  8. roms_tools/setup/boundary_forcing.py +45 -20
  9. roms_tools/setup/cdr_forcing.py +122 -8
  10. roms_tools/setup/cdr_release.py +161 -8
  11. roms_tools/setup/grid.py +150 -141
  12. roms_tools/setup/initial_conditions.py +113 -48
  13. roms_tools/setup/{datasets.py → lat_lon_datasets.py} +443 -938
  14. roms_tools/setup/mask.py +63 -7
  15. roms_tools/setup/nesting.py +314 -117
  16. roms_tools/setup/river_datasets.py +527 -0
  17. roms_tools/setup/river_forcing.py +46 -20
  18. roms_tools/setup/surface_forcing.py +7 -9
  19. roms_tools/setup/tides.py +2 -3
  20. roms_tools/setup/topography.py +8 -10
  21. roms_tools/setup/utils.py +396 -23
  22. roms_tools/tests/test_analysis/test_cdr_analysis.py +144 -0
  23. roms_tools/tests/test_analysis/test_cdr_ensemble.py +202 -0
  24. roms_tools/tests/test_analysis/test_roms_output.py +61 -3
  25. roms_tools/tests/test_setup/test_boundary_forcing.py +54 -52
  26. roms_tools/tests/test_setup/test_cdr_forcing.py +54 -0
  27. roms_tools/tests/test_setup/test_cdr_release.py +118 -1
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/c/0/0/0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_north/c/0/0/0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/c/0/0/0 +0 -0
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_north/c/0/0/0 +0 -0
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/c/0/0/0 +0 -0
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/c/0/0/0 +0 -0
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_north/c/0/0/0 +0 -0
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/c/0/0/0 +0 -0
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_north/c/0/0/0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/c/0/0/0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/c/0/0/0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_north/c/0/0/0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/c/0/0/0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/c/0/0/0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_north/c/0/0/0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/c/0/0/0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_north/c/0/0/0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/c/0/0/0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/c/0/0/0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_north/c/0/0/0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/c/0/0/0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/c/0/0/0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_north/c/0/0/0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/c/0/0/0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/c/0/0/0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_north/c/0/0/0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/c/0/0/0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_north/c/0/0/0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/c/0/0/0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/c/0/0/0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_north/c/0/0/0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/c/0/0/0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/c/0/0/0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/c/0/0/0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/c/0/0/0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/c/0/0/0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_north/c/0/0/0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/c/0/0/0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/c/0/0/0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_north/c/0/0/0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/c/0/0/0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/c/0/0/0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_north/c/0/0/0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/c/0/0/0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/c/0/0/0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_north/c/0/0/0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/c/0/0/0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/c/0/0/0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/c/0/0/0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/c/0/0/0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/c/0/0/0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/c/0/0/0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/c/0/0/0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/c/0/0/0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/c/0/0/0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/c/0/0/0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/c/0/0/0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/c/0/0/0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/c/0/0/0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/c/0/0/0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/c/0/0/0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/c/0/0/0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/c/0/0/0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/c/0/0/0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/c/0/0/0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/c/0/0/0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/c/0/0/0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/c/0/0/0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/c/0/0/0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/c/0/0/0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/c/0/0/0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/c/0/0/0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/c/0/0/0 +0 -0
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/c/0/0/0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/c/0/0/0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/c/0/0/0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/c/0/0/0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/c/0/0/0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/c/0/0/0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/c/0/0/0 +0 -0
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/c/0/0/0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/c/0/0/0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zarr.json +406 -406
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/c/0/0/0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/c/0/0/0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/c/0/0/0 +0 -0
  125. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/c/0/0/0 +0 -0
  126. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/c/0/0/0 +0 -0
  127. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/c/0/0/0 +0 -0
  128. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/c/0/0/0 +0 -0
  129. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/c/0/0/0 +0 -0
  130. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/c/0/0/0 +0 -0
  131. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/c/0/0/0 +0 -0
  132. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/c/0/0/0 +0 -0
  133. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/c/0/0/0 +0 -0
  134. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/c/0/0/0 +0 -0
  135. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/c/0/0/0 +0 -0
  136. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/c/0/0/0 +0 -0
  137. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/c/0/0 +0 -0
  138. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/c/0/0 +0 -0
  139. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/c/0/0 +0 -0
  140. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/c/0/0 +0 -0
  141. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/c/0/0/0 +0 -0
  142. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/c/0/0/0 +0 -0
  143. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/c/0/0/0 +0 -0
  144. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/c/0/0/0 +0 -0
  145. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/c/0/0 +0 -0
  146. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/c/0/0 +0 -0
  147. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/c/0/0 +0 -0
  148. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/c/0/0 +0 -0
  149. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zarr.json +182 -182
  150. roms_tools/tests/test_setup/test_data/grid.zarr/h/c/0/0 +0 -0
  151. roms_tools/tests/test_setup/test_data/grid.zarr/zarr.json +191 -191
  152. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/c/0/0 +0 -0
  153. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/zarr.json +210 -210
  154. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK/c/0/0/0/0 +0 -0
  155. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK_ALT_CO2/c/0/0/0/0 +0 -0
  156. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC/c/0/0/0/0 +0 -0
  157. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC_ALT_CO2/c/0/0/0/0 +0 -0
  158. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOC/c/0/0/0/0 +0 -0
  159. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOCr/c/0/0/0/0 +0 -0
  160. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DON/c/0/0/0/0 +0 -0
  161. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DONr/c/0/0/0/0 +0 -0
  162. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOP/c/0/0/0/0 +0 -0
  163. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOPr/c/0/0/0/0 +0 -0
  164. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Fe/c/0/0/0/0 +0 -0
  165. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Lig/c/0/0/0/0 +0 -0
  166. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/c/0/0/0/0 +0 -0
  167. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/c/0/0/0/0 +0 -0
  168. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/O2/c/0/0/0/0 +0 -0
  169. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/c/0/0/0/0 +0 -0
  170. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/SiO3/c/0/0/0/0 +0 -0
  171. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatC/c/0/0/0/0 +0 -0
  172. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatChl/c/0/0/0/0 +0 -0
  173. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatFe/c/0/0/0/0 +0 -0
  174. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatP/c/0/0/0/0 +0 -0
  175. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/c/0/0/0/0 +0 -0
  176. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazC/c/0/0/0/0 +0 -0
  177. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazChl/c/0/0/0/0 +0 -0
  178. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazFe/c/0/0/0/0 +0 -0
  179. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazP/c/0/0/0/0 +0 -0
  180. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/c/0/0/0/0 +0 -0
  181. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/c/0/0/0/0 +0 -0
  182. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/c/0/0/0/0 +0 -0
  183. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spChl/c/0/0/0/0 +0 -0
  184. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/c/0/0/0/0 +0 -0
  185. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spP/c/0/0/0/0 +0 -0
  186. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/c/0/0/0/0 +0 -0
  187. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/c/0/0/0/0 +0 -0
  188. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/c/0/0/0 +0 -0
  189. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/c/0/0/0/0 +0 -0
  190. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/c/0/0/0 +0 -0
  191. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zarr.json +182 -182
  192. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zooC/c/0/0/0/0 +0 -0
  193. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/salt/c/0/0/0/0 +0 -0
  194. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/temp/c/0/0/0/0 +0 -0
  195. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/u/c/0/0/0/0 +0 -0
  196. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/ubar/c/0/0/0 +0 -0
  197. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/v/c/0/0/0/0 +0 -0
  198. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/vbar/c/0/0/0 +0 -0
  199. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/zarr.json +187 -187
  200. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Im/c/0/0/0 +0 -0
  201. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Re/c/0/0/0 +0 -0
  202. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Im/c/0/0/0 +0 -0
  203. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Re/c/0/0/0 +0 -0
  204. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/zarr.json +66 -66
  205. roms_tools/tests/test_setup/test_grid.py +236 -115
  206. roms_tools/tests/test_setup/test_initial_conditions.py +94 -41
  207. roms_tools/tests/test_setup/{test_datasets.py → test_lat_lon_datasets.py} +409 -100
  208. roms_tools/tests/test_setup/test_nesting.py +119 -31
  209. roms_tools/tests/test_setup/test_river_datasets.py +48 -0
  210. roms_tools/tests/test_setup/test_surface_forcing.py +2 -1
  211. roms_tools/tests/test_setup/test_utils.py +92 -2
  212. roms_tools/tests/test_setup/utils.py +71 -0
  213. roms_tools/tests/test_tiling/test_join.py +241 -0
  214. roms_tools/tests/test_utils.py +139 -17
  215. roms_tools/tiling/join.py +189 -0
  216. roms_tools/utils.py +131 -99
  217. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/METADATA +12 -2
  218. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/RECORD +221 -211
  219. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/WHEEL +0 -0
  220. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/licenses/LICENSE +0 -0
  221. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from collections import OrderedDict
3
- from datetime import datetime
3
+ from datetime import datetime, timedelta
4
4
  from pathlib import Path
5
5
  from unittest import mock
6
6
 
@@ -9,17 +9,27 @@ import pytest
9
9
  import xarray as xr
10
10
 
11
11
  from roms_tools.download import download_test_data
12
- from roms_tools.setup.datasets import (
12
+ from roms_tools.setup.lat_lon_datasets import (
13
+ GLORYS_GLOBAL_GRID_PATH,
13
14
  CESMBGCDataset,
14
- Dataset,
15
15
  ERA5ARCODataset,
16
16
  ERA5Correction,
17
17
  GLORYSDataset,
18
18
  GLORYSDefaultDataset,
19
- RiverDataset,
19
+ LatLonDataset,
20
20
  TPXODataset,
21
+ _concatenate_longitudes,
22
+ choose_subdomain,
23
+ get_glorys_bounds,
21
24
  )
22
25
  from roms_tools.setup.surface_forcing import DEFAULT_ERA5_ARCO_PATH
26
+ from roms_tools.setup.utils import get_target_coords
27
+ from roms_tools.tests.test_setup.utils import download_regional_and_bigger
28
+
29
+ try:
30
+ import copernicusmarine # type: ignore
31
+ except ImportError:
32
+ copernicusmarine = None
23
33
 
24
34
 
25
35
  @pytest.fixture
@@ -29,9 +39,9 @@ def global_dataset():
29
39
  depth = np.linspace(0, 2000, 10)
30
40
  time = [
31
41
  np.datetime64("2022-01-01T00:00:00"),
32
- np.datetime64("2022-02-01T00:00:00"),
33
- np.datetime64("2022-03-01T00:00:00"),
34
- np.datetime64("2022-04-01T00:00:00"),
42
+ np.datetime64("2022-01-02T00:00:00"),
43
+ np.datetime64("2022-01-03T00:00:00"),
44
+ np.datetime64("2022-01-04T00:00:00"),
35
45
  ]
36
46
  data = np.random.rand(4, 10, 180, 360)
37
47
  ds = xr.Dataset(
@@ -52,9 +62,9 @@ def global_dataset_with_noon_times():
52
62
  lat = np.linspace(-90, 90, 180)
53
63
  time = [
54
64
  np.datetime64("2022-01-01T12:00:00"),
55
- np.datetime64("2022-02-01T12:00:00"),
56
- np.datetime64("2022-03-01T12:00:00"),
57
- np.datetime64("2022-04-01T12:00:00"),
65
+ np.datetime64("2022-01-02T12:00:00"),
66
+ np.datetime64("2022-01-03T12:00:00"),
67
+ np.datetime64("2022-01-04T12:00:00"),
58
68
  ]
59
69
  data = np.random.rand(4, 180, 360)
60
70
  ds = xr.Dataset(
@@ -75,12 +85,12 @@ def global_dataset_with_multiple_times_per_day():
75
85
  time = [
76
86
  np.datetime64("2022-01-01T00:00:00"),
77
87
  np.datetime64("2022-01-01T12:00:00"),
78
- np.datetime64("2022-02-01T00:00:00"),
79
- np.datetime64("2022-02-01T12:00:00"),
80
- np.datetime64("2022-03-01T00:00:00"),
81
- np.datetime64("2022-03-01T12:00:00"),
82
- np.datetime64("2022-04-01T00:00:00"),
83
- np.datetime64("2022-04-01T12:00:00"),
88
+ np.datetime64("2022-01-02T00:00:00"),
89
+ np.datetime64("2022-01-02T12:00:00"),
90
+ np.datetime64("2022-01-03T00:00:00"),
91
+ np.datetime64("2022-01-03T12:00:00"),
92
+ np.datetime64("2022-01-04T00:00:00"),
93
+ np.datetime64("2022-01-04T12:00:00"),
84
94
  ]
85
95
  data = np.random.rand(8, 180, 360)
86
96
  ds = xr.Dataset(
@@ -112,39 +122,42 @@ def non_global_dataset():
112
122
  (
113
123
  "global_dataset",
114
124
  [
115
- np.datetime64("2022-02-01T00:00:00"),
116
- np.datetime64("2022-03-01T00:00:00"),
125
+ np.datetime64("2022-01-02T00:00:00"),
126
+ np.datetime64("2022-01-03T00:00:00"),
127
+ np.datetime64("2022-01-04T00:00:00"),
117
128
  ],
118
129
  ),
119
130
  (
120
131
  "global_dataset_with_noon_times",
121
132
  [
122
133
  np.datetime64("2022-01-01T12:00:00"),
123
- np.datetime64("2022-02-01T12:00:00"),
124
- np.datetime64("2022-03-01T12:00:00"),
134
+ np.datetime64("2022-01-02T12:00:00"),
135
+ np.datetime64("2022-01-03T12:00:00"),
136
+ np.datetime64("2022-01-04T12:00:00"),
125
137
  ],
126
138
  ),
127
139
  (
128
140
  "global_dataset_with_multiple_times_per_day",
129
141
  [
130
- np.datetime64("2022-02-01T00:00:00"),
131
- np.datetime64("2022-02-01T12:00:00"),
132
- np.datetime64("2022-03-01T00:00:00"),
142
+ np.datetime64("2022-01-02T00:00:00"),
143
+ np.datetime64("2022-01-02T12:00:00"),
144
+ np.datetime64("2022-01-03T00:00:00"),
145
+ np.datetime64("2022-01-03T12:00:00"),
146
+ np.datetime64("2022-01-04T00:00:00"),
133
147
  ],
134
148
  ),
135
149
  ],
136
150
  )
137
151
  def test_select_times(data_fixture, expected_time_values, request, tmp_path, use_dask):
138
152
  """Test selecting times with different datasets."""
139
- start_time = datetime(2022, 2, 1)
140
- end_time = datetime(2022, 3, 1)
153
+ start_time = datetime(2022, 1, 2)
154
+ end_time = datetime(2022, 1, 4)
141
155
 
142
- # Get the fixture dynamically based on the parameter
143
156
  dataset = request.getfixturevalue(data_fixture)
144
157
 
145
158
  filepath = tmp_path / "test.nc"
146
159
  dataset.to_netcdf(filepath)
147
- dataset = Dataset(
160
+ dataset = LatLonDataset(
148
161
  filename=filepath,
149
162
  var_names={"var": "var"},
150
163
  start_time=start_time,
@@ -161,16 +174,15 @@ def test_select_times(data_fixture, expected_time_values, request, tmp_path, use
161
174
  @pytest.mark.parametrize(
162
175
  "data_fixture, expected_time_values",
163
176
  [
164
- ("global_dataset", [np.datetime64("2022-02-01T00:00:00")]),
165
- ("global_dataset_with_noon_times", [np.datetime64("2022-02-01T12:00:00")]),
177
+ ("global_dataset", [np.datetime64("2022-01-02T00:00:00")]),
178
+ ("global_dataset_with_noon_times", [np.datetime64("2022-01-02T12:00:00")]),
166
179
  ],
167
180
  )
168
181
  def test_select_times_valid_start_no_end_time(
169
182
  data_fixture, expected_time_values, request, tmp_path, use_dask
170
183
  ):
171
184
  """Test selecting times with only start_time specified."""
172
- start_time = datetime(2022, 2, 1)
173
-
185
+ start_time = datetime(2022, 1, 2)
174
186
  # Get the fixture dynamically based on the parameter
175
187
  dataset = request.getfixturevalue(data_fixture)
176
188
 
@@ -179,14 +191,16 @@ def test_select_times_valid_start_no_end_time(
179
191
  dataset.to_netcdf(filepath)
180
192
 
181
193
  # Instantiate Dataset object using the temporary file
182
- dataset = Dataset(
194
+ dataset = LatLonDataset(
183
195
  filename=filepath,
184
196
  var_names={"var": "var"},
185
197
  start_time=start_time,
186
198
  use_dask=use_dask,
199
+ allow_flex_time=True,
187
200
  )
188
201
 
189
202
  assert dataset.ds is not None
203
+ assert "time" in dataset.ds.dims
190
204
  assert len(dataset.ds.time) == len(expected_time_values)
191
205
  for expected_time in expected_time_values:
192
206
  assert expected_time in dataset.ds.time.values
@@ -212,9 +226,9 @@ def test_select_times_invalid_start_no_end_time(
212
226
 
213
227
  with pytest.raises(
214
228
  ValueError,
215
- match="The dataset does not contain any time entries between the specified start_time",
229
+ match="No exact match found ",
216
230
  ):
217
- dataset = Dataset(
231
+ dataset = LatLonDataset(
218
232
  filename=filepath,
219
233
  var_names={"var": "var"},
220
234
  start_time=datetime(2022, 5, 1),
@@ -230,14 +244,15 @@ def test_multiple_matching_times(
230
244
  """
231
245
  filepath = tmp_path / "test.nc"
232
246
  global_dataset_with_multiple_times_per_day.to_netcdf(filepath)
233
- dataset = Dataset(
247
+ dataset = LatLonDataset(
234
248
  filename=filepath,
235
249
  var_names={"var": "var"},
236
- start_time=datetime(2022, 1, 31, 22, 0),
250
+ start_time=datetime(2021, 12, 31, 22, 0),
237
251
  use_dask=use_dask,
252
+ allow_flex_time=True,
238
253
  )
239
254
 
240
- assert dataset.ds["time"].values == np.datetime64(datetime(2022, 2, 1, 0, 0))
255
+ assert dataset.ds["time"].values == np.datetime64(datetime(2022, 1, 1, 0, 0))
241
256
 
242
257
 
243
258
  def test_warnings_times(global_dataset, tmp_path, caplog, use_dask):
@@ -249,7 +264,7 @@ def test_warnings_times(global_dataset, tmp_path, caplog, use_dask):
249
264
  start_time = datetime(2021, 1, 1)
250
265
  end_time = datetime(2021, 2, 1)
251
266
 
252
- Dataset(
267
+ LatLonDataset(
253
268
  filename=filepath,
254
269
  var_names={"var": "var"},
255
270
  start_time=start_time,
@@ -257,13 +272,13 @@ def test_warnings_times(global_dataset, tmp_path, caplog, use_dask):
257
272
  use_dask=use_dask,
258
273
  )
259
274
  # Verify the warning message in the log
260
- assert "No records found at or before the start_time." in caplog.text
275
+ assert "No records found at or before the start_time" in caplog.text
261
276
 
262
277
  with caplog.at_level(logging.WARNING):
263
278
  start_time = datetime(2024, 1, 1)
264
279
  end_time = datetime(2024, 2, 1)
265
280
 
266
- Dataset(
281
+ LatLonDataset(
267
282
  filename=filepath,
268
283
  var_names={"var": "var"},
269
284
  start_time=start_time,
@@ -271,17 +286,17 @@ def test_warnings_times(global_dataset, tmp_path, caplog, use_dask):
271
286
  use_dask=use_dask,
272
287
  )
273
288
  # Verify the warning message in the log
274
- assert "No records found at or after the end_time." in caplog.text
289
+ assert "No records found at or after the end_time" in caplog.text
275
290
 
276
291
 
277
292
  def test_from_ds(global_dataset, global_dataset_with_noon_times, use_dask, tmp_path):
278
- """Test the from_ds method of the Dataset class."""
293
+ """Test the from_ds method of the LatLonDataset class."""
279
294
  start_time = datetime(2022, 1, 1)
280
295
 
281
296
  filepath = tmp_path / "test.nc"
282
297
  global_dataset.to_netcdf(filepath)
283
298
 
284
- dataset = Dataset(
299
+ dataset = LatLonDataset(
285
300
  filename=filepath,
286
301
  var_names={"var": "var"},
287
302
  dim_names={
@@ -292,11 +307,12 @@ def test_from_ds(global_dataset, global_dataset_with_noon_times, use_dask, tmp_p
292
307
  },
293
308
  start_time=start_time,
294
309
  use_dask=use_dask,
310
+ allow_flex_time=True,
295
311
  )
296
312
 
297
- new_dataset = Dataset.from_ds(dataset, global_dataset_with_noon_times)
313
+ new_dataset = LatLonDataset.from_ds(dataset, global_dataset_with_noon_times)
298
314
 
299
- assert isinstance(new_dataset, Dataset) # Check that the new instance is a Dataset
315
+ assert isinstance(new_dataset, LatLonDataset)
300
316
  assert new_dataset.ds.equals(
301
317
  global_dataset_with_noon_times
302
318
  ) # Verify the new ds attribute is set correctly
@@ -313,7 +329,7 @@ def test_reverse_latitude_reverse_depth_choose_subdomain(
313
329
  global_dataset, tmp_path, use_dask
314
330
  ):
315
331
  """Test reversing latitude when it is not ascending, the choose_subdomain method,
316
- and the convert_to_negative_depth method of the Dataset class.
332
+ and the convert_to_negative_depth method of the LatLonDataset class.
317
333
  """
318
334
  start_time = datetime(2022, 1, 1)
319
335
 
@@ -322,7 +338,7 @@ def test_reverse_latitude_reverse_depth_choose_subdomain(
322
338
  global_dataset["depth"] = global_dataset["depth"][::-1]
323
339
  global_dataset.to_netcdf(filepath)
324
340
 
325
- dataset = Dataset(
341
+ dataset = LatLonDataset(
326
342
  filename=filepath,
327
343
  var_names={"var": "var"},
328
344
  dim_names={
@@ -333,6 +349,7 @@ def test_reverse_latitude_reverse_depth_choose_subdomain(
333
349
  },
334
350
  start_time=start_time,
335
351
  use_dask=use_dask,
352
+ allow_flex_time=True,
336
353
  )
337
354
 
338
355
  assert np.all(np.diff(dataset.ds["latitude"]) > 0)
@@ -375,7 +392,9 @@ def test_reverse_latitude_reverse_depth_choose_subdomain(
375
392
  def test_check_if_global_with_global_dataset(global_dataset, tmp_path, use_dask):
376
393
  filepath = tmp_path / "test.nc"
377
394
  global_dataset.to_netcdf(filepath)
378
- dataset = Dataset(filename=filepath, var_names={"var": "var"}, use_dask=use_dask)
395
+ dataset = LatLonDataset(
396
+ filename=filepath, var_names={"var": "var"}, use_dask=use_dask
397
+ )
379
398
  is_global = dataset.check_if_global(dataset.ds)
380
399
  assert is_global
381
400
 
@@ -385,7 +404,9 @@ def test_check_if_global_with_non_global_dataset(
385
404
  ):
386
405
  filepath = tmp_path / "test.nc"
387
406
  non_global_dataset.to_netcdf(filepath)
388
- dataset = Dataset(filename=filepath, var_names={"var": "var"}, use_dask=use_dask)
407
+ dataset = LatLonDataset(
408
+ filename=filepath, var_names={"var": "var"}, use_dask=use_dask
409
+ )
389
410
  is_global = dataset.check_if_global(dataset.ds)
390
411
 
391
412
  assert not is_global
@@ -403,7 +424,7 @@ def test_check_dataset(global_dataset, tmp_path, use_dask):
403
424
  with pytest.raises(
404
425
  ValueError, match="Dataset does not contain all required variables."
405
426
  ):
406
- Dataset(
427
+ LatLonDataset(
407
428
  filename=filepath,
408
429
  var_names={"var": "var"},
409
430
  start_time=start_time,
@@ -422,7 +443,7 @@ def test_check_dataset(global_dataset, tmp_path, use_dask):
422
443
  with pytest.raises(
423
444
  ValueError, match="Dataset does not contain all required dimensions."
424
445
  ):
425
- Dataset(
446
+ LatLonDataset(
426
447
  filename=filepath,
427
448
  var_names={"var": "var"},
428
449
  start_time=start_time,
@@ -431,12 +452,12 @@ def test_check_dataset(global_dataset, tmp_path, use_dask):
431
452
  )
432
453
 
433
454
 
434
- def test_era5_correction_choose_subdomain(use_dask):
455
+ def test_era5_correction_match_subdomain(use_dask):
435
456
  data = ERA5Correction(use_dask=use_dask)
436
457
  lats = data.ds.latitude[10:20]
437
458
  lons = data.ds.longitude[10:20]
438
459
  target_coords = {"lat": lats, "lon": lons}
439
- data.choose_subdomain(target_coords, straddle=False)
460
+ data.match_subdomain(target_coords)
440
461
  assert (data.ds["latitude"] == lats).all()
441
462
  assert (data.ds["longitude"] == lons).all()
442
463
 
@@ -484,7 +505,7 @@ def test_default_glorys_dataset_loading_dask_not_installed() -> None:
484
505
 
485
506
  with (
486
507
  pytest.raises(RuntimeError),
487
- mock.patch("roms_tools.utils._has_dask", return_value=False),
508
+ mock.patch("roms_tools.utils.has_dask", return_value=False),
488
509
  ):
489
510
  _ = GLORYSDefaultDataset(
490
511
  filename=GLORYSDefaultDataset.dataset_name,
@@ -500,17 +521,51 @@ def test_default_glorys_dataset_loading_dask_not_installed() -> None:
500
521
  def test_default_glorys_dataset_loading() -> None:
501
522
  """Verify the default GLORYS dataset is loaded correctly."""
502
523
  start_time = datetime(2012, 1, 1)
503
- end_time = datetime(2013, 1, 1)
504
524
 
505
- ds = GLORYSDefaultDataset(
506
- filename=GLORYSDefaultDataset.dataset_name,
507
- start_time=start_time,
508
- end_time=end_time,
509
- use_dask=True,
510
- )
525
+ for end_time in [start_time, start_time + timedelta(days=0.5)]:
526
+ data = GLORYSDefaultDataset(
527
+ filename=GLORYSDefaultDataset.dataset_name,
528
+ start_time=start_time,
529
+ end_time=end_time,
530
+ use_dask=True,
531
+ )
511
532
 
512
- expected_vars = {"temp", "salt", "u", "v", "zeta"}
513
- assert set(ds.var_names).issuperset(expected_vars)
533
+ expected_vars = {"temp", "salt", "u", "v", "zeta"}
534
+ assert set(data.var_names).issuperset(expected_vars)
535
+
536
+ expected_vars = {"thetao", "so", "uo", "vo", "zos"}
537
+ assert "time" in data.ds.dims
538
+ assert set(data.ds.data_vars).issuperset(expected_vars)
539
+
540
+
541
+ @pytest.mark.parametrize(
542
+ "fname,start_time",
543
+ [
544
+ (download_test_data("GLORYS_NA_2012.nc"), datetime(2012, 1, 1, 12)),
545
+ (download_test_data("GLORYS_NA_20121231.nc"), datetime(2012, 12, 31, 12)),
546
+ (download_test_data("GLORYS_coarse_test_data.nc"), datetime(2021, 6, 29)),
547
+ ],
548
+ )
549
+ @pytest.mark.parametrize("allow_flex_time", [True, False])
550
+ def test_non_default_glorys_dataset_loading(
551
+ fname, start_time, allow_flex_time, use_dask
552
+ ) -> None:
553
+ """Verify the default GLORYS dataset is loaded correctly."""
554
+ for end_time in [None, start_time, start_time]:
555
+ data = GLORYSDataset(
556
+ filename=fname,
557
+ start_time=start_time,
558
+ end_time=end_time,
559
+ use_dask=use_dask,
560
+ allow_flex_time=allow_flex_time,
561
+ )
562
+
563
+ expected_vars = {"temp", "salt", "u", "v", "zeta"}
564
+ assert set(data.var_names).issuperset(expected_vars)
565
+
566
+ expected_vars = {"thetao", "so", "uo", "vo", "zos"}
567
+ assert "time" in data.ds.dims
568
+ assert set(data.ds.data_vars).issuperset(expected_vars)
514
569
 
515
570
 
516
571
  def test_data_concatenation(use_dask):
@@ -708,43 +763,297 @@ class TestTPXODataset:
708
763
  global_tpxo_dataset.select_constituents(11, omega)
709
764
 
710
765
 
711
- class TestRiverDataset:
712
- def test_deduplicate_river_names(self, tmp_path):
713
- sample_dim_and_var_names = {
714
- "dim_names": {"station": "station", "time": "time"},
715
- "var_names": {
716
- "latitude": "lat",
717
- "longitude": "lon",
718
- "flux": "flux",
719
- "ratio": "ratio",
720
- "name": "name",
721
- },
722
- }
766
+ # test _concatenate_longitudes
723
767
 
724
- data = {
725
- "lat": (["station"], [10.0, 20.0, 30.0]),
726
- "lon": (["station"], [100.0, 110.0, 120.0]),
727
- "flux": (["time", "station"], np.random.rand(1, 3)),
728
- "ratio": (["time", "station"], np.random.rand(1, 3)),
729
- "name": (["station"], ["Amazon", "Nile", "Amazon"]), # duplicate
730
- }
731
- coords = {"station": [0, 1, 2], "time": [0]}
732
- ds = xr.Dataset(data, coords=coords)
733
-
734
- # Write to temporary NetCDF file
735
- file_path = tmp_path / "rivers.nc"
736
- ds.to_netcdf(file_path)
737
-
738
- river_dataset = RiverDataset(
739
- filename=file_path,
740
- start_time=datetime(2000, 1, 1),
741
- end_time=datetime(2000, 1, 2),
742
- dim_names=sample_dim_and_var_names["dim_names"],
743
- var_names=sample_dim_and_var_names["var_names"],
768
+
769
+ @pytest.fixture
770
+ def sample_ds(use_dask):
771
+ lon = xr.DataArray(np.array([0, 90, 180]), dims="lon", name="lon")
772
+ lat = xr.DataArray(np.array([-30, 0, 30]), dims="lat", name="lat")
773
+
774
+ var_with_lon = xr.DataArray(
775
+ np.arange(9).reshape(3, 3),
776
+ dims=("lat", "lon"),
777
+ coords={"lat": lat, "lon": lon},
778
+ name="var_with_lon",
779
+ )
780
+
781
+ var_no_lon = xr.DataArray(
782
+ np.array([1, 2, 3]),
783
+ dims="lat",
784
+ coords={"lat": lat},
785
+ name="var_no_lon",
786
+ )
787
+
788
+ ds = xr.Dataset({"var_with_lon": var_with_lon, "var_no_lon": var_no_lon})
789
+
790
+ if use_dask:
791
+ ds = ds.chunk({"lat": -1, "lon": -1})
792
+
793
+ return ds
794
+
795
+
796
+ @pytest.mark.parametrize(
797
+ "end,expected_lons",
798
+ [
799
+ ("lower", [-360, -270, -180, 0, 90, 180]),
800
+ ("upper", [0, 90, 180, 360, 450, 540]),
801
+ ("both", [-360, -270, -180, 0, 90, 180, 360, 450, 540]),
802
+ ],
803
+ )
804
+ def test_concatenate_longitudes(sample_ds, end, expected_lons, use_dask):
805
+ dim_names = {"longitude": "lon"}
806
+
807
+ ds_concat = _concatenate_longitudes(
808
+ sample_ds, dim_names, end=end, use_dask=use_dask
809
+ )
810
+
811
+ # longitude should be extended as expected
812
+ np.testing.assert_array_equal(ds_concat.lon.values, expected_lons)
813
+
814
+ # variable with longitude should be extended in size
815
+ assert ds_concat.var_with_lon.shape[-1] == len(expected_lons)
816
+
817
+ # variable without longitude should remain untouched
818
+ np.testing.assert_array_equal(
819
+ ds_concat.var_no_lon.values,
820
+ sample_ds.var_no_lon.values,
821
+ )
822
+
823
+ if use_dask:
824
+ import dask
825
+
826
+ # Ensure dask array backing the data
827
+ assert isinstance(ds_concat.var_with_lon.data, dask.array.Array)
828
+ # Longitude dimension should be chunked (-1 → one chunk spanning the whole dim)
829
+ assert ds_concat.var_with_lon.chunks[-1] == (len(expected_lons),)
830
+ else:
831
+ # With use_dask=False, data should be a numpy array
832
+ assert isinstance(ds_concat.var_with_lon.data, np.ndarray)
833
+
834
+
835
+ def test_invalid_end_raises(sample_ds):
836
+ dim_names = {"longitude": "lon"}
837
+ with pytest.raises(ValueError):
838
+ _concatenate_longitudes(sample_ds, dim_names, end="invalid")
839
+
840
+
841
+ # test choose_subdomain
842
+
843
+
844
+ def test_choose_subdomain_basic(global_dataset, use_dask):
845
+ target_coords = {
846
+ "lat": xr.DataArray([0, 10]),
847
+ "lon": xr.DataArray([30, 40]),
848
+ "straddle": False,
849
+ }
850
+ out = choose_subdomain(
851
+ global_dataset,
852
+ dim_names={"latitude": "latitude", "longitude": "longitude"},
853
+ resolution=1.0,
854
+ is_global=True,
855
+ target_coords=target_coords,
856
+ buffer_points=2,
857
+ use_dask=use_dask,
858
+ )
859
+ assert out.latitude.min() <= 0
860
+ assert out.latitude.max() >= 10
861
+ assert out.longitude.min() <= 30
862
+ assert out.longitude.max() >= 40
863
+
864
+
865
+ def test_choose_subdomain_raises_on_empty_lon(non_global_dataset, use_dask):
866
+ target_coords = {
867
+ "lat": xr.DataArray([-10, 10]),
868
+ "lon": xr.DataArray([210, 215]), # outside 0-180
869
+ "straddle": False,
870
+ }
871
+ with pytest.raises(ValueError, match="longitude range"):
872
+ choose_subdomain(
873
+ non_global_dataset,
874
+ dim_names={"latitude": "latitude", "longitude": "longitude"},
875
+ resolution=1.0,
876
+ is_global=False,
877
+ target_coords=target_coords,
878
+ use_dask=use_dask,
879
+ )
880
+
881
+
882
+ def test_choose_subdomain_raises_on_empty_lat(global_dataset, use_dask):
883
+ target_coords = {
884
+ "lat": xr.DataArray([1000, 1010]), # outside dataset range
885
+ "lon": xr.DataArray([30, 40]),
886
+ "straddle": False,
887
+ }
888
+ with pytest.raises(ValueError, match="latitude range"):
889
+ choose_subdomain(
890
+ global_dataset,
891
+ dim_names={"latitude": "latitude", "longitude": "longitude"},
892
+ resolution=1.0,
893
+ is_global=True,
894
+ target_coords=target_coords,
895
+ use_dask=use_dask,
744
896
  )
745
897
 
746
- names = river_dataset.ds["name"].values
747
- assert "Amazon_1" in names
748
- assert "Amazon_2" in names
749
- assert "Nile" in names
750
- assert len(set(names)) == len(names) # all names must be unique
898
+
899
+ def test_choose_subdomain_straddle(global_dataset, use_dask):
900
+ target_coords = {
901
+ "lat": xr.DataArray([-10, 10]),
902
+ "lon": xr.DataArray([-170, 170]), # cross the dateline
903
+ "straddle": True,
904
+ }
905
+ out = choose_subdomain(
906
+ global_dataset,
907
+ dim_names={"latitude": "latitude", "longitude": "longitude"},
908
+ resolution=1.0,
909
+ is_global=True,
910
+ target_coords=target_coords,
911
+ buffer_points=5,
912
+ use_dask=use_dask,
913
+ )
914
+ # Ensure output includes both sides of the dateline, mapped into -180 - 180
915
+ assert (out.longitude.min() < 0) and (out.longitude.max() > 0)
916
+
917
+
918
+ def test_choose_subdomain_wraps_negative_lon(global_dataset, use_dask):
919
+ target_coords = {
920
+ "lat": xr.DataArray([0, 20]),
921
+ "lon": xr.DataArray([-20, -10]), # negative longitudes
922
+ "straddle": False,
923
+ }
924
+ out = choose_subdomain(
925
+ global_dataset,
926
+ dim_names={"latitude": "latitude", "longitude": "longitude"},
927
+ resolution=1.0,
928
+ is_global=True,
929
+ target_coords=target_coords,
930
+ buffer_points=2,
931
+ use_dask=use_dask,
932
+ )
933
+ # Output longitudes should be shifted into [0, 360]
934
+ assert (out.longitude >= 0).all()
935
+
936
+
937
+ def test_choose_subdomain_respects_buffer(global_dataset, use_dask):
938
+ target_coords = {
939
+ "lat": xr.DataArray([0, 0]),
940
+ "lon": xr.DataArray([50, 50]),
941
+ "straddle": False,
942
+ }
943
+ out = choose_subdomain(
944
+ global_dataset,
945
+ dim_names={"latitude": "latitude", "longitude": "longitude"},
946
+ resolution=1.0,
947
+ is_global=True,
948
+ target_coords=target_coords,
949
+ buffer_points=10,
950
+ use_dask=use_dask,
951
+ )
952
+ # Buffer should extend at least 10 degrees beyond target lon
953
+ assert out.longitude.min() <= 40
954
+ assert out.longitude.max() >= 60
955
+
956
+
957
+ # test get_glorys_bounds
958
+
959
+
960
+ @pytest.fixture
961
+ def glorys_grid_0_360(tmp_path):
962
+ lats = np.linspace(-90, 90, 181)
963
+ lons = np.linspace(0, 360, 361)
964
+ ds = xr.Dataset(coords={"latitude": lats, "longitude": lons})
965
+ path = tmp_path / "GLORYS_0_360.nc"
966
+ ds.to_netcdf(path)
967
+ return path
968
+
969
+
970
+ @pytest.fixture
971
+ def glorys_grid_neg180_180(tmp_path):
972
+ lats = np.linspace(-90, 90, 181)
973
+ lons = np.linspace(-180, 180, 361)
974
+ ds = xr.Dataset(coords={"latitude": lats, "longitude": lons})
975
+ path = tmp_path / "GLORYS_neg180_180.nc"
976
+ ds.to_netcdf(path)
977
+ return path
978
+
979
+
980
+ @pytest.fixture
981
+ def glorys_grid_real():
982
+ return GLORYS_GLOBAL_GRID_PATH
983
+
984
+
985
+ @pytest.mark.parametrize(
986
+ "grid_fixture",
987
+ [
988
+ "grid",
989
+ "grid_that_straddles_dateline",
990
+ "grid_that_straddles_180_degree_meridian",
991
+ "small_grid",
992
+ "tiny_grid",
993
+ ],
994
+ )
995
+ @pytest.mark.parametrize(
996
+ "glorys_grid_fixture",
997
+ ["glorys_grid_0_360", "glorys_grid_neg180_180", "glorys_grid_real"],
998
+ )
999
+ def test_get_glorys_bounds(tmp_path, grid_fixture, glorys_grid_fixture, request):
1000
+ grid = request.getfixturevalue(grid_fixture)
1001
+ glorys_grid_path = request.getfixturevalue(glorys_grid_fixture)
1002
+
1003
+ bounds = get_glorys_bounds(grid=grid, glorys_grid_path=glorys_grid_path)
1004
+ assert set(bounds) == {
1005
+ "minimum_latitude",
1006
+ "maximum_latitude",
1007
+ "minimum_longitude",
1008
+ "maximum_longitude",
1009
+ }
1010
+
1011
+
1012
+ @pytest.mark.use_copernicus
1013
+ @pytest.mark.skipif(copernicusmarine is None, reason="copernicusmarine required")
1014
+ @pytest.mark.parametrize(
1015
+ "grid_fixture",
1016
+ [
1017
+ "tiny_grid_that_straddles_dateline",
1018
+ "tiny_grid_that_straddles_180_degree_meridian",
1019
+ "tiny_rotated_grid",
1020
+ ],
1021
+ )
1022
+ def test_invariance_to_get_glorys_bounds(tmp_path, grid_fixture, use_dask, request):
1023
+ start_time = datetime(2012, 1, 1)
1024
+ grid = request.getfixturevalue(grid_fixture)
1025
+ target_coords = get_target_coords(grid)
1026
+
1027
+ regional_file, bigger_regional_file = download_regional_and_bigger(
1028
+ tmp_path, grid, start_time, variables=["thetao", "uo", "zos"]
1029
+ )
1030
+
1031
+ # create datasets from regional and bigger regional data
1032
+ regional_data = GLORYSDataset(
1033
+ var_names={"temp": "thetao", "u": "uo", "zeta": "zos"},
1034
+ filename=regional_file,
1035
+ start_time=start_time,
1036
+ climatology=False,
1037
+ allow_flex_time=False,
1038
+ use_dask=use_dask,
1039
+ )
1040
+ bigger_regional_data = GLORYSDataset(
1041
+ var_names={"temp": "thetao", "u": "uo", "zeta": "zos"},
1042
+ filename=bigger_regional_file,
1043
+ start_time=start_time,
1044
+ climatology=False,
1045
+ allow_flex_time=False,
1046
+ use_dask=use_dask,
1047
+ )
1048
+
1049
+ # subset both datasets and check they are the same
1050
+ regional_data.choose_subdomain(target_coords)
1051
+ bigger_regional_data.choose_subdomain(target_coords)
1052
+
1053
+ # Use assert_allclose instead of equals: necessary for grids that straddle the 180° meridian.
1054
+ # Copernicus returns data on [-180, 180] by default, but if you request a range
1055
+ # like [170, 190], it remaps longitudes. That remapping introduces tiny floating
1056
+ # point differences in the longitude coordinate, so strict equality will fail
1057
+ # even though the data are effectively identical.
1058
+ # For grids that do not straddle the 180° meridian, strict equality still holds.
1059
+ xr.testing.assert_allclose(bigger_regional_data.ds, regional_data.ds)