def zmld_so(s, t, p, threshold=0.05, smooth=None): """ Computes mixed layer depth of Southern Ocean waters. Parameters ---------- s : array_like salinity [psu (PSS-78)] t : array_like temperature [℃ (ITS-90)] p : array_like pressure [db]. smooth : int size of running mean window, to smooth data. References ---------- Mitchell B. G., Holm-Hansen, O., 1991. Observations of modeling of the Antartic phytoplankton crop in relation to mixing depth. Deep Sea Research, 38(89):981-1007. doi:10.1016/0198-0149(91)90093-U """ sigma_t = sw.dens0(s, t) - 1000. depth = p if smooth is not None: sigma_t = rolling_mean(sigma_t, smooth, min_periods=1) sublayer = np.where(depth[(depth >= 5) & (depth <= 10)])[0] sigma_x = np.nanmean(sigma_t[sublayer]) nan_sigma = np.where(sigma_t > sigma_x + threshold)[0] sigma_t[nan_sigma] = np.nan der = np.divide(np.diff(sigma_t), np.diff(depth)) mld = np.where(der == np.nanmax(der))[0] zmld = depth[mld] return zmld
def tsplot(sobs,tobs): smin=sobs.min() smax=sobs.max() tmin=tobs.min() tmax=tobs.max() s_inc=(smax-smin)/8. t_inc=(tmax-tmin)/8. t = np.arange(tmin,tmax+t_inc,t_inc) s = np.arange(smin,smax+s_inc,s_inc) S, T = np.meshgrid(s, t) st = sw.dens0(S, T) - 1000 st_inc=(st.max()-st.min())/8. levels = np.arange(st.min(),st.max()+st_inc,st_inc) from matplotlib import rcParams from matplotlib.ticker import MultipleLocator rcParams['xtick.direction'] = 'out' rcParams['ytick.direction'] = 'out' fig, ax = plt.subplots(figsize=(6, 4)) # ax.xaxis.set_major_locator(MultipleLocator(0.5)) # ax.xaxis.set_minor_locator(MultipleLocator(0.1)) # ax.yaxis.set_major_locator(MultipleLocator(5)) # ax.yaxis.set_minor_locator(MultipleLocator(1)) ax.set_ylabel(u"Temperature \u00b0C") ax.set_xlabel(r"Salinity [g kg$^{-1}$]") ax.axis([smin,smax,tmin,tmax]) cs = ax.contour(s, t, st, colors='black', levels=levels) ax.clabel(cs, fontsize=9, inline=1, fmt='%3.2f') #sg = ax.contour(s, t, sigma_theta, linestyle='--', colors='grey', levels=[0, line]) #ax.clabel(sg, fontsize=9, inline=1, fmt='%2.1f') ax.plot(sobs,tobs,'o')
def convert_oxygen(self): if hasattr( self.data, 'sbe63_oxygen' ) & \ hasattr( self.data, 'prwl_salt' ) & \ hasattr( self.data, 'prwl_temp' ): self.StatusBar.SetStatusText('Converting Oxygen') # conversion from mL/L to umol/L self.data.sbe63_oxygen = self.data.sbe63_oxygen * .7 * 44.661 # conversion to umol/kg density = dens0(self.data.prwl_salt, self.data.prwl_temp) self.data.sbe63_oxygen = self.data.sbe63_oxygen / density * 1000. # calculating saturation potential i = np.where( [key.startswith('sbe63_oxygen') for key in self.data.keys()])[0].max() + 1 sbe63_oxygen_sat = Series(None, index=self.data.index) sbe63_oxygen_sat[:] = satO2(self.data.prwl_salt, self.data.prwl_temp) sbe63_oxygen_sat = sbe63_oxygen_sat * 44.661 sbe63_oxygen_sat = sbe63_oxygen_sat / density * 1000. self.data.insert(i, 'sbe63_oxygen_sat', sbe63_oxygen_sat) # calculating saturation % sbe63_oxygen_sat_precent = Series(None, index=self.data.index) sbe63_oxygen_sat_precent = self.data.sbe63_oxygen / self.data.sbe63_oxygen_sat * 100. self.data.insert(i + 1, 'sbe63_oxygen_sat_precent', sbe63_oxygen_sat_precent) else: pass
def tsplot(sobs, tobs): smin = sobs.min() smax = sobs.max() tmin = tobs.min() tmax = tobs.max() s_inc = (smax - smin) / 8. t_inc = (tmax - tmin) / 8. t = np.arange(tmin, tmax + t_inc, t_inc) s = np.arange(smin, smax + s_inc, s_inc) S, T = np.meshgrid(s, t) st = sw.dens0(S, T) - 1000 st_inc = (st.max() - st.min()) / 8. levels = np.arange(st.min(), st.max() + st_inc, st_inc) from matplotlib import rcParams from matplotlib.ticker import MultipleLocator rcParams['xtick.direction'] = 'out' rcParams['ytick.direction'] = 'out' fig, ax = plt.subplots(figsize=(6, 4)) # ax.xaxis.set_major_locator(MultipleLocator(0.5)) # ax.xaxis.set_minor_locator(MultipleLocator(0.1)) # ax.yaxis.set_major_locator(MultipleLocator(5)) # ax.yaxis.set_minor_locator(MultipleLocator(1)) ax.set_ylabel(u"Temperature \u00b0C") ax.set_xlabel(r"Salinity [g kg$^{-1}$]") ax.axis([smin, smax, tmin, tmax]) cs = ax.contour(s, t, st, colors='black', levels=levels) ax.clabel(cs, fontsize=9, inline=1, fmt='%3.2f') #sg = ax.contour(s, t, sigma_theta, linestyle='--', colors='grey', levels=[0, line]) #ax.clabel(sg, fontsize=9, inline=1, fmt='%2.1f') ax.plot(sobs, tobs, 'o')
def mix5(t, s, d, u, v, j): #This subroutine mixes the arrays t, s, u, v down to level j. j = j + 1 #so that the j-th layer is included in the mixing t[:j] = np.mean(t[:j]) s[:j] = np.mean(s[:j]) d[:j] = sw.dens0(s[:j], t[:j]) u[:j] = np.mean(u[:j]) v[:j] = np.mean(v[:j]) return t, s, d, u, v
def mix5(t, s, d, u, v, j): #This subroutine mixes the arrays t, s, u, v down to level j. j = j+1 #so that the j-th layer is included in the mixing t[:j] = np.mean(t[:j]) s[:j] = np.mean(s[:j]) d[:j] = sw.dens0(s[:j], t[:j]) u[:j] = np.mean(u[:j]) v[:j] = np.mean(v[:j]) return t, s, d, u, v
def plot_salvtemp(salt, temp, press, srange=[28,34], trange=[-2,15], ptitle=""): plt.style.use('ggplot') # Figure out boudaries (mins and maxs) smin = srange[0] smax = srange[1] tmin = trange[0] tmax = trange[1] # Calculate how many gridcells we need in the x and y dimensions xdim = int(round((smax-smin)/0.1+1,0)) ydim = int(round((tmax-tmin)+1,0)) #print 'ydim: ' + str(ydim) + ' xdim: ' + str(xdim) + ' \n' if (xdim > 10000) or (ydim > 10000): print('To many dimensions for grid in {cruise} {cast} file. \ Likely missing data \n'.format(cruise=cruise,cast=cast)) return # Create empty grid of zeros dens = np.zeros((ydim,xdim)) # Create temp and salt vectors of appropiate dimensions ti = np.linspace(0,ydim-1,ydim)+tmin si = np.linspace(0,xdim-1,xdim)*0.1+smin # Loop to fill in grid with densities for j in range(0,int(ydim)): for i in range(0, int(xdim)): dens[j,i]=sw.dens0(si[i],ti[j]) # Substract 1000 to convert to sigma-t dens = dens - 1000 # Plot data *********************************************** fig, ax1 = plt.subplots(figsize=(8, 8), facecolor='w', edgecolor='w') CS = plt.contour(si,ti,dens, linestyles='dashed', colors='gray') plt.clabel(CS, fontsize=12, inline=1, fmt='%1.1f') # Label every second level ts = ax1.scatter(salt,temp, c=press, cmap='gray', s=10) cbar = plt.colorbar(ts) cbar.ax.tick_params(labelsize=14) plt.ylim(tmin,tmax) plt.xlim(smin,smax) plt.tick_params(axis='both', which='major', labelsize=14) ax1.set_xlabel('Salinity (PSU)',fontsize=16) ax1.set_ylabel('Temperature (C)',fontsize=16) t = fig.suptitle(ptitle, fontsize=18, fontweight='bold') return fig
def dens_back(smin, smax, tmin, tmax): """Calculate density for TS diagram.""" xdim = round((smax - smin) / 0.1 + 1, 0) ydim = round((tmax - tmin) + 1, 0) ti_size = np.linspace(tmin, tmax, int(ydim * 10)) si_size = np.linspace(smin, smax, int(xdim * 10)) si2d, ti2d = np.meshgrid(si_size, ti_size) dens = sw.dens0(si2d, ti2d) - 1000 return si2d, ti2d, dens
def plot_s0_line(tmin,smin,tmax,smax,siglvl=[27.88, 27.8, 27.68, 27.55]): # Create empty grid of zeros ydim=xdim=100 dens = np.zeros((ydim,xdim)) # Create temp and salt vectors of appropiate dimensions ti = np.linspace(tmin,tmax,ydim) si = np.linspace(smin,smax,xdim) # Loop to fill in grid with densities for j in range(0,int(ydim)): for i in range(0, int(xdim)): dens[j,i]=seawater.dens0(si[i],ti[j]) # Substract 1000 to convert to sigma-t dens = dens - 1000 # Plot data *********************************************** CS = plt.contour(si, ti, dens, levels=siglvl, linestyles='dashed', colors='k') plt.clabel(CS, fontsize=12, inline=1, fmt='%4.2f', inline_spacing=1, use_clabeltext=1) # Label every second level
def stir(t, s, d, u, v, rc, r, j,n): #copied from source script: # % This subroutine mixes cells j and j+1 just enough so that # % the Richardson number after the mixing is brought up to # % the value rnew. In order to have this mixing process # % converge, rnew must exceed the critical value of the # % richardson number where mixing is presumed to start. If # % r critical = rc = 0.25 (the nominal value), and r = 0.20, then # % rnew = 0.3 would be reasonable. If r were smaller, then a # % larger value of rnew - rc is used to hasten convergence. # # % This subroutine was modified by JFP in Sep 93 to allow for an # % aribtrary rc and to achieve faster convergence. #TODO: This needs better commenting rcon = 0.02+(rc-r)/2 rnew = rc+rcon/5. f = 1-r/rnew #mix temp dt = (t[j+1]-t[j])*f/2. t[j+1] = t[j+1]-dt t[j] = t[j]+dt #mix sal ds = (s[j+1]-s[j])*f/2. s[j+1] = s[j+1]-ds s[j] = s[j]+ds #recompute density #d[j:j+1] = sw.dens0(s[j:j+1], t[j:j+1]) #have to be careful here. x[j:j+1] in python is not the same as x[[j,j+1]]. We want the latter #ipdb.set_trace(context=9,cond=n>=12) d[[j,j+1]] = sw.dens0(s[[j,j+1]], t[[j,j+1]]) du = (u[j+1]-u[j])*f/2 u[j+1] = u[j+1]-du u[j] = u[j]+du dv = (v[j+1]-v[j])*f/2 v[j+1] = v[j+1]-dv v[j] = v[j]+dv return t, s, d, u, v
def stir(t, s, d, u, v, rc, r, j): #copied from source script: # % This subroutine mixes cells j and j+1 just enough so that # % the Richardson number after the mixing is brought up to # % the value rnew. In order to have this mixing process # % converge, rnew must exceed the critical value of the # % richardson number where mixing is presumed to start. If # % r critical = rc = 0.25 (the nominal value), and r = 0.20, then # % rnew = 0.3 would be reasonable. If r were smaller, then a # % larger value of rnew - rc is used to hasten convergence. # # % This subroutine was modified by JFP in Sep 93 to allow for an # % aribtrary rc and to achieve faster convergence. #TODO: This needs better commenting rcon = 0.02+(rc-r)/2 rnew = rc+rcon/5. f = 1-r/rnew #mix temp dt = (t[j+1]-t[j])*f/2. t[j+1] = t[j+1]-dt t[j] = t[j]+dt #mix sal ds = (s[j+1]-s[j])*f/2. s[j+1] = s[j+1]-ds s[j] = s[j]+ds #recompute density #d[j:j+1] = sw.dens0(s[j:j+1], t[j:j+1]) #have to be careful here. x[j:j+1] in python is not the same as x[[j,j+1]]. We want the latter d[[j,j+1]] = sw.dens0(s[[j,j+1]], t[[j,j+1]]) du = (u[j+1]-u[j])*f/2 u[j+1] = u[j+1]-du u[j] = u[j]+du dv = (v[j+1]-v[j])*f/2 v[j+1] = v[j+1]-dv v[j] = v[j]+dv return t, s, d, u, v
def add_den_usv(ds): import seawater as sw import numpy as np import xarray as xr ds['wspd']=np.sqrt(ds.UWND_MEAN**2+ds.VWND_MEAN**2) tem=sw.dens0(ds.SAL_CTD_MEAN,ds.TEMP_CTD_MEAN) ds['density_mean']=xr.DataArray(tem,dims=('time'),coords={'time':ds.time}) tem=sw.alpha(ds.SAL_CTD_MEAN,ds.TEMP_CTD_MEAN,ds.BARO_PRES_MEAN*0) #pressure =0 at surface ds['alpha_ME']=xr.DataArray(tem,dims=('time'),coords={'time':ds.time}) tem=sw.beta(ds.SAL_CTD_MEAN,ds.TEMP_CTD_MEAN,ds.BARO_PRES_MEAN*0) #pressure =0 at surface ds['beta_MEAN']=xr.DataArray(tem,dims=('time'),coords={'time':ds.time}) xlat=ds.lat xlon=ds.lon dkm2 = abs(np.abs((((xlon[1:].data-xlon[0:-1].data)**2+(xlat[1:].data-xlat[0:-1].data)**2)**.5)*110.567*np.cos(np.pi*xlat[1:].data/180))) dkm2=np.append(dkm2,dkm2[len(dkm2)-1]) #add on last point dkm3 = dkm2.cumsum() ds['dist_total']=xr.DataArray(dkm3,dims=('time'),coords={'time':ds.time}) ds['dist_between']=xr.DataArray(dkm2,dims=('time'),coords={'time':ds.time}) return ds
def pwpgo(forcing, params, pwp_out, diagnostics): """ This is the main driver of the PWP module. """ #unpack some of the variables #This is not necessary, but I don't want to update all the variable names just yet. q_in = forcing['q_in'] q_out = forcing['q_out'] emp = forcing['emp'] taux = forcing['tx'] tauy = forcing['ty'] absrb = forcing['absrb'] z = pwp_out['z'] dz = pwp_out['dz'] dt = pwp_out['dt'] zlen = len(z) tlen = len(pwp_out['time']) rb = params['rb'] rg = params['rg'] f = params['f'] cpw = params['cpw'] g = params['g'] ucon = params['ucon'] printDragWarning = True print("Number of time steps: %s" % tlen) for n in range(1, tlen): percent_comp = 100 * n / float(tlen) print('Loop iter. %s (%.1f %%)' % (n, percent_comp)) #select for previous profile data temp = pwp_out['temp'][:, n - 1] sal = pwp_out['sal'][:, n - 1] dens = pwp_out['dens'][:, n - 1] uvel = pwp_out['uvel'][:, n - 1] vvel = pwp_out['vvel'][:, n - 1] ### Absorb solar radiation and FWF in surf layer ### #save initial T,S (may not be necessary) temp_old = pwp_out['temp'][0, n - 1] sal_old = pwp_out['sal'][0, n - 1] #update layer 1 temp and sal temp[0] = temp[0] + (q_in[n - 1] * absrb[0] - q_out[n - 1]) * dt / (dz * dens[0] * cpw) sal[0] = sal[0] / (1 - emp[n - 1] * dt / dz) #check if temp is less than freezing point T_fz = sw.fp(sal_old, 1) #why use sal_old? Need to recheck if temp[0] < T_fz: temp[0] = T_fz ### Absorb rad. at depth ### temp[1:] = temp[1:] + q_in[n - 1] * absrb[1:] * dt / (dz * dens[1:] * cpw) ### compute new density ### dens = sw.dens0(sal, temp) ### relieve static instability ### temp, sal, dens, uvel, vvel = remove_si(temp, sal, dens, uvel, vvel) ### Compute MLD ### #find ml index ml_thresh = params['mld_thresh'] mld_idx = np.flatnonzero(dens - dens[0] > ml_thresh)[ 0] #finds the first index that exceed ML threshold #check to ensure that ML is defined assert mld_idx.size is not 0, "Error: Mixed layer depth is undefined." #get surf MLD mld = z[mld_idx] ### Rotate u,v do wind input, rotate again, apply mixing ### ang = -f * dt / 2 uvel, vvel = rot(uvel, vvel, ang) du = (taux[n - 1] / (mld * dens[0])) * dt dv = (tauy[n - 1] / (mld * dens[0])) * dt uvel[:mld_idx] = uvel[:mld_idx] + du vvel[:mld_idx] = vvel[:mld_idx] + dv ### Apply drag to current ### #Original comment: this is a horrible parameterization of inertial-internal wave dispersion if params['drag_ON']: if ucon > 1e-10: uvel = uvel * (1 - dt * ucon) vvel = vvel * (1 - dt * ucon) else: if printDragWarning: print( "Warning: Parameterization for inertial-internal wave dispersion is turned off." ) printDragWarning = False uvel, vvel = rot(uvel, vvel, ang) ### Apply Bulk Richardson number instability form of mixing (as in PWP) ### if rb > 1e-5: temp, sal, dens, uvel, vvel = bulk_mix(temp, sal, dens, uvel, vvel, g, rb, zlen, z, mld_idx) ### Do the gradient Richardson number instability form of mixing ### if rg > 0: temp, sal, dens, uvel, vvel = grad_mix(temp, sal, dens, uvel, vvel, dz, g, rg, zlen) ### Apply diffusion ### if params['rkz'] > 0: temp = diffus(params['dstab'], zlen, temp) sal = diffus(params['dstab'], zlen, sal) dens = sw.dens0(sal, temp) uvel = diffus(params['dstab'], zlen, uvel) vvel = diffus(params['dstab'], zlen, vvel) ### update output profile data ### pwp_out['temp'][:, n] = temp pwp_out['sal'][:, n] = sal pwp_out['dens'][:, n] = dens pwp_out['uvel'][:, n] = uvel pwp_out['vvel'][:, n] = vvel pwp_out['mld'][n] = mld #do diagnostics if diagnostics == 1: phf.livePlots(pwp_out, n) return pwp_out
def zmld_boyer(s, t, p): """ Computes mixed layer depth, based on de Boyer Montégut et al., 2004. Parameters ---------- s : array_like salinity [psu (PSS-78)] t : array_like temperature [℃ (ITS-90)] p : array_like pressure [db]. Notes ----- Based on density with fixed threshold criteria de Boyer Montégut et al., 2004. Mixed layer depth over the global ocean: An examination of profile data and a profile-based climatology. doi:10.1029/2004JC002378 dataset for test and more explanation can be found at: http://www.ifremer.fr/cerweb/deboyer/mld/Surface_Mixed_Layer_Depth.php Codes based on : http://mixedlayer.ucsd.edu/ """ m = len(s) # starti = min(find((pres-10).^2==min((pres-10).^2))); starti = np.min(np.where(((p - 10.)**2 == np.min((p - 10.)**2)))[0]) pres = p[starti:m] sal = s[starti:m] temp = t[starti:m] starti = 0 m = len(sal) pden = sw.dens0(sal, temp)-1000 mldepthdens_mldindex = m for i, pp in enumerate(pden): if np.abs(pden[starti] - pp) > .03: mldepthdens_mldindex = i break # Interpolate to exactly match the potential density threshold presseg = [pres[mldepthdens_mldindex-1], pres[mldepthdens_mldindex]] pdenseg = [pden[starti] - pden[mldepthdens_mldindex-1], pden[starti] - pden[mldepthdens_mldindex]] P = np.polyfit(presseg, pdenseg, 1) presinterp = np.linspace(presseg[0], presseg[1], 3) pdenthreshold = np.polyval(P, presinterp) # The potential density threshold MLD value: ix = np.max(np.where(np.abs(pdenthreshold) < 0.03)[0]) mldepthdens_mldindex = presinterp[ix] # Search for the first level that exceeds the temperature threshold mldepthptmp_mldindex = m for i, tt in enumerate(temp): if np.abs(temp[starti] - tt) > 0.2: mldepthptmp_mldindex = i break # Interpolate to exactly match the temperature threshold presseg = [pres[mldepthptmp_mldindex-1], pres[mldepthptmp_mldindex]] tempseg = [temp[starti] - temp[mldepthptmp_mldindex-1], temp[starti] - temp[mldepthptmp_mldindex]] P = np.polyfit(presseg, tempseg, 1) presinterp = np.linspace(presseg[0], presseg[1], 3) tempthreshold = np.polyval(P, presinterp) # The temperature threshold MLD value: ix = np.max(np.where(np.abs(tempthreshold) < 0.2)[0]) mldepthptemp_mldindex = presinterp[ix] return mldepthdens_mldindex, mldepthptemp_mldindex
def buoyancy_func(T, C): return -9.8 * 100 * (sw.dens0(C_B * C['g'], T_B * T['g']) - rho0) / rho0
): super().__init__( domain, layout, func, args=[], kw={}, out=None, ) self._parities = parity def meta_parity(self, axis): return self._parities.get(axis, 1) # by default, even parity rho0 = sw.dens0(20, 20) def buoyancy_func(T, C): return -9.8 * 100 * (sw.dens0(C_B * C['g'], T_B * T['g']) - rho0) / rho0 buoyancy = ParityFunction( domain, layout='g', func=buoyancy_func, ) # Buoyancy multiplier for parity constraints par = domain.new_field() par.set_scales(domain.dealias)
def tscomp(secref): ''' This function creates T-S plots for CTD stations and referred climatology from WOA (NOAA). ''' path = '/home/iury/Copy/TCC/dados/CLIMAT/WOA' woa = sio.loadmat(os.path.join(path, 'woa.mat')) tclim = woa['tclim'] sclim = woa['sclim'] lons = np.round(secref.minor_xs('lon').mean()).values + 360 lats = np.round(secref.minor_xs('lat').mean()).values + 90 tt = tclim[lats.tolist(), lons.tolist(), :].ravel() ss = sclim[lats.tolist(), lons.tolist(), :].ravel() fig, ax = plt.subplots() colors = cm.rainbow(np.linspace(0, 1, len(secref.items))) ax.plot(ss, tt, label='WOA', marker='o', color='k', linewidth=0, markersize=5) for i, c in zip(secref.items, colors): ax.scatter(secref.minor_xs('sp')[i], secref.minor_xs('pt')[i], label=i, alpha=0.8, edgecolor='None', color=c, s=7) ax.legend(fontsize=10, loc=4) clev = [20, 25.6, 26.9, 27.38, 27.53, 28] sal, temp = np.meshgrid(np.arange(33, 39), np.arange(0, 31)) dens = sw.dens0(sal, temp) - 1000 dens1 = sw.dens(sal, temp, 1000) - 1000 dens2 = sw.dens(sal, temp, 2000) - 1000 # dens4 = sw.dens(sal,temp,4000)-1000 # c1 = ax.contourf(sal,temp,dens,levels=clev,alpha=0.5, # colors=('#ff0000', '#ff9900', '#00c4ff', '#4fda66', '#0000ff')) c2 = ax.contour(sal, temp, dens, levels=[24.5, 26.8], colors='k') plt.clabel(c2, fontsize=10) c2 = ax.contour(sal, temp, dens1, levels=[32.15], colors='k') plt.clabel(c2, fontsize=10) c2 = ax.contour(sal, temp, dens2, levels=[37], colors='k') plt.clabel(c2, fontsize=10) plt.xlim([sal.min().min(), sal.max().max()]) plt.ylim([temp.min().min(), temp.max().max()]) plt.xlabel('Salinidade') plt.ylabel('Temperatura') # c2 = ax.contour(sal,temp,dens4,levels=[45.83,45.9],colors='k') # plt.clabel(c2, fontsize=10) # position=fig.add_axes([0.11,0.06,0.8,0.02]) # fig.colorbar(c1,orientation='horizontal',cax = position) # # position.text(x=0.1,y=-1.5,ha='center',s='AT') # position.text(x=0.3,y=-1.5,ha='center',s='ACAS') # position.text(x=0.5,y=-1.5,ha='center',s='AIA') # position.text(x=0.7,y=-1.5,ha='center',s='ACS') # position.text(x=0.9,y=-1.5,ha='center',s='APAN') pos = np.array(ax.get_position().bounds) pos[1] += pos[1] * 0.35 ax.set_position(pos) ax.set_title('Diagrama TS') return fig, ax
fn = indir + infile ds = nc.Dataset(fn) lon = ds['lon_rho'][:] lat = ds['lat_rho'][:] dsg = nc.Dataset(Ldir['grid'] + 'grid.nc') # get fields ubar = ds['ubar'][:] vbar = ds['vbar'][:] u = ds['u'][:] v = ds['v'][:] salt = ds['salt'][:] temp = ds['temp'][:] pdens = sw.dens0(salt, temp) z_w = ds['z_w'][:] z_rho = ds['z_rho'][:] ot = ds['ocean_time'][:] t_md = Lfun.modtime_to_mdate_vec(ot) t_dt = [] for t in ot: t_dt.append(Lfun.modtime_to_datetime(t)) def rot_vec(u, v, theta): ur = u * np.cos(theta) + v * np.sin(theta) vr = v * np.cos(theta) - u * np.sin(theta) return ur, vr
def plotTSdiagram(self, sxps): fig = plt.figure(figsize=(8 * 2.5, 10)) ax1 = plt.axes([0.10, 0.1, .2, .8]) ax2 = plt.axes([0.40, 0.1, .2, .8]) ax3 = plt.axes([0.70, 0.1, .2, .8]) texps, sexps = self.exps, sxps.exps # Calculate how many gridcells we need in the x and y dimensions tdata = np.ma.hstack([np.ma.array(e.data) for e in texps]) sdata = np.ma.hstack([np.ma.array(e.data) for e in sexps]) tmin, tmax = np.ma.min(tdata), np.ma.max(tdata) smin, smax = np.ma.min(sdata), np.ma.max(sdata) xdim, ydim = round((smax - smin) / 0.1 + 1, 0), round( (tmax - tmin) / 0.1 + 1, 0) # Create empty grid of zeros dens = np.zeros((ydim, xdim)) # Create temp and salt vectors of appropiate dimensions ti = np.linspace(1, ydim - 1, ydim) * 0.1 + tmin si = np.linspace(1, xdim - 1, xdim) * 0.1 + smin # Loop to fill in grid with densities for j in range(0, int(ydim)): for i in range(0, int(xdim)): dens[j, i] = dens0(si[i], ti[j]) # Substract 1000 to convert to sigma-t dens -= 1000 for ia, ax in enumerate([ax1, ax2, ax3]): CS = ax.contour(si, ti, dens, linestyles='dashed', colors='k') ax.clabel(CS, fontsize=12, inline=1, fmt='%2.1f') # Label every second level pnts, lgnds = [], [] lastidx = -2 # observed climatology 1 t = np.ma.hstack((texps[0].data, texps[0].data[lastidx])) s = np.ma.hstack((sexps[0].data[1:], sexps[0].data[lastidx])) pnts.append(ax.scatter(s, t, lw=3, color='black')) lgnds.append(texps[0].dset) # observed climatology 2 t = np.ma.hstack((texps[1].data, texps[1].data[lastidx])) s = np.ma.hstack((sexps[1].data[1:], sexps[1].data[lastidx])) pnts.append(ax.scatter(s, t, lw=3, color='lightgrey')) lgnds.append(texps[1].dset) # multi-model mean t = np.ma.hstack((texps[-1].data, texps[-1].data[lastidx])) s = np.ma.hstack((sexps[-1].data[1:], sexps[-1].data[lastidx])) pnts.append(ax.scatter(s, t, lw=3, color='darkgrey')) lgnds.append(texps[-1].dset) # then individual models for ename in self.ProductPanel[ia][self.vname]: texp = [e for e in texps if e.dset == ename][0] if ename == 'EN3': sexp = [e for e in sexps if e.dset == 'EN3v2a'][0] else: sexp = [e for e in sexps if e.dset == ename][0] t = np.ma.hstack((texp.data, texp.data[lastidx])) s = np.ma.hstack((sexp.data[1:], sexp.data[lastidx])) pnts.append( ax.scatter(s, t, lw=3, color=ModelLineColors[texp.dset])) if texp.dset == 'GSOP_GLORYS2V4': lgnds.append('GLORYS2V4') elif texp.dset == 'GSOP_GLORYS2V3': lgnds.append('GLORYS2V3') elif texp.dset == 'GloSea5_GO5': lgnds.append('GloSea5') elif texp.dset == 'EN4.2.0.g10': lgnds.append('EN4') else: lgnds.append(texp.dset) if ia == 0: ax.set_ylabel("temperature ($^\circ$C)") ax.set_title("%s) %s" % (Alphabets[ia], self.title)) ax.set_xlabel('salinity (ppm)') ax.legend(pnts, tuple(lgnds), ncol=1, bbox_to_anchor=(0.65, 1.0)) #plt.show() plt.savefig("S" + self.figfile)
#ax[0].plot(sw.dens0(df['s'],df['t']),df['z']) ax[0].plot(df['t'],df['z']) for i in range(N): df['t'] = filters.convolve1d(df['t'], ker) df['t'][start:] = m_t*df['z'][start:]+t_t df['t'][:end] = m_t2*df['z'][:end]+t_t2 ax[0].plot(df['t'],df['z']) ax[0].set_title("temp") ax[1].plot(df['s'],df['z']) for i in range(N): df['s'] = filters.convolve1d(df['s'], ker) df['s'][start:] = m_s*df['z'][start:]+t_s df['s'][:end] = m_s*df['z'][:end]+t_s2 ax[1].plot(df['s'],df['z']) ax[1].set_title("sal") ax[2].plot(sw.dens0(df['s'],df['s']),df['z']) ax[2].set_title("denS") ax[3].plot(np.diff(sw.dens0(df['s'],df['s'])),df['z'][:-1]) ax[3].axvline(0,color="black") ax[3].set_title("dens diff") print(length) for i in range(4): ax[i].invert_yaxis() ax[i].grid(linewidth=.3) plt.savefig('../plots/testtest.pdf',bbox_inches='tight') plt.show() ############################################################################ df.info()
def prep_data(met_dset, prof_dset, params): """ This function prepares the forcing and profile data for the model run. Below, the surface forcing and profile data are interpolated to the user defined time steps and vertical resolutions, respectively. Secondary quantities are also computed and packaged into dictionaries. The code also checks that the time and vertical increments meet the necessary stability requirements. Lastly, this function initializes the numpy arrays to collect the model's output. INPUT: met_data: dictionary-like object with forcing data. Fields should include: ['time', 'sw', 'lw', 'qlat', 'qsens', 'tx', 'ty', 'precip']. These fields should store 1-D time series of the same length. The model expects positive heat flux values to represent ocean warming. The time data field should contain a 1-D array representing fraction of day. For example, for 6 hourly data, met_data['time'] should contain a number series that increases in steps of 0.25, such as np.array([1.0, 1.25, 1.75, 2.0, 2.25...]). See https://github.com/earlew/pwp_python#input-data for more info about the expect intput data. TODO: Modify code to accept met_data['time'] as an array of datetime objects prof_data: dictionary-like object with initial profile data. Fields should include: ['z', 't', 's', 'lat']. These represent 1-D vertical profiles of temperature, salinity and density. 'lat' is expected to be a length=1 array-like object. e.g. prof_data['lat'] = [25.0] params: dictionary-like object with fields defined by set_params function OUTPUT: forcing: dictionary with interpolated surface forcing data. pwp_out: dictionary with initialized variables to collect model output. """ import warnings #create new time vector with time step dt_d #time_vec = np.arange(met_dset['time'][0], met_dset['time'][-1]+params['dt_d'], params['dt_d']) time_vec = np.arange(met_dset['time'][0], met_dset['time'][-1], params['dt_d']) tlen = len(time_vec) #debug_here() #interpolate surface forcing data to new time vector from scipy.interpolate import interp1d forcing = {} for vname in met_dset: p_intp = interp1d(met_dset['time'], met_dset[vname], axis=0) forcing[vname] = p_intp(time_vec) #interpolate E-P to dt resolution (not sure why this has to be done separately) evap_intp = interp1d(met_dset['time'], met_dset['qlat'], axis=0, kind='nearest', bounds_error=False) evap = (0.03456/(86400*1000))*evap_intp(np.floor(time_vec)) #(meters per second?) emp = evap-forcing['precip'] emp[np.isnan(emp)] = 0. forcing['emp'] = emp if params['emp_ON'] == False: print("WARNING: E-P is turned OFF.") forcing['emp'][:] = 0.0 if params['heat_ON'] == False: print("WARNING: Surface heating is turned OFF.") forcing['sw'][:] = 0.0 forcing['lw'][:] = 0.0 forcing['qlat'][:] = 0.0 forcing['qsens'][:] = 0.0 #define q_in and q_out (positive values should mean ocean warming) forcing['q_in'] = forcing['sw'] #heat flux into ocean forcing['q_out'] = -(forcing['lw'] + forcing['qlat'] + forcing['qsens']) #add time_vec to forcing forcing['time'] = time_vec if params['winds_ON'] == False: print("Winds are set to OFF.") forcing['tx'][:] = 0.0 forcing['ty'][:] = 0.0 #define depth coordinate, but first check to see if profile max depth #is greater than user defined max depth zmax = max(prof_dset.z) if zmax < params['max_depth']: depth = zmax print('Profile input shorter than depth selected, truncating to %sm' %depth) #define new z-coordinates init_prof = {} init_prof['z'] = np.arange(0, params['max_depth']+params['dz'], params['dz']) zlen = len(init_prof['z']) #compute absorption and incoming radiation (function defined in PWP_model.py) absrb = PWP.absorb(params['beta1'], params['beta2'], zlen, params['dz']) #(units unclear) dstab = params['dt']*params['rkz']/params['dz']**2 #courant number if dstab > 0.5: print("WARNING: unstable CFL condition for diffusion! dt*rkz/dz**2 > 0.5.") print("To fix this, try to reduce the time step or increase the depth increment.") inpt = eval(input("Proceed with simulation? Enter 'y'or 'n'. ")) if inpt is 'n': raise ValueError("Please restart PWP.m with a larger dz and/or smaller dt. Exiting...") forcing['absrb'] = absrb params['dstab'] = dstab #check depth resolution of profile data prof_incr = np.diff(prof_dset['z']).mean() if params['dz'] < prof_incr/5.: message = "Specified depth increment (%s m), is much smaller than mean profile resolution (%s m)." %(params['dz'], prof_incr) warnings.warn(message) # inpt = input("Depth increment, dz, is much smaller than profile resolution. Is this okay? (Enter 'y'or 'n')") # if inpt is 'n': # raise ValueError("Please restart PWP.m with a new dz >= %s Exiting..." %prof_incr/5.) #debug_here() #interpolate profile data to new z-coordinate from scipy.interpolate import InterpolatedUnivariateSpline for vname in prof_dset: if vname == 'lat' or vname=='lon': continue else: #first strip nans not_nan = np.logical_not(np.isnan(prof_dset[vname])) indices = np.arange(len(prof_dset[vname])) #p_intp = interp1d(prof_dset['z'], prof_dset[vname], axis=0, kind='linear', bounds_error=False) #interp1d doesn't work here because it doesn't extrapolate. Can't have Nans in interpolated profile p_intp = InterpolatedUnivariateSpline(prof_dset['z'][not_nan], prof_dset[vname][not_nan], k=1) init_prof[vname] = p_intp(init_prof['z']) #get profile variables temp0 = init_prof['t'] #initial profile temperature sal0 = init_prof['s'] #intial profile salinity dens0 = sw.dens0(sal0, temp0) #intial profile density #initialize variables for output pwp_out = {} pwp_out['time'] = time_vec pwp_out['dt'] = params['dt'] pwp_out['dz'] = params['dz'] pwp_out['lat'] = params['lat'] pwp_out['z'] = init_prof['z'] tlen = int(np.floor(tlen/params['dt_save'])) arr_sz = (zlen, tlen) pwp_out['temp'] = np.zeros(arr_sz) pwp_out['sal'] = np.zeros(arr_sz) pwp_out['dens'] = np.zeros(arr_sz) pwp_out['uvel'] = np.zeros(arr_sz) pwp_out['vvel'] = np.zeros(arr_sz) pwp_out['mld'] = np.zeros((tlen,)) #use temp, sal and dens profile data for the first time step pwp_out['sal'][:,0] = sal0 pwp_out['temp'][:,0] = temp0 pwp_out['dens'][:,0] = dens0 return forcing, pwp_out, params
oklonGOFS = np.where( np.logical_and(lonGOFS >= lon_limGOFS[0], lonGOFS <= lon_limGOFS[1]))[0] oklatGOFS = np.where( np.logical_and(latGOFS >= lat_limGOFS[0], latGOFS <= lat_limGOFS[1]))[0] #%% NPEA GOFS 100 ## Limit the depths to 100m okdGOFS = np.where(depthGOFS <= 100)[0] depthGOFS_100 = depthGOFS[okdGOFS] tempGOFS_100 = np.asarray(GOFS.variables['water_temp'][oktimeGOFS[4], okdGOFS, oklatGOFS, oklonGOFS]) saltGOFS_100 = np.asarray(GOFS.variables['salinity'][oktimeGOFS[4], okdGOFS, oklatGOFS, oklonGOFS]) densGOFS_100 = seawater.dens0(saltGOFS_100, tempGOFS_100) NPEA_GOFS_100 = Non_Dim_PEA(tempGOFS_100, saltGOFS_100, densGOFS_100, depthGOFS_100) #%% load RTOFS nc files ''' print('Loading 6 hourly RTOFS nc files from FTP server') for t in np.arange(len(nc_files_RTOFS)): #file = out_dir + '/' + nc_files_RTOFS[t] file = nc_files_RTOFS[t] # Login to ftp file ftp = FTP('ftp.ncep.noaa.gov') ftp.login() ftp.cwd('pub/data/nccf/com/rtofs/prod/')
def pwpgo(forcing, params, pwp_out, diagnostics): """ This is the main driver of the PWP module. """ #unpack some of the variables (I could probably do this more elegantly) q_in = forcing['q_in'] q_out = forcing['q_out'] emp = forcing['emp'] taux = forcing['tx'] tauy = forcing['ty'] absrb = forcing['absrb'] z = pwp_out['z'] dz = pwp_out['dz'] dt = pwp_out['dt'] zlen = len(z) tlen = len(pwp_out['time']) rb = params['rb'] rg = params['rg'] f = params['f'] cpw = params['cpw'] g = params['g'] ucon = params['ucon'] for n in xrange(1,tlen): print 'Loop iter. %s' %n #select for previous profile data temp = pwp_out['temp'][:, n-1] sal = pwp_out['sal'][:, n-1] dens = pwp_out['dens'][:, n-1] uvel = pwp_out['uvel'][:, n-1] vvel = pwp_out['vvel'][:, n-1] ### Absorb solar radiation and FWF in surf layer ### #save initial T,S (may not be necessary) temp_old = pwp_out['temp'][0, n-1] sal_old = pwp_out['sal'][0, n-1] #update layer 1 temp and sal temp[0] = temp[0] + (q_in[n-1]*absrb[0]-q_out[n-1])*dt/(dz*dens[0]*cpw) sal[0] = sal[0]/(1-emp[n-1]*dt/dz) #check if temp is less than freezing point T_fz = sw.fp(sal_old, 1) #why use sal_old? Need to recheck if temp[0] < T_fz: temp[0] = T_fz ### Absorb rad. at depth ### temp[1:] = temp[1:] + q_in[n-1]*absrb[1:]*dt/(dz*dens[1:]*cpw) ### compute new density ### dens = sw.dens0(sal, temp) ### relieve static instability ### temp, sal, dens, uvel, vvel = remove_si(temp, sal, dens, uvel, vvel) ### Compute MLD ### #find ml index ml_thresh = params['mld_thresh'] ml_idx = np.flatnonzero(np.diff(dens)>ml_thresh)[0] #finds the first index that exceed ML threshold ml_idx = ml_idx+1 #check to ensure that ML is defined assert ml_idx.size is not 0, "Error: Mixed layer depth is undefined." #get surf MLD mld = z[ml_idx] ### Rotate u,v do wind input, rotate again, apply mixing ### ang = -f*dt/2 uvel, vvel = rot(uvel, vvel, ang) du = (taux[n-1]/(mld*dens[0]))*dt dv = (tauy[n-1]/(mld*dens[0]))*dt uvel[:ml_idx] = uvel[:ml_idx]+du vvel[:ml_idx] = vvel[:ml_idx]+dv ### Apply drag to current ### #Original comment: this is a horrible parameterization of inertial-internal wave dispersion if ucon > 1e-10: uvel = uvel*(1-dt*ucon) vvel = vvel*(1-dt*ucon) uvel, vvel = rot(uvel, vvel, ang) ### Apply Bulk Richardson number instability form of mixing (as in PWP) ### if rb > 1e-5: temp, sal, dens, uvel, vvel = bulk_mix(temp, sal, dens, uvel, vvel, dz, g, rb, zlen, z, ml_idx) ### Do the gradient Richardson number instability form of mixing ### if rg > 0: temp, sal, dens, uvel, vvel = grad_mix(temp, sal, dens, uvel, vvel, dz, g, rg, zlen) ### Apply diffusion ### if params['rkz'] > 0: temp = diffus(params['dstab'], zlen, temp) sal = diffus(params['dstab'], zlen, sal) dens = sw.dens0(sal, temp) uvel = diffus(params['dstab'], zlen, uvel) vvel = diffus(params['dstab'], zlen, vvel) ### update output profile data ### pwp_out['temp'][:, n] = temp pwp_out['sal'][:, n] = sal pwp_out['dens'][:, n] = dens pwp_out['uvel'][:, n] = uvel pwp_out['vvel'][:, n] = vvel pwp_out['mld'][n] = mld #do diagnostics if diagnostics==1: phf.livePlots(pwp_out, n) return pwp_out
def zmld_boyer(s, t, p): """ Computes mixed layer depth, based on de Boyer Montégut et al., 2004. Parameters ---------- s : array_like salinity [psu (PSS-78)] t : array_like temperature [℃ (ITS-90)] p : array_like pressure [db]. Notes ----- Based on density with fixed threshold criteria de Boyer Montégut et al., 2004. Mixed layer depth over the global ocean: An examination of profile data and a profile-based climatology. doi:10.1029/2004JC002378 dataset for test and more explanation can be found at: http://www.ifremer.fr/cerweb/deboyer/mld/Surface_Mixed_Layer_Depth.php Codes based on : http://mixedlayer.ucsd.edu/ """ m = len(s) # starti = min(find((pres-10).^2==min((pres-10).^2))); starti = np.min(np.where(((p - 10.)**2 == np.min((p - 10.)**2)))[0]) pres = p[starti:m] sal = s[starti:m] temp = t[starti:m] starti = 0 m = len(sal) pden = sw.dens0(sal, temp) - 1000 mldepthdens_mldindex = m for i, pp in enumerate(pden): if np.abs(pden[starti] - pp) > .03: mldepthdens_mldindex = i break # Interpolate to exactly match the potential density threshold presseg = [pres[mldepthdens_mldindex - 1], pres[mldepthdens_mldindex]] pdenseg = [ pden[starti] - pden[mldepthdens_mldindex - 1], pden[starti] - pden[mldepthdens_mldindex] ] P = np.polyfit(presseg, pdenseg, 1) presinterp = np.linspace(presseg[0], presseg[1], 3) pdenthreshold = np.polyval(P, presinterp) # The potential density threshold MLD value: ix = np.max(np.where(np.abs(pdenthreshold) < 0.03)[0]) mldepthdens_mldindex = presinterp[ix] # Search for the first level that exceeds the temperature threshold mldepthptmp_mldindex = m for i, tt in enumerate(temp): if np.abs(temp[starti] - tt) > 0.2: mldepthptmp_mldindex = i break # Interpolate to exactly match the temperature threshold presseg = [pres[mldepthptmp_mldindex - 1], pres[mldepthptmp_mldindex]] tempseg = [ temp[starti] - temp[mldepthptmp_mldindex - 1], temp[starti] - temp[mldepthptmp_mldindex] ] P = np.polyfit(presseg, tempseg, 1) presinterp = np.linspace(presseg[0], presseg[1], 3) tempthreshold = np.polyval(P, presinterp) # The temperature threshold MLD value: ix = np.max(np.where(np.abs(tempthreshold) < 0.2)[0]) mldepthptemp_mldindex = presinterp[ix] return mldepthdens_mldindex, mldepthptemp_mldindex
def prep_data(met_dset, prof_dset, params): """ This function prepares the forcing and profile data for the model run. Below, the surface forcing and profile data are interpolated to the user defined time steps and vertical resolutions, respectively. Secondary quantities are also computed and packaged into dictionaries. The code also checks that the time and vertical increments meet the necessary stability requirements. Lastly, this function initializes the numpy arrays to collect the model's output. INPUT: met_data: dictionary-like object with forcing data. Fields should include: ['time', 'sw', 'lw', 'qlat', 'qsens', 'tx', 'ty', 'precip']. These fields should store 1-D time series of the respective quantities. prof_data: dictionary-like object with initial profile data. Fields should include: ['z', 't', 's', 'd']. These represent 1-D vertical profiles of temperature, salinity and density. params: dictionary-like object with fields defined by set_params function OUTPUT: forcing: dictionary with interpolated surface forcing data. pwp_out: dictionary with initialized variables to collect model output. """ #create new time vector with time step dt_d time_vec = np.arange(met_dset['time'][0], met_dset['time'][-1]+params['dt_d'], params['dt_d']) tlen = len(time_vec) #interpolate surface forcing data to new time vector from scipy.interpolate import interp1d forcing = {} for vname in met_dset: p_intp = interp1d(met_dset['time'], met_dset[vname], axis=0) forcing[vname] = p_intp(time_vec) #interpolate E-P to dt resolution (not sure why this has to be done separately) evap_intp = interp1d(met_dset['time'], met_dset['qlat'], axis=0, kind='nearest', bounds_error=False) evap = (0.03456/(86400*1000))*evap_intp(np.floor(time_vec)) #(meters per second?) emp = evap-forcing['precip'] emp[np.isnan(emp)] = 0. forcing['emp'] = emp #define q_in and q_out forcing['q_in'] = forcing['sw'] #heat flux into ocean forcing['q_out'] = forcing['lw'] + forcing['qlat'] + forcing['qsens'] #add time_vec to forcing forcing['time'] = time_vec #define depth coordinate, but first check to see if profile max depth #is greater than user defined max depth zmax = max(prof_dset.z) if zmax < params['max_depth']: depth = zmax print 'Profile input shorter than depth selected, truncating to %sm' %depth #define new z-coordinates init_prof = {} init_prof['z'] = np.arange(0, params['max_depth']+params['dz'], params['dz']) zlen = len(init_prof['z']) #compute absorption and incoming radiation (function defined in PWP_model.py) absrb = pwp.absorb(params['beta1'], params['beta2'], zlen, params['dz']) #(units unclear) dstab = params['dt']*params['rkz']/params['dz']**2 #courant number if dstab > 0.5: print "WARNING: unstable CFL condition for diffusion! dt*rkz/dz**2 > 0.5." print "To fix this, try to reduce the time step or increase the depth increment." inpt = input("Proceed with simulation? Enter 'y'or 'n'. ") if inpt is 'n': raise ValueError("Please restart PWP.m with a larger dz and/or smaller dt. Exiting...") forcing['absrb'] = absrb params['dstab'] = dstab #check depth resolution of profile data prof_incr = np.diff(prof_dset['z']).mean() if params['dz'] < prof_incr/5.: inpt = input("Depth increment, dz, is much smaller than profile resolution. Is this okay? (Enter 'y'or 'n')") if inpt is 'n': raise ValueError("Please restart PWP.m with a new dz >= %s Exiting..." %prof_incr/5.) #debug_here() #interpolate profile data to new z-coordinate from scipy.interpolate import InterpolatedUnivariateSpline for vname in prof_dset: #first strip nans not_nan = np.logical_not(np.isnan(prof_dset[vname])) indices = np.arange(len(prof_dset[vname])) #p_intp = interp1d(prof_dset['z'], prof_dset[vname], axis=0, kind='linear', bounds_error=False) #interp1d doesn't work here because it doesn't extrapolate. Can't have Nans in interpolated profile p_intp = InterpolatedUnivariateSpline(prof_dset['z'][not_nan], prof_dset[vname][not_nan], k=1) init_prof[vname] = p_intp(init_prof['z']) #get profile variables temp0 = init_prof['t'] #initial profile temperature sal0 = init_prof['s'] #intial profile salinity dens0 = sw.dens0(sal0, temp0) #intial profile density #initialize variables for output pwp_out = {} pwp_out['time'] = time_vec pwp_out['dt'] = params['dt'] pwp_out['dz'] = params['dz'] pwp_out['lat'] = params['lat'] pwp_out['z'] = init_prof['z'] tlen = int(np.floor(tlen/params['dt_save'])) arr_sz = (zlen, tlen) pwp_out['temp'] = np.zeros(arr_sz) pwp_out['sal'] = np.zeros(arr_sz) pwp_out['dens'] = np.zeros(arr_sz) pwp_out['uvel'] = np.zeros(arr_sz) pwp_out['vvel'] = np.zeros(arr_sz) pwp_out['mld'] = np.zeros((tlen,)) #use temp, sal and dens profile data for the first time step pwp_out['sal'][:,0] = sal0 pwp_out['temp'][:,0] = temp0 pwp_out['dens'][:,0] = dens0 return forcing, pwp_out, params