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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) 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 +76 -64
  6. roms_tools/{setup → datasets}/river_datasets.py +9 -4
  7. roms_tools/datasets/roms_dataset.py +767 -0
  8. roms_tools/datasets/utils.py +475 -0
  9. roms_tools/{setup/fill.py → fill.py} +110 -13
  10. roms_tools/plot.py +4 -4
  11. roms_tools/setup/boundary_forcing.py +51 -43
  12. roms_tools/setup/cdr_release.py +2 -4
  13. roms_tools/setup/grid.py +29 -12
  14. roms_tools/setup/initial_conditions.py +19 -19
  15. roms_tools/setup/nesting.py +8 -4
  16. roms_tools/setup/river_forcing.py +4 -4
  17. roms_tools/setup/surface_forcing.py +14 -9
  18. roms_tools/setup/tides.py +1 -1
  19. roms_tools/setup/topography.py +10 -2
  20. roms_tools/setup/utils.py +72 -524
  21. roms_tools/tests/test_analysis/test_cdr_ensemble.py +4 -6
  22. roms_tools/tests/test_analysis/test_roms_output.py +1 -220
  23. roms_tools/tests/{test_setup → test_datasets}/test_lat_lon_datasets.py +4 -4
  24. roms_tools/tests/{test_setup → test_datasets}/test_river_datasets.py +1 -1
  25. roms_tools/tests/test_datasets/test_roms_dataset.py +539 -0
  26. roms_tools/tests/test_datasets/test_utils.py +527 -0
  27. roms_tools/tests/{test_setup/test_fill.py → test_fill.py} +72 -9
  28. roms_tools/tests/test_setup/test_boundary_forcing.py +57 -138
  29. roms_tools/tests/test_setup/test_cdr_release.py +4 -5
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zarr.json +293 -2021
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zarr.json +294 -2022
  32. roms_tools/tests/test_setup/test_grid.py +42 -1
  33. roms_tools/tests/test_setup/test_initial_conditions.py +3 -94
  34. roms_tools/tests/test_setup/test_nesting.py +2 -1
  35. roms_tools/tests/test_setup/test_surface_forcing.py +1 -1
  36. roms_tools/tests/test_setup/test_tides.py +1 -1
  37. roms_tools/tests/test_setup/test_utils.py +100 -15
  38. roms_tools/tests/test_tiling/test_partition.py +63 -15
  39. roms_tools/tests/test_utils.py +78 -0
  40. roms_tools/tiling/partition.py +81 -211
  41. roms_tools/utils.py +193 -0
  42. {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/METADATA +1 -1
  43. {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/RECORD +46 -170
  44. {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/WHEEL +1 -1
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/zarr.json +0 -54
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/c/0/0/0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/zarr.json +0 -54
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/zarr.json +0 -54
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/c/0/0/0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/zarr.json +0 -54
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/c/0/0/0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/zarr.json +0 -54
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/zarr.json +0 -54
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/c/0/0/0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/zarr.json +0 -54
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/c/0/0/0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/zarr.json +0 -54
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/c/0/0/0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/zarr.json +0 -54
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/zarr.json +0 -54
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/c/0/0/0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/zarr.json +0 -54
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/c/0/0/0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/zarr.json +0 -54
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/c/0/0/0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/zarr.json +0 -54
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/c/0/0/0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/zarr.json +0 -54
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/c/0/0/0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/zarr.json +0 -54
  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/PO4_west/zarr.json +0 -54
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/zarr.json +0 -54
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/c/0/0/0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/zarr.json +0 -54
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/zarr.json +0 -54
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/zarr.json +0 -54
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/c/0/0/0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/zarr.json +0 -54
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/zarr.json +0 -54
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/c/0/0/0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/zarr.json +0 -54
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/zarr.json +0 -54
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/zarr.json +0 -54
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/c/0/0/0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/zarr.json +0 -54
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/c/0/0/0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/zarr.json +0 -54
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/zarr.json +0 -54
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/c/0/0/0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/zarr.json +0 -54
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/c/0/0/0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/zarr.json +0 -54
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/c/0/0/0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/zarr.json +0 -54
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/c/0/0/0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/zarr.json +0 -54
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_ALT_CO2_west/zarr.json +0 -54
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_west/c/0/0/0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/ALK_west/zarr.json +0 -54
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_ALT_CO2_west/zarr.json +0 -54
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_west/c/0/0/0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DIC_west/zarr.json +0 -54
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOC_west/c/0/0/0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOC_west/zarr.json +0 -54
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOCr_west/zarr.json +0 -54
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DON_west/c/0/0/0 +0 -0
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DON_west/zarr.json +0 -54
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DONr_west/c/0/0/0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DONr_west/zarr.json +0 -54
  125. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOP_west/c/0/0/0 +0 -0
  126. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOP_west/zarr.json +0 -54
  127. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
  128. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/DOPr_west/zarr.json +0 -54
  129. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Fe_west/c/0/0/0 +0 -0
  130. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Fe_west/zarr.json +0 -54
  131. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Lig_west/c/0/0/0 +0 -0
  132. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/Lig_west/zarr.json +0 -54
  133. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NH4_west/c/0/0/0 +0 -0
  134. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NH4_west/zarr.json +0 -54
  135. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NO3_west/c/0/0/0 +0 -0
  136. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/NO3_west/zarr.json +0 -54
  137. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/O2_west/c/0/0/0 +0 -0
  138. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/O2_west/zarr.json +0 -54
  139. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/PO4_west/c/0/0/0 +0 -0
  140. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/PO4_west/zarr.json +0 -54
  141. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
  142. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/SiO3_west/zarr.json +0 -54
  143. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatC_west/c/0/0/0 +0 -0
  144. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatC_west/zarr.json +0 -54
  145. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
  146. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatChl_west/zarr.json +0 -54
  147. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
  148. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatFe_west/zarr.json +0 -54
  149. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatP_west/c/0/0/0 +0 -0
  150. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatP_west/zarr.json +0 -54
  151. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
  152. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diatSi_west/zarr.json +0 -54
  153. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazC_west/c/0/0/0 +0 -0
  154. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazC_west/zarr.json +0 -54
  155. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
  156. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazChl_west/zarr.json +0 -54
  157. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
  158. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazFe_west/zarr.json +0 -54
  159. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazP_west/c/0/0/0 +0 -0
  160. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/diazP_west/zarr.json +0 -54
  161. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spC_west/c/0/0/0 +0 -0
  162. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spC_west/zarr.json +0 -54
  163. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
  164. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spCaCO3_west/zarr.json +0 -54
  165. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spChl_west/c/0/0/0 +0 -0
  166. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spChl_west/zarr.json +0 -54
  167. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spFe_west/c/0/0/0 +0 -0
  168. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spFe_west/zarr.json +0 -54
  169. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spP_west/c/0/0/0 +0 -0
  170. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/spP_west/zarr.json +0 -54
  171. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zooC_west/c/0/0/0 +0 -0
  172. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_unified_climatology.zarr/zooC_west/zarr.json +0 -54
  173. {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/licenses/LICENSE +0 -0
  174. {roms_tools-3.3.0.dist-info → roms_tools-3.4.0.dist-info}/top_level.txt +0 -0
roms_tools/__init__.py CHANGED
@@ -20,7 +20,7 @@ from roms_tools.setup.river_forcing import RiverForcing # noqa: F401
20
20
  from roms_tools.setup.surface_forcing import SurfaceForcing # noqa: F401
21
21
  from roms_tools.setup.tides import TidalForcing # noqa: F401
22
22
  from roms_tools.tiling.partition import partition_netcdf # noqa: F401
23
- from roms_tools.setup.lat_lon_datasets import get_glorys_bounds # noqa: F401
23
+ from roms_tools.datasets.lat_lon_datasets import get_glorys_bounds # noqa: F401
24
24
  from roms_tools.tiling.join import open_partitions, join_netcdf # noqa: F401
25
25
 
26
26
 
@@ -59,7 +59,7 @@ class Ensemble:
59
59
  Parameters
60
60
  ----------
61
61
  ds : xr.Dataset
62
- Dataset containing a "cdr_efficiency" variable and "abs_time" coordinate.
62
+ Dataset containing a "cdr_efficiency" variable and "time" coordinate.
63
63
 
64
64
  Returns
65
65
  -------
@@ -69,18 +69,20 @@ class Ensemble:
69
69
  Raises
70
70
  ------
71
71
  ValueError
72
- If 'abs_time' coordinate is missing or there are no valid efficiency values.
72
+ If 'time' coordinate is missing or there are no valid efficiency values.
73
73
  """
74
74
  eff = ds["cdr_efficiency"]
75
75
 
76
- # Check that abs_time exists
77
- if "abs_time" in eff.coords:
78
- abs_time = eff.coords["abs_time"]
79
- elif "abs_time" in ds.data_vars:
80
- abs_time = ds["abs_time"]
76
+ # Check that time coordinate exists
77
+ if "time" in eff.coords:
78
+ abs_time = eff.coords["time"]
79
+ eff = eff.drop_vars("time")
80
+ elif "time" in ds.data_vars:
81
+ abs_time = ds["time"]
82
+ eff = eff.drop_vars("time")
81
83
  else:
82
84
  raise ValueError(
83
- "Dataset must contain an 'abs_time' coordinate or data variable."
85
+ "Dataset must contain a 'time' coordinate or data variable."
84
86
  )
85
87
 
86
88
  # Drop NaNs to find first valid time
@@ -97,11 +99,6 @@ class Ensemble:
97
99
  eff_rel = eff.assign_coords(time=time_rel)
98
100
  eff_rel.time.attrs["long_name"] = "time since release start"
99
101
 
100
- if hasattr(eff_rel, "coords") and "abs_time" in eff_rel.coords:
101
- eff_rel = eff_rel.drop_vars("abs_time")
102
- elif hasattr(eff_rel, "variables") and "abs_time" in eff_rel.variables:
103
- eff_rel = eff_rel.drop_vars("abs_time")
104
-
105
102
  return eff_rel
106
103
 
107
104
  def _align_times(self, effs: dict[str, xr.DataArray]) -> xr.Dataset:
@@ -1,16 +1,11 @@
1
- import logging
2
- import re
3
- import warnings
4
- from dataclasses import dataclass, field
5
- from datetime import datetime, timedelta
6
- from pathlib import Path
1
+ from dataclasses import dataclass
7
2
 
8
3
  import numpy as np
9
4
  import xarray as xr
10
5
  from matplotlib.axes import Axes
11
6
 
12
- from roms_tools import Grid
13
7
  from roms_tools.analysis.cdr_analysis import compute_cdr_metrics
8
+ from roms_tools.datasets.roms_dataset import ROMSDataset
14
9
  from roms_tools.plot import plot, plot_uptake_efficiency
15
10
  from roms_tools.regrid import LateralRegridFromROMS, VerticalRegridFromROMS
16
11
  from roms_tools.utils import (
@@ -18,23 +13,19 @@ from roms_tools.utils import (
18
13
  infer_nominal_horizontal_resolution,
19
14
  interpolate_from_rho_to_u,
20
15
  interpolate_from_rho_to_v,
21
- load_data,
22
- )
23
- from roms_tools.vertical_coordinate import (
24
- compute_depth_coordinates,
25
16
  )
26
17
 
27
18
 
28
19
  @dataclass(kw_only=True)
29
- class ROMSOutput:
20
+ class ROMSOutput(ROMSDataset):
30
21
  """Represents ROMS model output.
31
22
 
32
23
  Parameters
33
24
  ----------
25
+ path: str | Path | list[str | Path]
26
+ Filename, or list of filenames with model output.
34
27
  grid : Grid
35
28
  Object representing the grid information.
36
- path : Union[str, Path, List[Union[str, Path]]]
37
- Filename, or list of filenames with model output.
38
29
  model_reference_date : datetime, optional
39
30
  Reference date of ROMS simulation.
40
31
  If not specified, this is inferred from metadata of the model output
@@ -46,32 +37,6 @@ class ROMSOutput:
46
37
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
47
38
  """
48
39
 
49
- grid: Grid
50
- """Object representing the grid information."""
51
- path: str | Path
52
- """Filename, or list of filenames with model output."""
53
- use_dask: bool = False
54
- """Whether to use dask for processing."""
55
- model_reference_date: datetime | None = None
56
- """Reference date of ROMS simulation."""
57
- adjust_depth_for_sea_surface_height: bool | None = False
58
- """Whether to account for sea surface height variations when computing depth
59
- coordinates."""
60
-
61
- ds: xr.Dataset = field(init=False, repr=False)
62
- """An xarray Dataset containing the ROMS output."""
63
-
64
- def __post_init__(self):
65
- ds = self._load_model_output()
66
- self._infer_model_reference_date_from_metadata(ds)
67
- self._check_vertical_coordinate(ds)
68
- ds = self._add_absolute_time(ds)
69
- ds = self._add_lat_lon_coords(ds)
70
- self.ds = ds
71
-
72
- # Dataset for depth coordinates
73
- self.ds_depth_coords = xr.Dataset()
74
-
75
40
  def cdr_metrics(self) -> None:
76
41
  """
77
42
  Compute and plot Carbon Dioxide Removal (CDR) metrics.
@@ -379,272 +344,8 @@ class ROMSOutput:
379
344
  if regridded_datasets:
380
345
  ds = xr.merge(regridded_datasets)
381
346
 
382
- with warnings.catch_warnings():
383
- warnings.filterwarnings("ignore", category=UserWarning)
384
- ds = ds.rename({"abs_time": "time"}).set_index(time="time")
385
347
  ds["time"].attrs = {"long_name": "Time"}
386
348
  ds["lon"].attrs = {"long_name": "Longitude", "units": "Degrees East"}
387
349
  ds["lat"].attrs = {"long_name": "Latitude", "units": "Degrees North"}
388
350
 
389
351
  return ds
390
-
391
- def _get_depth_coordinates(self, depth_type="layer", locations=["rho"]):
392
- """Ensure depth coordinates are stored for a given location and depth type.
393
-
394
- Calculates vertical depth coordinates (layer or interface) for specified locations (e.g., rho, u, v points)
395
- and updates them in the dataset (`self.ds`).
396
-
397
- Parameters
398
- ----------
399
- depth_type : str
400
- The type of depth coordinate to compute. Valid options:
401
- - "layer": Compute layer depth coordinates.
402
- - "interface": Compute interface depth coordinates.
403
- locations : list[str], optional
404
- Locations for which to compute depth coordinates. Default is ["rho", "u", "v"].
405
- Valid options include:
406
- - "rho": Depth coordinates at rho points.
407
- - "u": Depth coordinates at u points.
408
- - "v": Depth coordinates at v points.
409
-
410
- Updates
411
- -------
412
- self.ds_depth_coords : xarray.Dataset
413
-
414
- Raises
415
- ------
416
- ValueError
417
- If `adjust_depth_for_sea_surface_height` is enabled but `zeta` is missing from `self.ds`.
418
-
419
- Notes
420
- -----
421
- - This method relies on the `compute_depth_coordinates` function to perform calculations.
422
- - If `adjust_depth_for_sea_surface_height` is `True`, the method accounts for variations
423
- in sea surface height (`zeta`).
424
- """
425
- if self.adjust_depth_for_sea_surface_height:
426
- if "zeta" not in self.ds:
427
- raise ValueError(
428
- "`zeta` is required in provided ROMS output when `adjust_depth_for_sea_surface_height` is enabled."
429
- )
430
- zeta = self.ds.zeta
431
- else:
432
- zeta = 0
433
-
434
- for location in locations:
435
- var_name = f"{depth_type}_depth_{location}"
436
- if var_name not in self.ds_depth_coords:
437
- self.ds_depth_coords[var_name] = compute_depth_coordinates(
438
- self.grid.ds, zeta, depth_type, location
439
- )
440
-
441
- def _load_model_output(self) -> xr.Dataset:
442
- """Load the model output."""
443
- # Load the dataset
444
- ds = load_data(
445
- self.path,
446
- dim_names={"time": "time"},
447
- use_dask=self.use_dask,
448
- decode_times=False,
449
- decode_timedelta=False,
450
- time_chunking=True,
451
- force_combine_nested=True,
452
- )
453
-
454
- return ds
455
-
456
- def _infer_model_reference_date_from_metadata(self, ds: xr.Dataset) -> None:
457
- """Infer and validate the model reference date from `ocean_time` metadata.
458
-
459
- Parameters
460
- ----------
461
- ds : xr.Dataset
462
- Dataset with an `ocean_time` variable and a `long_name` attribute
463
- in the format `Time since YYYY/MM/DD`.
464
-
465
- Raises
466
- ------
467
- ValueError
468
- If `self.model_reference_date` is not set and the reference date cannot
469
- be inferred, or if the inferred date does not match `self.model_reference_date`.
470
-
471
- Warns
472
- -----
473
- UserWarning
474
- If `self.model_reference_date` is set but the reference date cannot be inferred.
475
- """
476
- # Check if 'long_name' exists in the attributes of 'ocean_time'
477
- if "long_name" in ds.ocean_time.attrs:
478
- input_string = ds.ocean_time.attrs["long_name"]
479
- match = re.search(r"(\d{4})/(\d{2})/(\d{2})", input_string)
480
-
481
- if match:
482
- # If a match is found, extract year, month, day and create the inferred date
483
- year, month, day = map(int, match.groups())
484
- inferred_date = datetime(year, month, day)
485
-
486
- if hasattr(self, "model_reference_date") and self.model_reference_date:
487
- # Check if the inferred date matches the provided model reference date
488
- if self.model_reference_date != inferred_date:
489
- raise ValueError(
490
- f"Mismatch between `self.model_reference_date` ({self.model_reference_date}) "
491
- f"and inferred reference date ({inferred_date})."
492
- )
493
- else:
494
- # Set the model reference date if not already set
495
- self.model_reference_date = inferred_date
496
- else:
497
- # Handle case where no match is found
498
- if hasattr(self, "model_reference_date") and self.model_reference_date:
499
- logging.warning(
500
- "Could not infer the model reference date from the metadata. "
501
- "`self.model_reference_date` will be used.",
502
- )
503
- else:
504
- raise ValueError(
505
- "Model reference date could not be inferred from the metadata, "
506
- "and `self.model_reference_date` is not set."
507
- )
508
- else:
509
- # Handle case where 'long_name' attribute doesn't exist
510
- if hasattr(self, "model_reference_date") and self.model_reference_date:
511
- logging.warning(
512
- "`long_name` attribute not found in ocean_time. "
513
- "`self.model_reference_date` will be used instead.",
514
- )
515
- else:
516
- raise ValueError(
517
- "Model reference date could not be inferred from the metadata, "
518
- "and `self.model_reference_date` is not set."
519
- )
520
-
521
- def _check_vertical_coordinate(self, ds: xr.Dataset) -> None:
522
- """Check that the vertical coordinate parameters in the dataset are consistent
523
- with the model grid.
524
-
525
- This method compares the vertical coordinate parameters (`theta_s`, `theta_b`, `hc`, `Cs_r`, `Cs_w`) in
526
- the provided dataset (`ds`) with those in the model grid (`self.grid`). The first three parameters are
527
- checked for exact equality, while the last two are checked for numerical closeness.
528
-
529
- Parameters
530
- ----------
531
- ds : xarray.Dataset
532
- The dataset containing vertical coordinate parameters in its attributes, such as `theta_s`, `theta_b`,
533
- `hc`, `Cs_r`, and `Cs_w`.
534
-
535
- Raises
536
- ------
537
- ValueError
538
- If the vertical coordinate parameters do not match the expected values (based on exact or approximate equality).
539
-
540
- Notes
541
- -----
542
- - Missing attributes trigger a warning instead of an exception.
543
- - `theta_s`, `theta_b`, and `hc` are checked for exact equality using `np.array_equal`.
544
- - `Cs_r` and `Cs_w` are checked for numerical closeness using `np.allclose`.
545
- """
546
- required_exact = ["theta_s", "theta_b", "hc"]
547
- required_close = ["Cs_r", "Cs_w"]
548
-
549
- # Check exact equality
550
- for param in required_exact:
551
- value = ds.attrs.get(param, None)
552
- if value is None:
553
- logging.warning(
554
- f"Dataset is missing attribute '{param}'. Skipping this check."
555
- )
556
- continue
557
- if not np.array_equal(getattr(self.grid, param), value):
558
- raise ValueError(
559
- f"{param} from grid ({getattr(self.grid, param)}) does not match dataset ({value})."
560
- )
561
-
562
- # Check numerical closeness
563
- for param in required_close:
564
- value = ds.attrs.get(param, None)
565
- if value is None:
566
- logging.warning(
567
- f"Dataset is missing attribute '{param}'. Skipping this check."
568
- )
569
- continue
570
- grid_value = getattr(self.grid.ds, param)
571
- if not np.allclose(grid_value, value):
572
- raise ValueError(
573
- f"{param} from grid ({grid_value}) is not close to dataset ({value})."
574
- )
575
-
576
- def _add_absolute_time(self, ds: xr.Dataset) -> xr.Dataset:
577
- """Add absolute time as a coordinate to the dataset.
578
-
579
- Computes "abs_time" based on "ocean_time" and a reference date,
580
- and adds it as a coordinate.
581
-
582
- Parameters
583
- ----------
584
- ds : xarray.Dataset
585
- Dataset containing "ocean_time" in seconds since the model reference date.
586
-
587
- Returns
588
- -------
589
- xarray.Dataset
590
- Dataset with "abs_time" added and "time" removed.
591
- """
592
- if self.model_reference_date is None:
593
- raise ValueError(
594
- "`model_reference_date` must be set before computing absolute time."
595
- )
596
-
597
- ocean_time_seconds = ds["ocean_time"].values
598
-
599
- abs_time = np.array(
600
- [
601
- self.model_reference_date + timedelta(seconds=seconds)
602
- for seconds in ocean_time_seconds
603
- ]
604
- )
605
-
606
- abs_time = xr.DataArray(
607
- abs_time, dims=["time"], coords={"time": ds["ocean_time"]}
608
- )
609
- abs_time.attrs["long_name"] = "absolute time"
610
- ds = ds.assign_coords({"abs_time": abs_time})
611
- ds = ds.drop_vars("time")
612
-
613
- return ds
614
-
615
- def _add_lat_lon_coords(self, ds: xr.Dataset) -> xr.Dataset:
616
- """Add latitude and longitude coordinates to the dataset based on the grid.
617
-
618
- This method assigns latitude and longitude coordinates from the grid to the dataset.
619
- It always adds the "lat_rho" and "lon_rho" coordinates. If the dataset contains the
620
- "xi_u" or "eta_v" dimensions, it also adds the corresponding "lat_u", "lon_u",
621
- "lat_v", and "lon_v" coordinates.
622
-
623
- Parameters
624
- ----------
625
- ds : xarray.Dataset
626
- Input dataset to which latitude and longitude coordinates will be added.
627
-
628
- Returns
629
- -------
630
- xarray.Dataset
631
- Updated dataset with the appropriate latitude and longitude coordinates
632
- assigned to "rho", "u", and "v" points if applicable.
633
- """
634
- coords_to_add = {
635
- "lat_rho": self.grid.ds["lat_rho"],
636
- "lon_rho": self.grid.ds["lon_rho"],
637
- }
638
-
639
- if "xi_u" in ds.dims:
640
- coords_to_add.update(
641
- {"lat_u": self.grid.ds["lat_u"], "lon_u": self.grid.ds["lon_u"]}
642
- )
643
- if "eta_v" in ds.dims:
644
- coords_to_add.update(
645
- {"lat_v": self.grid.ds["lat_v"], "lon_v": self.grid.ds["lon_v"]}
646
- )
647
-
648
- # Add all necessary coordinates in one go
649
- ds = ds.assign_coords(coords_to_add)
650
- return ds
@@ -90,6 +90,7 @@ pup_test_data = pooch.create(
90
90
  "GSHHS_l_L1.prj": "98aaf3d1c0ecadf1a424a4536de261c3daf4e373697cb86c40c43b989daf52eb",
91
91
  "GSHHS_l_L1.shp": "bc76f101f9b8671f90e734b4026da91c20066fc627cc8b5889ba22d90cbf97e9",
92
92
  "GSHHS_l_L1.shx": "72879354892d80d6c39c612f645661ec0edc75f3f9f8f74b19d9387ae0327377",
93
+ "EMODnet_C2_coarse100.nc": "4202a6a5877de726bf13f41de7a1edfea2db83278ce371fe7732eb4b6770ed6d",
93
94
  },
94
95
  )
95
96
 
@@ -16,22 +16,32 @@ import numpy as np
16
16
  import xarray as xr
17
17
 
18
18
  from roms_tools.constants import R_EARTH
19
- from roms_tools.download import (
19
+ from roms_tools.datasets.download import (
20
20
  download_correction_data,
21
21
  download_sal_data,
22
22
  download_topo,
23
23
  )
24
- from roms_tools.setup.fill import LateralFill
24
+ from roms_tools.datasets.utils import (
25
+ check_dataset,
26
+ convert_to_float64,
27
+ extrapolate_deepest_to_bottom,
28
+ select_relevant_fields,
29
+ select_relevant_times,
30
+ validate_start_end_time,
31
+ )
32
+ from roms_tools.fill import LateralFill
25
33
  from roms_tools.setup.utils import (
26
34
  Timed,
27
35
  assign_dates_to_climatology,
28
- check_dataset,
29
36
  get_target_coords,
37
+ )
38
+ from roms_tools.utils import (
39
+ get_dask_chunks,
40
+ get_pkg_error_msg,
41
+ has_gcsfs,
30
42
  interpolate_cyclic_time,
31
- one_dim_fill,
32
- select_relevant_times,
43
+ load_data,
33
44
  )
34
- from roms_tools.utils import get_dask_chunks, get_pkg_error_msg, has_gcsfs, load_data
35
45
 
36
46
  TConcatEndTypes = Literal["lower", "upper", "both"]
37
47
  REPO_ROOT = Path(__file__).resolve().parents[2]
@@ -121,11 +131,11 @@ class LatLonDataset:
121
131
  var_names: dict[str, str]
122
132
  opt_var_names: dict[str, str] = field(default_factory=dict)
123
133
  climatology: bool = False
124
- needs_lateral_fill: bool | None = True
134
+ needs_lateral_fill: bool = True
125
135
  use_dask: bool = False
126
136
  read_zarr: bool = False
127
137
  allow_flex_time: bool = False
128
- apply_post_processing: bool | None = True
138
+ apply_post_processing: bool = True
129
139
 
130
140
  ds_loader_fn: Callable[[], xr.Dataset] | None = None
131
141
  is_global: bool = field(init=False, repr=False)
@@ -140,21 +150,11 @@ class LatLonDataset:
140
150
  4. Ensures latitude, longitude, and depth values are in ascending order.
141
151
  5. Checks if the dataset covers the entire globe and adjusts if necessary.
142
152
  """
143
- # Validate start_time and end_time
144
- if self.start_time is not None and not isinstance(self.start_time, datetime):
145
- raise TypeError(
146
- f"start_time must be a datetime object, but got {type(self.start_time).__name__}."
147
- )
148
- if self.end_time is not None and not isinstance(self.end_time, datetime):
149
- raise TypeError(
150
- f"end_time must be a datetime object, but got {type(self.end_time).__name__}."
151
- )
152
-
153
+ validate_start_end_time(self.start_time, self.end_time)
153
154
  ds = self.load_data()
154
155
  ds = self.clean_up(ds)
155
- self.check_dataset(ds)
156
+ check_dataset(ds, self.dim_names, self.var_names, self.opt_var_names)
156
157
 
157
- # Select relevant fields
158
158
  ds = self.select_relevant_fields(ds)
159
159
 
160
160
  # Select relevant times
@@ -226,44 +226,30 @@ class LatLonDataset:
226
226
  """
227
227
  return ds # Default behavior (no-op, subclasses should override)
228
228
 
229
- def check_dataset(self, ds: xr.Dataset) -> None:
230
- """Check if the dataset contains the specified variables and dimensions.
231
-
232
- Parameters
233
- ----------
234
- ds : xr.Dataset
235
- The xarray Dataset to check.
236
-
237
- Raises
238
- ------
239
- ValueError
240
- If the dataset does not contain the specified variables or dimensions.
229
+ def select_relevant_fields(self, ds: xr.Dataset) -> xr.Dataset:
241
230
  """
242
- check_dataset(ds, self.dim_names, self.var_names)
231
+ Return a subset of the dataset containing only the required and optional
232
+ variables defined for this object.
243
233
 
244
- def select_relevant_fields(self, ds: xr.Dataset) -> xr.Dataset:
245
- """Selects and returns a subset of the dataset containing only the variables
246
- specified in `self.var_names`.
234
+ Variables retained are those listed in ``self.var_names`` and
235
+ ``self.opt_var_names``. Any other data variables are removed, except for
236
+ the special variable ``"mask"``, which is always preserved if present.
247
237
 
248
238
  Parameters
249
239
  ----------
250
240
  ds : xr.Dataset
251
- The input dataset from which variables will be selected.
241
+ The input dataset from which relevant variables will be selected.
252
242
 
253
243
  Returns
254
244
  -------
255
245
  xr.Dataset
256
- A dataset containing only the variables specified in `self.var_names`.
246
+ A new dataset containing only the required variables specified in
247
+ ``self.var_names`` and the optional variables specified in
248
+ ``self.opt_var_names``, along with ``"mask"`` if present.
257
249
  """
258
- for var in ds.data_vars:
259
- if (
260
- var not in self.var_names.values()
261
- and var not in self.opt_var_names.values()
262
- and var != "mask"
263
- ):
264
- ds = ds.drop_vars(var)
265
-
266
- return ds
250
+ return select_relevant_fields(
251
+ ds, [*self.var_names.values(), *self.opt_var_names.values()]
252
+ )
267
253
 
268
254
  def add_time_info(self, ds: xr.Dataset) -> xr.Dataset:
269
255
  """Dummy method to be overridden by child classes to add time information to the
@@ -336,12 +322,13 @@ class LatLonDataset:
336
322
  raise ValueError("select_relevant_times called but start_time is None.")
337
323
 
338
324
  ds = select_relevant_times(
339
- ds,
340
- time_dim,
341
- self.start_time,
342
- self.end_time,
343
- self.climatology,
344
- self.allow_flex_time,
325
+ ds=ds,
326
+ time_dim=time_dim,
327
+ time_coord=time_dim,
328
+ start_time=self.start_time,
329
+ end_time=self.end_time,
330
+ climatology=self.climatology,
331
+ allow_flex_time=self.allow_flex_time,
345
332
  )
346
333
 
347
334
  return ds
@@ -543,7 +530,7 @@ class LatLonDataset:
543
530
  None
544
531
  This method modifies the dataset in place and does not return anything.
545
532
  """
546
- ds = self.ds.astype({var: "float64" for var in self.ds.data_vars})
533
+ ds = convert_to_float64(self.ds)
547
534
  self.ds = ds
548
535
 
549
536
  return None
@@ -635,7 +622,7 @@ class LatLonDataset:
635
622
  if self.needs_lateral_fill:
636
623
  lateral_fill = LateralFill(
637
624
  self.ds["mask"],
638
- [self.dim_names["latitude"], self.dim_names["longitude"]],
625
+ (self.dim_names["latitude"], self.dim_names["longitude"]),
639
626
  )
640
627
 
641
628
  separate_fill_for_velocities = False
@@ -644,7 +631,7 @@ class LatLonDataset:
644
631
  if "mask_vel" in self.ds.data_vars:
645
632
  lateral_fill_vel = LateralFill(
646
633
  self.ds["mask_vel"],
647
- [self.dim_names["latitude"], self.dim_names["longitude"]],
634
+ (self.dim_names["latitude"], self.dim_names["longitude"]),
648
635
  )
649
636
  separate_fill_for_velocities = True
650
637
 
@@ -672,11 +659,7 @@ class LatLonDataset:
672
659
  propagating the deepest available data downward.
673
660
  """
674
661
  if "depth" in self.dim_names:
675
- for var_name in self.ds.data_vars:
676
- if self.dim_names["depth"] in self.ds[var_name].dims:
677
- self.ds[var_name] = one_dim_fill(
678
- self.ds[var_name], self.dim_names["depth"], direction="forward"
679
- )
662
+ self.ds = extrapolate_deepest_to_bottom(self.ds, self.dim_names["depth"])
680
663
 
681
664
  @classmethod
682
665
  def from_ds(cls, original_dataset: LatLonDataset, ds: xr.Dataset) -> LatLonDataset:
@@ -1106,7 +1089,7 @@ class UnifiedDataset(LatLonDataset):
1106
1089
  attribute is set to `False`.
1107
1090
  """
1108
1091
 
1109
- needs_lateral_fill: bool | None = False
1092
+ needs_lateral_fill: bool = False
1110
1093
 
1111
1094
  # overwrite clean_up method from parent class
1112
1095
  def clean_up(self, ds: xr.Dataset) -> xr.Dataset:
@@ -1147,7 +1130,8 @@ class UnifiedDataset(LatLonDataset):
1147
1130
  if var_name in ds.data_vars and "season" in ds[var_name].dims:
1148
1131
  ds[var_name] = interpolate_cyclic_time(
1149
1132
  ds[var_name],
1150
- time_dim_name="season",
1133
+ time_dim="season",
1134
+ time_coord="season",
1151
1135
  day_of_year=ds["month"],
1152
1136
  )
1153
1137
 
@@ -1671,6 +1655,7 @@ class ETOPO5Dataset(LatLonDataset):
1671
1655
  dim_names: dict[str, str] = field(
1672
1656
  default_factory=lambda: {"longitude": "lon", "latitude": "lat"}
1673
1657
  )
1658
+ needs_lateral_fill: bool = False
1674
1659
 
1675
1660
  def clean_up(self, ds: xr.Dataset) -> xr.Dataset:
1676
1661
  """Assign lat and lon as coordinates.
@@ -1706,6 +1691,32 @@ class SRTM15Dataset(LatLonDataset):
1706
1691
  dim_names: dict[str, str] = field(
1707
1692
  default_factory=lambda: {"longitude": "lon", "latitude": "lat"}
1708
1693
  )
1694
+ needs_lateral_fill: bool = False
1695
+
1696
+
1697
+ @dataclass(kw_only=True)
1698
+ class EMODDataset(LatLonDataset):
1699
+ """Represents topography data on the original grid from the EMOD dataset."""
1700
+
1701
+ var_names: dict[str, str] = field(
1702
+ default_factory=lambda: {
1703
+ "topo": "elevation",
1704
+ }
1705
+ )
1706
+ dim_names: dict[str, str] = field(
1707
+ default_factory=lambda: {"longitude": "lon", "latitude": "lat"}
1708
+ )
1709
+ needs_lateral_fill: bool = True
1710
+
1711
+ def post_process(self) -> None:
1712
+ """Assign land mask."""
1713
+ mask = xr.where(
1714
+ self.ds[self.var_names["topo"]].isnull(),
1715
+ 0,
1716
+ 1,
1717
+ )
1718
+
1719
+ self.ds["mask"] = mask
1709
1720
 
1710
1721
 
1711
1722
  @dataclass
@@ -2331,7 +2342,7 @@ def choose_subdomain(
2331
2342
  resolution: float,
2332
2343
  is_global: bool,
2333
2344
  target_coords: Mapping[str, Any],
2334
- buffer_points: int = 20,
2345
+ buffer_points: int = DEFAULT_NR_BUFFER_POINTS,
2335
2346
  use_dask: bool = False,
2336
2347
  ) -> xr.Dataset:
2337
2348
  """
@@ -2433,6 +2444,7 @@ def choose_subdomain(
2433
2444
  if lon_min - margin < 0:
2434
2445
  lon_min += 360
2435
2446
  lon_max += 360
2447
+
2436
2448
  # Select the subdomain in longitude direction
2437
2449
  subdomain = subdomain.sel(
2438
2450
  **{