Beispiel #1
0
    def horadv(self, vdx_in, b_in, dt):
        r"""
    Carry out horizon buoyancy advection into the column model from an adjoining model,
    for the timestepping solution. This function implements an upwind advection scheme.

    Parameters
    ----------

    vdx_in : float or ndarray
             Total advective transport per unit height into the column for the timestepping
             solution. Positive values indicate transport into the column. Units: m\ :sup:`2`/s
    b_in : float or ndarray
           Buoyancy vales from the adjoining module for the timestepping solution. Units: m/s\ :sup:`2`
    dt : int
         Numerical timestep over which solution are iterated. Units: s

    """

        vdx_in = make_array(vdx_in, self.z, 'vdx_in')
        b_in = make_array(b_in, self.z, 'b_in')

        adv_idx = vdx_in > 0.0
        db = b_in - self.b

        self.b[adv_idx] = self.b[adv_idx] + dt * vdx_in[adv_idx] * db[
            adv_idx] / self.Area(self.z[adv_idx])
Beispiel #2
0
  def __init__(
      self,
      y=None,    # grid (input)
      Ks=0.,    # hor. diffusivity (input)
      h=50.,    # ML depth (input)
      L=4e6,    # zonal width (input)
      surflux=0.,    # prescribed surface buoyancy flux (in m^2/s^3; input)
    # Notice that the first and last gridpoints are BCs and should have no flux
      rest_mask=0.,    # mask for surface restoring (1 where restoring is applied 0 elsewhere)
      b_rest=0.,    # surface buoyancy towards which we are restoring 
      v_pist=1.5 / 86400.,    # Piston velocity for restoring in SL (input)
      bs=0.0,    # Surface buoyancy (input, output)
      Psi_s=None    # Overturning in the ML (output)
  ):
    r"""
    Parameters
    ----------

    y : ndarray
        Uniform meridional mixed layer grid. Units: m
    Ks : float
         Horizontal diffusivity in the mixed layer. Units: 
    h : float
        Mixed layer depth. Units: m
    L : float
        Zonal length of the mixed layer. Units: m
    surflux : float, ndarray, or function; optional
              Prescribed surface buoyancy flux. Units: m\ :sup:`2`/s\ :sup:`3`
    rest_mask : float, ndarray, or function; optional
                Surface restoring mask, prescribed as 1 where restoring is desired, and 0 elsewhere.
    b_rest : float, ndarray, or function; optional
             Prescribed surface buoyancy profile towards which mixed layer buoyancy is restored. Units: 
    v_pist : float; optional
             Prescribed piston velocity for buoyancy restoration. Units: m/s
    bs : float, ndarray, or function; optional
         Initial meridional surface buoyancy profile in the mixed layer. Units:
    Psi_s : ndarray; optional
         Initial overturning transport in the mixed layer. Units: Sv 

    """
    # initialize grid:
    if isinstance(y, np.ndarray):
      self.y = y
    else:
      raise TypeError('y needs to be numpy array providing (regular) grid')

    self.Ks = Ks
    self.h = h
    self.L = L
    self.surflux = make_array(surflux, self.y, 'surflux')
    self.rest_mask = make_array(rest_mask, self.y, 'rest_mask')
    self.b_rest = make_array(b_rest, self.y, 'b_rest')
    self.v_pist = v_pist
    self.Psi_s = Psi_s
    self.bs = make_array(bs, self.y, 'bs')
Beispiel #3
0
    def Psib(self, nb=500):
        r"""
    Remap the overturning streamfunction from physical depth space, into isopycnal
    space

    .. math::
      \Psi^b\left(b\right) = \int_{-H}^0 \partial_z\Psi\left(z\right)\mathcal{H}\left[b - b_{up}\left(z\right)\right]

    by computing upwind density classes

    .. math::
      \begin{aligned}
      b_{up}\left(z\right) = 
      \begin{cases} 
        b_N\left(z\right), & \partial_z\Psi\left(z\right)  > 0 \\
        b_B\left(z\right), & \partial_z\Psi\left(z\right)  < 0
      \end{cases}
      \end{aligned}

    where :math:`b_N\left(z\right)` is the density profiles in the northern region, :math:`b_B\left(z\right)` is
    the density profile in the southern basin, and :math:`\mathcal{H}` is the Heaviside step function.

    Parameters
    ----------
    nb : int; optional
         Number of upstream density classes into which the streamfunction is to be remapped.

    Returns
    -------
    psib : ndarray
           An array representing the values of the overturning streamfunction in each upwind density class.
    """
        # map overturning into isopycnal space:
        b1 = make_array(self.b1, self.z, 'b1')
        b2 = make_array(self.b2, self.z, 'b2')
        bmin = min(np.min(b1), np.min(b2))
        bmax = max(np.max(b1), np.max(b2))
        self.bgrid = np.linspace(bmin, bmax, nb)
        udydz = -(self.Psi[1:] - self.Psi[:-1])
        psib = 0. * self.bgrid
        bup_bot = b1[:-1].copy()
        bup_top = b1[1:].copy()
        idx = udydz < 0
        bup_bot[idx] = b2[:-1][idx]
        bup_top[idx] = b2[1:][idx]
        for i in range(0, len(self.bgrid)):
            mask = np.clip((bup_top - self.bgrid[i]) / (bup_top - bup_bot), 0.,
                           1.)
            psib[i] = np.sum(mask * udydz)
        return psib
