roms-tools 2.3.0__py3-none-any.whl → 2.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. ci/environment.yml +1 -0
  2. roms_tools/__init__.py +1 -0
  3. roms_tools/analysis/roms_output.py +10 -6
  4. roms_tools/setup/boundary_forcing.py +178 -193
  5. roms_tools/setup/datasets.py +58 -1
  6. roms_tools/setup/grid.py +31 -97
  7. roms_tools/setup/initial_conditions.py +172 -126
  8. roms_tools/setup/nesting.py +2 -23
  9. roms_tools/setup/river_forcing.py +34 -67
  10. roms_tools/setup/surface_forcing.py +111 -61
  11. roms_tools/setup/tides.py +7 -30
  12. roms_tools/setup/utils.py +24 -70
  13. roms_tools/tests/test_setup/test_boundary_forcing.py +220 -57
  14. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zattrs +5 -3
  15. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +156 -121
  16. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zarray +2 -2
  17. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +2 -1
  18. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/0 +0 -0
  19. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zarray +2 -2
  20. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zattrs +1 -1
  21. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/0 +0 -0
  22. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/.zarray +4 -4
  23. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/.zarray +4 -4
  25. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/.zarray +4 -4
  27. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/.zarray +4 -4
  29. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/.zarray +4 -4
  31. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
  32. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/.zarray +4 -4
  33. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/.zarray +4 -4
  35. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/.zarray +4 -4
  37. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/0.0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/.zarray +4 -4
  39. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/.zarray +4 -4
  41. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/.zarray +4 -4
  43. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/.zarray +4 -4
  45. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/.zarray +4 -4
  47. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/.zarray +4 -4
  49. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/.zarray +4 -4
  51. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/.zarray +4 -4
  53. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/.zarray +4 -4
  55. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/.zarray +4 -4
  57. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/.zarray +4 -4
  59. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/.zarray +4 -4
  61. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/.zarray +4 -4
  63. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/.zarray +4 -4
  65. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/.zarray +4 -4
  67. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/.zarray +4 -4
  69. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zarray +4 -4
  71. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +8 -0
  72. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zarray +4 -4
  74. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +8 -0
  75. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zarray +4 -4
  77. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +8 -0
  78. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zarray +4 -4
  80. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +8 -0
  81. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +4 -4
  83. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +4 -4
  84. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +2 -1
  103. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +6 -4
  104. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_r/.zattrs +1 -1
  105. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_w/.zattrs +1 -1
  106. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/0.0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/0.0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/0.0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/abs_time/.zattrs +1 -0
  110. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/0.0.0.0 +0 -0
  111. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ocean_time/.zattrs +1 -1
  112. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/0.0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/0.0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/0.0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +30 -0
  121. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zarray +22 -0
  122. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +8 -0
  123. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +30 -0
  125. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zarray +22 -0
  126. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +8 -0
  127. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/0.0 +0 -0
  128. roms_tools/tests/test_setup/test_grid.py +0 -13
  129. roms_tools/tests/test_setup/test_initial_conditions.py +204 -66
  130. roms_tools/tests/test_setup/test_nesting.py +0 -16
  131. roms_tools/tests/test_setup/test_river_forcing.py +8 -36
  132. roms_tools/tests/test_setup/test_surface_forcing.py +102 -73
  133. roms_tools/tests/test_setup/test_tides.py +4 -16
  134. roms_tools/tests/test_setup/test_utils.py +1 -0
  135. roms_tools/tests/{test_utils.py → test_tiling/test_partition.py} +1 -1
  136. roms_tools/tiling/partition.py +338 -0
  137. roms_tools/utils.py +66 -333
  138. roms_tools/vertical_coordinate.py +54 -133
  139. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/METADATA +1 -1
  140. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/RECORD +143 -136
  141. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/LICENSE +0 -0
  142. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/WHEEL +0 -0
  143. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,8 @@ from roms_tools import Grid, SurfaceForcing
5
5
  from roms_tools.download import download_test_data
6
6
  import textwrap
7
7
  from pathlib import Path
