Source code for climlab.radiation.transmissivity

from __future__ import division
from builtins import object
import numpy as np

'''
Testing multi-dimensional column radiation

:Example:

    .. code-block:: python

        import numpy as np
        import climlab
        sfc, atm = climlab.domain.zonal_mean_column()
        absorb = np.ones(atm.shape)
        trans = climlab.radiation.transmissivity.Transmissivity(absorptivity=absorb,axis=1)
        fromspace = np.zeros(sfc.shape)
        emission = 200*np.ones(atm.shape)
        A = trans.flux_down(fluxDownTop=fromspace, emission=emission)
        A.shape

'''

[docs] class Transmissivity(object): '''Class for calculating and store transmissivity between levels, and computing radiative fluxes between levels. Input: numpy array of absorptivities. It is assumed that the last dimension is vertical levels. Attributes: (all stored as numpy arrays): * N: number of levels * absorptivity: level absorptivity (N) * transmissivity: level transmissivity (N) * Tup: transmissivity matrix for upwelling beam (N+1, N+1) * Tdown: transmissivity matrix for downwelling beam (N+1, N+1) Example for N = 3 atmospheric layers: tau is a vector of transmissivities .. math:: \\tau = \\left[ 1, \\tau_0, \\tau_1, \\tau_2 \\right] A is a matrix .. math:: A= \\left[ \\begin{array}{cccc} 1 & 1 & 1 & 1 \\\\ \\tau_0 & 1 & 1 & 1 \\\\ \\tau_1 & \\tau_1 & 1 & 1 \\\\ \\tau_2 & \\tau_2 & \\tau_2 & 1 \\\\ \\end{array} \\right] We then take the cumulative product along columns, and finally take the lower triangle of the result to get .. math:: T_{down} = \\left[ \\begin{array}{cccc} 1 & 0 & 0 & 0 \\\\ \\tau_0 & 1 & 0 & 0 \\\\ \\tau_0 \\tau_1 & \\tau_1 & 1 & 0 \\\\ \\tau_0 \\tau_1 \\tau_2 & \\tau_1 \\tau_2 & \\tau_2 & 1 \\\\ \\end{array} \\right] and Tup = transpose(Tdown) Construct a column emission vector for the downwelling beam: .. math:: E_{down} = \\left[ \\begin{array}{c} \text{flux_from_space} \\\\ E0 \\\\ E1 \\\\ E2 \\\\ \\end{array} \\right] Now we can get the downwelling beam at layer interfaces by matrix multiplication: D = Tdown * Edown For the upwelling beam, we start by adding the reflected part at the surface to the surface emissions: Eup = [emit_sfc + albedo_sfc*D[0], E0, E1, E2] .. math:: Eup = \\left[ \\begin{array}{c} E0 \\\\ E1 \\\\ E2 \\\\ emit_{sfc} + albedo_{sfc} * D[-1] \\end{array} \\right] So that the upwelling flux is U = Tup * Eup The total flux, positive up is thus F = U - D The absorbed radiation at the surface is then -F[-1] The absorbed radiation in the atmosphere is the flux convergence: -diff(F) ''' # quick hack to get some simple cloud albedo def __init__(self, absorptivity, reflectivity=None, axis=0): self.axis = axis if reflectivity is None: reflectivity = np.zeros_like(absorptivity) self.reflectivity = reflectivity self.absorptivity = absorptivity self.transmissivity = 1 - absorptivity - reflectivity self.shape = self.absorptivity.shape N = np.size(self.absorptivity, axis=self.axis) self.N = N # For now, let's assume that the vertical axis is the last axis Tup, Tdown = compute_T_vectorized(self.transmissivity) self.Tup = Tup self.Tdown = Tdown
[docs] def flux_up(self, fluxUpBottom, emission=None): '''Compute downwelling radiative flux at interfaces between layers. Inputs: * fluxDownTop: flux down at top * emission: emission from atmospheric levels (N) defaults to zero if not given Returns: * vector of downwelling radiative flux between levels (N+1) element 0 is the flux down to the surface. ''' if emission is None: emission = np.zeros_like(self.absorptivity) E = np.concatenate((emission, np.atleast_1d(fluxUpBottom)), axis=-1) # dot product (matrix multiplication) along last axes return np.squeeze(np.matmul(self.Tup, E[..., np.newaxis]))
[docs] def flux_reflected_up(self, fluxDown, albedo_sfc=0.): reflectivity = np.concatenate((self.reflectivity, np.atleast_1d(albedo_sfc)), axis=-1) return reflectivity*fluxDown
[docs] def flux_down(self, fluxDownTop, emission=None): '''Compute upwelling radiative flux at interfaces between layers. Inputs: * fluxUpBottom: flux up from bottom * emission: emission from atmospheric levels (N) defaults to zero if not given Returns: * vector of upwelling radiative flux between levels (N+1) element N is the flux up to space. ''' if emission is None: emission = np.zeros_like(self.absorptivity) E = np.concatenate((np.atleast_1d(fluxDownTop),emission), axis=-1) # dot product (matrix multiplication) along last axes return np.squeeze(np.matmul(self.Tdown, E[..., np.newaxis]))
[docs] def compute_T_vectorized(transmissivity): # really vectorized version... to work with arbitrary dimensions of input transmissivity # Assumption is the last dimension of transmissivity is vertical trans_shape = np.shape(transmissivity) N = trans_shape[-1] otherdims = trans_shape[:-1] ones = np.ones(otherdims)[..., np.newaxis] tau = np.concatenate((ones, transmissivity), axis=-1) tiletau = np.tile(tau[..., np.newaxis],N+1) matdims = np.append(np.array(otherdims),[1,1]) # dimensions of matrix should be [otherdims,N+1,N+1] tri = np.tile(np.tri(N+1).transpose(),matdims) A = np.tril(tiletau,k=-1) + tri Tdown = np.tril(np.cumprod(A, axis=-2)) # transpose over last two axes Tup = np.rollaxis(Tdown, -1, -2) return Tup, Tdown