def inertial_period(lat): """Calculate the inertial period as: .. math:: Ti = \\frac{2\\pi}{f} = \\frac{T_{sd}}{2\\sin\\phi} Parameters ---------- lat : array_like latitude in decimal degrees north [-90..+90] Returns ------- Ti : array_like period in seconds Examples -------- >>> from oceans import sw_extras as swe >>> lat = 30. >>> swe.inertial_period(lat)/3600 23.934849862785651 """ lat = np.asanyarray(lat) return 2 * np.pi / sw.f(lat)
def inertial_period(lat): """ Calculate the inertial period as: .. math:: Ti = \\frac{2\\pi}{f} = \\frac{T_{sd}}{2\\sin\\phi} Parameters ---------- lat : array_like latitude in decimal degrees north [-90..+90] Returns ------- Ti : array_like period in seconds Examples -------- >>> from oceans import sw_extras as swe >>> lat = 30. >>> swe.inertial_period(lat)/3600 23.934849862785651 """ lat = np.asanyarray(lat) return 2 * np.pi / sw.f(lat)
def qg_stability(N2, ubar, z, k=1., lat=50., structure=True): f0 = sw.f(lat) beta = 2 * 7.29e-5 * np.cos(lat * pi / 180.) / 6371.e3 dz = np.abs(z[1] - z[0]) S = (f0**2) / N2 L = stretching_matrix(z.size, S, dz) k2 = k**2 L2 = L - np.eye(ubar.size) * k2 U = np.eye(ubar.size) * ubar Qy = np.eye( ubar.size) * (beta - np.array(np.matrix(L) * np.matrix(ubar).T)) L3 = L2.copy() for i in range(ubar.size): L3[i, :] = L2[i, :] * ubar[i] A = L3 + Qy B = L2.copy() evals, evecs = sp.linalg.eig(A, B) imax = evals.imag.argmax() eval_max = evals[imax] evec_max = evecs[:, imax] if structure: PSI, X, Z = wave_structure(evec_max, z, k) return eval_max, evec_max, PSI, X, Z else: return eval_max, evec_max
def qg_stability(N2,ubar,z,k=1.,lat=50.,structure=True): f0 = sw.f(lat) beta = 2*7.29e-5*np.cos(lat*pi/180.)/6371.e3 dz = np.abs(z[1]-z[0]) S = (f0**2)/N2 L= stretching_matrix(z.size,S,dz) k2 = k**2 L2 = L - np.eye(ubar.size)*k2 U = np.eye(ubar.size)*ubar Qy = np.eye(ubar.size)*(beta - np.array( np.matrix(L)*np.matrix(ubar).T ) ) L3 = L2.copy() for i in range(ubar.size): L3[i,:] = L2[i,:]*ubar[i] A = L3 + Qy B = L2.copy() evals,evecs = sp.linalg.eig(A,B) imax = evals.imag.argmax() eval_max = evals[imax] evec_max = evecs[:,imax] if structure: PSI, X, Z = wave_structure(evec_max,z,k) return eval_max,evec_max,PSI,X,Z else: return eval_max,evec_max
def set_params(lat, dt=3., dz=1., max_depth=100., mld_thresh=1e-4, dt_save=1., rb=0.65, rg=0.25, rkz=0., beta1=0.6, beta2=20.0, heat_ON=True, winds_ON=True, emp_ON=True, drag_ON=True): """ This function sets the main paramaters/constants used in the model. These values are packaged into a dictionary, which is returned as output. Definitions are listed below. CONTROLS (default values are in [ ]): lat: latitude of profile dt: time-step increment. Input value in units of hours, but this is immediately converted to seconds.[3 hours] dz: depth increment (meters). [1m] max_depth: Max depth of vertical coordinate (meters). [100] mld_thresh: Density criterion for MLD (kg/m3). [1e-4] dt_save: time-step increment for saving to file (multiples of dt). [1] rb: critical bulk richardson number. [0.65] rg: critical gradient richardson number. [0.25] rkz: background vertical diffusion (m**2/s). [0.] beta1: longwave extinction coefficient (meters). [0.6] beta2: shortwave extinction coefficient (meters). [20] winds_ON: True/False flag to turn ON/OFF wind forcing. [True] emp_ON: True/False flag to turn ON/OFF freshwater forcing. [True] heat_ON: True/False flag to turn ON/OFF surface heat flux forcing. [True] drag_ON: True/False flag to turn ON/OFF current drag due to internal-inertial wave breaking. [True] OUTPUT is dict with fields containing the above variables plus the following: dt_d: time increment (dt) in units of days g: acceleration due to gravity [9.8 m/s^2] cpw: specific heat of water [4183.3 J/kgC] f: coriolis term (rad/s). [sw.f(lat)] ucon: coefficient of inertial-internal wave dissipation (s^-1) [0.1*np.abs(f)] """ params = {} params['dt'] = 3600.0*dt params['dt_d'] = params['dt']/86400. params['dz'] = dz params['dt_save'] = dt_save params['lat'] = lat params['rb'] = rb params['rg'] = rg params['rkz'] = rkz params['beta1'] = beta1 params['beta2'] = beta2 params['max_depth'] = max_depth params['g'] = 9.81 params['f'] = sw.f(lat) params['cpw'] = 4183.3 params['ucon'] = (0.1*np.abs(params['f'])) params['mld_thresh'] = mld_thresh params['winds_ON'] = winds_ON params['emp_ON'] = emp_ON params['heat_ON'] = heat_ON params['drag_ON'] = drag_ON return params
def set_params(dt=3., dz=1., max_depth=100., mld_thresh=1e-4, lat=74., dt_save=1., rb=0.65, rg=0.25, rkz=0., beta1=0.6, beta2=0.2): """ This function sets the main paramaters/constants used in the model. These values are packaged into a dictionary, which is returned as output. Definitions are listed below. CONTROLS (default values are in [ ]): dt: time-step increment. Input value in units of hours, but this is immediately converted to seconds.[3 hours] dz: depth increment (meters). [1m] max_depth: Max depth of vertical coordinate (meters). [100] mld_thresh: Density criterion for MLD (kg/m3). [1e-4] dt_save: time-step increment for saving to file (multiples of dt). [1] lat: latitude of profile (degrees). [74.0] rb: critical bulk richardson number. [0.65] rg: critical gradient richardson number. [0.25] rkz: background vertical diffusion (m**2/s). [0.] beta1: longwave extinction coefficient (meters). [0.6] beta2: shortwave extinction coefficient (meters). [20] OUTPUT is dict with fields containing the above variables plus the following: dt_d: time increment (dt) in units of days g: acceleration due to gravity [9.8 m/s^2] cpw: specific heat of water [4183.3 J/kgC] f: coriolis term (rad/s). [sw.f(lat)] ucon: coefficient of inertial-internal wave dissipation (s^-1) [0.1*np.abs(f)] """ params = {} params['dt'] = 3600.0*dt params['dt_d'] = params['dt']/86400. params['dz'] = dz params['dt_save'] = dt_save params['lat'] = lat params['rb'] = rb params['rg'] = rg params['rkz'] = rkz params['beta1'] = beta1 params['beta2'] = beta2 params['max_depth'] = max_depth params['g'] = 9.81 params['f'] = sw.f(lat) params['cpw'] = 4183.3 params['ucon'] = (0.1*np.abs(params['f'])) params['mld_thresh'] = mld_thresh return params
def __init__(self): # Beta = meridional rate of change of the Coriolis parameters (m^-1 s^-1) r = 6.371e+6 # Earth's radius (m) omega = 7.292115e-5 # Earth's rotation rate (s^-1) beta = 2.*omega*np.cos(np.pi*soest.LAT16_100/180.)/r # Coriolis parameter (s^-1) f = sw.f(soest.LAT16_100) # Rho = typical density of the sea water above the pycnocline (kg m^-3) # Since we are considering a incompressible ocean (i. e., Boussinesq approximation) it will be # constant rho = 1025.0 self.beta = beta self.rho = rho self.f = f
def __init__(self): # Beta = meridional rate of change of the Coriolis parameters (m^-1 s^-1) r = 6.371e+6 # Earth's radius (m) omega = 7.292115e-5 # Earth's rotation rate (s^-1) beta = 2. * omega * np.cos(np.pi * scow.latitude / 180.) / r # Coriolis parameter (s^-1) f = sw.f(scow.latitude) # Rho = typical density of the sea water above the pycnocline (kg m^-3) # Since we are considering a incompressible ocean (i. e., Boussinesq approximation) it will be # constant rho = 1025.0 self.beta = beta self.rho = rho self.f = f
def GM(lat, N, N0, b=1000, oned=False): try: import GM81.gm as gm except ImportError: raise ImportError("Please install the GM81 package.") # Coriolis frequency f = sw.f(lat=12) # frequency omg = np.logspace(np.log10(1.01 * f), np.log10(N), 401) # horizontal wavenumber k = 2 * np.pi * np.logspace(-6, -2, 401) # mode number j = np.arange(1, 100) # reshape to allow multiplication into 2D array Omg = np.reshape(omg, (omg.size, 1)) K = np.reshape(k, (k.size, 1)) J = np.reshape(j, (1, j.size)) # frequency spectra (KE and PE) K_omg_j = gm.K_omg_j(Omg, J, f, N, N0, b) P_omg_j = gm.P_omg_j(Omg, J, f, N, N0, b) # wavenumber spectra (KE and PE) K_k_j = gm.K_k_j(K, J, f, N, N0, b) P_k_j = gm.P_k_j(K, J, f, N, N0, b) # sum over modes K_omg = np.sum(K_omg_j, axis=1) P_omg = np.sum(P_omg_j, axis=1) K_k = np.sum(K_k_j, axis=1) P_k = np.sum(P_k_j, axis=1) # compute 1D spectra from 2D spectra K_k_1d = gm.calc_1d(k, K_k) P_k_1d = gm.calc_1d(k, P_k) return (omg, K_omg, P_omg, k, K_k_1d, P_k_1d)
def qg_stability_2d(N2, ubar, vbar, z, k, l, lat, structure=False): f0 = sw.f(lat) beta = 2 * 7.29e-5 * np.cos(lat * pi / 180.) / 6371.e3 dz = np.abs(z[1] - z[0]) S = (f0**2) / N2 L = stretching_matrix(z.size, S, dz) k2 = k**2 + l**2 L2 = L - np.eye(ubar.size) * k2 Qy = np.eye( ubar.size) * (beta - np.array(np.matrix(L) * np.matrix(ubar).T)) Qx = np.eye(ubar.size) * np.array(np.matrix(L) * np.matrix(vbar).T) Q = k * Qy - l * Qx L3 = np.empty_like(L2) for i in range(ubar.size): L3[i, :] = L2[i, :] * (ubar[i] * k + vbar[i] * l) A = (L3 + Q) B = L2.copy() try: evals, evecs = sp.linalg.eig(A, B) imax = evals.imag.argmax() eval_max = evals[imax] evec_max = evecs[:, imax] except: eval_max = np.nan + 1.j * np.nan evec_max = np.nan * z if structure: PSI, X, Z = wave_structure(evec_max, z, k) return eval_max, evec_max, PSI, X, Z else: return eval_max, evec_max
def qg_stability_2d(N2,ubar,vbar,z,k,l,lat,structure=False): f0 = sw.f(lat) beta = 2*7.29e-5*np.cos(lat*pi/180.)/6371.e3 dz = np.abs(z[1]-z[0]) S = (f0**2)/N2 L= stretching_matrix(z.size,S,dz) k2 = k**2 + l**2 L2 = L - np.eye(ubar.size)*k2 Qy = np.eye(ubar.size)*(beta - np.array( np.matrix(L)*np.matrix(ubar).T )) Qx = np.eye(ubar.size)*np.array( np.matrix(L)*np.matrix(vbar).T ) Q = k*Qy - l*Qx L3 = np.empty_like(L2) for i in range(ubar.size): L3[i,:] = L2[i,:]*(ubar[i]*k + vbar[i]*l) A = (L3 + Q) B = L2.copy() try: evals,evecs = sp.linalg.eig(A,B) imax = evals.imag.argmax() eval_max = evals[imax] evec_max = evecs[:,imax] except: eval_max = np.nan + 1.j*np.nan evec_max = np.nan*z if structure: PSI, X, Z = wave_structure(evec_max,z,k) return eval_max,evec_max,PSI,X,Z else: return eval_max,evec_max
def geostrophic_current(ix, lat): g = sw.g(lat.mean()) f = sw.f(lat.mean()) v = ix * g / f return v
def eqmodes(N2, z, nm, pmodes=False): ''' This function computes the equatorial velocity modes ======================================================== Input: N2 - Brunt-Vaisala frequency data array z - Depth data array (equaly spaced) lat - Latitude scalar nm - Number of modes to be computed pmodes - If the return of pressure modes is required. Default is False ======================================================== Output: Si - Equatorial modes matrix with MxN dimension being: M = z array size N = nm Rdi - Deformation Radii array Fi - Pressure modes matrix with MxN dimension being: M = z array size N = nm Returned only if input pmodes=True made by Hélio Almeida, Iury Sousa and Wandrey Watanabe Laboratório de Dinâmica Oceânica - Universidade de São Paulo 2016 ''' #needed to the problem lat = 0 #nm will be the number of baroclinic modes nm -= 1 #Barotropic mode will be added #defines the orthonormalization function onorm = lambda f: f/np.sqrt(dz*((np.array(f[1:])**2+\ np.array(f[:-1])**2)/(2*H)).sum()) # defines function to keep consistency multiply by plus/minus # to make the leading term positive plus_minus = lambda f: -f if (np.sign(f[1]) < 0) else f dz = np.abs(z[1] - z[0]) # assembling matrices # N2 matrix N2 = np.diag(N2[1:-1], 0) # 2nd order difference matrix A = np.diag(np.ones(z.size-2)*-2.,0)+\ np.diag(np.ones(z.size-3)*1.,-1)+\ np.diag(np.ones(z.size-3)*1.,1) A = A / (dz**2) A = np.matrix(A) N2 = np.matrix(N2) #C = A*N2 N02 = -1 / N2 N02[np.isinf(N02)] = 0 C = N02 * A # solve the eigenvalue problem egval, egvec = np.linalg.eig(C) i = np.argsort(egval) egval = egval[i] egvec = egvec[:, i] # truncate the eigenvalues and eigenvectors for the number of modes needed ei = egval[:nm] Si = egvec[:, :nm] # Applying Dirichlet boundary condition at top and bottom # adding a row of zeros at bottom and top Si = np.append(np.matrix(np.zeros(nm)), Si, axis=0) Si = np.append(Si, np.matrix(np.zeros(nm)), axis=0) Si = np.array(Si) H = np.abs(z).max() # normalizing to get orthonormal modes Si = np.array(list(map(onorm, Si.T))).T # to keep consistency multiply by plus/minus # to make the leading term positive Si = np.array(list(map(plus_minus, Si.T))).T # compute the deformation radii [km] beta = (7.2921150e-5 * 2 * np.cos(np.deg2rad(lat * 1.))) / 6371000 c = np.sqrt(1 / ei) radii = np.sqrt(c / beta) #append external deformation radius radii = np.hstack([(np.sqrt(9.81 * H) / np.abs(sw.f(lat))), radii]) #converting to km radii *= 1e-3 #BAROTROPIC MODE no = 1 fb = np.ones((Si.shape[0], 1)) * no sb = np.expand_dims(np.linspace(0, no + 1, Si.shape[0]), axis=1) # trying to compute the pmodes based on the polarization # relation between velocity/pressure modes # # F_j(z)=-g.he_j d/dzS_j # #### if pmodes == True: Fi = np.zeros(Si.shape) for i in np.arange(nm): Fi[1:-1,i] =\ (-1/ei[i])*((Si[1:-1,i]-Si[0:-2,i])/dz) #Aplying Neuman boundary condition d/dzFi=o @0,-H Fi[0], Fi[-1] = Fi[1], Fi[-2] # normalizing to get orthonormal modes Fi = np.array(list(map(onorm, Fi.T))).T # to keep consistency multiply by plus/minus # to make the leading term positive Fi = np.array(list(map(plus_minus, Fi.T))).T Si = np.hstack([sb, Si]) Fi = np.hstack([fb, Fi]) return Si, radii, Fi else: Si = np.hstack([sb, Si]) return Si, radii
def model_timestep(T, S, U, V, z, I, L, E, P, tau_x, tau_y, dt, nabla_b=None, Ekman_Q_flux=None, use_static_stability=True, use_mixed_layer_stability=True, use_shear_stability=True, use_Ekman_flux=False, use_MLI=False, tracer=None, vert_diffusivity=None, verbose=False, I1=0.62, I2=None, lambda1=.6, lambda2=20, T0=0, S0=34, rho0=None, alpha=None, beta=None, f=sw.f(40), return_MLD=False, advection=False, l_poly=1e4, phi=5e-2, T_cdw=1, S_cdw=34.5, shelf_thick=350, debug=False, T_out=-1, S_out=33.8, return_dTdS=False, return_TSrelax=False, return_vel=False): # define initial variables c = 4218 # heat capacity (J kg^-1 K^-1) if I2 is None: I2 = 1 - I1 if I1 + I2 != 1: raise Exception('Shortwave insolation amplitudes need to sum to unity') if rho0 is None: rho0 = sw.dens(S0, T0, 0) if alpha is None: alpha = -sw.alpha( S0, T0, 0 ) * rho0 # multiply by rho to fit into nice equation of state (see get_rho function) if beta is None: beta = sw.beta( S0, T0, 0 ) * rho0 # # multiply by rho to fit into nice equation of state (see get_rho function) dz = z[1] - z[0] if use_Ekman_flux and Ekman_Q_flux is None: raise Exception( 'Using Ekman-induced buoyacy flux but no buoyancy gradients were given.' ) if use_MLI and MLI_flux is None: raise Exception('Using MLI but no horizontal buoyancy gradient given.') T = T.copy() S = S.copy() U = U.copy() V = V.copy() # so don't overwrite data if tracer is not None: tracer = tracer.copy() # make initial heat profile I_profile = -I / dz * ( I1 * np.exp(-z / lambda1) * (np.exp(-dz / 2 / lambda1) - np.exp(dz / 2 / lambda1)) + I2 * np.exp(-z / lambda2) * (np.exp(-dz / 2 / lambda2) - np.exp(dz / 2 / lambda2))) L_profile = np.zeros(len(z)) L_profile[0] = L / dz Q_profile = I_profile + L_profile if use_Ekman_flux: A = 0.1 # eddy viscosity m^2 s^-1 z_Ek = np.sqrt(A / np.abs(f)) if verbose: print('Using Ekman depth of %d m' % z_Ek) z_Ek_ind = np.where(z > z_Ek)[0][0] Q_Ek_profile = np.zeros(len(z)) Q_Ek_profile[0:z_Ek_ind] = Ekman_Q_flux / z_Ek * dz Q_profile += Q_Ek_profile if advection == True: Tf = sw.fp(S, z) #freezing temperature for the column using salinity and pressure Tf_mean = np.mean(T[51::] - Tf[51::]) #find temperature of no motion v_profile = ((T - Tf) - Tf_mean) * phi #create the velocity profile v_profile[0:50] = 0 #there is no mean advection in the top 25 m h_interface = (np.abs(v_profile[51::] - 0)).argmin() + 50 #find the depth of the point of no motion inv_time_length = np.zeros(shape=len(z)) inv_time_length = np.absolute(v_profile) / l_poly #create 1/tau #Create the relaxation profiles for S and T T_relax = np.zeros(shape=len(z)) T_relax = np.tanh((z - z[h_interface]) / 100) * (T_cdw - T_out) / 2 + (T_cdw + T_out) / 2 T_relax[h_interface:len(z)] = T_relax[h_interface:len(z)] + 0.6 * ( (z[h_interface:len(z)] - z[h_interface]) / 1000) S_relax = np.zeros(shape=len(z)) S_relax = np.tanh((z - z[h_interface]) / 100) * (S_cdw - S_out) / 2 + (S_cdw + S_out) / 2 S_relax[h_interface:len(z)] = S_relax[h_interface:len(z)] + 0.25 * ( (z[h_interface:len(z)] - z[h_interface]) / 1000) # update temperature if advection == False: dTdt = Q_profile / (c * rho0) else: dTdt = Q_profile / (c * rho0) + inv_time_length * (T_relax - T) if debug == True: print('inv_time_length*(T_relax-T): ', inv_time_length[0:100] * (T_relax[0:100] - T[0:100])) if use_MLI: mld_ind = get_mld_ind(T, S, U, V, z) mld = z[mld_ind] C_e = 0.06 g = 9.81 # gravitational acceleration (m s^-2) c = 4218 # heat capacity (J kg^-1 degC^-1 MLI_dTdt = -C_e * nabla_b**2 * mld**2 * rho0 / (np.abs(f) * alpha * g) vert_profile = 4 / mld * (1 - 2 * z / mld) * ( 16 + 10 * (1 - 2 * z / mld)**2) / 21 # this is vertical derivative of mu(z) vert_profile[mld_ind::] = 0 vert_profile[0:mld_ind] -= np.mean( vert_profile[0:mld_ind] ) # just to ensure that no heat added to system dTdt += MLI_dTdt * vert_profile T += dTdt * dt # update salinity dSdt_0 = S[0] * (E - P) / dz / 1000 S[0] += dSdt_0 * dt if advection == True: dSdt = inv_time_length * (S_relax - S) S += dSdt * dt if use_MLI: rho = get_rho(T, S, T0, S0, rho0, alpha, beta) half_mld_ind = int(mld_ind / 2) if np.any(np.diff(rho[half_mld_ind::]) < 0): if verbose: print( 'Need to homogenize discontinuity at base of previous mixed layer' ) # get rid of discontinuity at base of previous mixed layer # homogenize the region of water from mld/2 to z* # z* is the shallowest value (> mld/2) such that the homogenized rho <= rho(z*) zstar_ind = mld_ind.copy() while np.mean(rho[half_mld_ind:zstar_ind]) >= rho[zstar_ind]: if verbose: print('Deepening z*...') zstar_ind += 1 T[half_mld_ind:zstar_ind] = np.mean(T[half_mld_ind:zstar_ind]) S[half_mld_ind:zstar_ind] = np.mean(S[half_mld_ind:zstar_ind]) if tracer is not None: tracer[:, half_mld_ind:zstar_ind] = np.atleast_2d( np.mean(tracer[:, half_mld_ind:zstar_ind], axis=1)).T elif verbose: print('No need to homogenize base of previous mixed layer') # update momentum # first rotate momentum halfway angle = -f * dt / 2 # currently assuming this is in rad U, V = rotate(angle, U, V) # then add wind stress mld_ind = get_mld_ind(T, S, U, V, z) mld = z[mld_ind] U[0:mld_ind] += tau_x / mld / rho0 * dz * dt V[0:mld_ind] += tau_y / mld / rho0 * dz * dt # then rotate second half U, V = rotate(angle, U, V) if use_static_stability: T, S, U, V = static_stability(T, S, U, V, z, T0, S0, rho0, alpha, beta) if get_mld_ind(T, S, U, V, z) == (T.size - 1): use_mixed_layer_stability = False use_shear_stability = False if use_mixed_layer_stability: T, S, U, V = mixed_layer_stability(T, S, U, V, z, T0, S0, rho0, alpha, beta, verbose=verbose) if use_shear_stability: T, S, U, V = shear_stability(T, S, U, V, z, T0, S0, rho0, alpha, beta, verbose=verbose) if vert_diffusivity is not None: dTdt_vd = np.zeros(len(T)) dTdt_vd[1:-1] = np.diff(np.diff(T)) / dz**2 T += vert_diffusivity * dTdt_vd * dt dSdt_vd = np.zeros(len(S)) dSdt_vd[1:-1] = np.diff(np.diff(S)) / dz**2 S += vert_diffusivity * dSdt_vd * dt dUdt = np.zeros(len(U)) dUdt[1:-1] = np.diff(np.diff(U)) / dz**2 U += vert_diffusivity * dUdt * dt dVdt = np.zeros(len(V)) dVdt[1:-1] = np.diff(np.diff(V)) / dz**2 V += vert_diffusivity * dVdt * dt if tracer is not None: dtdt = np.zeros(shape=tracer.shape) dtdt[:, 1:-1] = np.diff(np.diff(tracer, axis=1), axis=1) / dz**2 tracer += vert_diffusivity * dtdt * dt return_variables = (T, S, U, V) if tracer is not None: return_variables += (tracer, ) if return_MLD: return_variables += (get_mld(T, S, U, V, z), ) if return_dTdS: return_variables += ( dTdt, dSdt, ) if return_TSrelax: return_variables += ( T_relax, S_relax, ) if return_vel: return_variables += (v_profile, ) return return_variables
from datetime import datetime # set up initial parameters (these are also set as defaults in the PWP code, so don't need to put them in) T0 = 0 # reference temperature (degC) S0 = 34 # reference salinity (parts per thousand; ppt) rho0 = 1025 # reference density (kg m^-3) alpha = -sw.alpha( S0, T0, 0) * rho0 # thermal expansion coefficient (kg m^-3 degC^-1) beta = sw.beta(S0, T0, 0) * rho0 # haline contraction coefficient (kg m^-3 ppt^-1 ) latitude = -75 # degrees north f = sw.f(latitude) # planetary vorticity fwflux1 = np.loadtxt('/home/erobo/ThompsonResearch/Data/fwflux1_oneyear.csv', delimiter=',') qnet1 = np.loadtxt('/home/erobo/ThompsonResearch/Data/qnet1_oneyear.csv', delimiter=',') taux1 = np.loadtxt('/home/erobo/ThompsonResearch/Data/taux1_oneyear.csv', delimiter=',') tauy1 = np.loadtxt('/home/erobo/ThompsonResearch/Data/tauy1_oneyear.csv', delimiter=',') temp_profile = np.loadtxt( '/home/erobo/ThompsonResearch/Data/temp_profile_snapshot.txt') salt_profile = np.loadtxt( '/home/erobo/ThompsonResearch/Data/salt_profile_snapshot.txt') depth_profile = np.loadtxt( '/home/erobo/ThompsonResearch/Data/Schodlok_depths.txt')
# isotropic Eiso = np.empty((292, omg.size)) for i in range(omg.size): kiso, Eiso[:, i] = calc_ispec(k, l, E[:, :, i]) # linear dispersion relationship kr = 2 * pi * kiso * 1.e-3 kr2 = kr**2 m = np.logspace(-3, 0., 500) #omgr = 2*pi*omg/8600. N2 = (1.7e-5) f2 = sw.f(59.247177)**2 b = 1.e3 jmax = 100 #for j in range(jmax): for j in range(m.size): #m = (pi*j)/b m2 = m[i]**2 omgr = np.sqrt((f2 * m2 + N2 * kr2) / (kr2 + m2)) krp = 1.e3 * kr / (2 * pi) if j == 0: omgrp = 3600 * omgr / (2 * pi) else: omgrp = np.vstack([omgrp, 3600 * omgr / (2 * pi)])
def model_timestep(T, S, U, V, z, I, L, E, P, tau_x, tau_y, dt, nabla_b=None, Ekman_Q_flux=None, use_static_stability=True, use_mixed_layer_stability=True, use_shear_stability=True, use_Ekman_flux=False, use_MLI=False, tracer=None, vert_diffusivity=None, verbose=False, I1=0.62, I2=None, lambda1=.6, lambda2=20, T0=17, S0=36, rho0=None, alpha=None, beta=None, f=sw.f(40), return_MLD=False): # define initial variables c = 4218 # heat capacity (J kg^-1 K^-1) if I2 is None: I2 = 1 - I1 if I1 + I2 != 1: raise Exception('Shortwave insolation amplitudes need to sum to unity') if rho0 is None: rho0 = sw.dens(S0, T0, 0) if alpha is None: alpha = -sw.alpha( S0, T0, 0 ) * rho0 # multiply by rho to fit into nice equation of state (see get_rho function) if beta is None: beta = sw.beta( S0, T0, 0 ) * rho0 # # multiply by rho to fit into nice equation of state (see get_rho function) dz = z[1] - z[0] if use_Ekman_flux and Ekman_Q_flux is None: raise Exception( 'Using Ekman-induced buoyacy flux but no buoyancy gradients were given.' ) if use_MLI and nabla_b is None: raise Exception('Using MLI but no horizontal buoyancy gradient given.') T = T.copy() S = S.copy() U = U.copy() V = V.copy() # so don't overwrite data if tracer is not None: tracer = tracer.copy() # make initial heat profile I_profile = -I / dz * ( I1 * np.exp(-z / lambda1) * (np.exp(-dz / 2 / lambda1) - np.exp(dz / 2 / lambda1)) + I2 * np.exp(-z / lambda2) * (np.exp(-dz / 2 / lambda2) - np.exp(dz / 2 / lambda2))) L_profile = np.zeros(len(z)) L_profile[0] = L / dz Q_profile = I_profile + L_profile if use_Ekman_flux: A = 0.1 # eddy viscosity m^2 s^-1 z_Ek = np.sqrt(A / np.abs(f)) if verbose: print('Using Ekman depth of %d m' % z_Ek) z_Ek_ind = np.where(z > z_Ek)[0][0] Q_Ek_profile = np.zeros(len(z)) Q_Ek_profile[0:z_Ek_ind] = Ekman_Q_flux / z_Ek * dz Q_profile += Q_Ek_profile # update temperature dTdt = Q_profile / (c * rho0) if use_MLI: mld_ind = get_mld_ind(T, S, U, V, z) mld = z[mld_ind] C_e = 0.06 g = 9.81 # gravitational acceleration (m s^-2) c = 4218 # heat capacity (J kg^-1 degC^-1 MLI_dTdt = -C_e * nabla_b**2 * mld**2 * rho0 / (np.abs(f) * alpha * g) vert_profile = 4 / mld * (1 - 2 * z / mld) * ( 16 + 10 * (1 - 2 * z / mld)**2) / 21 # this is vertical derivative of mu(z) vert_profile[mld_ind::] = 0 vert_profile[0:mld_ind] -= np.mean( vert_profile[0:mld_ind] ) # just to ensure that no heat added to system dTdt += MLI_dTdt * vert_profile T += dTdt * dt # update salinity dSdt = S[0] * (E - P) / dz / 1000 S[0] += dSdt * dt if use_MLI: rho = get_rho(T, S, T0, S0, rho0, alpha, beta) half_mld_ind = int(mld_ind / 2) if np.any(np.diff(rho[half_mld_ind::]) < 0): if verbose: print( 'Need to homogenize discontinuity at base of previous mixed layer' ) # get rid of discontinuity at base of previous mixed layer # homogenize the region of water from mld/2 to z* # z* is the shallowest value (> mld/2) such that the homogenized rho <= rho(z*) zstar_ind = mld_ind.copy() while np.mean(rho[half_mld_ind:zstar_ind]) >= rho[zstar_ind]: if verbose: print('Deepening z*...') zstar_ind += 1 T[half_mld_ind:zstar_ind] = np.mean(T[half_mld_ind:zstar_ind]) S[half_mld_ind:zstar_ind] = np.mean(S[half_mld_ind:zstar_ind]) if tracer is not None: tracer[:, half_mld_ind:zstar_ind] = np.atleast_2d( np.mean(tracer[:, half_mld_ind:zstar_ind], axis=1)).T elif verbose: print('No need to homogenize base of previous mixed layer') # update momentum # first rotate momentum halfway angle = -f * dt / 2 # currently assuming this is in rad U, V = rotate(angle, U, V) # then add wind stress mld_ind = get_mld_ind(T, S, U, V, z) mld = z[mld_ind] U[0:mld_ind] += tau_x / mld / rho0 * dz * dt V[0:mld_ind] += tau_y / mld / rho0 * dz * dt # then rotate second half U, V = rotate(angle, U, V) if use_static_stability: T, S, U, V, tracer = static_stability(T, S, U, V, z, T0, S0, rho0, alpha, beta, tracer=tracer, verbose=verbose) if use_mixed_layer_stability: T, S, U, V, tracer = mixed_layer_stability(T, S, U, V, z, T0, S0, rho0, alpha, beta, tracer=tracer, verbose=verbose) if use_shear_stability: T, S, U, V, tracer = shear_stability(T, S, U, V, z, T0, S0, rho0, alpha, beta, tracer=tracer, verbose=verbose) if vert_diffusivity is not None: dTdt = np.zeros(len(T)) dTdt[1:-1] = np.diff(np.diff(T)) / dz**2 T += vert_diffusivity * dTdt * dt dSdt = np.zeros(len(S)) dSdt[1:-1] = np.diff(np.diff(S)) / dz**2 S += vert_diffusivity * dSdt * dt dUdt = np.zeros(len(U)) dUdt[1:-1] = np.diff(np.diff(U)) / dz**2 U += vert_diffusivity * dUdt * dt dVdt = np.zeros(len(V)) dVdt[1:-1] = np.diff(np.diff(V)) / dz**2 V += vert_diffusivity * dSdt * dt if tracer is not None: dtdt = np.zeros(shape=tracer.shape) dtdt[:, 1:-1] = np.diff(np.diff(tracer, axis=1), axis=1) / dz**2 tracer += vert_diffusivity * dtdt * dt return_variables = ( T, S, U, V, ) if tracer is not None: return_variables += (tracer, ) if return_MLD: return_variables += (get_mld(T, S, U, V, z), ) return return_variables
def vmodes(N2, z, nm, lat, ubdy='N', lbdy='N'): f0 = sw.f(lat) dz = np.abs(z[1] - z[0]) # f2n2 array f2n2 = (f0**2) / N2 # assembling matrices # N2F2 matrix N2F2 = np.eye(z.size - 1) for i in range(0, z.size - 1): N2F2[i, i] = (f2n2[i] + f2n2[i + 1]) / 2 # 1st order difference matrix A = np.eye(z.size)[:, :-1] for i in range(1, z.size): A[i, i - 1] = -1 A = A / dz # linear operator C = A*N2F2*(A.transpose()) A = np.matrix(A) N2F2 = np.matrix(N2F2) C = A * N2F2 * A.transpose() if ubdy == 'D': C = C[1:, 1:] if lbdy == 'D': C = C[:-1, :-1] # solve the eigenvalue problem lam, fi = np.linalg.eig(C) i = np.argsort(lam) ei = lam[i] fi = fi[:, i] ei = ei[:nm] fi = fi[:, :nm] if ubdy == 'D': fi = np.append(np.matrix(np.zeros(nm)), fi, axis=0) if lbdy == 'D': fi = np.append(fi, np.matrix(np.zeros(nm)), axis=0) # normalizing to get orthonormal modes H = np.abs(z).max() for i in range(nm): s = np.sqrt( dz * ((np.array(fi[1:, i])**2 + np.array(fi[:-1, i])**2) / 2 / H).sum()) fi[:, i] = fi[:, i] / s # to keep consistency multiply by plus/minus to make the leading term positive for i in range(nm): if np.sign(fi[1, i]) < 0: fi[:, i] = -fi[:, i] # compute the deformation radii [km]; the barotropic radius is computed by sqrt(gH)/f0 radii = np.zeros(nm) radii[0] = np.sqrt(9.81 * H) / np.abs(f0) / 1000 radii[1:] = 1 / np.sqrt(ei[1:]) / 1000 return fi, radii
def plotsec(opname, rad, pastafig, pathadcp, corsta='b', cond=False, ts=False, fmed=False, interp=False, radcomplicada=False, mdrcalc=True): vsec,latv,lonv,lat,lon,section,lonsta,latsta = [],[],[],[],[],[],[],[] adcp, lat_adcp, lon_adcp = [], [], [] radname = 'r' + os.path.split(rad)[1] if mdrcalc: vsec, lat, lon, section = extsect(rad, sup=True) else: vsec, lat, lon, section = extsect(rad, sup=False) # opname = section.items.values[0].split('0')[0] if ts: tscomp(section) plt.savefig(pastafig + opname + '/ts/' + 'ts' + radname + '.png', dpi=100, format='png') plt.close('all') lat_adcp, lon_adcp, adcp = adcpvel(lat, lon, pathadcp, dya=600, interp=interp) if mdrcalc: vsec = mdr(vsec, adcp) # lonsta = np.nanmean(section.minor_xs('lon'),axis=0) # latsta = np.nanmean(section.minor_xs('lat'),axis=0) latv = lat[:-1] + np.diff(lat) / 2 lonv = lon[:-1] + np.diff(lon) / 2 if fmed: f = sw.f(latv) vsec = vsec * (f / np.mean(f)) if cond: # SALINIDADE plotprop(z=section.minor_xs('sp'), lat=latsta, lon=lonsta, cor=corsta, csteps=0.5, title=u'Salinidade Prática', propmin=34, propmax=37) plt.savefig(pastafig + opname + '/salinidade/' + 'sal' + radname + '.png', dpi=100, format='png') plt.close('all') # TEMPERATURA plotprop(z=section.minor_xs('pt'), lat=latsta, lon=lonsta, cor=corsta, csteps=2.5, title=u'Temperatura Potencial ($\\theta_0$)', propmin=0, propmax=28) plt.savefig(pastafig + opname + '/temperatura/' + 'temp' + radname + '.png', dpi=100, format='png') plt.close('all') # MASSAS D'ÁGUA plotprop(z=section.minor_xs('psigma0'), lat=latsta, lon=lonsta, cor=corsta, wmass=True, title=u'Massas D\'água ($\\rho_0$)') plt.savefig(pastafig + opname + '/massas/' + 'mass' + radname + '.png', dpi=100, format='png') plt.close('all') # DENSIDADE POTENCIAL plotprop(z=section.minor_xs('psigma0'), lat=latsta, lon=lonsta, cor=corsta, csteps=0.5, title=u'Densidade Potencial ($\\rho_0$)', propmin=23, propmax=28) plt.savefig(pastafig + opname + '/densidade/' + 'dens' + radname + '.png', dpi=100, format='png') plt.close('all') # reff = adcp.iloc[150,:].values-vsec.iloc[150,:].values # vsec += reff if radcomplicada: latv[3] = -2.0933934592636545 lonv[3] = -38.442823159524877 if mdrcalc: ttitle = u'Velocidade Geostrófica (MDR)' else: ttitle = u'Velocidade Geostrófica (Isopicnal = 32.15)' # Velocidade Geostrófica plotprop(z=vsec, cmap='RdBu_r', lat=latv, lon=lonv, cor=corsta, csteps=0.1, title=ttitle, propmin=-1.5, propmax=1.5, lim=-300) plt.savefig(pastafig + opname + '/geostrofia/' + 'geos' + radname + '.png', dpi=100, format='png') plt.close('all') plotprop(z=vsec, lat=latv, lon=lonv, cor=corsta, z2=adcp, lat2=lat_adcp, lon2=lon_adcp, cor2='g', csteps=0.1, title=u'Velocidade Geostrófica X Velocidade Observada', cmap='RdBu_r', propmin=-1.5, propmax=1.5, comp=True, lim=-300) plt.savefig(pastafig + opname + '/velocidade/' + 'vel' + radname + 'adcp.png', dpi=100, format='png') plt.close('all')
""" params = {} params['dt'] = 3600.0*dt params['dt_d'] = params['dt']/86400. params['dz'] = dz params['dt_save'] = dt_save params['lat'] = lat params['rb'] = rb params['rg'] = rg params['rkz'] = rkz params['beta1'] = beta1 params['beta2'] = beta2 params['max_depth'] = max_depth params['g'] = 9.81 params['f'] = sw.f(lat) params['cpw'] = 4183.3 params['ucon'] = (0.1*np.abs(params['f'])) params['mld_thresh'] = mld_thresh params['winds_ON'] = winds_ON params['emp_ON'] = emp_ON params['heat_ON'] = heat_ON params['drag_ON'] = drag_ON return params def prep_data(met_dset, prof_dset, params):
def qg_stability_2d(N2, ubar, vbar, z, k, l, lat, hx=0., hy=0., structure=False): f0 = sw.f(lat) beta = 2 * 7.29e-5 * np.cos(lat * pi / 180.) / 6371.e3 # calculate surface shear ubarz, vbarz = ubar[0] - ubar[1], vbar[0] - vbar[1] dz = np.abs(z[1] - z[0]) S = (f0**2) / N2 L = stretching_matrix(z.size, S, dz) k2 = k**2 + l**2 L2 = L - np.eye(ubar.size) * k2 Qy = np.eye( ubar.size) * (beta - np.array(np.matrix(L) * np.matrix(ubar).T)) Qx = np.eye(ubar.size) * np.array(np.matrix(L) * np.matrix(vbar).T) # bottom bc with topography Qx[-1, -1] += (f0 / dz) * hx Qy[-1, -1] += (f0 / dz) * hy Q = k * Qy - l * Qx L3 = np.empty_like(L2) for i in range(ubar.size): L3[i, :] = L2[i, :] * (ubar[i] * k + vbar[i] * l) A = (L3 + Q) B = L2.copy() # append surface boundary condition #A = np.vstack([np.zeros_like(z),A]) #B = np.vstack([np.zeros_like(z),B]) #A = np.hstack([np.zeros(z),A]) #B = np.hstack([np.zeros(z),B]) #print(A.shape) #print(B.shape) # the upper boundary condition #A[0,0] = (ubar[0]*k+vbar[0]*l) #A[0,1] = -(ubar[0]*k+vbar[0]*l) #A[0,0] += -(k*ubarz + l*vbarz) #B[0,0], B[0,1] = dz,-dz #print(A[0,:5]) # bottom boundary condition satisfied # (may be should include topographic gradients) try: evals, evecs = sp.linalg.eig(A, B) imax = evals.imag.argmax() eval_max = evals[imax] evec_max = evecs[0:, imax] except: eval_max = np.nan + 1.j * np.nan evec_max = np.nan * z #print(eval_max) if structure: PSI, X, Z = wave_structure(evec_max, z, k) return eval_max, evec_max, PSI, X, Z else: return eval_max, evec_max
#Le = np.linspace(750.,10.,30) #k = 2*pi/(Le*1.e3) #gr = [] #for i in range(k.size): # c,psi,PSI,X,Z = qg_stability(N2i,Up,-zi,k=k[i],lat=58) # gr.append(c.imag*k[i]) #Le = np.linspace(1000.,1.,50) Le = np.logspace(3, 5., 50) k = 2 * pi / (Le * 1.e3) k = np.hstack([-np.flipud(k), k]) l = k.copy() f0 = sw.f(-58) S = f0**2 / N2i N = N2i.size evals, evecs = pmodes(N, S, -zi, nn=10, dz=zi[2] - zi[1]) xi = integrate.trapz(evecs[:, 1]**3, zi) / zi.max() delta = .25 * ((np.sqrt(xi**2 + 4.) - xi)**2) # project Ui onto evecs #A = evecs #ATA = np.dot(A.T,A) #ATU = np.dot(A.T,Up) #alpha = np.dot(np.linalg.inv(ATA),ATU) au, av = modal_projection(evecs, Up), modal_projection(evecs, Vp)
ds1 = xr.open_dataset('../data/model_stats/S0.01_gridded_stats.nc') ds1 = ds1.drop('index') ds1.set_coords(['alpha', 'slope', 'seed', 'period']) df1 = ds1.to_dataframe() ds5 = xr.open_dataset('../data/model_stats/S0.005_gridded_stats.nc') ds5 = ds5.drop('index') ds5.set_coords(['alpha', 'slope', 'seed', 'period']) df5 = ds5.to_dataframe() df = pd.concat([df1, df5]) cg = 9.8*df.t0m1_mean/4/np.pi cg1 = 9.8*df1.t0m1_mean/4/np.pi cg5 = 9.8*df5.t0m1_mean/4/np.pi c_llc = 9.8*dfl.t0m1_mean/4/np.pi f = sw.f(31.5) ind=df.alpha<0.9 ind1=df1.alpha<0.9 ind5=df5.alpha<0.9 corr_df = np.corrcoef(df.vorticity[ind].values, cg[ind]*df.hs_grad[ind]/df.hs_mean[ind]/df.slope[ind])[0, 1] cor_llc = np.corrcoef(dfl.vorticity, c_llc*dfl.hs_grad/dfl.hs_mean/dfl.EKE_psi_slope)[0,1] a, b, __, __, __ = linregress(df.vorticity[ind]/f, cg[ind]*df.hs_grad[ind]/df.hs_mean[ind]/df.slope[ind]/f) ############################################ # Normalized scatterplot ############################################ plt.figure(figsize=(8,8)) plt.plot(df1.vorticity[ind1]/f, cg1[ind1]*df1.hs_grad[ind1]/df1.hs_mean[ind1]/df1.slope[ind1]/f, 'o', color='steelblue', alpha=.5, label='KE = 0.01 m$^2$/s$^2$')
Nb = np.nanmean(N2) P = np.arange(0., N2.size) P0 = np.ones(N2.size) plt.figure() rdi = np.zeros(nmodes) for n in np.arange(1, nmodes): P[0] = (-1)**n * np.sqrt(2 * N2[0] / Nb) P[-1] = np.sqrt(2 * N2[-1] / Nb) rdi[n] = np.abs((n * np.pi * f / (Nb * N2.size - 1))) for z in np.arange(1, N2.size): P[z] = np.sqrt(2 * N2[z] / Nb) * np.cos( n * np.pi / (Nb * N2.size - 1) * np.sum(N2[z:])) plt.plot(np.flipud(P), -np.arange(0, N2.size), linewidth=1.6) plt.plot(np.flipud(P0), -np.arange(0, N2.size), linewidth=1.6) plt.plot(np.tile(0, N2.size), -np.arange(0, N2.size), '--k', linewidth=1.6) return rdi prof_max = 5000. z = np.arange(0., prof_max + 1) * -1 d = 900. f = sw.f(23.) N2 = 10**4 * np.exp(z / d) * f**2 #N2=np.ones(z.size)*5 plt.figure() plt.plot(N2, z) rdi = wkb(N2, f, nmodes=3)