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
@@ -91,14 +91,13 @@ class InitialConditions:
91
91
  self._input_checks()
92
92
 
93
93
  processed_fields = {}
94
- processed_fields = self._process_data(processed_fields, type="physics")
94
+ processed_fields, variable_info = self._process_data(
95
+ processed_fields, type="physics"
96
+ )
95
97
 
96
98
  if self.bgc_source is not None:
97
- processed_fields = self._process_data(processed_fields, type="bgc")
98
-
99
- for var_name in processed_fields.keys():
100
- processed_fields[var_name] = transpose_dimensions(
101
- processed_fields[var_name]
99
+ processed_fields, bgc_variable_info = self._process_data(
100
+ processed_fields, type="bgc"
102
101
  )
103
102
 
104
103
  d_meta = get_variable_metadata()
@@ -106,7 +105,9 @@ class InitialConditions:
106
105
 
107
106
  ds = self._add_global_metadata(ds)
108
107
 
109
- self._validate(ds)
108
+ if self.bgc_source is not None:
109
+ variable_info = {**variable_info, **bgc_variable_info}
110
+ self._validate(ds, variable_info)
110
111
 
111
112
  # substitute NaNs over land by a fill value to avoid blow-up of ROMS
112
113
  for var_name in ds.data_vars:
@@ -184,7 +185,12 @@ class InitialConditions:
184
185
  {"time": processed_fields["temp"]["time"]}
185
186
  )
186
187
 
187
- return processed_fields
188
+ for var_name in processed_fields.keys():
189
+ processed_fields[var_name] = transpose_dimensions(
190
+ processed_fields[var_name]
191
+ )
192
+
193
+ return processed_fields, variable_info
188
194
 
189
195
  def _input_checks(self):
190
196
 
@@ -259,11 +265,21 @@ class InitialConditions:
259
265
  - `vector_pair`: For vector variables, this indicates the associated variable that forms the vector (e.g., 'u' and 'v').
260
266
  - `is_3d`: Indicates whether the variable is 3D (True for variables like 'temp' and 'salt') or 2D (False for 'zeta').
261
267
 
268
+ Parameters
269
+ ----------
270
+ data : object
271
+ The data object which contains variable names for the "bgc" type variables.
272
+
273
+ type : str, optional, default="physics"
274
+ The type of variable metadata to return. Can be one of:
275
+ - "physics": for physical variables such as temperature, salinity, and velocity components.
276
+ - "bgc": for biogeochemical variables (like ALK).
277
+
262
278
  Returns
263
279
  -------
264
280
  dict
265
281
  A dictionary where the keys are variable names and the values are dictionaries of metadata
