def sep_battjes(eta, ot, h, x, sten, vd=True, verbose=False): """ PARAMETERS: ----------- eta : long wave water surface elevation matrix (time,points) ot : time vector [s] h : water depth [m] x : point location [m] sten : Point averaging stencil vd : Use the extension proposed by van Dongeren et al. (2007). Defaults to True. verbose : (optional) True for some printed info RETURNS: -------- etaInc : incident wave time series [m] etaRef : reflected wave time series [m] NOTES: ------ 1. eta has to be sampled at the same rate for all points 2. numpy arrays are assumed 3. x axis should point landward (i.e. eta_inc travel with increasing x) 4. van Dongeren et al. (2007) extended the separation method of Battjes et al. (2004) by considering wave shoaling and phase speed effects. REFERENCES: ----------- Battjes, J. A., H. J. Bakkenes, T. T. Janssen, and A. R. van Dongeren, 2004: Shoaling of subharmonic gravity waves. Journal of Geophysical Research, 109, C02009, doi:10.1029/2003JC001863. van Dongeren, A., J. Battjes, T. Janssen, J. van Noorloos, K. Steenhauer, G. Steenbergen, and A. Reniers, 2007: Shoaling and shoreline dissipation of low-frequency waves. Journal of Geophysical Research, 112, C02011, doi:10.1029/2006JC003701. """ # Compute general Fourier parameters N = ot.shape[0] dt = np.mean(ot[1:] - ot[:-1]) fN = 1.0 / (2 * dt) # Compute group velocities for incoming waves and shallow water propagation # velocities for reflected waves. freq = np.fft.fftfreq(N, dt) k = np.zeros_like(eta) c = np.zeros_like(eta) cg = np.zeros_like(eta) clin = np.zeros_like(eta) # Loop over frequencies (may take advantage of the symmetry properties) if verbose: print('Long wave incident and reflected wave separation') if vd: print(' Using the van Dongeren et al. (2007) methodology') else: print(' Using the Battjes et al. (2004) methodology') print('Computing phase and group velocities based on linear theory') print(' Be patient ...') # Time loop for aa in range(k.shape[0]): # Loop over points in the array for bb in range(k.shape[1]): # Compute wave number if np.isclose(freq[aa], 0): k[aa, bb] = 0.0 c[aa, bb] = 0.0 cg[aa, bb] = 0.0 clin[aa, bb] = 0.0 else: k[aa, bb] = _gwaves.dispersion(np.abs(1.0 / freq[aa]), h[bb]) # Compute the wave celerity and group velocity c[aa, bb], n, cg[aa, bb] = _gwaves.celerity(np.abs(1.0 / freq[aa]), h[bb]) # Compute celerity based on shallow water equations clin[aa, bb] = np.sqrt(9.81 * h[bb]) # Equation numbers based on van Dongeren et al 2007 # First working on equation A1 --------------------------------------------- # Compute the Fourier transform and arrange the frequencies z_mp = np.fft.fft(eta, axis=0) freq = np.fft.fftfreq(N, dt) # The reflected waves will be found by looking at finite stencils. eta_i_b = np.zeros_like(eta) * np.NAN eta_r_b = np.zeros_like(eta) * np.NAN # Compute where to start the counter based on the stencil and the dx half_sten = int((sten - 1) / 2) ind_min = half_sten ind_max = eta.shape[1] - ind_min # Loop over array for ii in range(ind_min, ind_max): # Progress update if verbose: print(' Point ' + np.str(ii - ind_min + 1) + ' of ' + np.str(ind_max - ind_min)) # Work on a subset of eta now subset_ind = np.arange(ii - half_sten, ii + half_sten + 1, 1) cg_work = cg[:, subset_ind].copy() clin_work = clin[:, subset_ind].copy() h_work = h[subset_ind].copy() x_work = x[subset_ind].copy() z_mp_work = z_mp[:, subset_ind].copy() # Equation A2 ---------------------------------------------------------- # z_mp = q_i_mpn z_i_mRn + Q_r_mpn Z_r_mRn + e_mpn # The Fourier transform is considered as the sum of an incoming wave # component and a reflected wave component with an error term. # The next step is to assume that the amplitude and phase factors are # close to the values produced by linear wave theory. # Estimate the factors Q (Equations A4,A5,A6) freq_mat = np.repeat(np.expand_dims(freq, axis=-1), h_work.shape[0], axis=-1) q_i_mp = np.zeros_like(freq_mat) * 1j q_r_mp = np.zeros_like(freq_mat) * 1j for aa in range(x_work.shape[0]): q_i_mp[:, aa] = np.exp(-1j * np.trapz( 2.0 * np.pi * freq_mat[:, 0:aa + 1] / cg_work[:, 0:aa + 1], x_work[0:aa + 1], axis=-1)) q_r_mp[:, aa] = np.exp(1j * np.trapz( 2.0 * np.pi * freq_mat[:, 0:aa + 1] / clin_work[:, 0:aa + 1], x_work[0:aa + 1], axis=-1)) # Solve equation A3 to estimate z_i_mR and z_r_mR by using the least # squares method since the system is overdetermined -------------------- z_i_mR = np.zeros((ot.shape[0], )) * 1j z_r_mR = np.zeros_like(z_i_mR) for aa in range(ot.shape[0]): # Put system of equations in the form Ax = B q_mat = np.vstack((q_i_mp[aa, :], q_r_mp[aa, :])).T if np.sum(np.isnan(q_mat)) > 0: continue z_i_mR[aa] = 0.0 z_r_mR[aa] = 0.0 else: # Use least squares method to find solution z_i_mR[aa], z_r_mR[aa] = np.linalg.lstsq( q_mat, z_mp_work[aa, :])[0] # So far the methodology applied is the one described in Battjes (2004). # If the user selected true then the modifications of # van Dongeren et al. (2007) will be implemented. if vd: # Correction for incident waves ------------------------------------ # Phase correction equation (A7) q_i_mp *= z_mp_work / np.repeat(np.expand_dims(np.abs(z_i_mR), axis=-1), z_mp_work.shape[1], axis=-1) # Solve equation (A3) z_i_mR, z_r_mR = _vanDongeren2007A3(q_i_mp, q_r_mp, z_mp_work) # Correction for amplitude (A8) q_i_mp *= (np.abs(z_mp_work) / np.repeat(np.expand_dims(np.abs(z_i_mR), axis=-1), z_mp_work.shape[1], axis=-1)) # Solve equation (A3) z_i_mR, z_r_mR = _vanDongeren2007A3(q_i_mp, q_r_mp, z_mp_work) # Correction for reflected waves ----------------------------------- # Phase correction equation (A7) q_r_mp *= (z_mp_work / np.repeat(np.expand_dims(np.abs(z_r_mR), axis=-1), z_mp_work.shape[1], axis=-1)) # Solve equation (A3) z_i_mR, z_r_mR = _vanDongeren2007A3(q_i_mp, q_r_mp, z_mp_work) # Correction for amplitude (A8) q_r_mp *= (np.abs(z_mp_work) / np.repeat(np.expand_dims(np.abs(z_r_mR), axis=-1), z_mp_work.shape[1], axis=-1)) # Solve equation (A3) z_i_mR, z_r_mR = _vanDongeren2007A3(q_i_mp, q_r_mp, z_mp_work) # Compute the inverse Fourier transform to get the time series of the # reflected and incident waves. eta_i_b[:, ii] = np.fft.ifft(z_i_mR).real eta_r_b[:, ii] = np.fft.ifft(z_r_mR).real # End of function return eta_i_b, eta_r_b
def sep_battjes(eta,ot,h,x,sten,vd=True,verbose=False): """ PARAMETERS: ----------- eta : long wave water surface elevation matrix (time,points) ot : time vector [s] h : water depth [m] x : point location [m] sten : Point averaging stencil vd : Use the extension proposed by van Dongeren et al. (2007). Defaults to True. verbose : (optional) True for some printed info RETURNS: -------- etaInc : incident wave time series [m] etaRef : reflected wave time series [m] NOTES: ------ 1. eta has to be sampled at the same rate for all points 2. numpy arrays are assumed 3. x axis should point landward (i.e. eta_inc travel with increasing x) 4. van Dongeren et al. (2007) extended the separation method of Battjes et al. (2004) by considering wave shoaling and phase speed effects. REFERENCES: ----------- Battjes, J. A., H. J. Bakkenes, T. T. Janssen, and A. R. van Dongeren, 2004: Shoaling of subharmonic gravity waves. Journal of Geophysical Research, 109, C02009, doi:10.1029/2003JC001863. van Dongeren, A., J. Battjes, T. Janssen, J. van Noorloos, K. Steenhauer, G. Steenbergen, and A. Reniers, 2007: Shoaling and shoreline dissipation of low-frequency waves. Journal of Geophysical Research, 112, C02011, doi:10.1029/2006JC003701. """ # Compute general Fourier parameters N = ot.shape[0] dt = np.mean(ot[1:] - ot[:-1]) fN = 1.0/(2*dt) # Compute group velocities for incoming waves and shallow water propagation # velocities for reflected waves. freq = np.fft.fftfreq(N,dt) k = np.zeros_like(eta) c = np.zeros_like(eta) cg = np.zeros_like(eta) clin = np.zeros_like(eta) # Loop over frequencies (may take advantage of the symmetry properties) if verbose: print('Long wave incident and reflected wave separation') if vd: print(' Using the van Dongeren et al. (2007) methodology') else: print(' Using the Battjes et al. (2004) methodology') print('Computing phase and group velocities based on linear theory') print(' Be patient ...') # Time loop for aa in range(k.shape[0]): # Loop over points in the array for bb in range(k.shape[1]): # Compute wave number if np.isclose(freq[aa],0): k[aa,bb] = 0.0 c[aa,bb] = 0.0 cg[aa,bb] = 0.0 clin[aa,bb] = 0.0 else: k[aa,bb] = _gwaves.dispersion(np.abs(1.0/freq[aa]),h[bb]) # Compute the wave celerity and group velocity c[aa,bb],n,cg[aa,bb] = _gwaves.celerity(np.abs(1.0/freq[aa]), h[bb]) # Compute celerity based on shallow water equations clin[aa,bb] = np.sqrt(9.81*h[bb]) # Equation numbers based on van Dongeren et al 2007 # First working on equation A1 --------------------------------------------- # Compute the Fourier transform and arrange the frequencies z_mp = np.fft.fft(eta,axis=0) freq = np.fft.fftfreq(N,dt) # The reflected waves will be found by looking at finite stencils. eta_i_b = np.zeros_like(eta) * np.NAN eta_r_b = np.zeros_like(eta) * np.NAN # Compute where to start the counter based on the stencil and the dx half_sten = int((sten - 1)/2) ind_min = half_sten ind_max = eta.shape[1] - ind_min # Loop over array for ii in range(ind_min,ind_max): # Progress update if verbose: print(' Point ' + np.str(ii-ind_min+1) + ' of ' + np.str(ind_max-ind_min)) # Work on a subset of eta now subset_ind = np.arange(ii-half_sten,ii+half_sten+1,1) cg_work = cg[:,subset_ind].copy() clin_work = clin[:,subset_ind].copy() h_work = h[subset_ind].copy() x_work = x[subset_ind].copy() z_mp_work = z_mp[:,subset_ind].copy() # Equation A2 ---------------------------------------------------------- # z_mp = q_i_mpn z_i_mRn + Q_r_mpn Z_r_mRn + e_mpn # The Fourier transform is considered as the sum of an incoming wave # component and a reflected wave component with an error term. # The next step is to assume that the amplitude and phase factors are # close to the values produced by linear wave theory. # Estimate the factors Q (Equations A4,A5,A6) freq_mat = np.repeat(np.expand_dims(freq,axis=-1), h_work.shape[0],axis=-1) q_i_mp = np.zeros_like(freq_mat)*1j q_r_mp = np.zeros_like(freq_mat)*1j for aa in range(x_work.shape[0]): q_i_mp[:,aa] = np.exp(-1j*np.trapz(2.0*np.pi*freq_mat[:,0:aa+1]/ cg_work[:,0:aa+1], x_work[0:aa+1],axis=-1)) q_r_mp[:,aa] = np.exp(1j*np.trapz(2.0*np.pi*freq_mat[:,0:aa+1]/ clin_work[:,0:aa+1], x_work[0:aa+1],axis=-1)) # Solve equation A3 to estimate z_i_mR and z_r_mR by using the least # squares method since the system is overdetermined -------------------- z_i_mR = np.zeros((ot.shape[0],)) * 1j z_r_mR = np.zeros_like(z_i_mR) for aa in range(ot.shape[0]): # Put system of equations in the form Ax = B q_mat = np.vstack((q_i_mp[aa,:],q_r_mp[aa,:])).T if np.sum(np.isnan(q_mat)) > 0: continue z_i_mR[aa] = 0.0 z_r_mR[aa] = 0.0 else: # Use least squares method to find solution z_i_mR[aa],z_r_mR[aa] = np.linalg.lstsq(q_mat, z_mp_work[aa,:])[0] # So far the methodology applied is the one described in Battjes (2004). # If the user selected true then the modifications of # van Dongeren et al. (2007) will be implemented. if vd: # Correction for incident waves ------------------------------------ # Phase correction equation (A7) q_i_mp *= z_mp_work / np.repeat(np.expand_dims(np.abs(z_i_mR), axis=-1), z_mp_work.shape[1],axis=-1) # Solve equation (A3) z_i_mR,z_r_mR = _vanDongeren2007A3(q_i_mp,q_r_mp,z_mp_work) # Correction for amplitude (A8) q_i_mp *= (np.abs(z_mp_work) / np.repeat(np.expand_dims(np.abs(z_i_mR),axis=-1), z_mp_work.shape[1],axis=-1)) # Solve equation (A3) z_i_mR,z_r_mR = _vanDongeren2007A3(q_i_mp,q_r_mp,z_mp_work) # Correction for reflected waves ----------------------------------- # Phase correction equation (A7) q_r_mp *= (z_mp_work / np.repeat(np.expand_dims(np.abs(z_r_mR),axis=-1), z_mp_work.shape[1],axis=-1)) # Solve equation (A3) z_i_mR,z_r_mR = _vanDongeren2007A3(q_i_mp,q_r_mp,z_mp_work) # Correction for amplitude (A8) q_r_mp *= (np.abs(z_mp_work) / np.repeat(np.expand_dims(np.abs(z_r_mR),axis=-1), z_mp_work.shape[1],axis=-1)) # Solve equation (A3) z_i_mR,z_r_mR = _vanDongeren2007A3(q_i_mp,q_r_mp,z_mp_work) # Compute the inverse Fourier transform to get the time series of the # reflected and incident waves. eta_i_b[:,ii] = np.fft.ifft(z_i_mR).real eta_r_b[:,ii] = np.fft.ifft(z_r_mR).real # End of function return eta_i_b,eta_r_b
def wave_tracks_predictor(local_maxima,wave_height,ot,xInst,x,h,wp=None): """ Code to track the wave crests througout a linear instrument array using bathymetric data and linear wave theory as predictors USAGE: ------ wave_tracks = wave_tracks(local_extrema,ot_lag,twind) PARAMETERS: ----------- local_maxima : Array of local maxima indices (see local_extrema) wave_height : Array of wave heights (see wave_height) ot : Time vector [s] xInst : Instrument easting x : Easting of topography/bathymetry [m] Must increase landward h : Water depth [m] wp : (optional) Representative wave period [s] RETURNS: -------- wave_tracks : Best approximation of the wave position across shore. wave_ind : Incides of the wave tracks for slicing local_maxima and wave_height NOTES: ------ 1. Do not include NANs in the x and h variables. 2. xInst should be within the limits of x 3. If waves were not tracked an index of -999999 will be used. 4. If wp is not provided, the shallow water wave celerity will be used as predictor. """ # Compute the wave celerity based on linear wave theory if the wave period # is provided, otherwise the shallow water celerity is computed if wp: cel = np.zeros_like(h) for aa in range(cel.shape[0]): tmpCel = _gwaves.celerity(wp,h[aa]) cel[aa] = tmpCel[0] del tmpCel else: cel = (9.81*h)**0.5 # Compute time of travel timeTravel = np.zeros_like(x) timeTravel[1:] = np.cumsum(2.0/(cel[1:]+cel[:-1])*(x[1:] - x[:-1])) # Interpolate the location of instruments timeInt = spi.interpolate.interp1d(x,timeTravel) timeOffset = timeInt(xInst) # Normalize the time travel vector timeOffset -= timeOffset[0] # Find the water depth at the instruments depthInt = spi.interpolate.interp1d(x,h) depth = depthInt(xInst) # Expected average velocity between instrument locations # Forward differences of course. meanCel = np.zeros_like(xInst) * np.NAN meanCel[1:] = np.abs(np.diff(xInst)) / np.diff(timeOffset) # Loop over local maxima and preallocate the variables wave_tracks = np.ones((len(local_maxima[0]),xInst.shape[0]), dtype=np.int64) * -999999 wave_ind = np.copy(wave_tracks) for aa in range(wave_tracks.shape[0]): # Preallocate temporary variables # tmp_ind will be allocated into wave_tracks # tmp_wave_ind will be allocated into wave_ind tmp_ind = np.ones((wave_tracks.shape[1],)).astype(int) * -999999 tmp_wave_ind = np.copy(tmp_ind) tmp_ind[0] = local_maxima[0][aa] tmp_wave_ind[0] = aa # Loop over sensors for bb in range(1,tmp_ind.shape[0]): # Previous wave time tmp_ot = ot[tmp_ind[bb-1]] # Find the next wave based on a celerity estimate tmp_dt = ot[local_maxima[bb]] - tmp_ot tmpCel = np.abs(xInst[bb-1] - xInst[bb]) / tmp_dt tmp_min_ind = np.argmin(np.abs(tmpCel - meanCel[bb])) # Check if we are tracking well into the past based on half of the # time series length futureFlag = ((ot[local_maxima[bb][tmp_min_ind]] - tmp_ot) < (ot[0] - ot[-1])/2.0) if futureFlag: break # Wave breaking flag based on saturated breaking (H=kh) dWH = ((wave_height[bb-1][tmp_wave_ind[bb-1]] - wave_height[bb]) / wave_height[bb-1][tmp_wave_ind[bb-1]]) maxdWH = (depth[bb-1] - depth[bb])/depth[bb-1] # Three checks here to consider the previous wave as the correct one # All must be true # 1. If the wave moves slower than the shallow water celerity # 2. The new trajectory does not exceed amplitude dispersion # 3. The wave is not much smaller than the one it maps to # Find amplitude dispersion effect ampDisp = (9.81*wave_height[bb-1][tmp_wave_ind[bb-1]])**0.5 if (tmpCel[tmp_min_ind] < meanCel[bb] and tmpCel[tmp_min_ind-1] < (meanCel[bb]+ampDisp) and dWH[tmp_min_ind-1]<=dWH[tmp_min_ind]): tmp_min_ind -= 1 # Check if we are tracking into the past futureFlag = (ot[local_maxima[bb][tmp_min_ind]] - tmp_ot) < 0 if futureFlag: tmp_min_ind += 1 # Stand alone wave breaking flag if (dWH[tmp_min_ind] > maxdWH and dWH[tmp_min_ind-1] < dWH[tmp_min_ind]): # Pick the previous wave if the speed makes sense dCel = (meanCel[bb] - tmpCel[tmp_min_ind-1]) / meanCel[bb] # Check if we are tracking into the past futureFlag = ot[local_maxima[bb][tmp_min_ind-1]] > tmp_ot # Negative means faster (need to do something objective here) if dCel > -0.5 and futureFlag: tmp_min_ind -= 1 # Finally check for wave crossing (brute force bore capture) if aa > 0: if local_maxima[bb][tmp_min_ind] < wave_tracks[aa-1,bb]: tmp_min_ind = wave_ind[aa-1,bb] # Allocate in arrays tmp_wave_ind[bb] = tmp_min_ind tmp_ind[bb] = local_maxima[bb][tmp_min_ind] # Allocate wave data wave_tracks[aa,:] = np.copy(tmp_ind) wave_ind[aa,:] = np.copy(tmp_wave_ind) # Get out return wave_tracks,wave_ind