foxes 0.7.1__py3-none-any.whl → 0.7.3__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 +60 -46
- foxes/algorithms/downwind/models/farm_wakes_calc.py +17 -6
- foxes/algorithms/downwind/models/point_wakes_calc.py +13 -45
- foxes/algorithms/iterative/iterative.py +1 -1
- foxes/algorithms/iterative/models/farm_wakes_calc.py +18 -4
- foxes/constants.py +5 -0
- foxes/core/__init__.py +2 -1
- foxes/core/ground_model.py +254 -0
- foxes/core/model.py +3 -2
- foxes/core/partial_wakes_model.py +19 -3
- foxes/core/states.py +33 -0
- foxes/core/wake_model.py +138 -2
- foxes/data/__init__.py +1 -1
- foxes/data/states/WRF-Timeseries-3000.nc +0 -0
- foxes/data/states/windio_timeseries_5000.nc +0 -0
- foxes/data/static_data.py +7 -0
- foxes/data/windio/DTU_10MW_turbine.yaml +10 -0
- foxes/data/windio/__init__.py +0 -0
- foxes/data/windio/windio_5turbines_timeseries.yaml +63 -0
- foxes/input/states/__init__.py +1 -0
- foxes/input/states/multi_height.py +225 -6
- foxes/input/windio/__init__.py +6 -1
- foxes/input/windio/get_states.py +115 -0
- foxes/input/windio/read_attributes.py +321 -0
- foxes/input/windio/read_farm.py +163 -0
- foxes/input/windio/read_fields.py +164 -0
- foxes/input/windio/runner.py +105 -0
- foxes/input/windio/windio.py +136 -254
- foxes/models/__init__.py +2 -1
- foxes/models/ground_models/__init__.py +2 -0
- foxes/models/ground_models/no_ground.py +12 -0
- foxes/models/ground_models/wake_mirror.py +161 -0
- foxes/models/model_book.py +114 -164
- foxes/models/partial_wakes/axiwake.py +27 -4
- foxes/models/partial_wakes/top_hat.py +26 -4
- foxes/models/turbine_types/PCt_file.py +1 -0
- foxes/models/turbine_types/PCt_from_two.py +92 -0
- foxes/models/wake_frames/yawed_wakes.py +41 -38
- foxes/models/wake_models/__init__.py +0 -1
- foxes/models/wake_models/induction/__init__.py +1 -0
- foxes/models/wake_models/induction/rankine_half_body.py +1 -1
- foxes/models/wake_models/induction/vortex_sheet.py +227 -0
- foxes/models/wake_models/ti/crespo_hernandez.py +26 -24
- foxes/models/wake_models/ti/iec_ti.py +33 -26
- foxes/models/wake_models/wind/bastankhah14.py +11 -32
- foxes/models/wake_models/wind/bastankhah16.py +30 -34
- foxes/models/wake_models/wind/jensen.py +13 -29
- foxes/models/wake_models/wind/turbopark.py +31 -61
- foxes/models/wake_superpositions/ws_max.py +1 -0
- foxes/models/wake_superpositions/ws_pow.py +1 -0
- foxes/models/wake_superpositions/ws_quadratic.py +1 -0
- foxes/output/grids.py +6 -6
- foxes/output/output.py +6 -6
- foxes/utils/__init__.py +1 -1
- foxes/utils/factory.py +284 -76
- {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/METADATA +8 -6
- {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/RECORD +65 -51
- {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/WHEEL +1 -1
- foxes/models/wake_models/wake_mirror.py +0 -196
- /foxes/models/{axial_induction_models → axial_induction}/__init__.py +0 -0
- /foxes/models/{axial_induction_models → axial_induction}/betz.py +0 -0
- /foxes/models/{axial_induction_models → axial_induction}/madsen.py +0 -0
- {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/LICENSE +0 -0
- {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/top_level.txt +0 -0
- {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/zip-safe +0 -0
|
@@ -123,6 +123,7 @@ class PCtFromTwo(TurbineType):
|
|
|
123
123
|
|
|
124
124
|
def __repr__(self):
|
|
125
125
|
a = f"D={self.D}, H={self.H}, P_nominal={self.P_nominal}, P_unit={self.P_unit}, rho={self.rho}"
|
|
126
|
+
a += f", var_ws_ct={self.WSCT}, var_ws_P={self.WSP}"
|
|
126
127
|
return f"{type(self).__name__}({a})"
|
|
127
128
|
|
|
128
129
|
def output_farm_vars(self, algo):
|
|
@@ -196,6 +197,97 @@ class PCtFromTwo(TurbineType):
|
|
|
196
197
|
|
|
197
198
|
return super().load_data(algo, verbosity)
|
|
198
199
|
|
|
200
|
+
def modify_cutin(
|
|
201
|
+
self,
|
|
202
|
+
modify_ct,
|
|
203
|
+
modify_P,
|
|
204
|
+
steps=20,
|
|
205
|
+
iterations=100,
|
|
206
|
+
a=0.55,
|
|
207
|
+
b=0.55,
|
|
208
|
+
):
|
|
209
|
+
"""
|
|
210
|
+
Modify the data such that a discontinuity
|
|
211
|
+
at cutin wind speed is avoided
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
variable: str
|
|
216
|
+
The target variable
|
|
217
|
+
modify_ct: bool
|
|
218
|
+
Flag for modification of the ct curve
|
|
219
|
+
modify_P: bool
|
|
220
|
+
Flag for modification of the power curve
|
|
221
|
+
steps: int
|
|
222
|
+
The number of wind speed steps between 0 and
|
|
223
|
+
the cutin wind speed
|
|
224
|
+
iterations: int
|
|
225
|
+
The number of iterations
|
|
226
|
+
a: float
|
|
227
|
+
Coefficient for iterative mixing
|
|
228
|
+
b: float
|
|
229
|
+
Coefficient for iterative mixing
|
|
230
|
+
|
|
231
|
+
"""
|
|
232
|
+
if modify_ct:
|
|
233
|
+
|
|
234
|
+
ws = self._data_ws_ct
|
|
235
|
+
ct = self._data_ct
|
|
236
|
+
|
|
237
|
+
i = 0
|
|
238
|
+
try:
|
|
239
|
+
while i < len(ws) and (not modify_ct or ct[i] < 1e-5):
|
|
240
|
+
i += 1
|
|
241
|
+
except IndexError:
|
|
242
|
+
raise IndexError(
|
|
243
|
+
f"Turbine type '{self.name}': Failed not determine cutin wind speed. ws = {ws}, ct = {ct}"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
if ws[i] > 0:
|
|
247
|
+
ws = ws[i:]
|
|
248
|
+
ct = ct[i:]
|
|
249
|
+
|
|
250
|
+
new_ws = np.linspace(0.0, ws[0], steps + 1, dtype=ws.dtype)
|
|
251
|
+
new_ct = np.zeros_like(new_ws)
|
|
252
|
+
|
|
253
|
+
new_ct[-1] = ct[0]
|
|
254
|
+
for it in range(iterations):
|
|
255
|
+
new_ct[1:-1] = a * new_ct[:-2] + (1 - a) * new_ct[2:]
|
|
256
|
+
|
|
257
|
+
self._data_ws_ct = np.concatenate([new_ws[:-1], ws], axis=0)
|
|
258
|
+
self._data_ct = np.concatenate([new_ct[:-1], ct], axis=0)
|
|
259
|
+
|
|
260
|
+
if modify_P:
|
|
261
|
+
|
|
262
|
+
ws = self._data_ws_P
|
|
263
|
+
P = self._data_P
|
|
264
|
+
|
|
265
|
+
i = 0
|
|
266
|
+
try:
|
|
267
|
+
while i < len(ws) and (not modify_P or P[i] < 0.1):
|
|
268
|
+
i += 1
|
|
269
|
+
except IndexError:
|
|
270
|
+
raise IndexError(
|
|
271
|
+
f"Turbine type '{self.name}': Failed not determine cutin wind speed. ws = {ws}, P = {P}"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
if ws[i] > 0:
|
|
275
|
+
ws = ws[i:]
|
|
276
|
+
P = P[i:]
|
|
277
|
+
|
|
278
|
+
new_ws = np.linspace(0.0, ws[0], steps + 1, dtype=ws.dtype)
|
|
279
|
+
new_P = np.zeros_like(new_ws)
|
|
280
|
+
|
|
281
|
+
new_P[-1] = P[0]
|
|
282
|
+
for it in range(iterations):
|
|
283
|
+
new_P[1:-1] = b * new_P[:-2] + (1 - b) * new_P[2:]
|
|
284
|
+
|
|
285
|
+
self._data_ws_P = np.concatenate([new_ws[:-1], ws], axis=0)
|
|
286
|
+
self._data_P = np.concatenate([new_P[:-1], P], axis=0)
|
|
287
|
+
|
|
288
|
+
if not modify_ct and not modify_P:
|
|
289
|
+
super().modify_cutin(modify_ct, modify_P)
|
|
290
|
+
|
|
199
291
|
def calculate(self, algo, mdata, fdata, st_sel):
|
|
200
292
|
""" "
|
|
201
293
|
The main model calculation.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
-
from foxes.core import WakeFrame
|
|
3
|
+
from foxes.core import WakeFrame, WakeK, TData
|
|
4
4
|
from foxes.models.wake_models.wind.bastankhah16 import (
|
|
5
5
|
Bastankhah2016Model,
|
|
6
6
|
Bastankhah2016,
|
|
@@ -28,16 +28,11 @@ class YawedWakes(WakeFrame):
|
|
|
28
28
|
The model for computing common data
|
|
29
29
|
model_pars: dict
|
|
30
30
|
Model parameters
|
|
31
|
-
K: float
|
|
32
|
-
The wake growth parameter k. If not given here
|
|
33
|
-
it will be searched in the farm data.
|
|
34
31
|
YAWM: float
|
|
35
32
|
The yaw misalignment YAWM. If not given here
|
|
36
33
|
it will be searched in the farm data.
|
|
37
34
|
base_frame: foxes.core.WakeFrame
|
|
38
35
|
The wake frame from which to start
|
|
39
|
-
k_var: str
|
|
40
|
-
The variable name for k
|
|
41
36
|
|
|
42
37
|
:group: models.wake_frames
|
|
43
38
|
|
|
@@ -45,45 +40,46 @@ class YawedWakes(WakeFrame):
|
|
|
45
40
|
|
|
46
41
|
def __init__(
|
|
47
42
|
self,
|
|
48
|
-
k=None,
|
|
49
43
|
base_frame=RotorWD(),
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
alpha=0.58,
|
|
45
|
+
beta=0.07,
|
|
46
|
+
induction="Madsen",
|
|
47
|
+
**wake_k,
|
|
52
48
|
):
|
|
53
49
|
"""
|
|
54
50
|
Constructor.
|
|
55
51
|
|
|
56
52
|
Parameters
|
|
57
53
|
----------
|
|
58
|
-
k: float, optional
|
|
59
|
-
The wake growth parameter k. If not given here
|
|
60
|
-
it will be searched in the farm data, by default None
|
|
61
54
|
base_frame: foxes.core.WakeFrame
|
|
62
55
|
The wake frame from which to start
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
alpha: float
|
|
57
|
+
model parameter used to determine onset of far wake region,
|
|
58
|
+
if not found in wake model
|
|
59
|
+
beta: float
|
|
60
|
+
model parameter used to determine onset of far wake region,
|
|
67
61
|
if not found in wake model
|
|
62
|
+
induction: foxes.core.AxialInductionModel or str
|
|
63
|
+
The induction model, if not found in wake model
|
|
64
|
+
wake_k: dict, optional
|
|
65
|
+
Parameters for the WakeK class, if not found in wake model
|
|
68
66
|
|
|
69
67
|
"""
|
|
70
68
|
super().__init__()
|
|
71
69
|
|
|
72
70
|
self.base_frame = base_frame
|
|
73
71
|
self.model = None
|
|
74
|
-
self.
|
|
75
|
-
self.
|
|
72
|
+
self.alpha = alpha
|
|
73
|
+
self.beta = beta
|
|
74
|
+
self.induction = induction
|
|
75
|
+
self.wake_k = None
|
|
76
|
+
self._wake_k_pars = wake_k
|
|
76
77
|
|
|
77
|
-
setattr(self, k_var, k)
|
|
78
78
|
setattr(self, FV.YAWM, 0.0)
|
|
79
79
|
|
|
80
80
|
def __repr__(self):
|
|
81
|
-
k = getattr(self, self.k_var)
|
|
82
81
|
s = f"{type(self).__name__}("
|
|
83
|
-
if
|
|
84
|
-
s += f"k_var={self.k_var}"
|
|
85
|
-
else:
|
|
86
|
-
s += f"{self.k_var}={k}"
|
|
82
|
+
s += self.wake_k.repr() if self.wake_k is not None else ""
|
|
87
83
|
s += ")"
|
|
88
84
|
return s
|
|
89
85
|
|
|
@@ -97,7 +93,7 @@ class YawedWakes(WakeFrame):
|
|
|
97
93
|
Names of all sub models
|
|
98
94
|
|
|
99
95
|
"""
|
|
100
|
-
return [self.base_frame, self.model]
|
|
96
|
+
return [self.wake_k, self.base_frame, self.model]
|
|
101
97
|
|
|
102
98
|
def initialize(self, algo, verbosity=0, force=False):
|
|
103
99
|
"""
|
|
@@ -114,20 +110,26 @@ class YawedWakes(WakeFrame):
|
|
|
114
110
|
|
|
115
111
|
"""
|
|
116
112
|
if not self.initialized:
|
|
117
|
-
for w in algo.wake_models:
|
|
113
|
+
for w in algo.wake_models.values():
|
|
118
114
|
if isinstance(w, Bastankhah2016):
|
|
119
115
|
if not w.initialized:
|
|
120
116
|
w.initialize(algo, verbosity, force)
|
|
121
117
|
self.model = w.model
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
self.wake_k = w.wake_k
|
|
119
|
+
break
|
|
124
120
|
if self.model is None:
|
|
125
|
-
self.model = Bastankhah2016Model(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
self.model = Bastankhah2016Model(
|
|
122
|
+
alpha=self.alpha, beta=self.beta, induction=self.induction
|
|
123
|
+
)
|
|
124
|
+
if self.wake_k is None:
|
|
125
|
+
wake_k = WakeK(**self._wake_k_pars)
|
|
126
|
+
if not wake_k.all_none:
|
|
127
|
+
self.wake_k = wake_k
|
|
128
|
+
else:
|
|
129
|
+
for w in algo.wake_models.values():
|
|
130
|
+
if hasattr(w, "wake_k"):
|
|
131
|
+
self.wake_k = w.wake_k
|
|
132
|
+
break
|
|
131
133
|
|
|
132
134
|
super().initialize(algo, verbosity, force)
|
|
133
135
|
|
|
@@ -175,10 +177,10 @@ class YawedWakes(WakeFrame):
|
|
|
175
177
|
gamma *= np.pi / 180
|
|
176
178
|
|
|
177
179
|
# get k:
|
|
178
|
-
k = self.
|
|
179
|
-
self.k_var,
|
|
180
|
+
k = self.wake_k(
|
|
180
181
|
FC.STATE_TARGET,
|
|
181
|
-
|
|
182
|
+
lookup_ti="f",
|
|
183
|
+
lookup_k="sf",
|
|
182
184
|
algo=algo,
|
|
183
185
|
fdata=fdata,
|
|
184
186
|
tdata=tdata,
|
|
@@ -299,6 +301,7 @@ class YawedWakes(WakeFrame):
|
|
|
299
301
|
points = self.base_frame.get_centreline_points(
|
|
300
302
|
algo, mdata, fdata, downwind_index, x
|
|
301
303
|
)
|
|
304
|
+
tdata = TData.from_points(points)
|
|
302
305
|
|
|
303
306
|
nx = np.zeros_like(points)
|
|
304
307
|
nx[:, 0] = points[:, 1] - points[:, 0]
|
|
@@ -314,7 +317,7 @@ class YawedWakes(WakeFrame):
|
|
|
314
317
|
del nx, nz
|
|
315
318
|
|
|
316
319
|
y = np.zeros_like(x)
|
|
317
|
-
self._update_y(algo, mdata, fdata,
|
|
320
|
+
self._update_y(algo, mdata, fdata, tdata, downwind_index, x, y)
|
|
318
321
|
|
|
319
322
|
points += y[:, :, None] * ny
|
|
320
323
|
|
|
@@ -6,7 +6,6 @@ from .dist_sliced import DistSlicedWakeModel
|
|
|
6
6
|
from .axisymmetric import AxisymmetricWakeModel
|
|
7
7
|
from .top_hat import TopHatWakeModel
|
|
8
8
|
from .gaussian import GaussianWakeModel
|
|
9
|
-
from .wake_mirror import WakeMirror, GroundMirror
|
|
10
9
|
|
|
11
10
|
from . import wind
|
|
12
11
|
from . import ti
|
|
@@ -195,7 +195,7 @@ class RankineHalfBody(TurbineInductionModel):
|
|
|
195
195
|
)
|
|
196
196
|
|
|
197
197
|
# stagnation point condition
|
|
198
|
-
xs = -np.sqrt(m / (4 * ws))
|
|
198
|
+
xs = -np.sqrt(m / (4 * ws + 1e-15))
|
|
199
199
|
|
|
200
200
|
# set values out of body shape
|
|
201
201
|
st_sel = (ct > 0) & ((RHB_shape < -1) | (x < xs))
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from foxes.core import TurbineInductionModel
|
|
4
|
+
from foxes.utils import uv2wd, wd2uv
|
|
5
|
+
import foxes.variables as FV
|
|
6
|
+
import foxes.constants as FC
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class VortexSheet(TurbineInductionModel):
|
|
10
|
+
"""
|
|
11
|
+
The Vortex Sheet model implemented with a radial dependency
|
|
12
|
+
|
|
13
|
+
Ref: Medici, D., et al. "The upstream flow of a wind turbine: blockage effect." Wind Energy 14.5 (2011): 691-697.
|
|
14
|
+
https://doi.org/10.1002/we.451
|
|
15
|
+
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
pre_rotor_only: bool
|
|
19
|
+
Calculate only the pre-rotor region
|
|
20
|
+
induction: foxes.core.AxialInductionModel or str
|
|
21
|
+
The induction model
|
|
22
|
+
|
|
23
|
+
:group: models.wake_models.induction
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, pre_rotor_only=False, induction="Madsen"):
|
|
28
|
+
"""
|
|
29
|
+
Constructor.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
pre_rotor_only: bool
|
|
34
|
+
Calculate only the pre-rotor region
|
|
35
|
+
induction: foxes.core.AxialInductionModel or str
|
|
36
|
+
The induction model
|
|
37
|
+
|
|
38
|
+
:group: models.wake_models.induction
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
super().__init__()
|
|
42
|
+
self.induction = induction
|
|
43
|
+
self.pre_rotor_only = pre_rotor_only
|
|
44
|
+
|
|
45
|
+
def __repr__(self):
|
|
46
|
+
iname = (
|
|
47
|
+
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
48
|
+
)
|
|
49
|
+
return f"{type(self).__name__}, induction={iname})"
|
|
50
|
+
|
|
51
|
+
def sub_models(self):
|
|
52
|
+
"""
|
|
53
|
+
List of all sub-models
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
smdls: list of foxes.core.Model
|
|
58
|
+
All sub models
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
return [self.induction]
|
|
62
|
+
|
|
63
|
+
def initialize(self, algo, verbosity=0, force=False):
|
|
64
|
+
"""
|
|
65
|
+
Initializes the model.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
algo: foxes.core.Algorithm
|
|
70
|
+
The calculation algorithm
|
|
71
|
+
verbosity: int
|
|
72
|
+
The verbosity level, 0 = silent
|
|
73
|
+
force: bool
|
|
74
|
+
Overwrite existing data
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
if isinstance(self.induction, str):
|
|
78
|
+
self.induction = algo.mbook.axial_induction[self.induction]
|
|
79
|
+
super().initialize(algo, verbosity, force)
|
|
80
|
+
|
|
81
|
+
def new_wake_deltas(self, algo, mdata, fdata, tdata):
|
|
82
|
+
"""
|
|
83
|
+
Initialize wake delta storage.
|
|
84
|
+
|
|
85
|
+
They are added on the fly to the wake_deltas dict.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
algo: foxes.core.Algorithm
|
|
90
|
+
The calculation algorithm
|
|
91
|
+
mdata: foxes.core.Data
|
|
92
|
+
The model data
|
|
93
|
+
fdata: foxes.core.Data
|
|
94
|
+
The farm data
|
|
95
|
+
pdata: foxes.core.Data
|
|
96
|
+
The evaluation point data
|
|
97
|
+
wake_deltas: dict
|
|
98
|
+
The wake deltas storage, add wake deltas
|
|
99
|
+
on the fly. Keys: Variable name str, for which the
|
|
100
|
+
wake delta applies, values: numpy.ndarray with
|
|
101
|
+
shape (n_states, n_points, ...)
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
return {FV.WS: np.zeros_like(tdata[FC.TARGETS][..., 0])}
|
|
105
|
+
|
|
106
|
+
def contribute(
|
|
107
|
+
self,
|
|
108
|
+
algo,
|
|
109
|
+
mdata,
|
|
110
|
+
fdata,
|
|
111
|
+
tdata,
|
|
112
|
+
downwind_index,
|
|
113
|
+
wake_coos,
|
|
114
|
+
wake_deltas,
|
|
115
|
+
):
|
|
116
|
+
"""
|
|
117
|
+
Modifies wake deltas at target points by
|
|
118
|
+
contributions from the specified wake source turbines.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
algo: foxes.core.Algorithm
|
|
123
|
+
The calculation algorithm
|
|
124
|
+
mdata: foxes.core.Data
|
|
125
|
+
The model data
|
|
126
|
+
fdata: foxes.core.Data
|
|
127
|
+
The farm data
|
|
128
|
+
tdata: foxes.core.Data
|
|
129
|
+
The target point data
|
|
130
|
+
downwind_index: int
|
|
131
|
+
The index of the wake causing turbine
|
|
132
|
+
in the downwnd order
|
|
133
|
+
wake_coos: numpy.ndarray
|
|
134
|
+
The wake frame coordinates of the evaluation
|
|
135
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
136
|
+
wake_deltas: dict
|
|
137
|
+
The wake deltas. Key: variable name,
|
|
138
|
+
value: numpy.ndarray with shape
|
|
139
|
+
(n_states, n_targets, n_tpoints, ...)
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
# get x, y and z. Rounding for safe x < 0 condition
|
|
144
|
+
x = np.round(wake_coos[..., 0], 12)
|
|
145
|
+
y = wake_coos[..., 1]
|
|
146
|
+
z = wake_coos[..., 2]
|
|
147
|
+
r = np.sqrt(y**2 + z**2)
|
|
148
|
+
r_sph = np.sqrt(r**2 + x**2)
|
|
149
|
+
|
|
150
|
+
# get ct
|
|
151
|
+
ct = self.get_data(
|
|
152
|
+
FV.CT,
|
|
153
|
+
FC.STATE_TARGET_TPOINT,
|
|
154
|
+
lookup="w",
|
|
155
|
+
algo=algo,
|
|
156
|
+
fdata=fdata,
|
|
157
|
+
tdata=tdata,
|
|
158
|
+
upcast=True,
|
|
159
|
+
downwind_index=downwind_index,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# get ws
|
|
163
|
+
ws = self.get_data(
|
|
164
|
+
FV.REWS,
|
|
165
|
+
FC.STATE_TARGET_TPOINT,
|
|
166
|
+
lookup="w",
|
|
167
|
+
algo=algo,
|
|
168
|
+
fdata=fdata,
|
|
169
|
+
tdata=tdata,
|
|
170
|
+
upcast=True,
|
|
171
|
+
downwind_index=downwind_index,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# get D
|
|
175
|
+
D = self.get_data(
|
|
176
|
+
FV.D,
|
|
177
|
+
FC.STATE_TARGET_TPOINT,
|
|
178
|
+
lookup="w",
|
|
179
|
+
algo=algo,
|
|
180
|
+
fdata=fdata,
|
|
181
|
+
tdata=tdata,
|
|
182
|
+
upcast=True,
|
|
183
|
+
downwind_index=downwind_index,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
sp_sel = (ct > 0) & (x <= 0)
|
|
187
|
+
ws_sel = ws[sp_sel]
|
|
188
|
+
ct_sel = ct[sp_sel]
|
|
189
|
+
r_sph_sel = r_sph[sp_sel]
|
|
190
|
+
D_sel = D[sp_sel]
|
|
191
|
+
|
|
192
|
+
if np.any(sp_sel):
|
|
193
|
+
blockage = (
|
|
194
|
+
ws_sel
|
|
195
|
+
* (
|
|
196
|
+
1
|
|
197
|
+
- self.induction.ct2a(ct_sel)
|
|
198
|
+
* ((1 + 2 * r_sph_sel / D_sel) * (1 + (2 * r_sph_sel / D_sel) ** 2))
|
|
199
|
+
** (-0.5)
|
|
200
|
+
)
|
|
201
|
+
) - ws_sel
|
|
202
|
+
wake_deltas[FV.WS][sp_sel] += blockage
|
|
203
|
+
|
|
204
|
+
if not self.pre_rotor_only:
|
|
205
|
+
sp_sel = (
|
|
206
|
+
(ct > 0) & (x > 0) & (r > D / 2)
|
|
207
|
+
) # mirror in rotor plane and inverse blockage, but not directly behind rotor
|
|
208
|
+
ws_sel = ws[sp_sel]
|
|
209
|
+
ct_sel = ct[sp_sel]
|
|
210
|
+
r_sph_sel = r_sph[sp_sel]
|
|
211
|
+
D_sel = D[sp_sel]
|
|
212
|
+
if np.any(sp_sel):
|
|
213
|
+
blockage = (
|
|
214
|
+
ws_sel
|
|
215
|
+
* (
|
|
216
|
+
1
|
|
217
|
+
- self.induction.ct2a(ct_sel)
|
|
218
|
+
* (
|
|
219
|
+
(1 + 2 * r_sph_sel / D_sel)
|
|
220
|
+
* (1 + (2 * r_sph_sel / D_sel) ** 2)
|
|
221
|
+
)
|
|
222
|
+
** (-0.5)
|
|
223
|
+
)
|
|
224
|
+
) - ws_sel
|
|
225
|
+
wake_deltas[FV.WS][sp_sel] += -blockage
|
|
226
|
+
|
|
227
|
+
return wake_deltas
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
+
from foxes.core import WakeK
|
|
3
4
|
from foxes.models.wake_models.top_hat import TopHatWakeModel
|
|
4
5
|
import foxes.variables as FV
|
|
5
6
|
import foxes.constants as FC
|
|
@@ -21,9 +22,6 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
21
22
|
|
|
22
23
|
Attributes
|
|
23
24
|
----------
|
|
24
|
-
k: float
|
|
25
|
-
The wake growth parameter k. If not given here
|
|
26
|
-
it will be searched in the farm data.
|
|
27
25
|
a_near: float
|
|
28
26
|
Model parameter
|
|
29
27
|
a_far: float
|
|
@@ -42,8 +40,8 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
42
40
|
near_wake_D: float
|
|
43
41
|
The near wake distance in units of D,
|
|
44
42
|
calculated from TI and ct if None
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
wake_k: foxes.core.WakeK
|
|
44
|
+
Handler for the wake growth parameter k
|
|
47
45
|
|
|
48
46
|
:group: models.wake_models.ti
|
|
49
47
|
|
|
@@ -52,7 +50,6 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
52
50
|
def __init__(
|
|
53
51
|
self,
|
|
54
52
|
superposition,
|
|
55
|
-
k=None,
|
|
56
53
|
use_ambti=False,
|
|
57
54
|
sbeta_factor=0.25,
|
|
58
55
|
near_wake_D=None,
|
|
@@ -61,8 +58,8 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
61
58
|
e1=0.83,
|
|
62
59
|
e2=-0.0325,
|
|
63
60
|
e3=-0.32,
|
|
64
|
-
|
|
65
|
-
**
|
|
61
|
+
induction="Betz",
|
|
62
|
+
**wake_k,
|
|
66
63
|
):
|
|
67
64
|
"""
|
|
68
65
|
Constructor.
|
|
@@ -94,11 +91,13 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
94
91
|
Model parameter
|
|
95
92
|
k_var: str
|
|
96
93
|
The variable name for k
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
induction: foxes.core.AxialInductionModel or str
|
|
95
|
+
The induction model
|
|
96
|
+
wake_k: dict, optional
|
|
97
|
+
Parameters for the WakeK class
|
|
99
98
|
|
|
100
99
|
"""
|
|
101
|
-
super().__init__(superpositions={FV.TI: superposition},
|
|
100
|
+
super().__init__(superpositions={FV.TI: superposition}, induction=induction)
|
|
102
101
|
|
|
103
102
|
self.a_near = a_near
|
|
104
103
|
self.a_far = a_far
|
|
@@ -108,24 +107,29 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
108
107
|
self.use_ambti = use_ambti
|
|
109
108
|
self.sbeta_factor = sbeta_factor
|
|
110
109
|
self.near_wake_D = near_wake_D
|
|
111
|
-
self.
|
|
112
|
-
|
|
113
|
-
setattr(self, k_var, k)
|
|
110
|
+
self.wake_k = WakeK(**wake_k)
|
|
114
111
|
|
|
115
112
|
def __repr__(self):
|
|
116
|
-
k = getattr(self, self.k_var)
|
|
117
113
|
iname = (
|
|
118
114
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
119
115
|
)
|
|
120
116
|
s = f"{type(self).__name__}"
|
|
121
|
-
s += f"({self.superpositions[FV.TI]}, induction={iname}"
|
|
122
|
-
|
|
123
|
-
s += f", k_var={self.k_var}"
|
|
124
|
-
else:
|
|
125
|
-
s += f", {self.k_var}={k}"
|
|
126
|
-
s += ")"
|
|
117
|
+
s += f"({self.superpositions[FV.TI]}, induction={iname}, "
|
|
118
|
+
s += self.wake_k.repr() + ")"
|
|
127
119
|
return s
|
|
128
120
|
|
|
121
|
+
def sub_models(self):
|
|
122
|
+
"""
|
|
123
|
+
List of all sub-models
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
smdls: list of foxes.core.Model
|
|
128
|
+
All sub models
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
return [self.wake_k]
|
|
132
|
+
|
|
129
133
|
def new_wake_deltas(self, algo, mdata, fdata, tdata):
|
|
130
134
|
"""
|
|
131
135
|
Creates new empty wake delta arrays.
|
|
@@ -200,10 +204,8 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
200
204
|
)
|
|
201
205
|
|
|
202
206
|
# get k:
|
|
203
|
-
k = self.
|
|
204
|
-
self.k_var,
|
|
207
|
+
k = self.wake_k(
|
|
205
208
|
FC.STATE_TARGET,
|
|
206
|
-
lookup="sw",
|
|
207
209
|
algo=algo,
|
|
208
210
|
fdata=fdata,
|
|
209
211
|
tdata=tdata,
|