Newer
Older
"""
Base class for wake models
Make a subclass and implement calc_deficit and calc_effective_WS
Implementations of linear and squared sum available through inherritance
Prefixs:
i: turbine
j: downstream points/turbines
k: wind speed
l: wind direction
m: turbine and wind direction (il.flatten())
n: from_turbine, to_turbine and wind direction (iil.flatten())
Arguments available for calc_deficit (specifiy in args4deficit):
- WS_lk: Local wind speed without wake effects
- WS_eff_lk: Local wind speed with wake effects
- D_src_l: Diameter of source turbine
- D_dst_jl: Diameter of destination turbine
- dw_jl: Downwind distance from turbine i to point/turbine j
- hcw_jl: Horizontal cross wind distance from turbine i to point/turbine j
- cw_jl: Cross wind(horizontal and vertical) distance from turbine i to point/turbine j
- ct_lk: Thrust coefficient
"""
def __init__(self, windTurbines):
self.windTurbines = windTurbines
def calc_wake(self, WS_ilk, TI_ilk, dw_iil, cw_iil, dh_iil, dw_order_indices_l, types_i):
i1, i2, _ = np.where((np.abs(dw_iil) + np.abs(cw_iil) + np.eye(I)[:, :, na]) == 0)
if len(i1):
msg = "\n".join(["Turbines %d and %d are at the same position" %
(i1[i], i2[i]) for i in np.unique([i1, i2], 0)])
raise Exception(msg)
K = WS_ilk.shape[2]
deficit_nk = np.zeros((I * I * L, K))
# deficit_ijlk = deficit_nk.reshape((I, I, L, K)) # from i to j
indices = np.arange(I * I * L).reshape((I, I, L))
WS_mk = WS_ilk.astype(np.float).reshape((I * L, K))
WS_eff_mk = WS_mk.copy()
dw_n = dw_iil.flatten()
cw_n = cw_iil.flatten()
power_ilk = np.zeros((I, L, K))
ct_ilk = np.zeros((I, L, K))
types_i = np.asarray(types_i)
D_i = self.windTurbines.diameter(types_i)
H_i = self.windTurbines.hub_height(types_i)
i_wd_l = np.arange(L)
for j in range(I):
i_wt_l = dw_order_indices_l[:, j]
m = i_wt_l * L + i_wd_l # current wt (j'th most upstream wts for all wdirs)
# n_uw = np.array([indices[dw_order_indices_l[l, :j], i, l] for i, l in zip(i_wt_l, i_wd_l)]).T
#
# n_dw = np.array([indices[i, dw_order_indices_l[l, j + 1:], l] for i, l in zip(i_wt_l, i_wd_l)]).T
n_uw = np.array([indices[uwi, i, l] for uwi, i, l in zip(dw_order_indices_l[:, :j], i_wt_l, i_wd_l)]).T
n_dw = np.array([indices[i, dwi, l] for dwi, i, l in zip(dw_order_indices_l[:, j + 1:], i_wt_l, i_wd_l)]).T
WS_eff_lk = self.calc_effective_WS(WS_mk[m], deficit_nk[n_uw])
WS_eff_mk[m] = WS_eff_lk
ct_lk, power_lk = self.windTurbines.ct_power(WS_eff_lk, types_i[i_wt_l])
power_ilk[i_wt_l, i_wd_l] = power_lk
ct_ilk[i_wt_l, i_wd_l, :] = ct_lk
if j < I - 1:
arg_funcs = {'WS_lk': lambda: WS_mk[m],
'WS_eff_lk': lambda: WS_eff_mk[m],
'D_src_l': lambda: D_i[i_wt_l],
'D_dst_jl': lambda: D_i[dw_order_indices_l[:, j + 1:]].T,
'dw_jl': lambda: dw_n[n_dw],
'cw_jl': lambda: np.sqrt(cw_n[n_dw]**2 + dh_n[n_dw]**2),
'hcw_jl': lambda: cw_n[n_dw],
'dh_jl': lambda: dh_n[n_dw],
'h_l': lambda: H_i[i_wt_l],
'ct_lk': lambda: ct_lk}
args = {k: arg_funcs[k]() for k in self.args4deficit}
deficit_nk[n_dw] = self.calc_deficit(**args)
# WS_mk[m], D_i[i_wt_l],
# D_i[dw_order_indices_l[:, j + 1:]].T,
# dw_n[n_dw],
# cw_n[n_dw],
# ct_lk)
WS_eff_ilk = WS_eff_mk.reshape((I, L, K))
return WS_eff_ilk, TI_ilk, power_ilk, ct_ilk
def wake_map(self, WS_ilk, WS_eff_ilk, dw_ijl, cw_ijl, dh_ijl, ct_ilk, types_i, WS_jlk):
H_i = self.windTurbines.hub_height(types_i)
I, J, L = dw_ijl.shape
K = WS_ilk.shape[2]
deficit_ijlk = []
for i in range(I):
deficit_jlk = np.zeros((J, L, K))
for l in range(L):
m = dw_ijl[i, :, l] > 0
arg_funcs = {'WS_lk': lambda: WS_ilk[i, l][na],
'WS_eff_lk': lambda: WS_eff_ilk[i, l][na],
'D_src_l': lambda: D_i[i][na],
'D_dst_jl': lambda: None,
'dw_jl': lambda: dw_ijl[i, :, l][m][:, na],
'cw_jl': lambda: np.sqrt(cw_ijl[i, :, l][m]**2 + dh_ijl[i, :, l][m]**2)[:, na],
'hcw_jl': lambda: cw_ijl[i, :, l][m][:, na],
'dh_jl': lambda: dh_ijl[i, :, l][m][:, na],
'h_l': lambda: H_i[i][na],
'ct_lk': lambda: ct_ilk[i, l][na]}
args = {k: arg_funcs[k]() for k in self.args4deficit}
deficit_jlk[:, l][m] = self.calc_deficit(**args)[:, 0]
deficit_ijlk = np.array(deficit_ijlk)
return self.calc_effective_WS(WS_jlk, deficit_ijlk)
@abstractmethod
def calc_deficit(self, WS_lk, D_src_l, D_dst_jl, dw_jl, cw_jl, ct_lk):
"""
ct or a???
"""
pass
class SquaredSum():
def calc_effective_WS(self, WS_lk, deficit_jlk):
return WS_lk - np.sqrt(np.sum(deficit_jlk**2, 0))
class LinearSum():
def calc_effective_WS(self, WS_lk, deficit_jlk):
return WS_lk - np.sum(deficit_jlk, 0)
def main():
if __name__ == '__main__':
from py_wake.examples.data.iea37 import iea37_path
from py_wake.examples.data.iea37.iea37_reader import read_iea37_windfarm,\
read_iea37_windrose
from py_wake.examples.data.iea37._iea37 import IEA37_WindTurbines
from py_wake.site._site import UniformSite
from py_wake.aep._aep import AEP
class MyWakeModel(WakeModel, SquaredSum):
args4deficit = ['WS_lk', 'dw_jl']
def calc_deficit(self, WS_lk, dw_jl):
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# 10% deficit downstream
return (WS_lk * .1)[na] * (dw_jl > 0)[:, :, na]
_, _, freq = read_iea37_windrose(iea37_path + "iea37-windrose.yaml")
n_wt = 16
x, y, _ = read_iea37_windfarm(iea37_path + 'iea37-ex%d.yaml' % n_wt)
site = UniformSite(freq, ti=0.75)
windTurbines = IEA37_WindTurbines(iea37_path + 'iea37-335mw.yaml')
import matplotlib.pyplot as plt
x_j = np.linspace(-1500, 1500, 500)
y_j = np.linspace(-1500, 1500, 300)
wake_model = MyWakeModel(windTurbines)
aep = AEP(site, windTurbines, wake_model)
X, Y, Z = aep.wake_map(x_j, y_j, 110, x, y, wd=[0, 30], ws=[8, 9, 10])
plt.figure()
c = plt.contourf(X, Y, Z, np.arange(2, 9.1, .01))
plt.colorbar(c)
plt.plot(x, y, '2k')
for i, (x_, y_) in enumerate(zip(x, y)):
plt.annotate(i, (x_, y_))
plt.axis('equal')
plt.show()
main()