# Path: indiff/_constants.py
# PHALF_STR = 'phalf'
# 
# PFULL_STR = 'pfull'
# 
# Path: indiff/utils.py
# def replace_coord(arr, old_dim, new_dim, new_coord):
#     """Replace a coordinate with new one; new and old must have same shape."""
#     new_arr = arr.rename({old_dim: new_dim})
#     ds = new_arr.to_dataset(name='new_arr')
#     ds[new_dim] = new_coord
#     return ds['new_arr']
# 
# Path: indiff/diff/centered.py
# class CenDiff(FiniteDiff):
#     """Centered finite differencing."""
#     _MIN_SPACING_FACTOR = 2
#     _DIFF_BWD_CLS = BwdDiff
#     _DIFF_FWD_CLS = FwdDiff
# 
#     def __init__(self, arr, dim, spacing=1, fill_edge=False, wrap=False):
#         super(CenDiff, self).__init__(arr, dim, spacing=spacing)
#         assert fill_edge in (False, 'left', 'right', 'both', True), fill_edge
#         self.fill_edge = fill_edge
#         self._diff_bwd = self._DIFF_BWD_CLS(arr, dim, spacing=spacing).diff
#         self._diff_fwd = self._DIFF_FWD_CLS(arr, dim, spacing=spacing).diff
# 
#     def _diff_edge(self, side='left'):
#         """One-sided differencing of array edge."""
#         if side == 'left':
#             trunc = slice(0, self.spacing + 1)
#             cls = self._DIFF_FWD_CLS
#         elif side == 'right':
#             trunc = slice(-(self.spacing + 1), None)
#             cls = self._DIFF_BWD_CLS
#         else:
#             raise ValueError("Parameter `side` must be either 'left' "
#                              "or 'right': {}").format(side)
#         arr_edge = self._slice_arr_dim(trunc, self.arr)
#         return cls(arr_edge, self.dim, spacing=self.spacing).diff()
# 
#     def _wrap(self):
#         return wraparound(self.arr, self.dim, left_to_right=self.spacing,
#                           right_to_left=self.spacing, circumf=0, spacing=1)
# 
#     def diff(self):
#         """Centered differencing of the DataArray or Dataset.
# 
#         :param fill_edge: Whether or not to fill in the edge cells
#             that don't have the needed neighbor cells for the stencil.  If
#             `True`, use one-sided differencing with the same order of accuracy
#             as `order`, and the outputted array is the same shape as `arr`.
# 
#             If `'left'` or `'right'`, fill only that side.
# 
#             If `False`, the outputted array has a length in the computed axis
#             reduced by `order`.
#         """
#         left = self._slice_arr_dim(slice(0, -self.spacing), self.arr)
#         right = self._slice_arr_dim(slice(self.spacing, None), self.arr)
#         interior = (self._DIFF_FWD_CLS(right, self.dim, self.spacing).diff() +
#                     self._DIFF_BWD_CLS(left, self.dim, self.spacing).diff())
# 
#         if self.fill_edge in ('left', 'both', True):
#             diff_left = self._diff_edge(side='left')
#             interior = xr.concat([diff_left, interior], dim=self.dim)
#         if self.fill_edge in ('right', 'both', True):
#             diff_right = self._diff_edge(side='right')
#             interior = xr.concat([interior, diff_right], dim=self.dim)
#         return interior
# 
# Path: indiff/coord/coord.py
# class Coord(object):
#     """Generic base class for physical coordinates."""
#     _POSSIBLY_CYCLIC = True
# 
#     def _prep_dim(self, dim):
#         msg = ("Specified dim '{}' does not match any dim in self.arr.  "
#                "self.arr.dims: {}").format(dim, self.arr.dims)
#         if dim in self.arr.dims:
#             return dim
#         if dim is None:
#             if self.arr.shape == 1:
#                 return self.arr.dims[0]
#         raise ValueError(msg)
# 
#     def __init__(self, arr, dim=None, cyclic=False):
#         self.arr = arr
#         self.dim = self._prep_dim(dim)
#         if cyclic and not self._POSSIBLY_CYCLIC:
#             raise ValueError("The coordinate cannot be cyclic in a physically "
#                              "meaningful way.")
#         self.cyclic = cyclic
# 
#     def __getitem__(self, key):
#         return self.arr[key]
# 
#     def deriv_prefactor(self, *args, **kwargs):
#         raise NotImplementedError
# 
#     def deriv_factor(self, *args, **kwargs):
#         raise NotImplementedError


