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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. roms_tools/__init__.py +3 -0
  2. roms_tools/analysis/cdr_analysis.py +203 -0
  3. roms_tools/analysis/cdr_ensemble.py +198 -0
  4. roms_tools/analysis/roms_output.py +80 -46
  5. roms_tools/data/grids/GLORYS_global_grid.nc +0 -0
  6. roms_tools/download.py +4 -0
  7. roms_tools/plot.py +113 -51
  8. roms_tools/setup/boundary_forcing.py +45 -20
  9. roms_tools/setup/cdr_forcing.py +122 -8
  10. roms_tools/setup/cdr_release.py +161 -8
  11. roms_tools/setup/grid.py +150 -141
  12. roms_tools/setup/initial_conditions.py +113 -48
  13. roms_tools/setup/{datasets.py → lat_lon_datasets.py} +443 -938
  14. roms_tools/setup/mask.py +63 -7
  15. roms_tools/setup/nesting.py +314 -117
  16. roms_tools/setup/river_datasets.py +527 -0
  17. roms_tools/setup/river_forcing.py +46 -20
  18. roms_tools/setup/surface_forcing.py +7 -9
  19. roms_tools/setup/tides.py +2 -3
  20. roms_tools/setup/topography.py +8 -10
  21. roms_tools/setup/utils.py +396 -23
  22. roms_tools/tests/test_analysis/test_cdr_analysis.py +144 -0
  23. roms_tools/tests/test_analysis/test_cdr_ensemble.py +202 -0
  24. roms_tools/tests/test_analysis/test_roms_output.py +61 -3
  25. roms_tools/tests/test_setup/test_boundary_forcing.py +54 -52
  26. roms_tools/tests/test_setup/test_cdr_forcing.py +54 -0
  27. roms_tools/tests/test_setup/test_cdr_release.py +118 -1
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/c/0/0/0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_north/c/0/0/0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/c/0/0/0 +0 -0
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/c/0/0/0 +0 -0
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_north/c/0/0/0 +0 -0
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/c/0/0/0 +0 -0
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/c/0/0/0 +0 -0
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_north/c/0/0/0 +0 -0
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/c/0/0/0 +0 -0
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/c/0/0/0 +0 -0
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_north/c/0/0/0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/c/0/0/0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/c/0/0/0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_north/c/0/0/0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/c/0/0/0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/c/0/0/0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_north/c/0/0/0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/c/0/0/0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/c/0/0/0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_north/c/0/0/0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/c/0/0/0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/c/0/0/0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_north/c/0/0/0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/c/0/0/0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/c/0/0/0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_north/c/0/0/0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/c/0/0/0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/c/0/0/0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_north/c/0/0/0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/c/0/0/0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/c/0/0/0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_north/c/0/0/0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/c/0/0/0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/c/0/0/0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_north/c/0/0/0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/c/0/0/0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/c/0/0/0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/c/0/0/0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/c/0/0/0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/c/0/0/0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_north/c/0/0/0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/c/0/0/0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/c/0/0/0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_north/c/0/0/0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/c/0/0/0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/c/0/0/0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_north/c/0/0/0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/c/0/0/0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/c/0/0/0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_north/c/0/0/0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/c/0/0/0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/c/0/0/0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/c/0/0/0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/c/0/0/0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/c/0/0/0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/c/0/0/0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/c/0/0/0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/c/0/0/0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/c/0/0/0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/c/0/0/0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/c/0/0/0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/c/0/0/0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/c/0/0/0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/c/0/0/0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/c/0/0/0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/c/0/0/0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/c/0/0/0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/c/0/0/0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/c/0/0/0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/c/0/0/0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/c/0/0/0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/c/0/0/0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/c/0/0/0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/c/0/0/0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/c/0/0/0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/c/0/0/0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/c/0/0/0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/c/0/0/0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/c/0/0/0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/c/0/0/0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/c/0/0/0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/c/0/0/0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/c/0/0/0 +0 -0
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/c/0/0/0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/c/0/0/0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/c/0/0/0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/c/0/0/0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/c/0/0/0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/c/0/0/0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/c/0/0/0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/c/0/0/0 +0 -0
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/c/0/0/0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/c/0/0/0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zarr.json +406 -406
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/c/0/0/0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/c/0/0/0 +0 -0
  124. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/c/0/0/0 +0 -0
  125. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/c/0/0/0 +0 -0
  126. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/c/0/0/0 +0 -0
  127. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/c/0/0/0 +0 -0
  128. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/c/0/0/0 +0 -0
  129. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/c/0/0/0 +0 -0
  130. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/c/0/0/0 +0 -0
  131. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/c/0/0/0 +0 -0
  132. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/c/0/0/0 +0 -0
  133. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/c/0/0/0 +0 -0
  134. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/c/0/0/0 +0 -0
  135. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/c/0/0/0 +0 -0
  136. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/c/0/0/0 +0 -0
  137. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/c/0/0 +0 -0
  138. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/c/0/0 +0 -0
  139. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/c/0/0 +0 -0
  140. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/c/0/0 +0 -0
  141. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/c/0/0/0 +0 -0
  142. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/c/0/0/0 +0 -0
  143. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/c/0/0/0 +0 -0
  144. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/c/0/0/0 +0 -0
  145. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/c/0/0 +0 -0
  146. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/c/0/0 +0 -0
  147. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/c/0/0 +0 -0
  148. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/c/0/0 +0 -0
  149. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zarr.json +182 -182
  150. roms_tools/tests/test_setup/test_data/grid.zarr/h/c/0/0 +0 -0
  151. roms_tools/tests/test_setup/test_data/grid.zarr/zarr.json +191 -191
  152. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/c/0/0 +0 -0
  153. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/zarr.json +210 -210
  154. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK/c/0/0/0/0 +0 -0
  155. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ALK_ALT_CO2/c/0/0/0/0 +0 -0
  156. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC/c/0/0/0/0 +0 -0
  157. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DIC_ALT_CO2/c/0/0/0/0 +0 -0
  158. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOC/c/0/0/0/0 +0 -0
  159. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOCr/c/0/0/0/0 +0 -0
  160. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DON/c/0/0/0/0 +0 -0
  161. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DONr/c/0/0/0/0 +0 -0
  162. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOP/c/0/0/0/0 +0 -0
  163. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/DOPr/c/0/0/0/0 +0 -0
  164. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Fe/c/0/0/0/0 +0 -0
  165. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Lig/c/0/0/0/0 +0 -0
  166. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/c/0/0/0/0 +0 -0
  167. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/c/0/0/0/0 +0 -0
  168. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/O2/c/0/0/0/0 +0 -0
  169. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/c/0/0/0/0 +0 -0
  170. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/SiO3/c/0/0/0/0 +0 -0
  171. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatC/c/0/0/0/0 +0 -0
  172. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatChl/c/0/0/0/0 +0 -0
  173. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatFe/c/0/0/0/0 +0 -0
  174. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatP/c/0/0/0/0 +0 -0
  175. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/c/0/0/0/0 +0 -0
  176. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazC/c/0/0/0/0 +0 -0
  177. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazChl/c/0/0/0/0 +0 -0
  178. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazFe/c/0/0/0/0 +0 -0
  179. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diazP/c/0/0/0/0 +0 -0
  180. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/salt/c/0/0/0/0 +0 -0
  181. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/c/0/0/0/0 +0 -0
  182. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/c/0/0/0/0 +0 -0
  183. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spChl/c/0/0/0/0 +0 -0
  184. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/c/0/0/0/0 +0 -0
  185. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spP/c/0/0/0/0 +0 -0
  186. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/c/0/0/0/0 +0 -0
  187. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/c/0/0/0/0 +0 -0
  188. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/c/0/0/0 +0 -0
  189. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/c/0/0/0/0 +0 -0
  190. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/c/0/0/0 +0 -0
  191. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zarr.json +182 -182
  192. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zooC/c/0/0/0/0 +0 -0
  193. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/salt/c/0/0/0/0 +0 -0
  194. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/temp/c/0/0/0/0 +0 -0
  195. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/u/c/0/0/0/0 +0 -0
  196. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/ubar/c/0/0/0 +0 -0
  197. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/v/c/0/0/0/0 +0 -0
  198. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/vbar/c/0/0/0 +0 -0
  199. roms_tools/tests/test_setup/test_data/initial_conditions_with_unified_bgc_from_climatology.zarr/zarr.json +187 -187
  200. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Im/c/0/0/0 +0 -0
  201. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/u_Re/c/0/0/0 +0 -0
  202. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Im/c/0/0/0 +0 -0
  203. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Re/c/0/0/0 +0 -0
  204. roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/zarr.json +66 -66
  205. roms_tools/tests/test_setup/test_grid.py +236 -115
  206. roms_tools/tests/test_setup/test_initial_conditions.py +94 -41
  207. roms_tools/tests/test_setup/{test_datasets.py → test_lat_lon_datasets.py} +409 -100
  208. roms_tools/tests/test_setup/test_nesting.py +119 -31
  209. roms_tools/tests/test_setup/test_river_datasets.py +48 -0
  210. roms_tools/tests/test_setup/test_surface_forcing.py +2 -1
  211. roms_tools/tests/test_setup/test_utils.py +92 -2
  212. roms_tools/tests/test_setup/utils.py +71 -0
  213. roms_tools/tests/test_tiling/test_join.py +241 -0
  214. roms_tools/tests/test_utils.py +139 -17
  215. roms_tools/tiling/join.py +189 -0
  216. roms_tools/utils.py +131 -99
  217. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/METADATA +12 -2
  218. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/RECORD +221 -211
  219. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/WHEEL +0 -0
  220. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/licenses/LICENSE +0 -0
  221. {roms_tools-3.1.2.dist-info → roms_tools-3.3.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from collections.abc import Callable
2
3
  from dataclasses import dataclass, field
3
4
  from pathlib import Path
4
5
  from typing import Any
@@ -9,8 +10,9 @@ from scipy.interpolate import griddata, interp1d
9
10
 
10
11
  from roms_tools import Grid
11
12
  from roms_tools.plot import plot_nesting
12
- from roms_tools.setup.topography import _clip_depth
13
+ from roms_tools.setup.topography import clip_depth
13
14
  from roms_tools.setup.utils import (
15
+ Timed,
14
16
  from_yaml,
15
17
  get_boundary_coords,
16
18
  interpolate_from_rho_to_u,
@@ -48,6 +50,8 @@ class ChildGrid(Grid):
48
50
 
49
51
  - `"prefix"` (str): Prefix for variable names in `ds_nesting`. Defaults to `"child"`.
50
52
  - `"period"` (float): Temporal resolution for boundary outputs in seconds. Defaults to 3600 (hourly).
53
+ verbose: bool, optional
54
+ Indicates whether to print grid generation steps with timing. Defaults to False.
51
55
  """
52
56
 
53
57
  parent_grid: Grid
@@ -67,6 +71,8 @@ class ChildGrid(Grid):
67
71
  default_factory=lambda: {"prefix": "child", "period": 3600.0}
68
72
  )
69
73
  """Dictionary configuring the boundary nesting process."""
74
+ verbose: bool = False
75
+ """Whether to print grid generation steps with timing."""
70
76
 
71
77
  ds: xr.Dataset = field(init=False, repr=False)
72
78
  """An xarray Dataset containing child grid variables aligned with the
@@ -75,87 +81,144 @@ class ChildGrid(Grid):
75
81
  """An xarray Dataset containing boundary mappings, where child grid boundaries are
76
82
  mapped onto parent grid indices."""
77
83
 
78
- def __post_init__(self):
79
- super().__post_init__()
80
- self._map_child_boundaries_onto_parent_grid_indices()
81
- self._modify_child_topography_and_mask()
82
-
83
- def _map_child_boundaries_onto_parent_grid_indices(self):
84
+ def _map_child_boundaries_onto_parent_grid_indices(self, verbose: bool = False):
84
85
  """Maps child grid boundary points onto absolute indices of the parent grid."""
85
- # Prepare parent and child grid datasets by adjusting longitudes for dateline crossing
86
- parent_grid_ds, child_grid_ds = self._prepare_grid_datasets()
87
-
88
- # Map child boundaries onto parent grid indices
89
- ds_nesting = map_child_boundaries_onto_parent_grid_indices(
90
- parent_grid_ds,
91
- child_grid_ds,
92
- self.boundaries,
93
- self.metadata["prefix"],
94
- self.metadata["period"],
95
- )
86
+ with Timed(
87
+ "=== Mapping the child grid boundary points onto the indices of the parent grid ===",
88
+ verbose=verbose,
89
+ ):
90
+ # Prepare parent and child grid datasets by adjusting longitudes for dateline crossing
91
+ parent_grid_ds, child_grid_ds = self._prepare_grid_datasets()
92
+
93
+ # Map child boundaries onto parent grid indices
94
+ ds_nesting = map_child_boundaries_onto_parent_grid_indices(
95
+ parent_grid_ds,
96
+ child_grid_ds,
97
+ self.boundaries,
98
+ self.metadata["prefix"],
99
+ self.metadata["period"],
100
+ )
96
101
 
97
- self.ds_nesting = ds_nesting
102
+ self.ds_nesting = ds_nesting
98
103
 
99
- def _modify_child_topography_and_mask(self):
100
- """Adjust the topography and mask of the child grid to align with the parent grid.
104
+ def _apply_child_modification(
105
+ self,
106
+ modifier: Callable,
107
+ modifier_name: str,
108
+ verbose: bool = False,
109
+ ):
110
+ """Shared logic for modifying child mask/topography."""
111
+ with Timed(f"=== Modifying the child {modifier_name} ===", verbose=verbose):
112
+ # Prepare datasets (fix dateline)
113
+ parent_grid_ds, child_grid_ds = self._prepare_grid_datasets()
114
+
115
+ # Apply modification function
116
+ child_grid_ds = modifier(parent_grid_ds, child_grid_ds)
117
+
118
+ # Restore longitudes to 0-360
119
+ _, child_grid_ds = self._finalize_grid_datasets(
120
+ parent_grid_ds, child_grid_ds
121
+ )
101
122
 
102
- Uses a weighted sum based on boundary distance and clips depth values to a
103
- minimum.
104
- """
105
- # Prepare parent and child grid datasets by adjusting longitudes for dateline crossing
106
- parent_grid_ds, child_grid_ds = self._prepare_grid_datasets()
123
+ self.ds = child_grid_ds
107
124
 
108
- child_grid_ds = modify_child_topography_and_mask(
109
- parent_grid_ds, child_grid_ds, self.boundaries, self.hmin
125
+ def _modify_child_mask(self, verbose: bool = False) -> None:
126
+ """Adjust child grid mask to align with the parent grid."""
127
+ self._apply_child_modification(
128
+ modifier=lambda p, c: modify_child_mask(p, c, self.boundaries),
129
+ modifier_name="mask",
130
+ verbose=verbose,
110
131
  )
111
132
 
112
- # Finalize grid datasets by adjusting longitudes back to [0, 360] range
113
- parent_grid_ds, child_grid_ds = self._finalize_grid_datasets(
114
- parent_grid_ds, child_grid_ds
133
+ def _modify_child_topography(self, hmin: float, verbose: bool = False) -> None:
134
+ """Adjust child grid topography to align with the parent grid."""
135
+ self._apply_child_modification(
136
+ modifier=lambda p, c: modify_child_topography(p, c, self.boundaries, hmin),
137
+ modifier_name="topography",
138
+ verbose=verbose,
115
139
  )
116
140
 
117
- self.ds = child_grid_ds
141
+ def update_mask(
142
+ self, mask_shapefile: str | Path | None = None, verbose: bool = False
143
+ ) -> None:
144
+ """
145
+ Update the child grid mask and ensure consistency with the parent grid.
146
+
147
+ This method performs the following steps:
148
+
149
+ 1. Derives the child mask from the provided ``mask_shapefile`` (or from the
150
+ default Natural Earth 10m coastline if ``None``).
151
+ 2. Updates the mapping of child boundaries to parent-grid indices.
152
+ This mapping depends on the updated mask, since masked (land) points may
153
+ extend outside the parent grid.
154
+ 3. Adjusts the child mask to ensure consistency with the parent mask.
155
+
156
+ Parameters
157
+ ----------
158
+ mask_shapefile : str or Path, optional
159
+ Path to a coastal shapefile used to derive the land mask. If ``None``,
160
+ a default coastline dataset is used.
161
+ verbose : bool, default False
162
+ If True, prints timing and progress information.
163
+
164
+ Returns
165
+ -------
166
+ None
167
+ Updates the internal datasets (``self.ds`` and ``self.ds_nesting``) in place,
168
+ modifying the mask and ensuring consistent parent-child boundary mapping.
169
+ """
170
+ super().update_mask(mask_shapefile=mask_shapefile, verbose=verbose)
171
+ self._map_child_boundaries_onto_parent_grid_indices(verbose=verbose)
172
+ self._modify_child_mask(verbose=verbose)
118
173
 
119
174
  def update_topography(
120
- self, topography_source=None, hmin=None, verbose=False
175
+ self,
176
+ topography_source: dict | None = None,
177
+ hmin: float | None = None,
178
+ verbose: bool = False,
121
179
  ) -> None:
122
- """Update the child grid topography via the following steps.
180
+ """
181
+ Update the child grid topography and ensure consistency with the parent grid.
182
+
183
+ This method performs the following operations:
123
184
 
124
- - Regrids the topography based on the specified source.
125
- - Applies global and local smoothing.
126
- - Adjusts the child grid topography and mask to match the parent grid.
127
- - Ensures the minimum depth constraint (`hmin`) is enforced.
128
- - Updates the internal dataset (`self.ds`) with the processed topography.
185
+ 1. Regrids the topography from the specified source.
186
+ 2. Applies domain-wide and local smoothing.
187
+ 3. Enforces the minimum depth constraint ``hmin``.
188
+ 4. Adjusts the child grid topography to maintain consistency with the
189
+ parent grid.
190
+ 5. Updates the internal dataset (``self.ds``) with the processed bathymetry.
129
191
 
130
192
  Parameters
131
193
  ----------
132
194
  topography_source : dict, optional
133
- A dictionary specifying the source of the topography data. Expected keys:
134
- - `"name"` (str): Name of the topography dataset (e.g., `"SRTM15"`).
135
- - `"path"` (str or Path): File path to the topography dataset.
195
+ Dictionary describing the topography data source. Expected keys:
196
+
197
+ - ``"name"`` (str): Name of the source dataset (e.g., ``"SRTM15"``).
198
+ - ``"path"`` (str or Path): Path to the dataset file.
136
199
 
137
- If not provided, the existing topography source remains unchanged.
200
+ If ``None``, the previously configured topography source is used.
138
201
 
139
202
  hmin : float, optional
140
- The minimum allowable ocean depth (in meters). If not provided, the existing
141
- value remains unchanged.
203
+ Minimum allowable ocean depth (meters). If ``None``, the existing value
204
+ is retained.
142
205
 
143
- verbose : bool, optional
144
- If `True`, prints detailed information about each processing step, including
145
- timing and modifications. Defaults to `False`.
206
+ verbose : bool, default False
207
+ If ``True``, prints progress messages and timing information.
146
208
 
147
209
  Returns
148
210
  -------
149
211
  None
150
- This method updates the internal dataset (`self.ds`) in place, modifying the
151
- topography variable. It does not return a value.
212
+ Updates ``self.ds`` in place by modifying the topography field. Nothing
213
+ is returned.
152
214
  """
215
+ hmin = hmin or self.hmin
153
216
  super().update_topography(
154
- topography_source=topography_source, hmin=hmin, verbose=verbose
217
+ topography_source=topography_source,
218
+ hmin=hmin,
219
+ verbose=verbose,
155
220
  )
156
-
157
- # Modify child topography and mask to match the parent grid
158
- self._modify_child_topography_and_mask()
221
+ self._modify_child_topography(hmin=hmin, verbose=verbose)
159
222
 
160
223
  def plot_nesting(self, with_dim_names=False) -> None:
161
224
  """Plot the parent and child grids in a single figure.
@@ -216,13 +279,19 @@ class ChildGrid(Grid):
216
279
  write_to_yaml(forcing_dict, filepath)
217
280
 
218
281
  @classmethod
219
- def from_yaml(cls, filepath: str | Path) -> "ChildGrid":
282
+ def from_yaml(
283
+ cls, filepath: str | Path, verbose: bool = False, **kwargs: Any
284
+ ) -> "ChildGrid":
220
285
  """Create an instance of the ChildGrid class from a YAML file.
221
286
 
222
287
  Parameters
223
288
  ----------
224
289
  filepath : Union[str, Path]
225
290
  The path to the YAML file from which the parameters will be read.
291
+ verbose : bool, optional
292
+ Indicates whether to print grid generation steps with timing. Defaults to False.
293
+ **kwargs : Any
294
+ Additional keyword arguments passed to Grid.from_yaml.
226
295
 
227
296
  Returns
228
297
  -------
@@ -231,10 +300,12 @@ class ChildGrid(Grid):
231
300
  """
232
301
  filepath = Path(filepath)
233
302
 
234
- parent_grid = Grid.from_yaml(filepath, "ParentGrid")
303
+ parent_grid = Grid.from_yaml(
304
+ filepath, verbose=verbose, section_name="ParentGrid"
305
+ )
235
306
  params = from_yaml(cls, filepath)
236
307
 
237
- return cls(parent_grid=parent_grid, **params)
308
+ return cls(parent_grid=parent_grid, **params, verbose=verbose)
238
309
 
239
310
  def _prepare_grid_datasets(self) -> tuple[xr.Dataset, xr.Dataset]:
240
311
  """Prepare parent and child grid datasets by adjusting longitudes for dateline
@@ -251,14 +322,17 @@ class ChildGrid(Grid):
251
322
  - The modified child grid dataset.
252
323
  """
253
324
  parent_grid_ds = wrap_longitudes(
254
- self.parent_grid.ds, straddle=self.parent_grid.straddle
325
+ self.parent_grid.ds.copy(), straddle=self.parent_grid.straddle
255
326
  )
256
- child_grid_ds = wrap_longitudes(self.ds, straddle=self.parent_grid.straddle)
327
+ child_grid_ds = wrap_longitudes(
328
+ self.ds.copy(), straddle=self.parent_grid.straddle
329
+ )
330
+
257
331
  return parent_grid_ds, child_grid_ds
258
332
 
259
333
  def _finalize_grid_datasets(
260
334
  self, parent_grid_ds: xr.Dataset, child_grid_ds: xr.Dataset
261
- ) -> None:
335
+ ) -> tuple[xr.Dataset, xr.Dataset]:
262
336
  """Finalize the grid datasets by converting longitudes back to the [0, 360]
263
337
  range.
264
338
 
@@ -269,13 +343,28 @@ class ChildGrid(Grid):
269
343
 
270
344
  child_grid_ds : xr.Dataset
271
345
  The child grid dataset after modifications.
346
+
347
+ Returns
348
+ -------
349
+ tuple[xr.Dataset, xr.Dataset]
350
+ The finalized parent and child grid datasets with longitudes wrapped to [0, 360].
351
+
272
352
  """
273
353
  parent_grid_ds = wrap_longitudes(parent_grid_ds, straddle=False)
274
354
  child_grid_ds = wrap_longitudes(child_grid_ds, straddle=False)
355
+
275
356
  return parent_grid_ds, child_grid_ds
276
357
 
277
358
  @classmethod
278
- def from_file(cls, filepath: str | Path, verbose: bool = False) -> "ChildGrid":
359
+ def from_file(
360
+ cls,
361
+ filepath: str | Path,
362
+ theta_s: float | None = None,
363
+ theta_b: float | None = None,
364
+ hc: float | None = None,
365
+ N: int | None = None,
366
+ verbose: bool = False,
367
+ ) -> "ChildGrid":
279
368
  """This method is disabled in this subclass.
280
369
 
281
370
  .. noindex::
@@ -286,12 +375,12 @@ class ChildGrid(Grid):
286
375
 
287
376
 
288
377
  def map_child_boundaries_onto_parent_grid_indices(
289
- parent_grid_ds,
290
- child_grid_ds,
291
- boundaries={"south": True, "east": True, "north": True, "west": True},
292
- prefix="child",
293
- period=3600.0,
294
- update_land_indices=True,
378
+ parent_grid_ds: xr.Dataset,
379
+ child_grid_ds: xr.Dataset,
380
+ boundaries: dict = {"south": True, "east": True, "north": True, "west": True},
381
+ prefix: str = "child",
382
+ period: float = 3600.0,
383
+ update_land_indices: bool = True,
295
384
  ):
296
385
  """Maps child grid boundary points onto absolute indices of the parent grid.
297
386
 
@@ -337,6 +426,7 @@ def map_child_boundaries_onto_parent_grid_indices(
337
426
  bdry_coords_dict = get_boundary_coords()
338
427
 
339
428
  # add angles at u- and v-points
429
+
340
430
  child_grid_ds["angle_u"] = interpolate_from_rho_to_u(child_grid_ds["angle"])
341
431
  child_grid_ds["angle_v"] = interpolate_from_rho_to_v(child_grid_ds["angle"])
342
432
 
@@ -367,10 +457,10 @@ def map_child_boundaries_onto_parent_grid_indices(
367
457
  mask_child = child_grid_ds[names["mask"]].isel(**bdry_coords[direction])
368
458
 
369
459
  i_eta, i_xi = interpolate_indices(
370
- parent_grid_ds, lon_child, lat_child, mask_child
460
+ parent_grid_ds, lon_child, lat_child, mask_child, direction
371
461
  )
372
462
 
373
- if update_land_indices:
463
+ if update_land_indices and mask_child.sum() > 0:
374
464
  i_eta, i_xi = update_indices_if_on_parent_land(
375
465
  i_eta, i_xi, grid_location, parent_grid_ds
376
466
  )
@@ -410,18 +500,25 @@ def map_child_boundaries_onto_parent_grid_indices(
410
500
  }
411
501
  ds = ds.rename(dims_to_rename)
412
502
 
413
- ds = ds.assign_coords(
414
- {
415
- "indices_rho": ("two", ["xi", "eta"]),
416
- "indices_vel": ("three", ["xi", "eta", "angle"]),
417
- }
418
- )
419
-
420
503
  return ds
421
504
 
422
505
 
423
- def interpolate_indices(parent_grid_ds, lon, lat, mask):
424
- """Interpolate the parent indices to the child grid.
506
+ def interpolate_indices(
507
+ parent_grid_ds: xr.Dataset,
508
+ lon: xr.DataArray,
509
+ lat: xr.DataArray,
510
+ mask: xr.DataArray,
511
+ direction: str,
512
+ ) -> tuple[xr.DataArray, xr.DataArray]:
513
+ """Interpolate the parent indices to the child grid boundary.
514
+
515
+ Uses the parent grid ``lon_rho``/``lat_rho`` coordinates to compute
516
+ fractional i/j indices at child-boundary longitude/latitude points using
517
+ linear interpolation. The function verifies that all ocean child points
518
+ (based on ``mask``) lie within the parent grid and warns if child
519
+ boundary points fall near the parent-grid edges. Land child points that fall
520
+ outside the parent grid (i.e., interpolation returns NaN) are filled with
521
+ ``-1e5``.
425
522
 
426
523
  Parameters
427
524
  ----------
@@ -433,6 +530,9 @@ def interpolate_indices(parent_grid_ds, lon, lat, mask):
433
530
  Latitudes of the child grid where interpolation is desired.
434
531
  mask: xarray.DataArray
435
532
  Mask for the child longitudes and latitudes under consideration.
533
+ direction : str
534
+ Boundary identifier (``"south"``, ``"north"``, ``"east"``, ``"west"``).
535
+ Used for generating informative warnings or errors.
436
536
  Returns
437
537
  -------
438
538
  i : xarray.DataArray
@@ -467,27 +567,67 @@ def interpolate_indices(parent_grid_ds, lon, lat, mask):
467
567
  i = xr.DataArray(i, dims=lon.dims)
468
568
  j = xr.DataArray(j, dims=lon.dims)
469
569
 
470
- # Check for NaN values
471
- if np.sum(np.isnan(i)) > 0 or np.sum(np.isnan(j)) > 0:
570
+ # Check whether ocean child points fall outside the parent grid
571
+ if (
572
+ i.where(mask, other=0.0).isnull().any()
573
+ or j.where(mask, other=0.0).isnull().any()
574
+ ):
472
575
  raise ValueError(
473
- "Some points are outside the grid. Please choose either a bigger parent grid or a smaller child grid."
576
+ f"Some wet points on the {direction}ern boundary of the child grid lie "
577
+ "outside the parent grid. Please use a larger parent grid or a smaller child grid."
474
578
  )
475
579
 
476
- # Check whether indices are close to border of parent grid
580
+ # Try to fix NaNs if there only a few per boundary. Fix with out-of-bounds points is not valid.
477
581
  nxp, nyp = lon_parent.shape
478
- if np.min(i) < 0 or np.max(i) > nxp - 2:
479
- logging.warning(
480
- "Some boundary points of the child grid are very close to the boundary of the parent grid."
582
+ nan_idx = (
583
+ i.isnull() | j.isnull() | (i > nxp - 2) | (i < 0) | (j > nyp - 2) | (j < 0)
584
+ )
585
+
586
+ idx = xr.DataArray(np.arange(i.size), dims=i.dims)
587
+
588
+ # Interpolate indices for points that are invalid (NaN or out-of-bounds)
589
+ if nan_idx.any() and not nan_idx.all():
590
+ idx_tmp = idx.where(~nan_idx, drop=True) # valid poins
591
+ i_tmp = i.where(~nan_idx, drop=True) # valid points
592
+ j_tmp = j.where(~nan_idx, drop=True) # valid points
593
+
594
+ interp_i = interp1d(
595
+ idx_tmp.values, i_tmp.values, kind="nearest", fill_value="extrapolate"
596
+ )
597
+ interp_j = interp1d(
598
+ idx_tmp.values, j_tmp.values, kind="nearest", fill_value="extrapolate"
599
+ )
600
+
601
+ i[nan_idx.values] = interp_i(idx[nan_idx].values)
602
+ j[nan_idx.values] = interp_j(idx[nan_idx].values)
603
+
604
+ # This should only occur in rare edge cases
605
+ if i.isnull().any() or j.isnull().any():
606
+ raise ValueError(
607
+ f"Mapping failed: the {direction}ern boundary of the child grid could not be "
608
+ "mapped onto parent-grid indices. Please adjust the parent/child grid configuration."
481
609
  )
482
- if np.min(j) < 0 or np.max(j) > nyp - 2:
610
+
611
+ # Warn if child boundary points are near the edges of the parent grid
612
+ if (
613
+ i.where(mask).min() < 0
614
+ or i.where(mask).max() > nxp - 2
615
+ or j.where(mask).min() < 0
616
+ or j.where(mask).max() > nyp - 2
617
+ ):
483
618
  logging.warning(
484
- "Some boundary points of the child grid are very close to the boundary of the parent grid."
619
+ f"Some wet points on the {direction}ern boundary of the child grid lie very close to the edges of the parent grid."
485
620
  )
486
621
 
487
622
  return i, j
488
623
 
489
624
 
490
- def update_indices_if_on_parent_land(i_eta, i_xi, grid_location, parent_grid_ds):
625
+ def update_indices_if_on_parent_land(
626
+ i_eta: xr.DataArray,
627
+ i_xi: xr.DataArray,
628
+ grid_location: str,
629
+ parent_grid_ds: xr.Dataset,
630
+ ) -> tuple[xr.DataArray, xr.DataArray]:
491
631
  """Finds points that are in the parent land mask but not land masked in the child
492
632
  and replaces parent indices with nearest neighbor wet points.
493
633
 
@@ -581,13 +721,88 @@ def update_indices_if_on_parent_land(i_eta, i_xi, grid_location, parent_grid_ds)
581
721
  return i_eta, i_xi
582
722
 
583
723
 
584
- def modify_child_topography_and_mask(
724
+ def _interpolate_parent(
725
+ parent_da: xr.DataArray, child_da: xr.DataArray
726
+ ) -> xr.DataArray:
727
+ """
728
+ Interpolate data from a parent grid onto a child grid using linear interpolation.
729
+
730
+ Parameters
731
+ ----------
732
+ parent_da : xr.DataArray
733
+ The data array on the parent grid. Must have coordinates `lon_rho` and `lat_rho`.
734
+ child_da : xr.DataArray
735
+ The target child grid data array. Must have coordinates `lon_rho` and `lat_rho`.
736
+
737
+ Returns
738
+ -------
739
+ xr.DataArray
740
+ The interpolated data on the child grid, with dimensions ("eta_rho", "xi_rho").
741
+ """
742
+ points = np.column_stack(
743
+ (parent_da.lon_rho.values.ravel(), parent_da.lat_rho.values.ravel())
744
+ )
745
+ xi = (child_da.lon_rho.values, child_da.lat_rho.values)
746
+ values = parent_da.values.ravel()
747
+
748
+ parent_interpolated = griddata(points, values, xi, method="linear")
749
+ return xr.DataArray(parent_interpolated, dims=("eta_rho", "xi_rho"))
750
+
751
+
752
+ def modify_child_mask(
753
+ parent_grid_ds: xr.Dataset,
754
+ child_grid_ds: xr.Dataset,
755
+ boundaries: dict = {"south": True, "east": True, "north": True, "west": True},
756
+ ):
757
+ """Adjust the child gridmask to align with the parent grid.
758
+
759
+ The mask of the child grid is adjusted using a weighted sum based on the boundary distance.
760
+
761
+ Parameters
762
+ ----------
763
+ parent_grid_ds : xarray.Dataset
764
+ The parent grid dataset containing `mask_rho` (land-sea mask).
765
+
766
+ child_grid_ds : xarray.Dataset
767
+ The child grid dataset whose `mask_rho` will be modified.
768
+
769
+ boundaries : dict, optional
770
+ A dictionary specifying which boundaries should be modified. Expected keys:
771
+ - `"south"` (bool): Whether to adjust the southern boundary.
772
+ - `"east"` (bool): Whether to adjust the eastern boundary.
773
+ - `"north"` (bool): Whether to adjust the northern boundary.
774
+ - `"west"` (bool): Whether to adjust the western boundary.
775
+ Defaults to modifying all boundaries.
776
+
777
+ Returns
778
+ -------
779
+ xarray.Dataset
780
+ The updated child grid dataset with modified mask (`mask_rho`).
781
+ """
782
+ # regrid parent mask onto child grid
783
+ mask_parent_interpolated = _interpolate_parent(
784
+ parent_grid_ds["mask_rho"], child_grid_ds["mask_rho"]
785
+ )
786
+
787
+ # compute weight based on distance
788
+ alpha = compute_boundary_distance(child_grid_ds["mask_rho"], boundaries)
789
+
790
+ # update child mask to be weighted sum between original child and interpolated parent
791
+ child_mask = (
792
+ alpha * child_grid_ds["mask_rho"] + (1 - alpha) * mask_parent_interpolated
793
+ )
794
+ child_grid_ds["mask_rho"] = xr.where(child_mask >= 0.5, 1, 0)
795
+
796
+ return child_grid_ds
797
+
798
+
799
+ def modify_child_topography(
585
800
  parent_grid_ds,
586
801
  child_grid_ds,
587
802
  boundaries={"south": True, "east": True, "north": True, "west": True},
588
803
  hmin=5.0,
589
804
  ):
590
- """Adjust the child grid topography and mask to align with the parent grid.
805
+ """Adjust the child grid topography to align with the parent grid.
591
806
 
592
807
  The topography of the child grid is adjusted using a weighted sum based on the boundary distance,
593
808
  and the depth values are clipped to enforce a minimum depth constraint.
@@ -595,10 +810,10 @@ def modify_child_topography_and_mask(
595
810
  Parameters
596
811
  ----------
597
812
  parent_grid_ds : xarray.Dataset
598
- The parent grid dataset containing `h` (topography) and `mask_rho` (land-sea mask).
813
+ The parent grid dataset containing `h` (topography).
599
814
 
600
815
  child_grid_ds : xarray.Dataset
601
- The child grid dataset whose `h` and `mask_rho` will be modified.
816
+ The child grid dataset whose `h` will be modified.
602
817
 
603
818
  boundaries : dict, optional
604
819
  A dictionary specifying which boundaries should be modified. Expected keys:
@@ -616,39 +831,21 @@ def modify_child_topography_and_mask(
616
831
  Returns
617
832
  -------
618
833
  xarray.Dataset
619
- The updated child grid dataset with modified topography (`h`) and mask (`mask_rho`).
834
+ The updated child grid dataset with modified topography (`h`).
620
835
  """
621
- # regrid parent topography and mask onto child grid
622
- points = np.column_stack(
623
- (parent_grid_ds.lon_rho.values.ravel(), parent_grid_ds.lat_rho.values.ravel())
624
- )
625
- xi = (child_grid_ds.lon_rho.values, child_grid_ds.lat_rho.values)
626
-
627
- values = parent_grid_ds["h"].values.ravel()
628
- h_parent_interpolated = griddata(points, values, xi, method="linear")
629
- h_parent_interpolated = xr.DataArray(
630
- h_parent_interpolated, dims=("eta_rho", "xi_rho")
631
- )
632
-
633
- values = parent_grid_ds["mask_rho"].values.ravel()
634
- mask_parent_interpolated = griddata(points, values, xi, method="linear")
635
- mask_parent_interpolated = xr.DataArray(
636
- mask_parent_interpolated, dims=("eta_rho", "xi_rho")
637
- )
836
+ # regrid parent mask onto child grid
837
+ h_parent_interpolated = _interpolate_parent(parent_grid_ds["h"], child_grid_ds["h"])
638
838
 
639
839
  # compute weight based on distance
640
840
  alpha = compute_boundary_distance(child_grid_ds["mask_rho"], boundaries)
641
- # update child topography and mask to be weighted sum between original child and interpolated parent
841
+
842
+ # update child topography to be weighted sum between original child and interpolated parent
642
843
  child_grid_ds["h"] = (
643
844
  alpha * child_grid_ds["h"] + (1 - alpha) * h_parent_interpolated
644
845
  )
645
- # Clip depth on modified child topography
646
- child_grid_ds["h"] = _clip_depth(child_grid_ds["h"], hmin)
647
846
 
648
- child_mask = (
649
- alpha * child_grid_ds["mask_rho"] + (1 - alpha) * mask_parent_interpolated
650
- )
651
- child_grid_ds["mask_rho"] = xr.where(child_mask >= 0.5, 1, 0)
847
+ # Clip depth on modified child topography
848
+ child_grid_ds["h"] = clip_depth(child_grid_ds["h"], hmin)
652
849
 
653
850
  return child_grid_ds
654
851