コード例 #1
0
ファイル: TIL.py プロジェクト: LisaNeef/DART-state-space
def Nsq_forcing_from_RC(E,datetime_in=None,debug=False,hostname='taurus'):

	"""
	Birner (2010) used the thermodynamic equation in the TEM form to derive an expression 
	for the rate of change of static stability (N2) due to residual motion and diabatic heating. 

	This subroutine compares those terms from the dynamical heating rates computed by Wuke Wang. 
	The vertical motion (wstar) term is -d(wsar*Nsq)/dz.  
	Wuke already computed WS = -wstar*HNsq/R, so it's easiest to load that data, divide out H and R, and then take the vertical gradient. 

	The horizontal term is -g d(vstar/theta * d(theta)dy)/dz. 
	Wuke already computed the heating rate term v*dtheta/dy = v*dTdy, 
	so the easiest thing to do is to multiply the heating rates by g/theta
	and then take the vertical gradient. 

	INPUTS:
	E: a DART experiment dictionary. Relevant fields are:
		E['exp_name'] - the experiment name
		E['daterange'] - helps to choose which date to load in case this isn't specifically given
		E['variable'] - if this is set to N2_forcing_vstar, the code returns the N2 forcing due to 
			meridional residual circulation. For anything else, it returns the forcing 
			due to vertical residual circulation. 
	datetime_in: the date for which we want to compute this diagnostic. 
		default is None -- in this case, just choose the fist date in E['daterange']


	OUTPUTS:
	N2_forcing: Nsquared forcing term  in s^2/day
	lev
	lat 
	"""

	# necessary constants  
	H=7000.0	# scale height in m  
	g = 9.80
	p0=1000.0	# reference pressure in hPa  

	if datetime_in is None:
		datetime_in = E['daterange'][0]

	# depending on which term we want, need to load the residual circulation component and some other stuff, 
	# and then derive a quantity for which we take the vertical gradient 
	ERC = E.copy()
	ET=E.copy()
	if E['variable'] == 'Nsq_vstar_forcing':
		ET['variable']='theta'
		lev,lat,lon,theta,P0,hybm,hyam = dart.load_DART_diagnostic_file(ET,datetime_in,hostname=hostname,debug=debug)
		ERC['variable']='VSTAR'
		vstar,lat,lev = DSS.compute_DART_diagn_from_Wang_TEM_files(ERC,datetime_in,hostname=hostname,debug=debug)

		# the above routines do not return arrays of consistent shape, so have to do 
		# some acrobatics to get everything to match up. 

		# find how the dimensions fit to the shape 
		nlon=len(lon)
		nlat=len(lat)
		nlev=len(lev)
		for idim,s in enumerate(theta.shape):
			if s==nlon:
				londim=idim
				latdim=idim
				levdim=idim
			
		# take the zonal mean of potential temp  - this should make its shape copy x lat x lev
		thetam = np.average(theta,axis=londim)

		# next step is to find the meridional gradient of theta 
		# latitude steps --> convert to distance (arclength)
		rlat = np.deg2rad(lat)
		Re = 6371000.0		# radius of Earth in m 
		y = Re*rlat
		dy = np.gradient(y)
		# need to replicate dy to suit the shape of zonal mean theta 
		dym = dy[None,:,None]
		dy3 = np.broadcast_to(dym,thetam.shape)
		# here is the gradient - need to squeeze out a possible length-1 
		# copy dimension 
		dthetady_list = np.gradient(np.squeeze(thetam),np.squeeze(dy3))

		# now find which dimension of _squeezed_ thetam corresponds to latitude - 
		# that's the gradient that we want
		# (is this a pain in the ass? Yes! But I haven't yet found a more clever approach) 
		for idim,s in enumerate(np.squeeze(thetam).shape):
			if s==nlat:
				newlatdim=idim
		dthetady = dthetady_list[newlatdim]

		# the meridional gradient of zonal mean theta then gets multiplied by vstar and g/theta. But...  
		
		# the subroutine compute_DART_diagn_from_Wang_TEM_files delivers an array with 
		# dimensions lev x lat x copy (or just levxlat)
		# whereas N2 should come out as copy x lat x lev (or simply lat x lev)
		# need to transpose this, but I don't trust np.reshape - do it manually 
		vstar2 = np.zeros(shape=dthetady.shape)
		if vstar2.ndim==3:
			for icopy in range(dthetady.shape[0]):
				for ilat in range(dthetady.shape[1]):
					for ilev in range(dthetady.shape[2]):
						vstar2[icopy,ilat,ilev]=vstar[icopy,ilev,ilat]
		else:
			for ilat in range(dthetady.shape[0]):
				for ilev in range(dthetady.shape[1]):
					vstar2[ilat,ilev]=vstar[ilev,ilat]

		X = (g/np.squeeze(thetam))*vstar2*dthetady

	else:
		
		ET['variable']='Nsq'
		lev,lat,lon,Nsq,P0,hybm,hyam = dart.load_DART_diagnostic_file(ET,datetime_in,hostname=hostname,debug=debug)
		ERC['variable']='WSTAR'
		wstar,lat,lev = DSS.compute_DART_diagn_from_Wang_TEM_files(ERC,datetime_in,hostname=hostname,debug=debug)

		# find how the dimensions fit to the shape 
		nlon=len(lon)
		nlat=len(lat)
		nlev=len(lev)
		for idim,s in enumerate(Nsq.shape):
			if s==nlon:
				londim=idim
				latdim=idim
				levdim=idim
        
		# take the zonal mean of buoyancy frequency 
		Nsqm = np.average(Nsq,axis=londim)
		
		# might have to squeeze out a length-1 copy dimension 
		Nsqm2 = np.squeeze(Nsqm)

		# the subroutine compute_DART_diagn_from_Wang_TEM_files delivers an array with dimensions lev x lat x copy (or just levxlat)
		# whereas N2 should come out as copy x lat x lev (or simply lat x lev)
		# need to transpose this, but I don't trust np.reshape - do it manually 
		wstar2 = np.zeros(shape=Nsqm2.shape)
		if wstar2.ndim==3:
			for icopy in range(Nsqm2.shape[0]):
				for ilat in range(Nsqm2.shape[1]):
					for ilev in range(Nsqm2.shape[2]):
						wstar2[icopy,ilat,ilev]=wstar[icopy,ilev,ilat]
		else:
			for ilat in range(Nsqm2.shape[0]):
				for ilev in range(Nsqm2.shape[1]):
					wstar2[ilat,ilev]=wstar[ilev,ilat]

		X = Nsqm2*wstar2

	# convert pressure levels to approximate altitude and take the vertical gradient  
	zlev = H*np.log(p0/lev)
	dZ = np.gradient(zlev)   # gradient of vertical levels in m

	# now X *should* have shape (copy x lat x lev) OR (lat x lev)
	# so need to copy dZ to look like this 
	if X.ndim==3:
		dZm = dZ[None,None,:]
		levdim=2
	if X.ndim==2:
		dZm = dZ[None,:]
		levdim=1
	dZ3 = np.broadcast_to(dZm,X.shape)
	dXdZ_3D = np.gradient(X,dZ3)
	dxdz = dXdZ_3D[levdim] # this is the vertical gradient with respect to height 

	# the above calculation yields a quantity in units s^-2/s, but it makes more sense 
	# in the grand scheme of things to look at buoyancy forcing per day, so here 
	# is a conversion factor.
	seconds_per_day = 60.*60.*24.0

	N2_forcing = -dxdz*seconds_per_day

	return N2_forcing,lat,lev
