Skip to content

Commit ed9abda

Browse files
Moving more diffusion unit tests from v3 to v4
1 parent 01dbffa commit ed9abda

3 files changed

Lines changed: 50 additions & 49 deletions

File tree

parcels/application_kernels/advectiondiffusion.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ def AdvectionDiffusionM1(particle, fieldset, time): # pragma: no cover
2424
The Wiener increment `dW` is normally distributed with zero
2525
mean and a standard deviation of sqrt(dt).
2626
"""
27+
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
2728
# Wiener increment with zero mean and std of sqrt(dt)
28-
dWx = random.normalvariate(0, math.sqrt(math.fabs(particle.dt)))
29-
dWy = random.normalvariate(0, math.sqrt(math.fabs(particle.dt)))
29+
dWx = random.normalvariate(0, math.sqrt(math.fabs(dt)))
30+
dWy = random.normalvariate(0, math.sqrt(math.fabs(dt)))
3031

3132
Kxp1 = fieldset.Kh_zonal[time, particle.depth, particle.lat, particle.lon + fieldset.dres]
3233
Kxm1 = fieldset.Kh_zonal[time, particle.depth, particle.lat, particle.lon - fieldset.dres]
@@ -42,8 +43,8 @@ def AdvectionDiffusionM1(particle, fieldset, time): # pragma: no cover
4243
by = math.sqrt(2 * fieldset.Kh_meridional[time, particle.depth, particle.lat, particle.lon])
4344

4445
# Particle positions are updated only after evaluating all terms.
45-
particle_dlon += u * particle.dt + 0.5 * dKdx * (dWx**2 + particle.dt) + bx * dWx # noqa
46-
particle_dlat += v * particle.dt + 0.5 * dKdy * (dWy**2 + particle.dt) + by * dWy # noqa
46+
particle_dlon += u * dt + 0.5 * dKdx * (dWx**2 + dt) + bx * dWx # noqa
47+
particle_dlat += v * dt + 0.5 * dKdy * (dWy**2 + dt) + by * dWy # noqa
4748

4849

4950
def AdvectionDiffusionEM(particle, fieldset, time): # pragma: no cover
@@ -59,9 +60,10 @@ def AdvectionDiffusionEM(particle, fieldset, time): # pragma: no cover
5960
The Wiener increment `dW` is normally distributed with zero
6061
mean and a standard deviation of sqrt(dt).
6162
"""
63+
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
6264
# Wiener increment with zero mean and std of sqrt(dt)
63-
dWx = random.normalvariate(0, math.sqrt(math.fabs(particle.dt)))
64-
dWy = random.normalvariate(0, math.sqrt(math.fabs(particle.dt)))
65+
dWx = random.normalvariate(0, math.sqrt(math.fabs(dt)))
66+
dWy = random.normalvariate(0, math.sqrt(math.fabs(dt)))
6567

6668
u, v = fieldset.UV[time, particle.depth, particle.lat, particle.lon]
6769

@@ -78,8 +80,8 @@ def AdvectionDiffusionEM(particle, fieldset, time): # pragma: no cover
7880
by = math.sqrt(2 * fieldset.Kh_meridional[time, particle.depth, particle.lat, particle.lon])
7981

8082
# Particle positions are updated only after evaluating all terms.
81-
particle_dlon += ax * particle.dt + bx * dWx # noqa
82-
particle_dlat += ay * particle.dt + by * dWy # noqa
83+
particle_dlon += ax * dt + bx * dWx # noqa
84+
particle_dlat += ay * dt + by * dWy # noqa
8385

8486

8587
def DiffusionUniformKh(particle, fieldset, time): # pragma: no cover

tests/test_diffusion.py

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,16 @@
11
import random
2-
from datetime import timedelta
32

43
import numpy as np
54
import pytest
65
from scipy import stats
76

87
from parcels import (
9-
AdvectionDiffusionEM,
10-
AdvectionDiffusionM1,
11-
Field,
128
Particle,
139
ParticleSet,
14-
RectilinearZGrid,
1510
)
1611
from tests.utils import create_fieldset_zeros_conversion
1712

1813

