roms-tools 3.1.2__py3-none-any.whl → 3.2.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 (41) hide show
  1. roms_tools/__init__.py +3 -0
  2. roms_tools/analysis/cdr_analysis.py +203 -0
  3. roms_tools/analysis/cdr_ensemble.py +198 -0
  4. roms_tools/analysis/roms_output.py +80 -46
  5. roms_tools/data/grids/GLORYS_global_grid.nc +0 -0
  6. roms_tools/download.py +4 -0
  7. roms_tools/plot.py +75 -21
  8. roms_tools/setup/boundary_forcing.py +44 -19
  9. roms_tools/setup/cdr_forcing.py +122 -8
  10. roms_tools/setup/cdr_release.py +161 -8
  11. roms_tools/setup/datasets.py +626 -340
  12. roms_tools/setup/grid.py +138 -137
  13. roms_tools/setup/initial_conditions.py +113 -48
  14. roms_tools/setup/mask.py +63 -7
  15. roms_tools/setup/nesting.py +67 -42
  16. roms_tools/setup/river_forcing.py +45 -19
  17. roms_tools/setup/surface_forcing.py +4 -6
  18. roms_tools/setup/tides.py +1 -2
  19. roms_tools/setup/topography.py +4 -4
  20. roms_tools/setup/utils.py +134 -22
  21. roms_tools/tests/test_analysis/test_cdr_analysis.py +144 -0
  22. roms_tools/tests/test_analysis/test_cdr_ensemble.py +202 -0
  23. roms_tools/tests/test_analysis/test_roms_output.py +61 -3
  24. roms_tools/tests/test_setup/test_boundary_forcing.py +54 -52
  25. roms_tools/tests/test_setup/test_cdr_forcing.py +54 -0
  26. roms_tools/tests/test_setup/test_cdr_release.py +118 -1
  27. roms_tools/tests/test_setup/test_datasets.py +392 -44
  28. roms_tools/tests/test_setup/test_grid.py +222 -115
  29. roms_tools/tests/test_setup/test_initial_conditions.py +94 -41
  30. roms_tools/tests/test_setup/test_surface_forcing.py +2 -1
  31. roms_tools/tests/test_setup/test_utils.py +91 -1
  32. roms_tools/tests/test_setup/utils.py +71 -0
  33. roms_tools/tests/test_tiling/test_join.py +241 -0
  34. roms_tools/tests/test_utils.py +139 -17
  35. roms_tools/tiling/join.py +189 -0
  36. roms_tools/utils.py +131 -99
  37. {roms_tools-3.1.2.dist-info → roms_tools-3.2.0.dist-info}/METADATA +12 -2
  38. {roms_tools-3.1.2.dist-info → roms_tools-3.2.0.dist-info}/RECORD +41 -33
  39. {roms_tools-3.1.2.dist-info → roms_tools-3.2.0.dist-info}/WHEEL +0 -0
  40. {roms_tools-3.1.2.dist-info → roms_tools-3.2.0.dist-info}/licenses/LICENSE +0 -0
  41. {roms_tools-3.1.2.dist-info → roms_tools-3.2.0.dist-info}/top_level.txt +0 -0
@@ -121,6 +121,33 @@ def grid_that_straddles_180_degree_meridian_with_global_srtm15_data():
121
121
  return grid
122
122
 
123
123
 
124
+ @pytest.fixture()
125
+ def grid_with_gshhs_coastlines():
126
+ iceland_fjord_kwargs = {
127
+ "nx": 80,
128
+ "ny": 40,
129
+ "size_x": 40,
130
+ "size_y": 20,
131
+ "center_lon": -21.76,
132
+ "center_lat": 64.325,
133
+ "rot": 0,
134
+ "N": 3,
135
+ }
136
+
137
+ # Make sure all 4 L1 files are downloaded
138
+ _ = download_test_data("GSHHS_l_L1.dbf")
139
+ _ = download_test_data("GSHHS_l_L1.prj")
140
+ _ = download_test_data("GSHHS_l_L1.shx")
141
+ shapefile = download_test_data("GSHHS_l_L1.shp")
142
+
143
+ grid = Grid(
144
+ **iceland_fjord_kwargs,
145
+ mask_shapefile=shapefile,
146
+ )
147
+
148
+ return grid
149
+
150
+
124
151
  def test_grid_creation(grid):
