"""
This module contains the IEC turbulence models.
See the
`Original-TurbSim user manual
<http://wind.nrel.gov/designcodes/preprocessors/turbsim/TurbSim.pdf>`_
for more info on IEC spectral models.
"""
from .base import specModelBase, np, ts_float, specObj
from ..misc import InvalidConfig, Lambda
import warnings
[docs]class iecbase(specModelBase):
r"""
This is a base class for the IEC spectral models (IECKAI and IECVKM).
Parameters
----------
IECwindtype : str {'NTM'=normal,
'xETM'=extreme turbulence,
'xEWM1'=extreme 1-year wind,
'xEWM50'=extreme 50-year wind,
where x=wind turbine class 1, 2, or 3}
IECstandard : int {1}
Currently this only supports IECstandard==1.
IECstandard == 2 and 3 correspond to small and
offshore wind, respectively, and have not yet been
implemented here.
IECedition : int {2,3}
This is the 'edition' number of the -1 standard.
IECturbc : str | float {'A','B','C'}
The string form correspodes to the IEC turbulence
'categories'. If a float is provided, it specifies a
specific value of Turbulence Intensity.
ETMc : float, optional (2.0)
ETMc specifies the value of the 'c' parameter in the
equation for :math:`\sigma_u`. It is only used when
`IECwindtype`=`xETM`.
Notes
-----
For further details on the IEC spectral models see the
`Original-TurbSim user manual
<http://wind.nrel.gov/designcodes/preprocessors/turbsim/TurbSim.pdf>`_.
"""
def __init__(self, IECwindtype, IECstandard,
IECedition, IECturbc, ETMc=None):
self.IECwindtype = IECwindtype
self.IECstandard = IECstandard
self.IECedition = IECedition
self.IECturbc = IECturbc
self.ETMc = ETMc
def _sumfile_string(self, tsrun):
windtype_desc = {'NTM': 'Normal Turbulence Model',
'ETM': 'Extreme Turbulence Model',
'1EWM1': 'Extreme 1-Year Wind Speed Model (Class 1)',
'2EWM1': 'Extreme 1-Year Wind Speed Model (Class 2)',
'3EWM1': 'Extreme 1-Year Wind Speed Model (Class 3)',
'1EWM50': 'Extreme 50-Year Wind Speed Model (Class 1)',
'2EWM50': 'Extreme 50-Year Wind Speed Model (Class 2)',
'3EWM50': 'Extreme 50-Year Wind Speed Model (Class 3)',
}
edition_desc = {(1, 1): 'IEC 61400-1 Ed. 1: 1993',
(1, 2): 'IEC 61400-1 Ed. 2: 1999',
(1, 3): 'IEC 61400-1 Ed. 3: 2005',
(2, 2): 'IEC 61400-2 Ed. 2: 2005',
(3, 1): 'IEC 61400-3 Ed. 1: 2006',
}
sumstring_format = """
Turbulence model used = {TurbModel_desc}
Turbulence characteristic = {IECturbc}
IEC turbulence type = {IECwindtype_desc}
IEC standard = {IECstandard_desc}
IEC Length scale (Lambda) = {Lambda:0.4g} [m]
IEC Sigma = {Sigma:0.4g} [m/s]
ETM 'c' value = {etmc}
"""
data = dict(
TurbModel_desc=self.model_desc,
IECturbc=(self.IECturbc if isinstance(self.IECturbc, basestring)
else '0.4g [%]'.format(self.IECturbc)),
IECwindtype_desc=windtype_desc[self.IECwindtype],
IECstandard_desc=edition_desc[(self.IECstandard, self.IECedition)],
etmc='N/A' if self.ETMc is None else '{0:0.4g} [m/s]'.format(self.ETMc),
Lambda=self.Lambda(tsrun.grid.zhub),
Sigma=self.IEC_Sigma(tsrun.prof.uhub),
)
return sumstring_format.format(**data)
[docs] def Lambda(self, zhub):
"""
Compute the value of Lambda for height `zhub`.
See also
--------
:func:`pyts.misc.Lambda`
"""
return Lambda(zhub, self.IECedition)
def _check_ewm(self, grid):
if (self.IECturbc.__class__ is str and
self.IECstandard == 1 and
self.IECedition == 3 and
self.IECwindtype.upper()[1:4] == 'EWM' and
self.grid.time_sec_out != 600.):
warnings.warn("The extreme wind model is only valid \
for 10min(600s) runs. Setting \
'UsableTime' to 600s.")
grid.time_sec_out = 600.
[docs] def IEC_Sigma(self, uhub):
r"""
Calculate the default value of the standard deviation of
u-component wind speed, :math:`\sigma` or :math:`\sigma_u`.
Notes
-----
For input :class:`IECturbc <iecbase>` a numeric value, it
simply specifies the turbulence intensity. That is,
.. math::
\sigma = \mathrm{IECturbc}\times u_{hub}
Otherwise only IECversion == 1 is supported. In that case:
- For IECedition == 2 the windtype must be 'NTM', in that case:
.. math::
\sigma = Ti (\frac{15+\beta u_{hub}}{\beta + 1})
Where,
Ti = (0.18,0.16) for input variable :class:`IECturbc
<iecbase>` = ('a','b'), respectively.
:math:`\beta` = (2.0,3.0) for input variable
:class:`IECturbc <iecbase>` = ('a','b'), respectively.
- For IECedition == 3, Ti=(0.16,0.14,0.12) for
IECturbc=(a,b,c), respectively. In this case, different
formulations are used for different :class:`IECwindtype
<iecbase>`.
- For IECwindtype == 'NTM':
.. math::
\sigma = Ti(0.75u_{hub} + 5.6)
- For IECwindtype == 'xETM', the value of 'x' in that
string provides another input variable,
Vref=(50,42.5,37.5) for x=1,2,3.
.. math::
\sigma = \mathrm{ETMc} \times Ti (0.072(0.2
V_{ref}/\mathrm{ETMc}+3)(u_{hub}/\mathrm{ETMc}-4)+10)
- For IECwindtype == 'xEWM1' | 'xEWM50', the same value of
Vref apply and
.. math::
\sigma = 0.11 V_{ref}
"""
iecver = self.IECstandard
if self.IECturbc.__class__ is str:
# !!!VERSION_INCONSISTENCY: add 'khtest' functionality.
val = self.IECturbc.lower()
wndtp = self.IECwindtype.lower()
edi = self.IECedition
if iecver is None:
return None
if iecver == 1: # Onshore-big wind.
if edi == 2: # 2nd edition
if (wndtp != 'ntm'):
raise InvalidConfig("For IEC Turbulence models \
other than NTM, the iec edition must be 3.")
if val == 'a':
TurbInt15 = 0.18
SigmaSlope = 2.0
elif val == 'b':
TurbInt15 = 0.16
SigmaSlope = 3.0
else:
raise InvalidConfig(
"For the 61400-1 2nd edition, IECturbc must be \
set to 'a', 'b', or a number (Turbulence \
intensity)."
)
IEC_Sigma = TurbInt15 * \
((15.0 + SigmaSlope * uhub) / (SigmaSlope + 1))
return IEC_Sigma
elif edi == 3: # 3rd edition
if val == 'a':
TurbInt15 = 0.16
elif val == 'b':
TurbInt15 = 0.14
elif val == 'c':
TurbInt15 = 0.12
else:
raise InvalidConfig(
"For the 61400-1 3rd edition, IECturbc must be \
set to 'a', 'b', 'c', or a number (Turbulence \
intensity)."
)
if wndtp == 'ntm':
IEC_Sigma = TurbInt15 * (0.75 * uhub + 5.6) # /uhub
return IEC_Sigma
elif wndtp[0] not in ['1', '2', '3']:
raise InvalidConfig(
"A wind turbine class (1, 2 or 3) must be \
specified with the extreme turbulence and \
extreme wind types (e.g. '1ETM' or '2EWM')."
)
elif wndtp[1:4] in ['etm', 'ewm']:
Vref = {'1': 50, '2': 42.5, '3': 37.5}[wndtp[0]]
wndtp = wndtp[1:]
if wndtp == 'etm':
if self.ETMc is None:
self.ETMc = 2.0
IEC_Sigma = self.ETMc * TurbInt15 * \
(0.072 * (0.2 * Vref / self.ETMc + 3.) * (
uhub / self.ETMc - 4) + 10.)
return IEC_Sigma
else:
return 0.11 * Vref
else:
raise InvalidConfig("Invalid 'IEC_WindType' specified in the input file.")
elif iecver == 2: # Small wind.
raise InvalidConfig("The 'small wind' spectral model (IEC version 2) is not "
"implemented in PyTurbSim")
elif iecver == 3: # Offshore wind.
raise InvalidConfig("The offshore wind IEC spectral model (IEC version 3) is "
"not implemented in PyTurbSim")
else: # The IECturbc is numeric.
if wndtp != 'ntm':
raise InvalidConfig("If the 'IECturbc' input option is a number (specifying "
"turbulence intensity), the IEC_WindType must be 'NTM'.")
[docs]class IECKai(iecbase):
r"""IEC Kaimal spectral model.
Notes
-----
The form of this model is,
.. math::
S_k(f) = \frac{4 \sigma_k^2/\hat{f}_k}{(1+6 f/\hat{f}_k
)^{5/3}} \qquad \mathrm{for}\ k=u,v,w
Where,
:math:`\hat{f}_k = \alpha_k \bar{u}_{hub}/\Lambda`
:math:`\alpha_k =` (8.1,2.7,0.66) for k= (u,v,w)
:math:`\sigma_u` is defined in :attr:`IEC_Sigma <iecbase.IEC_Sigma>`
:math:`\sigma_v=0.8\sigma_u` and :math:`\sigma_w=0.5\sigma_u`
:math:`\Lambda` is defined in :attr:`Lambda <iecbase.Lambda>`
"""
[docs] def __call__(self, tsrun):
"""Create the spectral object for a `tsrun` instance.
Parameters
----------
tsrun : :class:`.tsrun`
A TurbSim run object.
Returns
-------
out : :class:`.specObj`
An IEC spectral object for the grid in `tsrun`.
"""
self._check_ewm(tsrun.grid)
out = specObj(tsrun)
sig2 = 4 * self.IEC_Sigma(tsrun.prof.uhub) ** 2
fctr = np.array([1, 0.64, 0.25], dtype=ts_float)
L_u = self.Lambda(tsrun.grid.zhub) / tsrun.prof.uhub * \
np.array([8.10, 2.70, 0.66], dtype=ts_float)
for comp in self.comp:
out[comp] = (sig2 * fctr[comp] * L_u[comp] / (
1 + 6 * out.f * L_u[comp]) ** self.pow5_3)[None, None, :]
return out
[docs]class IECVKm(iecbase):
r"""IEC Von-Karman spectral model
Notes
-----
The form of this model is,
.. math::
S_u(f) = \frac{4 \sigma^2/\hat{f}}{(1+71(f/\hat{f})^2)^{5/6}}
S_v(f) = S_w(f) = (1+189(f/\hat{f})^2\frac{2\sigma^2/\hat{f}}
{(1+71 (f/\hat{f})^2)^{11/6}}
Where,
:math:`\hat{f} = \bar{u}_{hub}/\Lambda`
:math:`\sigma` is defined in :attr:`IEC_Sigma <iecbase.IEC_Sigma>`
:math:`\Lambda` is defined in :attr:`Lambda <iecbase.Lambda>`
"""
[docs] def __call__(self, tsrun):
"""
Create and calculate the spectral object for a `tsrun`
instance.
Parameters
----------
tsrun : :class:`.tsrun`
A TurbSim run object.
Returns
-------
out : :class:`.specObj`
An IEC spectral object for the grid in `tsrun`.
"""
self._check_ewm(tsrun.grid)
out = specObj(tsrun)
sig2 = 4 * self.IEC_Sigma(tsrun.prof.uhub) ** 2
L_u = 3.5 * self.Lambda(tsrun.grid.zhub) / tsrun.prof.uhub
dnm = 1 + 71 * (out.f * L_u) ** 2
out[0] = (sig2 * L_u / (dnm) ** 0.8333333)[None, None, :]
out[2] = out[1] = (sig2 / 2 * L_u / (dnm) ** 1.8333333 *
(1 + 189 * (out.f * L_u) ** 2))[None, None, :]
return out