# Need to install dolfyn, easy to do it with: pip3 install dolfyn
# Ivica, 3-May-2022
# Import core DOLfYN functions
import dolfyn as dlfn
# Import ADCP-specific API tools
from dolfyn.adp import api
from matplotlib import pyplot as plt
import matplotlib.dates as dt
import numpy as np
ds = dlfn.read('/home2/ivica/notebooks/ADCP/ADCP1000.000')
Reading file /home2/ivica/notebooks/ADCP/ADCP1000.000 ...
ds
<xarray.Dataset> Dimensions: (beam: 4, range: 40, time: 11302, dir: 4, x: 4, x*: 4, earth: 3, inst: 3) Coordinates: * beam (beam) int64 1 2 3 4 * range (range) float64 3.09 4.09 5.09 ... 40.09 41.09 42.09 * time (time) datetime64[ns] 2021-08-16T05:00:00 ... 2021-1... * dir (dir) <U3 'E' 'N' 'U' 'err' * x (x) int64 1 2 3 4 * x* (x*) int64 1 2 3 4 * earth (earth) <U1 'E' 'N' 'U' * inst (inst) <U1 'X' 'Y' 'Z' Data variables: (12/19) number (time) uint32 1 2 3 4 5 ... 11299 11300 11301 11302 builtin_test_fail (time) bool False False False ... False False False c_sound (time) float32 1.553e+03 1.553e+03 ... 1.518e+03 depth (time) float32 40.0 40.0 40.0 40.0 ... 40.0 40.0 40.0 pitch (time) float32 -0.31 -0.31 -0.3 ... -1.26 -1.26 -1.26 roll (time) float32 2.5 2.49 2.48 2.48 ... -3.14 -3.14 -3.14 ... ... vel (dir, range, time) float32 -0.618 -0.247 ... nan nan amp (beam, range, time) uint8 84 85 85 85 ... 154 161 147 corr (beam, range, time) uint8 68 67 67 66 ... 77 82 90 82 prcnt_gd (beam, range, time) uint8 28 12 11 21 14 ... 0 0 0 0 0 beam2inst_orientmat (x, x*) float64 1.462 -1.462 0.0 ... -1.034 -1.034 orientmat (earth, inst, time) float64 0.3861 0.3858 ... -0.9983 Attributes: (12/39) inst_make: TRDI inst_model: Workhorse inst_type: ADCP rotate_vars: ['vel'] has_imu: 0 name: wh-adcp ... ... water_ref_cells: [1, 5] fls_target_threshold: 50 xmit_lag_m: 0.5 fs: 0.004545454545454545 vel_gps_corrected: 0 h_deploy: 0.6
%matplotlib notebook
fig, ax = plt.subplots(figsize=(12,4))
ds['heading'].plot()
ax.grid(True);
%matplotlib inline
fig, ax = plt.subplots(figsize=(12,4))
ds['heading'].sel(time=slice('2021-8-16 14:30:00','2021-12-09 14:00:00')).plot()
ax.grid(True);
myds = ds.sel(range=slice(0,39), time=slice('2021-8-16 14:30:00','2021-12-09 14:00:00')).squeeze()
api.clean.set_range_offset(myds, 0.6) # instrument was 0.6m above the bottom (this is based on the mooring setup)
adcp_depth = 39.0 # based on notes from the deployment, we need this if there is no pressure sensor on ADCP
adcp_lon = 16.3882
adcp_lat = 43.5190
dlfn.set_declination(myds, declin=4.5, inplace=True) # 4 deg 30 sec East for Split
pg = myds['prcnt_gd'].values # % of 4 bin solutions
np.shape(pg)
(4, 36, 11039)
pg1 = pg[0,:,:] # % of 3 bin good solutions
pg4 = pg[3,:,:] # % of 3 bin good solutions
pg_test = pg1 + pg4
drange = myds['range'].values
t = dlfn.time.dt642date(myds.time)
%matplotlib inline
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, pg_test, cmap='gist_ncar', vmin=0, vmax=100)
ax.set_title('Percentage of good solutions for bin 3 & 4')
fig.colorbar(c, ax=ax)
ax.grid(True)
# if pg1 + Pg4 < 50% that is definitely bad bin/data
mask_pg = np.ma.masked_invalid(pg_test>50)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, mask_pg)
ax.set_title('Percent good mask')
fig.colorbar(c, ax=ax)
ax.grid(True)
# correlation btw beams, we can get surface reflection
myds['corr'].sel(beam=2).squeeze().plot(vmin = 0, vmax =110);
corr_3 = myds['corr'].sel(beam=2).squeeze().values # 3 bin
corr_4 = myds['corr'].sel(beam=3).squeeze().values # 4 bin
# correlation QC if corr_3 or corr_4 < 110
mask_corr = np.ma.masked_invalid(corr_3>110)
mask_corr = np.ma.masked_invalid(corr_4>110)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, mask_corr)
ax.set_title('Correlation mask')
fig.colorbar(c, ax=ax)
ax.grid(True)
# reflection amplitude, we can see surface reflection of high turbidity
myds['amp'].sel(beam=3).squeeze().plot(cmap='gist_ncar');
amp_1 = myds['amp'].sel(beam=1).squeeze().values # 1 bin
amp_2 = myds['amp'].sel(beam=2).squeeze().values # 2 bin
amp_3 = myds['amp'].sel(beam=3).squeeze().values # 3 bin
mask_amp = np.ma.masked_invalid(amp_1<180)
mask_amp = np.ma.masked_invalid(amp_2<180)
mask_amp = np.ma.masked_invalid(amp_3<180)
amp_mean=1./3*(amp_1+amp_2+amp_3)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange[:-1]-adcp_depth, np.diff(amp_mean, axis=0), vmin = 0, vmax=30)
ax.set_title('Amplitude (echo intensity) diff')
fig.colorbar(c, ax=ax)
ax.grid(True)
mask_amp[0:-1,:] = np.ma.masked_invalid(np.diff(amp_mean, axis=0) <30) # if change > 30 counts btw successive bins
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, mask_amp, vmin=0, vmax = 1)
ax.set_title('Amplitude (echo intensity) mask')
fig.colorbar(c, ax=ax)
ax.grid(True)
mask = mask_pg * mask_corr * mask_amp # stack all together
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, mask, vmin=0, vmax = 1)
ax.set_title('Combined mask (PG + CORR + AMP)')
fig.colorbar(c, ax=ax)
ax.grid(True)
vel_mag = myds.velds.U_mag
val_dir = myds.velds.U_dir
vel_east = myds.velds.u
vel_north = myds.velds.v
vel_up = myds.velds.w
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, vel_up, vmin=0, vmax = 0.03)
ax.set_title('Upward velocity')
fig.colorbar(c, ax=ax)
ax.grid(True)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, vel_mag, vmin=0, vmax = 0.3)
ax.set_title('Horizontal velocity magnitude')
fig.colorbar(c, ax=ax)
ax.grid(True)
mask_vel = np.ma.masked_invalid(vel_up < 0.03)
mask_vel = np.ma.masked_invalid(vel_mag < 0.8)
mask_vel[0:-1,:] = np.ma.masked_invalid(np.diff(vel_mag, axis=0) <0.1) # if too large shear its bad
mask_vel[-2:,:] = 0 # last 2 bins are bad as too close to surface (40 bins in 39m water)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, mask_vel, vmin=0, vmax = 1)
ax.set_title('Velocity mask')
fig.colorbar(c, ax=ax)
ax.grid(True)
mask = mask * mask_vel # stack masks
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, mask, vmin=0, vmax = 1)
ax.set_title('Combined final mask')
fig.colorbar(c, ax=ax)
ax.grid(True)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, np.ma.masked_where(~mask, vel_east),cmap='bwr', vmin=-0.3, vmax = 0.3)
ax.set_title('East component')
fig.colorbar(c, ax=ax)
ax.grid(True)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, np.ma.masked_where(~mask, vel_north),cmap='bwr', vmin=-0.3, vmax = 0.3)
ax.set_title('North component')
fig.colorbar(c, ax=ax)
ax.grid(True)
# fill small gaps (limit for maxgap of 3 nans)
north = np.ma.masked_where(~mask, vel_north)
north = north.filled(np.nan)
dlfn.tools.misc.fillgaps(north, dim=0, maxgap = 3)
dlfn.tools.misc.fillgaps(north, dim=1, maxgap = 3)
east = np.ma.masked_where(~mask, vel_east)
east = east.filled(np.nan)
dlfn.tools.misc.fillgaps(east, dim=0, maxgap = 3)
dlfn.tools.misc.fillgaps(east, dim=1, maxgap = 3)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, north,cmap='bwr', vmin=-0.3, vmax = 0.3)
ax.set_title('North component')
fig.colorbar(c, ax=ax)
ax.grid(True)
fig, ax = plt.subplots(figsize=(13,5))
c = ax.pcolormesh(t, drange-adcp_depth, east, cmap='bwr', vmin=-0.3, vmax = 0.3)
ax.set_title('East component')
ax.set_xlim()
fig.colorbar(c, ax=ax)
ax.grid(True)
temperature = myds['temp'].values
fig, ax = plt.subplots(figsize=(13,5))
ax.plot(t, temperature, color = 'k')
ax.set_title('Temperature at the bottom (38.4m)')
ax.set_xlim([t[0], t[-1]])
ax.grid(True)
# save that to netcdf file, no need for xarray as I have only 4 variables
outfile = 'ADCP_CAAT_01.nc'
!rm -f ADCP_CAAT_01.nc
from netCDF4 import Dataset, num2date, date2num
nf_out = Dataset(outfile, "w", clobber = True , format="NETCDF4_CLASSIC")
nf_out.title = "Analyzed data for ADCP 01 (lon = 16.3882, lat = 43.519, depth = 39m)"
nf_out.description = "ADCP deployed by IOR *Hrvoje Mihanovic* on 2021-08-16"
nf_out.author = "Analysis done by Ivica Janekovic"
nf_out.type = "netCDF4"
# dimensions
nf_out.createDimension('time', None)
nf_out.createDimension('bin', north.shape[0])
nf_out.createDimension('one',1)
# ADCP station depth
depth = nf_out.createVariable('depth', 'f', ('one'), zlib=False)
depth.units = 'm'
depth.long_name = 'ADCP station depth'
depth.short_name = 'depth'
depth[:] = adcp_depth
# ADCP station longitude
lon = nf_out.createVariable('lon', 'd', ('one'), zlib=False)
lon.units = 'degrees east'
lon.long_name = 'Longitude for ADCP location'
lon.short_name ='longitude'
lon[:] = adcp_lon
# ADCP station latitude
lat = nf_out.createVariable('lat', 'd', ('one'), zlib=False)
lat.units = 'degrees north'
lat.long_name = 'Latitude for ADCP location'
lat.short_name ='latitude'
lat[:] = adcp_lat
# ADCP station time
time = nf_out.createVariable('time', 'd', ('time'), zlib=False)
time.units = "days since 2000-01-01 00:00:00"
time.calendar = 'proleptic_gregorian'
time.long_name = 'ADCP time (UTC)'
time.standard_name = 'time'
time.coordinate_type = 'time'
time[:] = date2num(t, time.units)
# Temperature at the ADCP (bottom)
temp = nf_out.createVariable('temp', 'f', ('time'), zlib=True, least_significant_digit=3, complevel=7)
temp.units = 'C'
temp.coordinates = 'time'
temp.long_name = 'Ocean temperature at the bottom (ADCP location)'
temp.short_name ='temp'
temp[:] = temperature
# Beam depths (from the free surface)
beam_depth = nf_out.createVariable('beam_depth', 'f', ('bin'), zlib=False)
beam_depth.units = "m"
beam_depth.long_name = 'ADCP beam depths (from the free surace), negative values in [m]'
beam_depth.standard_name = 'depth'
beam_depth.coordinate_type = 'bin'
beam_depth[:] = drange - adcp_depth
# Eastward current
eastward_current = nf_out.createVariable('eastward_current', 'f', ('bin','time'), fill_value=1.e+20,
zlib=True, least_significant_digit=3, complevel=7)
eastward_current.units = "ms^-1"
eastward_current.long_name = 'ADCP eastward component of ocean current'
eastward_current.standard_name = 'east_current'
eastward_current.coordinate_type = 'time, bin'
eastward_current[:] = east
# Northward current
northward_current = nf_out.createVariable('northward_current', 'f', ('bin','time'), fill_value = 1.e+20,
zlib=True, least_significant_digit=3, complevel=7)
northward_current.units = "ms^-1"
northward_current.long_name = 'ADCP northward component of ocean current'
northward_current.standard_name = 'north_current'
northward_current.coordinate_type = 'time, bin'
northward_current[:] = north
nf_out.close()