foxes 0.5.0.2__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of foxes might be problematic. Click here for more details.
- foxes/VERSION +1 -1
- foxes/algorithms/downwind/downwind.py +41 -46
- foxes/algorithms/downwind/models/point_wakes_calc.py +4 -9
- foxes/algorithms/downwind/models/set_amb_point_results.py +5 -22
- foxes/core/algorithm.py +1 -1
- foxes/core/data_calc_model.py +26 -2
- foxes/core/partial_wakes_model.py +1 -1
- foxes/core/rotor_model.py +36 -2
- foxes/core/turbine_model.py +36 -0
- foxes/core/turbine_type.py +35 -1
- foxes/core/wake_frame.py +39 -3
- foxes/core/wake_model.py +36 -0
- foxes/models/model_book.py +129 -89
- foxes/models/turbine_models/rotor_centre_calc.py +1 -2
- foxes/models/turbine_types/CpCt_file.py +13 -3
- foxes/models/turbine_types/CpCt_from_two.py +14 -4
- foxes/models/vertical_profiles/abl_log_neutral_ws.py +32 -5
- foxes/models/vertical_profiles/abl_log_stable_ws.py +32 -4
- foxes/models/vertical_profiles/abl_log_unstable_ws.py +32 -4
- foxes/models/vertical_profiles/abl_log_ws.py +50 -18
- foxes/models/wake_frames/yawed_wakes.py +15 -9
- foxes/models/wake_models/induction/__init__.py +1 -1
- foxes/models/wake_models/induction/rankine_half_body.py +33 -7
- foxes/models/wake_models/ti/crespo_hernandez.py +6 -1
- foxes/models/wake_models/ti/iec_ti.py +5 -3
- foxes/models/wake_models/wind/__init__.py +2 -2
- foxes/models/wake_models/wind/{bastankhah.py → bastankhah14.py} +11 -14
- foxes/models/wake_models/wind/{porte_agel.py → bastankhah16.py} +24 -16
- foxes/models/wake_models/wind/turbopark.py +11 -22
- foxes/models/wake_superpositions/__init__.py +9 -5
- foxes/models/wake_superpositions/ti_linear.py +134 -0
- foxes/models/wake_superpositions/ti_max.py +134 -0
- foxes/models/wake_superpositions/{ti_superp.py → ti_pow.py} +15 -57
- foxes/models/wake_superpositions/ti_quadratic.py +134 -0
- foxes/models/wake_superpositions/ws_linear.py +170 -0
- foxes/models/wake_superpositions/ws_max.py +173 -0
- foxes/models/wake_superpositions/ws_pow.py +175 -0
- foxes/models/wake_superpositions/{product.py → ws_product.py} +43 -22
- foxes/models/wake_superpositions/ws_quadratic.py +170 -0
- foxes/output/__init__.py +4 -0
- foxes/output/calc_points.py +143 -0
- foxes/output/flow_plots_2d/__init__.py +1 -0
- foxes/output/flow_plots_2d/common.py +104 -1
- foxes/output/flow_plots_2d/flow_plots.py +237 -569
- foxes/output/flow_plots_2d/get_fig.py +183 -0
- foxes/output/flow_plots_2d/seq_flow_ani_plugin.py +0 -1
- foxes/output/grids.py +705 -0
- foxes/output/output.py +58 -11
- foxes/output/results_writer.py +101 -17
- foxes/output/round.py +10 -0
- foxes/output/slice_data.py +900 -0
- foxes/utils/__init__.py +5 -3
- foxes/utils/exec_python.py +56 -0
- foxes/utils/geopandas_utils.py +294 -0
- foxes/utils/pandas_utils.py +175 -0
- foxes/utils/plotly_utils.py +19 -0
- foxes/utils/xarray_utils.py +38 -0
- {foxes-0.5.0.2.dist-info → foxes-0.5.2.dist-info}/METADATA +1 -1
- {foxes-0.5.0.2.dist-info → foxes-0.5.2.dist-info}/RECORD +63 -49
- foxes/models/wake_superpositions/linear.py +0 -242
- foxes/models/wake_superpositions/max.py +0 -258
- foxes/models/wake_superpositions/quadratic.py +0 -252
- {foxes-0.5.0.2.dist-info → foxes-0.5.2.dist-info}/LICENSE +0 -0
- {foxes-0.5.0.2.dist-info → foxes-0.5.2.dist-info}/WHEEL +0 -0
- {foxes-0.5.0.2.dist-info → foxes-0.5.2.dist-info}/top_level.txt +0 -0
- {foxes-0.5.0.2.dist-info → foxes-0.5.2.dist-info}/zip-safe +0 -0
|
@@ -13,10 +13,32 @@ class ABLLogWsProfile(VerticalProfile):
|
|
|
13
13
|
This profile picks the profile according to the mol value
|
|
14
14
|
(neutral: mol = None or mol = 0)
|
|
15
15
|
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
ustar_input: bool
|
|
19
|
+
Flag for using ustar as an input
|
|
20
|
+
|
|
16
21
|
:group: models.vertical_profiles
|
|
17
22
|
|
|
18
23
|
"""
|
|
19
24
|
|
|
25
|
+
def __init__(self, *args, ustar_input=False, **kwargs):
|
|
26
|
+
"""
|
|
27
|
+
Constructor.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
args: tuple, optional
|
|
32
|
+
Additional arguments for VerticalProfile
|
|
33
|
+
ustar_input: bool
|
|
34
|
+
Flag for using ustar as an input
|
|
35
|
+
kwargs: dict, optional
|
|
36
|
+
Additional arguments for VerticalProfile
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
super().__init__(*args, **kwargs)
|
|
40
|
+
self.ustar_input = ustar_input
|
|
41
|
+
|
|
20
42
|
def input_vars(self):
|
|
21
43
|
"""
|
|
22
44
|
The input variables needed for the profile
|
|
@@ -28,7 +50,10 @@ class ABLLogWsProfile(VerticalProfile):
|
|
|
28
50
|
The variable names
|
|
29
51
|
|
|
30
52
|
"""
|
|
31
|
-
|
|
53
|
+
if self.ustar_input:
|
|
54
|
+
return [FV.USTAR, FV.Z0, FV.MOL]
|
|
55
|
+
else:
|
|
56
|
+
return [FV.WS, FV.H, FV.Z0, FV.MOL]
|
|
32
57
|
|
|
33
58
|
def calculate(self, data, heights):
|
|
34
59
|
"""
|
|
@@ -48,52 +73,59 @@ class ABLLogWsProfile(VerticalProfile):
|
|
|
48
73
|
shape as heights
|
|
49
74
|
|
|
50
75
|
"""
|
|
51
|
-
ws = np.zeros_like(heights)
|
|
52
|
-
ws[:] = data[FV.WS]
|
|
53
|
-
|
|
54
|
-
h0 = np.zeros_like(heights)
|
|
55
|
-
h0[:] = data[FV.H]
|
|
56
76
|
|
|
57
77
|
z0 = np.zeros_like(heights)
|
|
58
78
|
z0[:] = data[FV.Z0]
|
|
59
79
|
|
|
60
80
|
mol = np.zeros_like(heights)
|
|
61
81
|
mol[:] = data[FV.MOL]
|
|
82
|
+
|
|
83
|
+
if self.ustar_input:
|
|
84
|
+
ustar = data[FV.USTAR]
|
|
85
|
+
else:
|
|
86
|
+
ws = np.zeros_like(heights)
|
|
87
|
+
ws[:] = data[FV.WS]
|
|
88
|
+
|
|
89
|
+
h0 = np.zeros_like(heights)
|
|
90
|
+
h0[:] = data[FV.H]
|
|
62
91
|
|
|
63
92
|
out = np.zeros_like(heights)
|
|
64
93
|
|
|
65
94
|
# neutral profiles:
|
|
66
95
|
sel = np.isnan(mol) | (mol == 0.0)
|
|
67
96
|
if np.any(sel):
|
|
68
|
-
sws = ws[sel]
|
|
69
|
-
sh0 = h0[sel]
|
|
70
97
|
sz0 = z0[sel]
|
|
71
98
|
sh = heights[sel]
|
|
72
|
-
|
|
73
|
-
|
|
99
|
+
if self.ustar_input:
|
|
100
|
+
sus = ustar[sel]
|
|
101
|
+
else:
|
|
102
|
+
sus = abl.neutral.ustar(ws[sel], h0[sel], sz0, kappa=FC.KAPPA)
|
|
103
|
+
out[sel] = abl.neutral.calc_ws(sh, sz0, sus, kappa=FC.KAPPA)
|
|
74
104
|
|
|
75
105
|
# stable profiles:
|
|
76
106
|
sel = mol > 0.0
|
|
77
107
|
if np.any(sel):
|
|
78
|
-
sws = ws[sel]
|
|
79
|
-
sh0 = h0[sel]
|
|
80
108
|
sz0 = z0[sel]
|
|
81
109
|
smo = mol[sel]
|
|
82
110
|
sh = heights[sel]
|
|
83
|
-
|
|
111
|
+
if self.ustar_input:
|
|
112
|
+
sus = ustar[sel]
|
|
113
|
+
else:
|
|
114
|
+
sus = abl.stable.ustar(ws[sel], h0[sel], sz0, smo, kappa=FC.KAPPA)
|
|
84
115
|
psi = abl.stable.psi(sh, smo)
|
|
85
|
-
out[sel] = abl.stable.calc_ws(sh, sz0,
|
|
116
|
+
out[sel] = abl.stable.calc_ws(sh, sz0, sus, psi, kappa=FC.KAPPA)
|
|
86
117
|
|
|
87
118
|
# unstable profiles:
|
|
88
119
|
sel = mol < 0.0
|
|
89
120
|
if np.any(sel):
|
|
90
|
-
sws = ws[sel]
|
|
91
|
-
sh0 = h0[sel]
|
|
92
121
|
sz0 = z0[sel]
|
|
93
122
|
smo = mol[sel]
|
|
94
123
|
sh = heights[sel]
|
|
95
|
-
|
|
124
|
+
if self.ustar_input:
|
|
125
|
+
sus = ustar[sel]
|
|
126
|
+
else:
|
|
127
|
+
sus = abl.unstable.ustar(ws[sel], h0[sel], sz0, smo, kappa=FC.KAPPA)
|
|
96
128
|
psi = abl.unstable.psi(sh, smo)
|
|
97
|
-
out[sel] = abl.unstable.calc_ws(sh, sz0,
|
|
129
|
+
out[sel] = abl.unstable.calc_ws(sh, sz0, sus, psi, kappa=FC.KAPPA)
|
|
98
130
|
|
|
99
131
|
return out
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
3
|
from foxes.core import WakeFrame
|
|
4
|
-
from foxes.models.wake_models.wind.
|
|
4
|
+
from foxes.models.wake_models.wind.bastankhah16 import Bastankhah2016Model
|
|
5
5
|
import foxes.variables as FV
|
|
6
6
|
import foxes.constants as FC
|
|
7
7
|
from .rotor_wd import RotorWD
|
|
@@ -9,13 +9,19 @@ from .rotor_wd import RotorWD
|
|
|
9
9
|
|
|
10
10
|
class YawedWakes(WakeFrame):
|
|
11
11
|
"""
|
|
12
|
-
Bend the wakes for yawed turbines
|
|
12
|
+
Bend the wakes for yawed turbines, based on the
|
|
13
|
+
Bastankhah 2016 wake model
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Notes
|
|
16
|
+
-----
|
|
17
|
+
Reference:
|
|
18
|
+
"Experimental and theoretical study of wind turbine wakes in yawed conditions"
|
|
19
|
+
Majid Bastankhah, Fernando Porté-Agel
|
|
20
|
+
https://doi.org/10.1017/jfm.2016.595
|
|
15
21
|
|
|
16
22
|
Attributes
|
|
17
23
|
----------
|
|
18
|
-
model:
|
|
24
|
+
model: Bastankhah2016Model
|
|
19
25
|
The model for computing common data
|
|
20
26
|
K: float
|
|
21
27
|
The wake growth parameter k. If not given here
|
|
@@ -65,7 +71,7 @@ class YawedWakes(WakeFrame):
|
|
|
65
71
|
super().__init__()
|
|
66
72
|
|
|
67
73
|
self.base_frame = base_frame
|
|
68
|
-
self.model =
|
|
74
|
+
self.model = Bastankhah2016Model(ct_max, alpha, beta)
|
|
69
75
|
self.k_var = k_var
|
|
70
76
|
|
|
71
77
|
setattr(self, k_var, k)
|
|
@@ -143,20 +149,20 @@ class YawedWakes(WakeFrame):
|
|
|
143
149
|
)
|
|
144
150
|
|
|
145
151
|
# select targets:
|
|
146
|
-
sp_sel = self.model.get_data(
|
|
152
|
+
sp_sel = self.model.get_data(Bastankhah2016Model.SP_SEL, mdata)
|
|
147
153
|
if np.any(sp_sel):
|
|
148
154
|
# prepare:
|
|
149
155
|
n_sp_sel = np.sum(sp_sel)
|
|
150
156
|
ydef = np.zeros((n_sp_sel,), dtype=FC.DTYPE)
|
|
151
157
|
|
|
152
158
|
# collect data:
|
|
153
|
-
near = self.model.get_data(
|
|
159
|
+
near = self.model.get_data(Bastankhah2016Model.NEAR, mdata)
|
|
154
160
|
far = ~near
|
|
155
161
|
|
|
156
162
|
# near wake:
|
|
157
163
|
if np.any(near):
|
|
158
164
|
# collect data:
|
|
159
|
-
delta = self.model.get_data(
|
|
165
|
+
delta = self.model.get_data(Bastankhah2016Model.DELTA_NEAR, mdata)
|
|
160
166
|
|
|
161
167
|
# set deflection:
|
|
162
168
|
ydef[near] = delta
|
|
@@ -164,7 +170,7 @@ class YawedWakes(WakeFrame):
|
|
|
164
170
|
# far wake:
|
|
165
171
|
if np.any(far):
|
|
166
172
|
# collect data:
|
|
167
|
-
delta = self.model.get_data(
|
|
173
|
+
delta = self.model.get_data(Bastankhah2016Model.DELTA_FAR, mdata)
|
|
168
174
|
|
|
169
175
|
# set deflection:
|
|
170
176
|
ydef[far] = delta
|
|
@@ -6,7 +6,7 @@ import foxes.variables as FV
|
|
|
6
6
|
import foxes.constants as FC
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class RankineHalfBody(WakeModel):
|
|
10
10
|
"""
|
|
11
11
|
The Rankine half body induction wake model
|
|
12
12
|
|
|
@@ -18,11 +18,27 @@ class RHB(WakeModel):
|
|
|
18
18
|
|
|
19
19
|
https://www.fnc.co.uk/media/o5eosxas/a-potential-flow-model-for-wind-turbine-induction-and-wind-farm-blockage.pdf
|
|
20
20
|
|
|
21
|
+
Attributes
|
|
22
|
+
----------
|
|
23
|
+
ct_max: float
|
|
24
|
+
The maximal value for ct, values beyond will be limited
|
|
25
|
+
to this number
|
|
26
|
+
|
|
21
27
|
:group: models.wake_models.induction
|
|
22
28
|
|
|
23
29
|
"""
|
|
24
30
|
|
|
25
31
|
def __init__(self, ct_max=0.9999):
|
|
32
|
+
"""
|
|
33
|
+
Constructor.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
ct_max: float
|
|
38
|
+
The maximal value for ct, values beyond will be limited
|
|
39
|
+
to this number
|
|
40
|
+
|
|
41
|
+
"""
|
|
26
42
|
super().__init__()
|
|
27
43
|
self.ct_max = ct_max
|
|
28
44
|
|
|
@@ -157,17 +173,28 @@ class RHB(WakeModel):
|
|
|
157
173
|
# stagnation point condition
|
|
158
174
|
xs = -np.sqrt(m / (4 * ws))
|
|
159
175
|
|
|
160
|
-
#
|
|
161
|
-
sp_sel = (ct > 0) & ((RHB_shape
|
|
176
|
+
# set values out of body shape
|
|
177
|
+
sp_sel = (ct > 0) & ((RHB_shape < -1) | (x < xs))
|
|
162
178
|
if np.any(sp_sel):
|
|
163
179
|
# apply selection
|
|
164
|
-
xyz = wake_coos[sp_sel]
|
|
180
|
+
xyz = wake_coos[sp_sel]
|
|
165
181
|
|
|
166
182
|
# calc velocity components
|
|
167
183
|
vel_factor = m[sp_sel] / (4 * np.linalg.norm(xyz, axis=-1) ** 3)
|
|
168
184
|
wake_deltas["U"][sp_sel] += vel_factor * xyz[:, 0]
|
|
169
185
|
wake_deltas["V"][sp_sel] += vel_factor * xyz[:, 1]
|
|
170
186
|
|
|
187
|
+
# set values inside body shape
|
|
188
|
+
sp_sel = (ct > 0) & (RHB_shape >= -1) & (x >= xs) & (x < 0)
|
|
189
|
+
if np.any(sp_sel):
|
|
190
|
+
# apply selection
|
|
191
|
+
xyz = np.zeros_like(wake_coos[sp_sel])
|
|
192
|
+
xyz[:, 0] = xs[sp_sel]
|
|
193
|
+
|
|
194
|
+
# calc velocity components
|
|
195
|
+
vel_factor = m[sp_sel] / (4 * np.linalg.norm(xyz, axis=-1) ** 3)
|
|
196
|
+
wake_deltas["U"][sp_sel] += vel_factor * xyz[:, 0]
|
|
197
|
+
|
|
171
198
|
return wake_deltas
|
|
172
199
|
|
|
173
200
|
def finalize_wake_deltas(
|
|
@@ -212,9 +239,8 @@ class RHB(WakeModel):
|
|
|
212
239
|
|
|
213
240
|
# add ambient result to wake deltas
|
|
214
241
|
delta_uv = np.stack((wake_deltas["U"], wake_deltas["V"]), axis=2)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
del delta_uv, sel, ws0
|
|
242
|
+
wind_vec += delta_uv
|
|
243
|
+
del delta_uv, ws0
|
|
218
244
|
|
|
219
245
|
# deduce WS and WD deltas:
|
|
220
246
|
new_wd = uv2wd(wind_vec)
|
|
@@ -9,7 +9,12 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
9
9
|
"""
|
|
10
10
|
The Crespo and Hernandez TI empirical correlation
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Notes
|
|
13
|
+
-----
|
|
14
|
+
Reference:
|
|
15
|
+
"Turbulence characteristics in wind-turbine wakes"
|
|
16
|
+
A. Crespo, J. Hernandez
|
|
17
|
+
https://doi.org/10.1016/0167-6105(95)00033-X
|
|
13
18
|
|
|
14
19
|
For the wake diameter we use Eqns. (17), (15), (4), (5) from
|
|
15
20
|
doi:10.1088/1742-6596/625/1/012039
|
|
@@ -9,13 +9,15 @@ class IECTIWake(TopHatWakeModel):
|
|
|
9
9
|
"""
|
|
10
10
|
The TI wake model from IEC-64100-1-2005-8 (2005):
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Notes
|
|
13
|
+
-----
|
|
14
|
+
Reference:
|
|
15
|
+
http://orbit.dtu.dk/files/3750291/2009_31.pdf
|
|
13
16
|
v2: VolLuk: corrected implementation following: IEC-64100-1-2005-8
|
|
14
17
|
(Appearently an error in the document by DTU)
|
|
15
18
|
|
|
16
19
|
and the Frandsen wake TI model, from IEC-64100 (2019):
|
|
17
|
-
|
|
18
|
-
Source: http://orbit.dtu.dk/files/3750291/2009_31.pdf
|
|
20
|
+
http://orbit.dtu.dk/files/3750291/2009_31.pdf
|
|
19
21
|
|
|
20
22
|
Attributes
|
|
21
23
|
----------
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
Wind deficit wake models.
|
|
3
3
|
"""
|
|
4
4
|
from .jensen import JensenWake
|
|
5
|
-
from .
|
|
5
|
+
from .bastankhah14 import Bastankhah2014
|
|
6
|
+
from .bastankhah16 import Bastankhah2016Model, Bastankhah2016
|
|
6
7
|
from .turbopark import TurbOParkWake, TurbOParkWakeIX
|
|
7
|
-
from .porte_agel import PorteAgelModel, PorteAgelWake
|
|
@@ -5,14 +5,16 @@ import foxes.variables as FV
|
|
|
5
5
|
import foxes.constants as FC
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class Bastankhah2014(GaussianWakeModel):
|
|
9
9
|
"""
|
|
10
|
-
The Bastankhah wake model
|
|
10
|
+
The Bastankhah 2014 wake model
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
Notes
|
|
13
|
+
-----
|
|
14
|
+
Reference:
|
|
15
|
+
"A new analytical model for wind-turbine wakes"
|
|
16
|
+
Majid Bastankhah, Fernando Porté-Agel
|
|
17
|
+
https://doi.org/10.1016/j.renene.2014.01.002
|
|
16
18
|
|
|
17
19
|
Attributes
|
|
18
20
|
----------
|
|
@@ -32,7 +34,7 @@ class BastankhahWake(GaussianWakeModel):
|
|
|
32
34
|
"""
|
|
33
35
|
|
|
34
36
|
def __init__(
|
|
35
|
-
self, superposition, k=None, sbeta_factor=0.
|
|
37
|
+
self, superposition, k=None, sbeta_factor=0.2, ct_max=0.9999, k_var=FV.K
|
|
36
38
|
):
|
|
37
39
|
"""
|
|
38
40
|
Constructor.
|
|
@@ -188,13 +190,8 @@ class BastankhahWake(GaussianWakeModel):
|
|
|
188
190
|
del x, k, sbeta, sblim
|
|
189
191
|
|
|
190
192
|
# calculate amplitude:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
reals = radicant >= 0
|
|
194
|
-
ampld = -np.ones_like(radicant)
|
|
195
|
-
ampld[reals] = np.sqrt(radicant[reals]) - 1.0
|
|
196
|
-
else:
|
|
197
|
-
ampld = np.sqrt(1.0 - ct / (8 * (sigma / D) ** 2)) - 1.0
|
|
193
|
+
term = 1.0 - ct / (8 * (sigma / D) ** 2)
|
|
194
|
+
ampld = np.sqrt(np.where(term > 0, term, 0)) - 1
|
|
198
195
|
|
|
199
196
|
# case no targets:
|
|
200
197
|
else:
|
|
@@ -6,12 +6,16 @@ import foxes.variables as FV
|
|
|
6
6
|
import foxes.constants as FC
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class Bastankhah2016Model(Model):
|
|
10
10
|
"""
|
|
11
11
|
Common calculations for the wake model and the wake
|
|
12
12
|
frame, such that code repetitions can be avoided.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Notes
|
|
15
|
+
-----
|
|
16
|
+
Reference:
|
|
17
|
+
"Experimental and theoretical study of wind turbine wakes in yawed conditions"
|
|
18
|
+
Majid Bastankhah, Fernando Porté-Agel
|
|
15
19
|
https://doi.org/10.1017/jfm.2016.595
|
|
16
20
|
|
|
17
21
|
Attributes
|
|
@@ -28,7 +32,7 @@ class PorteAgelModel(Model):
|
|
|
28
32
|
|
|
29
33
|
"""
|
|
30
34
|
|
|
31
|
-
MDATA_KEY = "
|
|
35
|
+
MDATA_KEY = "Bastankhah2016Model"
|
|
32
36
|
PARS = "pars"
|
|
33
37
|
CHECK = "check"
|
|
34
38
|
SP_SEL = "sp_sel"
|
|
@@ -355,16 +359,20 @@ class PorteAgelModel(Model):
|
|
|
355
359
|
del mdata[self.MDATA_KEY]
|
|
356
360
|
|
|
357
361
|
|
|
358
|
-
class
|
|
362
|
+
class Bastankhah2016(DistSlicedWakeModel):
|
|
359
363
|
"""
|
|
360
|
-
The Bastankhah
|
|
364
|
+
The Bastankhah 2016 wake model
|
|
361
365
|
|
|
362
|
-
|
|
366
|
+
Notes
|
|
367
|
+
-----
|
|
368
|
+
Reference:
|
|
369
|
+
"Experimental and theoretical study of wind turbine wakes in yawed conditions"
|
|
370
|
+
Majid Bastankhah, Fernando Porté-Agel
|
|
363
371
|
https://doi.org/10.1017/jfm.2016.595
|
|
364
372
|
|
|
365
373
|
Attributes
|
|
366
374
|
----------
|
|
367
|
-
model:
|
|
375
|
+
model: Bastankhah2016Model
|
|
368
376
|
The model for computing common data
|
|
369
377
|
K: float
|
|
370
378
|
The wake growth parameter k. If not given here
|
|
@@ -413,7 +421,7 @@ class PorteAgelWake(DistSlicedWakeModel):
|
|
|
413
421
|
"""
|
|
414
422
|
super().__init__(superpositions={FV.WS: superposition})
|
|
415
423
|
|
|
416
|
-
self.model =
|
|
424
|
+
self.model = Bastankhah2016Model(ct_max, alpha, beta)
|
|
417
425
|
self.k_var = k_var
|
|
418
426
|
|
|
419
427
|
setattr(self, k_var, k)
|
|
@@ -533,7 +541,7 @@ class PorteAgelWake(DistSlicedWakeModel):
|
|
|
533
541
|
)
|
|
534
542
|
|
|
535
543
|
# select targets:
|
|
536
|
-
sp_sel = self.model.get_data(
|
|
544
|
+
sp_sel = self.model.get_data(Bastankhah2016Model.SP_SEL, mdata)
|
|
537
545
|
n_sp_sel = np.sum(sp_sel)
|
|
538
546
|
wdeltas = {FV.WS: np.zeros((n_sp_sel, n_y_per_z), dtype=FC.DTYPE)}
|
|
539
547
|
if np.any(sp_sel):
|
|
@@ -541,15 +549,15 @@ class PorteAgelWake(DistSlicedWakeModel):
|
|
|
541
549
|
yz = yz[sp_sel]
|
|
542
550
|
|
|
543
551
|
# collect data:
|
|
544
|
-
near = self.model.get_data(
|
|
552
|
+
near = self.model.get_data(Bastankhah2016Model.NEAR, mdata)
|
|
545
553
|
far = ~near
|
|
546
554
|
|
|
547
555
|
# near wake:
|
|
548
556
|
if np.any(near):
|
|
549
557
|
# collect data:
|
|
550
|
-
ampl = self.model.get_data(
|
|
551
|
-
r_pc = self.model.get_data(
|
|
552
|
-
s = self.model.get_data(
|
|
558
|
+
ampl = self.model.get_data(Bastankhah2016Model.AMPL_NEAR, mdata)
|
|
559
|
+
r_pc = self.model.get_data(Bastankhah2016Model.R_PC, mdata)
|
|
560
|
+
s = self.model.get_data(Bastankhah2016Model.R_PC_S, mdata)
|
|
553
561
|
|
|
554
562
|
# radial dependency:
|
|
555
563
|
r = np.linalg.norm(yz[near], axis=-1)
|
|
@@ -569,11 +577,11 @@ class PorteAgelWake(DistSlicedWakeModel):
|
|
|
569
577
|
yz = yz[far]
|
|
570
578
|
|
|
571
579
|
# collect data:
|
|
572
|
-
ampl = self.model.get_data(
|
|
573
|
-
sigma_y = self.model.get_data(
|
|
580
|
+
ampl = self.model.get_data(Bastankhah2016Model.AMPL_FAR, mdata)[:, None]
|
|
581
|
+
sigma_y = self.model.get_data(Bastankhah2016Model.SIGMA_Y_FAR, mdata)[
|
|
574
582
|
:, None
|
|
575
583
|
]
|
|
576
|
-
sigma_z = self.model.get_data(
|
|
584
|
+
sigma_z = self.model.get_data(Bastankhah2016Model.SIGMA_Z_FAR, mdata)[
|
|
577
585
|
:, None
|
|
578
586
|
]
|
|
579
587
|
|
|
@@ -9,6 +9,11 @@ class TurbOParkWake(GaussianWakeModel):
|
|
|
9
9
|
"""
|
|
10
10
|
The TurbOPark wake model
|
|
11
11
|
|
|
12
|
+
Notes
|
|
13
|
+
-----
|
|
14
|
+
Reference:
|
|
15
|
+
"Turbulence Optimized Park model with Gaussian wake profile"
|
|
16
|
+
J G Pedersen, E Svensson, L Poulsen and N G Nygaard
|
|
12
17
|
https://iopscience.iop.org/article/10.1088/1742-6596/2265/2/022063/pdf
|
|
13
18
|
|
|
14
19
|
Attributes
|
|
@@ -181,8 +186,6 @@ class TurbOParkWake(GaussianWakeModel):
|
|
|
181
186
|
|
|
182
187
|
# calculate sigma:
|
|
183
188
|
sbeta = np.sqrt(0.5 * (1 + np.sqrt(1.0 - ct)) / np.sqrt(1.0 - ct))
|
|
184
|
-
# sblim = 1 / (np.sqrt(8) * self.sbeta_factor)
|
|
185
|
-
# sbeta[sbeta > sblim] = sblim
|
|
186
189
|
epsilon = self.sbeta_factor * sbeta
|
|
187
190
|
|
|
188
191
|
alpha = self.c1 * ati
|
|
@@ -208,20 +211,14 @@ class TurbOParkWake(GaussianWakeModel):
|
|
|
208
211
|
del (
|
|
209
212
|
x,
|
|
210
213
|
sbeta,
|
|
211
|
-
# sblim,
|
|
212
214
|
alpha,
|
|
213
215
|
beta,
|
|
214
216
|
epsilon,
|
|
215
217
|
)
|
|
216
218
|
|
|
217
|
-
# calculate amplitude, same as in
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
reals = radicant >= 0
|
|
221
|
-
ampld = -np.ones_like(radicant)
|
|
222
|
-
ampld[reals] = np.sqrt(radicant[reals]) - 1.0
|
|
223
|
-
else:
|
|
224
|
-
ampld = np.sqrt(1.0 - ct / (8 * (sigma / D) ** 2)) - 1.0
|
|
219
|
+
# calculate amplitude, same as in Bastankhah model (eqn 7)
|
|
220
|
+
term = 1.0 - ct / (8 * (sigma / D) ** 2)
|
|
221
|
+
ampld = np.sqrt(np.where(term > 0, term, 0)) - 1
|
|
225
222
|
|
|
226
223
|
# case no targets:
|
|
227
224
|
else:
|
|
@@ -428,8 +425,6 @@ class TurbOParkWakeIX(GaussianWakeModel):
|
|
|
428
425
|
|
|
429
426
|
# calculate sigma:
|
|
430
427
|
sbeta = np.sqrt(0.5 * (1 + np.sqrt(1.0 - ct)) / np.sqrt(1.0 - ct))
|
|
431
|
-
# sblim = 1 / (np.sqrt(8) * self.sbeta_factor)
|
|
432
|
-
# sbeta[sbeta > sblim] = sblim
|
|
433
428
|
epsilon = self.sbeta_factor * sbeta
|
|
434
429
|
|
|
435
430
|
# get TI by integration along centre line:
|
|
@@ -452,18 +447,12 @@ class TurbOParkWakeIX(GaussianWakeModel):
|
|
|
452
447
|
del (
|
|
453
448
|
x,
|
|
454
449
|
sbeta,
|
|
455
|
-
# sblim,
|
|
456
450
|
epsilon,
|
|
457
451
|
)
|
|
458
452
|
|
|
459
|
-
# calculate amplitude, same as in
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
reals = radicant >= 0
|
|
463
|
-
ampld = -np.ones_like(radicant)
|
|
464
|
-
ampld[reals] = np.sqrt(radicant[reals]) - 1.0
|
|
465
|
-
else:
|
|
466
|
-
ampld = np.sqrt(1.0 - ct / (8 * (sigma / D) ** 2)) - 1.0
|
|
453
|
+
# calculate amplitude, same as in Bastankhah model (eqn 7)
|
|
454
|
+
term = 1.0 - ct / (8 * (sigma / D) ** 2)
|
|
455
|
+
ampld = np.sqrt(np.where(term > 0, term, 0)) - 1
|
|
467
456
|
|
|
468
457
|
# case no targets:
|
|
469
458
|
else:
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Wake superposition models.
|
|
3
3
|
"""
|
|
4
|
-
from .
|
|
5
|
-
from .
|
|
6
|
-
from .
|
|
7
|
-
from .
|
|
8
|
-
from .
|
|
4
|
+
from .ws_linear import WSLinear
|
|
5
|
+
from .ws_quadratic import WSQuadratic
|
|
6
|
+
from .ws_pow import WSPow
|
|
7
|
+
from .ws_max import WSMax
|
|
8
|
+
from .ws_product import WSProduct
|
|
9
|
+
from .ti_linear import TILinear
|
|
10
|
+
from .ti_quadratic import TIQuadratic
|
|
11
|
+
from .ti_pow import TIPow
|
|
12
|
+
from .ti_max import TIMax
|