roms-tools 2.4.0__py3-none-any.whl → 2.6.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 (214) hide show
  1. ci/environment-with-xesmf.yml +16 -0
  2. roms_tools/__init__.py +1 -1
  3. roms_tools/analysis/roms_output.py +339 -234
  4. roms_tools/analysis/utils.py +137 -0
  5. roms_tools/plot.py +353 -214
  6. roms_tools/regrid.py +154 -9
  7. roms_tools/setup/boundary_forcing.py +51 -37
  8. roms_tools/setup/datasets.py +129 -74
  9. roms_tools/setup/grid.py +32 -33
  10. roms_tools/setup/initial_conditions.py +30 -37
  11. roms_tools/setup/nesting.py +238 -64
  12. roms_tools/setup/river_forcing.py +256 -86
  13. roms_tools/setup/surface_forcing.py +40 -28
  14. roms_tools/setup/tides.py +10 -13
  15. roms_tools/setup/topography.py +27 -4
  16. roms_tools/setup/utils.py +28 -12
  17. roms_tools/tests/test_analysis/test_roms_output.py +299 -80
  18. roms_tools/tests/test_regrid.py +85 -2
  19. roms_tools/tests/test_setup/test_boundary_forcing.py +63 -0
  20. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zattrs +3 -1
  21. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/.zmetadata +3 -1
  22. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/0.0.0 +0 -0
  23. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/0.0.0 +0 -0
  25. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/0.0.0 +0 -0
  27. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/0.0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/0.0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/0.0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/0.0.0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/0.0.0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/0.0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/0.0.0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/0.0.0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/0.0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/0.0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/0.0.0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/0.0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
  125. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
  126. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
  127. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/0.0.0 +0 -0
  128. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
  129. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
  130. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
  131. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/0.0.0 +0 -0
  132. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
  133. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
  134. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zattrs +2 -2
  135. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/.zmetadata +8 -7
  136. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/abs_time/.zattrs +1 -0
  137. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust/0.0.0 +0 -0
  138. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/dust_time/.zattrs +1 -1
  139. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron/0.0.0 +0 -0
  140. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/iron_time/.zattrs +1 -1
  141. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy/0.0.0 +0 -0
  142. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nhy_time/.zattrs +1 -1
  143. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox/0.0.0 +0 -0
  144. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/nox_time/.zattrs +1 -1
  145. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air/0.0.0 +0 -0
  146. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_air_alt/0.0.0 +0 -0
  147. roms_tools/tests/test_setup/test_data/bgc_surface_forcing.zarr/pco2_time/.zattrs +1 -1
  148. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zattrs +2 -2
  149. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/.zmetadata +2 -2
  150. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/dust/0.0.0 +0 -0
  151. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/iron/0.0.0 +0 -0
  152. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nhy/0.0.0 +0 -0
  153. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/nox/0.0.0 +0 -0
  154. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air/0.0.0 +0 -0
  155. roms_tools/tests/test_setup/test_data/bgc_surface_forcing_from_climatology.zarr/pco2_air_alt/0.0.0 +0 -0
  156. roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +1 -1
  157. roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +2 -2
  158. roms_tools/tests/test_setup/test_data/grid.zarr/angle/0.0 +0 -0
  159. roms_tools/tests/test_setup/test_data/grid.zarr/angle_coarse/0.0 +0 -0
  160. roms_tools/tests/test_setup/test_data/grid.zarr/f/0.0 +0 -0
  161. roms_tools/tests/test_setup/test_data/grid.zarr/h/.zattrs +1 -1
  162. roms_tools/tests/test_setup/test_data/grid.zarr/h/0.0 +0 -0
  163. roms_tools/tests/test_setup/test_data/grid.zarr/lat_coarse/0.0 +0 -0
  164. roms_tools/tests/test_setup/test_data/grid.zarr/lat_rho/0.0 +0 -0
  165. roms_tools/tests/test_setup/test_data/grid.zarr/lat_u/0.0 +0 -0
  166. roms_tools/tests/test_setup/test_data/grid.zarr/lat_v/0.0 +0 -0
  167. roms_tools/tests/test_setup/test_data/grid.zarr/pm/0.0 +0 -0
  168. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +1 -1
  169. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +1 -1
  170. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
  171. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
  172. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
  173. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  174. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
  175. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
  176. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
  177. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
  178. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
  179. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
  180. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
  181. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
  182. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
  183. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
  184. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +1 -1
  185. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +1 -1
  186. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/0.0.0.0 +0 -0
  187. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  188. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  189. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  190. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  191. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  192. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zeta/0.0.0 +0 -0
  193. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +27 -1
  194. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zarray +20 -0
  195. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zattrs +6 -0
  196. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/0 +0 -0
  197. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +1 -1
  198. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +27 -1
  199. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zarray +20 -0
  200. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zattrs +6 -0
  201. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/0 +0 -0
  202. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +1 -1
  203. roms_tools/tests/test_setup/test_initial_conditions.py +16 -0
  204. roms_tools/tests/test_setup/test_nesting.py +141 -104
  205. roms_tools/tests/test_setup/test_river_forcing.py +580 -266
  206. roms_tools/tests/test_setup/test_surface_forcing.py +47 -0
  207. roms_tools/tests/test_setup/test_validation.py +34 -2
  208. roms_tools/utils.py +11 -7
  209. roms_tools/vertical_coordinate.py +1 -0
  210. {roms_tools-2.4.0.dist-info → roms_tools-2.6.0.dist-info}/METADATA +22 -11
  211. {roms_tools-2.4.0.dist-info → roms_tools-2.6.0.dist-info}/RECORD +214 -206
  212. {roms_tools-2.4.0.dist-info → roms_tools-2.6.0.dist-info}/WHEEL +1 -1
  213. {roms_tools-2.4.0.dist-info → roms_tools-2.6.0.dist-info/licenses}/LICENSE +0 -0
  214. {roms_tools-2.4.0.dist-info → roms_tools-2.6.0.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,7 @@ from roms_tools.setup.fill import LateralFill
25
25
  # lat-lon datasets
26
26
 
27
27
 
28
- @dataclass(frozen=True, kw_only=True)
28
+ @dataclass(kw_only=True)
29
29
  class Dataset:
30
30
  """Represents forcing data on original grid.
31
31
 
@@ -35,10 +35,11 @@ class Dataset:
35
35
  The path to the data file(s). Can be a single string (with or without wildcards), a single Path object,
36
36
  or a list of strings or Path objects containing multiple files.
37
37
  start_time : Optional[datetime], optional
38
- The start time for selecting relevant data. If not provided, the data is not filtered by start time.
38
+ Start time for selecting relevant data. If not provided, no time-based filtering is applied.
39
39
  end_time : Optional[datetime], optional
40
- The end time for selecting relevant data. If not provided, only data at the start_time is selected if start_time is provided,
41
- or no filtering is applied if start_time is not provided.
40
+ End time for selecting relevant data. If not provided, the dataset selects the time entry
41
+ closest to `start_time` within the range `[start_time, start_time + 24 hours]`.
42
+ If `start_time` is also not provided, no time-based filtering is applied.
42
43
  dim_names: Dict[str, str], optional
43
44
  Dictionary specifying the names of dimensions in the dataset.
44
45
  var_names: Dict[str, str]
@@ -131,8 +132,8 @@ class Dataset:
131
132
  self.infer_horizontal_resolution(ds)
132
133
 
133
134
  # Check whether the data covers the entire globe
134
- object.__setattr__(self, "is_global", self.check_if_global(ds))
135
- object.__setattr__(self, "ds", ds)
135
+ self.is_global = self.check_if_global(ds)
136
+ self.ds = ds
136
137
 
137
138
  if self.apply_post_processing:
138
139
  self.post_process()
@@ -353,7 +354,7 @@ class Dataset:
353
354
  resolution = np.mean([lat_resolution, lon_resolution])
354
355
 
355
356
  # Set the computed resolution as an attribute
356
- object.__setattr__(self, "resolution", resolution)
357
+ self.resolution = resolution
357
358
 
358
359
  def compute_minimal_grid_spacing(self, ds: xr.Dataset):
359
360
  """Compute the minimal grid spacing in a dataset based on latitude and longitude
@@ -510,6 +511,25 @@ class Dataset:
510
511
  """
511
512
  pass
512
513
 
514
+ def convert_to_float64(self) -> None:
515
+ """Convert all data variables in the dataset to float64.
516
+
517
+ This method updates the dataset by converting all of its data variables to the
518
+ `float64` data type, ensuring consistency for numerical operations that require
519
+ high precision. The dataset is modified in place.
520
+
521
+ Parameters
522
+ ----------
523
+ None
524
+
525
+ Returns
526
+ -------
527
+ None
528
+ This method modifies the dataset in place and does not return anything.
529
+ """
530
+ ds = self.ds.astype({var: "float64" for var in self.ds.data_vars})
531
+ self.ds = ds
532
+
513
533
  def choose_subdomain(
514
534
  self,
515
535
  target_coords,
@@ -651,7 +671,7 @@ class Dataset:
651
671
  if return_copy:
652
672
  return Dataset.from_ds(self, subdomain)
653
673
  else:
654
- object.__setattr__(self, "ds", subdomain)
674
+ self.ds = subdomain
655
675
 
656
676
  def apply_lateral_fill(self):
657
677
  """Apply lateral fill to variables using the dataset's mask and grid dimensions.
@@ -737,7 +757,7 @@ class Dataset:
737
757
  dataset = cls.__new__(cls)
738
758
 
739
759
  # Directly set the provided dataset as the 'ds' attribute
740
- object.__setattr__(dataset, "ds", ds)
760
+ dataset.ds = ds
741
761
 
742
762
  # Copy all other attributes from the original data instance
743
763
  for attr in vars(original_dataset):
@@ -747,7 +767,7 @@ class Dataset:
747
767
  return dataset
748
768
 
749
769
 
750
- @dataclass(frozen=True, kw_only=True)
770
+ @dataclass(kw_only=True)
751
771
  class TPXODataset(Dataset):
752
772
  """Represents tidal data on the original grid from the TPXO dataset.
753
773
 
@@ -838,15 +858,11 @@ class TPXODataset(Dataset):
838
858
  {"nx": "longitude", "ny": "latitude", self.dim_names["ntides"]: "ntides"}
839
859
  )
840
860
 
841
- object.__setattr__(
842
- self,
843
- "dim_names",
844
- {
845
- "latitude": "latitude",
846
- "longitude": "longitude",
847
- "ntides": "ntides",
848
- },
849
- )
861
+ self.dim_names = {
862
+ "latitude": "latitude",
863
+ "longitude": "longitude",
864
+ "ntides": "ntides",
865
+ }
850
866
 
851
867
  return ds
852
868
 
@@ -889,15 +905,15 @@ class TPXODataset(Dataset):
889
905
  ds["mask"] = mask
890
906
  ds = ds.drop_vars(["depth"])
891
907
 
892
- object.__setattr__(self, "ds", ds)
908
+ self.ds = ds
893
909
 
894
910
  # Remove "depth" from var_names
895
911
  updated_var_names = {**self.var_names} # Create a copy of the dictionary
896
912
  updated_var_names.pop("depth", None) # Remove "depth" if it exists
897
- object.__setattr__(self, "var_names", updated_var_names)
913
+ self.var_names = updated_var_names
898
914
 
899
915
 
900
- @dataclass(frozen=True, kw_only=True)
916
+ @dataclass(kw_only=True)
901
917
  class GLORYSDataset(Dataset):
902
918
  """Represents GLORYS data on original grid.
903
919
 
@@ -976,7 +992,7 @@ class GLORYSDataset(Dataset):
976
992
  self.ds["mask_vel"] = mask_vel
977
993
 
978
994
 
979
- @dataclass(frozen=True, kw_only=True)
995
+ @dataclass(kw_only=True)
980
996
  class CESMDataset(Dataset):
981
997
  """Represents CESM data on original grid.
982
998
 
@@ -1057,12 +1073,12 @@ class CESMDataset(Dataset):
1057
1073
  # Update dimension names
1058
1074
  updated_dim_names = self.dim_names.copy()
1059
1075
  updated_dim_names["time"] = "time"
1060
- object.__setattr__(self, "dim_names", updated_dim_names)
1076
+ self.dim_names = updated_dim_names
1061
1077
 
1062
1078
  return ds
1063
1079
 
1064
1080
 
1065
- @dataclass(frozen=True, kw_only=True)
1081
+ @dataclass(kw_only=True)
1066
1082
  class CESMBGCDataset(CESMDataset):
1067
1083
  """Represents CESM BGC data on original grid.
1068
1084
 
@@ -1164,12 +1180,12 @@ class CESMBGCDataset(CESMDataset):
1164
1180
  if "z_t_150m" in ds.variables:
1165
1181
  ds = ds.drop_vars("z_t_150m")
1166
1182
  # update dataset
1167
- object.__setattr__(self, "ds", ds)
1183
+ self.ds = ds
1168
1184
 
1169
1185
  # Update dim_names with "depth": "depth" key-value pair
1170
1186
  updated_dim_names = self.dim_names.copy()
1171
1187
  updated_dim_names["depth"] = "depth"
1172
- object.__setattr__(self, "dim_names", updated_dim_names)
1188
+ self.dim_names = updated_dim_names
1173
1189
 
1174
1190
  mask = xr.where(
1175
1191
  self.ds[self.var_names["PO4"]]
@@ -1182,7 +1198,7 @@ class CESMBGCDataset(CESMDataset):
1182
1198
  self.ds["mask"] = mask
1183
1199
 
1184
1200
 
1185
- @dataclass(frozen=True, kw_only=True)
1201
+ @dataclass(kw_only=True)
1186
1202
  class CESMBGCSurfaceForcingDataset(CESMDataset):
1187
1203
  """Represents CESM BGC surface forcing data on original grid.
1188
1204
 
@@ -1238,7 +1254,7 @@ class CESMBGCSurfaceForcingDataset(CESMDataset):
1238
1254
 
1239
1255
  if "z_t" in self.ds.variables:
1240
1256
  ds = self.ds.drop_vars("z_t")
1241
- object.__setattr__(self, "ds", ds)
1257
+ self.ds = ds
1242
1258
 
1243
1259
  mask = xr.where(
1244
1260
  self.ds[self.var_names["pco2_air"]]
@@ -1251,7 +1267,7 @@ class CESMBGCSurfaceForcingDataset(CESMDataset):
1251
1267
  self.ds["mask"] = mask
1252
1268
 
1253
1269
 
1254
- @dataclass(frozen=True, kw_only=True)
1270
+ @dataclass(kw_only=True)
1255
1271
  class ERA5Dataset(Dataset):
1256
1272
  """Represents ERA5 data on original grid.
1257
1273
 
@@ -1351,27 +1367,27 @@ class ERA5Dataset(Dataset):
1351
1367
  ds["qair"].attrs["long_name"] = "Absolute humidity at 2m"
1352
1368
  ds["qair"].attrs["units"] = "kg/kg"
1353
1369
  ds = ds.drop_vars([self.var_names["d2m"]])
1354
- object.__setattr__(self, "ds", ds)
1370
+ self.ds = ds
1355
1371
 
1356
1372
  # Update var_names dictionary
1357
1373
  var_names = {**self.var_names, "qair": "qair"}
1358
1374
  var_names.pop("d2m")
1359
- object.__setattr__(self, "var_names", var_names)
1375
+ self.var_names = var_names
1360
1376
 
1361
1377
  if "mask" in self.var_names.keys():
1362
1378
  ds = self.ds
1363
1379
  mask = xr.where(self.ds[self.var_names["mask"]].isel(time=0).isnull(), 0, 1)
1364
1380
  ds["mask"] = mask
1365
1381
  ds = ds.drop_vars([self.var_names["mask"]])
1366
- object.__setattr__(self, "ds", ds)
1382
+ self.ds = ds
1367
1383
 
1368
1384
  # Remove mask from var_names dictionary
1369
1385
  var_names = self.var_names
1370
1386
  var_names.pop("mask")
1371
- object.__setattr__(self, "var_names", var_names)
1387
+ self.var_names = var_names
1372
1388
 
1373
1389
 
1374
- @dataclass(frozen=True, kw_only=True)
1390
+ @dataclass(kw_only=True)
1375
1391
  class ERA5Correction(Dataset):
1376
1392
  """Global dataset to correct ERA5 radiation. The dataset contains multiplicative
1377
1393
  correction factors for the ERA5 shortwave radiation, obtained by comparing the
@@ -1473,10 +1489,10 @@ class ERA5Correction(Dataset):
1473
1489
  raise ValueError(
1474
1490
  "The correction dataset does not contain all specified longitude values."
1475
1491
  )
1476
- object.__setattr__(self, "ds", subdomain)
1492
+ self.ds = subdomain
1477
1493
 
1478
1494
 
1479
- @dataclass(frozen=True, kw_only=True)
1495
+ @dataclass(kw_only=True)
1480
1496
  class ETOPO5Dataset(Dataset):
1481
1497
  """Represents topography data on the original grid from the ETOPO5 dataset.
1482
1498
 
@@ -1533,7 +1549,7 @@ class ETOPO5Dataset(Dataset):
1533
1549
  return ds
1534
1550
 
1535
1551
 
1536
- @dataclass(frozen=True, kw_only=True)
1552
+ @dataclass(kw_only=True)
1537
1553
  class SRTM15Dataset(Dataset):
1538
1554
  """Represents topography data on the original grid from the SRTM15 dataset.
1539
1555
 
@@ -1569,7 +1585,7 @@ class SRTM15Dataset(Dataset):
1569
1585
 
1570
1586
 
1571
1587
  # river datasets
1572
- @dataclass(frozen=True, kw_only=True)
1588
+ @dataclass(kw_only=True)
1573
1589
  class RiverDataset:
1574
1590
  """Represents river data.
1575
1591
 
@@ -1627,7 +1643,7 @@ class RiverDataset:
1627
1643
 
1628
1644
  # Select relevant times
1629
1645
  ds = self.add_time_info(ds)
1630
- object.__setattr__(self, "ds", ds)
1646
+ self.ds = ds
1631
1647
 
1632
1648
  def load_data(self) -> xr.Dataset:
1633
1649
  """Load dataset from the specified file.
@@ -1765,13 +1781,13 @@ class RiverDataset:
1765
1781
 
1766
1782
  ds = assign_dates_to_climatology(self.ds, "month")
1767
1783
  ds = ds.swap_dims({"month": "time"})
1768
- object.__setattr__(self, "ds", ds)
1784
+ self.ds = ds
1769
1785
 
1770
1786
  updated_dim_names = {**self.dim_names}
1771
1787
  updated_dim_names["time"] = "time"
1772
- object.__setattr__(self, "dim_names", updated_dim_names)
1788
+ self.dim_names = updated_dim_names
1773
1789
 
1774
- object.__setattr__(self, "climatology", True)
1790
+ self.climatology = True
1775
1791
 
1776
1792
  def sort_by_river_volume(self, ds: xr.Dataset) -> xr.Dataset:
1777
1793
  """Sorts the dataset by river volume in descending order (largest rivers first),
@@ -1843,15 +1859,10 @@ class RiverDataset:
1843
1859
 
1844
1860
  Returns
1845
1861
  -------
1846
- indices : dict
1862
+ indices : dict[str, list[tuple]]
1847
1863
  A dictionary containing the indices of the rivers that are within the threshold distance from
1848
- the target coordinates. The dictionary keys are:
1849
- - "station" : numpy.ndarray
1850
- The indices of the rivers that satisfy the distance threshold.
1851
- - "eta_rho" : numpy.ndarray
1852
- The indices of the `eta_rho` dimension corresponding to the selected stations.
1853
- - "xi_rho" : numpy.ndarray
1854
- The indices of the `xi_rho` dimension corresponding to the selected stations.
1864
+ the target coordinates. The dictionary structure consists of river names as keys, and each value is a list of tuples. Each tuple represents
1865
+ a pair of indices corresponding to the `eta_rho` and `xi_rho` grid coordinates of the river.
1855
1866
  """
1856
1867
 
1857
1868
  # Retrieve longitude and latitude of river mouths
@@ -1878,33 +1889,78 @@ class RiverDataset:
1878
1889
 
1879
1890
  # Find the indices of the closest grid cell to the river mouth
1880
1891
  indices = np.where(dist == dist_min)
1892
+ stations = indices[0]
1893
+ eta_rho_values = indices[1]
1894
+ xi_rho_values = indices[2]
1881
1895
  names = (
1882
- self.ds[self.var_names["name"]]
1883
- .isel({self.dim_names["station"]: indices[0]})
1896
+ ds[self.var_names["name"]]
1897
+ .isel({self.dim_names["station"]: stations})
1884
1898
  .values
1885
1899
  )
1886
- # Return the indices in a dictionary format
1887
- indices = {
1888
- "station": indices[0],
1889
- "eta_rho": indices[1],
1890
- "xi_rho": indices[2],
1891
- "name": names,
1892
- }
1900
+ river_indices = {}
1901
+ for i in range(len(stations)):
1902
+ river_name = names[i]
1903
+ river_indices[river_name] = [
1904
+ (int(eta_rho_values[i]), int(xi_rho_values[i]))
1905
+ ] # list of tuples
1893
1906
  else:
1894
1907
  ds = xr.Dataset()
1895
- indices = {
1896
- "station": [],
1897
- "eta_rho": [],
1898
- "xi_rho": [],
1899
- "name": [],
1900
- }
1908
+ river_indices = {}
1909
+
1910
+ self.ds = ds
1911
+
1912
+ return river_indices
1913
+
1914
+ def extract_named_rivers(self, indices):
1915
+ """Extracts a subset of the dataset based on the provided river names in the
1916
+ indices dictionary.
1917
+
1918
+ This method filters the dataset to include only the rivers specified in the `indices` dictionary.
1919
+ The resulting subset is stored in the `ds` attribute of the class.
1901
1920
 
1902
- object.__setattr__(self, "ds", ds)
1921
+ Parameters
1922
+ ----------
1923
+ indices : dict
1924
+ A dictionary where the keys are river names (strings) and the values are dictionaries
1925
+ containing river-related data (e.g., river indices, coordinates).
1926
+
1927
+ Returns
1928
+ -------
1929
+ None
1930
+ The method modifies the `self.ds` attribute in place, setting it to the filtered dataset
1931
+ containing only the data related to the specified rivers.
1932
+
1933
+ Raises
1934
+ ------
1935
+ ValueError
1936
+ - If `indices` is not a dictionary.
1937
+ - If any of the requested river names are not found in the dataset.
1938
+ """
1939
+
1940
+ if not isinstance(indices, dict):
1941
+ raise ValueError("`indices` must be a dictionary.")
1903
1942
 
1904
- return indices
1943
+ river_names = list(indices.keys())
1944
+
1945
+ # Ensure the dataset is filtered based on the provided river names
1946
+ ds_filtered = self.ds.where(
1947
+ self.ds[self.var_names["name"]].isin(river_names), drop=True
1948
+ )
1949
+
1950
+ # Check that all requested rivers exist in the dataset
1951
+ filtered_river_names = set(ds_filtered[self.var_names["name"]].values)
1952
+ missing_rivers = set(river_names) - filtered_river_names
1953
+
1954
+ if missing_rivers:
1955
+ raise ValueError(
1956
+ f"The following rivers were not found in the dataset: {missing_rivers}"
1957
+ )
1958
+
1959
+ # Set the filtered dataset as the new `ds`
1960
+ self.ds = ds_filtered
1905
1961
 
1906
1962
 
1907
- @dataclass(frozen=True, kw_only=True)
1963
+ @dataclass(kw_only=True)
1908
1964
  class DaiRiverDataset(RiverDataset):
1909
1965
  """Represents river data from the Dai river dataset.
1910
1966
 
@@ -2050,7 +2106,7 @@ def _check_dataset(
2050
2106
 
2051
2107
 
2052
2108
  def _select_relevant_times(
2053
- ds, time_dim, start_time=None, end_time=None, climatology=False
2109
+ ds, time_dim, start_time, end_time=None, climatology=False
2054
2110
  ) -> xr.Dataset:
2055
2111
  """Select a subset of the dataset based on the specified time range.
2056
2112
 
@@ -2067,11 +2123,10 @@ def _select_relevant_times(
2067
2123
  The input dataset to be filtered. Must contain a time dimension.
2068
2124
  time_dim: str
2069
2125
  Name of time dimension.
2070
- start_time : Optional[datetime], optional
2071
- The start time for selecting relevant data. If not provided, the data is not filtered by start time.
2126
+ start_time : datetime
2127
+ The start time for selecting relevant data.
2072
2128
  end_time : Optional[datetime], optional
2073
- The end time for selecting relevant data. If not provided, only data at the start_time is selected if start_time is provided,
2074
- or no filtering is applied if start_time is not provided.
2129
+ The end time for selecting relevant data. If not provided, only data at the start_time is selected if start_time is provided.
2075
2130
  climatology : bool
2076
2131
  Indicates whether the dataset is climatological. Defaults to False.
2077
2132
 
roms_tools/setup/grid.py CHANGED
@@ -18,12 +18,13 @@ from roms_tools.setup.utils import (
18
18
  interpolate_from_rho_to_v,
19
19
  get_target_coords,
20
20
  gc_dist,
21
+ _pop_grid_data,
21
22
  )
22
23
  from roms_tools.setup.utils import extract_single_value
23
24
  from pathlib import Path
24
25
 
25
26
 
26
- @dataclass(frozen=True, kw_only=True)
27
+ @dataclass(kw_only=True)
27
28
  class Grid:
28
29
  """A single ROMS grid, used for creating, plotting, and then saving a new ROMS
29
30
  domain grid.
@@ -112,7 +113,7 @@ class Grid:
112
113
  # Coarsen the dataset if needed
113
114
  self._coarsen()
114
115
 
115
- # Topography and mask
116
+ # Topography
116
117
  self.update_topography(
117
118
  topography_source=self.topography_source,
118
119
  hmin=self.hmin,
@@ -130,7 +131,7 @@ class Grid:
130
131
 
131
132
  def _input_checks(self):
132
133
  if self.topography_source is None:
133
- object.__setattr__(self, "topography_source", {"name": "ETOPO5"})
134
+ self.topography_source = {"name": "ETOPO5"}
134
135
 
135
136
  if "name" not in self.topography_source:
136
137
  raise ValueError(
@@ -156,7 +157,7 @@ class Grid:
156
157
  "========================================================================================================"
157
158
  )
158
159
 
159
- object.__setattr__(self, "ds", ds)
160
+ self.ds = ds
160
161
 
161
162
  def update_topography(
162
163
  self, topography_source=None, hmin=None, verbose=False
@@ -205,7 +206,7 @@ class Grid:
205
206
  f"=== Generating the topography using {topography_source['name']} data and hmin = {hmin} meters ==="
206
207
  )
207
208
 
208
- # Add topography and mask to the dataset
209
+ # Add topography to the dataset
209
210
  ds = _add_topography(
210
211
  ds=self.ds,
211
212
  target_coords=target_coords,
@@ -222,9 +223,9 @@ class Grid:
222
223
  )
223
224
 
224
225
  # Update the grid's dataset and related attributes
225
- object.__setattr__(self, "ds", ds)
226
- object.__setattr__(self, "topography_source", topography_source)
227
- object.__setattr__(self, "hmin", hmin)
226
+ self.ds = ds
227
+ self.topography_source = topography_source
228
+ self.hmin = hmin
228
229
 
229
230
  def update_vertical_coordinate(
230
231
  self, N=None, theta_s=None, theta_b=None, hc=None, verbose=False
@@ -320,11 +321,11 @@ class Grid:
320
321
  "========================================================================================================"
321
322
  )
322
323
 
323
- object.__setattr__(self, "ds", ds)
324
- object.__setattr__(self, "theta_s", theta_s)
325
- object.__setattr__(self, "theta_b", theta_b)
326
- object.__setattr__(self, "hc", hc)
327
- object.__setattr__(self, "N", N)
324
+ self.ds = ds
325
+ self.theta_s = theta_s
326
+ self.theta_b = theta_b
327
+ self.hc = hc
328
+ self.N = N
328
329
 
329
330
  def _straddle(self) -> None:
330
331
  """Check if the Greenwich meridian goes through the domain.
@@ -341,9 +342,9 @@ class Grid:
341
342
  np.abs(self.ds.lon_rho.diff("xi_rho")).max() > 300
342
343
  or np.abs(self.ds.lon_rho.diff("eta_rho")).max() > 300
343
344
  ):
344
- object.__setattr__(self, "straddle", True)
345
+ self.straddle = True
345
346
  else:
346
- object.__setattr__(self, "straddle", False)
347
+ self.straddle = False
347
348
 
348
349
  def _coarsen(self):
349
350
  """Update the grid by adding grid variables that are coarsened versions of the
@@ -390,7 +391,7 @@ class Grid:
390
391
  ds[coarse_var].attrs["long_name"] = f"{long_name} on coarsened grid"
391
392
  ds[coarse_var].attrs["units"] = ds[fine_var].attrs["units"]
392
393
 
393
- object.__setattr__(self, "ds", ds)
394
+ self.ds = ds
394
395
 
395
396
  def plot(
396
397
  self, bathymetry: bool = True, title: str = None, with_dim_names: bool = False
@@ -460,7 +461,7 @@ class Grid:
460
461
  xi : int, optional
461
462
  The xi-index to plot. Default is None.
462
463
  ax : matplotlib.axes.Axes, optional
463
- The axes to plot on. If None, a new figure is created. Note that this argument does not work for horizontal plots that display the eta- and xi-dimensions at the same time.
464
+ The axes to plot on. If None, a new figure is created. Note that this argument does not work for 2D horizontal plots.
464
465
 
465
466
  Returns
466
467
  -------
@@ -580,14 +581,14 @@ class Grid:
580
581
  grid = cls.__new__(cls)
581
582
 
582
583
  # Set the dataset for the grid instance
583
- object.__setattr__(grid, "ds", ds)
584
+ grid.ds = ds
584
585
 
585
586
  # Check if the Greenwich meridian goes through the domain.
586
587
  grid._straddle()
587
588
 
588
589
  if not all(coord in grid.ds for coord in ["lat_u", "lon_u", "lat_v", "lon_v"]):
589
590
  ds = _add_lat_lon_at_velocity_points(grid.ds, grid.straddle)
590
- object.__setattr__(grid, "ds", ds)
591
+ grid.ds = ds
591
592
 
592
593
  # Coarsen the grid if necessary
593
594
  if not all(
@@ -605,7 +606,7 @@ class Grid:
605
606
  for var in ["lat_rho", "lon_rho", "lat_coarse", "lon_coarse"]:
606
607
  if var not in ds.coords:
607
608
  ds = grid.ds.set_coords(var)
608
- object.__setattr__(grid, "ds", ds)
609
+ grid.ds = ds
609
610
 
610
611
  # Update vertical coordinate if necessary
611
612
  if not all(var in grid.ds for var in ["Cs_r", "Cs_w"]):
@@ -619,14 +620,14 @@ class Grid:
619
620
  N=N, theta_s=theta_s, theta_b=theta_b, hc=hc, verbose=True
620
621
  )
621
622
  else:
622
- object.__setattr__(grid, "theta_s", ds.attrs["theta_s"].item())
623
- object.__setattr__(grid, "theta_b", ds.attrs["theta_b"].item())
624
- object.__setattr__(grid, "hc", ds.attrs["hc"].item())
625
- object.__setattr__(grid, "N", len(ds.s_rho))
623
+ grid.theta_s = ds.attrs["theta_s"].item()
624
+ grid.theta_b = ds.attrs["theta_b"].item()
625
+ grid.hc = ds.attrs["hc"].item()
626
+ grid.N = len(ds.s_rho)
626
627
 
627
628
  # Manually set the remaining attributes by extracting parameters from dataset
628
- object.__setattr__(grid, "nx", ds.sizes["xi_rho"] - 2)
629
- object.__setattr__(grid, "ny", ds.sizes["eta_rho"] - 2)
629
+ grid.nx = ds.sizes["xi_rho"] - 2
630
+ grid.ny = ds.sizes["eta_rho"] - 2
630
631
  if "center_lon" in ds.attrs:
631
632
  center_lon = ds.attrs["center_lon"]
632
633
  elif "tra_lon" in ds:
@@ -636,7 +637,7 @@ class Grid:
636
637
  "Missing grid information: 'center_lon' attribute or 'tra_lon' variable "
637
638
  "must be present in the dataset."
638
639
  )
639
- object.__setattr__(grid, "center_lon", center_lon)
640
+ grid.center_lon = center_lon
640
641
  if "center_lat" in ds.attrs:
641
642
  center_lat = ds.attrs["center_lat"]
642
643
  elif "tra_lat" in ds:
@@ -646,7 +647,7 @@ class Grid:
646
647
  "Missing grid information: 'center_lat' attribute or 'tra_lat' variable "
647
648
  "must be present in the dataset."
648
649
  )
649
- object.__setattr__(grid, "center_lat", center_lat)
650
+ grid.center_lat = center_lat
650
651
  if "rot" in ds.attrs:
651
652
  rot = ds.attrs["rot"]
652
653
  elif "rotate" in ds:
@@ -656,7 +657,7 @@ class Grid:
656
657
  "Missing grid information: 'rot' attribute or 'rotate' variable "
657
658
  "must be present in the dataset."
658
659
  )
659
- object.__setattr__(grid, "rot", rot)
660
+ grid.rot = rot
660
661
 
661
662
  for attr in [
662
663
  "size_x",
@@ -688,9 +689,7 @@ class Grid:
688
689
  filepath = Path(filepath)
689
690
 
690
691
  data = asdict(self)
691
- data.pop("ds", None)
692
- data.pop("straddle", None)
693
- data.pop("verbose", None)
692
+ data = _pop_grid_data(data)
694
693
 
695
694
  # Include the version of roms-tools
696
695
  try:
@@ -849,7 +848,7 @@ class Grid:
849
848
  "========================================================================================================"
850
849
  )
851
850
 
852
- object.__setattr__(self, "ds", ds)
851
+ self.ds = ds
853
852
 
854
853
  def _add_global_metadata(self, ds):
855
854
  """Add global metadata and attributes to the dataset.