125
152
  assert grid.nx == 1
126
153
  assert grid.ny == 1
@@ -175,11 +202,33 @@ def test_coords_relation(grid_fixture, request):
175
202
  "grid_that_straddles_180_degree_meridian_with_shifted_global_etopo_data",
176
203
  "grid_that_straddles_dateline_with_global_srtm15_data",
177
204
  "grid_that_straddles_180_degree_meridian_with_global_srtm15_data",
205
+ "grid_with_gshhs_coastlines",
178
206
  ],
179
207
  )
180
208
  def test_successful_initialization_with_topography(grid_fixture, request):
181
209
  grid = request.getfixturevalue(grid_fixture)
182
- assert grid is not None
210
+
211
+ expected_attrs = [
212
+ "nx",
213
+ "ny",
214
+ "size_x",
215
+ "size_y",
216
+ "center_lon",
217
+ "center_lat",
218
+ "rot",
219
+ "N",
220
+ "theta_s",
221
+ "theta_b",
222
+ "hc",
223
+ "topography_source",
224
+ "hmin",
225
+ "mask_shapefile",
226
+ "verbose",
227
+ "straddle",
228
+ ]
229
+
230
+ for attr in expected_attrs:
231
+ assert hasattr(grid, attr), f"Missing attribute: {attr}"
183
232
 
184
233
 
185
234
  def test_plot(grid_that_straddles_180_degree_meridian):
@@ -280,6 +329,7 @@ def test_grid_straddle_crosses_meridian():
280
329
  "grid",
281
330
  "grid_that_straddles_dateline_with_shifted_global_etopo_data",
282
331
  "grid_that_straddles_dateline_with_global_srtm15_data",
332
+ "grid_with_gshhs_coastlines",
283
333
  ],
284
334
  )
285
335
  def test_roundtrip_netcdf(grid_fixture, tmp_path, request):
@@ -301,8 +351,8 @@ def test_roundtrip_netcdf(grid_fixture, tmp_path, request):
301
351
  # Load the grid from the file
302
352
  grid_from_file = Grid.from_file(filepath.with_suffix(".nc"))
303
353
 
304
- # Assert that the initial grid and the loaded grid are equivalent (including the 'ds' attribute)
305
354
  assert grid == grid_from_file
355
+ xr.testing.assert_equal(grid.ds, grid_from_file.ds)
306
356
 
307
357
  # Clean up the .nc file
308
358
  (filepath.with_suffix(".nc")).unlink()
@@ -314,6 +364,7 @@ def test_roundtrip_netcdf(grid_fixture, tmp_path, request):
314
364
  "grid",
315
365
  "grid_that_straddles_dateline_with_shifted_global_etopo_data",
316
366
  "grid_that_straddles_dateline_with_global_srtm15_data",
367
+ "grid_with_gshhs_coastlines",
317
368
  ],
318
369
  )
319
370
  def test_roundtrip_yaml(grid_fixture, tmp_path, request):
@@ -332,8 +383,8 @@ def test_roundtrip_yaml(grid_fixture, tmp_path, request):
332
383
 
333
384
  grid_from_file = Grid.from_yaml(filepath)
334
385
 
335
- # Assert that the initial grid and the loaded grid are equivalent (including the 'ds' attribute)
336
386
  assert grid == grid_from_file
387
+ xr.testing.assert_equal(grid.ds, grid_from_file.ds)
337
388
 
338
389
  filepath = Path(filepath)
339
390
  filepath.unlink()
@@ -345,6 +396,7 @@ def test_roundtrip_yaml(grid_fixture, tmp_path, request):
345
396
  "grid",
346
397
  "grid_that_straddles_dateline_with_shifted_global_etopo_data",
347
398
  "grid_that_straddles_dateline_with_global_srtm15_data",
399
+ "grid_with_gshhs_coastlines",
348
400
  ],
349
401
  )
350
402
  def test_roundtrip_from_file_yaml(grid_fixture, tmp_path, request):
@@ -359,30 +411,33 @@ def test_roundtrip_from_file_yaml(grid_fixture, tmp_path, request):
359
411
  filepath_yaml = Path(tmp_path / "test.yaml")
360
412
  grid_from_file.to_yaml(filepath_yaml)
