Source code for climlab.dynamics.meridional_heat_diffusion
r"""Solver for the 1D meridional heat 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 ~ \frac{\partial T}{\partial \phi} \right]
for a temperature state variable :math:`T(\phi,t)`,
a vertically-integrated heat capacity :math:`C`,
and arbitrary thermal diffusivity :math:`D(\phi,t)`
in units of W/m2/K.
The diffusivity :math:`D` can be a single scalar,
or optionally a vector *specified at grid cell boundaries*
(so its length must be exactly 1 greater than the length of :math:`\phi`).
:math:`D` can be modified by the user at any time
(e.g., after each timestep, if it depends on other state variables).
The heat capacity :math:`C` is normally handled automatically by CLIMLAB
as part of the grid specification.
A fully implicit timestep is used for computational efficiency. Thus the computed
tendency :math:`\frac{\partial T}{\partial t}` will depend on the timestep.
The diagnostics ``diffusive_flux`` and ``flux_convergence`` are computed
as described in the parent class ``MeridionalDiffusion``.
Two additional diagnostics are computed here,
which are meaningful if :math:`T` represents a *zonally averaged temperature*:
- ``heat_transport`` given by :math:`\mathcal{H}(\phi) = -2 \pi ~ a^2 ~ \cos\phi ~ D ~ \frac{\partial T}{\partial \phi}` in units of PW (petawatts).
- ``heat_transport_convergence`` given by :math:`-\frac{1}{2 \pi ~a^2 \cos\phi} \frac{\partial \mathcal{H}}{\partial \phi}` in units of W/m2
Non-uniform grid spacing is supported.
The state variable :math:`T` may be multi-dimensional, but the diffusion
will operate along the latitude dimension only.
"""
from __future__ import division
import numpy as np
from .meridional_advection_diffusion import MeridionalDiffusion
from climlab import constants as const
[docs]
class MeridionalHeatDiffusion(MeridionalDiffusion):
'''A 1D diffusion solver for Energy Balance Models.
Solves the meridional heat diffusion equation
.. math::
C \frac{\partial T}{\partial t} = -\frac{1}{\cos\phi} \frac{\partial}{\partial \phi} \left[ -D \cos\phi \frac{\partial T}{\partial \phi} \right]
on an evenly-spaced latitude grid, with a state variable :math:`T`,
a heat capacity :math:`C` and diffusivity :math:`D`.
Assuming :math:`T` is a temperature in K or degC, then the units are:
- :math:`D` in W m-2 K-1
- :math:`C` in J m-2 K-1
:math:`D` is provided as input, and can be either scalar
or vector defined at latitude boundaries.
:math:`C` is normally handled automatically for temperature state variables in CLIMLAB.
'''
def __init__(self,
D=0.555, # in W / m^2 / degC
use_banded_solver=False,
**kwargs):
# First just use a dummy value for K
super(MeridionalHeatDiffusion, self).__init__(K=1.,
use_banded_solver=use_banded_solver, **kwargs)
# Now initialize properly
self.D = D
self.add_diagnostic('heat_transport', 0.*self.diffusive_flux)
self.add_diagnostic('heat_transport_convergence', 0.*self.flux_convergence)
@property
def D(self):
return self._D
@D.setter
def D(self, Dvalue):
self._D = Dvalue
self._update_diffusivity()
[docs]
def _update_diffusivity(self):
for varname, value in self.state.items():
heat_capacity = value.domain.heat_capacity
# diffusivity in units of m**2/s
self.K = self.D / heat_capacity * const.a**2
[docs]
def _update_diagnostics(self, newstate):
super(MeridionalHeatDiffusion, self)._update_diagnostics(newstate)
for varname, value in self.state.items():
heat_capacity = value.domain.heat_capacity
coslat_bounds = np.moveaxis(self._weight_bounds,-1,self.diffusion_axis_index)
self.heat_transport[:] = (self.diffusive_flux * heat_capacity *
2 * np.pi * const.a * coslat_bounds * 1E-15) # in PW
self.heat_transport_convergence[:] = (self.flux_convergence *
heat_capacity) # in W/m**2