roms-tools 1.6.0__py3-none-any.whl → 1.6.2__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 (159) hide show
  1. roms_tools/__init__.py +4 -1
  2. roms_tools/_version.py +1 -1
  3. roms_tools/setup/boundary_forcing.py +155 -52
  4. roms_tools/setup/datasets.py +5 -5
  5. roms_tools/setup/grid.py +9 -11
  6. roms_tools/setup/initial_conditions.py +82 -25
  7. roms_tools/setup/plot.py +68 -10
  8. roms_tools/setup/surface_forcing.py +60 -42
  9. roms_tools/setup/tides.py +35 -13
  10. roms_tools/setup/utils.py +15 -6
  11. roms_tools/tests/test_setup/test_boundary_forcing.py +140 -18
  12. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/0.0.0 +0 -0
  13. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/0.0.0 +0 -0
  14. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/0.0.0 +0 -0
  15. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/0.0.0 +0 -0
  16. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/0.0.0 +0 -0
  17. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
  18. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
  19. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
  20. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
  21. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
  22. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
  23. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
  25. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/0.0.0 +0 -0
  27. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/0.0.0 +0 -0
  29. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/0.0.0 +0 -0
  31. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
  32. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
  33. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
  39. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
  41. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
  43. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
  45. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
  47. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
  49. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/0.0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
  51. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
  53. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
  55. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
  57. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
  59. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
  61. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
  63. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
  65. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/0.0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
  67. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
  69. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/0.0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
  72. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
  75. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/0.0.0 +0 -0
  78. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
  80. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
  81. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
  83. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
  84. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/0.0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/0.0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/0.0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/0.0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
  105. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/0.0.0 +0 -0
  106. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/0.0.0 +0 -0
  110. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
  111. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
  112. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
  121. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/0.0.0 +0 -0
  122. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
  123. roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +0 -7
  125. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +0 -3
  126. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
  127. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
  128. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
  129. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
  130. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
  131. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
  132. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
  133. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
  134. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
  135. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
  136. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
  137. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
  138. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
  139. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
  140. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
  141. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
  142. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
  143. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
  144. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +0 -1
  145. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
  146. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +0 -1
  147. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +0 -1
  148. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
  149. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +0 -1
  150. roms_tools/tests/test_setup/test_datasets.py +8 -3
  151. roms_tools/tests/test_setup/test_grid.py +6 -5
  152. roms_tools/tests/test_setup/test_initial_conditions.py +8 -4
  153. roms_tools/tests/test_setup/test_surface_forcing.py +47 -27
  154. roms_tools/tests/test_setup/test_tides.py +6 -4
  155. {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/METADATA +2 -1
  156. {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/RECORD +159 -159
  157. {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/LICENSE +0 -0
  158. {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/WHEEL +0 -0
  159. {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/top_level.txt +0 -0
roms_tools/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from importlib.metadata import version as _version
2
-
2
+ import logging # noqa: F811
3
3
 
4
4
  try:
5
5
  __version__ = _version("roms_tools")
@@ -13,3 +13,6 @@ from roms_tools.setup.tides import TidalForcing # noqa: F401
13
13
  from roms_tools.setup.surface_forcing import SurfaceForcing # noqa: F401
14
14
  from roms_tools.setup.initial_conditions import InitialConditions # noqa: F401
15
15
  from roms_tools.setup.boundary_forcing import BoundaryForcing # noqa: F401
16
+
17
+ # Configure logging when the package is imported
18
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s - %(message)s")
roms_tools/_version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # Do not change! Do not track in version control!
2
- __version__ = "1.6.0"
2
+ __version__ = "1.6.2"
@@ -1,9 +1,10 @@
1
1
  import xarray as xr
2
2
  import numpy as np
3
3
  import pandas as pd
4
+ from scipy.ndimage import label
5
+ import logging
4
6
  import yaml
5
7
  import importlib.metadata
6
- import warnings
7
8
  from typing import Dict, Union, List
8
9
  from dataclasses import dataclass, field, asdict
9
10
  from roms_tools.setup.grid import Grid
@@ -197,6 +198,13 @@ class BoundaryForcing:
197
198
  ].isel(**bdry_coords[location][direction])
198
199
 
199
200
  if not self.apply_2d_horizontal_fill:
201
+ self._validate_1d_fill(
202
+ processed_fields,
203
+ variable_info,
204
+ bdry_coords,
205
+ direction,
206
+ bdry_data.dim_names["depth"],
207
+ )
200
208
  processed_fields = apply_1d_horizontal_fill(processed_fields)
201
209
 
202
210
  # vertical regridding
@@ -329,38 +337,46 @@ class BoundaryForcing:
329
337
  "is_vector": False,
330
338
  "vector_pair": None,
331
339
  "is_3d": False,
340
+ "validate": True,
332
341
  },
333
- "temp": default_info,
334
- "salt": default_info,
342
+ "temp": {**default_info, "validate": True},
343
+ "salt": {**default_info, "validate": False},
335
344
  "u": {
336
345
  "location": "u",
337
346
  "is_vector": True,
338
347
  "vector_pair": "v",
339
348
  "is_3d": True,
349
+ "validate": True,
340
350
  },
341
351
  "v": {
342
352
  "location": "v",
343
353
  "is_vector": True,
344
354
  "vector_pair": "u",
345
355
  "is_3d": True,
356
+ "validate": True,
346
357
  },
347
358
  "ubar": {
348
359
  "location": "u",
349
360
  "is_vector": True,
350
361
  "vector_pair": "vbar",
351
362
  "is_3d": False,
363
+ "validate": False,
352
364
  },
353
365
  "vbar": {
354
366
  "location": "v",
355
367
  "is_vector": True,
356
368
  "vector_pair": "ubar",
357
369
  "is_3d": False,
370
+ "validate": False,
358
371
  },
359
372
  }
360
373
  elif self.type == "bgc":
361
374
  variable_info = {}
362
375
  for var_name in data.var_names.keys():
363
- variable_info[var_name] = default_info
376
+ if var_name == "ALK":
377
+ variable_info[var_name] = {**default_info, "validate": True}
378
+ else:
379
+ variable_info[var_name] = {**default_info, "validate": False}
364
380
 
365
381
  return variable_info
366
382
 
@@ -506,9 +522,78 @@ class BoundaryForcing:
506
522
 
507
523
  return ds
508
524
 
525
+ def _validate_1d_fill(
526
+ self, processed_fields, variable_info, bdry_coords, direction, depth_dim
527
+ ):
528
+ """Check if any boundary is divided by land and issue a warning if so,
529
+ suggesting the use of 2D horizontal fill for safer regridding.
530
+
531
+ Parameters
532
+ ----------
533
+ processed_fields : dict
534
+ A dictionary where keys are variable names and values are `xarray.DataArray`
535
+ objects representing the processed data for each variable.
536
+
537
+ variable_info : dict
538
+ A dictionary containing metadata about each variable (e.g., location,
539
+ whether it's a 3D variable, etc.). Used to retrieve information for
540
+ validating each variable.
541
+
542
+ bdry_coords : dict
543
+ A dictionary containing boundary coordinates for different directions (north, south,
544
+ east, west), used to slice the boundary-specific data for each variable.
545
+
546
+ direction : str
547
+ The boundary direction being processed (e.g., "north", "south", "east", or "west").
548
+
549
+ depth_dim : str
550
+ The dimension representing depth (e.g., 'z', 'depth', etc.), used when slicing 3D
551
+ data for a specific depth level.
552
+
553
+ Returns
554
+ -------
555
+ None
556
+ If a boundary is divided by land, a warning is issued. No return value is provided.
557
+ """
558
+
559
+ for var_name in processed_fields.keys():
560
+ # Only validate variables based on "validate" flag if use_dask is False
561
+ if not self.use_dask or variable_info[var_name]["validate"]:
562
+ location = variable_info[var_name]["location"]
563
+
564
+ # Select the appropriate mask based on variable location
565
+ if location == "rho":
566
+ mask = self.grid.ds.mask_rho
567
+ elif location == "u":
568
+ mask = self.grid.ds.mask_u
569
+ elif location == "v":
570
+ mask = self.grid.ds.mask_v
571
+
572
+ mask = mask.isel(**bdry_coords[location][direction])
573
+
574
+ if variable_info[var_name]["is_3d"]:
575
+ da = processed_fields[var_name].isel({depth_dim: 0, "time": 0})
576
+ else:
577
+ da = processed_fields[var_name].isel({"time": 0})
578
+
579
+ wet_nans = xr.where(da.where(mask).isnull(), 1, 0)
580
+ # Apply label to find connected components of wet NaNs
581
+ labeled_array, num_features = label(wet_nans)
582
+ left_margin = labeled_array[0]
583
+ right_margin = labeled_array[-1]
584
+ if left_margin != 0:
585
+ num_features = num_features - 1
586
+ if right_margin != 0:
587
+ num_features = num_features - 1
588
+ if num_features > 0:
589
+ logging.warning(
590
+ f"For {var_name}, the {direction}ern boundary is divided by land. It would be safer (but slower) to use `apply_2d_horizontal_fill = True`."
591
+ )
592
+
509
593
  def _validate(self, ds, variable_info, bdry_coords):
510
- """Validate the dataset for NaN values at the first time step based on the fill
511
- method used.
594
+ """Validate the dataset for NaN values at the first time step (bry_time=0) for
595
+ specified variables. If NaN values are found at wet points, this function raises
596
+ an error.
512
597
 
513
598
  Parameters
514
599
  ----------
@@ -530,14 +615,11 @@ class BoundaryForcing:
530
615
  Notes
531
616
  -----
532
617
  Validation is performed on the initial boundary time step (`bry_time=0`) for each
533
- variable in the dataset. If the `apply_2d_horizontal_fill` attribute is set to False,
534
- a warning is issued instead of a strict NaN check, as the data may not be reliably validated.
535
- Conversely, if `apply_2d_horizontal_fill` is True, a strict NaN check is performed, raising
536
- a ValueError if any NaN values are detected.
618
+ variable in the dataset.
537
619
  """
538
- if self.apply_2d_horizontal_fill:
539
- # Strict NaN check with ValueError makes sense to be applied
540
- for var_name in variable_info:
620
+ for var_name in variable_info:
621
+ # only validate variables based on "validate" flag if use_dask is false
622
+ if not self.use_dask or variable_info[var_name]["validate"]:
541
623
  location = variable_info[var_name]["location"]
542
624
 
543
625
  # Select the appropriate mask based on variable location
@@ -547,38 +629,29 @@ class BoundaryForcing:
547
629
  mask = self.grid.ds.mask_u
548
630
  elif location == "v":
549
631
  mask = self.grid.ds.mask_v
550
- else:
551
- continue # Skip if location is not recognized
552
632
 
553
633
  for direction in ["south", "east", "north", "west"]:
554
634
  if self.boundaries[direction]:
555
635
  bdry_var_name = f"{var_name}_{direction}"
556
636
 
557
637
  # Check for NaN values at the first time step using the nan_check function
638
+ if self.apply_2d_horizontal_fill:
639
+ error_message = None
640
+ else:
641
+ error_message = (
642
+ f"{bdry_var_name} consists entirely of NaNs after regridding. "
643
+ f"This may be due to the {direction}ern boundary being on land in the "
644
+ f"{self.source['name']} data, which could have a coarser resolution than the ROMS domain. "
645
+ f"Try setting `apply_2d_horizontal_fill = True` to resolve this issue."
646
+ )
647
+
558
648
  nan_check(
559
649
  ds[bdry_var_name].isel(bry_time=0),
560
650
  mask.isel(**bdry_coords[location][direction]),
651
+ error_message=error_message,
561
652
  )
562
- else:
563
- # Can't apply strict NaN check because land values haven't been filled before regridding step; instead warn user
564
- for direction in ["south", "east", "north", "west"]:
565
- if self.boundaries[direction]:
566
- for var_name in variable_info:
567
- bdry_var_name = f"{var_name}_{direction}"
568
- if ds[bdry_var_name].isel(bry_time=0).isnull().any().values:
569
- warnings.warn(
570
- f"NaN values detected in regridded variables along the {direction}ern boundary. This may indicate that the entire boundary is on land in the source data, or that the source data does not cover this boundary.",
571
- UserWarning,
572
- )
573
- # Break after the first warning for this direction to avoid duplicates
574
- break
575
653
 
576
- def plot(
577
- self,
578
- var_name,
579
- time=0,
580
- layer_contours=False,
581
- ) -> None:
654
+ def plot(self, var_name, time=0, layer_contours=False, ax=None) -> None:
582
655
  """Plot the boundary forcing field for a given time-slice.
583
656
 
584
657
  Parameters
@@ -634,6 +707,8 @@ class BoundaryForcing:
634
707
  If True, contour lines representing the boundaries between vertical layers will
635
708
  be added to the plot. For clarity, the number of layer
636
709
  contours displayed is limited to a maximum of 10. Default is False.
710
+ ax : matplotlib.axes.Axes, optional
711
+ The axes to plot on. If None, a new figure is created.
637
712
 
638
713
  Returns
639
714
  -------
@@ -649,7 +724,14 @@ class BoundaryForcing:
649
724
  if var_name not in self.ds:
650
725
  raise ValueError(f"Variable '{var_name}' is not found in dataset.")
651
726
 
652
- field = self.ds[var_name].isel(bry_time=time).load()
727
+ field = self.ds[var_name].isel(bry_time=time)
728
+
729
+ if self.use_dask:
730
+ from dask.diagnostics import ProgressBar
731
+
732
+ with ProgressBar():
733
+ field = field.load()
734
+
653
735
  title = field.long_name
654
736
 
655
737
  if "s_rho" in field.dims:
@@ -693,30 +775,36 @@ class BoundaryForcing:
693
775
  interface_depth = None
694
776
 
695
777
  _section_plot(
696
- field, interface_depth=interface_depth, title=title, kwargs=kwargs
778
+ field,
779
+ interface_depth=interface_depth,
780
+ title=title,
781
+ kwargs=kwargs,
782
+ ax=ax,
697
783
  )
698
784
  else:
699
- _line_plot(field, title=title)
785
+ _line_plot(field, title=title, ax=ax)
700
786
 
701
787
  def save(
702
- self, filepath: Union[str, Path], np_eta: int = None, np_xi: int = None
788
+ self,
789
+ filepath: Union[str, Path],
790
+ np_eta: int = None,
791
+ np_xi: int = None,
792
+ group: bool = False,
703
793
  ) -> None:
704
- """Save the boundary forcing fields to netCDF4 files.
705
-
706
- This method saves the dataset by grouping it into subsets based on the data frequency. The subsets are then written
707
- to one or more netCDF4 files. The filenames of the output files reflect the temporal coverage of the data.
794
+ """Save the boundary forcing fields to one or more netCDF4 files.
708
795
 
709
- There are two modes of saving the dataset:
796
+ This method saves the dataset either as a single file or as multiple files depending on the partitioning and grouping options.
797
+ The dataset can be saved in two modes:
710
798
 
711
- 1. **Single File Mode (default)**:
799
+ 1. **Single File Mode (default)**:
800
+ - If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
801
+ - The file is named based on the `filepath`, with `.nc` automatically appended.
712
802
 
713
- If both `np_eta` and `np_xi` are `None`, the entire dataset, divided by temporal subsets, is saved as a single netCDF4 file
714
- with the base filename specified by `filepath.nc`.
803
+ 2. **Partitioned Mode**:
804
+ - If either `np_eta` or `np_xi` is specified, the dataset is partitioned into spatial tiles along the `eta` and `xi` axes.
805
+ - Each tile is saved as a separate netCDF4 file, and filenames are modified with an index (e.g., `"filepath_YYYYMM.0.nc"`, `"filepath_YYYYMM.1.nc"`).
715
806
 
716
- 2. **Partitioned Mode**:
717
-
718
- - If either `np_eta` or `np_xi` is specified, the dataset is divided into spatial tiles along the eta-axis and xi-axis.
719
- - Each spatial tile is saved as a separate netCDF4 file.
807
+ Additionally, if `group` is set to `True`, the dataset is first grouped into temporal subsets, resulting in multiple grouped files before partitioning and saving.
720
808
 
721
809
  Parameters
722
810
  ----------
@@ -728,6 +816,8 @@ class BoundaryForcing:
728
816
  The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed.
729
817
  np_xi : int, optional
730
818
  The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed.
819
+ group: bool, optional
820
+ If `True`, groups the dataset into multiple files based on temporal data frequency. Defaults to `False`.
731
821
 
732
822
  Returns
733
823
  -------
@@ -742,7 +832,18 @@ class BoundaryForcing:
742
832
  if filepath.suffix == ".nc":
743
833
  filepath = filepath.with_suffix("")
744
834
 
745
- dataset_list, output_filenames = group_dataset(self.ds.load(), str(filepath))
835
+ if self.use_dask:
836
+ from dask.diagnostics import ProgressBar
837
+
838
+ with ProgressBar():
839
+ self.ds.load()
840
+
841
+ if group:
842
+ dataset_list, output_filenames = group_dataset(self.ds, str(filepath))
843
+ else:
844
+ dataset_list = [self.ds]
845
+ output_filenames = [str(filepath)]
846
+
746
847
  saved_filenames = save_datasets(
747
848
  dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
748
849
  )
@@ -783,6 +884,7 @@ class BoundaryForcing:
783
884
  "boundaries": self.boundaries,
784
885
  "source": self.source,
785
886
  "type": self.type,
887
+ "apply_2d_horizontal_fill": self.apply_2d_horizontal_fill,
786
888
  "model_reference_date": self.model_reference_date.isoformat(),
787
889
  }
788
890
  }