19-
@pytest.mark.v4alpha
20-
@pytest.mark.xfail(reason="GH1946")
21-
@pytest.mark.parametrize("mesh", ["spherical", "flat"])
22-
@pytest.mark.parametrize("kernel", [AdvectionDiffusionM1, AdvectionDiffusionEM])
23-
def test_fieldKh_SpatiallyVaryingDiffusion(mesh, kernel):
24-
"""Test advection-diffusion kernels on a non-uniform diffusivity field with a linear gradient in one direction."""
25-
xdim = 200
26-
ydim = 100
27-
mesh_conversion = 1 / 1852.0 / 60 if mesh == "spherical" else 1
28-
fieldset = create_fieldset_zeros_conversion(mesh=mesh, xdim=xdim, ydim=ydim, mesh_conversion=mesh_conversion)
29-
30-
Kh = np.zeros((ydim, xdim), dtype=np.float32)
31-
for x in range(xdim):
32-
Kh[:, x] = np.tanh(fieldset.U.lon[x] / fieldset.U.lon[-1] * 10.0) * xdim / 2.0 + xdim / 2.0 + 100.0
33-
34-
grid = RectilinearZGrid(lon=fieldset.U.lon, lat=fieldset.U.lat, mesh=mesh)
35-
fieldset.add_field(Field("Kh_zonal", Kh, grid=grid))
36-
fieldset.add_field(Field("Kh_meridional", Kh, grid=grid))
37-
fieldset.add_constant("dres", fieldset.U.lon[1] - fieldset.U.lon[0])
38-
39-
npart = 100
40-
runtime = timedelta(days=1)
41-
42-
random.seed(1636)
43-
pset = ParticleSet(fieldset=fieldset, pclass=Particle, lon=np.zeros(npart), lat=np.zeros(npart))
44-
pset.execute(pset.Kernel(kernel), runtime=runtime, dt=timedelta(hours=1))
45-
46-
lats = pset.lat
47-
lons = pset.lon
48-
tol = 2000 * mesh_conversion # effectively 2000 m errors (because of low numbers of particles)
49-
assert np.allclose(np.mean(lons), 0, atol=tol)
50-
assert np.allclose(np.mean(lats), 0, atol=tol)
51-
assert stats.skew(lons) > stats.skew(lats)
52-
53-
5414
@pytest.mark.v4alpha
5515
@pytest.mark.xfail(reason="GH1946")
5616
@pytest.mark.parametrize("lambd", [1, 5])

tests/v4/test_diffusion.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import numpy as np
44
import pytest
5+
from scipy import stats
56

67
from parcels._datasets.structured.generic import simple_UV_dataset
7-
from parcels.application_kernels import DiffusionUniformKh
8+
from parcels.application_kernels import AdvectionDiffusionEM, AdvectionDiffusionM1, DiffusionUniformKh
89
from parcels.field import Field, VectorField
910
from parcels.fieldset import FieldSet
1011
from parcels.particleset import ParticleSet
@@ -71,3 +72,41 @@ def test_fieldKh_Brownian(mesh_type):
7172
assert np.allclose(np.std(pset.lon), expected_std_lon, atol=tol)
7273
assert np.allclose(np.mean(pset.lon), 0, atol=tol)
7374
assert np.allclose(np.mean(pset.lat), 0, atol=tol)
75+
76+
77+
@pytest.mark.parametrize("mesh_type", ["spherical", "flat"])
78+
@pytest.mark.parametrize("kernel", [AdvectionDiffusionM1, AdvectionDiffusionEM])
79+
def test_fieldKh_SpatiallyVaryingDiffusion(mesh_type, kernel):
80+
"""Test advection-diffusion kernels on a non-uniform diffusivity field with a linear gradient in one direction."""
81+
ydim, xdim = 100, 200
82+
83+
mesh_conversion = 1 / 1852.0 / 60 if mesh_type == "spherical" else 1
84+
ds = simple_UV_dataset(dims=(2, 1, ydim, xdim), mesh_type=mesh_type)
85+
ds["lon"].data = np.linspace(-1e6, 1e6, xdim)
86+
ds["lat"].data = np.linspace(-1e6, 1e6, ydim)
87+
grid = XGrid.from_dataset(ds)
88+
U = Field("U", ds["U"], grid, mesh_type=mesh_type, interp_method=BiLinear)
89+
V = Field("V", ds["V"], grid, mesh_type=mesh_type, interp_method=BiLinear)
90+
91+
Kh = np.zeros((ydim, xdim), dtype=np.float32)
92+
for x in range(xdim):
93+
Kh[:, x] = np.tanh(ds["lon"][x] / ds["lon"][-1] * 10.0) * xdim / 2.0 + xdim / 2.0 + 100.0
94+
95+
ds["Kh_zonal"] = (["time", "depth", "YG", "XG"], np.full((2, 1, ydim, xdim), Kh))
96+
ds["Kh_meridional"] = (["time", "depth", "YG", "XG"], np.full((2, 1, ydim, xdim), Kh))
97+
Kh_zonal = Field("Kh_zonal", ds["Kh_zonal"], grid=grid, mesh_type=mesh_type, interp_method=BiLinear)
98+
Kh_meridional = Field("Kh_meridional", ds["Kh_meridional"], grid=grid, mesh_type=mesh_type, interp_method=BiLinear)
99+
UV = VectorField("UV", U, V)
100+
fieldset = FieldSet([U, V, UV, Kh_zonal, Kh_meridional])
101+
fieldset.add_constant("dres", ds["lon"][1] - ds["lon"][0])
102+
103+
npart = 100
104+
105+
random.seed(1636)
106+
pset = ParticleSet(fieldset=fieldset, lon=np.zeros(npart), lat=np.zeros(npart))
107+
pset.execute(pset.Kernel(kernel), runtime=np.timedelta64(4, "h"), dt=np.timedelta64(1, "h"))
108+
109+
tol = 2000 * mesh_conversion # effectively 2000 m errors (because of low numbers of particles)
110+
assert np.allclose(np.mean(pset.lon), 0, atol=tol)
111+
assert np.allclose(np.mean(pset.lat), 0, atol=tol)
112+
assert stats.skew(pset.lon) > stats.skew(pset.lat)

0 commit comments

Comments
 (0)