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
roms_tools/utils.py CHANGED
@@ -10,6 +10,7 @@ from pathlib import Path
10
10
  from typing import TypeAlias
11
11
 
12
12
  import numpy as np
13
+ import pandas as pd
13
14
  import xarray as xr
14
15
 
15
16
  from roms_tools.constants import R_EARTH
@@ -476,96 +477,199 @@ def load_data(
476
477
  return ds
477
478
 
478
479
 
479
- def interpolate_from_rho_to_u(field, method="additive"):
480
- """Interpolates the given field from rho points to u points.
481
-
482
- This function performs an interpolation from the rho grid (cell centers) to the u grid
483
- (cell edges in the xi direction). Depending on the chosen method, it either averages
484
- (additive) or multiplies (multiplicative) the field values between adjacent rho points
485
- along the xi dimension. It also handles the removal of unnecessary coordinate variables
486
- and updates the dimensions accordingly.
480
+ def _interpolate_generic(
481
+ field: xr.DataArray,
482
+ dim_in: str,
483
+ dim_out: str,
484
+ method: str = "additive",
485
+ drop_coords: Sequence[str] | None = None,
486
+ pad_end: bool = False,
487
+ ) -> xr.DataArray:
488
+ """
489
+ Generic interpolation along one horizontal dimension.
487
490
 
488
491
  Parameters
489
492
  ----------
490
493
  field : xr.DataArray
491
- The input data array on the rho grid to be interpolated. It is assumed to have a dimension
492
- named "xi_rho".
493
-
494
- method : str, optional, default='additive'
495
- The method to use for interpolation. Options are:
496
- - 'additive': Average the field values between adjacent rho points.
497
- - 'multiplicative': Multiply the field values between adjacent rho points. Appropriate for
498
- binary masks.
494
+ Input array to interpolate.
495
+ dim_in : str
496
+ Dimension along which to interpolate (e.g., "xi_rho").
497
+ dim_out : str
498
+ New dimension name after interpolation (e.g., "xi_u").
499
+ method : str, default "additive"
500
+ Interpolation method:
501
+ - "additive": average adjacent points
502
+ - "multiplicative": multiply adjacent points (useful for masks)
503
+ drop_coords : Sequence[str] or None, optional
504
+ Coordinate variables to drop (e.g., ["lat_rho", "lon_rho"]).
505
+ pad_end : bool, default False
506
+ Whether to pad the last point with NaN (useful when interpolating back to rho grid).
499
507
 
500
508
  Returns
501
509
  -------
502
- field_interpolated : xr.DataArray
503
- The interpolated data array on the u grid with the dimension "xi_u".
510
+ xr.DataArray
511
+ Interpolated array with dimension `dim_out`.
504
512
  """
505
- if method == "additive":
506
- field_interpolated = 0.5 * (field + field.shift(xi_rho=1)).isel(
507
- xi_rho=slice(1, None)
513
+ if not isinstance(field, xr.DataArray):
514
+ raise TypeError(
515
+ "_interpolate_generic expects an xarray.DataArray, "
516
+ f"got {type(field).__name__}"
508
517
  )
518
+
519
+ if drop_coords:
520
+ for coord in drop_coords:
521
+ if coord in field.coords:
522
+ field = field.drop_vars(coord)
523
+
524
+ if method == "additive":
525
+ interp = 0.5 * (field + field.shift(**{dim_in: 1}))
509
526
  elif method == "multiplicative":
510
- field_interpolated = (field * field.shift(xi_rho=1)).isel(xi_rho=slice(1, None))
527
+ interp = field * field.shift(**{dim_in: 1})
511
528
  else:
512
529
  raise NotImplementedError(f"Unsupported method '{method}' specified.")
513
530
 
514
- vars_to_drop = ["lat_rho", "lon_rho", "eta_rho", "xi_rho"]
515
- for var in vars_to_drop:
516
- if var in field_interpolated.coords:
517
- field_interpolated = field_interpolated.drop_vars(var)
531
+ if pad_end:
532
+ pad_shape = {d: field.sizes[d] for d in field.dims}
533
+ pad_shape[dim_in] = 1
534
+ pad = xr.DataArray(
535
+ np.nan * np.ones(tuple(pad_shape[d] for d in field.dims)),
536
+ dims=field.dims,
537
+ )
538
+ interp = xr.concat([interp, pad], dim=dim_in)
539
+ else:
540
+ interp = interp.isel({dim_in: slice(1, None)})
541
+
542
+ interp = interp.swap_dims({dim_in: dim_out})
518
543
 
519
- field_interpolated = field_interpolated.swap_dims({"xi_rho": "xi_u"})
544
+ return interp
520
545
 
521
- return field_interpolated
546
+
547
+ def interpolate_from_rho_to_u(
548
+ field: xr.DataArray, method: str = "additive"
549
+ ) -> xr.DataArray:
550
+ """Interpolate a field from rho points to u points (xi direction)."""
551
+ return _interpolate_generic(
552
+ field,
553
+ dim_in="xi_rho",
554
+ dim_out="xi_u",
555
+ method=method,
556
+ drop_coords=["lat_rho", "lon_rho", "eta_rho", "xi_rho"],
557
+ )
522
558
 
523
559
 
524
- def interpolate_from_rho_to_v(field, method="additive"):
525
- """Interpolates the given field from rho points to v points.
560
+ def interpolate_from_rho_to_v(
561
+ field: xr.DataArray, method: str = "additive"
562
+ ) -> xr.DataArray:
563
+ """Interpolate a field from rho points to v points (eta direction)."""
564
+ return _interpolate_generic(
565
+ field,
566
+ dim_in="eta_rho",
567
+ dim_out="eta_v",
568
+ method=method,
569
+ drop_coords=["lat_rho", "lon_rho", "eta_rho", "xi_rho"],
570
+ )
526
571
 
527
- This function performs an interpolation from the rho grid (cell centers) to the v grid
528
- (cell edges in the eta direction). Depending on the chosen method, it either averages
529
- (additive) or multiplies (multiplicative) the field values between adjacent rho points
530
- along the eta dimension. It also handles the removal of unnecessary coordinate variables
531
- and updates the dimensions accordingly.
572
+
573
+ def interpolate_from_u_to_rho(
574
+ field: xr.DataArray, method: str = "additive"
575
+ ) -> xr.DataArray:
576
+ """Interpolate a field from u points back to rho points (xi direction)."""
577
+ return _interpolate_generic(
578
+ field,
579
+ dim_in="xi_u",
580
+ dim_out="xi_rho",
581
+ method=method,
582
+ drop_coords=["lat_u", "lon_u", "eta_rho", "xi_u"],
583
+ pad_end=True,
584
+ )
585
+
586
+
587
+ def interpolate_from_v_to_rho(
588
+ field: xr.DataArray, method: str = "additive"
589
+ ) -> xr.DataArray:
590
+ """Interpolate a field from v points back to rho points (eta direction)."""
591
+ return _interpolate_generic(
592
+ field,
593
+ dim_in="eta_v",
594
+ dim_out="eta_rho",
595
+ method=method,
596
+ drop_coords=["lat_v", "lon_v", "eta_v", "xi_rho"],
597
+ pad_end=True,
598
+ )
599
+
600
+
601
+ def rotate_velocities(
602
+ u: xr.DataArray,
603
+ v: xr.DataArray,
604
+ angle: xr.DataArray,
605
+ interpolate_before: bool = False,
606
+ interpolate_after: bool = False,
607
+ ) -> tuple[xr.DataArray, xr.DataArray]:
608
+ """
609
+ Rotate horizontal velocity components to align with a rotated grid.
610
+
611
+ This function rotates zonal (u) and meridional (v) velocity components
612
+ using a grid angle field. It can be used to:
613
+
614
+ 1. Rotate model velocities from the ROMS model grid to a lat-lon reference frame.
615
+ 2. Rotate lat-lon velocities onto the ROMS model grid.
616
+
617
+ Optionally, velocities can be interpolated between staggered C-grid
618
+ locations (u-, v-, and rho-points) before and/or after rotation.
532
619
 
533
620
  Parameters
534
621
  ----------
535
- field : xr.DataArray
536
- The input data array on the rho grid to be interpolated. It is assumed to have a dimension
537
- named "eta_rho".
538
-
539
- method : str, optional, default='additive'
540
- The method to use for interpolation. Options are:
541
- - 'additive': Average the field values between adjacent rho points.
542
- - 'multiplicative': Multiply the field values between adjacent rho points. Appropriate for
543
- binary masks.
622
+ u : xarray.DataArray
623
+ Zonal (east-west) velocity component defined on u-points.
624
+ v : xarray.DataArray
625
+ Meridional (north-south) velocity component defined on v-points.
626
+ angle : xarray.DataArray
627
+ Grid orientation angle in radians, defined at rho-points. This is the
628
+ ROMS grid angle: the angle between the model xi-direction and true east.
629
+ Positive values indicate that the model grid is rotated counterclockwise
630
+ relative to east (which is mathematically equivalent to rotating velocity
631
+ vectors clockwise).
632
+ The rotation transforms velocity components between earth-relative
633
+ (east/north) and grid-relative (xi/eta) coordinates. To reverse the
634
+ transformation (e.g., model → lat-lon), provide ``-angle``.
635
+ interpolate_before : bool, optional
636
+ If True, interpolate ``u`` and ``v`` to rho-points before rotation.
637
+ Default is False.
638
+ interpolate_after : bool, optional
639
+ If True, interpolate the rotated velocities back to u- and v-points
640
+ after rotation. Default is True.
544
641
 
545
642
  Returns
546
643
  -------
547
- field_interpolated : xr.DataArray
548
- The interpolated data array on the v grid with the dimension "eta_v".
644
+ u_rot, v_rot : tuple of xarray.DataArray
645
+ Rotated velocity components. If ``interpolate_after`` is True, ``u_rot``
646
+ is defined on u-points and ``v_rot`` on v-points; otherwise, both are
647
+ defined at rho-points.
648
+
649
+ Notes
650
+ -----
651
+ The rotation follows the standard ROMS convention:
652
+ - ``u_rot = u * cos(angle) + v * sin(angle)``
653
+ - ``v_rot = v * cos(angle) - u * sin(angle)``
654
+ This function is versatile and can be used for both directions of rotation:
655
+ - Lat-lon → model grid: provide the grid angle.
656
+ - Model grid → lat-lon: provide the negative of the grid angle.
549
657
  """
550
- if method == "additive":
551
- field_interpolated = 0.5 * (field + field.shift(eta_rho=1)).isel(
552
- eta_rho=slice(1, None)
553
- )
554
- elif method == "multiplicative":
555
- field_interpolated = (field * field.shift(eta_rho=1)).isel(
556
- eta_rho=slice(1, None)
557
- )
558
- else:
559
- raise NotImplementedError(f"Unsupported method '{method}' specified.")
658
+ # Interpolate to rho-points
659
+ if interpolate_before:
660
+ u = interpolate_from_u_to_rho(u)
661
+ v = interpolate_from_v_to_rho(v)
560
662
 
561
- vars_to_drop = ["lat_rho", "lon_rho", "eta_rho", "xi_rho"]
562
- for var in vars_to_drop:
563
- if var in field_interpolated.coords:
564
- field_interpolated = field_interpolated.drop_vars(var)
663
+ # Rotate velocities to grid orientation
664
+ u_rot = u * np.cos(angle) + v * np.sin(angle)
665
+ v_rot = v * np.cos(angle) - u * np.sin(angle)
565
666
 
566
- field_interpolated = field_interpolated.swap_dims({"eta_rho": "eta_v"})
667
+ # Interpolate to u- and v-points
668
+ if interpolate_after:
669
+ u_rot = interpolate_from_rho_to_u(u_rot)
670
+ v_rot = interpolate_from_rho_to_v(v_rot)
567
671
 
568
- return field_interpolated
672
+ return u_rot, v_rot
569
673
 
570
674
 
571
675
  def transpose_dimensions(da: xr.DataArray) -> xr.DataArray:
@@ -1031,3 +1135,197 @@ def get_pkg_error_msg(purpose: str, package_name: str, option_name: str) -> str:
1031
1135
  • `pip install {package_name}` or
1032
1136
  • `conda install {package_name}`
1033
1137
  Alternatively, install `roms-tools` with conda to include all dependencies.""")
1138
+
1139
+
1140
+ def wrap_longitudes(ds: xr.Dataset, straddle: bool) -> xr.Dataset:
1141
+ """
1142
+ Safely adjust longitude coordinates for datasets that may or may not cross
1143
+ the dateline. Only modifies longitude-like coordinates that are present.
1144
+
1145
+ Parameters
1146
+ ----------
1147
+ ds : xr.Dataset
1148
+ Dataset containing longitude coordinates (e.g., lon_rho, lon_u, lon_v).
1149
+ straddle : bool
1150
+ - True: force longitudes into [-180, 180]
1151
+ - False: force longitudes into [0, 360]
1152
+
1153
+ Returns
1154
+ -------
1155
+ xr.Dataset
1156
+ A new dataset with adjusted longitude coordinates.
1157
+ """
1158
+ lon_coords = ["lon_rho", "lon_u", "lon_v"]
1159
+
1160
+ for lon_name in lon_coords:
1161
+ if lon_name not in ds.coords:
1162
+ continue # skip missing coordinate
1163
+
1164
+ lon = ds.coords[lon_name]
1165
+
1166
+ if straddle:
1167
+ # wrap into [-180, 180]
1168
+ lon_wrapped = xr.where(lon > 180, lon - 360, lon)
1169
+ else:
1170
+ # wrap into [0, 360]
1171
+ lon_wrapped = xr.where(lon < 0, lon + 360, lon)
1172
+
1173
+ # preserve attributes
1174
+ lon_wrapped.attrs = lon.attrs.copy()
1175
+
1176
+ # reassign explicitly as a coordinate
1177
+ ds = ds.assign_coords({lon_name: lon_wrapped})
1178
+
1179
+ return ds
1180
+
1181
+
1182
+ def interpolate_from_climatology(
1183
+ field: xr.DataArray | xr.Dataset,
1184
+ time_dim: str,
1185
+ time_coord: str,
1186
+ time: xr.DataArray | pd.DatetimeIndex,
1187
+ ) -> xr.DataArray | xr.Dataset:
1188
+ """Interpolates a climatological field to specified time points.
1189
+
1190
+ This function interpolates the input `field` based on `day_of_year` values
1191
+ extracted from the provided `time` points. If `field` is an `xarray.Dataset`,
1192
+ interpolation is applied to all its data variables individually.
1193
+
1194
+ Parameters
1195
+ ----------
1196
+ field : xarray.DataArray or xarray.Dataset
1197
+ The input field to be interpolated.
1198
+ - If `field` is an `xarray.DataArray`, it must have a time dimension identified by `time_dim_name`.
1199
+ - If `field` is an `xarray.Dataset`, all variables within the dataset are interpolated along `time_dim_name`.
1200
+ The time dimension is assumed to represent `day_of_year` for climatological purposes.
1201
+ time_dim : str
1202
+ The name of the time dimension in `field`.
1203
+ time_coord : str
1204
+ The name of the time coordinate in `field`.
1205
+ time : xarray.DataArray or pandas.DatetimeIndex
1206
+ The target time points for interpolation. These are internally converted to `day_of_year`
1207
+ before performing interpolation.
1208
+
1209
+ Returns
1210
+ -------
1211
+ xarray.DataArray or xarray.Dataset
1212
+ The interpolated field, maintaining the same type (`xarray.DataArray` or `xarray.Dataset`)
1213
+ but aligned to the specified `time` values.
1214
+
1215
+ Notes
1216
+ -----
1217
+ - This function assumes that `field` represents a climatological dataset, where time is expressed as `day_of_year` (1-365).
1218
+ - The `time` input is automatically converted to `day_of_year`, so manual conversion is not required before calling this function.
1219
+ """
1220
+
1221
+ def np_times_to_fractional_days(
1222
+ np_times: np.ndarray | pd.DatetimeIndex | np.datetime64 | pd.Timestamp,
1223
+ ) -> np.ndarray:
1224
+ """Convert datetime(s) to fractional day-of-year values."""
1225
+ pd_times = pd.to_datetime(np_times)
1226
+
1227
+ # scalar input -> make it a 1-element array
1228
+ if np.isscalar(pd_times):
1229
+ pd_times = np.array([pd_times])
1230
+
1231
+ fractional_days = pd_times.dayofyear + (
1232
+ pd_times.hour / 24 + pd_times.minute / 1440 + pd_times.second / 86400
1233
+ )
1234
+ return (
1235
+ fractional_days.values
1236
+ if hasattr(fractional_days, "values")
1237
+ else np.array(fractional_days)
1238
+ )
1239
+
1240
+ def interpolate_single_field(data_array: xr.DataArray) -> xr.DataArray:
1241
+ if isinstance(time, xr.DataArray):
1242
+ day_of_year = time.dt.dayofyear
1243
+ else:
1244
+ day_of_year = np_times_to_fractional_days(time)
1245
+
1246
+ data_array_interpolated = interpolate_cyclic_time(
1247
+ data_array, time_dim, time_coord, day_of_year
1248
+ )
1249
+
1250
+ # expand dims if single element
1251
+ if day_of_year.size == 1:
1252
+ data_array_interpolated = data_array_interpolated.expand_dims({time_dim: 1})
1253
+ return data_array_interpolated
1254
+
1255
+ if isinstance(field, xr.DataArray):
1256
+ return interpolate_single_field(field)
1257
+ elif isinstance(field, xr.Dataset):
1258
+ interpolated_data_vars = {
1259
+ var: interpolate_single_field(data_array)
1260
+ for var, data_array in field.data_vars.items()
1261
+ }
1262
+ return xr.Dataset(interpolated_data_vars, attrs=field.attrs)
1263
+
1264
+ else:
1265
+ raise TypeError("Input 'field' must be an xarray.DataArray or xarray.Dataset.")
1266
+
1267
+
1268
+ def interpolate_cyclic_time(
1269
+ data_array: xr.DataArray,
1270
+ time_dim: str,
1271
+ time_coord: str,
1272
+ day_of_year: int | float | np.ndarray | xr.DataArray | Sequence[int | float],
1273
+ ) -> xr.DataArray:
1274
+ """Interpolates a DataArray cyclically across the start and end of the year.
1275
+
1276
+ This function extends the data cyclically by appending the last time step
1277
+ (shifted back by one year) at the beginning and the first time step
1278
+ (shifted forward by one year) at the end. It then performs linear interpolation
1279
+ to match the specified `day_of_year` values.
1280
+
1281
+ Parameters
1282
+ ----------
1283
+ data_array : xr.DataArray
1284
+ The input data array containing a time-like dimension.
1285
+ time_dim : str
1286
+ The name of the time dimension in the dataset.
1287
+ time_coord : str
1288
+ The name of the time coordinate in the dataset.
1289
+ day_of_year : Union[int, float, np.ndarray, xr.DataArray, Sequence[Union[int, float]]]
1290
+ The target day(s) of the year for interpolation. This can be:
1291
+ - A single integer or float representing the day of the year.
1292
+ - A NumPy array or xarray DataArray containing multiple days.
1293
+ - A list or tuple of integers or floats for multiple target days.
1294
+
1295
+ Returns
1296
+ -------
1297
+ xr.DataArray
1298
+ The interpolated DataArray, ensuring cyclic continuity across year boundaries.
1299
+
1300
+ Notes
1301
+ -----
1302
+ - This function is useful for interpolating climatological data, where the time axis
1303
+ represents a repeating annual cycle.
1304
+ - The `day_of_year` values should be within the range [1, 365] or [1, 366] for leap years.
1305
+ """
1306
+ # Concatenate across the beginning and end of the year
1307
+ time_concat = xr.concat(
1308
+ [
1309
+ data_array[time_coord][-1] - 365.25, # Shift last time backward
1310
+ data_array[time_coord],
1311
+ data_array[time_coord][0] + 365.25, # Shift first time forward
1312
+ ],
1313
+ dim=time_dim,
1314
+ )
1315
+
1316
+ data_array_concat = xr.concat(
1317
+ [
1318
+ data_array.isel(**{time_dim: -1}), # Append last value at the beginning
1319
+ data_array,
1320
+ data_array.isel(**{time_dim: 0}), # Append first value at the end
1321
+ ],
1322
+ dim=time_dim,
1323
+ )
1324
+ data_array_concat[time_dim] = time_concat
1325
+
1326
+ # Interpolate to specified times
1327
+ data_array_interpolated = data_array_concat.interp(
1328
+ **{time_dim: day_of_year}, method="linear"
1329
+ )
1330
+
1331
+ return data_array_interpolated
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roms-tools
3
- Version: 3.3.0
3
+ Version: 3.5.0
4
4
  Summary: Tools for running and analysing UCLA-ROMS simulations
5
5
  Author-email: Nora Loose <nora.loose@gmail.com>, Thomas Nicholas <tom@cworthy.org>, Scott Eilerman <scott.eilerman@cworthy.org>
6
6
  License: Apache-2
@@ -64,7 +64,7 @@ Dynamic: license-file
64
64
 
65
65
  ## Overview
66
66
 
67
- A suite of Python tools for setting up and analyzing a [UCLA-ROMS](https://github.com/CESR-lab/ucla-roms) simulation with or without [MARBL biogeochemistry](https://marbl-ecosys.github.io/versions/latest_release/index.html).
67
+ A suite of Python tools for setting up and analyzing a [UCLA-ROMS](https://github.com/CWorthy-ocean/ucla-roms) simulation with or without [MARBL biogeochemistry](https://marbl-ecosys.github.io/versions/latest_release/index.html).
68
68
 
69
69
  ## Installation
70
70
 
@@ -152,5 +152,4 @@ We also accept contributions in the form of Pull Requests.
152
152
 
153
153
  ## See also
154
154
 
155
- - [ROMS source code](https://github.com/CESR-lab/ucla-roms)
156
155
  - [C-Star](https://github.com/CWorthy-ocean/C-Star)