Esempio n. 1
0
    def peak_direction(self):
        '''Compute peak wave direction

        Returns
        -------
        theta : xarray.DataArray
            Peak wave direction at each point in time and location in
            the dataset

        '''

        if self.has_dimension('direction', raise_error=True):

            coords = OrderedDict(self.coords)
            k = self._key_lookup('_energy')
            E = self.variables[k]
            dims = list(E.dims)

            # determine peak directions
            k = self._key_lookup('_direction')
            theta = coords.pop(k).values
            ix = E.argmax(dim=k).values
            peaks = theta[ix.flatten()].reshape(ix.shape)
            dims.remove(k)

            # determine units
            units = self.variables[k].attrs['units']
            units = simplify(units)
            
            return xr.DataArray(peaks, coords=coords, dims=dims,
                                attrs=dict(units=units))
Esempio n. 2
0
    def as_omnidirectional(self):
        '''Convert directional spectrum to an omnidirectional spectrum

        Integrate spectral energy over the directions.

        Returns
        -------
        OceanWaves
            New OceanWaves object

        '''

        if not self.has_dimension('direction'):
            return self

        # expand energy matrix
        k1 = self._key_lookup('_energy')
        k2 = self._key_lookup('_direction')
        energy = np.abs(np.trapz(self.variables[k1].values,
                                 self.coords[k2].values, axis=-1))

        # determine units
        units = '(%s)*(%s)' % (self.variables[k1].attrs['units'],
                               self.variables[k2].attrs['units'])

        # reinitialize object with new dimensions
        return self.reinitialize(
            direction=self.peak_direction(),
            spreading=self.directional_spreading(),
            energy=energy,
            energy_units=simplify(units),
            directional=False
        )
Esempio n. 3
0
    def peak_period(self):
        '''Compute peak wave period
        
        Returns
        -------
        T : xarray.DataArray
            Peak wave period at each point in time and location in the
            dataset

        '''
    
        if self.has_dimension('frequency', raise_error=True):

            coords = OrderedDict(self.coords)
            k = self._key_lookup('_energy')
            E = self.variables[k]
            dims = list(E.dims)

            # determine peak frequencies
            k = self._key_lookup('_frequency')
            f = coords.pop(k).values
            ix = E.argmax(dim=k).values
            peaks = 1. / f[ix.flatten()].reshape(ix.shape)
            dims.remove(k)

            # determine units
            units = '1/(%s)' % self.variables[k].attrs['units']
            units = simplify(units)
            
            return xr.DataArray(peaks, coords=coords, dims=dims,
                                attrs=dict(units=units))
Esempio n. 4
0
    def Hm0(self, f_min=0, f_max=np.inf):
        '''Compute significant wave height based on zeroth order moment

        Parameters
        ----------
        f_min : float
            Minimum frequency to include in moment
        f_max : float
            Maximum frequency to include in moment

        Returns
        -------
        H : xarray.DataArray
            Significant wave height at each point in time and location
            in the dataset

        '''

        # compute moments
        m0 = self.moment(0, f_min=f_min, f_max=f_max)
        
        # compute wave height
        H = 4. * np.sqrt(m0)

        # determine units
        units = '(%s)^0.5' % m0.attrs['units']
        H.attrs['units'] = simplify(units)

        return H
Esempio n. 5
0
    def directional_spreading(self):
        '''Estimate directional spreading

        Estimate directional spreading by assuming a gaussian
        distribution and computing the variance of the directional
        spreading. The directional spreading is assumed to be
        ``3000/var``.

        Notes
        -----
        This estimate is inaccurate and should be improved based on
        the cosine model.

        '''

        if self.has_dimension('direction', raise_error=True):

            coords = OrderedDict(self.coords)
            k = self._key_lookup('_energy')
            E = self.variables[k]
            dims = list(E.dims)

            # determine peak directions
            k = self._key_lookup('_direction')
            theta = coords.pop(k).values
            ix_direction = dims.index(k)
            dims.remove(k)

            # determine directional spreading
            E /= expand_and_repeat(
                np.trapz(E, theta, axis=ix_direction),
                repeat=len(theta),
                expand_dims=ix_direction
            )
            m1 = np.trapz(theta * E, theta)
            m2 = np.trapz(theta**2. * E, theta)
            var = m2 - m1**2.
            spreading = np.round(3000./var / 5.) * 5.
            
            # determine units
            units = self.variables[k].attrs['units']
            units = simplify(units)
            
            return xr.DataArray(spreading, coords=coords, dims=dims,
                                attrs=dict(units=units))