Beispiel #4
0
    def test_so_ml_init(self, so_ml_config):
        if not isinstance(so_ml_config['y'], np.ndarray) or not len(
                so_ml_config['y']):
            with pytest.raises(TypeError) as yinfo:
                SO_ML(**so_ml_config)
            assert (str(yinfo.value) ==
                    "y needs to be numpy array providing (regular) grid")
            return

        so_ml = SO_ML(**so_ml_config)
        for k in [
                'y', 'Ks', 'h', 'L', 'surflux', 'rest_mask', 'b_rest',
                'v_pist', 'Psi_s', 'bs'
        ]:
            assert hasattr(so_ml, k)

        so_ml_signature = funcsigs.signature(SO_ML)

        for k in ['Ks', 'h', 'L', 'v_pist', 'Psi_s']:
            assert getattr(
                so_ml,
                k) == (so_ml_config[k] if k in so_ml_config and so_ml_config[k]
                       else so_ml_signature.parameters[k].default)

        for k in ['surflux', 'rest_mask', 'b_rest', 'bs']:
            assert all(
                getattr(so_ml, k) == make_array((
                    so_ml_config[k] if k in so_ml_config and so_ml_config[k]
                    else so_ml_signature.parameters[k].default), so_ml.y, k))
Beispiel #5
0
    def __init__(
        self,
        z=None,  # grid (input)
        kappa=None,  # diffusivity profile (input)
        bs=0.025,  # surface buoyancy bound. cond (input)
        bbot=0.0,  # bottom buoyancy boundary condition (input)  
        bzbot=None,  # bottom strat. as alternative boundary condition (input) 
        b=0.0,  # Buoyancy profile (input, output)
        Area=None,  # Horizontal area (can be function of depth)
        N2min=1e-7  # Minimum strat. for conv adjustment
    ):
        r"""
    Parameters
    ----------

    z : ndarray
        Vertical depth levels of column grid. Units: m
    kappa : float, function, or ndarray
            Vertical diffusivity profile. Units: m\ :sup:`2`/s
    bs : float
         Surface level buoyancy boundary condition. Units: m/s\ :sup:`2`
    bbot : float; optional
           Bottom level buoyancy boundary condition. Units: m/s\ :sup:`2`
    bzbot : float; optional
            Bottom level buoyancy stratification. Can be used as an alternative to **bbot**. Units: s\ :sup:`-2`
    b : float, function, or ndarray
        Initial vertical buoyancy profile. Recalculated on model run. Units: m/s
    Area : float, function, or ndarray
           Horizontal area of basin. Units: m\ :sup:`2`
    N2min : float; optional
            Minimum stratification for convective adjustment. Units: s\ :sup:`-1`
    """

        # initialize grid:
        if isinstance(z, np.ndarray) and len(z) > 0:
            self.z = z
        else:
            raise TypeError('z needs to be numpy array providing grid levels')

        self.kappa = make_func(kappa, self.z, 'kappa')
        self.Area = make_func(Area, self.z, 'Area')

        self.bs = bs
        self.bbot = bbot
        self.bzbot = bzbot

        self.N2min = N2min

        self.b = make_array(b, self.z, 'b')

        if check_numpy_version():
            self.bz = np.gradient(self.b, z)
        else:
            self.bz = 0. * z  # notice that this is just for initialization of ode solver
Beispiel #6
0
    def vertadvdiff(self, wA, dt, do_conv=False):
        r"""
    Calculate and apply the forcing from advection and diffusion on the vertical buoyancy
    profile, for the timestepping solution. This function implements an upwind advection
    scheme.

    Parameters
    ----------

    wA : float or ndarray
         Area integrated velocity profile for the timestepping solution. Units: m\ :sup:`3`/s
    dt : int
         Numerical timestep over which solution are iterated. Units: s

    """

        wA = make_array(wA, self.z, 'wA')
        dz = self.z[1:] - self.z[:-1]

        # apply boundary conditions:
        if not do_conv:  # if we use convection, upper BC is already applied there
            self.b[-1] = self.bs
        self.b[0] = (self.bbot if self.bzbot is None else self.b[1] -
                     self.bzbot * dz[0])

        bz = (self.b[1:] - self.b[:-1]) / dz
        bz_up = bz[1:]
        bz_down = bz[:-1]
        bzz = (bz_up - bz_down) / (0.5 * (dz[1:] + dz[:-1]))

        #upwind advection:
        weff = wA - self.dAkappa_dz(self.z)
        bz = bz_down
        bz[weff[1:-1] < 0] = bz_up[weff[1:-1] < 0]

        db_dt = (-weff[1:-1] * bz / self.Area(self.z[1:-1]) +
                 self.kappa(self.z[1:-1]) * bzz)
        self.b[1:-1] = self.b[1:-1] + dt * db_dt