You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2486 lines
88 KiB
Python

from __future__ import division
import warnings
import numpy as np
from numpy import (pi, inf, meshgrid, zeros, ones, where, nonzero, #@UnresolvedImport
flatnonzero, ceil, sqrt, exp, log, arctan2, #@UnresolvedImport
tanh, cosh, sinh, random, atleast_1d, maximum, #@UnresolvedImport
minimum, diff, isnan, any, r_, conj, mod, #@UnresolvedImport
hstack, vstack, interp, ravel, finfo, linspace, #@UnresolvedImport
arange, array, nan, newaxis, fliplr, sign) #@UnresolvedImport
from numpy.fft import fft
from scipy.integrate import simps, trapz
from scipy.special import erf
from scipy.linalg import toeplitz
import scipy.interpolate as interpolate
from pylab import stineman_interp
from dispersion_relation import w2k #, k2w
from wafo.wafodata import WafoData, now
from wafo.misc import sub_dict_select, nextpow2, discretize, JITImport
from wafo.gaussian import Rind
from wafo.transform import TrData
from wafo.plotbackend import plotbackend
from wafo import c_library
# Trick to avoid error due to circular import
_WAFOCOV = JITImport('wafo.covariance')
__all__ = ['SpecData1D', 'SpecData2D']
def _set_seed(iseed):
'''Set seed of random generator'''
if iseed != None:
try:
random.set_state(iseed)
except:
random.seed(iseed)
def qtf(w, h=inf, g=9.81):
"""
Return Quadratic Transfer Function
Parameters
------------
w : array-like
angular frequencies
h : scalar
water depth
g : scalar
acceleration of gravity
Returns
-------
h_s = sum frequency effects
h_d = difference frequency effects
h_dii = diagonal of h_d
"""
w = atleast_1d(w)
num_w = w.size
k_w = w2k(w, theta=0, h=h, g=g)[0]
k_1, k_2 = meshgrid(k_w, k_w)
if h == inf: # go here for faster calculations
h_s = 0.25 * (abs(k_1) + abs(k_2))
h_d = -0.25 * abs(abs(k_1) - abs(k_2))
h_dii = zeros(num_w)
return h_s, h_d , h_dii
[w_1, w_2] = meshgrid(w, w)
w12 = (w_1 * w_2)
w1p2 = (w_1 + w_2)
w1m2 = (w_1 - w_2)
k12 = (k_1 * k_2)
k1p2 = (k_1 + k_2)
k1m2 = abs(k_1 - k_2)
if 0: # Langley
p_1 = (-2 * w1p2 * (k12 * g ** 2. - w12 ** 2.) +
w_1 * (w_2 ** 4. - g ** 2 * k_2 ** 2) +
w_2 * (w_1 ** 4 - g * 2. * k_1 ** 2)) / (4. * w12)
p_2 = w1p2 ** 2. * cosh((k1p2) * h) - g * (k1p2) * sinh((k1p2) * h)
h_s = (-p_1 / p_2 * w1p2 * cosh((k1p2) * h) / g -
(k12 * g ** 2 - w12 ** 2.) / (4 * g * w12) +
(w_1 ** 2 + w_2 ** 2) / (4 * g))
p_3 = (-2 * w1m2 * (k12 * g ** 2 + w12 ** 2) -
w_1 * (w_2 ** 4 - g ** 2 * k_2 ** 2) +
w_2 * (w_1 ** 4 - g ** 2 * k_1 ** 2)) / (4. * w12)
p_4 = w1m2 ** 2. * cosh(k1m2 * h) - g * (k1m2) * sinh((k1m2) * h)
h_d = (-p_3 / p_4 * (w1m2) * cosh((k1m2) * h) / g -
(k12 * g ** 2 + w12 ** 2) / (4 * g * w12) +
(w_1 ** 2. + w_2 ** 2.) / (4. * g))
else: # # Marthinsen & Winterstein
tmp1 = 0.5 * g * k12 / w12
tmp2 = 0.25 / g * (w_1 ** 2. + w_2 ** 2. + w12)
h_s = (tmp1 - tmp2 + 0.25 * g * (w_1 * k_2 ** 2. + w_2 * k_1 ** 2) /
(w12 * (w1p2))) / (1. - g * (k1p2) / (w1p2) ** 2. *
tanh((k1p2) * h)) + tmp2 - 0.5 * tmp1 ## OK
tmp2 = 0.25 / g * (w_1 ** 2 + w_2 ** 2 - w12) # #OK
h_d = (tmp1 - tmp2 - 0.25 * g * (w_1 * k_2 ** 2 - w_2 * k_1 ** 2) /
(w12 * (w1m2))) / (1. - g * (k1m2) / (w1m2) ** 2. *
tanh((k1m2) * h)) + tmp2 - 0.5 * tmp1 # # OK
##tmp1 = 0.5*g*k_w./(w.*sqrt(g*h))
##tmp2 = 0.25*w.^2/g
# Wave group velocity
c_g = 0.5 * g * (tanh(k_w * h) + k_w * h * (1.0 - tanh(k_w * h) ** 2)) / w
h_dii = (0.5 * (0.5 * g * (k_w / w) ** 2. - 0.5 * w ** 2 / g +
g * k_w / (w * c_g))
/ (1. - g * h / c_g ** 2.) - 0.5 * k_w / sinh(2 * k_w * h))# # OK
h_d.flat[0::num_w + 1] = h_dii
##k = find(w_1==w_2)
##h_d(k) = h_dii
#% The NaN's occur due to division by zero. => Set the isnans to zero
h_dii = where(isnan(h_dii), 0, h_dii)
h_d = where(isnan(h_d), 0, h_d)
h_s = where(isnan(h_s), 0, h_s)
return h_s, h_d , h_dii
class SpecData1D(WafoData):
"""
Container class for 1D spectrum data objects in WAFO
Member variables
----------------
data : array-like
One sided Spectrum values, size nf
args : array-like
freguency/wave-number-lag values of freqtype, size nf
type : String
spectrum type, one of 'freq', 'k1d', 'enc' (default 'freq')
freqtype : letter
frequency type, one of: 'f', 'w' or 'k' (default 'w')
tr : Transformation function (default (none)).
h : real scalar
Water depth (default inf).
v : real scalar
Ship speed, if type = 'enc'.
norm : bool
Normalization flag, True if S is normalized, False if not
date : string
Date and time of creation or change.
Examples
--------
>>> import numpy as np
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap(Hm0=3)
>>> w = np.linspace(0,4,256)
>>> S1 = Sj.tospecdata(w) #Make spectrum object from numerical values
>>> S = SpecData1D(Sj(w),w) # Alternatively do it manually
See also
--------
WafoData
CovData
"""
def __init__(self, *args, **kwds):
super(SpecData1D, self).__init__(*args, **kwds)
self.name = 'WAFO Spectrum Object'
self.type = 'freq'
self.freqtype = 'w'
self.angletype = ''
self.h = inf
self.tr = None
self.phi = 0.0
self.v = 0.0
self.norm = False
somekeys = ['angletype', 'phi', 'name', 'h', 'tr', 'freqtype', 'v',
'type', 'norm']
self.__dict__.update(sub_dict_select(kwds, somekeys))
self.setlabels()
def tocov_matrix(self, nr=0, nt=None, dt=None):
'''
Computes covariance function and its derivatives, alternative version
Parameters
----------
nr : scalar integer
number of derivatives in output, nr<=4 (default 0)
nt : scalar integer
number in time grid, i.e., number of time-lags.
(default rate*(n_f-1)) where rate = round(1/(2*f(end)*dt)) or
rate = round(pi/(w(n_f)*dt)) depending on S.
dt : real scalar
time spacing for acfmat
Returns
-------
acfmat : [R0, R1,...Rnr], shape Nt+1 x Nr+1
matrix with autocovariance and its derivatives, i.e., Ri (i=1:nr)
are column vectors with the 1'st to nr'th derivatives of R0.
NB! This routine requires that the spectrum grid is equidistant
starting from zero frequency.
Example
-------
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap()
>>> S = Sj.tospecdata()
>>> acfmat = S.tocov_matrix(nr=3, nt=256, dt=0.1)
>>> acfmat[:2,:]
array([[ 3.06075987, 0. , -1.67750289, 0. ],
[ 3.05246132, -0.16662376, -1.66819445, 0.18634189]])
See also
--------
cov,
resample,
objects
'''
ftype = self.freqtype # %options are 'f' and 'w' and 'k'
freq = self.args
n_f = len(freq)
dt_old = self.sampling_period()
if dt is None:
dt = dt_old
rate = 1
else:
rate = max(round(dt_old * 1. / dt), 1.)
if nt is None:
nt = rate * (n_f - 1)
else: #%check if Nt is ok
nt = minimum(nt, rate * (n_f - 1))
checkdt = 1.2 * min(diff(freq)) / 2. / pi
if ftype in 'k':
lagtype = 'x'
else:
lagtype = 't'
if ftype in 'f':
checkdt = checkdt * 2 * pi
msg1 = 'Step dt = %g in computation of the density is too small.' % dt
msg2 = 'Step dt = %g is small, and may cause numerical inaccuracies.' % dt
if (checkdt < 2. ** -16 / dt):
print(msg1)
print('The computed covariance (by FFT(2^K)) may differ from the')
print('theoretical. Solution:')
raise ValueError('use larger dt or sparser grid for spectrum.')
# Calculating covariances
#~~~~~~~~~~~~~~~~~~~~~~~~
spec = self.copy()
spec.resample(dt)
acf = spec.tocovdata(nr, nt, rate=1)
acfmat = zeros((nt + 1, nr + 1), dtype=float)
acfmat[:, 0] = acf.data[0:nt + 1]
fieldname = 'R' + lagtype * nr
for i in range(1, nr + 1):
fname = fieldname[:i + 1]
r_i = getattr(acf, fname)
acfmat[:, i] = r_i[0:nt + 1]
eps0 = 0.0001
if nt + 1 >= 5:
cc2 = acfmat[0, 0] - acfmat[4, 0] * (acfmat[4, 0] / acfmat[0, 0])
if (cc2 < eps0):
warnings.warn(msg1)
cc1 = acfmat[0, 0] - acfmat[1, 0] * (acfmat[1, 0] / acfmat[0, 0])
if (cc1 < eps0):
warnings.warn(msg2)
return acfmat
def tocovdata(self, nr=0, nt=None, rate=None):
'''
Computes covariance function and its derivatives
Parameters
----------
nr : number of derivatives in output, nr<=4 (default = 0).
nt : number in time grid, i.e., number of time-lags
(default rate*(length(S.data)-1)).
rate = 1,2,4,8...2**r, interpolation rate for R
(default = 1, no interpolation)
Returns
-------
R : CovData1D
auto covariance function
The input 'rate' gives together with the spectrum
the time-grid-spacing: dt=pi/(S.w[-1]*rate), S.w[-1] is the Nyquist freq.
This results in the time-grid: 0:dt:Nt*dt.
What output is achieved with different S and choices of Nt,Nx and Ny:
1) S.type='freq' or 'dir', Nt set, Nx,Ny not set: then result R(time) (one-dim)
2) S.type='k1d' or 'k2d', Nt set, Nx,Ny not set: then result R(x) (one-dim)
3) Any type, Nt and Nx set =>R(x,time); Nt and Ny set =>R(y,time)
4) Any type, Nt, Nx and Ny set => R(x,y,time)
5) Any type, Nt not set, Nx and/or Ny set => Nt set to default, goto 3) or 4)
NB! This routine requires that the spectrum grid is equidistant
starting from zero frequency.
NB! If you are using a model spectrum, spec, with sharp edges
to calculate covariances then you should probably round off the sharp
edges like this:
Example:
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap()
>>> S = Sj.tospecdata()
>>> S.data[0:40] = 0.0
>>> S.data[100:-1] = 0.0
>>> Nt = len(S.data)-1
>>> acf = S.tocovdata(nr=0, nt=Nt)
R = spec2cov(spec,0,Nt)
win = parzen(2*Nt+1)
R.data = R.data.*win(Nt+1:end)
S1 = cov2spec(acf)
R2 = spec2cov(S1)
figure(1)
plotspec(S),hold on, plotspec(S1,'r')
figure(2)
covplot(R), hold on, covplot(R2,[],[],'r')
figure(3)
semilogy(abs(R2.data-R.data)), hold on,
semilogy(abs(S1.data-S.data)+1e-7,'r')
See also
--------
cov2spec
'''
freq = self.args
n_f = len(freq)
if freq[0] > 0:
txt = '''Spectrum does not start at zero frequency/wave number.
Correct it with resample, for example.'''
raise ValueError(txt)
d_w = abs(diff(freq, n_f=2, axis=0))
if any(d_w > 1.0e-8):
txt = '''Not equidistant frequencies/wave numbers in spectrum.
Correct it with resample, for example.'''
raise ValueError(txt)
if rate is None:
rate = 1 # %interpolation rate
elif rate > 16:
rate = 16
else: # make sure rate is a power of 2
rate = 2 ** nextpow2(rate)
if nt is None:
nt = rate * (n_f - 1)
else: #check if Nt is ok
nt = minimum(nt, rate * (n_f - 1))
spec = self.copy()
if self.freqtype in 'k':
lagtype = 'x'
else:
lagtype = 'time'
d_t = spec.sampling_period()
#normalize spec so that sum(specn)/(n_f-1)=acf(0)=var(X)
specn = spec.data * freq[-1]
if spec.freqtype in 'f':
w = freq * 2 * pi
else:
w = freq
nfft = rate * 2 ** nextpow2(2 * n_f - 2)
# periodogram
rper = r_[specn, zeros(nfft - (2 * n_f) + 2), conj(specn[n_f - 1:0:-1])]
time = r_[0:nt + 1] * d_t * (2 * n_f - 2) / nfft
r = fft(rper, nfft).real / (2 * n_f - 2)
acf = _WAFOCOV.CovData1D(r[0:nt + 1], time, lagtype=lagtype)
acf.tr = spec.tr
acf.h = spec.h
acf.norm = spec.norm
if nr > 0:
w = r_[w , zeros(nfft - 2 * n_f + 2) , -w[n_f - 1:0:-1] ]
fieldname = 'R' + lagtype * nr
for i in range(1, nr + 1):
rper = -1j * w * rper
d_acf = fft(rper, nfft).real / (2 * n_f - 2)
setattr(acf, fieldname[0:i + 1], d_acf[0:nt + 1])
return acf
def to_linspec(self, ns=None, dt=None, cases=20, iseed=None,
fn_limit=sqrt(2), gravity=9.81):
'''
Split the linear and non-linear component from the Spectrum
according to 2nd order wave theory
Returns
-------
SL, SN : SpecData1D objects
with linear and non-linear components only, respectively.
Parameters
----------
ns : scalar integer
giving ns load points. (default length(S)-1=n-1).
If np>n-1 it is assummed that S(k)=0 for all k>n-1
cases : scalar integer
number of cases (default=20)
dt : real scalar
step in grid (default dt is defined by the Nyquist freq)
iseed : scalar integer
starting seed number for the random number generator
(default none is set)
fnLimit : real scalar
normalized upper frequency limit of spectrum for 2'nd order
components. The frequency is normalized with
sqrt(gravity*tanh(kbar*water_depth)/Amax)/(2*pi)
(default sqrt(2), i.e., Convergence criterion).
Generally this should be the same as used in the final
non-linear simulation (see example below).
SPEC2LINSPEC separates the linear and non-linear component of the
spectrum according to 2nd order wave theory. This is useful when
simulating non-linear waves because:
If the spectrum does not decay rapidly enough towards zero, the
contribution from the 2nd order wave components at the upper tail can
be very large and unphysical. Another option to ensure convergence of
the perturbation series in the simulation, is to truncate the upper tail
of the spectrum at FNLIMIT in the calculation of the 2nd order wave
components, i.e., in the calculation of sum and difference frequency effects.
Example:
--------
np = 10000;
iseed = 1;
pflag = 2;
S = jonswap(10);
fnLimit = inf;
[SL,SN] = spec2linspec(S,np,[],[],fnLimit);
x0 = spec2nlsdat(SL,8*np,[],iseed,[],fnLimit);
x1 = spec2nlsdat(S,8*np,[],iseed,[],fnLimit);
x2 = spec2nlsdat(S,8*np,[],iseed,[],sqrt(2));
Se0 = dat2spec(x0);
Se1 = dat2spec(x1);
Se2 = dat2spec(x2);
clf
plotspec(SL,'r',pflag), % Linear components
hold on
plotspec(S,'b',pflag) % target spectrum for simulated data
plotspec(Se0,'m',pflag), % approx. same as S
plotspec(Se1,'g',pflag) % unphysical spectrum
plotspec(Se2,'k',pflag) % approx. same as S
axis([0 10 -80 0])
hold off
See also
--------
spec2nlsdat
References
----------
P. A. Brodtkorb (2004),
The probability of Occurrence of dangerous Wave Situations at Sea.
Dr.Ing thesis, Norwegian University of Science and Technolgy, NTNU,
Trondheim, Norway.
Nestegaard, A and Stokka T (1995)
A Third Order Random Wave model.
In proc.ISOPE conf., Vol III, pp 136-142.
R. S Langley (1987)
A statistical analysis of non-linear random waves.
Ocean Engng, Vol 14, pp 389-407
Marthinsen, T. and Winterstein, S.R (1992)
'On the skewness of random surface waves'
In proc. ISOPE Conf., San Francisco, 14-19 june.
'''
# by pab 13.08.2002
# TODO % Replace inputs with options structure
# TODO % Can be improved further.
method = 'apstochastic'
trace = 1 #% trace the convergence
max_sim = 30
tolerance = 5e-4
L = 200 #%maximum lag size of the window function used in
#%spectral estimate
#ftype = self.freqtype #options are 'f' and 'w' and 'k'
# switch ftype
# case 'f',
# ftype = 'w';
# S = ttspec(S,ftype);
# end
Hm0 = self.characteristic('Hm0')
Tm02 = self.characteristic('Tm02')
if not iseed is None:
_set_seed(iseed) #% set the the seed
n = len(self.data)
if ns is None:
ns = max(n - 1, 5000)
if dt is None:
S = self.interp(dt) # interpolate spectrum
else:
S = self.copy()
ns = ns + mod(ns, 2) # make sure np is even
water_depth = abs(self.h);
kbar = w2k(2 * pi / Tm02, 0, water_depth)[0]
# Expected maximum amplitude for 1000 waves seastate
num_waves = 10000
Amax = sqrt(2 * log(num_waves)) * Hm0 / 4
fLimitLo = sqrt(gravity * tanh(kbar * water_depth) * Amax / water_depth ** 3);
freq = S.args
eps = finfo(float).eps
freq[-1] = freq[-1] - sqrt(eps)
Hw2 = 0
SL = S
indZero = nonzero(freq < fLimitLo)[0]
if len(indZero):
SL.data[indZero] = 0
maxS = max(S.data);
#Fs = 2*freq(end)+eps; % sampling frequency
for ix in xrange(max_sim):
[x2, x1] = spec2nlsdat(SL, [np, cases], [], iseed, method, fnLimit)
#%x2(:,2:end) = x2(:,2:end) -x1(:,2:end);
S2 = dat2spec(x2, L)
S1 = dat2spec(x1, L)
#%[tf21,fi] = tfe(x2(:,2),x1(:,2),1024,Fs,[],512);
#%Hw11 = interp1q(fi,tf21.*conj(tf21),freq);
if True:
Hw1 = exp(interp1q(S2.args, log(abs(S1.data / S2.data)), freq))
else:
# Geometric mean
Hw1 = exp((interp1q(S2.args, log(abs(S1.data / S2.data)), freq) + log(Hw2)) / 2)
#end
#Hw1 = (interp1q( S2.w,abs(S1.S./S2.S),freq)+Hw2)/2;
#plot(freq, abs(Hw11-Hw1),'g')
#title('diff')
#pause
#clf
#d1 = interp1q( S2.w,S2.S,freq);;
SL.data = (Hw1 * S.data)
if len(indZero):
SL.data[indZero] = 0
#end
k = nonzero(SL.data < 0)[0]
if len(k): # Make sure that the current guess is larger than zero
#%k
#Hw1(k)
Hw1[k] = min(S1.data[k] * 0.9, S.data[k])
SL.data[k] = max(Hw1[k] * S.data[k], eps)
#end
Hw12 = Hw1 - Hw2
maxHw12 = max(abs(Hw12))
if trace == 1:
plotbackend.figure(1),
plotbackend.semilogy(freq, Hw1, 'r')
plotbackend.title('Hw')
plotbackend.figure(2),
plotbackend.semilogy(freq, abs(Hw12), 'r')
plotbackend.title('Hw-HwOld')
#pause(3)
plotbackend.figure(1),
plotbackend.semilogy(freq, Hw1, 'b')
plotbackend.title('Hw')
plotbackend.figure(2),
plotbackend.semilogy(freq, abs(Hw12), 'b')
plotbackend.title('Hw-HwOld')
#figtile
#end
print('Iteration : %d, Hw12 : %g Hw12/maxS : %g' % (ix, maxHw12, (maxHw12 / maxS)))
if (maxHw12 < maxS * tolerance) and (Hw1[-1] < Hw2[-1]) :
break
#end
Hw2 = Hw1
#end
#%Hw1(end)
#%maxS*1e-3
#%if Hw1(end)*S.>maxS*1e-3,
#% warning('The Nyquist frequency of the spectrum may be too low')
#%end
SL.date = now() #datestr(now)
#if nargout>1
SN = SL.copy()
SN.data = S.data - SL.data
SN.note = SN.note + ' non-linear component (spec2linspec)'
#end
SL.note = SL.note + ' linear component (spec2linspec)'
return SL, SN
def to_t_pdf(self, u=None, pdef='Tc', paramt=None, **options):
'''
Density of crest/trough- period or length, version 2.
Parameters
----------
u : real scalar
reference level (default the most frequently crossed level).
pdef : string, 'Tc', Tt', 'Lc' or 'Lt'
'Tc', gives half wave period, Tc (default).
'Tt', gives half wave period, Tt
'Lc' and 'Lt' ditto for wave length.
paramt : [t0, tn, nt]
where t0, tn and nt is the first value, last value and the number
of points, respectively, for which the density will be computed.
paramt= [5, 5, 51] implies that the density is computed only for
T=5 and using 51 equidistant points in the interval [0,5].
options : optional parameters
controlling the performance of the integration. See Rind for details.
Notes
-----
SPEC2TPDF2 calculates pdf of halfperiods Tc, Tt, Lc or Lt
in a stationary Gaussian transform process X(t),
where Y(t) = g(X(t)) (Y zero-mean Gaussian with spectrum given in S).
The transformation, g, can be estimated using LC2TR,
DAT2TR, HERMITETR or OCHITR.
Example
-------
The density of Tc is computed by:
>>> import pylab as plb
>>> from wafo.spectrum import models as sm
>>> w = np.linspace(0,3,100)
>>> Sj = sm.Jonswap()
>>> S = Sj.tospecdata()
>>> f = S.to_t_pdf(pdef='Tc', paramt=(0, 10, 51), speed=7)
>>> h = f.plot()
estimated error bounds
>>> h2 = plb.plot(f.args, f.data+f.err, 'r', f.args, f.data-f.err, 'r')
>>> plb.close('all')
See also
--------
Rind, spec2cov2, specnorm, dat2tr, dat2gaus,
definitions.wave_periods,
definitions.waves
'''
opts = dict(speed=9)
opts.update(options)
if pdef[0] in ('l', 'L'):
if self.type != 'k1d':
raise ValueError('Must be spectrum of type: k1d')
elif pdef[0] in ('t', 'T'):
if self.type != 'freq':
raise ValueError('Must be spectrum of type: freq')
else:
raise ValueError('pdef must be Tc,Tt or Lc, Lt')
# if strncmpi('l',def,1)
# spec=spec2spec(spec,'k1d')
# elseif strncmpi('t',def,1)
# spec=spec2spec(spec,'freq')
# else
# error('Unknown def')
# end
pdef2defnr = dict(tc=1, lc=1, tt= -1, lt= -1)
defnr = pdef2defnr[pdef.lower()]
S = self.copy()
S.normalize()
m, unused_mtxt = self.moment(nr=2, even=True)
A = sqrt(m[0] / m[1])
if self.tr is None:
y = linspace(-5, 5, 513)
#g = _wafotransform.
g = TrData(y, sqrt(m[0]) * y)
else:
g = self.tr
if u is None:
u = g.gauss2dat(0) #% most frequently crossed level
# transform reference level into Gaussian level
un = g.dat2gauss(u)
#disp(['The level u for Gaussian process = ', num2str(u)])
if paramt is None:
#% z2 = u^2/2
z = -sign(defnr) * un / sqrt(2)
expectedMaxPeriod = 2 * ceil(2 * pi * A * exp(z) * (0.5 + erf(z) / 2))
paramt = [0, expectedMaxPeriod, 51]
t0 = paramt[0]
tn = paramt[1]
Ntime = paramt[2]
t = linspace(0, tn / A, Ntime) #normalized times
Nstart = max(round(t0 / tn * (Ntime - 1)), 1) #% index to starting point to
#% evaluate
dt = t[1] - t[0]
nr = 2
R = S.tocov_matrix(nr, Ntime - 1, dt)
#R = spec2cov2(S,nr,Ntime-1,dt)
xc = vstack((un, un))
indI = -ones(4, dtype=int)
Nd = 2
Nc = 2
XdInf = 100.e0 * sqrt(-R[0, 2])
XtInf = 100.e0 * sqrt(R[0, 0])
B_up = hstack([un + XtInf, XdInf, 0])
B_lo = hstack([un, 0, -XdInf])
#%INFIN = [1 1 0]
#BIG = zeros((Ntime+2,Ntime+2))
ex = zeros(Ntime + 2, dtype=float)
#%CC = 2*pi*sqrt(-R(1,1)/R(1,3))*exp(un^2/(2*R(1,1)))
#% XcScale = log(CC)
opts['xcscale'] = log(2 * pi * sqrt(-R[0, 0] / R[0, 2])) + (un ** 2 / (2 * R[0, 0]))
f = zeros(Ntime, dtype=float)
err = zeros(Ntime, dtype=float)
rind = Rind(**opts)
#h11 = fwaitbar(0,[],sprintf('Please wait ...(start at: %s)',datestr(now)))
for pt in xrange(Nstart, Ntime):
Nt = pt - Nd + 1
Ntd = Nt + Nd
Ntdc = Ntd + Nc
indI[1] = Nt - 1
indI[2] = Nt
indI[3] = Ntd - 1
#% positive wave period
BIG = self._covinput(pt, R)
tmp = rind(BIG, ex[:Ntdc], B_lo, B_up, indI, xc, Nt)
f[pt], err[pt] = tmp[:2]
#fwaitbar(pt/Ntime,h11,sprintf('%s Ready: %d of %d',datestr(now),pt,Ntime))
#end
#close(h11)
titledict = dict(tc='Density of Tc', tt='Density of Tt', lc='Density of Lc', lt='Density of Lt')
Htxt = titledict.get(pdef.lower())
if pdef[0].lower() == 'l':
xtxt = 'wave length [m]'
else:
xtxt = 'period [s]'
Htxt = '%s_{v =%2.5g}' % (Htxt, u)
pdf = WafoData(f / A, t * A, title=Htxt, xlab=xtxt)
pdf.err = err / A
pdf.u = u
pdf.options = opts
return pdf
def _covinput(self, pt, R):
"""
Return covariance matrix for Tc or Tt period problems
Parameters
----------
pt : scalar integer
time
R : array-like, shape Ntime x 3
[R0,R1,R2] column vectors with autocovariance and its derivatives,
i.e., Ri (i=1:2) are vectors with the 1'st and 2'nd derivatives of R0.
The order of the variables in the covariance matrix are organized as follows:
For pt>1:
||X(t2)..X(ts),..X(tn-1)|| X'(t1) X'(tn)|| X(t1) X(tn) ||
= [Xt Xd Xc]
where
Xt = time points in the indicator function
Xd = derivatives
Xc=variables to condition on
Computations of all covariances follows simple rules:
Cov(X(t),X(s))=r(t,s),
then Cov(X'(t),X(s))=dr(t,s)/dt. Now for stationary X(t) we have
a function r(tau) such that Cov(X(t),X(s))=r(s-t) (or r(t-s) will give
the same result).
Consequently
Cov(X'(t),X(s)) = -r'(s-t) = -sign(s-t)*r'(|s-t|)
Cov(X'(t),X'(s)) = -r''(s-t) = -r''(|s-t|)
Cov(X''(t),X'(s)) = r'''(s-t) = sign(s-t)*r'''(|s-t|)
Cov(X''(t),X(s)) = r''(s-t) = r''(|s-t|)
Cov(X''(t),X''(s)) = r''''(s-t) = r''''(|s-t|)
"""
# cov(Xd)
Sdd = -toeplitz(R[[0, pt], 2])
# cov(Xc)
Scc = toeplitz(R[[0, pt], 0])
# cov(Xc,Xd)
Scd = array([[0, R[pt, 1]], [ -R[pt, 1], 0]])
if pt > 1 :
#%cov(Xt)
Stt = toeplitz(R[:pt - 1, 0]) # Cov(X(tn),X(ts)) = r(ts-tn) = r(|ts-tn|)
#%cov(Xc,Xt)
Sct = R[1:pt, 0] # Cov(X(tn),X(ts)) = r(ts-tn) = r(|ts-tn|)
Sct = vstack((Sct, Sct[::-1]))
#%Cov(Xd,Xt)
Sdt = -R[1:pt, 1] # Cov(X'(t1),X(ts)) = -r'(ts-t1) = r(|s-t|)
Sdt = vstack((Sdt, -Sdt[::-1]))
#N = pt + 3
big = vstack((hstack((Stt, Sdt.T, Sct.T)),
hstack((Sdt, Sdd, Scd.T)),
hstack((Sct, Scd, Scc))))
else:
#N = 4
big = vstack((hstack((Sdd, Scd.T)),
hstack((Scd, Scc))))
return big
def to_specnorm(self):
S = self.copy()
S.normalize()
return S
def sim(self, ns=None, cases=1, dt=None, iseed=None, method='random', derivative=False):
''' Simulates a Gaussian process and its derivative from spectrum
Parameters
----------
ns : scalar
number of simulated points. (default length(spec)-1=n-1).
If ns>n-1 it is assummed that acf(k)=0 for all k>n-1
cases : scalar
number of replicates (default=1)
dt : scalar
step in grid (default dt is defined by the Nyquist freq)
iseed : int or state
starting state/seed number for the random number generator
(default none is set)
method : string
if 'exact' : simulation using cov2sdat
if 'random' : random phase and amplitude simulation (default)
derivative : bool
if true : return derivative of simulated signal as well
otherwise
Returns
-------
xs = a cases+1 column matrix ( t,X1(t) X2(t) ...).
xsder = a cases+1 column matrix ( t,X1'(t) X2'(t) ...).
Details
-------
Performs a fast and exact simulation of stationary zero mean
Gaussian process through circulant embedding of the covariance matrix
or by summation of sinus functions with random amplitudes and random
phase angle.
If the spectrum has a non-empty field .tr, then the transformation is
applied to the simulated data, the result is a simulation of a transformed
Gaussian process.
Note: The method 'exact' simulation may give high frequency ripple when
used with a small dt. In this case the method 'random' works better.
Example:
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap();S = Sj.tospecdata()
>>> ns =100; dt = .2
>>> x1 = S.sim(ns,dt=dt)
>>> import numpy as np
>>> import scipy.stats as st
>>> x2 = S.sim(20000,20)
>>> truth1 = [0,np.sqrt(S.moment(1)[0]),0., 0.]
>>> funs = [np.mean,np.std,st.skew,st.kurtosis]
>>> for fun,trueval in zip(funs,truth1):
... res = fun(x2[:,1::],axis=0)
... m = res.mean()
... sa = res.std()
... assert(np.abs(m-trueval)<sa)
waveplot(x1,'r',x2,'g',1,1)
See also
--------
cov2sdat, gaus2dat
Reference
-----------
C.S Dietrich and G. N. Newsam (1997)
"Fast and exact simulation of stationary
Gaussian process through circulant embedding
of the Covariance matrix"
SIAM J. SCI. COMPT. Vol 18, No 4, pp. 1088-1107
Hudspeth, S.T. and Borgman, L.E. (1979)
"Efficient FFT simulation of Digital Time sequences"
Journal of the Engineering Mechanics Division, ASCE, Vol. 105, No. EM2,
'''
spec = self.copy()
if dt is not None:
spec.resample(dt)
ftype = spec.freqtype
freq = spec.args
d_t = spec.sampling_period()
Nt = freq.size
if ns is None:
ns = Nt - 1
if method in 'exact':
#nr=0,Nt=None,dt=None
acf = spec.tocovdata(nr=0)
T = Nt * d_t
i = flatnonzero(acf.args > T)
# Trick to avoid adding high frequency noise to the spectrum
if i.size > 0:
acf.data[i[0]::] = 0.0
return acf.sim(ns=ns, cases=cases, iseed=iseed, derivative=derivative)
_set_seed(iseed)
ns = ns + mod(ns, 2) # make sure it is even
f_i = freq[1:-1]
s_i = spec.data[1:-1]
if ftype in ('w', 'k'):
fact = 2. * pi
s_i = s_i * fact
f_i = f_i / fact
x = zeros((ns, cases + 1))
d_f = 1 / (ns * d_t)
# interpolate for freq. [1:(N/2)-1]*d_f and create 2-sided, uncentered spectra
f = arange(1, ns / 2.) * d_f
f_u = hstack((0., f_i, d_f * ns / 2.))
s_u = hstack((0., abs(s_i) / 2., 0.))
s_i = interp(f, f_u, s_u)
s_u = hstack((0., s_i, 0, s_i[(ns / 2) - 2::-1]))
del(s_i, f_u)
# Generate standard normal random numbers for the simulations
randn = random.randn
z_r = randn((ns / 2) + 1, cases)
z_i = vstack((zeros((1, cases)), randn((ns / 2) - 1, cases), zeros((1, cases))))
amp = zeros((ns, cases), dtype=complex)
amp[0:(ns / 2 + 1), :] = z_r - 1j *z_i
del(z_r, z_i)
amp[(ns / 2 + 1):ns, :] = amp[ns / 2 - 1:0:-1, :].conj()
amp[0, :] = amp[0, :]*sqrt(2.)
amp[(ns / 2), :] = amp[(ns / 2), :]*sqrt(2.)
# Make simulated time series
T = (ns - 1) * d_t
Ssqr = sqrt(s_u * d_f / 2.)
# stochastic amplitude
amp = amp * Ssqr[:, newaxis]
# Deterministic amplitude
#amp = sqrt[1]*Ssqr(:,ones(1,cases)).*exp(sqrt(-1)*atan2(imag(amp),real(amp)))
del(s_u, Ssqr)
x[:, 1::] = fft(amp, axis=0).real
x[:, 0] = linspace(0, T, ns) #' %(0:d_t:(np-1)*d_t).'
if derivative:
xder = zeros(ns, cases + 1)
w = 2. * pi * hstack((0, f, 0., -f[-1::-1]))
amp = -1j * amp * w[:, newaxis]
xder[:, 1:(cases + 1)] = fft(amp, axis=0).real
xder[:, 0] = x[:, 0]
if spec.tr is not None:
print(' Transforming data.')
g = spec.tr
G = fliplr(g) #% the invers of g
if derivative:
for i in range(cases):
tmp = tranproc(hstack((x[:, i + 1], xder[:, i + 1])), G)
x[:, i + 1] = tmp[:, 0]
xder[:, i + 1] = tmp[:, 1]
else:
for i in range(cases):
x[:, i + 1] = tranproc(x[:, i + 1], G)
if derivative:
return x, xder
else:
return x
# function [x2,x,svec,dvec,amp]=spec2nlsdat(spec,np,dt,iseed,method,truncationLimit)
def sim_nl(self, ns=None, cases=1, dt=None, iseed=None, method='random',
fnlimit=1.4142, reltol=1e-3, g=9.81):
"""
Simulates a Randomized 2nd order non-linear wave X(t)
Parameters
----------
ns : scalar
number of simulated points. (default length(spec)-1=n-1).
If ns>n-1 it is assummed that R(k)=0 for all k>n-1
cases : scalar
number of replicates (default=1)
dt : scalar
step in grid (default dt is defined by the Nyquist freq)
iseed : int or state
starting state/seed number for the random number generator
(default none is set)
method : string
'apStochastic' : Random amplitude and phase (default)
'aDeterministic' : Deterministic amplitude and random phase
'apDeterministic' : Deterministic amplitude and phase
fnlimit : scalar
normalized upper frequency limit of spectrum for 2'nd order
components. The frequency is normalized with
sqrt(gravity*tanh(kbar*water_depth)/amp_max)/(2*pi)
(default sqrt(2), i.e., Convergence criterion [1]_).
Other possible values are:
sqrt(1/2) : No bump in trough criterion
sqrt(pi/7) : Wave steepness criterion
reltol : scalar
relative tolerance defining where to truncate spectrum for the
sum and difference frequency effects
Returns
-------
xs2 = a cases+1 column matrix ( t,X1(t) X2(t) ...).
xs1 = a cases+1 column matrix ( t,X1'(t) X2'(t) ...).
Details
-------
Performs a Fast simulation of Randomized 2nd order non-linear
waves by summation of sinus functions with random amplitudes and
phase angles. The extent to which the simulated result are applicable
to real seastates are dependent on the validity of the assumptions:
1. Seastate is unidirectional
2. Surface elevation is adequately represented by 2nd order random
wave theory
3. The first order component of the surface elevation is a Gaussian
random process.
If the spectrum does not decay rapidly enough towards zero, the
contribution from the 2nd order wave components at the upper tail can
be very large and unphysical. To ensure convergence of the perturbation
series, the upper tail of the spectrum is truncated at FNLIMIT in the
calculation of the 2nd order wave components, i.e., in the calculation
of sum and difference frequency effects. This may also be combined with
the elimination of second order effects from the spectrum, i.e., extract
the linear components from the spectrum. One way to do this is to use
SPEC2LINSPEC.
Example
--------
np =100; dt = .2
[x1, x2] = spec2nlsdat(jonswap,np,dt)
waveplot(x1,'r',x2,'g',1,1)
See also
--------
spec2linspec, spec2sdat, cov2sdat
References
----------
.. [1] Nestegaard, amp and Stokka T (1995)
amp Third Order Random Wave model.
In proc.ISOPE conf., Vol III, pp 136-142.
.. [2] R. spec Langley (1987)
amp statistical analysis of non-linear random waves.
Ocean Engng, Vol 14, pp 389-407
.. [3] Marthinsen, T. and Winterstein, spec.R (1992)
'On the skewness of random surface waves'
In proc. ISOPE Conf., San Francisco, 14-19 june.
"""
# TODO % Check the methods: 'apdeterministic' and 'adeterministic'
Hm0, Tm02 = self.characteristic(['Hm0', 'Tm02'])[0].tolist()
_set_seed(iseed)
spec = self.copy()
if dt is not None:
spec.resample(dt)
ftype = spec.freqtype
freq = spec.args
d_t = spec.sampling_period()
Nt = freq.size
if ns is None:
ns = Nt - 1
ns = ns + mod(ns, 2) # make sure it is even
f_i = freq[1:-1]
s_i = spec.data[1:-1]
if ftype in ('w', 'k'):
fact = 2. * pi
s_i = s_i * fact
f_i = f_i / fact
s_max = max(s_i)
water_depth = min(abs(spec.h), 10. ** 30)
x = zeros((ns, cases + 1))
df = 1 / (ns * d_t)
# interpolate for freq. [1:(N/2)-1]*df and create 2-sided, uncentered spectra
f = arange(1, ns / 2.) * df
f_u = hstack((0., f_i, df * ns / 2.))
w = 2. * pi * hstack((0., f, df * ns / 2.))
kw = w2k(w , 0., water_depth, g)[0]
s_u = hstack((0., abs(s_i) / 2., 0.))
s_i = interp(f, f_u, s_u)
nmin = (s_i > s_max * reltol).argmax()
nmax = flatnonzero(s_i > 0).max()
s_u = hstack((0., s_i, 0, s_i[(ns / 2) - 2::-1]))
del(s_i, f_u)
# Generate standard normal random numbers for the simulations
randn = random.randn
z_r = randn((ns / 2) + 1, cases)
z_i = vstack((zeros((1, cases)),
randn((ns / 2) - 1, cases),
zeros((1, cases))))
amp = zeros((ns, cases), dtype=complex)
amp[0:(ns / 2 + 1), :] = z_r - 1j * z_i
del(z_r, z_i)
amp[(ns / 2 + 1):ns, :] = amp[ns / 2 - 1:0:-1, :].conj()
amp[0, :] = amp[0, :]*sqrt(2.)
amp[(ns / 2), :] = amp[(ns / 2), :]*sqrt(2.)
# Make simulated time series
T = (ns - 1) * d_t
Ssqr = sqrt(s_u * df / 2.)
if method.startswith('apd') : # apdeterministic
# Deterministic amplitude and phase
amp[1:(ns / 2), :] = amp[1, 0]
amp[(ns / 2 + 1):ns, :] = amp[1, 0].conj()
amp = sqrt(2) * Ssqr[:, newaxis] * exp(1J * arctan2(amp.imag, amp.real))
elif method.startswith('ade'): # adeterministic
# Deterministic amplitude and random phase
amp = sqrt(2) * Ssqr[:, newaxis] * exp(1J * arctan2(amp.imag, amp.real))
else:
# stochastic amplitude
amp = amp * Ssqr[:, newaxis]
# Deterministic amplitude
#amp = sqrt(2)*Ssqr(:,ones(1,cases)).*exp(sqrt(-1)*atan2(imag(amp),real(amp)))
del(s_u, Ssqr)
x[:, 1::] = fft(amp, axis=0).real
x[:, 0] = linspace(0, T, ns) #' %(0:d_t:(np-1)*d_t).'
x2 = x.copy()
# If the spectrum does not decay rapidly enough towards zero, the
# contribution from the wave components at the upper tail can be very
# large and unphysical.
# To ensure convergence of the perturbation series, the upper tail of
# the spectrum is truncated in the calculation of sum and difference
# frequency effects.
# Find the critical wave frequency to ensure convergence.
num_waves = 1000. # Typical number of waves in 3 hour seastate
kbar = w2k(2. * pi / Tm02, 0., water_depth)[0]
amp_max = sqrt(2 * log(num_waves)) * Hm0 / 4 #% Expected maximum amplitude for 1000 waves seastate
f_limit_up = fnlimit * sqrt(g * tanh(kbar * water_depth) / amp_max) / (2 * pi)
f_limit_lo = sqrt(g * tanh(kbar * water_depth) * amp_max / water_depth) / (2 * pi * water_depth)
nmax = min(flatnonzero(f <= f_limit_up).max(), nmax) + 1
nmin = max(flatnonzero(f_limit_lo <= f).min(), nmin) + 1
#if isempty(nmax),nmax = np/2end
#if isempty(nmin),nmin = 2end % Must always be greater than 1
f_limit_up = df * nmax
f_limit_lo = df * nmin
print('2nd order frequency Limits = %g,%g' % (f_limit_lo, f_limit_up))
## if nargout>3,
## %compute the sum and frequency effects separately
## [svec, dvec] = disufq((amp.'),w,kw,min(h,10^30),g,nmin,nmax)
## svec = svec.'
## dvec = dvec.'
##
## x2s = fft(svec) % 2'nd order sum frequency component
## x2d = fft(dvec) % 2'nd order difference frequency component
##
## % 1'st order + 2'nd order component.
## x2(:,2:end) =x(:,2:end)+ real(x2s(1:np,:))+real(x2d(1:np,:))
## else
amp = amp.T
rvec, ivec = c_library.disufq(amp.real, amp.imag, w, kw, water_depth, g, nmin, nmax, cases, ns)
svec = rvec + 1J * ivec
svec.shape = (cases, ns)
x2o = fft(svec, axis=1).T # 2'nd order component
# 1'st order + 2'nd order component.
x2[:, 1::] = x[:, 1::] + x2o[0:ns, :].real
return x2, x
def stats_nl(self, h=None, moments='sk', method='approximate', g=9.81):
"""
Statistics of 2'nd order waves to the leading order.
Parameters
----------
h : scalar
water depth (default self.h)
moments : string (default='sk')
composed of letters ['mvsk'] specifying which moments to compute:
'm' = mean,
'v' = variance,
's' = (Fisher's) skew,
'k' = (Fisher's) kurtosis.
method : string
'approximate' method due to Marthinsen & Winterstein (default)
'eigenvalue' method due to Kac and Siegert
Skewness = kurtosis-3 = 0 for a Gaussian process.
The mean, sigma, skewness and kurtosis are determined as follows:
method == 'approximate': due to Marthinsen and Winterstein
mean = 2 * int Hd(w1,w1)*S(w1) dw1
sigma = sqrt(int S(w1) dw1)
skew = 6 * int int [Hs(w1,w2)+Hd(w1,w2)]*S(w1)*S(w2) dw1*dw2/m0^(3/2)
kurt = (4*skew/3)^2
where Hs = sum frequency effects and Hd = difference frequency effects
method == 'eigenvalue'
mean = sum(E)
sigma = sqrt(sum(C^2)+2*sum(E^2))
skew = sum((6*C^2+8*E^2).*E)/sigma^3
kurt = 3+48*sum((C^2+E^2).*E^2)/sigma^4
where
h1 = sqrt(S*dw/2)
C = (ctranspose(V)*[h1;h1])
and E and V is the eigenvalues and eigenvectors, respectively, of the 2'order
transfer matrix. S is the spectrum and dw is the frequency spacing of S.
Example:
--------
#Simulate a Transformed Gaussian process:
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap()
>>> S = Sj.tospecdata()
>>> me, va, sk, ku = S.stats_nl(moments='mvsk')
Hm0=7;Tp=11
S = jonswap([],[Hm0 Tp]); [sk, ku, me]=spec2skew(S)
g=hermitetr([],[Hm0/4 sk ku me]); g2=[g(:,1), g(:,2)*Hm0/4]
ys = spec2sdat(S,15000) % Simulated in the Gaussian world
xs = gaus2dat(ys,g2) % Transformed to the real world
See also
---------
hermitetr, ochitr, lc2tr, dat2tr
References:
-----------
Langley, RS (1987)
'A statistical analysis of nonlinear random waves'
Ocean Engineering, Vol 14, No 5, pp 389-407
Marthinsen, T. and Winterstein, S.R (1992)
'On the skewness of random surface waves'
In proceedings of the 2nd ISOPE Conference, San Francisco, 14-19 june.
Winterstein, S.R, Ude, T.C. and Kleiven, G. (1994)
'Springing and slow drift responses:
predicted extremes and fatigue vs. simulation'
In Proc. 7th International behaviour of Offshore structures, (BOSS)
Vol. 3, pp.1-15
"""
#% default options
if h is None:
h = self.h
#S = ttspec(S,'w')
w = ravel(self.args)
S = ravel(self.data)
if self.freqtype in ['f', 'w']:
vari = 't'
if self.freqtype == 'f':
w = 2. * pi * w
S = S / (2. * pi)
#m0 = self.moment(nr=0)
m0 = simps(S, w)
sa = sqrt(m0)
Nw = w.size
Hs, Hd, Hdii = qtf(w, h, g)
#%return
#%skew=6/sqrt(m0)^3*simpson(S.w,simpson(S.w,(Hs+Hd).*S1(:,ones(1,Nw))).*S1.')
Hspd = trapz(trapz((Hs + Hd) * S[newaxis, :], w) * S, w)
output = []
if method[0] == 'a': # %approx : Marthinsen, T. and Winterstein, S.R (1992) method
if 'm' in moments:
output.append(2. * trapz(Hdii * S, w))
if 'v' in moments:
output.append(m0)
skew = 6. / sa ** 3 * Hspd
if 's' in moments:
output.append(skew)
if 'k' in moments:
output.append((4. * skew / 3.) ** 2. + 3.)
else:
raise ValueError('Unknown option!')
## elif method[0]== 'q': #, #% quasi method
## Fn = self.nyquist_freq()
## dw = Fn/Nw
## tmp1 =sqrt(S[:,newaxis]*S[newaxis,:])*dw
## Hd = Hd*tmp1
## Hs = Hs*tmp1
## k = 6
## stop = 0
## while !stop:
## E = eigs([Hd,Hs;Hs,Hd],[],k)
## %stop = (length(find(abs(E)<1e-4))>0 | k>1200)
## %stop = (any(abs(E(:))<1e-4) | k>1200)
## stop = (any(abs(E(:))<1e-4) | k>=min(2*Nw,1200))
## k = min(2*k,2*Nw)
## #end
##
##
## m02=2*sum(E.^2) % variance of 2'nd order contribution
##
## %Hstd = 16*trapz(S.w,(Hdii.*S1).^2)
## %Hstd = trapz(S.w,trapz(S.w,((Hs+Hd)+ 2*Hs.*Hd).*S1(:,ones(1,Nw))).*S1.')
## ma = 2*trapz(S.w,Hdii.*S1)
## %m02 = Hstd-ma^2% variance of second order part
## sa = sqrt(m0+m02)
## skew = 6/sa^3*Hspd
## kurt = (4*skew/3).^2+3
## elif method[0]== 'e': #, % Kac and Siegert eigenvalue analysis
## Fn = self.nyquist_freq()
## dw = Fn/Nw
## tmp1 =sqrt(S[:,newaxis]*S[newaxis,:])*dw
## Hd = Hd*tmp1
## Hs = Hs*tmp1
## k = 6
## stop = 0
##
##
## while (not stop):
## [V,D] = eigs([Hd,HsHs,Hd],[],k)
## E = diag(D)
## %stop = (length(find(abs(E)<1e-4))>0 | k>=min(2*Nw,1200))
## stop = (any(abs(E(:))<1e-4) | k>=min(2*Nw,1200))
## k = min(2*k,2*Nw)
## #end
##
##
## h1 = sqrt(S*dw/2)
## C = (ctranspose(V)*[h1;h1])
##
## E2 = E.^2
## C2 = C.^2
##
## ma = sum(E) % mean
## sa = sqrt(sum(C2)+2*sum(E2)) % standard deviation
## skew = sum((6*C2+8*E2).*E)/sa^3 % skewness
## kurt = 3+48*sum((C2+E2).*E2)/sa^4 % kurtosis
return output
def moment(self, nr=2, even=True, j=0):
''' Calculates spectral moments from spectrum
Parameters
----------
nr : int
order of moments (recomended maximum 4)
even : bool
False for all moments,
True for only even orders
j : int
0 or 1
Returns
-------
m : list of moments
mtext : list of strings describing the elements of m, see below
Details
-------
Calculates spectral moments of up to order NR by use of
Simpson-integration.
/ /
mj_t^i = | w^i S(w)^(j+1) dw, or mj_x^i = | k^i S(k)^(j+1) dk
/ /
where k=w^2/gravity, i=0,1,...,NR
The strings in output mtext have the same position in the list
as the corresponding numerical value has in output m
Notation in mtext: 'm0' is the variance,
'm0x' is the first-order moment in x,
'm0xx' is the second-order moment in x,
'm0t' is the first-order moment in t,
etc.
For the calculation of moments see Baxevani et al.
Example:
>>> import numpy as np
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap(Hm0=3)
>>> w = np.linspace(0,4,256)
>>> S = SpecData1D(Sj(w),w) #Make spectrum object from numerical values
>>> S.moment()
([0.56220770033914191, 0.35433180985851975], ['m0', 'm0tt'])
References
----------
Baxevani A. et al. (2001)
Velocities for Random Surfaces
'''
one_dim_spectra = ['freq', 'enc', 'k1d']
if self.type not in one_dim_spectra:
raise ValueError('Unknown spectrum type!')
f = ravel(self.args)
S = ravel(self.data)
if self.freqtype in ['f', 'w']:
vari = 't'
if self.freqtype == 'f':
f = 2. * pi * f
S = S / (2. * pi)
else:
vari = 'x'
S1 = abs(S) ** (j + 1.)
m = [simps(S1, x=f)]
mtxt = 'm%d' % j
mtext = [mtxt]
step = mod(even, 2) + 1
df = f ** step
for i in range(step, nr + 1, step):
S1 = S1 * df
m.append(simps(S1, x=f))
mtext.append(mtxt + vari * i)
return m, mtext
def nyquist_freq(self):
"""
Return Nyquist frequency
"""
return self.args[-1]
def sampling_period(self):
''' Returns sampling interval from Nyquist frequency of spectrum
Returns
---------
dT : scalar
sampling interval, unit:
[m] if wave number spectrum,
[s] otherwise
Let wm be maximum frequency/wave number in spectrum,
then dT=pi/wm if angular frequency, dT=1/(2*wm) if natural frequency (Hz)
Example
-------
S = jonswap
dt = spec2dt(S)
See also
'''
if self.freqtype in 'f':
wmdt = 0.5 # Nyquist to sampling interval factor
else: # ftype == w og ftype == k
wmdt = pi
wm = self.args[-1] #Nyquist frequency
dt = wmdt / wm #sampling interval = 1/Fs
return dt
def resample(self, dt=None, Nmin=0, Nmax=2 ** 13 + 1, method='stineman'):
'''
Interpolate and zero-padd spectrum to change Nyquist freq.
Parameters
----------
dt : scalar
wanted sampling interval (default as given by S, see spec2dt)
unit: [s] if frequency-spectrum, [m] if wave number spectrum
Nmin : scalar
minimum number of frequencies.
Nmax : scalar
minimum number of frequencies
method : string
interpolation method (options are 'linear', 'cubic' or 'stineman')
To be used before simulation (e.g. spec2sdat) or evaluation of covariance
function (spec2cov) to directly get wanted sampling interval.
The input spectrum is interpolated and padded with zeros to reach
the right max-frequency, w(end)=pi/dt, f(end)=1/(2*dt), or k(end)=pi/dt.
The objective is that output frequency grid should be at least as dense
as the input grid, have equidistant spacing and length equal to
2^k+1 (>=Nmin). If the max frequency is changed, the number of points
in the spectrum is maximized to 2^13+1.
Note: Also zero-padding down to zero freq, if S does not start there.
If empty input dt, this is the only effect.
See also
--------
spec2cov, spec2sdat, covinterp, spec2dt
'''
ftype = self.freqtype
w = self.args.ravel()
n = w.size
#%doInterpolate = 0
if ftype == 'f':
Cnf2dt = 0.5 # Nyquist to sampling interval factor
else: #% ftype == w og ftype == k
Cnf2dt = pi
wnOld = w[-1] # Old Nyquist frequency
dTold = Cnf2dt / wnOld # sampling interval=1/Fs
if dt is None:
dt = dTold
# Find how many points that is needed
nfft = 2 ** nextpow2(max(n - 1, Nmin - 1))
dttest = dTold * (n - 1) / nfft
while (dttest > dt) and (nfft < Nmax - 1):
nfft = nfft * 2
dttest = dTold * (n - 1) / nfft
nfft = nfft + 1
wnNew = Cnf2dt / dt #% New Nyquist frequency
dWn = wnNew - wnOld
doInterpolate = dWn > 0 or w[1] > 0 or (nfft != n) or dt != dTold or any(abs(diff(w, axis=0)) > 1.0e-8)
if doInterpolate > 0:
S1 = self.data
dw = min(diff(w))
if dWn > 0:
#% add a zero just above old max-freq, and a zero at new max-freq
#% to get correct interpolation there
Nz = 1 + (dWn > dw) # % Number of zeros to add
if Nz == 2:
w = hstack((w, wnOld + dw, wnNew))
else:
w = hstack((w, wnNew))
S1 = hstack((S1, zeros(Nz)))
if w[0] > 0:
#% add a zero at freq 0, and, if there is space, a zero just below min-freq
Nz = 1 + (w[0] > dw) #% Number of zeros to add
if Nz == 2:
w = hstack((0, w[0] - dw, w))
else:
w = hstack((0, w))
S1 = hstack((zeros(Nz), S1))
#% Do a final check on spacing in order to check that the gridding is
#% sufficiently dense:
#np1 = S1.size
dwMin = finfo(float).max
#%wnc = min(wnNew,wnOld-1e-5)
wnc = wnNew
specfun = lambda xi : stineman_interp(xi, w, S1)
x, unused_y = discretize(specfun, 0, wnc)
dwMin = minimum(min(diff(x)), dwMin)
newNfft = 2 ** nextpow2(ceil(wnNew / dwMin)) + 1
if newNfft > nfft:
if (nfft <= 2 ** 15 + 1) and (newNfft > 2 ** 15 + 1):
warnings.warn('Spectrum matrix is very large (>33k). Memory problems may occur.')
nfft = newNfft
self.args = linspace(0, wnNew, nfft)
if method == 'stineman':
self.data = stineman_interp(self.args, w, S1)
else:
intfun = interpolate.interp1d(w, S1, kind=method)
self.data = intfun(self.args)
self.data = self.data.clip(0) # clip negative values to 0
def normalize(self, gravity=9.81):
'''
Normalize a spectral density such that m0=m2=1
Paramter
--------
gravity=9.81
Notes
-----
Normalization performed such that
INT S(freq) dfreq = 1 INT freq^2 S(freq) dfreq = 1
where integration limits are given by freq and S(freq) is the
spectral density; freq can be frequency or wave number.
The normalization is defined by
A=sqrt(m0/m2); B=1/A/m0; freq'=freq*A; S(freq')=S(freq)*B
If S is a directional spectrum then a normalized gravity (.g) is added
to Sn, such that mxx normalizes to 1, as well as m0 and mtt.
(See spec2mom for notation of moments)
If S is complex-valued cross spectral density which has to be
normalized, then m0, m2 (suitable spectral moments) should be given.
Example:
-------
S = jonswap
[Sn,mn4] = specnorm(S)
mts = spec2mom(S,2) % Should be equal to one!
'''
mom, unused_mtext = self.moment(nr=4, even=True)
m0 = mom[0]
m2 = mom[1]
m4 = mom[2]
SM0 = sqrt(m0)
SM2 = sqrt(m2)
A = SM0 / SM2
B = SM2 / (SM0 * m0)
if self.freqtype == 'f':
self.args = self.args * A / 2 / pi
self.data = self.data * B * 2 * pi
elif self.freqtype == 'w' :
self.args = self.args * A
self.data = self.data * B
m02 = m4 / gravity ** 2
m20 = m02
self.g = gravity * sqrt(m0 * m20) / m2
self.A = A
self.norm = True
self.date = now()
def bandwidth(self, factors=0):
'''
Return some spectral bandwidth and irregularity factors
Parameters
-----------
factors : array-like
Input vector 'factors' correspondence:
0 alpha=m2/sqrt(m0*m4) (irregularity factor)
1 eps2 = sqrt(m0*m2/m1^2-1) (narrowness factor)
2 eps4 = sqrt(1-m2^2/(m0*m4))=sqrt(1-alpha^2) (broadness factor)
3 Qp=(2/m0^2)int_0^inf f*S(f)^2 df (peakedness factor)
Returns
--------
bw : arraylike
vector of bandwidth factors
Order of output is the same as order in 'factors'
Example:
>>> import numpy as np
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap(Hm0=3)
>>> w = np.linspace(0,4,256)
>>> S = SpecData1D(Sj(w),w) #Make spectrum object from numerical values
>>> S.bandwidth([0,1,2,3])
array([ 0.65354446, 0.3975428 , 0.75688813, 2.00207912])
'''
# if self.freqtype in 'k':
# vari = 'k'
# else:
# vari = 'w'
m, unused_mtxt = self.moment(nr=4, even=False)
fact = atleast_1d(factors)
alpha = m[2] / sqrt(m[0] * m[4])
eps2 = sqrt(m[0] * m[2] / m[1] ** 2. - 1.)
eps4 = sqrt(1. - m[2] ** 2. / m[0] / m[4])
f = self.args
S = self.data
Qp = 2 / m[0] ** 2. * simps(f * S ** 2, x=f)
bw = array([alpha, eps2, eps4, Qp])
return bw[fact]
def characteristic(self, fact='Hm0', T=1200, g=9.81):
"""
Returns spectral characteristics and their covariance
Parameters
----------
fact : vector with factor integers or a string or a list of strings
defining spectral characteristic, see description below.
T : scalar
recording time (sec) (default 1200 sec = 20 min)
g : scalar
acceleration of gravity [m/s^2]
Returns
-------
ch : vector
of spectral characteristics
R : matrix
of the corresponding covariances given T
chtext : a list of strings
describing the elements of ch, see example.
Description
------------
If input spectrum is of wave number type, output are factors for
corresponding 'k1D', else output are factors for 'freq'.
Input vector 'factors' correspondence:
1 Hm0 = 4*sqrt(m0) Significant wave height
2 Tm01 = 2*pi*m0/m1 Mean wave period
3 Tm02 = 2*pi*sqrt(m0/m2) Mean zero-crossing period
4 Tm24 = 2*pi*sqrt(m2/m4) Mean period between maxima
5 Tm_10 = 2*pi*m_1/m0 Energy period
6 Tp = 2*pi/{w | max(S(w))} Peak period
7 Ss = 2*pi*Hm0/(g*Tm02^2) Significant wave steepness
8 Sp = 2*pi*Hm0/(g*Tp^2) Average wave steepness
9 Ka = abs(int S(w)*exp(i*w*Tm02) dw ) /m0 Groupiness parameter
10 Rs = (S(0.092)+S(0.12)+S(0.15)/(3*max(S(w))) Quality control parameter
11 Tp1 = 2*pi*int S(w)^4 dw Peak Period (robust estimate for Tp)
------------------
int w*S(w)^4 dw
12 alpha = m2/sqrt(m0*m4) Irregularity factor
13 eps2 = sqrt(m0*m2/m1^2-1) Narrowness factor
14 eps4 = sqrt(1-m2^2/(m0*m4))=sqrt(1-alpha^2) Broadness factor
15 Qp = (2/m0^2)int_0^inf w*S(w)^2 dw Peakedness factor
Order of output is same as order in 'factors'
The covariances are computed with a Taylor expansion technique
and is currently only available for factors 1, 2, and 3. Variances
are also available for factors 4,5,7,12,13,14 and 15
Quality control:
----------------
Critical value for quality control parameter Rs is Rscrit = 0.02
for surface displacement records and Rscrit=0.0001 for records of
surface acceleration or slope. If Rs > Rscrit then probably there
are something wrong with the lower frequency part of S.
Ss may be used as an indicator of major malfunction, by checking that
it is in the range of 1/20 to 1/16 which is the usual range for
locally generated wind seas.
Examples:
---------
>>> import numpy as np
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap(Hm0=5)
>>> S = Sj.tospecdata() #Make spectrum ob
>>> S.characteristic(1)
(array([ 8.59007646]), array([[ 0.03040216]]), ['Tm01'])
>>> [ch, R, txt] = S.characteristic([1,2,3]) # fact a vector of integers
>>> S.characteristic('Ss') # fact a string
(array([ 0.04963112]), array([[ 2.63624782e-06]]), ['Ss'])
>>> S.characteristic(['Hm0','Tm02']) # fact a list of strings
(array([ 4.99833578, 8.03139757]), array([[ 0.05292989, 0.02511371],
[ 0.02511371, 0.0274645 ]]), ['Hm0', 'Tm02'])
See also
---------
bandwidth,
moment
References
----------
Krogstad, H.E., Wolf, J., Thompson, S.P., and Wyatt, L.R. (1999)
'Methods for intercomparison of wave measurements'
Coastal Enginering, Vol. 37, pp. 235--257
Krogstad, H.E. (1982)
'On the covariance of the periodogram'
Journal of time series analysis, Vol. 3, No. 3, pp. 195--207
Tucker, M.J. (1993)
'Recommended standard for wave data sampling and near-real-time processing'
Ocean Engineering, Vol.20, No.5, pp. 459--474
Young, I.R. (1999)
"Wind generated ocean waves"
Elsevier Ocean Engineering Book Series, Vol. 2, pp 239
"""
#% TODO % Need more checking on computing the variances for Tm24,alpha, eps2 and eps4
#% TODO % Covariances between Tm24,alpha, eps2 and eps4 variables are also needed
tfact = dict(Hm0=0, Tm01=1, Tm02=2, Tm24=3, Tm_10=4, Tp=5, Ss=6, Sp=7, Ka=8,
Rs=9, Tp1=10, Alpha=11, Eps2=12, Eps4=13, Qp=14)
tfact1 = ('Hm0', 'Tm01', 'Tm02', 'Tm24', 'Tm_10', 'Tp', 'Ss', 'Sp', 'Ka',
'Rs', 'Tp1', 'Alpha', 'Eps2', 'Eps4', 'Qp')
if isinstance(fact, str):
fact = list((fact,))
if isinstance(fact, (list, tuple)):
nfact = []
for k in fact:
if isinstance(k, str):
nfact.append(tfact.get(k.capitalize(), 15))
else:
nfact.append(k)
else:
nfact = fact
nfact = atleast_1d(nfact)
if any((nfact > 14) | (nfact < 0)):
raise ValueError('Factor outside range (0,...,14)')
#vari = self.freqtype
f = self.args.ravel()
S1 = self.data.ravel()
m, unused_mtxt = self.moment(nr=4, even=False)
#% moments corresponding to freq in Hz
for k in range(1, 5):
m[k] = m[k] / (2 * pi) ** k
#pi = np.pi
ind = flatnonzero(f > 0)
m.append(simps(S1[ind] / f[ind], f[ind]) * 2. * pi) # % = m_1
m_10 = simps(S1[ind] ** 2 / f[ind], f[ind]) * (2 * pi) ** 2 / T # % = COV(m_1,m0|T=t0)
m_11 = simps(S1[ind] ** 2. / f[ind] ** 2, f[ind]) * (2 * pi) ** 3 / T #% = COV(m_1,m_1|T=t0)
#sqrt = np.sqrt
#% Hm0 Tm01 Tm02 Tm24 Tm_10
Hm0 = 4. * sqrt(m[0])
Tm01 = m[0] / m[1]
Tm02 = sqrt(m[0] / m[2])
Tm24 = sqrt(m[2] / m[4])
Tm_10 = m[5] / m[0]
Tm12 = m[1] / m[2]
ind = S1.argmax()
maxS = S1[ind]
#[maxS ind] = max(S1)
Tp = 2. * pi / f[ind] # % peak period /length
Ss = 2. * pi * Hm0 / g / Tm02 ** 2 # % Significant wave steepness
Sp = 2. * pi * Hm0 / g / Tp ** 2 # % Average wave steepness
Ka = abs(simps(S1 * exp(1J * f * Tm02), f)) / m[0] #% groupiness factor
#% Quality control parameter
#% critical value is approximately 0.02 for surface displacement records
#% If Rs>0.02 then there are something wrong with the lower frequency part
#% of S.
Rs = np.sum(interp(r_[0.0146, 0.0195, 0.0244] * 2 * pi, f, S1)) / 3. / maxS
Tp2 = 2 * pi * simps(S1 ** 4, f) / simps(f * S1 ** 4, f)
alpha1 = Tm24 / Tm02 # % m(3)/sqrt(m(1)*m(5))
eps2 = sqrt(Tm01 / Tm12 - 1.)# % sqrt(m(1)*m(3)/m(2)^2-1)
eps4 = sqrt(1. - alpha1 ** 2) # % sqrt(1-m(3)^2/m(1)/m(5))
Qp = 2. / m[0] ** 2 * simps(f * S1 ** 2, f)
ch = r_[Hm0, Tm01, Tm02, Tm24, Tm_10, Tp, Ss, Sp, Ka, Rs, Tp2, alpha1, eps2, eps4, Qp]
#% Select the appropriate values
ch = ch[nfact]
chtxt = [tfact1[i] for i in nfact]
#if nargout>1,
#% covariance between the moments:
#%COV(mi,mj |T=t0) = int f^(i+j)*S(f)^2 df/T
mij, unused_mijtxt = self.moment(nr=8, even=False, j=1)
for ix, tmp in enumerate(mij):
mij[ix] = tmp / T / ((2. * pi) ** (ix - 1.0))
#% and the corresponding variances for
#%{'hm0', 'tm01', 'tm02', 'tm24', 'tm_10','tp','ss', 'sp', 'ka', 'rs', 'tp1','alpha','eps2','eps4','qp'}
R = r_[4 * mij[0] / m[0],
mij[0] / m[1] ** 2. - 2. * m[0] * mij[1] / m[1] ** 3. + m[0] ** 2. * mij[2] / m[1] ** 4.,
0.25 * (mij[0] / (m[0] * m[2]) - 2. * mij[2] / m[2] ** 2 + m[0] * mij[4] / m[2] ** 3),
0.25 * (mij[4] / (m[2] * m[4]) - 2 * mij[6] / m[4] ** 2 + m[2] * mij[8] / m[4] ** 3) ,
m_11 / m[0] ** 2 + (m[5] / m[0] ** 2) ** 2 * mij[0] - 2 * m[5] / m[0] ** 3 * m_10,
nan,
(8 * pi / g) ** 2 * (m[2] ** 2 / (4 * m[0] ** 3) * mij[0] + mij[4] / m[0] - m[2] / m[0] ** 2 * mij[2]),
nan * ones(4),
m[2] ** 2 * mij[0] / (4 * m[0] ** 3 * m[4]) + mij[4] / (m[0] * m[4]) + mij[8] * m[2] ** 2 / (4 * m[0] * m[4] ** 3) -
m[2] * mij[2] / (m[0] ** 2 * m[4]) + m[2] ** 2 * mij[4] / (2 * m[0] ** 2 * m[4] ** 2) - m[2] * mij[6] / m[0] / m[4] ** 2,
(m[2] ** 2 * mij[0] / 4 + (m[0] * m[2] / m[1]) ** 2 * mij[2] + m[0] ** 2 * mij[4] / 4 - m[2] ** 2 * m[0] * mij[1] / m[1] +
m[0] * m[2] * mij[2] / 2 - m[0] ** 2 * m[2] / m[1] * mij[3]) / eps2 ** 2 / m[1] ** 4,
(m[2] ** 2 * mij[0] / (4 * m[0] ** 2) + mij[4] + m[2] ** 2 * mij[8] / (4 * m[4] ** 2) - m[2] * mij[2] / m[0] +
m[2] ** 2 * mij[4] / (2 * m[0] * m[4]) - m[2] * mij[6] / m[4]) * m[2] ** 2 / (m[0] * m[4] * eps4) ** 2,
nan]
#% and covariances by a taylor expansion technique:
#% Cov(Hm0,Tm01) Cov(Hm0,Tm02) Cov(Tm01,Tm02)
S0 = r_[ 2. / (sqrt(m[0]) * m[1]) * (mij[0] - m[0] * mij[1] / m[1]),
1. / sqrt(m[2]) * (mij[0] / m[0] - mij[2] / m[2]),
1. / (2 * m[1]) * sqrt(m[0] / m[2]) * (mij[0] / m[0] - mij[2] / m[2] - mij[1] / m[1] + m[0] * mij[3] / (m[1] * m[2]))]
R1 = ones((15, 15))
R1[:, :] = nan
for ix, Ri in enumerate(R):
R1[ix, ix] = Ri
R1[0, 2:4] = S0[:2]
R1[1, 2] = S0[2]
for ix in [0, 1]: #%make lower triangular equal to upper triangular part
R1[ix + 1:, ix] = R1[ix, ix + 1:]
R = R[nfact]
R1 = R1[nfact, :][:, nfact]
#% Needs further checking:
#% Var(Tm24)= 0.25*(mij[4]/(m[2]*m[4])-2*mij[6]/m[4]**2+m[2]*mij[8]/m[4]**3) ...
return ch, R1, chtxt
def setlabels(self):
''' Set automatic title, x-,y- and z- labels on SPECDATA object
based on type, angletype, freqtype
'''
N = len(self.type)
if N == 0:
raise ValueError('Object does not appear to be initialized, it is empty!')
labels = ['', '', '']
if self.type.endswith('dir'):
title = 'Directional Spectrum'
if self.freqtype.startswith('w'):
labels[0] = 'Frequency [rad/s]'
labels[2] = 'S(w,\theta) [m^2 s / rad^2]'
else:
labels[0] = 'Frequency [Hz]'
labels[2] = 'S(f,\theta) [m^2 s / rad]'
if self.angletype.startswith('r'):
labels[1] = 'Wave directions [rad]'
elif self.angletype.startswith('d'):
labels[1] = 'Wave directions [deg]'
elif self.type.endswith('freq'):
title = 'Spectral density'
if self.freqtype.startswith('w'):
labels[0] = 'Frequency [rad/s]'
labels[1] = 'S(w) [m^2 s/ rad]'
else:
labels[0] = 'Frequency [Hz]'
labels[1] = 'S(f) [m^2 s]'
else:
title = 'Wave Number Spectrum'
labels[0] = 'Wave number [rad/m]'
if self.type.endswith('k1d'):
labels[1] = 'S(k) [m^3/ rad]'
elif self.type.endswith('k2d'):
labels[1] = labels[0]
labels[2] = 'S(k1,k2) [m^4/ rad^2]'
else:
raise ValueError('Object does not appear to be initialized, it is empty!')
if self.norm != 0:
title = 'Normalized ' + title
labels[0] = 'Normalized ' + labels[0].split('[')[0]
if not self.type.endswith('dir'):
labels[1] = labels[1].split('[')[0]
labels[2] = labels[2].split('[')[0]
self.labels.title = title
self.labels.xlab = labels[0]
self.labels.ylab = labels[1]
self.labels.zlab = labels[2]
class SpecData2D(WafoData):
""" Container class for 2D spectrum data objects in WAFO
Member variables
----------------
data : array_like
args : vector for 1D, list of vectors for 2D, 3D, ...
type : string
spectrum type (default 'freq')
freqtype : letter
frequency type (default 'w')
angletype : string
angle type of directional spectrum (default 'radians')
Examples
--------
>>> import numpy as np
>>> import wafo.spectrum.models as sm
>>> Sj = sm.Jonswap(Hm0=3)
>>> w = np.linspace(0,4,256)
>>> S = SpecData1D(Sj(w),w) #Make spectrum object from numerical values
See also
--------
WafoData
CovData
"""
def __init__(self, *args, **kwds):
super(SpecData2D, self).__init__(*args, **kwds)
self.name = 'WAFO Spectrum Object'
self.type = 'freq'
self.freqtype = 'w'
self.angletype = ''
self.h = inf
self.tr = None
self.phi = 0.
self.v = 0.
self.norm = 0
somekeys = ['angletype', 'phi', 'name', 'h', 'tr', 'freqtype', 'v', 'type', 'norm']
self.__dict__.update(sub_dict_select(kwds, somekeys))
if self.type.endswith('dir') and self.angletype == '':
self.angletype = 'radians'
self.setlabels()
def toacf(self):
pass
def sim(self):
pass
def sim_nl(self):
pass
def rotate(self):
pass
def moment(self, nr=2, vari='xt', even=True):
'''
Calculates spectral moments from spectrum
Parameters
----------
nr : int
order of moments (maximum 4)
vari : string
variables in model, optional when two-dim.spectrum,
string with 'x' and/or 'y' and/or 't'
even : bool
False for all moments,
True for only even orders
Returns
-------
m : list of moments
mtext : list of strings describing the elements of m, see below
Details
-------
Calculates spectral moments of up to order four by use of
Simpson-integration.
//
m_jkl=|| k1^j*k2^k*w^l S(w,th) dw dth
//
where k1=w^2/gravity*cos(th-phi), k2=w^2/gravity*sin(th-phi)
and phi is the angle of the rotation in S.phi. If the spectrum
has field .g, gravity is replaced by S.g.
The strings in output mtext have the same position in the cell array
as the corresponding numerical value has in output m
Notation in mtext: 'm0' is the variance,
'mx' is the first-order moment in x,
'mxx' is the second-order moment in x,
'mxt' is the second-order cross moment between x and t,
'myyyy' is the fourth-order moment in y
etc.
For the calculation of moments see Baxevani et al.
Example:
S=demospec('dir')
[m,mtext]=spec2mom(S,2,'xyt')
References
----------
Baxevani A. et al. (2001)
Velocities for Random Surfaces
'''
##% Tested on: Matlab 6.0
##% Tested on: Matlab 5.3
##% History:
##% Revised by I.R. 04.04.2001: Introducing the rotation angle phi.
##% Revised by A.B. 23.05.2001: Correcting 'mxxyy' and introducing
##% 'mxxyt','mxyyt' and 'mxytt'.
##% Revised by A.B. 21.10.2001: Correcting 'mxxyt'.
##% Revised by A.B. 21.10.2001: Adding odd-order moments.
##% By es 27.08.1999
pi = pi
two_dim_spectra = ['dir', 'encdir', 'k2d']
if self.type not in two_dim_spectra:
raise ValueError('Unknown 2D spectrum type!')
## if (vari==None and nr<=1:
## vari='x'
## elif vari==None:
## vari='xt'
## else #% secure the mutual order ('xyt')
## vari=''.join(sorted(vari.lower()))
## Nv=len(vari)
##
## if vari[0]=='t' and Nv>1:
## vari = vari[1::]+ vari[0]
##
## Nv = len(vari)
##
## if not self.type.endswith('dir'):
## S1 = self.tospecdata(self.type[:-2]+'dir')
## else:
## S1 = self
## w = ravel(S1.args[0])
## theta = S1.args[1]-S1.phi
## S = S1.data
## Sw = simps(S,x=theta)
## m = [simps(Sw,x=w)]
## mtext=['m0']
##
## if nr>0:
##
## nw=w.size
## if strcmpi(vari(1),'x')
## Sc=simpson(th,S1.S.*(cos(th)*ones(1,nw))).'
## % integral S*cos(th) dth
## end
## if strcmpi(vari(1),'y')
## Ss=simpson(th,S1.S.*(sin(th)*ones(1,nw))).'
## % integral S*sin(th) dth
## if strcmpi(vari(1),'x')
## Sc=simpson(th,S1.S.*(cos(th)*ones(1,nw))).'
## end
## end
## if ~isfield(S1,'g')
## S1.g=gravity
## end
## kx=w.^2/S1.g(1) % maybe different normalization in x and y => diff. g
## ky=w.^2/S1.g(end)
##
## if Nv>=1
## switch vari
## case 'x'
## vec = kx.*Sc
## mtext(end+1)={'mx'}
## case 'y'
## vec = ky.*Ss
## mtext(end+1)={'my'}
## case 't'
## vec = w.*Sw
## mtext(end+1)={'mt'}
## end
## else
## vec = [kx.*Sc ky.*Ss w*Sw]
## mtext(end+(1:3))={'mx', 'my', 'mt'}
## end
## if nr>1
## if strcmpi(vari(1),'x')
## Sc=simpson(th,S1.S.*(cos(th)*ones(1,nw))).'
## % integral S*cos(th) dth
## Sc2=simpson(th,S1.S.*(cos(th).^2*ones(1,nw))).'
## % integral S*cos(th)^2 dth
## end
## if strcmpi(vari(1),'y')||strcmpi(vari(2),'y')
## Ss=simpson(th,S1.S.*(sin(th)*ones(1,nw))).'
## % integral S*sin(th) dth
## Ss2=simpson(th,S1.S.*(sin(th).^2*ones(1,nw))).'
## % integral S*sin(th)^2 dth
## if strcmpi(vari(1),'x')
## Scs=simpson(th,S1.S.*((cos(th).*sin(th))*ones(1,nw))).'
## % integral S*cos(th)*sin(th) dth
## end
## end
## if ~isfield(S1,'g')
## S1.g=gravity
## end
##
## if Nv==2
## switch vari
## case 'xy'
## vec=[kx.*Sc ky.*Ss kx.^2.*Sc2 ky.^2.*Ss2 kx.*ky.*Scs]
## mtext(end+(1:5))={'mx','my','mxx', 'myy', 'mxy'}
## case 'xt'
## vec=[kx.*Sc w.*Sw kx.^2.*Sc2 w.^2.*Sw kx.*w.*Sc]
## mtext(end+(1:5))={'mx','mt','mxx', 'mtt', 'mxt'}
## case 'yt'
## vec=[ky.*Ss w.*Sw ky.^2.*Ss2 w.^2.*Sw ky.*w.*Ss]
## mtext(end+(1:5))={'my','mt','myy', 'mtt', 'myt'}
## end
## else
## vec=[kx.*Sc ky.*Ss w.*Sw kx.^2.*Sc2 ky.^2.*Ss2 w.^2.*Sw kx.*ky.*Scs kx.*w.*Sc ky.*w.*Ss]
## mtext(end+(1:9))={'mx','my','mt','mxx', 'myy', 'mtt', 'mxy', 'mxt', 'myt'}
## end
## if nr>3
## if strcmpi(vari(1),'x')
## Sc3=simpson(th,S1.S.*(cos(th).^3*ones(1,nw))).'
## % integral S*cos(th)^3 dth
## Sc4=simpson(th,S1.S.*(cos(th).^4*ones(1,nw))).'
## % integral S*cos(th)^4 dth
## end
## if strcmpi(vari(1),'y')||strcmpi(vari(2),'y')
## Ss3=simpson(th,S1.S.*(sin(th).^3*ones(1,nw))).'
## % integral S*sin(th)^3 dth
## Ss4=simpson(th,S1.S.*(sin(th).^4*ones(1,nw))).'
## % integral S*sin(th)^4 dth
## if strcmpi(vari(1),'x') %both x and y
## Sc2s=simpson(th,S1.S.*((cos(th).^2.*sin(th))*ones(1,nw))).'
## % integral S*cos(th)^2*sin(th) dth
## Sc3s=simpson(th,S1.S.*((cos(th).^3.*sin(th))*ones(1,nw))).'
## % integral S*cos(th)^3*sin(th) dth
## Scs2=simpson(th,S1.S.*((cos(th).*sin(th).^2)*ones(1,nw))).'
## % integral S*cos(th)*sin(th)^2 dth
## Scs3=simpson(th,S1.S.*((cos(th).*sin(th).^3)*ones(1,nw))).'
## % integral S*cos(th)*sin(th)^3 dth
## Sc2s2=simpson(th,S1.S.*((cos(th).^2.*sin(th).^2)*ones(1,nw))).'
## % integral S*cos(th)^2*sin(th)^2 dth
## end
## end
## if Nv==2
## switch vari
## case 'xy'
## vec=[vec kx.^4.*Sc4 ky.^4.*Ss4 kx.^3.*ky.*Sc3s ...
## kx.^2.*ky.^2.*Sc2s2 kx.*ky.^3.*Scs3]
## mtext(end+(1:5))={'mxxxx','myyyy','mxxxy','mxxyy','mxyyy'}
## case 'xt'
## vec=[vec kx.^4.*Sc4 w.^4.*Sw kx.^3.*w.*Sc3 ...
## kx.^2.*w.^2.*Sc2 kx.*w.^3.*Sc]
## mtext(end+(1:5))={'mxxxx','mtttt','mxxxt','mxxtt','mxttt'}
## case 'yt'
## vec=[vec ky.^4.*Ss4 w.^4.*Sw ky.^3.*w.*Ss3 ...
## ky.^2.*w.^2.*Ss2 ky.*w.^3.*Ss]
## mtext(end+(1:5))={'myyyy','mtttt','myyyt','myytt','myttt'}
## end
## else
## vec=[vec kx.^4.*Sc4 ky.^4.*Ss4 w.^4.*Sw kx.^3.*ky.*Sc3s ...
## kx.^2.*ky.^2.*Sc2s2 kx.*ky.^3.*Scs3 kx.^3.*w.*Sc3 ...
## kx.^2.*w.^2.*Sc2 kx.*w.^3.*Sc ky.^3.*w.*Ss3 ...
## ky.^2.*w.^2.*Ss2 ky.*w.^3.*Ss kx.^2.*ky.*w.*Sc2s ...
## kx.*ky.^2.*w.*Scs2 kx.*ky.*w.^2.*Scs]
## mtext(end+(1:15))={'mxxxx','myyyy','mtttt','mxxxy','mxxyy',...
## 'mxyyy','mxxxt','mxxtt','mxttt','myyyt','myytt','myttt','mxxyt','mxyyt','mxytt'}
##
## end % if Nv==2 ... else ...
## end % if nr>3
## end % if nr>1
## m=[m simpson(w,vec)]
## end % if nr>0
## % end %%if Nv==1... else... to be removed
## end % ... else two-dim spectrum
def interp(self):
pass
def normalize(self):
pass
def bandwidth(self):
pass
def setlabels(self):
''' Set automatic title, x-,y- and z- labels on SPECDATA object
based on type, angletype, freqtype
'''
N = len(self.type)
if N == 0:
raise ValueError('Object does not appear to be initialized, it is empty!')
labels = ['', '', '']
if self.type.endswith('dir'):
title = 'Directional Spectrum'
if self.freqtype.startswith('w'):
labels[0] = 'Frequency [rad/s]'
labels[2] = 'S(w,\theta) [m**2 s / rad**2]'
else:
labels[0] = 'Frequency [Hz]'
labels[2] = 'S(f,\theta) [m**2 s / rad]'
if self.angletype.startswith('r'):
labels[1] = 'Wave directions [rad]'
elif self.angletype.startswith('d'):
labels[1] = 'Wave directions [deg]'
elif self.type.endswith('freq'):
title = 'Spectral density'
if self.freqtype.startswith('w'):
labels[0] = 'Frequency [rad/s]'
labels[1] = 'S(w) [m**2 s/ rad]'
else:
labels[0] = 'Frequency [Hz]'
labels[1] = 'S(f) [m**2 s]'
else:
title = 'Wave Number Spectrum'
labels[0] = 'Wave number [rad/m]'
if self.type.endswith('k1d'):
labels[1] = 'S(k) [m**3/ rad]'
elif self.type.endswith('k2d'):
labels[1] = labels[0]
labels[2] = 'S(k1,k2) [m**4/ rad**2]'
else:
raise ValueError('Object does not appear to be initialized, it is empty!')
if self.norm != 0:
title = 'Normalized ' + title
labels[0] = 'Normalized ' + labels[0].split('[')[0]
if not self.type.endswith('dir'):
labels[1] = labels[1].split('[')[0]
labels[2] = labels[2].split('[')[0]
self.labels.title = title
self.labels.xlab = labels[0]
self.labels.ylab = labels[1]
self.labels.zlab = labels[2]
def test_specdata():
import wafo.spectrum.models as sm
Sj = sm.Jonswap()
S = Sj.tospecdata()
me, va, sk, ku = S.stats_nl(moments='mvsk')
def main():
import matplotlib
matplotlib.interactive(True)
from wafo.spectrum import models as sm
w = linspace(0, 3, 100)
Sj = sm.Jonswap()
S = Sj.tospecdata()
f = S.to_t_pdf(pdef='Tc', paramt=(0, 10, 51), speed=7)
f.err
f.plot()
f.show()
#pdfplot(f)
#hold on,
#plot(f.x{:}, f.f+f.err,'r',f.x{:}, f.f-f.err) estimated error bounds
#hold off
#S = SpecData1D(Sj(w),w)
R = S.tocovdata(nr=1)
S1 = S.copy()
Si = R.tospecdata()
ns = 5000
dt = .2
x1 = S.sim_nl(ns=ns, dt=dt)
x2 = TimeSeries(x1[:, 1], x1[:, 0])
R = x2.tocovdata(lag=100)
R.plot()
S.plot('ro')
t = S.moment()
t1 = S.bandwidth([0, 1, 2, 3])
S1 = S.copy()
S1.resample(dt=0.3, method='cubic')
S1.plot('k+')
x = S1.sim(ns=100)
import pylab
pylab.clf()
pylab.plot(x[:, 0], x[:, 1])
pylab.show()
pylab.close('all')
print('done')
if __name__ == '__main__':
if True: #False : #
import doctest
doctest.testmod()
else:
main()