Esempio n. 6
0
    def Tm02(self):
        '''Compute wave period based on second order moment

        Returns
        -------
        T : xarray.DataArray
            Spectral wave period at each point in time and location in
            the dataset

        '''

        # compute moments
        m0 = self.moment(0)
        m2 = self.moment(2)

        # compute wave period
        T = np.sqrt(m0/m2)

        # determine units
        units = '((%s)/(%s))^0.5' % (m0.attrs['units'], m2.attrs['units'])
        T.attrs['units'] = simplify(units)

        return T
Esempio n. 7
0
    def Tm01(self):
        '''Compute wave period based on first order moment

        Returns
        -------
        T : xarray.DataArray
            Spectral wave period at each point in time and location in
            the dataset

        '''
    
        # compute moments
        m0 = self.moment(0)
        m1 = self.moment(1)

        # compute wave period
        T = m0/m1

        # determine units
        units = '(%s)/(%s)' % (m0.attrs['units'], m1.attrs['units'])
        T.attrs['units'] = simplify(units)

        return T
Esempio n. 8
0
    def as_directional(self, direction, direction_units='deg',
                       theta_peak=None, s=None, normalize=True):
        '''Convert omnidirectional spectrum to a directional spectrum

        Spreads total wave energy over a given set of directions
        according to a spreading factor ``s``.

        See :func:`oceanwaves.spectral.directional_spreading` for options.

        Returns
        -------
        OceanWaves
            New OceanWaves object

        '''

        if self.has_dimension('direction'):
            return self

        direction = np.asarray(direction, dtype=np.float)
        
        # expand energy matrix
        k = self._key_lookup('_energy')
        energy = self.variables[k].values
        energy_units = self.variables[k].attrs['units']
        ix_direction = energy.ndim

        # determine peak direction matrix
        if theta_peak is None:
            k = self._key_lookup('_direction')
            if k in self.variables.keys():
                theta_peak = self.variables[k].values
            else:
                theta_peak = 0.
        if isinstance(theta_peak, (int, float)):
            theta_peak = theta_peak * np.ones(energy.shape)
            
        # determine spreading matrix
        if s is None:
            k = self._key_lookup('_spreading')
            if k in self.variables.keys():
                s = self.variables[k].values
            else:
                s = 20.
        if isinstance(s, (int, float)):
            s = s * np.ones(energy.shape)

        # expand energy matrix
        energy = expand_and_repeat(
            energy,
            repeat=len(direction),
            expand_dims=ix_direction
        )
        theta_mtx = expand_and_repeat(
            direction,
            shape=energy.shape,
            exist_dims=ix_direction
        )
        thetap_mtx = expand_and_repeat(
            theta_peak,
            shape=energy.shape,
            expand_dims=ix_direction
        )
        s_mtx = expand_and_repeat(
            s,
            shape=energy.shape,
            expand_dims=ix_direction
        )

        # compute directional spreading
        spreading = directional_spreading(
            theta_mtx,
            units=direction_units,
            theta_peak=thetap_mtx,
            s=s_mtx,
            normalize=False
        )

        # normalize shape
        if normalize:
            spreading /= trapz_and_repeat(spreading, theta_mtx,
                                          axis=ix_direction)

        # apply shape
        energy = np.multiply(energy, spreading)

        # determine units
        units = '(%s)/(%s)' % (energy_units, direction_units)

        # reinitialize object with new dimensions
        return self.reinitialize(
            direction=direction,
            direction_units=direction_units,
            energy=energy,
            energy_units=simplify(units)
        )
