def track_orient(x,y,orient=False): # ;Calculate track orientation (on 3 points lagrangian interpolation - see DERIV help page) getorient=orient dxy=deriv(x,y) dx=np.median(deriv(x)) dy=np.median(deriv(y)) orient = np.arctan(dxy) if dx > 0 else np.arctan(dxy) + np.pi #Rotate backward (180�) #orientation when track goes #westward orient = np.remainder(orient,2.0*np.pi) # if (medorient < 0) : orient += 2.0*np.pi # if (medorient > 2.0*np.pi) THEN orient -= 2D*!dpi #Sense is positive for ascending tracks sense = True if np.median(orient) < np.pi else False if getorient : return sense, orient else : return sense
def track_orient(x, y, orient=False): # ;Calculate track orientation (on 3 points lagrangian interpolation - see DERIV help page) getorient = orient dxy = deriv(x, y) dx = np.median(deriv(x)) dy = np.median(deriv(y)) orient = np.arctan( dxy) if dx > 0 else np.arctan(dxy) + np.pi #Rotate backward (180�) #orientation when track goes #westward orient = np.remainder(orient, 2.0 * np.pi) # if (medorient < 0) : orient += 2.0*np.pi # if (medorient > 2.0*np.pi) THEN orient -= 2D*!dpi #Sense is positive for ascending tracks sense = True if np.median(orient) < np.pi else False if getorient: return sense, orient else: return sense
alti_pattern = '/archive/MSA/rdussurget/sauvegarde_13032013/VMShared/data/alti/regional/mfstep-dt/j2_cf/dt_upd_med_j2_sla_vxxc_*.nc' alti=alti_data(alti_pattern,limit=limit,verbose=verbose,time_range=trange.tolist(),track=t) alti.update_with_slice(alti.slice('track', t)) alti.reorder() cycle_list=alti.cycle_list() tracks=alti.track_list() trange_str = alti.time_range()[0] nt=len(cycle_list) sla=alti.sla*100.0 lon=alti.lon lat=alti.lat dst=AT.calcul_distance(lat,lon) dx=np.median(AT.deriv(dst)) N=len(lon) slaft=np.ma.empty(sla.shape) ugpl=slaft.copy() ugpllo=slaft.copy() ugfd=slaft.copy() ugfds=slaft.copy() #Filter small scales for i in np.arange(nt): slaft[i,:]=AT.loess(sla[i,:], dst, 40.) ugpl[i,:] = geost_1d(lon,lat,sla[i,:]/100.,pl04=True,p=20.,q=20.) ugpllo[i,:] = geost_1d(lon,lat,slaft[i,:]/100.,pl04=True,p=20.,q=20.) ugfd[i,:] = geost_1d(lon,lat,slaft[i,:]/100.,pl04=False) dumlon,dumlat,ugfds[i,:-1] = geost_1d(lon,lat,slaft[i,:]/100.,pl04=False,strict=True)
time_range=trange.tolist(), track=t) alti.update_with_slice(alti.slice('track', t)) alti.reorder() cycle_list = alti.cycle_list() tracks = alti.track_list() trange_str = alti.time_range()[0] nt = len(cycle_list) sla = alti.sla * 100.0 lon = alti.lon lat = alti.lat dst = AT.calcul_distance(lat, lon) dx = np.median(AT.deriv(dst)) N = len(lon) slaft = np.ma.empty(sla.shape) ugpl = slaft.copy() ugpllo = slaft.copy() ugfd = slaft.copy() ugfds = slaft.copy() #Filter small scales for i in np.arange(nt): slaft[i, :] = AT.loess(sla[i, :], dst, 40.) ugpl[i, :] = geost_1d(lon, lat, sla[i, :] / 100., pl04=True,
def geost_1d(*args, **kwargs): #(lon,lat,nu): OR (dst,nu) """ ;+ ; ; GEOST_1D : Compute geostrophic speeds from a sea level dataset <br /> ; ; Reference : Powell, B. S., et R. R. Leben (2004), An Optimal Filter for <br /> ; Geostrophic Mesoscale Currents from Along-Track Satellite Altimetry, <br /> ; Journal of Atmospheric and Oceanic Technology, 21(10), 1633-1642. ; ; @param lon {in}{optional}{type:NUMERIC} longitude in degrees ; @param lat {in}{optional}{type:NUMERIC} latitude in degrees ; @param dst {in}{optional}{type:NUMERIC} along-track distance. ; @param z {in}{required}{type:NUMERIC} sea level surface. Can either be absolute<br /> ; values (SSH) or relative values (SLA). This MUST be given in METERS. ; @keyword strict {in}{optional}{type:BOOLEAN} If True, compute gradient at mid-distance. ; @keyword pl04 {in}{optional}{type:BOOLEAN} If True, use the Powell & Leben 2004 method. ; ; @returns Geostrophic velocity component, positive eastward ; ; ; ; @author Renaud DUSSURGET, LEGOS/CTOH ; @history Created Sep. 2009 from genweights.m (Brian Powell (c) 2004, <br /> ; University of Colorado, Boulder)<br /> ; Modified May 2010 to be compliant with 20Hz datasets (p & n can vary).<br /> ; Warining may also be issued for data with holes within the width of the<br /> ; window.<br /> ; Modified June 2010 to include filtering window width in KM instead of nb. of<br /> ; points (Equivalent btw. 1Hz and 20Hz data).<br /> ; ; @uses CALCUL_DISTANCE, EXIST, GENWEIGTHS, SETINTERSECTION, SETUNION, <br /> ; OPTIMAL_SLOPE, GRAVITY, CORIOLIS, TRACK_ORIENT ; ; @example dummy1=geost_1D(lon,lat,sla,pl04=True,p=11,q=11) :<br /> ; Return along-track velocity anomalies using a 11km by 11km Powell & Leben 2004 filter window <br /> ; dummy2=geost_1D(dst,sla,strict=True) :<br /> ; Return along-track velocity anomalies computed at mid-distance <br /> ; ;- """ lon = args[0] lat = args[1] dst = args[2] if len(args) == 4 else calcul_distance( lat, lon) * 1e3 #distance in meters nu = args[3] if len(args) == 4 else args[2] isVector = len(np.shape(nu)) == 1 #Reshape nu if vector if isVector: nu = np.reshape(nu, (len(nu), 1)) nt = np.shape(nu)[1] if not isVector else 1 sh = nu.shape nufilt = np.ma.array(np.empty(sh), mask=True, dtype=nu.dtype) pl04 = kwargs.pop('pl04', False) filter = kwargs.pop('filter', None) strict = kwargs.pop('strict', False) verbose = kwargs.pop('verbose', False) if filter is not None: for t in np.arange(nt): nufilt[:, t] = loess(nu[:, t], dst, filter * 1e3) nu = nufilt if pl04: ug = np.ma.array(np.empty(sh), mask=True, dtype=nu.dtype) for t in np.arange(nt): ug[:, t] = powell_leben_filter_km(lon, lat, nu[:, t], verbose=verbose, **kwargs) if isVector: ug = ug.flatten() return ug #If strict option is set to True, compute gradients at mid-distance between points if strict: lon = (lon[1:] - lon[:-1]) / 2. + lon[0:-1] lat = (lat[1:] - lat[:-1]) / 2. + lat[0:-1] #Compute gravitational & coriolis forces if strict: sh = (sh[0] - 1, sh[1]) g = np.repeat(gravity(lat), nt).reshape(sh) f = np.repeat(coriolis(lat), nt).reshape(sh) #Compute SSH 1st derivative # dh = deriv(dst,nu) #(deriv is very bad...) dh = np.ma.array(np.empty(sh), mask=True, dtype=nu.dtype) for t in np.arange(nt): dh[:, t] = (nu[1:, t] - nu[:-1, t]) / (dst[1:] - dst[:-1]) if strict else deriv( dst, nu[:, t]) #Compute geostrophy # print f # print g # print dh ug = -(g * dh) / (f) #Inverse sign of ug for descending tracks as Coriolis is oriented to the right #northward if (not track_orient(lon, lat)): #descending tracks ug *= -1 if isVector: ug = ug.flatten() return (lon, lat, ug) if strict else ug
def decorrelation_scale(var, lat, lon, ind, verbose=1): ''' solid_body_scale @summary: Compute the decorrelation length-scale of detected eddies. @note: This is the original technique applied in : Dussurget, R, F Birol, R.A. Morrow, et P. De Mey. 2011. « Fine Resolution<br /> Altimetry Data for a Regional Application in the Bay of Biscay ». Marine<br /> Geodesy 2 (34): 1‑30. doi:10.1080/01490419.2011.584835. @note: A linear regression is applied between the two points around the decorrelation<br /> scale to better detect its position. @note: If no sufficient data is found on one of both sides, eddy is considered as<br /> symmetric and scales are thus only computed from one side. @param var: variable on which to apply the analysis : SLA, wavelet-filtered SLA,<br /> daughter wavelets, etc... @param lon, lat: Longitude/latitude arrays. @ind: Indices of detected eddies. @return diameter, symmetric : Diameter (km) of detected eddies, and symmetric flag to<br /> check whether symmetry assumption was used or not. @author: Renaud DUSSURGET, LER/PAC IFREMER. @since : November 2012. @change: Create in November 2012 by RD. ''' xid = ind[1] yid = ind[0] ne = np.size(xid) diameter = np.zeros(ne, dtype=np.float64) symmetric = np.zeros(ne, dtype=bool) if verbose >= 1: print '\tdecorrelation_scale() running' for j in np.arange(ne): # print j #Extract current SLA profile cursla = var[xid[j], :] fg = ~cursla.mask dumy = np.where(np.arange(len(cursla))[fg] == yid[j])[0][ 0] #update yid with compressed SLA array cursla = cursla[fg] cursla -= np.median(cursla) #Fill gaps dst, dumlon, dumlat, dumsla, gaplen, ngaps, gapedges, interpolated = grid_track( lat[fg], lon[fg], cursla) #Update yid with resampled SLA array dumy = np.arange(len(dst))[~interpolated][dumy] #Shrink data if there are any gaps greater than 3 points (ie. no valid interpolation) # if (gaplen > 3).any() : # gapedges=gapedges[:,gaplen > 3] #Remove gaps smaller than 3 points # left=gapedges[1,np.where(gapedges[1,:] >= dumy)[0][0]] if (gapedges[1,:] < dumy).any() else 0 # right=gapedges[0,np.where(gapedges[0,:] >= dumy)[0][0]] if (gapedges[0,:] >= dumy).any() else len(dumsla)-1 # dumsla=dumsla[left:right+1] #Get sla profile on both sides of the eddy dumsla_r = dumsla[dumy:] #np.roll(cursla,-dumy) dumsla_l = dumsla[dumy:0:-1] #np.roll(cursla[::-1],dumy+1) #Compute the auto-correlation on each sides. nr = len(dumsla_r) nl = len(dumsla_l) acorr_l = np.zeros(nl) acorr_r = np.zeros(nr) lag_r = np.arange(nr) lag_l = np.arange(nl) for i, l in enumerate(lag_l): acorr_l[i] = np.corrcoef(dumsla_l, np.roll(dumsla_l, l))[0][1] for i, l in enumerate(lag_r): acorr_r[i] = np.corrcoef(dumsla_r, np.roll(dumsla_r, l))[0][1] #detect first zero crossing of auto-corr function with the derivative of its absolute zc_l = (np.where( deriv(np.abs(acorr_l)) > acorr_l))[0] if nl >= 3 else [] zc_r = (np.where( deriv(np.abs(acorr_r)) > acorr_r))[0] if nr >= 3 else [] zer_cross = [] if len(zc_l) != 0: zer_cross = np.append(zer_cross, zc_l[0]) if len(zc_r) != 0: zer_cross = np.append(zer_cross, zc_r[0]) #Linearly interpolate the auto-correlation function to get the zero-crossing distance fit = np.ma.array(np.zeros((2, 2)), mask=np.ones((2, 2), dtype=bool)) if len(zc_l) != 0: fit[0, :] = np.ma.array(np.polyfit( dst[zc_l[0] - 1:zc_l[0] + 1], acorr_l[zc_l[0] - 1:zc_l[0] + 1], 1), mask=np.zeros((2), dtype=bool)) if len(zc_r) != 0: fit[1, :] = np.ma.array(np.polyfit( dst[zc_r[0] - 1:zc_r[0] + 1], acorr_r[zc_r[0] - 1:zc_r[0] + 1], 1), mask=np.zeros((2), dtype=bool)) #If no decorrelation is found on one side, use twice the decorrelation on valid side if fit.mask.sum() == 2: fit[fit.mask] = fit[~fit.mask] symmetric[j] = True #Get diameter diameter[j] = -((fit[0][1] / fit[0][0]) + (fit[1][1] / fit[1][0])) return diameter, symmetric
def solid_body_scale(var, lat, lon, ind, verbose=1, **kwargs): ''' solid_body_scale @summary: Compute the diameter of eddy core using maxima of geostrophic velocities<br /> computed on both sides of the eddy, and computes the equivalent Relative<br /> Vorticity for a solid body rotating eddy. @note: This technique was first applied in Le Hénaff et al., 2012. Cyclonic<br /> activity in the eastern Gulf of Mexico: characterization. Submitted to <br /> Progress in Oceanography. @note: A 2nd order polynomial over 3-4 points around the velocity maximum is<br /> computed to better detect its position. @note: Geostrophic velocities are computed using the Powell and Leben (2004)<br /> methodology - powell_leben_filter_km() function. Filtering parameters are<br/> p=q=12km on each sides of the point. Powell, B.S., et R.R. Leben. 2004. « An Optimal Filter for Geostrophic Mesoscale<br/> Currents from Along-Track Satellite Altimetry ». Journal of Atmospheric and<br/> Oceanic Technology 21 (10) (octobre 1): 1633‑1642. @param var: variable on which to apply the analysis : SLA, wavelet-filtered SLA,<br /> daughter wavelets, etc... @param lon, lat: Longitude/latitude arrays. @ind: Indices of detected eddies. @return diameter, relvort : Diameter (km) and Relative Vorticity (s-1) of detected eddies. @author: Renaud DUSSURGET, LER/PAC IFREMER. @since : November 2012. @change: Created in November 2012 by RD. ''' #Set defaults values for geostrophic velocities p = kwargs.pop('p', 20.) q = kwargs.pop('q', p) filter = kwargs.pop('filter', 40.) if verbose >= 1: print '\tsolid_body_scale() running: SLA filtering prior computation of velocities:{0} km, Velocity filtering:{1} km'.format( np.int(filter) if filter is not None else 'None', np.int(p + q)) xid = ind[1] yid = ind[0] ne = np.size(xid) diameter = np.zeros(ne, dtype=np.float32) #Peak-to-peak diameter amplitude = np.zeros( ne, dtype=np.float32) #Amplitude over peak-to-peak baseline north = np.zeros(ne, dtype=np.float32) #Position of northern edge south = np.zeros(ne, dtype=np.float32) #Position of southern edge relvort = np.zeros( ne, dtype=np.float32 ) #Estimated relative vorticity (linear fitted of speed vs radius - chose another model) rk_diameter = np.zeros( ne, dtype=np.float32) #Peak-to-peak diameter of the Rankine vortex rk_relvort = np.zeros( ne, dtype=np.float32 ) #Estimated relative vorticity (for a fitted rankine vortex model) rk_center = np.zeros( ne, dtype=int) #Center of unbiased rankine vortex (cf. self advection) self_advect = np.zeros( ne, dtype=np.float32) #Self advection (velocity anomaly) for j in np.arange(ne): # print j #Extract current SLA profile cursla = var[xid[j], :] fg = ~cursla.mask dumy = np.where(np.arange(len(cursla))[fg] == yid[j])[0][ 0] #update yid with compressed SLA array cursla = cursla[fg] cursla -= np.median(cursla) #Fill gaps dst, dumlon, dumlat, dumsla, gaplen, ngaps, gapedges, interpolated = grid_track( lat[fg], lon[fg], cursla) ugeo = geost_1d(dumlon, dumlat, dumsla, pl04=True, filter=filter, p=p, q=q) ncur = len(dst) northward = dumlat[-1] > dumlat[0] #Update yid with resampled SLA array dumy = np.arange(len(dst))[~interpolated][dumy] # #Shrink data if there are any gaps greater than 3 points (ie. no valid interpolation) # if (gaplen > 3).any() : # gapedges=gapedges[:,gaplen > 3] #Remove gaps smaller than 3 points # left=gapedges[1,np.where(gapedges[1,:] >= dumy)[0][0]] if (gapedges[1,:] < dumy).any() else 0 # right=gapedges[0,np.where(gapedges[0,:] >= dumy)[0][0]] if (gapedges[0,:] >= dumy).any() else len(dumsla)-1 # dumsla=dumsla[left:right+1] #Get sla profile on both sides of the eddy dumsla_n = dumsla[dumy:] if northward else dumsla[dumy:0:-1] dumsla_s = dumsla[dumy:0:-1] if northward else dumsla[dumy:] ugeo_n = ugeo[dumy:] if northward else ugeo[dumy:0:-1] ugeo_s = ugeo[dumy:0:-1] if northward else ugeo[dumy:] # dumsla_s = dumsla[dumy:0:-1] #np.roll(cursla[::-1],dumy+1) nn = len(dumsla_n) ns = len(dumsla_s) iscyclone = dumsla[dumy] < 0 # #If not enough data on one side, take the opposite # # if nn < 3 : # dumsla_n = dumsla_s # ugeo_n = ugeo_s # nn=ns # if ns < 3 : # dumsla_s = dumsla_n # ugeo_s = ugeo_n # ns=nn #Detect local velocity maxima on both sides of eddy (we keep only the four first peaks) if (iscyclone ): #cyclone : negative speeds to the north, positive to the south mx_s = np.where(maximum_filter1d(ugeo_s, 3) == ugeo_s)[0] mx_n = np.where(maximum_filter1d(-ugeo_n, 3) == -ugeo_n)[0] elif ( not iscyclone ): #anticyclone : positive speeds to the north, negative to the south mx_s = np.where(maximum_filter1d(-ugeo_s, 3) == -ugeo_s)[0] mx_n = np.where(maximum_filter1d(ugeo_n, 3) == ugeo_n)[0] else: raise Exception('This case is not possible') #We only retain peaks with a SLA difference over than 1 cm from SLA peak to avoid points near peak #Rq: This happens when peak is found at eddy center... This could possibly avoided? mx_s = mx_s[(mx_s != 0) & (np.abs(dumsla_s[mx_s] - dumsla[dumy]) > 0.01)] mx_n = mx_n[(mx_n != 0) & (np.abs(dumsla_n[mx_n] - dumsla[dumy]) > 0.01)] #Replace with data from the opposite side if no maxima are found if len(mx_n) == 0: dumsla_n = dumsla_s.copy() ugeo_n = ugeo_s.copy() nn = ns mx_n = mx_s if len(mx_s) == 0: dumsla_s = dumsla_n.copy() ugeo_s = ugeo_n.copy() ns = nn mx_s = mx_n mx_s = mx_s[0] #Retain first peak only mx_n = mx_n[0] #Retain first peak only # north[j] = dumy - mx_n # south[j] = mx_s amplitude[j] = np.abs(dumsla[dumy] - np.mean([dumsla_n[mx_n], dumsla_s[mx_s]])) #Show detection # plt.plot(dst,dumsla);plt.plot(dst[dumy],dumsla[dumy],'or');plt.plot(dst[dumy+mx_n],dumsla[dumy+mx_n],'og');plt.plot(dst[dumy-mx_s],dumsla[dumy-mx_s],'og');plt.show() #Fit a 2nd order polynomial on the 4 points surrounding the extrema if mx_s != ns - 1: #if the peak is not the furthest point if np.abs(ugeo_s[mx_s - 1]) > np.abs(ugeo_s[mx_s + 1]): fit = np.polyfit( dst[mx_s - 1:mx_s + 3 if mx_s + 3 <= ns else ns], ugeo_s[mx_s - 1:mx_s + 3 if mx_s + 3 <= ns else ns], 2) else: fit = np.polyfit(dst[mx_s - 2 if mx_s >= 2 else 0:mx_s + 2], ugeo_s[mx_s - 2 if mx_s >= 2 else 0:mx_s + 2], 2) else: fit = np.polyfit(dst[mx_s - 2 if mx_s >= 2 else 0:mx_s + 1], ugeo_s[mx_s - 2 if mx_s >= 2 else 0:mx_s + 1], 2) radius_s = (-fit[1]) / ( 2 * fit[0]) #This is the minimum value of the 2nd order polynomial if (mx_s > 1) and (mx_s < ns - 1) and (mx_n > 1) and (mx_n < ns - 1): if (radius_s > dst[mx_s - 1]) & (radius_s < dst[mx_s + 1]): diameter[j] += radius_s else: diameter[j] += dst[mx_s] else: diameter[j] += dst[mx_s] # dtoto=np.arange(dst[mx_s-1],dst[mx_s+3 if mx_s+3 <= ns else ns]) # toto=fit[2]+dtoto*fit[1]+(dtoto**2)*fit[0] # plt.plot(dst[mx_s-1:mx_s+3 if mx_s+3 <= ns else ns],ugeo_s[mx_s-1:mx_s+3 if mx_s+3 <= ns else ns]);plt.plot(dtoto,toto) if mx_n != nn - 1: if np.abs(ugeo_n[mx_n - 1]) > np.abs(ugeo_n[mx_n + 1]): fit = np.polyfit( dst[mx_n - 1:mx_n + 3 if mx_n + 3 <= nn else nn], ugeo_n[mx_n - 1:mx_n + 3 if mx_n + 3 <= nn else nn], 2) else: fit = np.polyfit(dst[mx_n - 2 if mx_n >= 2 else 0:mx_n + 2], ugeo_n[mx_n - 2 if mx_n >= 2 else 0:mx_n + 2], 2) else: fit = np.polyfit(dst[mx_n - 2 if mx_n >= 2 else 0:mx_n + 1], ugeo_n[mx_n - 2 if mx_n >= 2 else 0:mx_n + 1], 2) radius_n = (-fit[1]) / ( 2 * fit[0]) #This is the minimum value of the 2nd order polynomial if (mx_s > 1) and (mx_s < ns - 1) and (mx_n > 1) and (mx_n < ns - 1): if (radius_n > dst[mx_n - 1]) & (radius_n < dst[mx_n + 1]): diameter[j] += radius_n else: diameter[j] += dst[mx_n] else: diameter[j] += dst[mx_n] # diameter[j] += np.abs((-fit[1]) / (2 * fit[0])) # print diameter[j], \ # calcul_distance(dumlat[dumy-mx_s],dumlon[dumy-mx_s],dumlat[dumy+mx_n],dumlon[dumy+mx_n]), \ # np.abs(dst[dumy] - dst[dumy+mx_n]) + np.abs(dst[dumy] - dst[dumy-mx_s]) , \ # np.abs(dst[mx_n]) + np.abs(dst[mx_s]) # dumdiam=np.append(dumdiam,np.abs(dst[mx_n]) + np.abs(dst[mx_s])) if j > 0 else [np.abs(dst[mx_n]) + np.abs(dst[mx_s])] # dtoto=np.arange(dst[mx_n-1],dst[mx_n+3 if mx_n+3 <= nn else nn]) # toto=fit[2]+dtoto*fit[1]+(dtoto**2)*fit[0] # plt.plot(dst[mx_n-1:mx_n+3 if mx_n+3 <= nn else nn],ugeo_n[mx_n-1:mx_n+3 if mx_n+3 <= nn else nn]);plt.plot(dtoto,toto);plt.show() #Compute relative vorticity #--> Linear fitting of speed against distance # print j curdst = np.append(-dst[mx_s:0:-1] * 1e3, dst[1:mx_n + 1] * 1e3) curugeo = np.append(ugeo_s[mx_s:0:-1], ugeo_n[1:mx_n + 1]) relvort[j] = -np.polyfit(curdst, curugeo, 1)[0] # if northward : relvort[j]*=-1. # print relvort[j] #Fit a Rankine vortex profile #First center eddy on zero (remove self-advection) Vanom = np.mean([ugeo_s[mx_s], ugeo_n[mx_n]]) # V=np.max(np.abs([ugeo_s[mx_s],ugeo_n[mx_n]]-Vanom)) #estimated circulation V = ugeo_n[ mx_n] - Vanom #estimated circulation (negative northward for cyclones, positive for anticyclones) R = (diameter[j] / 2.0) if northward else -(diameter[j] / 2.0) #estimated radius dx = np.median(deriv(dst)) #sampling # print j, len(np.abs(ugeo-Vanom)[np.abs(dst - dst[dumy]) < np.abs(R)]) rid = np.arange(ncur)[np.abs(dst - dst[dumy]) < np.abs(R)][np.argmin( np.abs(ugeo - Vanom)[np.abs(dst - dst[dumy]) < np.abs(R)])] r = (dst - dst[dumy])[rid] #distance offset to debiased eddy pn = np.ceil(R / dx) #Number of points to theorical peaks u1 = ugeo_s[:pn * 4][::-1] u2 = ugeo_n[1:pn * 4] d1 = dst[0:len(u1)] d2 = dst[1:len(u2) + 1] # curdst2=np.append(-d1[::-1],d2) # curugeo2=np.append(u1,u2) # [Rout, Vout], flag = optimize.leastsq(resid, [R,V], args=(dst-dst[dumy]-r,ugeo-Vanom) [Rout, Vout], flag = leastsq_bounds(resid, [R, V], [[0, 1.5 * R], [0, 2 * V]], args=(dst - dst[dumy] - r, ugeo - Vanom)) # if (iscyclone and northward) or (not iscyclone and not northward): # [Rout, Vout], flag = leastsq_bounds( resid, [R,V], [[0,R],[2*V,0]], args=(dst-dst[dumy]-r,ugeo-Vanom)) #constrained lsq fit # else : # [Rout, Vout], flag = leastsq_bounds( resid, [R,V], [[0,R],[0,2*V]], args=(dst-dst[dumy]-r,ugeo-Vanom)) rk_diameter[j] = Rout * 2.0 rk_relvort[j] = Vout / ( Rout * 1e3 ) #if ( (Rout/R) < 1.5) else relvort[j] #This is now constrained whithin LS fitting if (northward): rk_relvort[j] *= -1. # print rk_relvort[j], relvort[j] self_advect[j] = Vanom rid_input = nearest(lon, dumlon[np.arange(ncur)[rid]]) if calcul_distance(lat[rid_input], lon[rid_input], dumlat[rid], dumlon[rid]) < dx: rk_center[j] = rid_input else: rk_center[j] = rid print '[kernel.getScales()]WARNING:center has not been offsetted due to its distance to original location' # dumsla_n[mx_n] # try : # plt.subplot(2,1,1);plt.title('Eddy #{0} (x:{1} , t:{2})\nRV: rk={3}, lin={4}'.format(j,xid[j],yid[j],rk_relvort[j],relvort[j])) # plt.plot(dst-dst[dumy]-r,dumsla,'-ok',markersize=2);plt.plot(0,dumsla[np.where((dst-dst[dumy]-r) == 0)[0]],'ob');plt.plot(-r,dumsla[dumy],'or');plt.plot((dst-dst[dumy]-r)[dumy+mx_n],dumsla[dumy+mx_n],'og');plt.plot((dst-dst[dumy]-r)[dumy-mx_s],dumsla[dumy-mx_s],'og');plt.ylabel('SLA (m)') # dum=rankine_model(dst-dst[dumy]-r, Rout, Vout) # plt.subplot(2,1,2);plt.plot(dst-dst[dumy]-r,ugeo-Vanom,'-k');plt.plot(0,(ugeo-Vanom)[np.where((dst-dst[dumy]-r) == 0)[0]],'ob');plt.plot(-r,(ugeo-Vanom)[dumy],'or');plt.plot((dst-dst[dumy]-r)[dumy+mx_n],(ugeo-Vanom)[dumy+mx_n],'og');plt.plot((dst-dst[dumy]-r)[dumy-mx_s],(ugeo-Vanom)[dumy-mx_s],'og');plt.plot(dst-dst[dumy]-r,dum,'-b');plt.ylabel('Velocity anomaly(m.s-1)');plt.xlabel('Distance to offseted center (km)') # plt.show() # except : # pass # relvort[j] = np.median(np.append(np.abs(ugeo_n[1:mx_n+1])/(dst[1:mx_n+1]*1e3),np.abs(ugeo_s[1:mx_s+1])/(dst[1:mx_s+1]*1e3))) # if (dumsla[dumy] > dumsla[dumy-1]) | (dumsla[dumy] > dumsla[dumy+1]) : relvort[j] *= -1 #Inver sign if anticyclonic return diameter, relvort, amplitude, rk_relvort, rk_center, rk_diameter, self_advect
alti.reorder() #2D reordering of the data track_list = [9] # track_list = alti.track_list() #Loop over tracks ################# for t in track_list: #Load variables and split by track number fg = alti.slice('track', t) lon = alti.lon[fg] lat = alti.lat[fg] dst = AT.calcul_distance(lat, lon) N = len(lon) dx = np.median(AT.deriv(dst)) time = AT.grid_time(alti.date[:, fg]) sla = alti.sla[:, fg] # time = np.mean(time,axis=1) nt = len(time) dt = np.median(AT.deriv(time)) #Loess filtering of SLA # for i in np.arange(nt): # sla[i,:]=AT.loess(sla[i,:], dst, 40.) #Run wavelet analysis ##################### #WV analysis sa_spectrum, sa_lscales, wvsla, daughter = ke.runAnalysis(
def get_spec(dx,Vin,verbose=False,m=6,gain=1.0,res_factor=10.,integration=False,periodogram=False,mother='morlet'): #Setup Wavelet Transform parameters ################################### if mother == 'morlet': scale2len = (4*np.pi) / (m + np.sqrt(2+m*m)) #"fourrier factor" (for Morlet) len2scale = 1/scale2len elif mother=='dog': len2scale = np.sqrt(m+0.5) / (2*np.pi) scale2len = (2*np.pi) / np.sqrt(m+0.5) #"fourrier factor" else : raise Exception('Wavelet function "{0}" is not defined - choose between "morlet" & "dog"') N=len(Vin) T = dx*N s0 = 2.0 * dx if mother == 'morlet' else (2*dx) * len2scale #smallest wavescale (devided by fourier wavelength) dj = res_factor * (dx/T) * len2scale #set scale interval (remember this is scaled by s0^(-1/2) ; no units! dj0 = (dx/T) * len2scale J = np.fix((np.log((T*len2scale)/s0) / np.log(2.)) / dj).astype(int) #number of scales from s0 to T #Setup wavelet object exec('mother_obj=kernel.wavelet.{0}({1})'.format(mother,m)) #Run transform wave, s, k, coi, daughter, fft, fftfreqs = kernel.wavelet.cwt(Vin, dx, dj, s0, J,mother_obj) Cd = mother_obj.cdelta p = 1. / k #Mask data ########## # 1) Out of confidence interval mask = np.repeat(coi,J+1).reshape((N,J+1)).transpose() <= np.repeat(p,N).reshape((J+1,N)) # mask = np.ones(N,J+1) a = np.ma.array(np.real(wave),mask=mask) #Real part of the transform b = np.ma.array(np.imag(wave),mask=mask) csquared = a**2.0 + b**2.0 c = np.sqrt(csquared) if not periodogram : csquared = csquared.sum(axis=1) / (~mask).sum(axis=1) # integration of the spectrum # shift to have values centered on the intervals dk = deriv(k) k_ = interp1d(np.arange(J+1),k,np.arange(J)+0.5) #Shift wavelengths by half the unit #Spectral integration if integration and not periodogram: esd = k_*0.0 psd = k_*0.0 dk = deriv(k) for i in np.arange(len(k_)): esd[i] = np.sum((csquared * (N/2.0))[(k > (k_[i]-dk[i])) & (k < (k_[i]+dk[i]))]) / 2.0 psd[i] = np.sum(csquared[(k > (k_[i]-dk[i])) & (k < (k_[i]+dk[i]))]) / (2.0**2) #This is variance units integration fq=k_ else : esd = csquared psd = esd.copy() /2. fq=k.copy() psd = (psd / (dj*res_factor)) #Get frequencies and period p=1/fq return {'psd':psd,'esd':esd,'fq':fq,'p':p,'gain':gain}
def geost_1d(*args,**kwargs) : #(lon,lat,nu): OR (dst,nu) """ ;+ ; ; GEOST_1D : Compute geostrophic speeds from a sea level dataset <br /> ; ; Reference : Powell, B. S., et R. R. Leben (2004), An Optimal Filter for <br /> ; Geostrophic Mesoscale Currents from Along-Track Satellite Altimetry, <br /> ; Journal of Atmospheric and Oceanic Technology, 21(10), 1633-1642. ; ; @param lon {in}{optional}{type:NUMERIC} longitude in degrees ; @param lat {in}{optional}{type:NUMERIC} latitude in degrees ; @param dst {in}{optional}{type:NUMERIC} along-track distance. ; @param z {in}{required}{type:NUMERIC} sea level surface. Can either be absolute<br /> ; values (SSH) or relative values (SLA). This MUST be given in METERS. ; @keyword strict {in}{optional}{type:BOOLEAN} If True, compute gradient at mid-distance. ; @keyword pl04 {in}{optional}{type:BOOLEAN} If True, use the Powell & Leben 2004 method. ; ; @returns Geostrophic velocity component, positive eastward ; ; ; ; @author Renaud DUSSURGET, LEGOS/CTOH ; @history Created Sep. 2009 from genweights.m (Brian Powell (c) 2004, <br /> ; University of Colorado, Boulder)<br /> ; Modified May 2010 to be compliant with 20Hz datasets (p & n can vary).<br /> ; Warining may also be issued for data with holes within the width of the<br /> ; window.<br /> ; Modified June 2010 to include filtering window width in KM instead of nb. of<br /> ; points (Equivalent btw. 1Hz and 20Hz data).<br /> ; ; @uses CALCUL_DISTANCE, EXIST, GENWEIGTHS, SETINTERSECTION, SETUNION, <br /> ; OPTIMAL_SLOPE, GRAVITY, CORIOLIS, TRACK_ORIENT ; ; @example dummy1=geost_1D(lon,lat,sla,pl04=True,p=11,q=11) :<br /> ; Return along-track velocity anomalies using a 11km by 11km Powell & Leben 2004 filter window <br /> ; dummy2=geost_1D(dst,sla,strict=True) :<br /> ; Return along-track velocity anomalies computed at mid-distance <br /> ; ;- """ lon = args[0] lat = args[1] dst = args[2] if len(args) == 4 else calcul_distance(lat,lon) * 1e3 #distance in meters nu = args [3] if len(args) == 4 else args[2] isVector = len(np.shape(nu)) == 1 #Reshape nu if vector if isVector : nu=np.reshape(nu,(len(nu),1)) nt = np.shape(nu)[1] if not isVector else 1 sh = nu.shape nufilt=np.ma.array(np.empty(sh),mask=True,dtype=nu.dtype) pl04 = kwargs.pop('pl04',False) filter = kwargs.pop('filter', None) strict = kwargs.pop('strict',False) verbose = kwargs.pop('verbose',False) if filter is not None : for t in np.arange(nt) : nufilt[:,t] =loess(nu[:,t],dst,filter*1e3) nu=nufilt if pl04 : ug = np.ma.array(np.empty(sh),mask=True,dtype=nu.dtype) for t in np.arange(nt) : ug[:,t] = powell_leben_filter_km(lon,lat,nu[:,t],verbose=verbose,**kwargs) if isVector : ug=ug.flatten() return ug #If strict option is set to True, compute gradients at mid-distance between points if strict : lon = (lon[1:] - lon[:-1])/2. + lon[0:-1] lat = (lat[1:] - lat[:-1])/2. + lat[0:-1] #Compute gravitational & coriolis forces if strict : sh = (sh[0]-1,sh[1]) g = np.repeat(gravity(lat),nt).reshape(sh) f = np.repeat(coriolis(lat),nt).reshape(sh) #Compute SSH 1st derivative # dh = deriv(dst,nu) #(deriv is very bad...) dh = np.ma.array(np.empty(sh),mask=True,dtype=nu.dtype) for t in np.arange(nt) : dh[:,t] = (nu[1:,t] - nu[:-1,t])/(dst[1:] - dst[:-1]) if strict else deriv(dst,nu[:,t]) #Compute geostrophy # print f # print g # print dh ug = - (g*dh) / (f) #Inverse sign of ug for descending tracks as Coriolis is oriented to the right #northward if (not track_orient(lon,lat)) : #descending tracks ug *=-1 if isVector : ug=ug.flatten() return (lon,lat,ug) if strict else ug
def uvgrid(*args, **kwargs): #;lon, lat, time, sla, STRICT=True): lon = args[0] lat = args[1] if len(args) == 3: sla = args[2] time = np.arange(1) else: time = args[2] sla = args[3] strict = kwargs.get('strict', False) nx = len(lon) ny = len(lat) nt = len(time) sla = sla.reshape((nt, ny, nx)) nxout = nx - 1 if strict else nx nyout = ny - 1 if strict else ny dx = np.median(deriv(lon)) dy = np.median(deriv(lat)) #Compute spatial distance gradients (convert to meters) xgrad = np.repeat( [calcul_distance(l, 0.0, l, dx) * 1e3 for l in np.float64(lat)], nx).reshape((ny, nx)) ygrad = np.repeat(calcul_distance(0, 0, dy, 0) * 1e3, nx * ny).reshape( (ny, nx)) #meridional distance gradient is constant everywhere lonout = interp1d(np.arange(nx), lon, np.arange(nxout) + 0.5) if strict else lon latout = interp1d(np.arange(ny), lat, np.arange(nyout) + 0.5) if strict else lat glon, glat = np.meshgrid(lonout, latout) g = gravity(glat) f = coriolis(glat) #Init gradients dhx = np.ma.array(np.zeros((nt, nyout, nxout)), mask=True) dhx.data[:] = dhx.fill_value dhy = dhx.copy() dhdx = dhx.copy() dhdy = dhx.copy() u = dhx.copy() v = dhx.copy() #Loop over time for i in np.arange(nt): #2 points differenciation if strict: dhx[i, :, :] = np.diff(sla[i, :, :], axis=1) dhy[i, :, :] = np.diff(sla[i, :, :], axis=0) else: #3 points differenciation dhy[i, :, :], dhx[i, :, :] = np.gradient(sla[i, :, :]) dhdx[i, :, :] = dhx[i, :, :] / xgrad dhdy[i, :, :] = dhy[i, :, :] / ygrad u[i, :, :] = -(g * dhdy[i, :, :]) / (f) #m.s-1 v[i, :, :] = (g * dhdx[i, :, :]) / (f) #m.s-1 u = np.squeeze(u) v = np.squeeze(v) return u, v