361
413
 
414
+ grid_from_yaml = Grid.from_yaml(filepath_yaml)
415
+
416
+ assert grid_from_yaml == grid
417
+ xr.testing.assert_equal(grid.ds, grid_from_yaml.ds)
418
+
362
419
  filepath.unlink()
363
420
  filepath_yaml.unlink()
364
421
 
365
422
 
366
- def test_files_have_same_hash(tmp_path):
367
- # Initialize a Grid object using the initializer
368
- grid_init = Grid(
369
- nx=10,
370
- ny=15,
371
- size_x=100.0,
372
- size_y=150.0,
373
- center_lon=0.0,
374
- center_lat=0.0,
375
- rot=0.0,
376
- topography_source={"name": "ETOPO5"},
377
- hmin=5.0,
378
- )
423
+ @pytest.mark.parametrize(
424
+ "grid_fixture",
425
+ [
426
+ "grid",
427
+ "grid_that_straddles_dateline_with_shifted_global_etopo_data",
428
+ "grid_that_straddles_dateline_with_global_srtm15_data",
429
+ "grid_with_gshhs_coastlines",
430
+ ],
431
+ )
432
+ def test_files_have_same_hash(grid_fixture, tmp_path, request):
433
+ grid = request.getfixturevalue(grid_fixture)
379
434
 
380
435
  yaml_filepath = tmp_path / "test_yaml"
381
436
  filepath1 = tmp_path / "test1.nc"
382
437
  filepath2 = tmp_path / "test2.nc"
383
438
 
384
- grid_init.to_yaml(yaml_filepath)
385
- grid_init.save(filepath1)
439
+ grid.to_yaml(yaml_filepath)
440
+ grid.save(filepath1)
386
441
 
387
442
  grid_from_file = Grid.from_yaml(yaml_filepath)
388
443
  grid_from_file.save(filepath2)
@@ -505,6 +560,9 @@ def test_from_yaml_version_mismatch(tmp_path, caplog):
505
560
  yaml_filepath.unlink()
506
561
 
507
562
 
563
+ # Vertical coordinate tests
564
+
565
+
508
566
  def test_invalid_theta_s_value():
509
567
  """Test the validation of the theta_s value."""
510
568
  with pytest.raises(ValueError):
@@ -608,6 +666,152 @@ def test_plot_vertical_coordinate():
608
666
  grid.plot_vertical_coordinate(eta=-1, xi=0, s=-1)
609
667
 
610
668
 
