Skip to content
Snippets Groups Projects
enstools_utils.py 5.74 KiB
Newer Older
import logging
import six
from enstools.misc import is_additional_coordinate_variable
import numpy as np

# this one is already in enstools.misc, but not in the version on the w2w cluster. simple copy.
def distance(lat1, lat2, lon1, lon2, radius=6371229.0):
    """
    distance between two points on a globe.

    Parameters
    ----------
    lat1:
            latitude of first point in radian.

    lat2:
            latitude of second point in radian.

    lon1:
            longitude of first point in radian.

    lon2:
            logitude of second point in radian.

    radius:
            radius of the globe in m.

    Returns
    -------
    distance in m
    """
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = np.sin(dlat / 2) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2) ** 2
    c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))
    return radius * c


def get_latitude_dim(ds):
    """
    get the name of the latitude dimension from a dataset or array

    Parameters
    ----------
    ds : xarray.Dataset or xarray.DataArray

    Returns
    -------
    str or None :
            if no latitude dimension was found, None is returned.
    """
    lat_names = ["lat", "latitude"]
    for lat_name in lat_names:
        if lat_name in ds.dims:
            logging.debug("get_latitude_dim: found name '%s'" % lat_name)
            return lat_name
    return None


def get_longitude_dim(ds):
    """
    get the name of the longitude dimension from a dataset or array

    Parameters
    ----------
    ds : xarray.Dataset or xarray.DataArray

    Returns
    -------
    str or None :
            if no longitude dimension was found, None is returned.
    """
    lon_names = ["lon", "longitude"]
    for lon_name in lon_names:
        if lon_name in ds.dims:
            logging.debug("get_longitude_dim: found name '%s'" % lon_name)
            return lon_name
    return None


def get_u_var(ds):
    u_names = ["u", "U"]
    for u_name in u_names:
        if u_name in ds.data_vars:
            logging.debug("get_u_dim: found name '%s'" % u_name)
            return u_name
    return None


def get_v_var(ds):
    v_names = ["v", "V"]
    for v_name in v_names:
        if v_name in ds.data_vars:
            logging.debug("get_v_dim: found name '%s'" % v_name)
            return v_name
    return None


# TODO get_X_dim functions can be written as one single abstract getter
def get_vertical_dim(ds):
    """
    get the name of the vertical dimension from a dataset or array.
    Needs to be expanded.

    Parameters
    ----------
    ds : xarray.Dataset or xarray.DataArray

    Returns
    -------
    str or None :
            if no vertical dimension was found, None is returned.
    """
    v_names = ["pres", "p", "lev", "level", "isobaric", "layer", "hybrid", "height", "height_2", "th_level", "plev"]
    for v_name in v_names:
        if v_name in ds.dims:
            logging.debug("get_longitude_dim: found name '%s'" % v_name)
            return v_name
    return None

def get_init_time_dim(ds):
    """
    get the name of the init_time dimension from a dataset or array.

    Parameters
    ----------
    ds : xarray.Dataset or xarray.DataArray

    Returns
    -------
    str or None :
            if no init_time dimension was found, None is returned.
    """
    v_names = ["init_time"]
    for v_name in v_names:
        if v_name in ds.dims:
            logging.debug("get_longitude_dim: found name '%s'" % v_name)
            return v_name
    return None
def add_time_dim(ds, time, inplace=True):
    """
    create an ensemble dimension with in the dataset

    Parameters
    ----------
    ds : xarray.Dataset

    time : datetime
            time of the dataset

    inplace : bool
            modify the dataset directly?

    Returns
    -------
    xarray.Dataset:
            A copy of the dataset expanded by the time dimension or the expanded dataset
            if the expansion was done inplace.
    """
    # create a lazy copy of the dataset
    if inplace:
        new_ds = ds
    else:
        new_ds = ds.copy()

    if time is None:
        import datetime
        time = datetime.datetime.now()

    # loop over all data variables.
    # those with time dimension are extended behind the time dimension, others at the front
    for one_name, one_var in six.iteritems(ds.data_vars):
        if is_additional_coordinate_variable(one_var):
            continue
        new_ds[one_name] = one_var.expand_dims("time")
    new_ds.coords["time"] = [time]

    return new_ds


def add_vertical_dim(ds, vertical_value=0, vertical_name='level', inplace=True):
    """
    create a vertical dimension with in the dataset

    Parameters
    ----------
    ds : xarray.Dataset

    vertical_value : float
            value for the added vertical dimension of the dataset, default 0

    vertical_name : str
            name for the added vertical dimension of the dataset, default 'level'

    inplace : bool
            modify the dataset directly?

    Returns
    -------
    xarray.Dataset:
            A copy of the dataset expanded by the vertical dimension or the expanded dataset
            if the expansion was done inplace.
    """
    # create a lazy copy of the dataset
    if inplace:
        new_ds = ds
    else:
        new_ds = ds.copy()

    # loop over all data variables.
    # those with time dimension are extended behind the time dimension, others at the front
    for one_name, one_var in six.iteritems(ds.data_vars):
        if is_additional_coordinate_variable(one_var):
            continue
        if len(one_var.dims) > 0 and one_var.dims[0] == "time":
            new_ds[one_name] = one_var.expand_dims(vertical_name, 1)
        else:
            new_ds[one_name] = one_var.expand_dims(vertical_name)
    new_ds.coords[vertical_name] = [vertical_value]

    return new_ds