Skip to content

Commit 9e6b09e

Browse files
Merge pull request #7 from OceanParcels/benchmark-MOi_curvilinear
Benchmark MOi curvilinear
2 parents f4480ae + 89dc3dc commit 9e6b09e

1 file changed

Lines changed: 189 additions & 0 deletions

File tree

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
from argparse import ArgumentParser
2+
from pathlib import Path
3+
import xarray as xr
4+
import tracemalloc
5+
import time
6+
7+
from glob import glob
8+
9+
import numpy as np
10+
11+
import parcels
12+
13+
runtime = np.timedelta64(2, "D")
14+
dt = np.timedelta64(15, "m")
15+
16+
parcelsv4 = True
17+
try:
18+
import xgcm
19+
from parcels.interpolators import XLinear
20+
except ImportError:
21+
parcelsv4 = False
22+
23+
DATA_ROOT = "/storage/shared/oceanparcels/input_data/MOi"
24+
25+
def run_benchmark(interpolator: str, trace_memory: bool = False, surface_simulation: bool =False, preload: bool = False, cycle_chunks: bool = False):
26+
27+
lon0_expected, lat0_expected = -10.128929, -29.721205 # values from v3 using from_netcf (so assuming A-grid!)
28+
29+
if cycle_chunks:
30+
xy_chunks = [64, 128, 256, 512, 1024, 2084, 32, 16, 8, 4]
31+
nparts = [10_000]
32+
else:
33+
xy_chunks = [256]
34+
nparts = [1, 10, 100, 1_000, 5_000, 10_000, 50_000, 100_000, 500_000, 1_000_000]
35+
fileU = f"{DATA_ROOT}/GLO12/psy4v3r1-daily_U_2010-01-0[1-3].nc"
36+
filenames = {"U": glob(fileU), "V": glob(fileU.replace("_U_", "_V_")), "W": glob(fileU.replace("_U_", "_W_"))}
37+
mesh_mask = f"{DATA_ROOT}/domain_ORCA0083-N006/PSY4V3R1_mesh_hgr.nc"
38+
39+
for chunk in xy_chunks:
40+
if parcelsv4:
41+
if interpolator == "XLinear":
42+
interp_method = XLinear
43+
else:
44+
raise ValueError(f"Unknown interpolator: {interpolator}")
45+
46+
fileargs = {"concat_dim": "time_counter",
47+
"combine": "nested",
48+
"data_vars": 'minimal',
49+
"coords": 'minimal',
50+
"compat": 'override',
51+
}
52+
if chunk:
53+
fileargs["chunks"] = {"time_counter": 1, "depth":2, "y": chunk, "x": chunk}
54+
55+
ds_u = xr.open_mfdataset(filenames["U"], **fileargs)[["vozocrtx"]].drop_vars(["nav_lon", "nav_lat"])
56+
ds_v = xr.open_mfdataset(filenames["V"], **fileargs)[["vomecrty"]].drop_vars(["nav_lon", "nav_lat"])
57+
ds_depth = xr.open_mfdataset(filenames["W"], **fileargs)[["depthw"]]
58+
ds_mesh = xr.open_dataset(mesh_mask)[["glamf", "gphif"]].isel(t=0)
59+
60+
ds = xr.merge([ds_u, ds_v, ds_depth, ds_mesh], compat="identical").rename({"vozocrtx": "U", "vomecrty": "V"}).rename({"glamf": "lon", "gphif": "lat", "time_counter": "time", "depthw": "depth"})
61+
ds = xr.merge([ds_u, ds_v, ds_depth, ds_mesh], compat="identical")
62+
ds = ds.rename({"vozocrtx": "U", "vomecrty": "V", "glamf": "lon", "gphif": "lat", "time_counter": "time", "depthw": "depth"})
63+
ds.deptht.attrs["c_grid_axis_shift"] = -0.5
64+
65+
coords={"X": {"left": "x"}, "Y": {"left": "y"}, "T": {"center": "time"}}
66+
67+
coords={
68+
"X": {"left": "x"},
69+
"Y": {"left": "y"},
70+
"T": {"center": "time"},
71+
}
72+
if surface_simulation:
73+
ds = ds.isel(depth=0, deptht=0)
74+
else:
75+
coords["Z"] = {"center": "deptht", "left": "depth"}
76+
print(ds)
77+
78+
grid = parcels._core.xgrid.XGrid(xgcm.Grid(ds, coords=coords, autoparse_metadata=False, periodic=False), mesh="spherical")
79+
80+
U = parcels.Field("U", ds["U"], grid, interp_method=interp_method)
81+
V = parcels.Field("V", ds["V"], grid, interp_method=interp_method)
82+
U.units = parcels.GeographicPolar()
83+
V.units = parcels.Geographic()
84+
UV = parcels.VectorField("UV", U, V)
85+
86+
fieldset = parcels.FieldSet([U, V, UV])
87+
else:
88+
filenames = {
89+
"U": {"lon": mesh_mask, "lat": mesh_mask, "depth": filenames["W"][0], "data": filenames["U"]},
90+
"V": {"lon": mesh_mask, "lat": mesh_mask, "depth": filenames["W"][0], "data": filenames["V"]},
91+
}
92+
interpolator = "v3_default"
93+
if surface_simulation:
94+
indices={"depth": range(2)}
95+
else:
96+
indices=None
97+
98+
fieldset = parcels.FieldSet.from_netcdf(
99+
filenames,
100+
variables={"U": "vozocrtx", "V": "vomecrty"},
101+
dimensions={"time": "time_counter", "lat": "gphif", "lon": "glamf", "depth": "depthw"},
102+
indices=indices,
103+
)
104+
105+
pclass = parcels.Particle if parcelsv4 else parcels.JITParticle
106+
107+
if parcelsv4 and preload:
108+
fieldset.U.data.load()
109+
fieldset.V.data.load()
110+
111+
for npart in nparts:
112+
if cycle_chunks:
113+
X, Y = np.meshgrid(np.linspace(-10, 10, int(np.sqrt(npart))), np.linspace(-30, -20, int(np.sqrt(npart))))
114+
lon = X.flatten()
115+
lat = Y.flatten()
116+
else:
117+
lon = np.linspace(-10, 10, npart)
118+
lat = np.linspace(-30, -20, npart)
119+
120+
pset = parcels.ParticleSet(fieldset=fieldset, pclass=pclass, lon=lon, lat=lat)
121+
122+
print(f"Running {len(lon):_} particles on {"surface" if surface_simulation else "3D"} with parcels v{4 if parcelsv4 else 3}, chunksize {chunk} ({'preloaded' if preload else 'not preloaded'}) and {interpolator} interpolator")
123+
124+
if trace_memory:
125+
tracemalloc.start()
126+
else:
127+
start = time.time()
128+
129+
pset.execute(parcels.kernels.AdvectionEE, runtime=runtime, dt=dt, verbose_progress=False)
130+
131+
if trace_memory:
132+
current, peak = tracemalloc.get_traced_memory()
133+
tracemalloc.stop()
134+
print(f"Memory usage: current={current / 1e6:.0f} MB, peak={peak / 1e6:.0f} MB")
135+
else:
136+
elapsed_time = time.time() - start
137+
print(f"Execution time: {elapsed_time:.0f} seconds")
138+
139+
print("")
140+
141+
if not cycle_chunks:
142+
assert np.allclose(pset[0].lon, lon0_expected, atol=1e-5), f"Expected lon {lon0_expected}, got {pset[0].lon}"
143+
assert np.allclose(pset[0].lat, lat0_expected, atol=1e-5), f"Expected lat {lat0_expected}, got {pset[0].lat}"
144+
145+
146+
def main(args=None):
147+
p = ArgumentParser()
148+
149+
p.add_argument(
150+
"-i",
151+
"--Interpolator",
152+
choices=("XLinear", "BiRectiLinear", "PureXarrayInterp", "NoFieldAccess"),
153+
default="XLinear",
154+
)
155+
156+
p.add_argument(
157+
"-m",
158+
"--memory",
159+
action="store_true",
160+
help="Enable memory tracing (default: False)",
161+
)
162+
163+
p.add_argument(
164+
"-s",
165+
"--surface",
166+
action="store_true",
167+
help="Run surface simulation with only 1 or 2 depth levels (default: False)",
168+
)
169+
170+
p.add_argument(
171+
"-l",
172+
"--preload",
173+
action="store_true",
174+
help="Preload data into memory (default: False)",
175+
)
176+
177+
p.add_argument(
178+
"-c",
179+
"--chunks",
180+
action="store_true",
181+
help="Cycle through different chunk sizes (default: False)",
182+
)
183+
184+
args = p.parse_args(args)
185+
run_benchmark(args.Interpolator, args.memory, args.surface, args.preload, args.chunks)
186+
187+
188+
if __name__ == "__main__":
189+
main()

0 commit comments

Comments
 (0)