669
+ # Topography tests
670
+
671
+
672
+ def test_enclosed_regions():
673
+ """Test that there are only two connected regions, one dry and one wet."""
674
+ grid = Grid(
675
+ nx=100,
676
+ ny=100,
677
+ size_x=1800,
678
+ size_y=2400,
679
+ center_lon=30,
680
+ center_lat=61,
681
+ rot=20,
682
+ )
683
+
684
+ reg, nreg = label(grid.ds.mask_rho)
685
+ npt.assert_equal(nreg, 2)
686
+
687
+
688
+ def test_rmax_criterion():
689
+ grid = Grid(
690
+ nx=100,
691
+ ny=100,
692
+ size_x=1800,
693
+ size_y=2400,
694
+ center_lon=30,
695
+ center_lat=61,
696
+ rot=20,
697
+ )
698
+ r_eta, r_xi = _compute_rfactor(grid.ds.h)
699
+ rmax0 = np.max([r_eta.max(), r_xi.max()])
700
+ npt.assert_array_less(rmax0, 0.2)
701
+
702
+
703
+ def test_hmin_criterion_and_update_topography():
704
+ grid = Grid(
705
+ nx=100,
706
+ ny=100,
707
+ size_x=1800,
708
+ size_y=2400,
709
+ center_lon=30,
710
+ center_lat=61,
711
+ rot=20,
712
+ hmin=5.0,
713
+ )
714
+
715
+ assert grid.hmin == 5.0
716
+ assert np.less_equal(grid.hmin, grid.ds.h.min())
717
+
718
+ grid.update_topography(hmin=10.0)
719
+
720
+ assert grid.hmin == 10.0
721
+ assert np.less_equal(grid.hmin, grid.ds.h.min())
722
+
723
+ # this should not do anything
724
+ grid.update_topography()
725
+
726
+ assert grid.hmin == 10.0
727
+ assert np.less_equal(grid.hmin, grid.ds.h.min())
728
+
729
+
730
+ # Mask tests
731
+
732
+
733
+ def test_update_mask():
734
+ iceland_fjord_kwargs = {
735
+ "nx": 80,
736
+ "ny": 40,
737
+ "size_x": 40,
738
+ "size_y": 20,
739
+ "center_lon": -21.76,
740
+ "center_lat": 64.325,
741
+ "rot": 0,
742
+ "N": 3,
743
+ }
744
+
745
+ # Make sure all 4 L1 files are downloaded
746
+ _ = download_test_data("GSHHS_l_L1.dbf")
747
+ _ = download_test_data("GSHHS_l_L1.prj")
748
+ _ = download_test_data("GSHHS_l_L1.shx")
749
+ shapefile = download_test_data("GSHHS_l_L1.shp")
750
+
751
+ grid = Grid(
752
+ **iceland_fjord_kwargs,
753
+ mask_shapefile=shapefile,
754
+ )
755
+
756
+ assert grid.mask_shapefile == shapefile
757
+
758
+ # Save original mask
759
+ mask_orig = grid.ds.mask_rho.copy()
760
+
761
+ # Update mask (switches to Natural Earth)
762
+ grid.update_mask()
763
+
764
+ assert grid.mask_shapefile is None
765
+
766
+ # New mask after update
767
+ mask_new = grid.ds.mask_rho.copy()
768
+
769
+ assert abs(mask_new - mask_orig).max() == 1, (
770
+ "Mask should change after update_mask()"
771
+ )
772
+
773
+
774
+ # Boundary tests
775
+
776
+
777
+ def test_mask_topography_boundary():
778
+ """Test that the mask and topography along the grid boundaries (north, south, east,
779
+ west) are identical to the adjacent inland cells.
780
+ """
781
+ # Create a grid with some land along the northern boundary
782
+ grid = Grid(
783
+ nx=10, ny=10, size_x=1000, size_y=1000, center_lon=-20, center_lat=60, rot=0
784
+ )
785
+
786
+ # Toopography
787
+ np.testing.assert_array_equal(
788
+ grid.ds.h.isel(eta_rho=0).data, grid.ds.h.isel(eta_rho=1).data
789
+ )
790
+ np.testing.assert_array_equal(
791
+ grid.ds.h.isel(eta_rho=-1).data, grid.ds.h.isel(eta_rho=-2).data
792
+ )
793
+ np.testing.assert_array_equal(
794
+ grid.ds.h.isel(xi_rho=0).data, grid.ds.h.isel(xi_rho=1).data
795
+ )
796
+ np.testing.assert_array_equal(
797
+ grid.ds.h.isel(xi_rho=-1).data, grid.ds.h.isel(xi_rho=-2).data
798
+ )
799
+
800
+ # Mask
801
+ np.testing.assert_array_equal(
802
+ grid.ds.mask_rho.isel(eta_rho=0).data, grid.ds.mask_rho.isel(eta_rho=1).data
803
+ )
804
+ np.testing.assert_array_equal(
805
+ grid.ds.mask_rho.isel(eta_rho=-1).data, grid.ds.mask_rho.isel(eta_rho=-2).data
806
+ )
807
+ np.testing.assert_array_equal(
808
+ grid.ds.mask_rho.isel(xi_rho=0).data, grid.ds.mask_rho.isel(xi_rho=1).data
809
+ )
810
+ np.testing.assert_array_equal(
811
+ grid.ds.mask_rho.isel(xi_rho=-1).data, grid.ds.mask_rho.isel(xi_rho=-2).data
812
+ )
813
+
814
+
611
815
  # More Grid.from_file() tests
612
816
 
613
817
 
@@ -723,100 +927,3 @@ def test_from_file_partial_parameters_raises_error(grid, tmp_path):
723
927
 
724
928
  with pytest.raises(ValueError, match="must provide all of"):
725
929
  Grid.from_file(path, theta_s=5.0)
