def promote16(u, fn=None, *args, **kwargs):
    r"""
    Utility function for use with functions that do not support arrays
    of dtype ``np.float16``. This function has two distinct modes of
    operation. If called with only the `u` parameter specified, the
    returned value is either `u` itself if `u` is not of dtype
    ``np.float16``, or `u` promoted to ``np.float32`` dtype if it is. If
    the function parameter `fn` is specified then `u` is conditionally
    promoted as described above, passed as the first argument to
    function `fn`, and the returned values are converted back to dtype
    ``np.float16`` if `u` is of that dtype. Note that if parameter `fn`
    is specified, it may not be be specified as a keyword argument if it
    is followed by any non-keyword arguments.

    Parameters
    ----------
    u : array_like
      Array to be promoted to np.float32 if it is of dtype ``np.float16``
    fn : function or None, optional (default None)
      Function to be called with promoted `u` as first parameter and
      \*args and \*\*kwargs as additional parameters
    *args
      Variable length list of arguments for function `fn`
    **kwargs
      Keyword arguments for function `fn`

    Returns
    -------
    up : ndarray
      Conditionally dtype-promoted version of `u` if `fn` is None,
      or value(s) returned by `fn`, converted to the same dtype as `u`,
      if `fn` is a function
    """

    dtype = np.float32 if u.dtype == np.float16 else u.dtype
    up = np.asarray(u, dtype=dtype)
    if fn is None:
        return up
    else:
        v = fn(up, *args, **kwargs)
        if isinstance(v, tuple):
            vp = tuple([np.asarray(vk, dtype=u.dtype) for vk in v])
        else:
            vp = np.asarray(v, dtype=u.dtype)
        return vp