266
- about each variable, including 'location', 'is_vector', 'vector_pair', and 'is_3d'.
282
+ about each variable, including 'location', 'is_vector', 'vector_pair', 'is_3d', and 'validate'.
267
283
  """
268
284
  default_info = {
269
285
  "location": "rho",
@@ -272,7 +288,6 @@ class InitialConditions:
272
288
  "is_3d": True,
273
289
  }
274
290
 
275
- # Define a dictionary for variable names and their associated information
276
291
  if type == "physics":
277
292
  variable_info = {
278
293
  "zeta": {
@@ -280,38 +295,54 @@ class InitialConditions:
280
295
  "is_vector": False,
281
296
  "vector_pair": None,
282
297
  "is_3d": False,
298
+ "validate": True,
283
299
  },
284
- "temp": default_info,
285
- "salt": default_info,
300
+ "temp": {**default_info, "validate": False},
301
+ "salt": {**default_info, "validate": False},
286
302
  "u": {
287
303
  "location": "u",
288
304
  "is_vector": True,
289
305
  "vector_pair": "v",
290
306
  "is_3d": True,
307
+ "validate": False,
291
308
  },
292
309
  "v": {
293
310
  "location": "v",
294
311
  "is_vector": True,
295
312
  "vector_pair": "u",
296
313
  "is_3d": True,
314
+ "validate": False,
297
315
  },
298
316
  "ubar": {
299
317
  "location": "u",
300
318
  "is_vector": True,
301
319
  "vector_pair": "vbar",
302
320
  "is_3d": False,
321
+ "validate": False,
303
322
  },
304
323
  "vbar": {
305
324
  "location": "v",
306
325
  "is_vector": True,
307
326
  "vector_pair": "ubar",
308
327
  "is_3d": False,
328
+ "validate": False,
329
+ },
330
+ "w": {
331
+ "location": "rho",
332
+ "is_vector": False,
333
+ "vector_pair": None,
334
+ "is_3d": True,
335
+ "validate": False,
309
336
  },
310
337
  }
311
- elif type == "bgc":
338
+
339
+ if type == "bgc":
312
340
  variable_info = {}
313
341
  for var_name in data.var_names.keys():
314
- variable_info[var_name] = default_info
342
+ if var_name == "ALK":
343
+ variable_info[var_name] = {**default_info, "validate": True}
344
+ else:
345
+ variable_info[var_name] = {**default_info, "validate": False}
315
346
 
316
347
  return variable_info
317
348
 
@@ -373,7 +404,7 @@ class InitialConditions:
373
404
 
374
405
  return ds
375
406
 
376
- def _validate(self, ds):
407
+ def _validate(self, ds, variable_info):
377
408
  """Validates the dataset by checking for NaN values in SSH at wet points, which
378
409
  would indicate missing raw data coverage over the target domain.
379
410
 
@@ -381,6 +412,8 @@ class InitialConditions:
381
412
  ----------
382
413
  ds : xarray.Dataset
383
414
  The dataset to validate.
415
+ variable_info : dict
416
+ A dictionary containing metadata about the variables, including whether to validate them.
384
417
 
385
418
  Raises
386
419
  ------
@@ -393,8 +426,11 @@ class InitialConditions:
393
426
  This check is only applied to the 2D variable SSH to improve performance.
394
427
  """
395
428
 
396
- ds["zeta"].load()
397
- nan_check(ds["zeta"].squeeze(), self.grid.ds.mask_rho)
429
+ for var_name in variable_info:
430
+ # Only validate variables based on "validate" flag if use_dask is False
431
+ if not self.use_dask or variable_info[var_name]["validate"]:
432
+ ds[var_name].load()
433
+ nan_check(ds[var_name].squeeze(), self.grid.ds.mask_rho)
398
434
 
399
435
  def _add_global_metadata(self, ds):
400
436
 
@@ -425,6 +461,7 @@ class InitialConditions:
425
461
  xi=None,
426
462
  depth_contours=False,
427
463
  layer_contours=False,
464
+ ax=None,
428
465
  ) -> None:
429
466
  """Plot the initial conditions field for a given eta-, xi-, or s_rho- slice.
430
467
 
@@ -492,6 +529,8 @@ class InitialConditions:
492
529
  be added to the plot. This is particularly useful in vertical sections to
493
530
  visualize the layering of the water column. For clarity, the number of layer
494
531
  contours displayed is limited to a maximum of 10. Default is False.
532
+ ax : matplotlib.axes.Axes, optional
533
+ 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.
495
534
 
496
535
  Returns
497
536
  -------
@@ -518,7 +557,12 @@ class InitialConditions:
518
557
  ):
519
558
  raise ValueError("For 2D fields, specify either eta or xi, not both.")
520
559
 
521
- self.ds[var_name].load()
560
+ if self.use_dask:
561
+ from dask.diagnostics import ProgressBar
562
+
563
+ with ProgressBar():
564
+ self.ds[var_name].load()
565
+
522
566
  field = self.ds[var_name].squeeze()
523
567
 
524
568
  if all(dim in field.dims for dim in ["eta_rho", "xi_rho"]):
@@ -634,13 +678,17 @@ class InitialConditions:
634
678
 
635
679
  if len(field.dims) == 2:
