Example

In this example, we use the CoherentStructures.jl package to work with some data derived from satelite altimetry. As is usual for Julia, this will take much longer than normal to run the first time as things are being compiled.

For the sake of simplicity we will restrict ourselves to the case of only a single worker, but parallelising is straightforward.

Getting data

Download velocity fields from Copernicus CMES. After having registered an account click on "Download", then "Download options" and then use the "FTP Access" to download the files you want.

In our case, we have stored the files in /foo/ww_ocean_data, an example filename is dt_global_allsat_phy_l4_20070414_20190101.nc.

Tip

If you are using NetCDF files where the date is not in the filename the same way, adjust the filename_match_to_date parameter.

Loading the data

Load the packages used:

using OceanTools, CoherentStructures, Dates, StaticArrays

The space-time window we are interested in:

start_date = DateTime("2006-11-25T00:00:00")
end_date = DateTime("2006-12-30T00:00:00")
LL_space = [-50.0, -50.0]
UR_space = [50.0, 50.0]

Read in velocity files (from "ugos" and "vgos" attributes in the NetCDF files)

schema = r"^dt_global_allsat_phy_l4_([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])_.*.nc$"
ww_ocean_data = "/foo/ww_ocean_data/"

p, ust1, (Lon, Lat, times)  = read_ocean_velocities(ww_ocean_data,start_date, end_date; schema=schema, LL_space=LL_space, UR_space=UR_space)

Plot a heatmap of the longitudinal component of a tricubic interpolation of the velocity field.

using Plots
xs = range(-20, stop=15, length=400)
ys = range(-10, stop=5, length=400)
t = times[10]
Plots.heatmap(xs, ys, (x,y) -> uv_tricubic((@SVector [x,y]), p, t)[1], title="Longitudinal velocity [deg/day]", color=:viridis, aspect_ratio=1.0)

Notice how areas where the velocity field is missing (i.e. on land), the velocity is zero. Because knowing land areas is sometimes useful, the ust1 variable contains a single time slice of the velocity where missing values are nan.

The velocity fields used are derived from sea surface height measurements. Load the sea surface height mesurements directly:

p2, _  = read_ocean_scalars(ww_ocean_data, start_date, end_date; schema=schema, LL_space=LL_space, UR_space=UR_space, scalar_field_name="sla")
Plots.heatmap(xs, ys, (x,y) -> scalar_tricubic((@SVector [x,y]), p2, t), title="Sea surface height anomaly [m]", color=:viridis, aspect_ratio=1.0)

It is straightforward to plot trajectories of single particles, we'll make an animation (download this script into "animations.jl" for the animatemp4 function):

include("animations.jl")
x0 = [0.0,0.0]
trajectory = flow(uv_tricubic,x0,times; p=p)

frames = []
for (i,t) in enumerate(times)
    fig = Plots.heatmap(xs,ys,(x,y) -> scalar_tricubic((@SVector [x,y]),p2,t ),title="Sea surface height anomaly [m]",color=:viridis,aspect_ratio=1.0,clim=(-0.1,0.1))
    Plots.scatter!(fig,(trajectory[i][1],trajectory[i][2]),label="simulated drifter position")
    push!(frames,fig)
end
animatemp4(frames)

The flow function used above came from the CoherentStructures.jl package (which in turn calls DifferentialEquations.jl). CoherentStructures.jl is also capable of fancier analyis:

vortices, singularities, bg = materialbarriers(
       uv_tricubic, range(-5,7.5,length=300), range(-40,stop=-28,length=300), range(times[2],stop=times[2]+30,length=30),
       LCSParameters(boxradius=4, indexradius=0.25, pmax=1.4,
                     merge_heuristics=[combine_20_aggressive]),
       p=p, on_torus=false);

fig = plot_vortices(vortices, singularities, (-5, -40), (7.5, -28);
    bg=bg, title="DBS field and transport barriers", showlabel=false)

Computations on larger domains are of course possible. Here is a paper that computes similar structures as above using OceanTools.jl and CoherentStructures.jl on a global scale.