726
-
727
-
728
- # Topography tests
729
- def test_enclosed_regions():
730
- """Test that there are only two connected regions, one dry and one wet."""
731
- grid = Grid(
732
- nx=100,
733
- ny=100,
734
- size_x=1800,
735
- size_y=2400,
736
- center_lon=30,
737
- center_lat=61,
738
- rot=20,
739
- )
740
-
741
- reg, nreg = label(grid.ds.mask_rho)
742
- npt.assert_equal(nreg, 2)
743
-
744
-
745
- def test_rmax_criterion():
746
- grid = Grid(
747
- nx=100,
748
- ny=100,
749
- size_x=1800,
750
- size_y=2400,
751
- center_lon=30,
752
- center_lat=61,
753
- rot=20,
754
- )
755
- r_eta, r_xi = _compute_rfactor(grid.ds.h)
756
- rmax0 = np.max([r_eta.max(), r_xi.max()])
757
- npt.assert_array_less(rmax0, 0.2)
758
-
759
-
760
- def test_hmin_criterion():
761
- grid = Grid(
762
- nx=100,
763
- ny=100,
764
- size_x=1800,
765
- size_y=2400,
766
- center_lon=30,
767
- center_lat=61,
768
- rot=20,
769
- hmin=5.0,
770
- )
771
-
772
- assert grid.hmin == 5.0
773
- assert np.less_equal(grid.hmin, grid.ds.h.min())
774
-
775
- grid.update_topography(hmin=10.0)
776
-
777
- assert grid.hmin == 10.0
778
- assert np.less_equal(grid.hmin, grid.ds.h.min())
779
-
780
- # this should not do anything
781
- grid.update_topography()
782
-
783
- assert grid.hmin == 10.0
784
- assert np.less_equal(grid.hmin, grid.ds.h.min())
785
-
786
-
787
- def test_mask_topography_boundary():
788
- """Test that the mask and topography along the grid boundaries (north, south, east,
789
- west) are identical to the adjacent inland cells.
790
- """
791
- # Create a grid with some land along the northern boundary
792
- grid = Grid(
793
- nx=10, ny=10, size_x=1000, size_y=1000, center_lon=-20, center_lat=60, rot=0
794
- )
795
-
796
- # Toopography
797
- np.testing.assert_array_equal(
798
- grid.ds.h.isel(eta_rho=0).data, grid.ds.h.isel(eta_rho=1).data
799
- )
800
- np.testing.assert_array_equal(
801
- grid.ds.h.isel(eta_rho=-1).data, grid.ds.h.isel(eta_rho=-2).data
802
- )
803
- np.testing.assert_array_equal(
804
- grid.ds.h.isel(xi_rho=0).data, grid.ds.h.isel(xi_rho=1).data
805
- )
806
- np.testing.assert_array_equal(
807
- grid.ds.h.isel(xi_rho=-1).data, grid.ds.h.isel(xi_rho=-2).data
808
- )
809
-
810
- # Mask
811
- np.testing.assert_array_equal(
812
- grid.ds.mask_rho.isel(eta_rho=0).data, grid.ds.mask_rho.isel(eta_rho=1).data
813
- )
814
- np.testing.assert_array_equal(
815
- grid.ds.mask_rho.isel(eta_rho=-1).data, grid.ds.mask_rho.isel(eta_rho=-2).data
816
- )
817
- np.testing.assert_array_equal(
818
- grid.ds.mask_rho.isel(xi_rho=0).data, grid.ds.mask_rho.isel(xi_rho=1).data
819
- )
820
- np.testing.assert_array_equal(
821
- grid.ds.mask_rho.isel(xi_rho=-1).data, grid.ds.mask_rho.isel(xi_rho=-2).data
822
- )
@@ -11,7 +11,35 @@ import xarray as xr
11
11
  from conftest import calculate_data_hash
12
12
  from roms_tools import Grid, InitialConditions
13
13
  from roms_tools.download import download_test_data
14
- from roms_tools.setup.datasets import CESMBGCDataset, UnifiedBGCDataset
14
+ from roms_tools.setup.datasets import (
15
+ CESMBGCDataset,
16
+ UnifiedBGCDataset,
17
+ )
18
+ from roms_tools.tests.test_setup.utils import download_regional_and_bigger
19
+
20
+ try:
21
+ import copernicusmarine # type: ignore
22
+ except ImportError:
23
+ copernicusmarine = None
24
+
25
+
26
+ @pytest.fixture
27
+ def example_grid():
28
+ grid = Grid(
29
+ nx=2,
30
+ ny=2,
31
+ size_x=500,
32
+ size_y=1000,
33
+ center_lon=0,
34
+ center_lat=55,
35
+ rot=10,
36
+ N=3, # number of vertical levels
37
+ theta_s=5.0, # surface control parameter
38
+ theta_b=2.0, # bottom control parameter
39
+ hc=250.0, # critical depth
40
+ )
41
+
42
+ return grid
15
43
 
