As part of the ILB9 session of the OTC23 online training course.
This Jupyter notebook will use the SEAScope viewer and SEAScope Python bindings to retrieve inputs data
author: dr.fab, OceanDataLab
date: 2022-10-31
Study wave current interaction in the Agulhas current region¶
Wave current interactions are well known to bend wave propagation rays.
We will simulate the wave current interactions and compare it to atimeter and SAR wave observations
This notebook will teach you how to:
Import surface current data from SEAScope
Simulate wave current interactions
Export simulated wave rays in SEAScope
directory:
woc-bfnqg-3h, label:WOC BFN QG currentdirectory:
sentinel1_sar_roughness, label:Sentinel1 SARdirectory:
jason2_swh, label:Jason2 GDR
Check that you have them in your SEAScope catalogue before continuing with this notebook
These collections can be downloaded at https://
To learn how to add data to SEAScope, you can watch one of the following YouTube videos:
How to add data to SEAScope on Windows
or the “Add data to SEAScope” chapter of How to add data to SEAScope on Windows
import matplotlib
%matplotlib inline
import matplotlib.pyplot as plt
import numpy
import os
import datetime
import scipy
from numpy.random import randn
import pyproj
import numpy as np
from netCDF4 import Dataset, chartostring, date2num, num2date, default_fillvals
import pyproj
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))Load and select data from SEAScope¶
locate the WOC BFN QG current data in the SEAScope viewer¶
In the “Catalogue” (the panel on the right), select :
collection
WOC BFN QG current, variablecurrent
Select the date where you have data available (highlighted in white in the timeline)
The 17th of December 2015 should be good
Locate the data on the globe
It should be in the Agulhas current region
Steps for data extraction from SEAScope¶
Zoom-in to properly see the current (the resolution of the extraction depends on the zoom level)
Draw a polygon over the region you want to extract:
Start drawing by clicking on the 3rd button to the right of the pen icon in the toolbar or by hitting the P key
Place the corners of the polygon by left-clicking on the globe
Once all the corners placed, hit the Enter or Return key to commit your polygon
Select the polygon you just created by clicking on it
Extract the data intersecting that polygon by clicking on the extract button in the “Selected annotation” panel
# Load data directly from viewer memory
from SEAScope.lib import get_extracted_data
extractions = get_extracted_data()Print selected granules
#from SEAScope.lib import load_pyo
#data_path = ''
#extractions = load_pyo(data_path)
## Print a list of path of extracted granules
print('\n'.join(extractions.keys()))Play with the data¶
** Compute trajectory of fictive swell ray at initial position (x0, y0) using the dispertion relation in presence of surface current :
**
** Fill variable name for your selected velocity **
# Get longitudes, latitudes from gcps and time from first granule
granule_uri = next( v for i, v in enumerate(extractions.keys()) if i == 0)
extraction = extractions[granule_uri]
start = extraction['meta']['start']
print(extraction['meta']['fields'])name_var_u = 'ug'
name_var_v = 'vg'
extraction = extractions[granule_uri]
u = extraction['data'][name_var_u]
v = extraction['data'][name_var_v]
from SEAScope.lib.utils import get_lonlat
lon2D, lat2D = get_lonlat(extraction, numpy.shape(u))
velocity = (u**2+v**2)**(1./2)
print(velocity.shape)Verify the extracted current by plotting it¶
fig = plt.figure(figsize = (16,10))
plt.imshow(numpy.flipud(velocity),interpolation='bicubic',cmap='jet', vmin=0 , vmax=2)
cbar=plt.colorbar()
X, Y = numpy.meshgrid(numpy.arange(0, u.shape[1]), numpy.arange(0, u.shape[0]))
Q = plt.quiver(X[::15, ::15], Y[::15, ::15],numpy.clip(numpy.flipud(u[::15,::15]),-2,2), numpy.clip(numpy.flipud(v[::15,::15]),-2,2))
qk = plt.quiverkey(Q, 0.5, 0.98, 2, r'$2 \frac{m}{s}$', labelpos='W',
fontproperties={'weight': 'bold'})
#plt.xlim(40, 150)
#plt.ylim(180, 120)res = (lat2D[1,0] - lat2D[0,0])*100000
res = (lat2D[int(u.shape[0]/2+1), int(u.shape[1]/2)]
- lat2D[int(u.shape[0]/2), int(u.shape[1]/2)]) * 100000
print(res)Simulate wave current interactions¶
** Play with nstep (number of time steps) and nseed (number of rays) **
def ev(var, x, y):
if numpy.isnan(x) or numpy.isnan(y):
return numpy.nan
nx, ny = numpy.shape(var)
i = int(x)
j = int(y)
if (i > nx - 2) or (j > ny - 2):
return numpy.nan
dx = x - i
dy = y - j
uix = dx * var[i + 1, j] + (1 - dx) * var[i, j]
uiy = dx * var[i + 1, j + 1] + (1 - dx) * var[i, j + 1]
ui = dy * uiy + (1 - dy) * uix
return ui
# Initialize wave ray position
granule_id = None
# Time step for advection (in sec)
s=1
dt = 3600/3/s
# Number of seeds
nseed = 120*2 #! Change this
# Wave number
# 17 dec 2015
wl = 250
tetad = 35
tetad = (30-numpy.arange(nseed)/nseed*20)*0+35
c0 = 10 # Starting column
# Starting line
l0 = u.shape[0]-130
# Total time of advection
nstep = int(130*s) #! Change this
k = 2*numpy.pi/wl
# Wave direction in radians
teta0 = tetad*numpy.pi/180
# Starting column
tmpu = extraction['data'][name_var_u]
tmpv = extraction['data'][name_var_v]
res = (lat2D[int(u.shape[0]/2+1), int(u.shape[1]/2)]
-lat2D[int(u.shape[0]/2), int(u.shape[1]/2)]) * 100000
x = numpy.full((nstep+1, nseed), numpy.nan)
y = numpy.full((nstep+1, nseed), numpy.nan)
lon = numpy.full((nstep+1, nseed), numpy.nan)
lat = numpy.full((nstep+1, nseed), numpy.nan)
for c in range(nseed):
x[0,c] = -l0 - 1.5*c/3
y[0,c] = c0 + 6*c/3
lon[0,c] = ev(lon2D, x[0,c], y[0,c])
lat[0,c] = ev(lat2D, x[0,c], y[0,c])
teta = teta0[c]
kx = k*numpy.sin(teta)
ky = k*numpy.cos(teta)
for i in range(nstep):
u = tmpu
v = tmpv
if x[i, c] is numpy.ma.masked or y[i, c] is numpy.ma.masked:
continue
omega = numpy.sqrt(9.81*(kx**2+ky**2)) + (ky*ev(u,x[i,c],y[i,c])
+kx*ev(v,x[i,c],y[i,c]))
dx = dt*(ev(v,x[i,c],y[i,c])+numpy.sqrt(9.81/(numpy.sqrt(kx**2+ky**2)))
*numpy.sin(teta)/2)/res
dy = dt*(ev(u,x[i,c],y[i,c])+numpy.sqrt(9.81/(numpy.sqrt(kx**2+ky**2)))
*numpy.cos(teta))/2/res
teta = numpy.arctan2(kx,ky)
if dx is numpy.ma.masked or dy is numpy.ma.masked:
continue
if (x[i, c]+dx<(1-u.shape[0])) or (y[i, c]+dy>(u.shape[1]-1)):
continue
if (x[i, c]+dx>0) or (y[i, c]+dy<0):
continue
domegax = omega - (numpy.sqrt(9.81*(kx**2+ky**2))
+ (ky*ev(u,x[i,c]+dx,y[i,c])
+ kx*ev(v,x[i,c]+dx,y[i,c])))
domegay = omega - (numpy.sqrt(9.81*(kx**2+ky**2))
+ (ky*ev(u,x[i,c],y[i,c]+dy)
+ kx*ev(v,x[i,c],y[i,c]+dy)))
x[i+1,c] = x[i,c] + dx
y[i+1,c] = y[i,c] + dy
lon[i+1,c] = ev(lon2D, x[i+1,c], y[i+1,c])
lat[i+1,c] = ev(lat2D, x[i+1,c], y[i+1,c])
dkx = dt*domegax/dx/res
dky = dt*domegay/dy/res
kx = kx + dkx
ky = ky + dkyplot the simulated wave rays¶
fig = plt.figure(figsize = (18,10))
plt.imshow(numpy.flipud(velocity),interpolation='bicubic',cmap='jet', vmin=0 , vmax=2)
cbar=plt.colorbar()
Q = plt.quiver(X[::15, ::15], Y[::15, ::15],
numpy.clip(numpy.flipud(u[::15,::15]),-2,2),
numpy.clip(numpy.flipud(v[::15,::15]),-2,2))
qk = plt.quiverkey(Q, 0.5, 0.98, 2, r'$2 \frac{m}{s}$', labelpos='W',
fontproperties={'weight': 'bold'})
for c in range(nseed):
plt.plot(y[:,c],-x[:,c],color='w',linewidth=1.5)
Export wave rays to SEAScope¶
Define Collection:
** A granule has to belong to a collection **
from SEAScope.lib.utils import create_collection, init_ids
# Make sure to keep the collection identifier, we will need it later
# collection_id, collection = create_collection('Simulated wave rays 11sec 25°- 10 aout 2019 - mercator GLOBAL OCEAN 1/12°')
# collection_id, collection = create_collection('Simulated wave rays 15sec 37°- 20 fev 2019 - BFN')
collection_id, collection = create_collection('Simulated wave rays - BFNQG ')
# collection_id, collection = create_collection('Simulated wave rays 15sec 37°- 20 fev 2019 - DUACS')
# collection_id, collection = create_collection('Simulated wave rays 15sec 35°- 20 fev 2019 - DUACS')
print(collection_id)
# IP address and port used to reach the SEAScope standalone application
host = '127.0.0.1'
port = 11155
import SEAScope.upload
with SEAScope.upload.connect(host, port) as link:
# Send create collection job
SEAScope.upload.collection(link, collection)Upload granule to SEAScope:
Define shell of granule, create granule
The granule will contain each matrix of modified data as what we call a “field”
** Enter a name for each field (field_name_time) **
Upload collection and granule to SEAScope
from SEAScope.lib.utils import create_granule
# Shell of Granule
# Each position of the trajectory will be used as GCP
with SEAScope.upload.connect(host, port) as link:
for c in range(nseed):
gcps = []
count = 0
for i in range(0, nstep+1):
gcp = {'lon': lon[i,c], 'lat': lat[i,c], 'i': i, 'j': 0}
if lat[i,c] != 0:
gcps.append(gcp)
count=count+1
# Create granule
stop_dt = start + datetime.timedelta(days=7)
granule_id, granule = create_granule(collection_id, gcps, start, stop_dt)
# Set variables
# Enter a name for each variable field
from SEAScope.lib.utils import set_field
field_name_time ='time rays'
set_field(granule, field_name_time, numpy.arange(count)*dt/3600)
# Send create granule job
SEAScope.upload.granule(link, granule)**Telling SEAScope what to display:
The next step is variables definition. **
Enter a name for each variable you want to display (variable_name_time, ...)
from SEAScope.lib.utils import create_variable
# Create a variable for each parameter
variable_name_time = 'time_rays'
var1 = create_variable(collection, variable_name_time, [field_name_time], dims=1)
# Upload to SEAScope
with SEAScope.upload.connect(host, port) as link:
# Send create variable jobs
SEAScope.upload.variable(link, var1)** Telling SEAScope how to display variables:
The rendering options are defined in the following cell.
You can play with min, max values, the colormap ... and check how the rendering changes on SEAScope
**
# Rendering configuration for the time
rcfg1 = var1['rendering']
rcfg1['min'] = 1
rcfg1['max'] = 40
rcfg1['colormap'] = ''
rcfg1['color'] = [255,255,255]
rcfg1['opacity'] = 0.25
rcfg1['zindex'] = .923
# Upload to SEAScope
with SEAScope.upload.connect(host, port) as link:
# Send create rendering config jobs
SEAScope.upload.rendering_config(link, rcfg1)