8
- from conftest import calculate_file_hash
8
+ import logging
9
+ from conftest import calculate_data_hash
9
10
 
10
11
 
11
12
  @pytest.fixture
@@ -163,7 +164,9 @@ def grid_that_lies_west_of_dateline_more_than_five_degrees_away():
163
164
  "grid_that_lies_west_of_dateline_more_than_five_degrees_away",
164
165
  ],
165
166
  )
166
- def test_successful_initialization_with_regional_data(grid_fixture, request, use_dask):
167
+ def test_successful_initialization_with_regional_data(
168
+ grid_fixture, request, caplog, use_dask
169
+ ):
167
170
  """Test the initialization of SurfaceForcing with regional ERA5 data.
168
171
 
169
172
  The test is performed twice:
@@ -178,16 +181,17 @@ def test_successful_initialization_with_regional_data(grid_fixture, request, use
178
181
 
179
182
  grid = request.getfixturevalue(grid_fixture)
180
183
 
181
- for use_coarse_grid in [False, True]:
182
- sfc_forcing = SurfaceForcing(
183
- grid=grid,
184
- use_coarse_grid=use_coarse_grid,
185
- start_time=start_time,
186
- end_time=end_time,
187
- source={"name": "ERA5", "path": fname},
188
- correct_radiation=True,
189
- use_dask=use_dask,
190
- )
184
+ for coarse_grid_mode in ["always", "never"]:
185
+ with caplog.at_level(logging.INFO):
186
+ sfc_forcing = SurfaceForcing(
187
+ grid=grid,
188
+ start_time=start_time,
189
+ end_time=end_time,
190
+ source={"name": "ERA5", "path": fname},
191
+ correct_radiation=True,
192
+ coarse_grid_mode=coarse_grid_mode,
193
+ use_dask=use_dask,
194
+ )
191
195
 
192
196
  assert sfc_forcing.ds is not None
193
197
  assert "uwnd" in sfc_forcing.ds
@@ -208,10 +212,15 @@ def test_successful_initialization_with_regional_data(grid_fixture, request, use
208
212
  }
209
213
  assert sfc_forcing.ds.coords["time"].attrs["units"] == "days"
210
214
 
211
- if use_coarse_grid:
215
+ if coarse_grid_mode == "always":
212
216
  assert sfc_forcing.use_coarse_grid
213
- else:
217
+ assert (
218
+ "Data will be interpolated onto grid coarsened by factor 2."
219
+ in caplog.text
220
+ )
221
+ elif coarse_grid_mode == "never":
214
222
  assert not sfc_forcing.use_coarse_grid
223
+ assert "Data will be interpolated onto fine grid." in caplog.text
215
224
 
216
225
  sfc_forcing.plot("uwnd", time=0)
217
226
  sfc_forcing.plot("vwnd", time=0)
@@ -239,12 +248,12 @@ def test_nan_detection_initialization_with_regional_data(
239
248
 
240
249
  grid = request.getfixturevalue(grid_fixture)
241
250
 
242
- for use_coarse_grid in [True, False]:
251
+ for coarse_grid_mode in ["always", "never"]:
243
252
  with pytest.raises(ValueError, match="NaN values found"):
244
253
 
245
254
  SurfaceForcing(
246
255
  grid=grid,
247
- use_coarse_grid=use_coarse_grid,
256
+ coarse_grid_mode=coarse_grid_mode,
248
257
  start_time=start_time,
249
258
  end_time=end_time,
250
259
  source={"name": "ERA5", "path": fname},
@@ -266,14 +275,14 @@ def test_no_longitude_intersection_initialization_with_regional_data(
266
275
 
267
276
  fname = Path(download_test_data("ERA5_regional_test_data.nc"))
268
277
 
269
- for use_coarse_grid in [True, False]:
278
+ for coarse_grid_mode in ["always", "never"]:
270
279
  with pytest.raises(
271
280
  ValueError, match="Selected longitude range does not intersect with dataset"
272
281
  ):
273
282
 
274
283
  SurfaceForcing(
275
284
  grid=grid_that_straddles_180_degree_meridian,
276
- use_coarse_grid=use_coarse_grid,
285
+ coarse_grid_mode=coarse_grid_mode,
277
286
  start_time=start_time,
278
287
  end_time=end_time,
279
288
  source={"name": "ERA5", "path": fname},
@@ -308,10 +317,10 @@ def test_successful_initialization_with_global_data(grid_fixture, request, use_d
308
317
 
309
318
  grid = request.getfixturevalue(grid_fixture)
310
319
 
311
- for use_coarse_grid in [True, False]:
320
+ for coarse_grid_mode in ["always", "never"]:
312
321
  sfc_forcing = SurfaceForcing(
313
322
  grid=grid,
314
- use_coarse_grid=use_coarse_grid,
323
+ coarse_grid_mode=coarse_grid_mode,
315
324
  start_time=start_time,
316
325
  end_time=end_time,
317
326
  source={"name": "ERA5", "path": fname},
@@ -336,9 +345,9 @@ def test_successful_initialization_with_global_data(grid_fixture, request, use_d
336
345
  assert sfc_forcing.ds.attrs["source"] == "ERA5"
337
346
  assert sfc_forcing.ds.coords["time"].attrs["units"] == "days"
338
347
 
339
- if use_coarse_grid:
348
+ if coarse_grid_mode == "always":
340
349
  assert sfc_forcing.use_coarse_grid
341
- else:
350
+ elif coarse_grid_mode == "never":
342
351
  assert not sfc_forcing.use_coarse_grid
343
352
 
344
353
 
@@ -356,10 +365,10 @@ def test_nans_filled_in(grid_that_straddles_dateline, use_dask):
356
365
  fname = Path(download_test_data("ERA5_regional_test_data.nc"))
357
366
  fname_bgc = Path(download_test_data("CESM_surface_global_test_data_climatology.nc"))
358
367
 
359
- for use_coarse_grid in [True, False]:
368
+ for coarse_grid_mode in ["always", "never"]:
360
369
  sfc_forcing = SurfaceForcing(
361
370
  grid=grid_that_straddles_dateline,
362
- use_coarse_grid=use_coarse_grid,
371
+ coarse_grid_mode=coarse_grid_mode,
363
372
  start_time=start_time,
364
373
  end_time=end_time,
365
374
  source={"name": "ERA5", "path": fname},
@@ -375,7 +384,7 @@ def test_nans_filled_in(grid_that_straddles_dateline, use_dask):
375
384
 
376
385
  sfc_forcing = SurfaceForcing(
377
386
  grid=grid_that_straddles_dateline,
378
- use_coarse_grid=use_coarse_grid,
387
+ coarse_grid_mode=coarse_grid_mode,
379
388
  start_time=start_time,
380
389
  end_time=end_time,
381
390
  source={"name": "CESM_REGRIDDED", "path": fname_bgc, "climatology": True},
@@ -479,6 +488,57 @@ def test_surface_forcing_pco2_replication(sfc_forcing_fixture, request):
479
488
  )
480
489
 
481
490
 
491
+ def test_determine_usage_coarse_grid():
492
+ # ERA5 data with 1/4 degree resolution spanning [-50E, 30E] and [40N, 80N]
493
+ fname = download_test_data("ERA5_regional_test_data.nc")
494
+
495
+ # at 50N, 1/4 degree of longitude is about 17.87 km; to automatically use the coarse grid, the ROMS grid needs to be of resolution < 17.87km / 2 = 8.9km
496
+ grid_10km = Grid(
497
+ nx=3, ny=3, size_x=30, size_y=30, center_lon=-10, center_lat=50, rot=0
498
+ )
499
+ surface_forcing = SurfaceForcing(
500
+ grid=grid_10km,
501
+ start_time=datetime(2020, 2, 1),
502
+ end_time=datetime(2020, 2, 2),
503
+ source={"name": "ERA5", "path": fname},
504
+ )
505
+ assert not surface_forcing.use_coarse_grid
506
+
507
+ grid_7km = Grid(
508
+ nx=3, ny=3, size_x=21, size_y=21, center_lon=-10, center_lat=50, rot=0
509
+ )
510
+ surface_forcing = SurfaceForcing(
511
+ grid=grid_7km,
512
+ start_time=datetime(2020, 2, 1),
513
+ end_time=datetime(2020, 2, 2),
514
+ source={"name": "ERA5", "path": fname},
515
+ )
516
+ assert surface_forcing.use_coarse_grid
517
+
518
+ # at 70N, 1/4 degree of longitude is about 9.5 km; to automatically use the coarse grid, the ROMS grid needs to be of resolution < 9.5km / 2 = 4.75km
519
+ grid_7km = Grid(
520
+ nx=3, ny=3, size_x=21, size_y=21, center_lon=-10, center_lat=70, rot=0
521
+ )
522
+ surface_forcing = SurfaceForcing(
523
+ grid=grid_7km,
524
+ start_time=datetime(2020, 2, 1),
525
+ end_time=datetime(2020, 2, 2),
526
+ source={"name": "ERA5", "path": fname},
527
+ )
528
+ assert not surface_forcing.use_coarse_grid
529
+
530
+ grid_4km = Grid(
531
+ nx=3, ny=3, size_x=12, size_y=12, center_lon=-10, center_lat=70, rot=0
532
+ )
533
+ surface_forcing = SurfaceForcing(
534
+ grid=grid_4km,
535
+ start_time=datetime(2020, 2, 1),
536
+ end_time=datetime(2020, 2, 2),
537
+ source={"name": "ERA5", "path": fname},
538
+ )
539
+ assert surface_forcing.use_coarse_grid
540
+
541
+
482
542
  @pytest.mark.parametrize(
483
543
  "sfc_forcing_fixture",
484
544
  [
@@ -499,15 +559,15 @@ def test_surface_forcing_save(sfc_forcing_fixture, request, tmp_path):
499
559
  str(tmp_path / file_str),
500
560
  ]: # test for Path object and str
501
561
 
502
- # Test saving without partitioning and grouping
503
- saved_filenames = sfc_forcing.save(filepath)
562
+ # Test saving without grouping
563
+ saved_filenames = sfc_forcing.save(filepath, group=False)
504
564
  filepath_str = str(Path(filepath).with_suffix(""))
505
565
  expected_filepath = Path(f"{filepath_str}.nc")
506
566
  assert saved_filenames == [expected_filepath]
507
567
  assert expected_filepath.exists()
508
568
  expected_filepath.unlink()
509
569
 
510
- # Test saving without partitioning but with grouping
570
+ # Test saving with grouping
511
571
  saved_filenames = sfc_forcing.save(filepath, group=True)
512
572
  filepath_str = str(Path(filepath).with_suffix(""))
513
573
  expected_filepath = Path(f"{filepath_str}_202002.nc")
@@ -515,17 +575,6 @@ def test_surface_forcing_save(sfc_forcing_fixture, request, tmp_path):
515
575
  assert expected_filepath.exists()
516
576
  expected_filepath.unlink()
517
577
 
518
- # Test saving with partitioning
519
- saved_filenames = sfc_forcing.save(filepath, np_eta=1, group=True)
520
-
521
- expected_filepath_list = [
522
- Path(filepath_str + f"_202002.{index}.nc") for index in range(1)
523
- ]
524
- assert saved_filenames == expected_filepath_list
525
- for expected_filepath in expected_filepath_list:
526
- assert expected_filepath.exists()
527
- expected_filepath.unlink()
528
-
529
578
 
530
579
  def test_surface_forcing_bgc_plot(bgc_surface_forcing):
531
580
  """Test plot method."""
@@ -543,15 +592,15 @@ def test_surface_forcing_bgc_save(bgc_surface_forcing, tmp_path):
543
592
  str(tmp_path / file_str),
544
593
  ]: # test for Path object and str
545
594
 
546
- # Test saving without partitioning and grouping
547
- saved_filenames = bgc_surface_forcing.save(filepath)
595
+ # Test saving without grouping
596
+ saved_filenames = bgc_surface_forcing.save(filepath, group=False)
548
597
  filepath_str = str(Path(filepath).with_suffix(""))
549
598
  expected_filepath = Path(f"{filepath_str}.nc")
550
599
  assert saved_filenames == [expected_filepath]
551
600
  assert expected_filepath.exists()
552
601
  expected_filepath.unlink()
553
602
 
554
- # Test saving without partitioning but with grouping
603
+ # Test saving with grouping
555
604
  saved_filenames = bgc_surface_forcing.save(filepath, group=True)
556
605
  filepath_str = str(Path(filepath).with_suffix(""))
557
606
  expected_filepath = Path(f"{filepath_str}_202002.nc")
@@ -559,17 +608,6 @@ def test_surface_forcing_bgc_save(bgc_surface_forcing, tmp_path):
559
608
  assert expected_filepath.exists()
560
609
  expected_filepath.unlink()
561
610
 
562
- # Test saving with partitioning
563
- saved_filenames = bgc_surface_forcing.save(filepath, np_xi=5, group=True)
564
-
565
- expected_filepath_list = [
566
- Path(filepath_str + f"_202002.{index}.nc") for index in range(5)
567
- ]
568
- assert saved_filenames == expected_filepath_list
569
- for expected_filepath in expected_filepath_list:
570
- assert expected_filepath.exists()
571
- expected_filepath.unlink()
572
-
573
611
 
574
612
  def test_surface_forcing_bgc_from_clim_save(
575
613
  bgc_surface_forcing_from_climatology, tmp_path
@@ -583,15 +621,17 @@ def test_surface_forcing_bgc_from_clim_save(
583
621
  str(tmp_path / file_str),
584
622
  ]: # test for Path object and str
585
623
 
586
- # Test saving without partitioning and grouping
587
- saved_filenames = bgc_surface_forcing_from_climatology.save(filepath)
624
+ # Test saving without grouping
625
+ saved_filenames = bgc_surface_forcing_from_climatology.save(
626
+ filepath, group=False
627
+ )
588
628
  filepath_str = str(Path(filepath).with_suffix(""))
589
629
  expected_filepath = Path(f"{filepath_str}.nc")
590
630
  assert saved_filenames == [expected_filepath]
591
631
  assert expected_filepath.exists()
592
632
  expected_filepath.unlink()
593
633
 
594
- # Test saving without partitioning but with grouping
634
+ # Test saving with grouping
595
635
  saved_filenames = bgc_surface_forcing_from_climatology.save(
596
636
  filepath, group=True
597
637
  )
@@ -601,19 +641,6 @@ def test_surface_forcing_bgc_from_clim_save(
601
641
  assert expected_filepath.exists()
602
642
  expected_filepath.unlink()
603
643
 
604
- # Test saving with partitioning and grouping
605
- saved_filenames = bgc_surface_forcing_from_climatology.save(
606
- filepath, np_eta=5, group=True
607
- )
608
-
609
- expected_filepath_list = [
610
- Path(filepath_str + f"_clim.{index}.nc") for index in range(5)
611
- ]
612
- assert saved_filenames == expected_filepath_list
613
- for expected_filepath in expected_filepath_list:
614
- assert expected_filepath.exists()
615
- expected_filepath.unlink()
616
-
617
644
 
618
645
  @pytest.mark.parametrize(
619
646
  "sfc_forcing_fixture",
@@ -675,8 +702,9 @@ def test_files_have_same_hash(sfc_forcing_fixture, request, tmp_path, use_dask):
675
702
  expected_filepath1 = f"{filepath_str1}_202002.nc"
676
703
  expected_filepath2 = f"{filepath_str2}_202002.nc"
677
704
 
678
- hash1 = calculate_file_hash(expected_filepath1)
679
- hash2 = calculate_file_hash(expected_filepath2)
705
+ # Only compare hash of datasets because metadata is non-deterministic with dask
706
+ hash1 = calculate_data_hash(expected_filepath1)
707
+ hash2 = calculate_data_hash(expected_filepath2)
680
708
 
681
709
  assert hash1 == hash2, f"Hashes do not match: {hash1} != {hash2}"
682
710
 
@@ -703,8 +731,9 @@ def test_files_have_same_hash_clim(
703
731
  expected_filepath1 = f"{filepath_str1}_clim.nc"
704
732
  expected_filepath2 = f"{filepath_str2}_clim.nc"
705
733
 
706
- hash1 = calculate_file_hash(expected_filepath1)
707
- hash2 = calculate_file_hash(expected_filepath2)
734
+ # Only compare hash of datasets because metadata is non-deterministic with dask
735
+ hash1 = calculate_data_hash(expected_filepath1)
736
+ hash2 = calculate_data_hash(expected_filepath2)
708
737
 
709
738
  assert hash1 == hash2, f"Hashes do not match: {hash1} != {hash2}"
710
739
 
@@ -4,7 +4,7 @@ import xarray as xr
4
4
  from roms_tools.download import download_test_data
5
5
  import textwrap
6
6
  from pathlib import Path
7
- from conftest import calculate_file_hash
7
+ from conftest import calculate_data_hash
8
8
 
9
9
 
10
10
  @pytest.fixture
@@ -185,7 +185,6 @@ def test_tidal_forcing_save(tidal_forcing, tmp_path):
185
185
  str(tmp_path / file_str),
186
186
  ]: # test for Path object and str
187
187
 
188
- # Test saving without partitioning
189
188
  saved_filenames = tidal_forcing.save(filepath)
190
189
  # Check if the .nc file was created
191
190
  filepath = Path(filepath).with_suffix(".nc")
@@ -194,18 +193,6 @@ def test_tidal_forcing_save(tidal_forcing, tmp_path):
194
193
  # Clean up the .nc file
195
194
  filepath.unlink()
196
195
 
197
- # Test saving with partitioning
198
- saved_filenames = tidal_forcing.save(filepath, np_eta=3, np_xi=3)
199
-
200
- filepath_str = str(filepath.with_suffix(""))
201
- expected_filepath_list = [
202
- Path(filepath_str + f".{index}.nc") for index in range(9)
203
- ]
204
- assert saved_filenames == expected_filepath_list
205
- for expected_filepath in expected_filepath_list:
206
- assert expected_filepath.exists()
207
- expected_filepath.unlink()
208
-
209
196
 
210
197
  def test_roundtrip_yaml(tidal_forcing, tmp_path, use_dask):
211
198
  """Test that creating a TidalForcing object, saving its parameters to yaml file, and
