Source code for climlab.dynamics.meridional_moist_diffusion
r"""Solver for the 1D meridional moist static energy diffusion equation on the sphere:
.. math::
C\frac{\partial}{\partial t} T(\phi,t) = \frac{1}{\cos\phi} \frac{\partial}{\partial \phi} \left[ \cos\phi ~ D ~(1+f(T))~ \frac{\partial T}{\partial \phi} \right]
where :math:`f(T)` is a temperature-dependent moisture amplification factor given by
.. math::
f(T) = \frac{L^2 r q^*(T)}{c_p R_v T^2}
which expresses the effect of latent heat on the near-surface moist static energy,
where :math:`q^*(T)` is the saturation specific humidity at temperature :math:`T`
and :math:`r` is a relative humidity.
This class operates identically to ``MeridionalHeatDiffusion``
but calculates :math:`f`
automatically at each timestep and applies it to the diffusivity.
The magnitude of the moisture amplification is controlled by the input parameter
`relative_humidity` (i.e. :math:`r` in the equation above).
It can be used to implement a modified Energy Balance Model accounting for the
effects of moisture on the heat transport efficiency.
Derivation of the moist diffusion equation
------------------------------------------
Assume that heat transport is down the gradient of **moist static energy**
:math:`m = c_p T + L q + g Z`
For an EBM we want to parameterize everything in terms of a surface temperature :math:`T_s`.
So we write :math:`m_s = c_p T_s + L r q^*(T_s)`,
where :math:`m_s` is the moist static energy of near-surface air parcels,
:math:`r` is a near-surface relative humidity,
and :math:`q^*` is the **saturation specific humidity** at a reference surface pressure.
Now express this quantity in temperature units by defining a *moist temperature*
.. math::
T_m = \frac{m_s}{c_p} = T_s + \frac{L r}{c_p} q^*(T_s)
:math:`T_m` is the temperature a dry air parcel would have
that has the same total enthalpy as a moist air parcel at temperature :math:`T_s`
The down-gradient heat transport parameterization can then be written
.. math::
\mathcal{H} = -2 \pi a^2 D_m \frac{\partial T_m}{\partial \phi}
where :math:`D_m` is the thermal diffusion coefficient for this moist model, in units of W/m2/K.
The equation we are trying to solve is thus
.. math::
C \frac{\partial T_s}{\partial t} = \frac{1}{\cos\phi} \frac{\partial}{\partial \phi} \left( \cos\phi D_m \frac{\partial T_m}{\partial \phi} \right)
which we can write in terms of :math:`T_s` only by substituting in for :math:`T_m`:
.. math::
C \frac{\partial T_s}{\partial t} = \frac{1}{\cos\phi} \frac{\partial}{\partial \phi} \left( \cos\phi D_m \left(\frac{\partial T_s}{\partial \phi} + \frac{\partial}{\partial \phi} \left(\frac{L r}{c_p} q^*(T_s)\right)\right)\right)
If we make the simplifying assumption that the **relative humidity :math:`r` is constant**
(not a function of latitude), then
.. math::
C \frac{\partial T_s}{\partial t} = \frac{1}{\cos\phi} \frac{\partial}{\partial \phi} \left( \cos\phi D_m \left(\frac{\partial T_s}{\partial \phi} + \frac{L r}{c_p} \frac{\partial q^*}{\partial \phi} \right)\right)
To a good approximation (see Hartmann's book and others),
the Clausius-Clapeyron relation for saturation specific humidity gives
.. math::
\frac{\partial q^*}{dT} = \frac{L}{R_v T^2} q^*(T)
Then using a chain rule we have
.. math::
\frac{\partial q^*}{\partial \phi} = \frac{\partial q^*}{\partial T_s} \frac{\partial T_s}{\partial \phi} = \frac{L q^*(T_s)}{R_v T_s^2} \frac{\partial T_s}{\partial \phi}
Plugging this into our model equation we get
.. math::
C \frac{\partial T_s}{\partial t} = \frac{1}{\cos\phi} \frac{\partial}{\partial \phi} \left( \cos\phi D_m \frac{\partial T_s}{\partial \phi} \left(1 + \frac{L^2 r q^*(T_s)}{c_p R_v T_s^2} \right)\right)
This is now in a form that is compatible with our diffusion solver.
Just let
.. math::
D = D_m \left( 1 + f(T_s) \right)
where
.. math::
f(T_s) = \frac{L^2 r q^*(T_s)}{c_p R_v T_s^2}
or, equivalently,
.. math::
f(T_s) = \frac{L r }{c_p} \frac{\partial q^*}{dT}\bigg|_{T_s}
Given a temperature distribution :math:`T_s(\phi)` at any given time,
we can calculate the diffusion coefficient :math:`D(\phi)` from this formula.
This calculation is implemented in the ``MeridionalMoistDiffusion`` class.
"""
from __future__ import division
import numpy as np
from .meridional_heat_diffusion import MeridionalHeatDiffusion
from climlab.utils.thermo import qsat
from climlab import constants as const
[docs]
class MeridionalMoistDiffusion(MeridionalHeatDiffusion):
def __init__(self, D=0.24, relative_humidity=0.8, **kwargs):
self.relative_humidity = relative_humidity
super(MeridionalMoistDiffusion, self).__init__(D=D, **kwargs)
self._update_diffusivity()
[docs]
def _update_diffusivity(self):
Tinterp = np.interp(self.lat_bounds, self.lat, np.squeeze(self.Ts))
Tkelvin = Tinterp + const.tempCtoK
f = moist_amplification_factor(Tkelvin, self.relative_humidity)
heat_capacity = self.Ts.domain.heat_capacity
self.K = self.D / heat_capacity * const.a**2 * (1+f)
[docs]
def _implicit_solver(self):
self._update_diffusivity()
# and then do all the same stuff the parent class would do...
return super(MeridionalMoistDiffusion, self)._implicit_solver()
[docs]
def moist_amplification_factor(Tkelvin, relative_humidity=0.8):
'''Compute the moisture amplification factor for the moist diffusivity
given relative humidity and reference temperature profile.'''
deltaT = 0.01
# slope of saturation specific humidity at 1000 hPa
dqsdTs = (qsat(Tkelvin+deltaT/2, 1000.) - qsat(Tkelvin-deltaT/2, 1000.)) / deltaT
return const.Lhvap / const.cp * relative_humidity * dqsdTs