Source code for climlab.convection.convadj

from __future__ import division
from builtins import range
import numpy as np
from climlab import constants as const
from climlab.utils.thermo import rho_moist, pseudoadiabat
from climlab.process.time_dependent_process import TimeDependentProcess
from climlab.domain.field import Field
from .akmaev_adjustment import convective_adjustment_direct


[docs] class ConvectiveAdjustment(TimeDependentProcess): '''Hard Convective Adjustment to a prescribed lapse rate. This process computes the instantaneous adjustment to conservatively remove any instabilities in each column. Instability is defined as a temperature decrease with height that exceeds the prescribed critical lapse rate. This critical rate is set by input argument ``adj_lapse_rate``, which can be either a numerical or string value. Numerical values for ``adj_lapse_rate`` are given in units of K / km. Both array and scalar values are valid. For scalar values, the assumption is that the critical lapse rate is the same at every level. If an array is given, it is assumed to represent the in-situ critical lapse rate (in K/km) at every grid point. Alternatively, string arguments can be given as follows: - ``'DALR'`` or ``'dry adiabat'``: critical lapse rate is set to g/cp = 9.8 K / km - ``'MALR'`` or ``'moist adiabat'`` or ``'pseudoadiabat'``: critical lapse rate follows the in-situ moist pseudoadiabat at every level Adjustment includes the surface if ``'Ts'`` is included in the ``state`` dictionary. This implicitly accounts for turbulent surface fluxes. Otherwise only the atmospheric temperature is adjusted. If ``adj_lapse_rate`` is an array, its size must match the number of vertical levels of the adjustment. This is number of pressure levels if the surface is not adjusted, or number of pressure levels + 1 if the surface is adjusted. This process implements the conservative adjustment algorithm described in Akmaev (1991) Monthly Weather Review. ''' def __init__(self, adj_lapse_rate=None, **kwargs): super(ConvectiveAdjustment, self).__init__(**kwargs) # lapse rate for convective adjustment, in K / km self.adj_lapse_rate = adj_lapse_rate self.param['adj_lapse_rate'] = adj_lapse_rate self.time_type = 'adjustment' self.adjustment = {} @property def pcol(self): patm = self.lev if 'Ts' in self.state: # surface pressure should correspond to model domain! ps = self.lev_bounds[-1] return np.append(patm, ps) else: return patm @property def ccol(self): c_atm = self.Tatm.domain.heat_capacity if 'Ts' in self.state: c_sfc = self.Ts.domain.heat_capacity return np.append(c_atm, c_sfc) else: return c_atm @property def Tcol(self): # For now, let's assume that the vertical axis is the last axis Tatm = self.Tatm if 'Ts' in self.state: Ts = np.atleast_1d(self.Ts) return np.concatenate((Tatm, Ts),axis=-1) else: return Tatm @property def adj_lapse_rate(self): lapserate = self._adj_lapse_rate if type(lapserate) is str: if lapserate in ['DALR', 'dry adiabat']: return const.g / const.cp * 1.E3 elif lapserate in ['MALR', 'moist adiabat', 'pseudoadiabat']: # critical lapse rate at each level is set by pseudoadiabat dTdp = pseudoadiabat(self.Tcol,self.pcol) / 100. # K / Pa # Could include water vapor effect on density here ... # Replace Tcol with virtual temperature rho = self.pcol*100./const.Rd/self.Tcol # in kg/m**3 return dTdp * const.g * rho * 1000. # K / km else: raise ValueError('adj_lapse_rate must be either numeric or any of \'DALR\', \'dry adiabat\', \'MALR\', \'moist adiabat\', \'pseudoadiabat\'.') else: return lapserate @adj_lapse_rate.setter def adj_lapse_rate(self, lapserate): self._adj_lapse_rate = lapserate self.param['adj_lapse_rate'] = lapserate def _compute(self): if self.adj_lapse_rate is None: self.adjustment['Ts'] = self.Ts * 0. self.adjustment['Tatm'] = self.Tatm * 0. else: # convective adjustment routine expect reversered vertical axis pflip = self.pcol[..., ::-1] Tflip = self.Tcol[..., ::-1] cflip = self.ccol[..., ::-1] lapseflip = np.atleast_1d(self.adj_lapse_rate)[..., ::-1] Tadj_flip = convective_adjustment_direct(pflip, Tflip, cflip, lapserate=lapseflip) Tadj = Tadj_flip[..., ::-1] if 'Ts' in self.state: Ts = Field(Tadj[...,-1], domain=self.Ts.domain) Tatm = Field(Tadj[...,:-1], domain=self.Tatm.domain) self.adjustment['Ts'] = Ts - self.Ts else: Tatm = Field(Tadj, domain=self.Tatm.domain) self.adjustment['Tatm'] = Tatm - self.Tatm # return the adjustment, independent of timestep # because the parent process might have set a different timestep! return self.adjustment