@@ -242,8 +229,9 @@ def test_files_have_same_hash(tidal_forcing, tmp_path, use_dask):
242
229
  tidal_forcing_from_file = TidalForcing.from_yaml(yaml_filepath, use_dask=use_dask)
243
230
  tidal_forcing_from_file.save(filepath2)
244
231
 
245
- hash1 = calculate_file_hash(filepath1)
246
- hash2 = calculate_file_hash(filepath2)
232
+ # Only compare hash of datasets because metadata is non-deterministic with dask
233
+ hash1 = calculate_data_hash(filepath1)
234
+ hash2 = calculate_data_hash(filepath2)
247
235
 
248
236
  assert hash1 == hash2, f"Hashes do not match: {hash1} != {hash2}"
249
237
 
@@ -15,6 +15,7 @@ def test_interpolate_from_climatology(use_dask):
15
15
 
16
16
  climatology = ERA5Correction(use_dask=use_dask)
17
17
  field = climatology.ds["ssr_corr"]
18
+ field["time"] = field["time"].dt.days
18
19
 
19
20
  interpolated_field = interpolate_from_climatology(field, "time", era5_times)
20
21
  assert len(interpolated_field.time) == len(era5_times)
@@ -2,7 +2,7 @@ import pytest
2
2
  from pathlib import Path
3
3
  import xarray.testing as xrt
4
4
 
5
- from roms_tools.utils import partition, partition_netcdf
5
+ from roms_tools.tiling.partition import partition, partition_netcdf
6
6
  from roms_tools import Grid
7
7
 
8
8