roms-tools 3.1.1__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.
- roms_tools/__init__.py +8 -1
- roms_tools/analysis/cdr_analysis.py +203 -0
- roms_tools/analysis/cdr_ensemble.py +198 -0
- roms_tools/analysis/roms_output.py +80 -46
- roms_tools/data/grids/GLORYS_global_grid.nc +0 -0
- roms_tools/download.py +4 -0
- roms_tools/plot.py +131 -30
- roms_tools/regrid.py +6 -1
- roms_tools/setup/boundary_forcing.py +94 -44
- roms_tools/setup/cdr_forcing.py +123 -15
- roms_tools/setup/cdr_release.py +161 -8
- roms_tools/setup/datasets.py +709 -341
- roms_tools/setup/grid.py +167 -139
- roms_tools/setup/initial_conditions.py +113 -48
- roms_tools/setup/mask.py +63 -7
- roms_tools/setup/nesting.py +67 -42
- roms_tools/setup/river_forcing.py +45 -19
- roms_tools/setup/surface_forcing.py +16 -10
- roms_tools/setup/tides.py +1 -2
- roms_tools/setup/topography.py +4 -4
- roms_tools/setup/utils.py +134 -22
- roms_tools/tests/test_analysis/test_cdr_analysis.py +144 -0
- roms_tools/tests/test_analysis/test_cdr_ensemble.py +202 -0
- roms_tools/tests/test_analysis/test_roms_output.py +61 -3
- roms_tools/tests/test_setup/test_boundary_forcing.py +111 -52
- roms_tools/tests/test_setup/test_cdr_forcing.py +54 -0
- roms_tools/tests/test_setup/test_cdr_release.py +118 -1
- roms_tools/tests/test_setup/test_datasets.py +458 -34
- roms_tools/tests/test_setup/test_grid.py +238 -121
- roms_tools/tests/test_setup/test_initial_conditions.py +94 -41
- roms_tools/tests/test_setup/test_surface_forcing.py +28 -3
- roms_tools/tests/test_setup/test_utils.py +91 -1
- roms_tools/tests/test_setup/test_validation.py +21 -15
- roms_tools/tests/test_setup/utils.py +71 -0
- roms_tools/tests/test_tiling/test_join.py +241 -0
- roms_tools/tests/test_tiling/test_partition.py +45 -0
- roms_tools/tests/test_utils.py +224 -2
- roms_tools/tiling/join.py +189 -0
- roms_tools/tiling/partition.py +44 -30
- roms_tools/utils.py +488 -161
- {roms_tools-3.1.1.dist-info → roms_tools-3.2.0.dist-info}/METADATA +15 -4
- {roms_tools-3.1.1.dist-info → roms_tools-3.2.0.dist-info}/RECORD +45 -37
- {roms_tools-3.1.1.dist-info → roms_tools-3.2.0.dist-info}/WHEEL +0 -0
- {roms_tools-3.1.1.dist-info → roms_tools-3.2.0.dist-info}/licenses/LICENSE +0 -0
- {roms_tools-3.1.1.dist-info → roms_tools-3.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from datetime import datetime
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
2
|
from unittest import mock
|
|
3
3
|
|
|
4
|
+
import numpy as np
|
|
4
5
|
import pytest
|
|
5
6
|
from pydantic import ValidationError
|
|
6
7
|
|
|
@@ -377,3 +378,119 @@ class TestTracerPerturbation:
|
|
|
377
378
|
def test_get_tracer_metadata(self):
|
|
378
379
|
d = VolumeRelease.get_tracer_metadata()
|
|
379
380
|
assert len(d) == NUM_TRACERS
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
class TestReleaseAccounting:
|
|
384
|
+
def setup_method(self):
|
|
385
|
+
self.start = datetime(2020, 1, 1)
|
|
386
|
+
self.end = datetime(2020, 1, 11) # 10 days later
|
|
387
|
+
|
|
388
|
+
def make_release(self, release_type, fluxes=None, concentrations=None, times=None):
|
|
389
|
+
times = times or [self.start, self.end]
|
|
390
|
+
|
|
391
|
+
if release_type == "volume":
|
|
392
|
+
vr = VolumeRelease(
|
|
393
|
+
name="test",
|
|
394
|
+
lat=0.0,
|
|
395
|
+
lon=0.0,
|
|
396
|
+
depth=100.0,
|
|
397
|
+
times=times,
|
|
398
|
+
volume_fluxes=Flux("volume", fluxes),
|
|
399
|
+
tracer_concentrations={"DIC": Concentration("DIC", concentrations)},
|
|
400
|
+
)
|
|
401
|
+
vr._extend_to_endpoints(self.start, self.end)
|
|
402
|
+
return vr
|
|
403
|
+
|
|
404
|
+
elif release_type == "tracer":
|
|
405
|
+
tp = TracerPerturbation(
|
|
406
|
+
name="test",
|
|
407
|
+
lat=0.0,
|
|
408
|
+
lon=0.0,
|
|
409
|
+
depth=100.0,
|
|
410
|
+
times=times,
|
|
411
|
+
tracer_fluxes={"DIC": Flux("DIC", fluxes)},
|
|
412
|
+
)
|
|
413
|
+
tp._extend_to_endpoints(self.start, self.end)
|
|
414
|
+
return tp
|
|
415
|
+
else:
|
|
416
|
+
raise ValueError(f"Unknown release type {release_type}")
|
|
417
|
+
|
|
418
|
+
@pytest.mark.parametrize("release_type", ["volume", "tracer"])
|
|
419
|
+
def test_constant_flux(self, release_type):
|
|
420
|
+
"""Case 0: constant fluxes."""
|
|
421
|
+
flux = 2.0
|
|
422
|
+
conc = 3.0 # only used for VolumeRelease
|
|
423
|
+
vr = self.make_release(release_type, fluxes=flux, concentrations=conc)
|
|
424
|
+
roms_stamps = np.array([0.0, 5.0, 10.0])
|
|
425
|
+
result = vr._do_accounting(roms_stamps, self.start)
|
|
426
|
+
|
|
427
|
+
expected = flux * (roms_stamps[-1] - roms_stamps[0])
|
|
428
|
+
if release_type == "volume":
|
|
429
|
+
expected *= conc
|
|
430
|
+
|
|
431
|
+
assert result["DIC"] == pytest.approx(expected)
|
|
432
|
+
|
|
433
|
+
@pytest.mark.parametrize("release_type", ["volume", "tracer"])
|
|
434
|
+
def test_aligned_releases(self, release_type):
|
|
435
|
+
"""Case 1: release times exactly aligned with ROMS stamps."""
|
|
436
|
+
# ROMS time step: 5 days (we want to cover the simulation time of 10 days exactly)
|
|
437
|
+
roms = np.array([0.0, 5.0, 10.0]) * 24 * 3600 # seconds
|
|
438
|
+
release_times = [self.start + timedelta(seconds=s) for s in roms]
|
|
439
|
+
fluxes = [1, 3, 5]
|
|
440
|
+
concs = [1, 2, 1]
|
|
441
|
+
|
|
442
|
+
if release_type == "volume":
|
|
443
|
+
r = self.make_release(
|
|
444
|
+
release_type, fluxes=fluxes, concentrations=concs, times=release_times
|
|
445
|
+
)
|
|
446
|
+
elif release_type == "tracer":
|
|
447
|
+
r = self.make_release(release_type, fluxes=fluxes, times=release_times)
|
|
448
|
+
|
|
449
|
+
# Expected calculation
|
|
450
|
+
series = (
|
|
451
|
+
np.array([f * c for f, c in zip(fluxes, concs)])
|
|
452
|
+
if release_type == "volume"
|
|
453
|
+
else np.array(fluxes)
|
|
454
|
+
)
|
|
455
|
+
dt = np.diff(roms)
|
|
456
|
+
expected = np.sum(series[:-1] * dt)
|
|
457
|
+
|
|
458
|
+
result = r._do_accounting(roms, self.start)
|
|
459
|
+
assert result["DIC"] == pytest.approx(expected)
|
|
460
|
+
|
|
461
|
+
@pytest.mark.parametrize("release_type", ["volume", "tracer"])
|
|
462
|
+
def test_unaligned_releases(self, release_type):
|
|
463
|
+
"""Case 2: release times unaligned with ROMS stamps."""
|
|
464
|
+
# ROMS time step: 5 days (we want to cover the simulation time of 10 days exactly)
|
|
465
|
+
roms = np.array([0.0, 2.5, 5.0, 7.5, 10.0]) * 24 * 3600 # seconds
|
|
466
|
+
rel_release_times = np.array([0.0, 1.0, 3.0, 6.0, 10.0]) * 24 * 3600 # seconds
|
|
467
|
+
release_times = [self.start + timedelta(seconds=s) for s in rel_release_times]
|
|
468
|
+
fluxes = [1, 2, 3, 4, 5]
|
|
469
|
+
concs = [1, 1.5, 2, 2.5, 1]
|
|
470
|
+
|
|
471
|
+
if release_type == "volume":
|
|
472
|
+
vr = self.make_release(
|
|
473
|
+
release_type, fluxes=fluxes, concentrations=concs, times=release_times
|
|
474
|
+
)
|
|
475
|
+
elif release_type == "tracer":
|
|
476
|
+
vr = self.make_release(release_type, fluxes=fluxes, times=release_times)
|
|
477
|
+
|
|
478
|
+
# Expected calculation
|
|
479
|
+
series = (
|
|
480
|
+
np.array([f * c for f, c in zip(fluxes, concs)])
|
|
481
|
+
if release_type == "volume"
|
|
482
|
+
else np.array(fluxes)
|
|
483
|
+
)
|
|
484
|
+
interp = np.interp(roms, rel_release_times, series)
|
|
485
|
+
dt = np.diff(roms)
|
|
486
|
+
expected = np.sum(interp[:-1] * dt)
|
|
487
|
+
|
|
488
|
+
result = vr._do_accounting(roms, self.start)
|
|
489
|
+
assert result["DIC"] == pytest.approx(expected)
|
|
490
|
+
|
|
491
|
+
@pytest.mark.parametrize("release_type", ["volume", "tracer"])
|
|
492
|
+
def test_raises_with_single_roms_timestamp(self, release_type):
|
|
493
|
+
vr = self.make_release(release_type, fluxes=1.0, concentrations=1.0)
|
|
494
|
+
roms_stamps = np.array([0.0])
|
|
495
|
+
with pytest.raises(ValueError, match="at least two ROMS time stamps"):
|
|
496
|
+
vr._do_accounting(roms_stamps, self.start)
|