from .._constants import PHALF_STR, PFULL_STR
from ..utils import replace_coord
from ..diff import CenDiff
from . import Coord
class Sigma(VertCoord):
    """Pressure divided by surface pressure vertical coordinates."""
    def __init__(self, sigma, dim=None):
        super(Sigma, self).__init__(sigma, dim=dim)

    def pressure(self, ps):
        """Get pressure from sigma levels and surface pressure."""
        return ps * self.sigma


class Eta(VertCoord):
    """Hybrid sigma-pressure vertical coordinates."""
    def __init__(self, pk, bk, pfull, dim=None):
        self.pk = pk
        self.bk = bk
        self.pfull = pfull
        self.phalf = self.pk[PHALF_STR]
        self.arr = self.phalf
        self.dim = dim if dim is not None else self.pk.dims[0]

    def phalf_from_ps(self, ps):
        """Compute pressure at level edges from surface pressure."""
        return self.pk + self.bk*ps

    def to_pfull_from_phalf(self, arr):
        """Compute data at full pressure levels from values at half levels."""
        arr_top = arr.copy()[{PHALF_STR: slice(1, None)}]
        arr_top = replace_coord(arr_top, PHALF_STR, PFULL_STR, self.pfull)

        arr_bot = arr.copy()[{PHALF_STR: slice(None, -1)}]

