Skip to content

Commit 3f6a82e

Browse files
Adding 3D-advection test
1 parent ae38599 commit 3f6a82e

3 files changed

Lines changed: 19 additions & 135 deletions

File tree

parcels/_datasets/structured/generic.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,14 @@ def _unrolled_cone_curvilinear_grid():
134134
)
135135

136136

137-
def simple_UV_dataset(dims=(360, 2, 30, 4), mesh_type="spherical"):
137+
def simple_UV_dataset(dims=(360, 2, 30, 4), maxdepth=1, mesh_type="spherical"):
138138
max_lon = 180.0 if mesh_type == "spherical" else 1e6
139139

140140
return xr.Dataset(
141141
{"U": (["time", "depth", "YG", "XG"], np.zeros(dims)), "V": (["time", "depth", "YG", "XG"], np.zeros(dims))},
142142
coords={
143143
"time": (["time"], xr.date_range("2000", "2001", dims[0]), {"axis": "T"}),
144-
"depth": (["depth"], np.linspace(0, 1, dims[1]), {"axis": "Z"}),
144+
"depth": (["depth"], np.linspace(0, maxdepth, dims[1]), {"axis": "Z"}),
145145
"YC": (["YC"], np.arange(dims[2]) + 0.5, {"axis": "Y"}),
146146
"YG": (["YG"], np.arange(dims[2]), {"axis": "Y", "c_grid_axis_shift": -0.5}),
147147
"XC": (["XC"], np.arange(dims[3]) + 0.5, {"axis": "X"}),

tests/test_advection.py

Lines changed: 0 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
AdvectionRK4,
1414
AdvectionRK4_3D,
1515
AdvectionRK45,
16-
Field,
1716
FieldSet,
1817
Particle,
1918
ParticleSet,
@@ -274,119 +273,6 @@ def test_length1dimensions(u, v, w):
274273
assert ((np.array([p.depth - y0 for p in pset]) - 4 * w) < 1e-6).all()
275274

276275

277-
def truth_stationary(x_0, y_0, t):
278-
lat = y_0 - u_0 / f * (1 - math.cos(f * t))
279-
lon = x_0 + u_0 / f * math.sin(f * t)
280-
return lon, lat
281-
282-
283-
def create_fieldset_stationary(xdim=100, ydim=100, maxtime=timedelta(hours=6)):
284-
"""Generate a FieldSet encapsulating the flow field of a stationary eddy.
285-
286-
Reference: N. Fabbroni, 2009, "Numerical simulations of passive
287-
tracers dispersion in the sea"
288-
"""
289-
time = np.arange(0.0, maxtime.total_seconds() + 1e-5, 60.0, dtype=np.float64)
290-
dimensions = {
291-
"lon": np.linspace(0, 25000, xdim, dtype=np.float32),
292-
"lat": np.linspace(0, 25000, ydim, dtype=np.float32),
293-
"time": time,
294-
}
295-
data = {
296-
"U": np.transpose(np.ones((xdim, ydim, 1), dtype=np.float32) * u_0 * np.cos(f * time)),
297-
"V": np.transpose(np.ones((xdim, ydim, 1), dtype=np.float32) * -u_0 * np.sin(f * time)),
298-
}
299-
fieldset = FieldSet.from_data(data, dimensions, mesh="flat")
300-
# setting some constants for AdvectionRK45 kernel
301-
fieldset.RK45_min_dt = 1e-3
302-
fieldset.RK45_max_dt = 1e2
303-
fieldset.RK45_tol = 1e-5
304-
return fieldset
305-
306-
307-
@pytest.fixture
308-
def fieldset_stationary():
309-
return create_fieldset_stationary()
310-
311-
312-
@pytest.mark.v4alpha
313-
@pytest.mark.xfail(reason="GH1946")
314-
@pytest.mark.parametrize(
315-
"method, rtol, diffField",
316-
[
317-
("EE", 1e-2, False),
318-
("AdvDiffEM", 1e-2, True),
319-
("AdvDiffM1", 1e-2, True),
320-
("RK4", 1e-5, False),
321-
("RK45", 1e-5, False),
322-
],
323-
)
324-
def test_stationary_eddy(fieldset_stationary, method, rtol, diffField):
325-
npart = 1
326-
fieldset = fieldset_stationary
327-
if diffField:
328-
fieldset.add_field(Field("Kh_zonal", np.zeros(fieldset.U.data.shape), grid=fieldset.U.grid))
329-
fieldset.add_field(Field("Kh_meridional", np.zeros(fieldset.V.data.shape), grid=fieldset.V.grid))
330-
fieldset.add_constant("dres", 0.1)
331-
lon = np.linspace(12000, 21000, npart)
332-
lat = np.linspace(12500, 12500, npart)
333-
dt = timedelta(minutes=3).total_seconds()
334-
endtime = timedelta(hours=6).total_seconds()
335-
336-
RK45Particles = Particle.add_variable("next_dt", dtype=np.float32, initial=dt)
337-
338-
pclass = RK45Particles if method == "RK45" else Particle
339-
pset = ParticleSet(fieldset, pclass=pclass, lon=lon, lat=lat)
340-
pset.execute(kernel[method], dt=dt, endtime=endtime)
341-
342-
exp_lon = [truth_stationary(x, y, pset[0].time)[0] for x, y in zip(lon, lat, strict=True)]
343-
exp_lat = [truth_stationary(x, y, pset[0].time)[1] for x, y in zip(lon, lat, strict=True)]
344-
assert np.allclose(pset.lon, exp_lon, rtol=rtol)
345-
assert np.allclose(pset.lat, exp_lat, rtol=rtol)
346-
347-
348-
@pytest.mark.v4alpha
349-
@pytest.mark.xfail(reason="GH1946")
350-
def test_stationary_eddy_vertical():
351-
npart = 1
352-
lon = np.linspace(12000, 21000, npart)
353-
lat = np.linspace(10000, 20000, npart)
354-
depth = np.linspace(12500, 12500, npart)
355-
endtime = timedelta(hours=6).total_seconds()
356-
dt = timedelta(minutes=3).total_seconds()
357-
358-
xdim = ydim = 100
359-
lon_data = np.linspace(0, 25000, xdim, dtype=np.float32)
360-
lat_data = np.linspace(0, 25000, ydim, dtype=np.float32)
361-
time_data = np.arange(0.0, 6 * 3600 + 1e-5, 60.0, dtype=np.float64)
362-
fld1 = np.transpose(np.ones((xdim, ydim, 1), dtype=np.float32) * u_0 * np.cos(f * time_data))
363-
fld2 = np.transpose(np.ones((xdim, ydim, 1), dtype=np.float32) * -u_0 * np.sin(f * time_data))
364-
fldzero = np.transpose(np.zeros((xdim, ydim, 1), dtype=np.float32) * time_data)
365-
366-
dimensions = {"lon": lon_data, "lat": lat_data, "time": time_data}
367-
data = {"U": fld1, "V": fldzero, "W": fld2}
368-
fieldset = FieldSet.from_data(data, dimensions, mesh="flat")
369-
370-
pset = ParticleSet(fieldset, pclass=Particle, lon=lon, lat=lat, depth=depth)
371-
pset.execute(AdvectionRK4_3D, dt=dt, endtime=endtime)
372-
exp_lon = [truth_stationary(x, z, pset[0].time)[0] for x, z in zip(lon, depth, strict=True)]
373-
exp_depth = [truth_stationary(x, z, pset[0].time)[1] for x, z in zip(lon, depth, strict=True)]
374-
assert np.allclose(pset.lon, exp_lon, rtol=1e-5)
375-
assert np.allclose(pset.lat, lat, rtol=1e-5)
376-
assert np.allclose(pset.depth, exp_depth, rtol=1e-5)
377-
378-
data = {"U": fldzero, "V": fld2, "W": fld1}
379-
fieldset = FieldSet.from_data(data, dimensions, mesh="flat")
380-
381-
pset = ParticleSet(fieldset, pclass=Particle, lon=lon, lat=lat, depth=depth)
382-
pset.execute(AdvectionRK4_3D, dt=dt, endtime=endtime)
383-
exp_depth = [truth_stationary(z, y, pset[0].time)[0] for z, y in zip(depth, lat, strict=True)]
384-
exp_lat = [truth_stationary(z, y, pset[0].time)[1] for z, y in zip(depth, lat, strict=True)]
385-
assert np.allclose(pset.lon, lon, rtol=1e-5)
386-
assert np.allclose(pset.lat, exp_lat, rtol=1e-5)
387-
assert np.allclose(pset.depth, exp_depth, rtol=1e-5)
388-
389-
390276
@pytest.mark.v4alpha
391277
@pytest.mark.xfail(reason="GH1946")
392278
def test_analyticalAgrid():

tests/v4/test_advection.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,6 @@ def test_horizontal_advection_in_3D_flow(npart=10):
116116
@pytest.mark.parametrize("direction", ["up", "down"])
117117
@pytest.mark.parametrize("wErrorThroughSurface", [True, False])
118118
def test_advection_3D_outofbounds(direction, wErrorThroughSurface):
119-
# xdim = ydim = zdim = 2
120-
# dimensions = {
121-
# "lon": np.linspace(0.0, 1, xdim, dtype=np.float32),
122-
# "lat": np.linspace(0.0, 1, ydim, dtype=np.float32),
123-
# "depth": np.linspace(0.0, 1, zdim, dtype=np.float32),
124-
# }
125-
# wfac = -1.0 if direction == "up" else 1.0
126-
# data = {
127-
# "U": 0.01 * np.ones((xdim, ydim, zdim), dtype=np.float32),
128-
# "V": np.zeros((xdim, ydim, zdim), dtype=np.float32),
129-
# "W": wfac * np.ones((xdim, ydim, zdim), dtype=np.float32),
130-
# }
131-
# fieldset = FieldSet.from_data(data, dimensions, mesh="flat")
132-
133119
ds = simple_UV_dataset(mesh_type="flat")
134120
grid = XGrid.from_dataset(ds)
135121
U = Field("U", ds["U"], grid, interp_method=TriLinear)
@@ -177,7 +163,7 @@ def SubmergeParticle(particle, fieldset, time): # pragma: no cover
177163
# ("AdvDiffEM", 1e-2),
178164
# ("AdvDiffM1", 1e-2),
179165
("RK4", 1e-5),
180-
# ('RK4_3D', 1e-5),
166+
("RK4_3D", 1e-5),
181167
# ("RK45", 1e-5),
182168
],
183169
)
@@ -193,7 +179,7 @@ def truth_moving(x_0, y_0, t):
193179

