roms-tools 0.0.6__py3-none-any.whl → 0.20__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.
@@ -0,0 +1,1645 @@
1
+ import pytest
2
+ from datetime import datetime
3
+ from roms_tools import Grid, AtmosphericForcing, SWRCorrection
4
+ from roms_tools.setup.datasets import download_test_data
5
+ import xarray as xr
6
+ import tempfile
7
+ import os
8
+ import pooch
9
+ import numpy as np
10
+ import textwrap
11
+
12
+
13
+ @pytest.fixture
14
+ def grid_that_straddles_dateline():
15
+ """
16
+ Fixture for creating a domain that straddles the dateline and lies within the bounds of the regional ERA5 data.
17
+ """
18
+ grid = Grid(
19
+ nx=5,
20
+ ny=5,
21
+ size_x=1800,
22
+ size_y=2400,
23
+ center_lon=-10,
24
+ center_lat=61,
25
+ rot=20,
26
+ )
27
+
28
+ return grid
29
+
30
+
31
+ @pytest.fixture
32
+ def grid_that_straddles_dateline_but_is_too_big_for_regional_test_data():
33
+ """
34
+ Fixture for creating a domain that straddles the dateline but exceeds the bounds of the regional ERA5 data.
35
+ Centered east of dateline.
36
+ """
37
+ grid = Grid(
38
+ nx=5,
39
+ ny=5,
40
+ size_x=2000,
41
+ size_y=2400,
42
+ center_lon=10,
43
+ center_lat=61,
44
+ rot=20,
45
+ )
46
+
47
+ return grid
48
+
49
+
50
+ @pytest.fixture
51
+ def another_grid_that_straddles_dateline_but_is_too_big_for_regional_test_data():
52
+ """
53
+ Fixture for creating a domain that straddles the dateline but exceeds the bounds of the regional ERA5 data.
54
+ Centered west of dateline. This one was hard to catch for the nan_check for a long time, but should work now.
55
+ """
56
+ grid = Grid(
57
+ nx=5,
58
+ ny=5,
59
+ size_x=1950,
60
+ size_y=2400,
61
+ center_lon=-30,
62
+ center_lat=61,
63
+ rot=25,
64
+ )
65
+
66
+ return grid
67
+
68
+
69
+ @pytest.fixture
70
+ def grid_that_lies_east_of_dateline_less_than_five_degrees_away():
71
+ """
72
+ Fixture for creating a domain that lies east of Greenwich meridian, but less than 5 degrees away.
73
+ We care about the 5 degree mark because it decides whether the code handles the longitudes as straddling the dateline or not.
74
+ """
75
+
76
+ grid = Grid(
77
+ nx=5,
78
+ ny=5,
79
+ size_x=500,
80
+ size_y=2000,
81
+ center_lon=10,
82
+ center_lat=61,
83
+ rot=0,
84
+ )
85
+
86
+ return grid
87
+
88
+
89
+ @pytest.fixture
90
+ def grid_that_lies_east_of_dateline_more_than_five_degrees_away():
91
+ """
92
+ Fixture for creating a domain that lies east of Greenwich meridian, more than 5 degrees away.
93
+ We care about the 5 degree mark because it decides whether the code handles the longitudes as straddling the dateline or not.
94
+ """
95
+ grid = Grid(
96
+ nx=5,
97
+ ny=5,
98
+ size_x=500,
99
+ size_y=2400,
100
+ center_lon=15,
101
+ center_lat=61,
102
+ rot=0,
103
+ )
104
+
105
+ return grid
106
+
107
+
108
+ @pytest.fixture
109
+ def grid_that_lies_west_of_dateline_less_than_five_degrees_away():
110
+ """
111
+ Fixture for creating a domain that lies west of Greenwich meridian, less than 5 degrees away.
112
+ We care about the 5 degree mark because it decides whether the code handles the longitudes as straddling the dateline or not.
113
+ """
114
+
115
+ grid = Grid(
116
+ nx=5,
117
+ ny=5,
118
+ size_x=700,
119
+ size_y=2400,
120
+ center_lon=-15,
121
+ center_lat=61,
122
+ rot=0,
123
+ )
124
+
125
+ return grid
126
+
127
+
128
+ @pytest.fixture
129
+ def grid_that_lies_west_of_dateline_more_than_five_degrees_away():
130
+ """
131
+ Fixture for creating a domain that lies west of Greenwich meridian, more than 5 degrees away.
132
+ We care about the 5 degree mark because it decides whether the code handles the longitudes as straddling the dateline or not.
133
+ """
134
+
135
+ grid = Grid(
136
+ nx=5,
137
+ ny=5,
138
+ size_x=1000,
139
+ size_y=2400,
140
+ center_lon=-25,
141
+ center_lat=61,
142
+ rot=0,
143
+ )
144
+
145
+ return grid
146
+
147
+
148
+ @pytest.fixture
149
+ def grid_that_straddles_180_degree_meridian():
150
+ """
151
+ Fixture for creating a domain that straddles 180 degree meridian. This is a good test grid for the global ERA5 data, which comes on an [-180, 180] longitude grid.
152
+ """
153
+
154
+ grid = Grid(
155
+ nx=5,
156
+ ny=5,
157
+ size_x=1800,
158
+ size_y=2400,
159
+ center_lon=180,
160
+ center_lat=61,
161
+ rot=20,
162
+ )
163
+
164
+ return grid
165
+
166
+
167
+ @pytest.mark.parametrize(
168
+ "grid_fixture",
169
+ [
170
+ "grid_that_straddles_dateline",
171
+ "grid_that_lies_east_of_dateline_less_than_five_degrees_away",
172
+ "grid_that_lies_east_of_dateline_more_than_five_degrees_away",
173
+ "grid_that_lies_west_of_dateline_less_than_five_degrees_away",
174
+ "grid_that_lies_west_of_dateline_more_than_five_degrees_away",
175
+ ],
176
+ )
177
+ def test_successful_initialization_with_regional_data(grid_fixture, request):
178
+ start_time = datetime(2020, 1, 31)
179
+ end_time = datetime(2020, 2, 2)
180
+
181
+ fname = download_test_data("ERA5_regional_test_data.nc")
182
+
183
+ grid = request.getfixturevalue(grid_fixture)
184
+
185
+ atm_forcing = AtmosphericForcing(
186
+ grid=grid,
187
+ start_time=start_time,
188
+ end_time=end_time,
189
+ source="ERA5",
190
+ filename=fname,
191
+ )
192
+
193
+ assert atm_forcing.ds is not None
194
+
195
+ grid.coarsen()
196
+
197
+ atm_forcing = AtmosphericForcing(
198
+ grid=grid,
199
+ use_coarse_grid=True,
200
+ start_time=start_time,
201
+ end_time=end_time,
202
+ source="ERA5",
203
+ filename=fname,
204
+ )
205
+
206
+ assert isinstance(atm_forcing.ds, xr.Dataset)
207
+ assert "uwnd" in atm_forcing.ds
208
+ assert "vwnd" in atm_forcing.ds
209
+ assert "swrad" in atm_forcing.ds
210
+ assert "lwrad" in atm_forcing.ds
211
+ assert "Tair" in atm_forcing.ds
212
+ assert "qair" in atm_forcing.ds
213
+ assert "rain" in atm_forcing.ds
214
+
215
+ assert atm_forcing.start_time == start_time
216
+ assert atm_forcing.end_time == end_time
217
+ assert atm_forcing.filename == fname
218
+ assert atm_forcing.source == "ERA5"
219
+
220
+
221
+ @pytest.mark.parametrize(
222
+ "grid_fixture",
223
+ [
224
+ "grid_that_straddles_dateline_but_is_too_big_for_regional_test_data",
225
+ "another_grid_that_straddles_dateline_but_is_too_big_for_regional_test_data",
226
+ ],
227
+ )
228
+ def test_nan_detection_initialization_with_regional_data(grid_fixture, request):
229
+ start_time = datetime(2020, 1, 31)
230
+ end_time = datetime(2020, 2, 2)
231
+
232
+ fname = download_test_data("ERA5_regional_test_data.nc")
233
+
234
+ grid = request.getfixturevalue(grid_fixture)
235
+
236
+ with pytest.raises(ValueError, match="NaN values found"):
237
+
238
+ AtmosphericForcing(
239
+ grid=grid,
240
+ start_time=start_time,
241
+ end_time=end_time,
242
+ source="ERA5",
243
+ filename=fname,
244
+ )
245
+
246
+ grid.coarsen()
247
+
248
+ with pytest.raises(ValueError, match="NaN values found"):
249
+ AtmosphericForcing(
250
+ grid=grid,
251
+ use_coarse_grid=True,
252
+ start_time=start_time,
253
+ end_time=end_time,
254
+ source="ERA5",
255
+ filename=fname,
256
+ )
257
+
258
+
259
+ def test_no_longitude_intersection_initialization_with_regional_data(
260
+ grid_that_straddles_180_degree_meridian,
261
+ ):
262
+ start_time = datetime(2020, 1, 31)
263
+ end_time = datetime(2020, 2, 2)
264
+
265
+ fname = download_test_data("ERA5_regional_test_data.nc")
266
+
267
+ with pytest.raises(
268
+ ValueError, match="Selected longitude range does not intersect with dataset"
269
+ ):
270
+
271
+ AtmosphericForcing(
272
+ grid=grid_that_straddles_180_degree_meridian,
273
+ start_time=start_time,
274
+ end_time=end_time,
275
+ source="ERA5",
276
+ filename=fname,
277
+ )
278
+
279
+ grid_that_straddles_180_degree_meridian.coarsen()
280
+
281
+ with pytest.raises(
282
+ ValueError, match="Selected longitude range does not intersect with dataset"
283
+ ):
284
+ AtmosphericForcing(
285
+ grid=grid_that_straddles_180_degree_meridian,
286
+ use_coarse_grid=True,
287
+ start_time=start_time,
288
+ end_time=end_time,
289
+ source="ERA5",
290
+ filename=fname,
291
+ )
292
+
293
+
294
+ @pytest.mark.parametrize(
295
+ "grid_fixture",
296
+ [
297
+ "grid_that_straddles_dateline",
298
+ "grid_that_lies_east_of_dateline_less_than_five_degrees_away",
299
+ "grid_that_lies_east_of_dateline_more_than_five_degrees_away",
300
+ "grid_that_lies_west_of_dateline_less_than_five_degrees_away",
301
+ "grid_that_lies_west_of_dateline_more_than_five_degrees_away",
302
+ "grid_that_straddles_dateline_but_is_too_big_for_regional_test_data",
303
+ "another_grid_that_straddles_dateline_but_is_too_big_for_regional_test_data",
304
+ "grid_that_straddles_180_degree_meridian",
305
+ ],
306
+ )
307
+ def test_successful_initialization_with_global_data(grid_fixture, request):
308
+ start_time = datetime(2020, 1, 31)
309
+ end_time = datetime(2020, 2, 2)
310
+
311
+ fname = download_test_data("ERA5_global_test_data.nc")
312
+
313
+ grid = request.getfixturevalue(grid_fixture)
314
+
315
+ atm_forcing = AtmosphericForcing(
316
+ grid=grid,
317
+ start_time=start_time,
318
+ end_time=end_time,
319
+ source="ERA5",
320
+ filename=fname,
321
+ )
322
+
323
+ assert isinstance(atm_forcing.ds, xr.Dataset)
324
+ assert "uwnd" in atm_forcing.ds
325
+ assert "vwnd" in atm_forcing.ds
326
+ assert "swrad" in atm_forcing.ds
327
+ assert "lwrad" in atm_forcing.ds
328
+ assert "Tair" in atm_forcing.ds
329
+ assert "qair" in atm_forcing.ds
330
+ assert "rain" in atm_forcing.ds
331
+
332
+ grid.coarsen()
333
+
334
+ atm_forcing = AtmosphericForcing(
335
+ grid=grid,
336
+ use_coarse_grid=True,
337
+ start_time=start_time,
338
+ end_time=end_time,
339
+ source="ERA5",
340
+ filename=fname,
341
+ )
342
+
343
+ assert isinstance(atm_forcing.ds, xr.Dataset)
344
+ assert "uwnd" in atm_forcing.ds
345
+ assert "vwnd" in atm_forcing.ds
346
+ assert "swrad" in atm_forcing.ds
347
+ assert "lwrad" in atm_forcing.ds
348
+ assert "Tair" in atm_forcing.ds
349
+ assert "qair" in atm_forcing.ds
350
+ assert "rain" in atm_forcing.ds
351
+
352
+ assert atm_forcing.start_time == start_time
353
+ assert atm_forcing.end_time == end_time
354
+ assert atm_forcing.filename == fname
355
+ assert atm_forcing.source == "ERA5"
356
+
357
+
358
+ @pytest.fixture
359
+ def atmospheric_forcing(grid_that_straddles_180_degree_meridian):
360
+ """
361
+ Fixture for creating a AtmosphericForcing object.
362
+ """
363
+
364
+ start_time = datetime(2020, 1, 31)
365
+ end_time = datetime(2020, 2, 2)
366
+
367
+ fname = download_test_data("ERA5_global_test_data.nc")
368
+
369
+ return AtmosphericForcing(
370
+ grid=grid_that_straddles_180_degree_meridian,
371
+ start_time=start_time,
372
+ end_time=end_time,
373
+ source="ERA5",
374
+ filename=fname,
375
+ )
376
+
377
+
378
+ @pytest.fixture
379
+ def corrected_atmospheric_forcing(grid_that_straddles_180_degree_meridian):
380
+ """
381
+ Fixture for creating a AtmosphericForcing object with shortwave radiation correction.
382
+ """
383
+ correction_filename = pooch.retrieve(
384
+ url="https://github.com/CWorthy-ocean/roms-tools-data/raw/main/SSR_correction.nc",
385
+ known_hash="a170c1698e6cc2765b3f0bb51a18c6a979bc796ac3a4c014585aeede1f1f8ea0",
386
+ )
387
+ correction = SWRCorrection(
388
+ filename=correction_filename,
389
+ varname="ssr_corr",
390
+ dim_names={
391
+ "longitude": "longitude",
392
+ "latitude": "latitude",
393
+ "time": "time",
394
+ },
395
+ temporal_resolution="climatology",
396
+ )
397
+
398
+ start_time = datetime(2020, 1, 31)
399
+ end_time = datetime(2020, 2, 2)
400
+
401
+ fname = download_test_data("ERA5_global_test_data.nc")
402
+
403
+ return AtmosphericForcing(
404
+ grid=grid_that_straddles_180_degree_meridian,
405
+ start_time=start_time,
406
+ end_time=end_time,
407
+ source="ERA5",
408
+ filename=fname,
409
+ swr_correction=correction,
410
+ )
411
+
412
+
413
+ @pytest.mark.parametrize(
414
+ "atm_forcing_fixture, expected_swrad",
415
+ [
416
+ (
417
+ "atmospheric_forcing",
418
+ [
419
+ np.array(
420
+ [
421
+ [
422
+ [
423
+ 2.5448965e01,
424
+ 8.8304184e01,
425
+ 1.5497435e02,
426
+ 2.1024680e02,
427
+ 2.8283499e02,
428
+ 2.6214133e02,
429
+ 2.4970605e02,
430
+ ],
431
+ [
432
+ 2.5577042e01,
433
+ 2.7288372e01,
434
+ 1.4094499e02,
435
+ 1.7884666e02,
436
+ 2.6748840e02,
437
+ 1.2747585e02,
438
+ 2.2650435e02,
439
+ ],
440
+ [
441
+ 1.3132906e02,
442
+ 1.3878809e02,
443
+ 1.5093468e02,
444
+ 2.1958447e02,
445
+ 1.0976684e02,
446
+ 8.2733292e01,
447
+ 1.0512259e02,
448
+ ],
449
+ [
450
+ 7.4981323e01,
451
+ 1.2092771e02,
452
+ 7.1995018e01,
453
+ 6.9545914e01,
454
+ 4.7777473e01,
455
+ 3.1707375e01,
456
+ 5.3525272e01,
457
+ ],
458
+ [
459
+ 3.9274761e01,
460
+ 3.0639046e01,
461
+ 4.3424637e01,
462
+ 2.4530054e01,
463
+ 1.6565577e01,
464
+ 1.3347603e01,
465
+ 2.5200823e01,
466
+ ],
467
+ [
468
+ 1.7271753e01,
469
+ 1.4315107e01,
470
+ 1.3658159e01,
471
+ 8.3364201e00,
472
+ 3.3492086e00,
473
+ 2.3186626e00,
474
+ 2.1239614e00,
475
+ ],
476
+ [
477
+ 1.0622320e01,
478
+ 6.2983389e00,
479
+ 1.1909541e00,
480
+ 0.0000000e00,
481
+ 0.0000000e00,
482
+ 0.0000000e00,
483
+ 0.0000000e00,
484
+ ],
485
+ ],
486
+ [
487
+ [
488
+ 4.1088528e01,
489
+ 9.1928207e01,
490
+ 2.3474031e02,
491
+ 2.4281172e02,
492
+ 2.6647174e02,
493
+ 2.4823715e02,
494
+ 2.1143315e02,
495
+ ],
496
+ [
497
+ 3.6657497e01,
498
+ 2.7476831e01,
499
+ 9.0126961e01,
500
+ 1.7404645e02,
501
+ 2.3448058e02,
502
+ 1.1212284e02,
503
+ 2.0697829e02,
504
+ ],
505
+ [
506
+ 1.4556892e02,
507
+ 1.6401329e02,
508
+ 1.5902567e02,
509
+ 2.2931277e02,
510
+ 8.7306885e01,
511
+ 7.2889664e01,
512
+ 8.1363327e01,
513
+ ],
514
+ [
515
+ 8.6130676e01,
516
+ 1.4423856e02,
517
+ 7.9196968e01,
518
+ 6.6386658e01,
519
+ 4.0398537e01,
520
+ 2.5448185e01,
521
+ 4.4373856e01,
522
+ ],
523
+ [
524
+ 6.2564697e01,
525
+ 4.2063602e01,
526
+ 5.5343834e01,
527
+ 2.2452137e01,
528
+ 1.8183729e01,
529
+ 1.0362802e01,
530
+ 1.8347992e01,
531
+ ],
532
+ [
533
+ 3.0191124e01,
534
+ 2.2519470e01,
535
+ 2.0529692e01,
536
+ 1.1819255e01,
537
+ 3.7765646e00,
538
+ 1.6394116e00,
539
+ 2.3138286e-01,
540
+ ],
541
+ [
542
+ 2.4330065e01,
543
+ 1.2172291e01,
544
+ 2.9702284e00,
545
+ 4.5030624e-02,
546
+ 0.0000000e00,
547
+ 0.0000000e00,
548
+ 0.0000000e00,
549
+ ],
550
+ ],
551
+ ],
552
+ dtype=np.float32,
553
+ )
554
+ ],
555
+ ),
556
+ (
557
+ "corrected_atmospheric_forcing",
558
+ [
559
+ np.array(
560
+ [
561
+ [
562
+ [2.0345396e01, 3.2848579e01],
563
+ [7.1687141e01, 7.4629196e01],
564
+ [1.2855386e02, 1.9472108e02],
565
+ [1.7835985e02, 2.0598582e02],
566
+ [2.4759137e02, 2.3326712e02],
567
+ [2.3055316e02, 2.1832445e02],
568
+ [2.1866821e02, 1.8515254e02],
569
+ ],
570
+ [
571
+ [1.8974031e01, 2.7193939e01],
572
+ [2.0692104e01, 2.0835009e01],
573
+ [1.1139817e02, 7.1233307e01],
574
+ [1.4908310e02, 1.4508174e02],
575
+ [2.3330269e02, 2.0451334e02],
576
+ [1.1800186e02, 1.0378988e02],
577
+ [2.1442001e02, 1.9593568e02],
578
+ ],
579
+ [
580
+ [9.5367859e01, 1.0570849e02],
581
+ [1.1730759e02, 1.3862865e02],
582
+ [1.3710544e02, 1.4445511e02],
583
+ [2.1596344e02, 2.2553131e02],
584
+ [1.1043810e02, 8.7840797e01],
585
+ [7.6220886e01, 6.7152107e01],
586
+ [9.0121040e01, 6.9752350e01],
587
+ ],
588
+ [
589
+ [7.7221687e01, 8.8704170e01],
590
+ [1.2257743e02, 1.4620630e02],
591
+ [7.8461441e01, 8.6310257e01],
592
+ [7.3550613e01, 7.0209435e01],
593
+ [4.7777473e01, 4.0398537e01],
594
+ [3.1707375e01, 2.5448185e01],
595
+ [5.4698215e01, 4.5346252e01],
596
+ ],
597
+ [
598
+ [3.9274761e01, 6.2564697e01],
599
+ [3.0638720e01, 4.2063152e01],
600
+ [4.3393879e01, 5.5304630e01],
601
+ [2.4530497e01, 2.2452543e01],
602
+ [1.6565577e01, 1.8183731e01],
603
+ [1.3347603e01, 1.0362802e01],
604
+ [2.5458445e01, 1.8535559e01],
605
+ ],
606
+ [
607
+ [1.7271362e01, 3.0190443e01],
608
+ [1.4313513e01, 2.2516962e01],
609
+ [1.3647677e01, 2.0513937e01],
610
+ [8.3360624e00, 1.1818749e01],
611
+ [3.3492086e00, 3.7765646e00],
612
+ [2.3186626e00, 1.6394116e00],
613
+ [2.1239614e00, 2.3138286e-01],
614
+ ],
615
+ [
616
+ [1.0622130e01, 2.4329630e01],
617
+ [6.2982388e00, 1.2172097e01],
618
+ [1.1909422e00, 2.9701986e00],
619
+ [0.0000000e00, 4.5030624e-02],
620
+ [0.0000000e00, 0.0000000e00],
621
+ [0.0000000e00, 0.0000000e00],
622
+ [0.0000000e00, 0.0000000e00],
623
+ ],
624
+ ],
625
+ dtype=np.float32,
626
+ )
627
+ ],
628
+ ),
629
+ ],
630
+ )
631
+ def test_atmospheric_forcing_data_consistency_plot_save(
632
+ atm_forcing_fixture, expected_swrad, request, tmp_path
633
+ ):
634
+ """
635
+ Test that the data within the AtmosphericForcing object remains consistent.
636
+ Also test plot and save methods in the same test since we dask arrays are already computed.
637
+ """
638
+ atm_forcing = request.getfixturevalue(atm_forcing_fixture)
639
+
640
+ atm_forcing.ds.load()
641
+
642
+ expected_uwnd = np.array(
643
+ [
644
+ [
645
+ [
646
+ -6.007625,
647
+ -3.42977,
648
+ 0.21806262,
649
+ 5.360945,
650
+ 11.831764,
651
+ 12.798729,
652
+ 11.391347,
653
+ ],
654
+ [
655
+ -6.8175993,
656
+ -6.792098,
657
+ -0.18560751,
658
+ 3.0563045,
659
+ 8.710696,
660
+ -9.596989,
661
+ 8.090187,
662
+ ],
663
+ [
664
+ -13.518117,
665
+ -2.7482898,
666
+ 5.891369,
667
+ 5.1779666,
668
+ -7.379195,
669
+ -0.9924428,
670
+ 2.5351613,
671
+ ],
672
+ [
673
+ -4.3629227,
674
+ -6.437724,
675
+ -13.663748,
676
+ -12.0565195,
677
+ -1.2215672,
678
+ 1.772012,
679
+ 1.8781031,
680
+ ],
681
+ [
682
+ 0.47687545,
683
+ -1.5980082,
684
+ -1.5401305,
685
+ 0.6282165,
686
+ -3.6321266,
687
+ 1.5924206,
688
+ 1.7971114,
689
+ ],
690
+ [
691
+ 0.31979027,
692
+ 0.78913784,
693
+ 0.7617095,
694
+ 2.0062523,
695
+ 3.6265984,
696
+ 1.6264036,
697
+ 3.1069543,
698
+ ],
699
+ [
700
+ -0.7392475,
701
+ 0.82420295,
702
+ 2.6924515,
703
+ 4.395664,
704
+ 5.582694,
705
+ 5.9560614,
706
+ 5.92299,
707
+ ],
708
+ ],
709
+ [
710
+ [
711
+ -6.615435,
712
+ -4.2648177,
713
+ -0.41911495,
714
+ 4.795286,
715
+ 10.781866,
716
+ 13.12497,
717
+ 11.968585,
718
+ ],
719
+ [
720
+ -6.980567,
721
+ -6.5854936,
722
+ -1.6141279,
723
+ 3.2338471,
724
+ 8.506243,
725
+ -9.9049225,
726
+ 7.0150843,
727
+ ],
728
+ [
729
+ -13.734081,
730
+ -4.05169,
731
+ 5.714791,
732
+ 4.696765,
733
+ -7.2288737,
734
+ -2.1190686,
735
+ 2.7367542,
736
+ ],
737
+ [
738
+ -4.908295,
739
+ -6.761066,
740
+ -13.2362175,
741
+ -11.991249,
742
+ -0.5408727,
743
+ 2.354013,
744
+ 2.0929167,
745
+ ],
746
+ [
747
+ 0.41402856,
748
+ -1.3379232,
749
+ -1.4920205,
750
+ 0.5738855,
751
+ -3.6704328,
752
+ 1.5384829,
753
+ 2.1192918,
754
+ ],
755
+ [
756
+ 0.30093297,
757
+ 0.971923,
758
+ 0.93931144,
759
+ 2.0213463,
760
+ 3.6705747,
761
+ 1.5487708,
762
+ 2.9228706,
763
+ ],
764
+ [
765
+ -0.54992414,
766
+ 0.91518635,
767
+ 2.853546,
768
+ 4.5043445,
769
+ 5.6799273,
770
+ 6.0500693,
771
+ 6.0166245,
772
+ ],
773
+ ],
774
+ ],
775
+ dtype=np.float32,
776
+ )
777
+
778
+ expected_vwnd = np.array(
779
+ [
780
+ [
781
+ [
782
+ 15.366679,
783
+ 16.297422,
784
+ 11.619374,
785
+ 7.6118455,
786
+ -0.17023174,
787
+ -0.70611537,
788
+ 0.918054,
789
+ ],
790
+ [
791
+ 5.8549976,
792
+ 12.26627,
793
+ 8.788221,
794
+ 0.95048386,
795
+ -3.6778722,
796
+ 2.183071,
797
+ -8.522625,
798
+ ],
799
+ [
800
+ -4.818965,
801
+ -2.2781372,
802
+ 0.78555244,
803
+ -2.8322685,
804
+ 8.696728,
805
+ -1.6022366,
806
+ -6.2568765,
807
+ ],
808
+ [
809
+ -8.705794,
810
+ -6.312195,
811
+ -8.364019,
812
+ -8.063112,
813
+ 11.151511,
814
+ -0.7562826,
815
+ -3.3305223,
816
+ ],
817
+ [
818
+ 0.5503583,
819
+ -0.5612107,
820
+ -3.0128725,
821
+ -3.231436,
822
+ -4.0373354,
823
+ -4.1747046,
824
+ -1.7400578,
825
+ ],
826
+ [
827
+ -2.0023136,
828
+ -1.1348844,
829
+ -1.0493268,
830
+ -1.3957449,
831
+ -5.500842,
832
+ -2.139104,
833
+ 0.51574695,
834
+ ],
835
+ [
836
+ -3.2449172,
837
+ -3.0666091,
838
+ -2.4833071,
839
+ -1.8068211,
840
+ -0.78668225,
841
+ 0.16977428,
842
+ 0.3107974,
843
+ ],
844
+ ],
845
+ [
846
+ [
847
+ 15.095973,
848
+ 16.516485,
849
+ 12.527047,
850
+ 7.781989,
851
+ 0.30071807,
852
+ -1.2423985,
853
+ 1.2902508,
854
+ ],
855
+ [
856
+ 4.8637633,
857
+ 12.339098,
858
+ 9.587799,
859
+ 2.498924,
860
+ -3.3265023,
861
+ 1.8309238,
862
+ -8.718385,
863
+ ],
864
+ [
865
+ -4.9639883,
866
+ -2.5414722,
867
+ 1.0753635,
868
+ -1.1787742,
869
+ 9.358983,
870
+ -1.7478623,
871
+ -5.962756,
872
+ ],
873
+ [
874
+ -8.519542,
875
+ -5.2647786,
876
+ -8.270556,
877
+ -8.179985,
878
+ 11.100576,
879
+ -0.04957591,
880
+ -3.1815748,
881
+ ],
882
+ [
883
+ 0.52395123,
884
+ -0.5520083,
885
+ -2.9008386,
886
+ -3.410807,
887
+ -4.2334967,
888
+ -3.9974525,
889
+ -1.6043161,
890
+ ],
891
+ [
892
+ -2.1034713,
893
+ -1.2443935,
894
+ -1.030919,
895
+ -1.4211166,
896
+ -5.52376,
897
+ -2.4278605,
898
+ 0.50510705,
899
+ ],
900
+ [
901
+ -3.3849669,
902
+ -3.3259943,
903
+ -2.9783287,
904
+ -1.9397556,
905
+ -1.0758145,
906
+ -0.05373563,
907
+ 0.31708276,
908
+ ],
909
+ ],
910
+ ],
911
+ dtype=np.float32,
912
+ )
913
+
914
+ expected_lwrad = np.array(
915
+ [
916
+ [
917
+ [
918
+ 349.6652,
919
+ 329.73444,
920
+ 309.8111,
921
+ 277.24893,
922
+ 269.887,
923
+ 259.9545,
924
+ 252.45737,
925
+ ],
926
+ [
927
+ 334.3932,
928
+ 324.30423,
929
+ 303.3417,
930
+ 279.31738,
931
+ 239.14821,
932
+ 268.40637,
933
+ 218.19089,
934
+ ],
935
+ [
936
+ 272.54703,
937
+ 261.52417,
938
+ 250.08763,
939
+ 211.38737,
940
+ 274.8412,
941
+ 180.4204,
942
+ 157.67314,
943
+ ],
944
+ [
945
+ 248.60202,
946
+ 238.70256,
947
+ 265.10126,
948
+ 272.1406,
949
+ 229.21912,
950
+ 204.43091,
951
+ 163.18147,
952
+ ],
953
+ [
954
+ 182.60025,
955
+ 159.89,
956
+ 168.08347,
957
+ 145.50589,
958
+ 175.71254,
959
+ 171.32625,
960
+ 145.81366,
961
+ ],
962
+ [
963
+ 167.89578,
964
+ 161.10167,
965
+ 144.19185,
966
+ 147.91838,
967
+ 174.96294,
968
+ 173.1659,
969
+ 133.38031,
970
+ ],
971
+ [
972
+ 168.20734,
973
+ 154.56114,
974
+ 139.15524,
975
+ 131.65768,
976
+ 129.26778,
977
+ 138.174,
978
+ 167.19113,
979
+ ],
980
+ ],
981
+ [
982
+ [
983
+ 349.78278,
984
+ 328.78848,
985
+ 276.55316,
986
+ 273.44193,
987
+ 266.28256,
988
+ 254.63882,
989
+ 252.37021,
990
+ ],
991
+ [
992
+ 335.17593,
993
+ 324.55026,
994
+ 305.78543,
995
+ 283.29886,
996
+ 234.72049,
997
+ 270.92914,
998
+ 210.62497,
999
+ ],
1000
+ [
1001
+ 276.69925,
1002
+ 260.20963,
1003
+ 248.46922,
1004
+ 209.37762,
1005
+ 281.7514,
1006
+ 173.16145,
1007
+ 161.8793,
1008
+ ],
1009
+ [
1010
+ 254.5296,
1011
+ 238.58257,
1012
+ 261.37173,
1013
+ 277.44806,
1014
+ 217.54973,
1015
+ 211.76616,
1016
+ 162.33136,
1017
+ ],
1018
+ [
1019
+ 176.24583,
1020
+ 155.69785,
1021
+ 164.8903,
1022
+ 158.71767,
1023
+ 170.58754,
1024
+ 174.22809,
1025
+ 148.65094,
1026
+ ],
1027
+ [
1028
+ 168.88553,
1029
+ 162.3888,
1030
+ 143.45665,
1031
+ 144.12737,
1032
+ 174.56282,
1033
+ 173.04388,
1034
+ 137.46349,
1035
+ ],
1036
+ [
1037
+ 167.90765,
1038
+ 154.07794,
1039
+ 145.49677,
1040
+ 132.35924,
1041
+ 131.83968,
1042
+ 147.98386,
1043
+ 153.403,
1044
+ ],
1045
+ ],
1046
+ ],
1047
+ dtype=np.float32,
1048
+ )
1049
+
1050
+ expected_Tair = np.array(
1051
+ [
1052
+ [
1053
+ [
1054
+ 8.382342,
1055
+ 6.532331,
1056
+ 4.761866,
1057
+ 3.7316587,
1058
+ 3.4166677,
1059
+ 3.1780987,
1060
+ 2.1949596,
1061
+ ],
1062
+ [
1063
+ 4.619903,
1064
+ 3.0633764,
1065
+ 3.041514,
1066
+ 2.3080995,
1067
+ 1.5948807,
1068
+ -1.6109427,
1069
+ -4.4116983,
1070
+ ],
1071
+ [
1072
+ -1.5634246,
1073
+ -2.5504875,
1074
+ -1.4600551,
1075
+ -0.5690303,
1076
+ -2.3495994,
1077
+ -15.370553,
1078
+ -19.28607,
1079
+ ],
1080
+ [
1081
+ -9.177512,
1082
+ -3.4015727,
1083
+ -4.4484963,
1084
+ -3.7056556,
1085
+ -10.697647,
1086
+ -18.948631,
1087
+ -23.336071,
1088
+ ],
1089
+ [
1090
+ -15.661556,
1091
+ -23.100325,
1092
+ -21.965107,
1093
+ -29.660362,
1094
+ -26.471872,
1095
+ -26.348959,
1096
+ -28.95834,
1097
+ ],
1098
+ [
1099
+ -24.605135,
1100
+ -26.762844,
1101
+ -29.470911,
1102
+ -30.107069,
1103
+ -25.887157,
1104
+ -26.812098,
1105
+ -32.926876,
1106
+ ],
1107
+ [
1108
+ -26.673147,
1109
+ -29.413385,
1110
+ -30.100338,
1111
+ -29.684294,
1112
+ -30.642694,
1113
+ -29.153072,
1114
+ -27.390156,
1115
+ ],
1116
+ ],
1117
+ [
1118
+ [
1119
+ 8.394094,
1120
+ 6.5803084,
1121
+ 4.8808966,
1122
+ 3.784057,
1123
+ 3.468052,
1124
+ 3.2407324,
1125
+ 2.3333037,
1126
+ ],
1127
+ [
1128
+ 4.6263127,
1129
+ 3.4498365,
1130
+ 3.032674,
1131
+ 2.4419715,
1132
+ 1.5665886,
1133
+ -1.6296549,
1134
+ -4.232036,
1135
+ ],
1136
+ [
1137
+ -1.5784973,
1138
+ -2.401672,
1139
+ -1.3959641,
1140
+ -0.6793834,
1141
+ -1.9613675,
1142
+ -15.220611,
1143
+ -19.080027,
1144
+ ],
1145
+ [
1146
+ -8.883304,
1147
+ -3.3834128,
1148
+ -4.6765513,
1149
+ -3.9015381,
1150
+ -10.273033,
1151
+ -18.5212,
1152
+ -23.289701,
1153
+ ],
1154
+ [
1155
+ -14.871544,
1156
+ -22.748655,
1157
+ -21.94059,
1158
+ -29.776194,
1159
+ -26.546505,
1160
+ -26.173407,
1161
+ -29.121338,
1162
+ ],
1163
+ [
1164
+ -24.285597,
1165
+ -26.681915,
1166
+ -29.44489,
1167
+ -30.269762,
1168
+ -26.018312,
1169
+ -26.81835,
1170
+ -32.84492,
1171
+ ],
1172
+ [
1173
+ -26.240913,
1174
+ -29.185444,
1175
+ -30.05311,
1176
+ -29.673147,
1177
+ -30.659044,
1178
+ -29.123781,
1179
+ -27.695673,
1180
+ ],
1181
+ ],
1182
+ ],
1183
+ dtype=np.float32,
1184
+ )
1185
+
1186
+ expected_qair = np.array(
1187
+ [
1188
+ [
1189
+ [
1190
+ 0.00608214,
1191
+ 0.00485943,
1192
+ 0.00434451,
1193
+ 0.00392352,
1194
+ 0.00350673,
1195
+ 0.00393602,
1196
+ 0.00386828,
1197
+ ],
1198
+ [
1199
+ 0.00496389,
1200
+ 0.0044483,
1201
+ 0.00362612,
1202
+ 0.00270351,
1203
+ 0.00263139,
1204
+ 0.00267393,
1205
+ 0.00188468,
1206
+ ],
1207
+ [
1208
+ 0.00218881,
1209
+ 0.00164652,
1210
+ 0.00180753,
1211
+ 0.00201019,
1212
+ 0.00222358,
1213
+ 0.00087732,
1214
+ 0.00062476,
1215
+ ],
1216
+ [
1217
+ 0.00138125,
1218
+ 0.00205246,
1219
+ 0.00235401,
1220
+ 0.00265417,
1221
+ 0.00145432,
1222
+ 0.00066223,
1223
+ 0.00043854,
1224
+ ],
1225
+ [
1226
+ 0.00082216,
1227
+ 0.00046076,
1228
+ 0.00046506,
1229
+ 0.00023305,
1230
+ 0.00031997,
1231
+ 0.00033908,
1232
+ 0.00025723,
1233
+ ],
1234
+ [
1235
+ 0.0003852,
1236
+ 0.00031066,
1237
+ 0.00024204,
1238
+ 0.00022732,
1239
+ 0.00032728,
1240
+ 0.00030786,
1241
+ 0.00018567,
1242
+ ],
1243
+ [
1244
+ 0.00032216,
1245
+ 0.00024468,
1246
+ 0.00023315,
1247
+ 0.00026326,
1248
+ 0.00022695,
1249
+ 0.00025577,
1250
+ 0.00029945,
1251
+ ],
1252
+ ],
1253
+ [
1254
+ [
1255
+ 0.00611161,
1256
+ 0.00487512,
1257
+ 0.00432123,
1258
+ 0.00410325,
1259
+ 0.00363046,
1260
+ 0.00388684,
1261
+ 0.00398353,
1262
+ ],
1263
+ [
1264
+ 0.004997,
1265
+ 0.00455964,
1266
+ 0.00391408,
1267
+ 0.00286727,
1268
+ 0.00263738,
1269
+ 0.00266042,
1270
+ 0.00188382,
1271
+ ],
1272
+ [
1273
+ 0.00221575,
1274
+ 0.00166183,
1275
+ 0.00184349,
1276
+ 0.00198757,
1277
+ 0.00228314,
1278
+ 0.00087687,
1279
+ 0.00063481,
1280
+ ],
1281
+ [
1282
+ 0.00141166,
1283
+ 0.0020337,
1284
+ 0.00229338,
1285
+ 0.00262552,
1286
+ 0.00149892,
1287
+ 0.00068138,
1288
+ 0.00043698,
1289
+ ],
1290
+ [
1291
+ 0.00085138,
1292
+ 0.00047381,
1293
+ 0.00046279,
1294
+ 0.00023133,
1295
+ 0.00031678,
1296
+ 0.00034515,
1297
+ 0.00025435,
1298
+ ],
1299
+ [
1300
+ 0.0003951,
1301
+ 0.0003132,
1302
+ 0.0002411,
1303
+ 0.00022282,
1304
+ 0.00031977,
1305
+ 0.00031098,
1306
+ 0.00018751,
1307
+ ],
1308
+ [
1309
+ 0.00033148,
1310
+ 0.00024932,
1311
+ 0.00023372,
1312
+ 0.00026297,
1313
+ 0.00022758,
1314
+ 0.00025663,
1315
+ 0.00029231,
1316
+ ],
1317
+ ],
1318
+ ],
1319
+ dtype=np.float32,
1320
+ )
1321
+
1322
+ expected_rain = np.array(
1323
+ [
1324
+ [
1325
+ [
1326
+ 3.61238503e00,
1327
+ 3.40189010e-01,
1328
+ 5.07690720e-02,
1329
+ 4.43821624e-02,
1330
+ 2.10043773e-01,
1331
+ 4.68921065e-01,
1332
+ 1.17921913e00,
1333
+ ],
1334
+ [
1335
+ 3.48412228e00,
1336
+ 3.86755776e00,
1337
+ 2.48581603e-01,
1338
+ 3.64060067e-02,
1339
+ 5.49296811e-02,
1340
+ 2.96591282e-01,
1341
+ 5.34151215e-03,
1342
+ ],
1343
+ [
1344
+ 9.73676443e-02,
1345
+ 1.11962268e-02,
1346
+ 9.25773978e-02,
1347
+ 1.36675648e-04,
1348
+ 1.21756345e-01,
1349
+ 0.00000000e00,
1350
+ 1.35925822e-02,
1351
+ ],
1352
+ [
1353
+ 7.06618875e-02,
1354
+ 3.13506752e-01,
1355
+ 4.56249267e-01,
1356
+ 1.30473804e00,
1357
+ 2.46778969e-02,
1358
+ 1.31649813e-02,
1359
+ 3.05349231e-02,
1360
+ ],
1361
+ [
1362
+ 0.00000000e00,
1363
+ 7.10712187e-03,
1364
+ 1.27204210e-02,
1365
+ 1.14610912e-02,
1366
+ 5.81587963e-02,
1367
+ 1.63536705e-02,
1368
+ 1.79725345e-02,
1369
+ ],
1370
+ [
1371
+ 2.68480852e-02,
1372
+ 2.18332373e-02,
1373
+ 1.34839285e-02,
1374
+ 8.68453179e-03,
1375
+ 2.92103793e-02,
1376
+ 2.44176220e-02,
1377
+ 4.91440296e-03,
1378
+ ],
1379
+ [
1380
+ 2.63865143e-02,
1381
+ 2.48033050e-02,
1382
+ 1.22478902e-02,
1383
+ 3.26886214e-03,
1384
+ 0.00000000e00,
1385
+ 7.88422953e-03,
1386
+ 3.26447487e-02,
1387
+ ],
1388
+ ],
1389
+ [
1390
+ [
1391
+ 5.15011311e00,
1392
+ 1.07888615e00,
1393
+ 4.82074311e-03,
1394
+ 6.01990409e-02,
1395
+ 1.50670428e-02,
1396
+ 4.35768366e-01,
1397
+ 1.39202833e00,
1398
+ ],
1399
+ [
1400
+ 3.28930092e00,
1401
+ 3.81418300e00,
1402
+ 7.94297993e-01,
1403
+ 1.15379691e-02,
1404
+ 7.38679767e-02,
1405
+ 3.54040235e-01,
1406
+ 2.69045797e-03,
1407
+ ],
1408
+ [
1409
+ 1.16046831e-01,
1410
+ 5.51193906e-03,
1411
+ 9.27464366e-02,
1412
+ 2.15600841e-02,
1413
+ 1.63438022e-01,
1414
+ 0.00000000e00,
1415
+ 1.25724524e-02,
1416
+ ],
1417
+ [
1418
+ 6.99779242e-02,
1419
+ 3.13537568e-01,
1420
+ 3.66352916e-01,
1421
+ 1.00021172e00,
1422
+ 2.27534100e-02,
1423
+ 2.78335381e-02,
1424
+ 3.07151340e-02,
1425
+ ],
1426
+ [
1427
+ 0.00000000e00,
1428
+ 6.13165740e-03,
1429
+ 1.10241473e-02,
1430
+ 1.25272367e-02,
1431
+ 4.68330160e-02,
1432
+ 2.40270998e-02,
1433
+ 1.89573225e-02,
1434
+ ],
1435
+ [
1436
+ 2.87261512e-02,
1437
+ 2.31106617e-02,
1438
+ 1.35146212e-02,
1439
+ 7.67777674e-03,
1440
+ 3.07483859e-02,
1441
+ 1.98638346e-02,
1442
+ 6.78182067e-03,
1443
+ ],
1444
+ [
1445
+ 2.84809899e-02,
1446
+ 2.73145214e-02,
1447
+ 1.30094122e-02,
1448
+ 3.26886214e-03,
1449
+ 0.00000000e00,
1450
+ 8.95222276e-03,
1451
+ 2.09490024e-02,
1452
+ ],
1453
+ ],
1454
+ ],
1455
+ dtype=np.float32,
1456
+ )
1457
+
1458
+ # Check the values in the dataset
1459
+ assert np.allclose(atm_forcing.ds["uwnd"].values, expected_uwnd)
1460
+ assert np.allclose(atm_forcing.ds["vwnd"].values, expected_vwnd)
1461
+ assert np.allclose(atm_forcing.ds["swrad"].values, expected_swrad)
1462
+ assert np.allclose(atm_forcing.ds["lwrad"].values, expected_lwrad)
1463
+ assert np.allclose(atm_forcing.ds["Tair"].values, expected_Tair)
1464
+ assert np.allclose(atm_forcing.ds["qair"].values, expected_qair)
1465
+ assert np.allclose(atm_forcing.ds["rain"].values, expected_rain)
1466
+
1467
+ atm_forcing.plot(varname="uwnd", time=0)
1468
+
1469
+ # Create a temporary file
1470
+ with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
1471
+ filepath = tmpfile.name
1472
+
1473
+ atm_forcing.save(filepath)
1474
+ extended_filepath = filepath + ".20200201-01.nc"
1475
+
1476
+ try:
1477
+ assert os.path.exists(extended_filepath)
1478
+ finally:
1479
+ os.remove(extended_filepath)
1480
+
1481
+
1482
+ @pytest.mark.parametrize(
1483
+ "atm_forcing_fixture",
1484
+ [
1485
+ "atmospheric_forcing",
1486
+ "corrected_atmospheric_forcing",
1487
+ ],
1488
+ )
1489
+ def test_roundtrip_yaml(atm_forcing_fixture, request):
1490
+ """Test that creating an AtmosphericForcing object, saving its parameters to yaml file, and re-opening yaml file creates the same object."""
1491
+
1492
+ atm_forcing = request.getfixturevalue(atm_forcing_fixture)
1493
+
1494
+ # Create a temporary file
1495
+ with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
1496
+ filepath = tmpfile.name
1497
+
1498
+ try:
1499
+ atm_forcing.to_yaml(filepath)
1500
+
1501
+ atm_forcing_from_file = AtmosphericForcing.from_yaml(filepath)
1502
+
1503
+ assert atm_forcing == atm_forcing_from_file
1504
+
1505
+ finally:
1506
+ os.remove(filepath)
1507
+
1508
+
1509
+ def test_from_yaml_missing_atmospheric_forcing():
1510
+ yaml_content = textwrap.dedent(
1511
+ """\
1512
+ ---
1513
+ roms_tools_version: 0.0.0
1514
+ ---
1515
+ Grid:
1516
+ nx: 100
1517
+ ny: 100
1518
+ size_x: 1800
1519
+ size_y: 2400
1520
+ center_lon: -10
1521
+ center_lat: 61
1522
+ rot: -20
1523
+ topography_source: ETOPO5
1524
+ smooth_factor: 8
1525
+ hmin: 5.0
1526
+ rmax: 0.2
1527
+ """
1528
+ )
1529
+
1530
+ with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
1531
+ yaml_filepath = tmp_file.name
1532
+ tmp_file.write(yaml_content.encode())
1533
+
1534
+ try:
1535
+ with pytest.raises(
1536
+ ValueError,
1537
+ match="No AtmosphericForcing configuration found in the YAML file.",
1538
+ ):
1539
+ AtmosphericForcing.from_yaml(yaml_filepath)
1540
+ finally:
1541
+ os.remove(yaml_filepath)
1542
+
1543
+
1544
+ # SWRCorrection unit checks
1545
+
1546
+
1547
+ @pytest.fixture
1548
+ def swr_correction():
1549
+
1550
+ correction_filename = pooch.retrieve(
1551
+ url="https://github.com/CWorthy-ocean/roms-tools-data/raw/main/SSR_correction.nc",
1552
+ known_hash="a170c1698e6cc2765b3f0bb51a18c6a979bc796ac3a4c014585aeede1f1f8ea0",
1553
+ )
1554
+ correction_filename
1555
+
1556
+ return SWRCorrection(
1557
+ filename=correction_filename,
1558
+ varname="ssr_corr",
1559
+ dim_names={"time": "time", "latitude": "latitude", "longitude": "longitude"},
1560
+ temporal_resolution="climatology",
1561
+ )
1562
+
1563
+
1564
+ def test_check_dataset(swr_correction):
1565
+
1566
+ ds = swr_correction.ds.copy()
1567
+ ds = ds.drop_vars("ssr_corr")
1568
+ with pytest.raises(ValueError):
1569
+ swr_correction._check_dataset(ds)
1570
+
1571
+ ds = swr_correction.ds.copy()
1572
+ ds = ds.rename({"latitude": "lat", "longitude": "long"})
1573
+ with pytest.raises(ValueError):
1574
+ swr_correction._check_dataset(ds)
1575
+
1576
+
1577
+ def test_ensure_latitude_ascending(swr_correction):
1578
+
1579
+ ds = swr_correction.ds.copy()
1580
+
1581
+ ds["latitude"] = ds["latitude"][::-1]
1582
+ ds = swr_correction._ensure_latitude_ascending(ds)
1583
+ assert np.all(np.diff(ds["latitude"]) > 0)
1584
+
1585
+
1586
+ def test_handle_longitudes(swr_correction):
1587
+ swr_correction.ds["longitude"] = (
1588
+ (swr_correction.ds["longitude"] + 180) % 360
1589
+ ) - 180 # Convert to [-180, 180]
1590
+ swr_correction._handle_longitudes(straddle=False)
1591
+ assert np.all(
1592
+ (swr_correction.ds["longitude"] >= 0) & (swr_correction.ds["longitude"] <= 360)
1593
+ )
1594
+
1595
+
1596
+ def test_choose_subdomain(swr_correction):
1597
+ lats = swr_correction.ds.latitude[10:20]
1598
+ lons = swr_correction.ds.longitude[10:20]
1599
+ coords = {"latitude": lats, "longitude": lons}
1600
+ subdomain = swr_correction._choose_subdomain(coords)
1601
+ assert (subdomain["latitude"] == lats).all()
1602
+ assert (subdomain["longitude"] == lons).all()
1603
+
1604
+
1605
+ def test_interpolate_temporally(swr_correction):
1606
+ field = swr_correction.ds["ssr_corr"]
1607
+
1608
+ fname = download_test_data("ERA5_regional_test_data.nc")
1609
+ era5_times = xr.open_dataset(fname).time
1610
+ interpolated_field = swr_correction._interpolate_temporally(field, era5_times)
1611
+ assert len(interpolated_field.time) == len(era5_times)
1612
+
1613
+
1614
+ def test_from_yaml_missing_swr_correction():
1615
+ yaml_content = textwrap.dedent(
1616
+ """\
1617
+ ---
1618
+ roms_tools_version: 0.0.0
1619
+ ---
1620
+ Grid:
1621
+ nx: 100
1622
+ ny: 100
1623
+ size_x: 1800
1624
+ size_y: 2400
1625
+ center_lon: -10
1626
+ center_lat: 61
1627
+ rot: -20
1628
+ topography_source: ETOPO5
1629
+ smooth_factor: 8
1630
+ hmin: 5.0
1631
+ rmax: 0.2
1632
+ """
1633
+ )
1634
+
1635
+ with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
1636
+ yaml_filepath = tmp_file.name
1637
+ tmp_file.write(yaml_content.encode())
1638
+
1639
+ try:
1640
+ with pytest.raises(
1641
+ ValueError, match="No SWRCorrection configuration found in the YAML file."
1642
+ ):
1643
+ SWRCorrection.from_yaml(yaml_filepath)
1644
+ finally:
1645
+ os.remove(yaml_filepath)