# Path: indiff/_constants.py
# PHALF_STR = 'phalf'
# 
# PFULL_STR = 'pfull'
# 
# Path: indiff/utils.py
# def replace_coord(arr, old_dim, new_dim, new_coord):
#     """Replace a coordinate with new one; new and old must have same shape."""
#     new_arr = arr.rename({old_dim: new_dim})
#     ds = new_arr.to_dataset(name='new_arr')
#     ds[new_dim] = new_coord
#     return ds['new_arr']
# 
# Path: indiff/diff/centered.py
# class CenDiff(FiniteDiff):
#     """Centered finite differencing."""
#     _MIN_SPACING_FACTOR = 2
#     _DIFF_BWD_CLS = BwdDiff
#     _DIFF_FWD_CLS = FwdDiff
# 
#     def __init__(self, arr, dim, spacing=1, fill_edge=False, wrap=False):
#         super(CenDiff, self).__init__(arr, dim, spacing=spacing)
#         assert fill_edge in (False, 'left', 'right', 'both', True), fill_edge
#         self.fill_edge = fill_edge
#         self._diff_bwd = self._DIFF_BWD_CLS(arr, dim, spacing=spacing).diff
#         self._diff_fwd = self._DIFF_FWD_CLS(arr, dim, spacing=spacing).diff
# 
#     def _diff_edge(self, side='left'):
#         """One-sided differencing of array edge."""
#         if side == 'left':
#             trunc = slice(0, self.spacing + 1)
#             cls = self._DIFF_FWD_CLS
#         elif side == 'right':
#             trunc = slice(-(self.spacing + 1), None)
#             cls = self._DIFF_BWD_CLS
#         else:
#             raise ValueError("Parameter `side` must be either 'left' "
#                              "or 'right': {}").format(side)
#         arr_edge = self._slice_arr_dim(trunc, self.arr)
#         return cls(arr_edge, self.dim, spacing=self.spacing).diff()
# 
#     def _wrap(self):
#         return wraparound(self.arr, self.dim, left_to_right=self.spacing,
#                           right_to_left=self.spacing, circumf=0, spacing=1)
# 
#     def diff(self):
#         """Centered differencing of the DataArray or Dataset.
# 
#         :param fill_edge: Whether or not to fill in the edge cells
#             that don't have the needed neighbor cells for the stencil.  If
#             `True`, use one-sided differencing with the same order of accuracy
#             as `order`, and the outputted array is the same shape as `arr`.
# 
#             If `'left'` or `'right'`, fill only that side.
# 
#             If `False`, the outputted array has a length in the computed axis
#             reduced by `order`.
#         """
#         left = self._slice_arr_dim(slice(0, -self.spacing), self.arr)
#         right = self._slice_arr_dim(slice(self.spacing, None), self.arr)
#         interior = (self._DIFF_FWD_CLS(right, self.dim, self.spacing).diff() +
#                     self._DIFF_BWD_CLS(left, self.dim, self.spacing).diff())
# 
#         if self.fill_edge in ('left', 'both', True):
#             diff_left = self._diff_edge(side='left')
#             interior = xr.concat([diff_left, interior], dim=self.dim)
#         if self.fill_edge in ('right', 'both', True):
#             diff_right = self._diff_edge(side='right')
#             interior = xr.concat([interior, diff_right], dim=self.dim)
#         return interior
# 
# Path: indiff/coord/coord.py
# class Coord(object):
#     """Generic base class for physical coordinates."""
#     _POSSIBLY_CYCLIC = True
# 
#     def _prep_dim(self, dim):
#         msg = ("Specified dim '{}' does not match any dim in self.arr.  "
#                "self.arr.dims: {}").format(dim, self.arr.dims)
#         if dim in self.arr.dims:
#             return dim
#         if dim is None:
#             if self.arr.shape == 1:
#                 return self.arr.dims[0]
#         raise ValueError(msg)
# 
#     def __init__(self, arr, dim=None, cyclic=False):
#         self.arr = arr
#         self.dim = self._prep_dim(dim)
#         if cyclic and not self._POSSIBLY_CYCLIC:
#             raise ValueError("The coordinate cannot be cyclic in a physically "
#                              "meaningful way.")
#         self.cyclic = cyclic
# 
#     def __getitem__(self, key):
#         return self.arr[key]
# 
#     def deriv_prefactor(self, *args, **kwargs):
#         raise NotImplementedError
# 
#     def deriv_factor(self, *args, **kwargs):
#         raise NotImplementedError


# Path: indiff/coord/vert.py
from .._constants import PHALF_STR, PFULL_STR
from ..utils import replace_coord
from ..diff import CenDiff
from . import Coord

class Sigma(VertCoord):
    """Pressure divided by surface pressure vertical coordinates."""
    def __init__(self, sigma, dim=None):
        super(Sigma, self).__init__(sigma, dim=dim)

    def pressure(self, ps):
        """Get pressure from sigma levels and surface pressure."""
        return ps * self.sigma


class Eta(VertCoord):
    """Hybrid sigma-pressure vertical coordinates."""
    def __init__(self, pk, bk, pfull, dim=None):
        self.pk = pk
        self.bk = bk
        self.pfull = pfull
        self.phalf = self.pk[PHALF_STR]
        self.arr = self.phalf
        self.dim = dim if dim is not None else self.pk.dims[0]

    def phalf_from_ps(self, ps):
        """Compute pressure at level edges from surface pressure."""
        return self.pk + self.bk*ps

    def to_pfull_from_phalf(self, arr):
        """Compute data at full pressure levels from values at half levels."""
        arr_top = arr.copy()[{PHALF_STR: slice(1, None)}]
        arr_top = replace_coord(arr_top, PHALF_STR, PFULL_STR, self.pfull)

        arr_bot = arr.copy()[{PHALF_STR: slice(None, -1)}]

        arr_bot = replace_coord(arr_bot, PHALF_STR, PFULL_STR, self.pfull)