tides and winds
import netCDF4 as nc
import matplotlib.pyplot as plt
import numpy as np
import datetime
import os
import glob
import csv
import pandas as pd
from dateutil import tz
import seaborn as sns
from salishsea_tools import viz_tools, grid_tools
from salishsea_tools import tidetools, places
import matplotlib.dates as md
%matplotlib inline
sns.set_palette('gray')
sns.set_style('ticks')
sns.set_color_codes
start=datetime.datetime(2015,2,6)
end=datetime.datetime(2015,8,4)
times= [start + datetime.timedelta(days=x) for x in range(0, (end-start).days)]
def list_wind_files(
to, tf,
wind_dir='/results/forcing/atmospheric/GEM2.5/operational/'
):
"""List operational wind files in date range [to, tf]
:arg to: beginning of date range
:type to: datetime object
:arg tf: end of date range
:type tf: datetime object
:arg wind_dir: directory were wind files are stored
:type wind_dir: str
:returns: filesOP, a list of files in date range
"""
sstr = to.strftime('ops_y%Ym%md%d.nc')
estr = tf.strftime('ops_y%Ym%md%d.nc')
files = glob.glob(os.path.join(wind_dir, 'ops_*.nc'))
filesOP = []
for filename in files:
if os.path.basename(filename) >= sstr:
if os.path.basename(filename) <= estr:
if not filename.endswith('orig.nc'):
filesOP.append(filename)
filesOP.sort(key=os.path.basename)
filesOP.sort(key=os.path.basename)
return filesOP
def compile_winds(j, i, files):
"""
Compile a time series of operational atmospheric products stored in files
at grid point j, i. First 24 hours of each file is used
:arg j: the y-index of the grid point
:type j: non-negative integer
:arg i: the x-index of the grid point
:type i: non-negative interger
:arg files: list of atmospheric operational files
:type files: list
:returns: wind, direc, t, pr, tem, sol, the, qr, pre, all arrays
wind speed, diretion, time, pressure, temperature, solar radiation,
humidity, precipitation.
"""
wind = []
direc = []
t = []
pr = []
sol = []
the = []
pre = []
tem = []
qr = []
for f in files:
G = nc.Dataset(f)
u = G.variables['u_wind'][0:24, j, i]
v = G.variables['v_wind'][0:24, j, i]
pr.append(G.variables['atmpres'][0:24, j, i])
sol.append(G.variables['solar'][0:24, j, i])
qr.append(G.variables['qair'][0:24, j, i])
the.append(G.variables['therm_rad'][0:24, j, i])
pre.append(G.variables['precip'][0:24, j, i])
tem.append(G.variables['tair'][0:24, j, i])
speed = np.sqrt(u**2 + v**2)
wind.append(speed)
d = np.arctan2(v, u)
d = np.rad2deg(d + (d < 0)*2*np.pi)
direc.append(d)
ts = G.variables['time_counter']
torig = datetime.datetime(1970, 1, 1)
# there is no time_origin attriubte in OP files, so I hard coded torig
for ind in np.arange(24):
t.append(torig + datetime.timedelta(seconds=ts[ind]))
numdays = len(files)
wind = np.array(wind).reshape(numdays*24,)
direc = np.array(direc, 'double').reshape(numdays*24,)
t = np.array(t).reshape(numdays*24,)
pr = np.array(pr).reshape(numdays*24,)
tem = np.array(tem).reshape(numdays*24,)
sol = np.array(sol).reshape(numdays*24,)
the = np.array(the).reshape(numdays*24,)
qr = np.array(qr).reshape(numdays*24,)
pre = np.array(pre).reshape(numdays*24,)
return wind, direc, t, pr, tem, sol, the, qr, pre
def find_model_point(lon, lat, X, Y, tol_lon=0.016, tol_lat=0.011):
"""Finds a model grid point close to a specified latitude and longitude.
Should be used for non-NEMO grids like the atmospheric forcing grid.
:arg lon: The longitude we are trying to match.
:type lon: float
:arg lat: The latitude we are trying to match.
:type lat: float
:arg X: The model longitude grid.
:type X: numpy array
:arg Y: The model latitude grid.
:type Y: numpy array
:arg tol_lon: tolerance on grid spacing for longitude
:type tol_lon: float
:arg tol_lat: tolerance on grid spacing for latitude
:type tol_lat: float
:returns: j-index and i-index of the closest model grid point.
"""
# Search for a grid point with longitude or latitude within
# tolerance of measured location
j, i = np.where(
np.logical_and(
(np.logical_and(X > lon - tol_lon, X < lon + tol_lon)),
(np.logical_and(Y > lat - tol_lat, Y < lat + tol_lat))))
if j.size > 1 or i.size > 1:
raise ValueError(
'Multiple model points found. tol_lon/tol_lat too big.'
)
elif not j or not i:
raise ValueError(
'No model point found. tol_lon/tol_lat too small or '
'lon/lat outside of domain.'
)
return j, i
def load_tidal_predictions(filename):
"""Load tidal prediction from a file.
:arg str filename: The path and file name of a CSV file that contains
ttide tidal predictions generated by
:kbd:`get_ttide_8.m`.
:returns: ttide: Tidal predictions and mean sea level,
the mean component from the harmonic analysis.
:rtype: :py:class:`pandas.DataFrame`
"""
with open(filename) as f:
mycsv = list(csv.reader(f))
msl = float(mycsv[1][1])
ttide = pd.read_csv(
filename, skiprows=3, parse_dates=[0], date_parser=dateParserMeasured2)
ttide = ttide.rename(
columns={
'Time_Local ': 'time',
' pred_8 ': 'pred_8',
' pred_all ': 'pred_all',
})
return ttide, msl
def dateParserMeasured2(s):
"""
converts string in %d-%b-%Y %H:%M:%S format Pacific time to a
datetime object UTC time.
"""
PST = tz.tzoffset("PST", -28800)
# convert the string to a datetime object
unaware = datetime.datetime.strptime(s, "%d-%b-%Y %H:%M:%S ")
# add in the local time zone (Canada/Pacific)
aware = unaware.replace(tzinfo=PST)
# convert to UTC
return aware.astimezone(tz.tzutc())
SITES = places.PLACES
SITES
{'Boundary Bay': {'NEMO grid ji': (380, 335), 'hist max sea lvl': 5.61, 'lon lat': (-122.925, 49.0), 'mean sea lvl': 3.09, 'stn number': None, 'wind grid ji': (129, 162), 'ww3 grid ji': (222, 439)}, 'British Columbia': {'lon lat': (-123.6, 49.9)}, 'Calamity Point': {'NEMO grid ji': None, 'lon lat': (-123.1276, 49.31262), 'mean sea lvl': 3.09, 'stn number': 7724, 'ww3 grid ji': None}, 'Campbell River': {'NEMO grid ji': (747, 125), 'hist max sea lvl': 5.35, 'lon lat': (-125.24, 50.04), 'mean sea lvl': 2.916, 'stn number': 8074, 'wind grid ji': (190, 102), 'ww3 grid ji': (453, 109)}, 'Central node': {'NEMO grid ji': (424, 266), 'ONC stationCode': 'SCVIP', 'depth': 294, 'lon lat': (-123.425825, 49.040066666)}, 'Cherry Point': {'NEMO grid ji': (343, 342), 'hist max sea lvl': 5.846, 'lon lat': (-122.766667, 48.866667), 'mean sea lvl': 3.543, 'stn number': 9449424, 'wind grid ji': (122, 166), 'ww3 grid ji': (193, 462)}, 'Cluster_1': {'NEMO grid ji': (241, 212), 'Vector Stn': '64', 'lon lat': (48.215, -123.099)}, 'Cluster_2': {'NEMO grid ji': (294, 127), 'Vector Stn': '69', 'lon lat': (48.261, -123.717)}, 'Cluster_3': {'NEMO grid ji': (376, 291), 'Vector Stn': '45', 'lon lat': (48.899, -123.138)}, 'Cluster_4': {'NEMO grid ji': (282, 305), 'Vector Stn': '53', 'lon lat': (48.555, -122.75)}, 'Cluster_5': {'NEMO grid ji': (344, 271), 'Vector Stn': '57', 'lon lat': (48.735, -123.135)}, 'Cluster_6': {'NEMO grid ji': (320, 68), 'Vector Stn': '73', 'lon lat': (48.249, -124.11)}, 'Cluster_7': {'NEMO grid ji': (504, 246), 'Vector Stn': '27', 'lon lat': (49.317, -123.801)}, 'Cluster_8': {'NEMO grid ji': (646, 168), 'Vector Stn': '12', 'lon lat': (49.726, -124.679)}, 'Cluster_9': {'NEMO grid ji': (423, 300), 'lon lat': (49.101, -123.249)}, 'Delta BBL node': {'NEMO grid ji': (424, 283), 'ONC stationCode': 'LSBBL', 'depth': 143, 'lon lat': (-123.339633, 49.074766)}, 'Delta DDL node': {'NEMO grid ji': (426, 286), 'ONC stationCode': 'USDDL', 'depth': 107, 'lon lat': (-123.32972, 49.08495)}, 'Departure Bay': {'lon lat': (-123.8909, 49.1632)}, 'Duke Pt.': {'in berth radius': 0.002, 'lon lat': (-123.89095676900132, 49.16340592936349)}, 'East node': {'NEMO grid ji': (417, 283), 'ONC stationCode': 'SEVIP', 'depth': 164, 'lon lat': (-123.316836666, 49.04316)}, 'Friday Harbor': {'NEMO grid ji': (300, 267), 'hist max sea lvl': 4.572, 'lon lat': (-123.016667, 48.55), 'mean sea lvl': 2.561, 'stn number': 9449880, 'wind grid ji': (108, 155), 'ww3 grid ji': (124, 427)}, 'Halfmoon Bay': {'NEMO grid ji': (549, 254), 'hist max sea lvl': 5.61, 'lon lat': (-123.912, 49.511), 'mean sea lvl': 3.14, 'stn number': 7830, 'wind grid ji': (158, 136), 'ww3 grid ji': (331, 297)}, 'Halibut Bank': {'EC buoy number': 46146, 'GEM2.5 grid ji': (149, 141), 'NEMO grid ji': (503, 261), 'lon lat': (-123.72, 49.34)}, 'Horseshoe Bay': {'lon lat': (-123.2728, 49.3742)}, 'Indian Arm Head': {'NEMO grid ji': None, 'lon lat': (-122.8864, 49.4615), 'mean sea lvl': 3.09, 'stn number': 7774, 'ww3 grid ji': None}, 'Juan de Fuca Strait': {'lon lat': (-124.7, 48.47)}, 'Nanaimo': {'NEMO grid ji': (484, 208), 'hist max sea lvl': 5.47, 'lon lat': (-123.93, 49.16), 'mean sea lvl': 3.08, 'stn number': 7917, 'wind grid ji': (142, 133), 'ww3 grid ji': (261, 298)}, 'Neah Bay': {'NEMO grid ji': (384, 15), 'hist max sea lvl': 4.359, 'lon lat': (-124.6, 48.4), 'mean sea lvl': 1.925, 'stn number': 9443090, 'wind grid ji': (111, 105), 'ww3 grid ji': (89, 200)}, 'New Westminster': {'NEMO grid ji': (423, 363), 'hist max sea lvl': 4.66, 'lon lat': (-122.90535, 49.203683), 'mean sea lvl': 2.481, 'stn number': 7654, 'wind grid ji': (138, 164)}, 'Pacific Ocean': {'lon lat': (-125.6, 48.1)}, 'Patricia Bay': {'NEMO grid ji': (351, 214), 'hist max sea lvl': 4.38, 'lon lat': (-123.4515, 48.6536), 'mean sea lvl': 2.256, 'stn number': 7277, 'wind grid ji': (115, 143), 'ww3 grid ji': (145, 363)}, 'Point Atkinson': {'NEMO grid ji': (468, 329), 'hist max sea lvl': 5.61, 'lon lat': (-123.25, 49.33), 'mean sea lvl': 3.09, 'stn number': 7795, 'wind grid ji': (146, 155), 'ww3 grid ji': (296, 393)}, 'Port Moody': {'NEMO grid ji': None, 'lon lat': (-122.8658, 49.28814), 'mean sea lvl': 3.09, 'stn number': 7755, 'ww3 grid ji': None}, 'Port Renfrew': {'NEMO grid ji': (401, 61), 'hist max sea lvl': 4.359, 'lon lat': (-124.421, 48.555), 'mean sea lvl': 1.937, 'stn number': 8525, 'wind grid ji': (117, 112), 'ww3 grid ji': (123, 226)}, 'Puget Sound': {'lon lat': (-122.67, 48)}, 'S3': {'GEM2.5 grid ji': (146, 155), 'NEMO grid ji': (450, 258), 'lon lat': (-123.558, 49.125)}, 'Sand Heads': {'GEM2.5 grid ji': (135, 151), 'NEMO grid ji': (426, 293), 'hist max sea lvl': 5.3950000000000005, 'lon lat': (-123.3, 49.1), 'mean sea lvl': 2.875, 'stn number': 7594, 'wind grid ji': (135, 151), 'ww3 grid ji': (246, 385)}, 'Sandheads': {'GEM2.5 grid ji': (135, 151), 'NEMO grid ji': (426, 293), 'hist max sea lvl': 5.3950000000000005, 'lon lat': (-123.3, 49.1), 'mean sea lvl': 2.875, 'stn number': 7594, 'wind grid ji': (135, 151), 'ww3 grid ji': (246, 385)}, 'Sandy Cove': {'NEMO grid ji': (468, 333), 'hist max sea lvl': 5.61, 'lon lat': (-123.23, 49.34), 'mean sea lvl': 3.09, 'stn number': 7786, 'wind grid ji': (146, 155), 'ww3 grid ji': (294, 396)}, 'Sentry Shoal': {'EC buoy number': 46131, 'GEM2.5 grid ji': (183, 107), 'NEMO grid ji': (707, 145), 'lon lat': (-125.0, 49.92)}, 'Sisters Islet': {'GEM2.5 grid ji': (160, 120), 'NEMO grid ji': (582, 175), 'lon lat': (-124.43, 49.49)}, 'Squamish': {'NEMO grid ji': (532, 389), 'hist max sea lvl': 5.61, 'lon lat': (-123.155, 49.694), 'mean sea lvl': 3.14, 'stn number': 7811, 'wind grid ji': (162, 160), 'ww3 grid ji': (370, 404)}, 'Strait of Georgia': {'lon lat': (-123.8, 49.3)}, 'Swartz Bay': {'lon lat': (-123.4102, 48.6882)}, 'Tsawwassen': {'in berth radius': 0.0015, 'lon lat': (-123.132722, 49.006165)}, 'Vancouver': {'lon lat': (-123.1207, 49.2827)}, 'Vancouver Harbour': {'NEMO grid ji': None, 'lon lat': (-123.1069, 49.28937), 'mean sea lvl': 3.09, 'stn number': 7735, 'ww3 grid ji': None}, 'Victoria': {'NEMO grid ji': (302, 196), 'hist max sea lvl': 3.76, 'lon lat': (-123.3707, 48.424666), 'mean sea lvl': 1.881, 'stn number': 7120, 'wind grid ji': (104, 144), 'ww3 grid ji': (90, 374)}, 'Washington State': {'lon lat': (-123.8, 47.8)}, 'Woodwards Landing': {'NEMO grid ji': (414, 329), 'hist max sea lvl': 4.66, 'lon lat': (-123.0754, 49.1251), 'mean sea lvl': 2.215, 'stn number': 7610, 'wind grid ji': (135, 138)}, 'YVR': {'GEM2.5 grid ji': (139, 155), 'lon lat': (-123.184, 49.195)}}
def plot_tides(ax, sdt, edt, times, ts_lines, tdir = '/data/nsoontie/MEOPAR/tools/SalishSeaNowcast/tidal_predictions/',
name = 'Campbell River'):
fname = os.path.join(tdir, '{}_tidal_prediction_01-Jan-2015_01-Jan-2020.csv'.format(name))
ttide, _ = load_tidal_predictions(fname)
ax.plot(ttide.time, ttide.pred_all,'-k')
ax.set_xlim([sdt,edt])
ax.set_ylabel('Tidal height [m]')
ax.set_ylim([-3,3])
for t in ts_lines:
ax.plot([times[t], times[t]], [-3,3], '--', color='gray')
return ttide.time,ttide.pred_all
OP = nc.Dataset('/results/forcing/atmospheric/GEM2.5/operational/ops_y2014m11d18.nc')
OPlon =OP.variables['nav_lon'][:]-360
OPlat = OP.variables['nav_lat'][:]
ts_lines=np.array([ 7, 14, 21, 28])
def plot_winds(ax, location, lons, lats, sdt, edt, times, ts_lines):
#lon, lat = SITES['East node']['lon lat'] # Giorgio: change this lat lon to Iona outfall coordinates
wind_files = list_wind_files(sdt, edt)
j=190
i=102
#find_model_point(lon,lat,lons,lats)
wind, direc, t, pr, tem, sol, the, qr, pre = compile_winds(j,i,wind_files)
datenum = md.date2num(t)
us0 = wind*np.cos(np.deg2rad(direc))
vs0 = wind*np.sin(np.deg2rad(direc))
us,vs=viz_tools.rotate_vel(us0,vs0,origin='map')
q=ax.quiver(datenum, np.zeros(datenum.shape), us, vs,scale=8*10e0)
qk = ax.quiverkey(q, 0.1, 0.1, 10, '10 m/s')
ax.set_yticklabels([])
ax.set_xlim([sdt,edt])
ax.set_ylabel('Wind Vectors')
for t in ts_lines:
ax.plot([times[t], times[t]], [-1,1], '--', color='gray')
return datenum, wind, direc, us, vs
sns.set_style('whitegrid')
fig, axs = plt.subplots(2,1, figsize=(8, 6), sharex=True)
level = 0
tidetime,tideAll=plot_tides(axs[0], start, end, times, ts_lines)
winddatenum, wind, direc, us, vs=plot_winds(axs[1], 'East', OPlon, OPlat, start, end,times, ts_lines)
saveloc='/ocean/eolson/MEOPAR/analysis-elise/notebooks/plotResults/fullDomain398x898/exploreNowcast/NorthernNitrate/Movie/'
np.savez(saveloc+'saveTideWindCampbellR.npz',tidetime=tidetime,tideAll=tideAll,winddatenum=winddatenum,
wind=wind, direc=direc, us=us, vs=vs)