コード例 #2
0
ファイル: TIL.py プロジェクト: timhoar/DARTpy
def Nsq_forcing_from_RC(E, datetime_in=None, debug=False, hostname='taurus'):
    """
	Birner (2010) used the thermodynamic equation in the TEM form to derive an expression 
	for the rate of change of static stability (N2) due to residual motion and diabatic heating. 

	This subroutine compares those terms from the dynamical heating rates computed by Wuke Wang. 
	The vertical motion (wstar) term is -d(wsar*Nsq)/dz.  
	Wuke already computed WS = -wstar*HNsq/R, so it's easiest to load that data, divide out H and R, and then take the vertical gradient. 

	The horizontal term is -g d(vstar/theta * d(theta)dy)/dz. 
	Wuke already computed the heating rate term v*dtheta/dy = v*dTdy, 
	so the easiest thing to do is to multiply the heating rates by g/theta
	and then take the vertical gradient. 

	INPUTS:
	E: a DART experiment dictionary. Relevant fields are:
		E['exp_name'] - the experiment name
		E['daterange'] - helps to choose which date to load in case this isn't specifically given
		E['variable'] - if this is set to N2_forcing_vstar, the code returns the N2 forcing due to 
			meridional residual circulation. For anything else, it returns the forcing 
			due to vertical residual circulation. 
	datetime_in: the date for which we want to compute this diagnostic. 
		default is None -- in this case, just choose the fist date in E['daterange']


	OUTPUTS:
	N2_forcing: Nsquared forcing term  in s^2/day
	lev
	lat 
	"""

    # necessary constants
    H = 7000.0  # scale height in m
    g = 9.80
    p0 = 1000.0  # reference pressure in hPa

    if datetime_in is None:
        datetime_in = E['daterange'][0]

    # depending on which term we want, need to load the residual circulation component and some other stuff,
    # and then derive a quantity for which we take the vertical gradient
    ERC = E.copy()
    ET = E.copy()
    if E['variable'] == 'Nsq_vstar_forcing':
        ET['variable'] = 'theta'
        Dtheta = dart.load_DART_diagnostic_file(ET,
                                                datetime_in,
                                                hostname=hostname,
                                                debug=debug)
        theta = Dtheta['data']
        lat = Dtheta['lat']
        lon = Dtheta['lon']
        lev = Dtheta['lev']
        ERC['variable'] = 'VSTAR'
        Dvstar = DSS.compute_DART_diagn_from_Wang_TEM_files(ERC,
                                                            datetime_in,
                                                            hostname=hostname,
                                                            debug=debug)
        vstar = Dvstar['data']

        # the above routines do not return arrays of consistent shape, so have to do
        # some acrobatics to get everything to match up.

        # find how the dimensions fit to the shape
        nlon = len(lon)
        nlat = len(lat)
        nlev = len(lev)
        for idim, s in enumerate(theta.shape):
            if s == nlon:
                londim = idim
                latdim = idim
                levdim = idim

        # take the zonal mean of potential temp  - this should make its shape copy x lat x lev
        thetam = np.average(theta, axis=londim)

        # next step is to find the meridional gradient of theta
        # latitude steps --> convert to distance (arclength)
        rlat = np.deg2rad(lat)
        Re = 6371000.0  # radius of Earth in m
        y = Re * rlat
        dy = np.gradient(y)
        # need to replicate dy to suit the shape of zonal mean theta
        dym = dy[None, :, None]
        dy3 = np.broadcast_to(dym, thetam.shape)
        # here is the gradient - need to squeeze out a possible length-1
        # copy dimension
        dthetady_list = np.gradient(np.squeeze(thetam), np.squeeze(dy3))

        # now find which dimension of _squeezed_ thetam corresponds to latitude -
        # that's the gradient that we want
        # (is this a pain in the ass? Yes! But I haven't yet found a more clever approach)
        for idim, s in enumerate(np.squeeze(thetam).shape):
            if s == nlat:
                newlatdim = idim
        dthetady = dthetady_list[newlatdim]

        # the meridional gradient of zonal mean theta then gets multiplied by vstar and g/theta. But...

        # the subroutine compute_DART_diagn_from_Wang_TEM_files delivers an array with
        # dimensions lev x lat x copy (or just levxlat)
        # whereas N2 should come out as copy x lat x lev (or simply lat x lev)
        # need to transpose this, but I don't trust np.reshape - do it manually
        vstar2 = np.zeros(shape=dthetady.shape)
        if vstar2.ndim == 3:
            for icopy in range(dthetady.shape[0]):
                for ilat in range(dthetady.shape[1]):
                    for ilev in range(dthetady.shape[2]):
                        vstar2[icopy, ilat, ilev] = vstar[icopy, ilev, ilat]
        else:
            for ilat in range(dthetady.shape[0]):
                for ilev in range(dthetady.shape[1]):
                    vstar2[ilat, ilev] = vstar[ilev, ilat]

        X = (g / np.squeeze(thetam)) * vstar2 * dthetady

    else:

        ET['variable'] = 'Nsq'
        D = dart.load_DART_diagnostic_file(ET,
                                           datetime_in,
                                           hostname=hostname,
                                           debug=debug)
        Nsq = D['data']
        lat = D['lat']
        lon = D['lon']
        lev = D['lev']

        ERC['variable'] = 'WSTAR'
        Dwstar = DSS.compute_DART_diagn_from_Wang_TEM_files(ERC,
                                                            datetime_in,
                                                            hostname=hostname,
                                                            debug=debug)
        wstar = Dwstar['data']

        # find how the dimensions fit to the shape
        nlon = len(lon)
        nlat = len(lat)
        nlev = len(lev)
        for idim, s in enumerate(Nsq.shape):
            if s == nlon:
                londim = idim
                latdim = idim
                levdim = idim

        # take the zonal mean of buoyancy frequency
        Nsqm = np.average(Nsq, axis=londim)

        # might have to squeeze out a length-1 copy dimension
        Nsqm2 = np.squeeze(Nsqm)

        # the subroutine compute_DART_diagn_from_Wang_TEM_files delivers an array with dimensions lev x lat x copy (or just levxlat)
        # whereas N2 should come out as copy x lat x lev (or simply lat x lev)
        # need to transpose this, but I don't trust np.reshape - do it manually
        wstar2 = np.zeros(shape=Nsqm2.shape)
        if wstar2.ndim == 3:
            for icopy in range(Nsqm2.shape[0]):
                for ilat in range(Nsqm2.shape[1]):
                    for ilev in range(Nsqm2.shape[2]):
                        wstar2[icopy, ilat, ilev] = wstar[icopy, ilev, ilat]
        else:
            for ilat in range(Nsqm2.shape[0]):
                for ilev in range(Nsqm2.shape[1]):
                    wstar2[ilat, ilev] = wstar[ilev, ilat]

        X = Nsqm2 * wstar2

    # convert pressure levels to approximate altitude and take the vertical gradient
    zlev = H * np.log(p0 / lev)
    dZ = np.gradient(zlev)  # gradient of vertical levels in m

    # now X *should* have shape (copy x lat x lev) OR (lat x lev)
    # so need to copy dZ to look like this
    if X.ndim == 3:
        dZm = dZ[None, None, :]
        levdim = 2
    if X.ndim == 2:
        dZm = dZ[None, :]
        levdim = 1
    dZ3 = np.broadcast_to(dZm, X.shape)
    dXdZ_3D = np.gradient(X, dZ3)
    dxdz = dXdZ_3D[
        levdim]  # this is the vertical gradient with respect to height

    # the above calculation yields a quantity in units s^-2/s, but it makes more sense
    # in the grand scheme of things to look at buoyancy forcing per day, so here
    # is a conversion factor.
    seconds_per_day = 60. * 60. * 24.0

    N2_forcing = -dxdz * seconds_per_day

    D = dict()
    D['data'] = N2_forcing
    D['lat'] = lat
    D['lev'] = lev
    D['units'] = 's^{-2}/day'
    D['long_name'] = 'N^{2} Forcing'

    return D