Esempio n. 9
0
    def as_spectral(self, frequency, frequency_units='Hz', Tp=None,
                    gamma=3.3, sigma_low=.07, sigma_high=.09,
                    shape='jonswap', method='yamaguchi', g=9.81,
                    normalize=True):
        '''Convert wave energy to spectrum

        Spreads total wave energy over a given set of frequencies
        according to the JONSWAP spectrum shape.

        See :func:`oceanwaves.spectral.jonswap` for options.

        Returns
        -------
        OceanWaves
            New OceanWaves object

        '''

        if self.has_dimension('frequency'):
            return self

        frequency = np.asarray(frequency, dtype=np.float)
        frequency = frequency[frequency>0]
        
        k = self._key_lookup('_energy')
        energy = self.variables[k].values
        energy_units = self.variables[k].attrs['units']
        ix_frequency = energy.ndim - int(self.has_dimension('direction'))
        
        # convert to energy
        if self.units == 'm':
            energy = energy**2. / 16.
            energy_units = '(%s)^2' % energy_units
            
        # determine peak period matrix
        if Tp is None:
            k = self._key_lookup('_frequency')
            if k in self.variables.keys():
                Tp = 1./np.asarray(self.variables[k])
            else:
                Tp = 4.
        if isinstance(Tp, (int, float)):
            Tp = Tp * np.ones(energy.shape)

        # expand energy matrices
        energy = expand_and_repeat(
            energy,
            repeat=len(frequency),
            expand_dims=ix_frequency
        )
        f_mtx = expand_and_repeat(
            frequency,
            shape=energy.shape,
            exist_dims=ix_frequency
        )
        Tp_mtx = expand_and_repeat(
            Tp,
            shape=energy.shape,
            expand_dims=ix_frequency
        )

        # compute spectrum shape
        if shape.lower() == 'jonswap':
            spectrum = jonswap(f_mtx, Hm0=1., Tp=Tp_mtx, gamma=gamma,
                               sigma_low=sigma_low,
                               sigma_high=sigma_high, g=g,
                               method=method, normalize=False)

            # normalize shape
            if normalize:
                spectrum /= trapz_and_repeat(spectrum, f_mtx,
                                             axis=ix_frequency)

        else:
            raise ValueError('Unknown spectrum shape: %s', shape)

        # apply shape
        energy = np.multiply(energy, spectrum)

        # determine units
        units = '(%s)/(%s)' % (energy_units,
                               frequency_units)

        # reinitialize object with new dimensions
        return self.reinitialize(
            frequency=frequency,
            frequency_units=frequency_units,
            energy=energy,
            energy_units=simplify(units)
        )
