Ray tracing in the Agulhas current¶
import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import time
import mantaray
from src import *
Introduction¶
This notebook test the Ray Tracing Package in a realistic analysis. We focus on the wave-trapping process occuring in the Agulhas current along the South Africa Coast. We use a realistic current field and a constant bathymetry field. Both fields are already constructed but can be re-constructed by the use of the present notebook. Before running this example, it is recommended that users run idealized_fields.ipynb first
Initialization:
The initial conditions (wave wavelength/period/wavenumber, and direction) for the present example are inspired from the Halsne et al. 2023. (current data are from the globcurrent product: Rio, M. H., Mulet, S., & Picot, N. (2014). Beyond GOCE for the ocean circulation estimate: Synergetic use of altimetry, gravimetry, and in situ data provides new insight into geostrophic and Ekman currents. Geophysical Research Letters, 41(24), 8918-8925., https://data.marine.copernicus.eu/product/MULTIOBS_GLO_PHY_MYNRT_015_003/description)
num_rays: is the number of rays shot. All rays have the same initial parameters
x\(_{0}\), y\(_{0}\): are the locations where the rays are shot
plot_current: a flag to vizualize the current field.
plot_current = True # flag to plot current
T0 = 10 # Period [s]
theta0_deg = 60 # Direction [deg]
theta0 = theta0_deg * np.pi/180
g = 9.81 # Acceleration of Gravity
# Convert period to wavenumber magnitude
f = (1/T0)
k0 = (2*np.pi*f)**2/g
n_rays = 500 # number of rays to shot
depth0 = 4000 # constant depth for this case
Load data and convert lon/lat to meter axes. Define a sub domain that will be cropped from the global current dataset (Globcurrent product Rio et al. 2014)¶
##########
# -- Define the subdomain
##########
lon_min = 16
lon_max = 29.5
lat_min = -42
lat_max = -32
# Create a rectangle
lons_domain = [lon_min, lon_max, lon_max, lon_min, lon_min]
lats_domain = [lat_min, lat_min, lat_max, lat_max, lat_min]
##########
# -- Define the starting line for ray tracing computation
##########
x_line = [10_000, 400_000]
y_line = [200_000, 100_000]
##########
# -- Load current data and crop it
##########
ds_current = xr.open_dataset('../data/globcurrent.nc')
ds_current['current_speed'] = (ds_current.eastward_geostrophic_current_velocity**2 + ds_current.northward_geostrophic_current_velocity**2)**(1/2)
sub_ds_current = ds_current.sel(lon = slice(lon_min, lon_max), lat = slice(lat_min, lat_max))
# --- Convert lon/lat to meter/meter
Lon_meter = ((sub_ds_current.lon.values-np.mean(sub_ds_current.lon.values))*1_852*60*np.cos(np.mean(sub_ds_current.lat.values)*np.pi/180))
Lat_meter = ((sub_ds_current.lat.values-np.mean(sub_ds_current.lat.values))*1_852*60)
Lon_meter += Lon_meter.max()
Lat_meter += Lat_meter.max()
Plot¶
if plot_current:
fig, axes = plt.subplots(ncols = 2, figsize = (10, 5))
ax = axes[0]
ds_current.current_speed.squeeze().plot(ax =ax, vmin = 0, vmax = 2, add_colorbar = False)
ax.fill_between(lons_domain, lats_domain, facecolor = 'r', alpha = .6)
ax.set_aspect('equal', 'box')
ax = axes[1]
p2 = sub_ds_current.current_speed.squeeze().plot(ax = ax, vmin = 0, vmax = 2, add_colorbar = False)
cax = fig.add_axes([.1, .02, .8, 0.02])
cbar = plt.colorbar(p2, cax = cax, orientation = 'horizontal')
ax.set_aspect('equal', 'box')
ax.set_title(' ')
cbar.ax.set_xlabel('Current Speed [m/sec]')

Define the initial location of rays. Instead of shotting rays from the edges of the domain, we create a dummy line that will be used to defined starting points (\(x_{0}\), \(y_{0}\))¶
x0, y0 = start_line_ray_tracing(x_line, y_line, Lon_meter, Lat_meter, n_rays) # starting locations
Create forcings for ray tracing (Not mandatory because already created)¶
ucur = sub_ds_current.eastward_geostrophic_current_velocity.values.squeeze()
vcur = sub_ds_current.northward_geostrophic_current_velocity.values.squeeze()
output_file_cur = "../data/current_agulhas.nc"
output_file_depth = "../data/bathy_agulhas.nc"
create_current_forcing(Lon_meter, Lat_meter, ucur, vcur, output_file_cur, output_file_depth, depth = depth0)
Perform the ray tracing¶
cg = group_velocity(k0, f, depth0)
# Calculate wavenumber components
kx0 = k0*np.cos(theta0)
ky0 = k0*np.sin(theta0)
# Number of rays
# Initialize wavenumber for all rays
Kx0 = kx0*np.ones(n_rays)
Ky0 = ky0*np.ones(n_rays)
# Current and bathymetry file path
current = output_file_cur
bathymetry = output_file_depth
# Read x and y from file to get domain size
ds = xr.open_dataset(current)
# Estimates CFL
# Computes grid smallest spacing
dd = np.min([np.diff(ds.x).mean(), np.diff(ds.y).mean()])
# Computes group velocity
cg = group_velocity(k0, f, depth0)
# Computes CFL
cfl = dd/cg
duration = round(ds.x.max().values/cg)
step_size = cfl
t = time.time() # - How long this ray tracing process takes? (1/2)
bando = mantaray.ray_tracing(x0, y0, Kx0, Ky0, duration, step_size, bathymetry, current)
elapsed = time.time() - t # - How long this ray tracing process takes? (2/2)
print(f'{n_rays} rays computed in {elapsed} sec')
500 rays computed in 0.10207271575927734 sec
Remove 10 outliers rays (certainly due to discontinuity in current field)¶
id_rays1 = np.array(np.arange(0, 370, 1), dtype = int)
id_rays2 = np.array(np.arange(380, 500, 1), dtype = int)
id_ray_final = np.concatenate([id_rays1, id_rays2])
Final Plot In the Agulhas current¶
fig, ax = plt.subplots()
ax.plot(x_line, y_line, color = 'w')
cs = ax.pcolormesh(Lon_meter, Lat_meter, (ds.u**2 + ds.v**2)**(1/2), vmin = 0, vmax = 2)
for i in id_ray_final:
ray = bando.isel(ray=i)
ax.plot(ray.x, ray.y, 'r', lw=.78)
cbar = plt.colorbar(cs, orientation = 'vertical')
cbar.ax.set_ylabel('Current Speed [m/sec]')
ax.set_xlabel('Distance [km]')
ax.set_ylabel('Distance [km]')
ax.set_yticks([0, 250_000, 500_000, 750_000, 1_000_000])
ax.set_xticks([0, 250_000, 500_000, 750_000, 1_000_000])
ax.set_yticklabels([0, 250, 500, 750, 1_000])
ax.set_xticklabels([0, 250, 500, 750, 1_000])
ax.set_aspect('equal', 'box')
