Newer
Older

Mads M. Pedersen
committed
import numpy as np
from py_wake.deficit_models import DeficitModel
from py_wake.superposition_models import SquaredSum
from py_wake.wind_farm_models.engineering_models import PropagateDownwind
class AreaOverlappingFactor():
def __init__(self, k=.1):

Mads M. Pedersen
committed
def overlapping_area_factor(self, wake_radius_ijlk, dw_ijlk, cw_ijlk, D_src_il, D_dst_ijl):
"""Calculate overlapping factor
Parameters
----------
dw_jl : array_like
down wind distance [m]
cw_jl : array_like
cross wind distance [m]
D_src_l : array_like
Diameter of source turbines [m]
D_dst_jl : array_like or None
Diameter of destination turbines [m]. If None destination is assumed to be a point
Returns
-------
A_ol_factor_jl : array_like
area overlaping factor
"""

Mads M. Pedersen
committed
if D_dst_ijl is None:

Mads M. Pedersen
committed
return wake_radius_ijlk > cw_ijlk

Mads M. Pedersen
committed
return self._cal_overlapping_area_factor(wake_radius_ijlk,
(D_dst_ijl[..., na] / 2),
np.abs(cw_ijlk))

Mads M. Pedersen
committed
def _cal_overlapping_area_factor(self, R1, R2, d):
""" Calculate the overlapping area of two circles with radius R1 and
R2, centers distanced d.
The calculation formula can be found in Eq. (A1) of :
[Ref] Feng J, Shen WZ, Solving the wind farm layout optimization
problem using Random search algorithm, Reneable Energy 78 (2015)
182-192
Note that however there are typos in Equation (A1), '2' before alpha
and beta should be 1.
Parameters
----------
R1: array:float
Radius of the first circle [m]
R2: array:float
Radius of the second circle [m]
d: array:float
Distance between two centers [m]
Returns
-------
A_ol: array:float
Overlapping area [m^2]
"""
# treat all input as array
R1, R2, d = [np.asarray(a) for a in [R1, R2, d]]

Mads M. Pedersen
committed
if R2.shape != R1.shape:
R2 = np.zeros_like(R1) + R2
A_ol_f = np.zeros_like(R1)
p = (R1 + R2 + d) / 2.0
# make sure R_big >= R_small
Rmax = np.where(R1 < R2, R2, R1)
Rmin = np.where(R1 < R2, R1, R2)
# full wake cases
index_fullwake = (d <= (Rmax - Rmin))
A_ol_f[index_fullwake] = 1
# partial wake cases
mask = (d > (Rmax - Rmin)) & (d < (Rmin + Rmax))
# in somecases cos_alpha or cos_beta can be larger than 1 or less than
# -1.0, cause problem to arccos(), resulting nan values, here fix this
# issue.
def arccos_lim(x):
return np.arccos(np.maximum(np.minimum(x, 1), -1))
alpha = arccos_lim((Rmax[mask]**2.0 + d[mask]**2 - Rmin[mask]**2) /
(2.0 * Rmax[mask] * d[mask]))
beta = arccos_lim((Rmin[mask]**2.0 + d[mask]**2 - Rmax[mask]**2) /
(2.0 * Rmin[mask] * d[mask]))
A_triangle = np.sqrt(p[mask] * (p[mask] - Rmin[mask]) *
(p[mask] - Rmax[mask]) * (p[mask] - d[mask]))
A_ol_f[mask] = (alpha * Rmax[mask]**2 + beta * Rmin[mask]**2 -
2.0 * A_triangle) / (R2[mask]**2 * np.pi)
return A_ol_f

Mads M. Pedersen
committed
class NOJDeficit(DeficitModel, AreaOverlappingFactor):
args4deficit = ['WS_ilk', 'D_src_il', 'D_dst_ijl', 'dw_ijlk', 'cw_ijlk', 'ct_ilk']