Esempio n. 10
0
    def __init__(self, time=None, location=None, frequency=None,
                 direction=None, energy=None, spreading=None,
                 time_units='s', location_units='m',
                 frequency_units='Hz', direction_units='deg',
                 energy_units='m^2/Hz', spreading_units='deg',
                 time_var='time', location_var='location',
                 frequency_var='frequency', direction_var='direction',
                 energy_var='energy', spreading_var='spreading',
                 frequency_convention='absolute',
                 direction_convention='nautical',
                 spreading_convention='cosine', spectral=True,
                 directional=True, attrs={}, crs=None, **kwargs):
        '''Initialize class

        Sets dimensions, converts coordinates and fills the dataset,
        if data is provided.

        Parameters
        ----------
        time : iterable, optional
            Time coordinates, each item can be a datetime object or
            float
        location : iterable of 2-tuples, optional
            Location coordinates, each item is a 2-tuple with x- and
            y-coordinates
        frequency : iterable, optional
            Frequency cooridinates
        direction : iterable, optional
            Direction coordinates
        energy : matrix, optional
            Wave energy
        time_units : str, optional
            Units of time coordinates (default: s)
        location_units : str, optional
            Units of location coordinates (default: m)
        frequency_units : str, optional
            Units of frequency coordinates (default: Hz)
        direction_units : str, optional
            Units of direction coordinates (default: deg)
        energy_units : str, optional
            Units of wave energy (default: m^2/Hz)
        time_var : str, optional
            Name of time variable (default: time)
        location_var : str, optional
            Name of location variable (default: location)
        frequency_var : str, optional
            Name of frequency variable (default: frequency)
        direction_var : str, optional
            Name of direction variable (default: direction)
        energy_var : str, optional
            Name of wave energy variable (default: energy)
        frequency_convention : str, optional
            Convention of frequency definition (default: absolute)
        direction_convention : str, optional
            Convention of direction definition (default: nautical)
        attrs : dict-like, optional
            Global attributes
        crs : str, optional
            Proj4 specification of local coordinate reference system
        kwargs : dict, optional
            Additional options passed to the xarray.Dataset
            initialization method

        See Also
        --------
        oceanwaves.OceanWaves.reinitialize

        '''

        dims = []
        coords = OrderedDict()
        data_vars = OrderedDict()

        # simplify dimensions
        time = np.asarray(time)
        location = np.asarray(location)
        frequency = np.asarray(frequency, dtype=np.float)
        direction = np.asarray(direction, dtype=np.float)
        spreading = np.asarray(spreading, dtype=np.float)
        energy = np.asarray(energy, dtype=np.float)
        
        # simplify units
        time_units = simplify(time_units)
        location_units = simplify(location_units)
        frequency_units = simplify(frequency_units)
        direction_units = simplify(direction_units)
        energy_units = simplify(energy_units)
        
        # determine object dimensions
        if self._isvalid(time):
            dims.append(time_var)
            coords[time_var] = xr.Variable(
                time_var,
                time,
                attrs=dict(units=time_units)
            )

        if self._isvalid(location):
            dims.append(location_var)
            coords[location_var] = xr.Variable(
                location_var,
                np.arange(len(location))
            )
            
            x, y = list(zip(*location))
            coords['%s_x' % location_var] = xr.Variable(
                location_var,
                np.asarray(x),
                attrs=dict(units=location_units)
            )
            coords['%s_y' % location_var] = xr.Variable(
                location_var,
                np.asarray(y),
                attrs=dict(units=location_units)
            )
                
            coords['%s_lat' % location_var] = xr.Variable(
                location_var,
                np.asarray(x) + np.nan,
                attrs=dict(units='degN')
            )
            coords['%s_lon' % location_var] = xr.Variable(
                location_var,
                np.asarray(y) + np.nan,
                attrs=dict(units='degE')
            )

        if self._isvalid(frequency, mask=frequency>0) and spectral:
            dims.append(frequency_var)
            coords[frequency_var] = xr.Variable(
                frequency_var,
                frequency[frequency>0],
                attrs=dict(units=frequency_units)
            )
            
        if self._isvalid(direction) and directional:
            dims.append(direction_var)
            coords[direction_var] = xr.Variable(
                direction_var,
                direction,
                attrs=dict(units=direction_units)
            )

        # determine object shape
        shp = tuple([len(c) for k, c in coords.items() if k in dims])

        # initialize energy variable
        data_vars[energy_var] = xr.DataArray(
            np.nan + np.zeros(shp),
            dims=dims,
            coords=coords,
            attrs=dict(units=energy_units)
        )

        # store parameterized frequencies
        if not spectral:
            if self._isvalid(frequency):
                data_vars[frequency_var] = xr.DataArray(
                    frequency,
                    dims=dims,
                    coords=coords,
                    attrs=dict(units=direction_units)
                )
        
        # store parameterized directions
        if not directional:
            if self._isvalid(direction):
                data_vars[direction_var] = xr.DataArray(
                    direction,
                    dims=dims,
                    coords=coords,
                    attrs=dict(units=direction_units)
                )
            if self._isvalid(spreading):
                data_vars[spreading_var] = xr.DataArray(
                    spreading,
                    dims=dims,
                    coords=coords,
                    attrs=dict(units=spreading_units)
                )
        
        # collect global attributes
        attrs.update(dict(
            _init=kwargs.copy(),
            _crs=crs,
            _names=dict(
                time = time_var,
                location = location_var,
                frequency = frequency_var,
                direction = direction_var,
                spreading = spreading_var,
                energy = energy_var
            ),
            _units=dict(
                time = time_units,
                location = location_units,
                frequency = frequency_units,
                direction = direction_units,
                energy = energy_units
            ),
            _conventions=dict(
                frequency = frequency_convention,
                direction = direction_convention,
                spreading = spreading_convention
            )
        ))
        
        # initialize empty object
        super(OceanWaves, self).__init__(
            data_vars=data_vars,
            coords=coords,
            attrs=attrs,
            **kwargs
        )

        # set wave energy
        if self._isvalid(energy):
            self['_energy'] = dims, energy.squeeze()

        # convert coordinates
        self.convert_coordinates(crs)