16
44
 
17
45
  @pytest.mark.parametrize(
@@ -25,7 +53,9 @@ from roms_tools.setup.datasets import CESMBGCDataset, UnifiedBGCDataset
25
53
  "initial_conditions_with_unified_bgc_from_climatology",
26
54
  ],
27
55
  )
28
- def test_initial_conditions_creation(ic_fixture, request):
56
+ def test_initial_conditions_creation_with_nondefault_glorys_dataset(
57
+ ic_fixture, request
58
+ ):
29
59
  """Test the creation of the InitialConditions object."""
30
60
  ic = request.getfixturevalue(ic_fixture)
31
61
 
@@ -37,12 +67,65 @@ def test_initial_conditions_creation(ic_fixture, request):
37
67
  }
38
68
  assert hasattr(ic.ds, "adjust_depth_for_sea_surface_height")
39
69
  assert isinstance(ic.ds, xr.Dataset)
40
- assert "temp" in ic.ds
41
- assert "salt" in ic.ds
42
- assert "u" in ic.ds
43
- assert "v" in ic.ds
44
- assert "zeta" in ic.ds
45
70
  assert ic.ds.coords["ocean_time"].attrs["units"] == "seconds"
71
+ expected_vars = {"temp", "salt", "u", "v", "zeta", "ubar", "vbar"}
72
+ assert set(ic.ds.data_vars).issuperset(expected_vars)
73
+
74
+
75
+ @pytest.mark.stream
76
+ @pytest.mark.use_copernicus
77
+ @pytest.mark.use_dask
78
+ def test_initial_conditions_creation_with_default_glorys_dataset(example_grid: Grid):
79
+ """Verify the default GLORYS dataset is loaded when a path is not provided."""
80
+ ic = InitialConditions(
81
+ grid=example_grid,
82
+ ini_time=datetime(2021, 6, 29),
83
+ source={"name": "GLORYS"},
84
+ use_dask=True,
85
+ bypass_validation=True,
86
+ )
87
+ expected_vars = {"temp", "salt", "u", "v", "zeta", "ubar", "vbar"}
88
+ assert set(ic.ds.data_vars).issuperset(expected_vars)
89
+
90
+
91
+ @pytest.mark.use_copernicus
92
+ @pytest.mark.skipif(copernicusmarine is None, reason="copernicusmarine required")
93
+ @pytest.mark.parametrize(
94
+ "grid_fixture",
95
+ [
96
+ "tiny_grid_that_straddles_dateline",
97
+ "tiny_grid_that_straddles_180_degree_meridian",
98
+ "tiny_rotated_grid",
99
+ ],
100
+ )
101
+ def test_invariance_to_get_glorys_bounds(tmp_path, grid_fixture, use_dask, request):
102
+ ini_time = datetime(2012, 1, 1)
103
+ grid = request.getfixturevalue(grid_fixture)
104
+ regional_file, bigger_regional_file = download_regional_and_bigger(
105
+ tmp_path, grid, ini_time
106
+ )
107
+
108
+ ic_from_regional = InitialConditions(
109
+ grid=grid,
110
+ source={"name": "GLORYS", "path": str(regional_file)},
111
+ ini_time=ini_time,
112
+ use_dask=use_dask,
113
+ )
114
+ ic_from_bigger_regional = InitialConditions(
115
+ grid=grid,
116
+ source={"name": "GLORYS", "path": str(bigger_regional_file)},
117
+ ini_time=ini_time,
118
+ use_dask=use_dask,
119
+ )
120
+
121
+ # Use assert_allclose instead of equals: necessary for grids that straddle the 180° meridian.
122
+ # Copernicus returns data on [-180, 180] by default, but if you request a range
123
+ # like [170, 190], it remaps longitudes. That remapping introduces tiny floating
124
+ # point differences in the longitude coordinate, which will then propagate into further differences once you do regridding.
125
+ # Need to adjust the tolerances for these grids that straddle the 180° meridian.
126
+ xr.testing.assert_allclose(
127
+ ic_from_bigger_regional.ds, ic_from_regional.ds, rtol=1e-4, atol=1e-5
128
+ )
46
129
 