194180
dt = np.timedelta64(3, "m")
195181
time = np.arange(np.timedelta64(0, "s"), np.timedelta64(7, "h"), np.timedelta64(1, "m"))
196-
ds = simple_UV_dataset(dims=(len(time), 2, 2, 2), mesh_type="flat")
182+
ds = simple_UV_dataset(dims=(len(time), 2, 2, 2), mesh_type="flat", maxdepth=25000)
197183
grid = XGrid.from_dataset(ds)
198184
for t in range(len(time)):
199185
ds["U"].data[t, :, :, :] = u_g + (u_0 - u_g) * np.cos(f * (time[t] / np.timedelta64(1, "s")))
@@ -203,15 +189,27 @@ def truth_moving(x_0, y_0, t):
203189
ds = ds.assign_coords(time=time)
204190
U = Field("U", ds["U"], grid, interp_method=BiLinear)
205191
V = Field("V", ds["V"], grid, interp_method=BiLinear)
206-
UV = VectorField("UV", U, V)
207-
fieldset = FieldSet([U, V, UV])
192+
if method == "RK4_3D":
193+
# Using W to test 3D advection (assuming same velocity as V)
194+
W = Field("W", ds["V"], grid, interp_method=TriLinear)
195+
UVW = VectorField("UVW", U, V, W)
196+
fieldset = FieldSet([U, V, W, UVW])
197+
start_depth = start_lat
198+
else:
199+
UV = VectorField("UV", U, V)
200+
fieldset = FieldSet([U, V, UV])
201+
start_depth = 0
208202

209203
RK45Particles = Particle.add_variable(Variable("next_dt", initial=dt))
210204

211205
pclass = RK45Particles if method == "RK45" else Particle
212-
pset = ParticleSet(fieldset, pclass=pclass, lon=start_lon, lat=start_lat, time=np.timedelta64(0, "s"))
206+
pset = ParticleSet(
207+
fieldset, pclass=pclass, lon=start_lon, lat=start_lat, depth=start_depth, time=np.timedelta64(0, "s")
208+
)
213209
pset.execute(kernel[method], dt=dt, endtime=np.timedelta64(6, "h"))
214210

215211
exp_lon, exp_lat = truth_moving(start_lon, start_lat, pset.time[0])
216212
assert np.allclose(pset.lon_nextloop, exp_lon, rtol=rtol)
217213
assert np.allclose(pset.lat_nextloop, exp_lat, rtol=rtol)
214+
if method == "RK4_3D":
215+
assert np.allclose(pset.depth_nextloop, exp_lat, rtol=rtol)

0 commit comments

Comments
 (0)