Esempio n. 11
0
    def moment(self, n, f_min=0., f_max=np.inf):
        '''Compute nth order moment of wave spectrum

        Parameters
        ----------
        n : int
            Order of moment
        f_min : float
            Minimum frequency to include in moment
        f_max : float
            Maximum frequency to include in moment

        Returns
        -------
        m : xarray.DataArray
            nth order moment of the wave spectrum at each point in
            time and location in the dataset

        '''
        
        if self.has_dimension('frequency', raise_error=True):

            coords = OrderedDict(self.coords)
            k = self._key_lookup('_energy')
            E = self.variables[k]
            dims = list(E.dims)
        
            # integrate frequencies
            k = self._key_lookup('_frequency')
            f = coords.pop(k).values
            ix_frequency = dims.index(k)
            f_mtx = expand_and_repeat(
                f,
                shape=E.values.shape,
                exist_dims=ix_frequency
            )
            dims.remove(k)
            
            if f_min == 0. and f_max == np.inf:
                
                m = np.trapz(E.values * f_mtx**n, f_mtx, axis=ix_frequency)
                
            else:

                if n != 0:
                    logger.warn('Computing %d-order moment using a frequency range; '
                                'Are you sure what you are doing?', n)
                    
                # integrate range of frequencies
                f_min = np.maximum(f_min, np.min(f))
                f_max = np.minimum(f_max, np.max(f))
                
                m = scipy.integrate.cumtrapz(E.values * f_mtx**n,
                                             f_mtx, axis=ix_frequency, initial=0)

                vals = []
                if self.has_dimension('time'):
                    k = self._key_lookup('_time')
                    vals.append(coords[k].values.flatten().astype(np.float))
                if self.has_dimension('location'):
                    k = self._key_lookup('_location')
                    vals.append(coords[k].values.flatten().astype(np.float))
                    
                vals.append(f.flatten())
                    
                if self.has_dimension('direction'):
                    k = self._key_lookup('_direction')
                    vals.append(coords[k].values.flatten().astype(np.float))
                
                points = tuple(vals)
                points_min = vals.copy()
                points_min[ix_frequency] = [f_min]
                points_max = vals.copy()
                points_max[ix_frequency] = [f_max]
                
                xi_min = list(zip(*[x.flatten() for x in np.meshgrid(*points_min)]))
                xi_max = list(zip(*[x.flatten() for x in np.meshgrid(*points_max)]))

                m_min = scipy.interpolate.interpn(points, m, xi_min)
                m_max = scipy.interpolate.interpn(points, m, xi_max)
                
                m = (m_max - m_min).reshape([
                    len(x)
                    for i, x in enumerate(vals)
                    if i is not ix_freqency
                ])

            # determine units
            k = self._key_lookup('_energy')
            E_units = self.variables[k].attrs['units']
            k = self._key_lookup('_frequency')
            f_units = self.variables[k].attrs['units']
            units = E_units + ('*((%s)^%d)' % (f_units, n+1))
            units = simplify(units)

            return xr.DataArray(m, dims=dims, coords=coords,
                                attrs=dict(units=units))