Mads M. Pedersen
committed
def __init__(self, k=.1):
AreaOverlappingFactor.__init__(self, k)

Mads M. Pedersen
committed
def _calc_layout_terms(self, WS_ilk, D_src_il, D_dst_ijl, dw_ijlk, cw_ijlk, **_):
R_src_il = D_src_il / 2

Mads M. Pedersen
committed
term_denominator_ijlk = (1 + self.k * dw_ijlk / R_src_il[:, na, :, na])**2
term_denominator_ijlk += (term_denominator_ijlk == 0)

Mads M. Pedersen
committed
A_ol_factor_ijlk = self.overlapping_area_factor(self.wake_radius(D_src_il, dw_ijlk),
dw_ijlk, cw_ijlk, D_src_il, D_dst_ijl)
with np.warnings.catch_warnings():
np.warnings.filterwarnings('ignore', r'invalid value encountered in true_divide')

Mads M. Pedersen
committed
self.layout_factor_ijlk = WS_ilk[:, na] * (dw_ijlk > 0) * (A_ol_factor_ijlk / term_denominator_ijlk)

Mads M. Pedersen
committed
def calc_deficit(self, WS_ilk, D_src_il, D_dst_ijl, dw_ijlk, cw_ijlk, ct_ilk, **_):
if not self.deficit_initalized:

Mads M. Pedersen
committed
self._calc_layout_terms(WS_ilk, D_src_il, D_dst_ijl, dw_ijlk, cw_ijlk)
ct_ilk = np.minimum(ct_ilk, 1) # treat ct_ilk for np.sqrt()
term_numerator_ilk = (1 - np.sqrt(1 - ct_ilk))
return term_numerator_ilk[:, na] * self.layout_factor_ijlk

Mads M. Pedersen
committed
def wake_radius(self, D_src_il, dw_ijlk, **_):
wake_radius_ijlk = (self.k * dw_ijlk + D_src_il[:, na, :, na] / 2)
return wake_radius_ijlk

Mads M. Pedersen
committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
class NOJ(PropagateDownwind):
def __init__(self, site, windTurbines, k=.1, superpositionModel=SquaredSum(),
deflectionModel=None, turbulenceModel=None):
"""
Parameters
----------
site : Site
Site object
windTurbines : WindTurbines
WindTurbines object representing the wake generating wind turbines
k : float, default 0.1
wake expansion factor
superpositionModel : SuperpositionModel, default SquaredSum
Model defining how deficits sum up
blockage_deficitModel : DeficitModel, default None
Model describing the blockage(upstream) deficit
deflectionModel : DeflectionModel, default None
Model describing the deflection of the wake due to yaw misalignment, sheared inflow, etc.
turbulenceModel : TurbulenceModel, default None
Model describing the amount of added turbulence in the wake
"""
PropagateDownwind.__init__(self, site, windTurbines,
wake_deficitModel=NOJDeficit(k),
superpositionModel=superpositionModel,
deflectionModel=deflectionModel,
turbulenceModel=turbulenceModel)
if __name__ == '__main__':
from py_wake.examples.data.iea37._iea37 import IEA37Site
from py_wake.examples.data.iea37._iea37 import IEA37_WindTurbines

Mads M. Pedersen
committed
import matplotlib.pyplot as plt

Mads M. Pedersen
committed
# setup site, turbines and wind farm model
site = IEA37Site(16)
x, y = site.initial_position.T
windTurbines = IEA37_WindTurbines()

Mads M. Pedersen
committed
wf_model = NOJ(site, windTurbines)
print(wf_model)

Mads M. Pedersen
committed
# run wind farm simulation
sim_res = wf_model(x, y)

Mads M. Pedersen
committed
# calculate AEP
aep = sim_res.aep()
print(aep)
# plot wake map
flow_map = sim_res.flow_map(wd=30, ws=9.8)
flow_map.plot_wake_map()
flow_map.plot_windturbines()
plt.title('AEP: %.2f GWh' % aep)
plt.show()