636
680
  _section_plot(
637
- field, interface_depth=interface_depth, title=title, kwargs=kwargs
681
+ field,
682
+ interface_depth=interface_depth,
683
+ title=title,
684
+ kwargs=kwargs,
685
+ ax=ax,
638
686
  )
639
687
  else:
640
688
  if "s_rho" in field.dims:
641
- _profile_plot(field, title=title)
689
+ _profile_plot(field, title=title, ax=ax)
642
690
  else:
643
- _line_plot(field, title=title)
691
+ _line_plot(field, title=title, ax=ax)
644
692
 
645
693
  def save(
646
694
  self, filepath: Union[str, Path], np_eta: int = None, np_xi: int = None
@@ -681,7 +729,13 @@ class InitialConditions:
681
729
  if filepath.suffix == ".nc":
682
730
  filepath = filepath.with_suffix("")
683
731
 
684
- dataset_list = [self.ds.load()]
732
+ if self.use_dask:
733
+ from dask.diagnostics import ProgressBar
734
+
735
+ with ProgressBar():
736
+ self.ds.load()
737
+
738
+ dataset_list = [self.ds]
685
739
  output_filenames = [str(filepath)]
686
740
 
687
741
  saved_filenames = save_datasets(
@@ -719,15 +773,18 @@ class InitialConditions:
719
773
 
720
774
  initial_conditions_data = {
721
775
  "InitialConditions": {
722
- "source": self.source,
723
776
  "ini_time": self.ini_time.isoformat(),
724
- "model_reference_date": self.model_reference_date.isoformat(),
777
+ "source": self.source,
725
778
  }
726
779
  }
727
780
  # Include bgc_source if it's not None
728
781
  if self.bgc_source is not None:
729
782
  initial_conditions_data["InitialConditions"]["bgc_source"] = self.bgc_source
730
783
 
784
+ initial_conditions_data["InitialConditions"][
785
+ "model_reference_date"
786
+ ] = self.model_reference_date.isoformat()
787
+
731
788
  yaml_data = {
732
789
  **grid_yaml_data,
733
790
  **initial_conditions_data,
@@ -737,7 +794,7 @@ class InitialConditions:
737
794
  # Write header
738
795
  file.write(header)
739
796
  # Write YAML data
740
- yaml.dump(yaml_data, file, default_flow_style=False)
797
+ yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
741
798
 
742
799
  @classmethod
743
800
  def from_yaml(
roms_tools/setup/plot.py CHANGED
@@ -12,6 +12,32 @@ def _plot(
12
12
  title="",
13
13
  kwargs={},
14
14
  ):
15
+ """Plots a grid or field on a map with optional depth contours.
16
+
17
+ This function plots a map using Cartopy projections. It supports plotting a grid, a field, and adding depth contours if desired.
18
+ The projection can be customized, and the grid can be adjusted for domains straddling the 180° meridian.
19
+
20
+ Parameters
21
+ ----------
22
+ grid_ds : xarray.Dataset
23
+ The grid dataset containing coordinates (`lon_rho`, `lat_rho`).
24
+ field : xarray.DataArray, optional
25
+ The field to plot. If None, only the grid is plotted.
26
+ depth_contours : bool, optional
27
+ If True, adds depth contours to the plot.
28
+ straddle : bool, optional
29
+ If True, adjusts longitude values to straddle across the 180° meridian.
30
+ c : str, optional
31
+ Color for the boundary plot (default is 'red').
32
+ title : str, optional
33
+ Title of the plot.
34
+ kwargs : dict, optional
35
+ Additional keyword arguments to pass to `pcolormesh` (e.g., colormap or color limits).
36
+
37
+ Notes
38
+ -----
39
+ The function raises a `NotImplementedError` if the domain contains the North or South Pole.
40
+ """
15
41
 
16
42
  if field is None:
17
43
  lon_deg = grid_ds["lon_rho"]
@@ -84,12 +110,11 @@ def _plot(
84
110
  cs = ax.contour(lon_deg, lat_deg, field.layer_depth, transform=proj, colors="k")
85
111
  ax.clabel(cs, inline=True, fontsize=10)
86
112
 
87
- return fig
88
113
 
114
+ def _section_plot(field, interface_depth=None, title="", kwargs={}, ax=None):
89
115
 
90
- def _section_plot(field, interface_depth=None, title="", kwargs={}):
91
-
92
- fig, ax = plt.subplots(1, 1, figsize=(9, 5))
116
+ if ax is None:
117
+ fig, ax = plt.subplots(1, 1, figsize=(9, 5))
93
118
 
94
119
  dims_to_check = ["eta_rho", "eta_u", "eta_v", "xi_rho", "xi_u", "xi_v"]
95
120
  try:
@@ -132,7 +157,23 @@ def _section_plot(field, interface_depth=None, title="", kwargs={}):
132
157
  ax.set_title(title)
133
158
 
134
159
 
135
- def _profile_plot(field, title=""):
160
+ def _profile_plot(field, title="", ax=None):
161
+ """Plots a profile of the given field against depth.
162
+
163
+ Parameters
164
+ ----------
165
+ field : xarray.DataArray
166
+ Data to plot.
167
+ title : str, optional
168
+ Title of the plot.
169
+ ax : matplotlib.axes.Axes, optional
170
+ Axes to plot on. If None, a new figure is created.
171
+
172
+ Raises
173
+ ------
174
+ ValueError
175
+ If no expected depth coordinate is found in the field.
176
+ """
136
177
 
137
178
  depths_to_check = [
138
179
  "layer_depth_rho",
@@ -153,16 +194,33 @@ def _profile_plot(field, title=""):
153
194
  "None of the expected coordinates (layer_depth_rho, layer_depth_u, layer_depth_v, interface_depth_rho, interface_depth_u, interface_depth_v) found in field.coords"
154
195
  )
155
196
 
156
- fig, ax = plt.subplots(1, 1, figsize=(4, 7))
197
+ if ax is None:
198
+ fig, ax = plt.subplots(1, 1, figsize=(4, 7))
157
199
  kwargs = {"y": depth_label, "yincrease": False}
158
200
  field.plot(**kwargs)
159
201
  ax.set_title(title)
160
202
  ax.grid()
161
203
 
162
204
 
163
- def _line_plot(field, title=""):
164
-
165
- fig, ax = plt.subplots(1, 1, figsize=(7, 4))
166
- field.plot()
205
+ def _line_plot(field, title="", ax=None):
206
+ """Plots a line graph of the given field.
207
+
208
+ Parameters
209
+ ----------
210
+ field : xarray.DataArray
211
+ Data to plot.
212
+ title : str, optional
213
+ Title of the plot.
214
+ ax : matplotlib.axes.Axes, optional
215
+ Axes to plot on. If None, a new figure is created.
216
+
217
+ Returns
218
+ -------
219
+ None
220
+ Modifies the plot in-place.
221
+ """
222
+ if ax is None:
223
+ fig, ax = plt.subplots(1, 1, figsize=(7, 4))
224
+ field.plot(ax=ax)
167
225
  ax.set_title(title)
168
226
  ax.grid()
@@ -133,14 +133,7 @@ class SurfaceForcing:
133
133
 
134
134
  ds = self._write_into_dataset(processed_fields, data, d_meta)
135
135
 
136
- if self.use_coarse_grid:
137
- mask = self.grid.ds["mask_coarse"].rename(
138
- {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
139
- )
140
- else:
141
- mask = self.grid.ds["mask_rho"]
142
-
143
- self._validate(ds, mask)
136
+ self._validate(ds, target_coords["mask"], variable_info)
144
137
 
145
138
  # substitute NaNs over land by a fill value to avoid blow-up of ROMS
146
139
  for var_name in ds.data_vars:
@@ -233,28 +226,34 @@ class SurfaceForcing:
233
226
  # Define a dictionary for variable names and their associated information
234
227
  if self.type == "physics":
235
228
  variable_info = {
236
- "swrad": default_info,
237
- "lwrad": default_info,
238
- "Tair": default_info,
239
- "qair": default_info,
240
- "rain": default_info,
229
+ "swrad": {**default_info, "validate": True},
230
+ "lwrad": {**default_info, "validate": False},
231
+ "Tair": {**default_info, "validate": False},
232
+ "qair": {**default_info, "validate": True},
233
+ "rain": {**default_info, "validate": False},
241
234
  "uwnd": {
242
235
  "location": "u",
243
236
  "is_vector": True,
244
237
  "vector_pair": "vwnd",
245
238
  "is_3d": False,
239
+ "validate": True,
246
240
  },
247
241
  "vwnd": {
248
242
  "location": "v",
249
243
  "is_vector": True,
250
244
  "vector_pair": "uwnd",
251
245
  "is_3d": False,
246
+ "validate": True,
252
247
  },
253
248
  }
254
249
  elif self.type == "bgc":
255
250
  variable_info = {}
256
- for var in data.var_names.keys():
257
- variable_info[var] = default_info
251
+ for var_name in data.var_names.keys():
252
+ variable_info[var_name] = default_info
253
+ if var_name == "pco2_air":
254
+ variable_info[var_name] = {**default_info, "validate": True}
255
+ else:
256
+ variable_info[var_name] = {**default_info, "validate": False}
258
257
 
259
258
  return variable_info
260
259
 
@@ -300,9 +299,6 @@ class SurfaceForcing:
300
299
  ds[var_name].attrs["long_name"] = d_meta[var_name]["long_name"]
301
300
  ds[var_name].attrs["units"] = d_meta[var_name]["units"]
302
301
 
303
- if self.use_coarse_grid:
304
- ds = ds.rename({"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"})
305
-
306
302
  ds = self._add_global_metadata(ds)
307
303
 
308
304
  # Convert the time coordinate to the format expected by ROMS
@@ -369,7 +365,7 @@ class SurfaceForcing:
369
365
 
370
366
  return ds
371
367
 
372
- def _validate(self, ds, mask):
368
+ def _validate(self, ds, mask, variable_info):
373
369
  """Validates the dataset by checking for NaN values at wet points, which would
374
370
  indicate missing raw data coverage over the target domain.
375
371
 
@@ -379,6 +375,10 @@ class SurfaceForcing:
379
375
  The dataset to validate.
380
376
  mask : xarray.DataArray
381
377
  Land mask (1=ocean, 0=land) to determine wet points in the domain.
378
+ variable_info : dict
379
+ A dictionary containing metadata about each variable (e.g., location,
380
+ whether it's a 3D variable, etc.). Used to retrieve information for
381
+ validating each variable.
382
382
 
383
383
  Raises
384
384
  ------
@@ -392,7 +392,9 @@ class SurfaceForcing:
392
392
  """
393
393
 
394
394
  for var_name in ds.data_vars:
395
- nan_check(ds[var_name].isel(time=0), mask)
395
+ # Only validate variables based on "validate" flag if use_dask is False
396
+ if not self.use_dask or variable_info[var_name]["validate"]:
397
+ nan_check(ds[var_name].isel(time=0), mask)
396
398
 
397
399
  def _add_global_metadata(self, ds=None):
398
400
 
@@ -462,15 +464,16 @@ class SurfaceForcing:
462
464
  if var_name not in self.ds:
463
465
  raise ValueError(f"Variable '{var_name}' is not found in dataset.")
464
466
 
465
- field = self.ds[var_name].isel(time=time).load()
467
+ field = self.ds[var_name].isel(time=time)
468
+ if self.use_dask:
469
+ from dask.diagnostics import ProgressBar
470
+
471
+ with ProgressBar():
472
+ field = field.load()
473
+
466
474
  title = field.long_name
467
475
 
468
- # assign lat / lon
469
- if self.use_coarse_grid:
470
- field = field.rename({"eta_rho": "eta_coarse", "xi_rho": "xi_coarse"})
471
- field = field.where(self.grid.ds.mask_coarse)
472
- else:
473
- field = field.where(self.grid.ds.mask_rho)
476
+ field = field.where(self.target_coords["mask"])
474
477
 
475
478
  field = field.assign_coords(
476
479
  {"lon": self.target_coords["lon"], "lat": self.target_coords["lat"]}
@@ -502,24 +505,26 @@ class SurfaceForcing:
502
505
  )
503
506
 
504
507
  def save(
505
- self, filepath: Union[str, Path], np_eta: int = None, np_xi: int = None
508
+ self,
509
+ filepath: Union[str, Path],
510
+ np_eta: int = None,
511
+ np_xi: int = None,
512
+ group: bool = False,
506
513
  ) -> None:
507
- """Save the surface forcing fields to netCDF4 files.
514
+ """Save the surface forcing fields to one or more netCDF4 files.
508
515
 
509
- This method saves the dataset by grouping it into subsets based on the data frequency. The subsets are then written
510
- to one or more netCDF4 files. The filenames of the output files reflect the temporal coverage of the data.
516
+ This method saves the dataset either as a single file or as multiple files depending on the partitioning and grouping options.
517
+ The dataset can be saved in two modes:
511
518
 
512
- There are two modes of saving the dataset:
519
+ 1. **Single File Mode (default)**:
520
+ - If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
521
+ - The file is named based on the `filepath`, with `.nc` automatically appended.
513
522
 
514
- 1. **Single File Mode (default)**:
523
+ 2. **Partitioned Mode**:
524
+ - If either `np_eta` or `np_xi` is specified, the dataset is partitioned into spatial tiles along the `eta` and `xi` axes.
525
+ - 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"`).
515
526
 
516
- If both `np_eta` and `np_xi` are `None`, the entire dataset, divided by temporal subsets, is saved as a single netCDF4 file
517
- with the base filename specified by `filepath.nc`.
518
-
519
- 2. **Partitioned Mode**:
520
-
521
- - If either `np_eta` or `np_xi` is specified, the dataset is divided into spatial tiles along the eta-axis and xi-axis.
522
- - Each spatial tile is saved as a separate netCDF4 file.
527
+ Additionally, if `group` is set to `True`, the dataset is first grouped into temporal subsets, resulting in multiple grouped files before partitioning and saving.
523
528
 
524
529
  Parameters
525
530
  ----------
@@ -531,6 +536,8 @@ class SurfaceForcing:
531
536
  The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed.
532
537
  np_xi : int, optional
533
538
  The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed.
539
+ group: bool, optional
540
+ If `True`, groups the dataset into multiple files based on temporal data frequency. Defaults to `False`.
534
541
 
535
542
  Returns
536
543
  -------
@@ -545,7 +552,18 @@ class SurfaceForcing:
545
552
  if filepath.suffix == ".nc":
546
553
  filepath = filepath.with_suffix("")
547
554
 
548
- dataset_list, output_filenames = group_dataset(self.ds.load(), str(filepath))
555
+ if self.use_dask:
556
+ from dask.diagnostics import ProgressBar
557
+
558
+ with ProgressBar():
559
+ self.ds.load()
560
+
561
+ if group:
562
+ dataset_list, output_filenames = group_dataset(self.ds, str(filepath))
563
+ else:
564
+ dataset_list = [self.ds]
565
+ output_filenames = [str(filepath)]
566
+
549
567
  saved_filenames = save_datasets(
550
568
  dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
551
569
  )
@@ -603,7 +621,7 @@ class SurfaceForcing:
603
621
  # Write header
604
622
  file.write(header)
605
623
  # Write YAML data
606
- yaml.dump(yaml_data, file, default_flow_style=False)
624
+ yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
607
625
 
608
626
  @classmethod
609
627
  def from_yaml(