Beispiel #1
0
def Nsq_forcing_from_Q(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 the term due to diabatic heating, i.e.: 
	g d(Q/theta)dz

	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'] - this determines what kind of diabatic heating we use:
			the value of E['variable'] should be a string like 'Nsq_forcing_XXXXX'
			where XXXXX is the model variable corresponding to whatever diabatic 
			heating type we are looking for. 
			For example, in WACCM, 'QRL_TOT' is the total longwave heating, so to get the 
			N2 forcing from that, just set E['variable']='Nsq_forcing_QRL_TOT'
	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  
	p0=1000.0	# reference pressure in hPa  
	g=9.8		# acceleration of gravity 

	# load the desired diabatic heating term
	# this is not typically part of the DART output, so load from model history files
	# (right now this really only works for WACCM/CAM)  
	Qstring = E['variable'].strip('Nsq_forcing_')
	EQ = E.copy()
	EQ['variable']=Qstring
	Q2,lat,lon,lev = DSS.compute_DART_diagn_from_model_h_files(EQ,datetime_in,verbose=debug)
	# remove the time dimension, which should have length 1 
	Q = np.squeeze(Q2)

	# also load potential temperature 
	ET = E.copy()
	ET['variable']='theta'
	lev,lat,lon,theta2,P0,hybm,hyam = dart.load_DART_diagnostic_file(ET,datetime_in,hostname=hostname,debug=debug)
	# squeeze out extra dims, which we get if we load single copies (e.g. ensemble mean)
	theta = np.squeeze(theta2)

	# now find the longitude dimension and average over it  
	# for both Q and theta  
	nlon=len(lon)
	Mean_arrays = []
	for A in [Q,theta]:
		for idim,s in enumerate(A.shape):
			if s == nlon:
				londim=idim
		Mean_arrays.append(np.average(A,axis=londim))
	Q_mean=Mean_arrays[0]
	theta_mean=Mean_arrays[1]

	# if the shapes don't match up, might have to transpose one of them
#	if Mean_arrays[1].shape[0] != Q_mean.shape[0]:
#		theta_mean=np.transpose(Mean_arrays[1])
#	else:
#		theta_mean=Mean_arrays[1]
	
	# Q_mean should come out as copy x lev x lat, whereas theta_mean is copy x lat x lev  
	# to manually transpose Q_mean
	Q_mean2 = np.zeros(shape=theta_mean.shape)
	if Q_mean2.ndim==3:
		for icopy in range(theta_mean.shape[0]):
			for ilat in range(theta_mean.shape[1]):
				for ilev in range(theta_mean.shape[2]):
					Q_mean2[icopy,ilat,ilev]=Q_mean[icopy,ilev,ilat]
	else:
		for ilat in range(theta_mean.shape[0]):
			for ilev in range(theta_mean.shape[1]):
				Q_mean2[ilat,ilev]=Q_mean[ilev,ilat]
		
	# divide Q by theta
	X = Q_mean2/theta_mean

	# 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

	# now loop over ensemble members and compute the n2 forcing for each one
	N2_forcing = g*dxdz*seconds_per_day

	return N2_forcing,lat,lev
Beispiel #2
0
def ano(E,climatology_option = 'NODA',hostname='taurus',verbose=False):

	"""
	Compute anomaly fields relative to some climatology

	Inputs allowed for climatology_option:  
	'NODA': take the ensemble mean of the corresponding no-DA experiment as a 40-year climatology  
	'F_W4_L66': daily climatology of a CESM+WACCM simulation with realistic forcings, 1951-2010
	None: don't subtract out anything -- just return the regular fields in the same shape as other "anomalies"  
	"""

	# load climatology 
	Xclim,lat,lon,lev,DR = load_climatology(E,climatology_option,hostname)

	# change the daterange in the anomalies to suit what was found for climatology  
	if len(DR) != len(E['daterange']):
		print('Changing the experiment daterange to the dates found for the requested climatology')
		E['daterange'] = DR
		d1 = DR[0].strftime("%Y-%m-%d")
		d2 = DR[len(E['daterange'])-1].strftime("%Y-%m-%d")
		print('new daterange goes from '+d1+' to '+d2)

	# some climatologies are only available at daily resolution, so 
	# in that case we have to change the daterange in E to be daily  
	if (climatology_option == 'F_W4_L66'):
		d0 = E['daterange'][0]
		df = E['daterange'][len(E['daterange'])-1]
		days = df-d0
		DRnew =  dart.daterange(date_start=d0, periods=days.days+1, DT='1D')
		E['daterange'] = DRnew

	# load the desired model fields for the experiment
	Xlist = []	# empty list to hold the fields we retrieve for every day  
	for date in E['daterange']:
		X,lat0,lon0,lev0 = DSS.compute_DART_diagn_from_model_h_files(E,date,hostname=hostname,verbose=verbose)
		if X is not None:
			Xs = np.squeeze(X)
			Xlist.append(Xs)
			lat = lat0
			lon = lon0
			lev = lev0

	# check that the right vertical levels were loaded
	if verbose:
		print('------computing daily anomalies for the following vertical levels and variable:-------')
		print(lev)
		print(E['variable'])

	# compute anomalies:
	# for this we turn the model fields into a matrix and subtract from the climatology
	XX = np.concatenate([X[..., np.newaxis] for X in Xlist], axis=len(Xs.shape))
	if climatology_option == None:
		AA = XX
	else:
		# if the climatology does not have shape lat x lon x lev x time, 
		# run swapaxes 2x to get it as such  
		# NOTE: this is still a kludge and probably wont work with all datasets - check this carefully 
		# with your own data 
		XclimS = np.squeeze(Xclim)
		nT = len(DRnew)
		lastdim = len(XclimS.shape)-1
		for s,ii in zip(XclimS.shape,range(len(XclimS.shape))):
			if s == nT:
				time_dim = ii

		# if only retrieveing a single date, don't need to do any reshaping
		# but might need to squeeze out a length-one time dimension
		if nT == 1:
			XclimR = XclimS
			XX = np.squeeze(XX)
		else:
			# if time is the last dimension, don't need to reshape Xclim 
			if time_dim == lastdim: 
				XclimR = XclimS
			# if time is the first dimension, need to reshape Xclim
			if time_dim == 0:	
				Xclim2 = XclimS.swapaxes(0,lastdim)
				XclimR = Xclim2.swapaxes(0,1)


		AA = XX-XclimR

	return AA,XclimR,lat,lon,lev,DR
Beispiel #3
0
def Nsq_forcing_from_Q(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 the term due to diabatic heating, i.e.: 
	g d(Q/theta)dz

	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'] - this determines what kind of diabatic heating we use:
			the value of E['variable'] should be a string like 'Nsq_forcing_XXXXX'
			where XXXXX is the model variable corresponding to whatever diabatic 
			heating type we are looking for. 
			For example, in WACCM, 'QRL_TOT' is the total longwave heating, so to get the 
			N2 forcing from that, just set E['variable']='Nsq_forcing_QRL_TOT'
	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
    p0 = 1000.0  # reference pressure in hPa
    g = 9.8  # acceleration of gravity

    # load the desired diabatic heating term
    # this is not typically part of the DART output, so load from model history files
    # (right now this really only works for WACCM/CAM)
    Qstring = E['variable'].strip('Nsq_forcing_')
    EQ = E.copy()
    EQ['variable'] = Qstring
    DQ = DSS.compute_DART_diagn_from_model_h_files(EQ,
                                                   datetime_in,
                                                   verbose=debug)
    # remove the time dimension, which should have length 1
    DQ['data'] = np.squeeze(DQ['data'])

    # also load potential temperature
    ET = E.copy()
    ET['variable'] = 'theta'
    Dtheta = dart.load_DART_diagnostic_file(ET,
                                            datetime_in,
                                            hostname=hostname,
                                            debug=debug)
    # squeeze out extra dims, which we get if we load single copies (e.g. ensemble mean)
    Dtheta['data'] = np.squeeze(Dtheta['data'])

    # now find the longitude dimension and average over it
    # for both Q and theta
    Q_mean = DSS.average_over_named_dimension(DQ['data'], DQ['lon'])
    theta_mean = DSS.average_over_named_dimension(Dtheta['data'],
                                                  Dtheta['lon'])

    # if the shapes don't match up, might have to transpose one of them
    #	if Mean_arrays[1].shape[0] != Q_mean.shape[0]:
    #		theta_mean=np.transpose(Mean_arrays[1])
    #	else:
    #		theta_mean=Mean_arrays[1]

    # Q_mean should come out as copy x lev x lat, whereas theta_mean is copy x lat x lev
    # to manually transpose Q_mean
    Q_mean2 = np.zeros(shape=theta_mean.shape)
    if Q_mean2.ndim == 3:
        for icopy in range(theta_mean.shape[0]):
            for ilat in range(theta_mean.shape[1]):
                for ilev in range(theta_mean.shape[2]):
                    Q_mean2[icopy, ilat, ilev] = Q_mean[icopy, ilev, ilat]
    else:
        for ilat in range(theta_mean.shape[0]):
            for ilev in range(theta_mean.shape[1]):
                Q_mean2[ilat, ilev] = Q_mean[ilev, ilat]

    # divide Q by theta
    X = Q_mean2 / theta_mean

    # convert pressure levels to approximate altitude and take the vertical gradient
    lev = DQ['lev']
    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

    # now loop over ensemble members and compute the n2 forcing for each one
    N2_forcing = g * dxdz * seconds_per_day

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

    return D