47
130
 
48
131
  def test_initial_conditions_creation_with_duplicates(use_dask: bool) -> None:
@@ -67,6 +150,7 @@ def test_initial_conditions_creation_with_duplicates(use_dask: bool) -> None:
67
150
  grid=grid,
68
151
  ini_time=datetime(2012, 1, 1),
69
152
  source={"path": [fname1, fname2], "name": "GLORYS"},
153
+ allow_flex_time=True,
70
154
  use_dask=use_dask,
71
155
  )
72
156
 
@@ -74,6 +158,7 @@ def test_initial_conditions_creation_with_duplicates(use_dask: bool) -> None:
74
158
  grid=grid,
75
159
  ini_time=datetime(2012, 1, 1),
76
160
  source={"path": [fname1, fname1, fname2], "name": "GLORYS"},
161
+ allow_flex_time=True,
77
162
  use_dask=use_dask,
78
163
  )
79
164
 
@@ -134,28 +219,9 @@ def test_initial_condition_creation_with_bgc(ic_fixture, request):
134
219
  assert var in ic.ds
135
220
 
136
221
 
137
- @pytest.fixture
138
- def example_grid():
139
- grid = Grid(
140
- nx=2,
141
- ny=2,
142
- size_x=500,
143
- size_y=1000,
144
- center_lon=0,
145
- center_lat=55,
146
- rot=10,
147
- N=3, # number of vertical levels
148
- theta_s=5.0, # surface control parameter
149
- theta_b=2.0, # bottom control parameter
150
- hc=250.0, # critical depth
151
- )
152
-
153
- return grid
154
-
155
-
156
222
  # Test initialization with missing 'name' in source
157
223
  def test_initial_conditions_missing_physics_name(example_grid, use_dask):
158
- with pytest.raises(ValueError, match="`source` must include a 'name'."):
224
+ with pytest.raises(ValueError, match="`source` must include a 'name'"):
159
225
  InitialConditions(
160
226
  grid=example_grid,
161
227
  ini_time=datetime(2021, 6, 29),
@@ -164,23 +230,10 @@ def test_initial_conditions_missing_physics_name(example_grid, use_dask):
164
230
  )
165
231
 
166
232
 
167
- # Test initialization with missing 'path' in source
168
- def test_initial_conditions_missing_physics_path(example_grid, use_dask):
169
- with pytest.raises(ValueError, match="`source` must include a 'path'."):
170
- InitialConditions(
171
- grid=example_grid,
172
- ini_time=datetime(2021, 6, 29),
173
- source={"name": "GLORYS"},
174
- use_dask=use_dask,
175
- )
176
-
177
-
178
233
  # Test initialization with missing 'name' in bgc_source
179
234
  def test_initial_conditions_missing_bgc_name(example_grid, use_dask):
180
235
  fname = Path(download_test_data("GLORYS_coarse_test_data.nc"))
181
- with pytest.raises(
182
- ValueError, match="`bgc_source` must include a 'name' if it is provided."
183
- ):
236
+ with pytest.raises(ValueError, match="`bgc_source` must include a 'name'"):
184
237
  InitialConditions(
185
238
  grid=example_grid,
186
239
  ini_time=datetime(2021, 6, 29),
@@ -10,6 +10,7 @@ import xarray as xr
10
10
  from conftest import calculate_data_hash
11
11
  from roms_tools import Grid, SurfaceForcing
12
12
  from roms_tools.download import download_test_data
13
+ from roms_tools.setup.datasets import RawDataSource
13
14
 
14
15
 
15
16
  @pytest.fixture
@@ -159,7 +160,7 @@ def _test_successful_initialization(
159
160
  grid: Grid,
160
161
  start_time: datetime,
161
162
  end_time: datetime,
162
- source: dict[str, str],
163
+ source: RawDataSource,
163
164
  coarse_grid_mode: str,
164
165
  use_dask: bool,
165
166
  caplog,