@@ -796,7 +898,7 @@ class BoundaryForcing:
796
898
  # Write header
797
899
  file.write(header)
798
900
  # Write YAML data
799
- yaml.dump(yaml_data, file, default_flow_style=False)
901
+ yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
800
902
 
801
903
  @classmethod
802
904
  def from_yaml(
@@ -930,6 +1032,7 @@ def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
930
1032
  raise ValueError(
931
1033
  f"No valid horizontal dimension found for variable '{var_name}'."
932
1034
  )
1035
+
933
1036
  # Forward and backward fill in the horizontal direction
934
1037
  filled = one_dim_fill(
935
1038
  processed_fields[var_name], selected_horizontal_dim, direction="forward"
@@ -6,7 +6,7 @@ from datetime import datetime, timedelta
6
6
  import numpy as np
7
7
  from typing import Dict, Optional, Union, List
8
8
  from pathlib import Path
9
- import warnings
9
+ import logging
10
10
  from roms_tools.setup.utils import (
11
11
  assign_dates_to_climatology,
12
12
  interpolate_from_climatology,
@@ -410,7 +410,7 @@ class Dataset:
410
410
  ds[time_dim].where(before_start, drop=True).max()
411
411
  )
412
412
  else:
413
- warnings.warn("No records found at or before the start_time.")
413
+ logging.warning("No records found at or before the start_time.")
414
414
  closest_before_start = ds[time_dim].min()
415
415
 
416
416
  # Identify records after or at end_time
@@ -420,7 +420,7 @@ class Dataset:
420
420
  ds[time_dim].where(after_end, drop=True).min()
421
421
  )
422
422
  else:
423
- warnings.warn("No records found at or after the end_time.")
423
+ logging.warning("No records found at or after the end_time.")
424
424
  closest_after_end = ds[time_dim].max()
425
425
 
426
426
  # Select records within the time range and add the closest before/after
@@ -451,11 +451,11 @@ class Dataset:
451
451
  if ds.sizes[time_dim] > 1:
452
452
  # Pick the time closest to self.start_time
453
453
  ds = ds.isel({time_dim: 0})
454
- print(
454
+ logging.info(
455
455
  f"Selected time entry closest to the specified start_time ({self.start_time}) within the range [{self.start_time}, {self.start_time + timedelta(hours=24)}]: {ds[time_dim].values}"
456
456
  )
457
457
  else:
458
- warnings.warn(
458
+ logging.warning(
459
459
  "Dataset does not contain any time information. Please check if the time dimension "
460
460
  "is correctly named or if the dataset includes time data."
461
461
  )
roms_tools/setup/grid.py CHANGED
@@ -13,7 +13,7 @@ from roms_tools.setup.plot import _plot, _section_plot, _profile_plot, _line_plo
13
13
  from roms_tools.setup.utils import interpolate_from_rho_to_u, interpolate_from_rho_to_v
14
14
  from roms_tools.setup.vertical_coordinate import sigma_stretch, compute_depth
15
15
  from roms_tools.setup.utils import extract_single_value, save_datasets
16
- import warnings
16
+ import logging
17
17
  from pathlib import Path
18
18
 
19
19
  RADIUS_OF_EARTH = 6371315.0 # in m
@@ -350,11 +350,7 @@ class Grid:
350
350
  _plot(self.ds, straddle=self.straddle)
351
351
 
352
352
  def plot_vertical_coordinate(
353
- self,
354
- varname="layer_depth_rho",
355
- s=None,
356
- eta=None,
357
- xi=None,
353
+ self, varname="layer_depth_rho", s=None, eta=None, xi=None, ax=None
358
354
  ) -> None:
359
355
  """Plot the vertical coordinate system for a given eta-, xi-, or s-slice.
360
356
 
@@ -376,6 +372,8 @@ class Grid:
376
372
  The eta-index to plot. Default is None.
377
373
  xi : int, optional
378
374
  The xi-index to plot. Default is None.
375
+ ax : matplotlib.axes.Axes, optional
376
+ 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.
379
377
 
380
378
  Returns
381
379
  -------
@@ -477,12 +475,13 @@ class Grid:
477
475
  interface_depth=interface_depth,
478
476
  title=title,
479
477
  kwargs=kwargs,
478
+ ax=ax,
480
479
  )
481
480
  else:
482
481
  if "s_rho" in field.dims or "s_w" in field.dims:
483
- _profile_plot(field, title=title)
482
+ _profile_plot(field, title=title, ax=ax)
484
483
  else:
485
- _line_plot(field, title=title)
484
+ _line_plot(field, title=title, ax=ax)
486
485
 
487
486
  def save(
488
487
  self, filepath: Union[str, Path], np_eta: int = None, np_xi: int = None
@@ -679,7 +678,7 @@ class Grid:
679
678
  # Write header
680
679
  file.write(header)
681
680
  # Write YAML data
682
- yaml.dump(yaml_data, file, default_flow_style=False)
681
+ yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
683
682
 
684
683
  @classmethod
685
684
  def from_yaml(cls, filepath: Union[str, Path]) -> "Grid":
@@ -728,9 +727,8 @@ class Grid:
728
727
  roms_tools_version_current = "unknown"
729
728
 
730
729
  if roms_tools_version_header != roms_tools_version_current:
731
- warnings.warn(
730
+ logging.warning(
732
731
  f"Current roms-tools version ({roms_tools_version_current}) does not match the version in the YAML header ({roms_tools_version_header}).",
733
- UserWarning,
734
732
  )
735
733
 
736
734
  if grid_data is None: