foxes 1.3__py3-none-any.whl → 1.4__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.
- docs/source/conf.py +3 -3
- examples/abl_states/run.py +2 -2
- examples/compare_rotors_pwakes/run.py +1 -1
- examples/compare_wakes/run.py +1 -2
- examples/dyn_wakes/run.py +29 -6
- examples/induction/run.py +3 -3
- examples/multi_height/run.py +1 -1
- examples/power_mask/run.py +2 -2
- examples/quickstart/run.py +0 -1
- examples/random_timeseries/run.py +3 -4
- examples/scan_row/run.py +3 -3
- examples/sequential/run.py +33 -10
- examples/single_state/run.py +3 -4
- examples/states_lookup_table/run.py +3 -3
- examples/streamline_wakes/run.py +27 -4
- examples/tab_file/run.py +3 -3
- examples/timelines/run.py +29 -5
- examples/timeseries/run.py +3 -3
- examples/timeseries_slurm/run.py +3 -3
- examples/wind_rose/run.py +3 -3
- examples/yawed_wake/run.py +16 -8
- foxes/__init__.py +21 -17
- foxes/algorithms/__init__.py +6 -6
- foxes/algorithms/downwind/__init__.py +2 -2
- foxes/algorithms/downwind/downwind.py +44 -12
- foxes/algorithms/downwind/models/__init__.py +6 -6
- foxes/algorithms/downwind/models/farm_wakes_calc.py +11 -9
- foxes/algorithms/downwind/models/init_farm_data.py +0 -1
- foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
- foxes/algorithms/downwind/models/set_amb_point_results.py +6 -6
- foxes/algorithms/iterative/__init__.py +7 -3
- foxes/algorithms/iterative/iterative.py +1 -2
- foxes/algorithms/iterative/models/__init__.py +7 -3
- foxes/algorithms/iterative/models/farm_wakes_calc.py +9 -5
- foxes/algorithms/sequential/__init__.py +3 -3
- foxes/algorithms/sequential/models/__init__.py +2 -2
- foxes/algorithms/sequential/sequential.py +3 -4
- foxes/config/__init__.py +5 -1
- foxes/constants.py +16 -0
- foxes/core/__init__.py +45 -22
- foxes/core/algorithm.py +0 -1
- foxes/core/data.py +19 -18
- foxes/core/engine.py +9 -13
- foxes/core/farm_controller.py +2 -2
- foxes/core/ground_model.py +4 -13
- foxes/core/model.py +5 -5
- foxes/core/partial_wakes_model.py +147 -10
- foxes/core/point_data_model.py +2 -3
- foxes/core/rotor_model.py +3 -3
- foxes/core/states.py +2 -3
- foxes/core/turbine.py +2 -1
- foxes/core/wake_deflection.py +130 -0
- foxes/core/wake_model.py +222 -9
- foxes/core/wake_superposition.py +122 -4
- foxes/core/wind_farm.py +6 -6
- foxes/data/__init__.py +7 -2
- foxes/data/states/weibull_sectors_12.csv +13 -0
- foxes/data/states/weibull_sectors_12.nc +0 -0
- foxes/engines/__init__.py +14 -15
- foxes/engines/dask.py +39 -14
- foxes/engines/numpy.py +0 -3
- foxes/input/__init__.py +3 -3
- foxes/input/farm_layout/__init__.py +8 -8
- foxes/input/farm_layout/from_csv.py +1 -1
- foxes/input/farm_layout/ring.py +0 -1
- foxes/input/states/__init__.py +22 -12
- foxes/input/states/create/__init__.py +3 -2
- foxes/input/states/field_data_nc.py +10 -24
- foxes/input/states/multi_height.py +9 -6
- foxes/input/states/one_point_flow.py +0 -4
- foxes/input/states/single.py +1 -1
- foxes/input/states/states_table.py +10 -7
- foxes/input/states/weibull_sectors.py +225 -0
- foxes/input/states/wrg_states.py +7 -5
- foxes/input/yaml/__init__.py +9 -3
- foxes/input/yaml/dict.py +19 -19
- foxes/input/yaml/windio/__init__.py +10 -5
- foxes/input/yaml/windio/read_attributes.py +2 -2
- foxes/input/yaml/windio/read_farm.py +5 -5
- foxes/input/yaml/windio/read_fields.py +4 -2
- foxes/input/yaml/windio/read_site.py +52 -0
- foxes/input/yaml/windio/windio.py +1 -1
- foxes/models/__init__.py +15 -14
- foxes/models/axial_induction/__init__.py +2 -2
- foxes/models/farm_controllers/__init__.py +1 -1
- foxes/models/farm_models/__init__.py +1 -1
- foxes/models/ground_models/__init__.py +3 -2
- foxes/models/ground_models/wake_mirror.py +3 -3
- foxes/models/model_book.py +175 -49
- foxes/models/partial_wakes/__init__.py +6 -6
- foxes/models/partial_wakes/axiwake.py +30 -5
- foxes/models/partial_wakes/centre.py +47 -0
- foxes/models/partial_wakes/rotor_points.py +41 -11
- foxes/models/partial_wakes/segregated.py +2 -25
- foxes/models/partial_wakes/top_hat.py +27 -2
- foxes/models/point_models/__init__.py +4 -4
- foxes/models/rotor_models/__init__.py +3 -3
- foxes/models/turbine_models/__init__.py +11 -11
- foxes/models/turbine_models/set_farm_vars.py +0 -1
- foxes/models/turbine_types/PCt_file.py +0 -2
- foxes/models/turbine_types/PCt_from_two.py +0 -2
- foxes/models/turbine_types/__init__.py +9 -9
- foxes/models/vertical_profiles/__init__.py +7 -7
- foxes/models/wake_deflections/__init__.py +3 -0
- foxes/models/{wake_frames/yawed_wakes.py → wake_deflections/bastankhah2016.py} +32 -111
- foxes/models/wake_deflections/jimenez.py +277 -0
- foxes/models/wake_deflections/no_deflection.py +94 -0
- foxes/models/wake_frames/__init__.py +6 -7
- foxes/models/wake_frames/dynamic_wakes.py +12 -3
- foxes/models/wake_frames/rotor_wd.py +3 -1
- foxes/models/wake_frames/seq_dynamic_wakes.py +41 -7
- foxes/models/wake_frames/streamlines.py +8 -6
- foxes/models/wake_frames/timelines.py +9 -3
- foxes/models/wake_models/__init__.py +7 -7
- foxes/models/wake_models/dist_sliced.py +50 -84
- foxes/models/wake_models/gaussian.py +20 -0
- foxes/models/wake_models/induction/__init__.py +5 -5
- foxes/models/wake_models/induction/rankine_half_body.py +30 -71
- foxes/models/wake_models/induction/rathmann.py +65 -64
- foxes/models/wake_models/induction/self_similar.py +65 -68
- foxes/models/wake_models/induction/self_similar2020.py +0 -3
- foxes/models/wake_models/induction/vortex_sheet.py +71 -75
- foxes/models/wake_models/ti/__init__.py +2 -2
- foxes/models/wake_models/ti/crespo_hernandez.py +5 -3
- foxes/models/wake_models/ti/iec_ti.py +6 -4
- foxes/models/wake_models/top_hat.py +58 -7
- foxes/models/wake_models/wind/__init__.py +6 -4
- foxes/models/wake_models/wind/bastankhah14.py +25 -7
- foxes/models/wake_models/wind/bastankhah16.py +35 -3
- foxes/models/wake_models/wind/jensen.py +15 -2
- foxes/models/wake_models/wind/turbopark.py +28 -2
- foxes/models/wake_superpositions/__init__.py +18 -9
- foxes/models/wake_superpositions/ti_linear.py +4 -4
- foxes/models/wake_superpositions/ti_max.py +4 -4
- foxes/models/wake_superpositions/ti_pow.py +4 -4
- foxes/models/wake_superpositions/ti_quadratic.py +4 -4
- foxes/models/wake_superpositions/wind_vector.py +257 -0
- foxes/models/wake_superpositions/ws_linear.py +9 -10
- foxes/models/wake_superpositions/ws_max.py +8 -8
- foxes/models/wake_superpositions/ws_pow.py +8 -8
- foxes/models/wake_superpositions/ws_product.py +4 -4
- foxes/models/wake_superpositions/ws_quadratic.py +8 -8
- foxes/output/__init__.py +21 -19
- foxes/output/farm_layout.py +2 -2
- foxes/output/farm_results_eval.py +15 -15
- foxes/output/flow_plots_2d/__init__.py +2 -2
- foxes/output/flow_plots_2d/get_fig.py +4 -2
- foxes/output/rose_plot.py +3 -3
- foxes/output/seq_plugins/__init__.py +2 -2
- foxes/output/seq_plugins/seq_flow_ani_plugin.py +0 -3
- foxes/output/seq_plugins/seq_wake_debug_plugin.py +0 -1
- foxes/output/turbine_type_curves.py +7 -8
- foxes/utils/__init__.py +37 -19
- foxes/utils/abl/__init__.py +4 -4
- foxes/utils/cubic_roots.py +1 -1
- foxes/utils/data_book.py +4 -3
- foxes/utils/dict.py +3 -3
- foxes/utils/exec_python.py +5 -5
- foxes/utils/factory.py +1 -3
- foxes/utils/geom2d/__init__.py +7 -5
- foxes/utils/geopandas_utils.py +2 -2
- foxes/utils/pandas_utils.py +4 -3
- foxes/utils/tab_files.py +0 -1
- foxes/utils/weibull.py +28 -0
- foxes/utils/wrg_utils.py +3 -1
- foxes/utils/xarray_utils.py +9 -2
- foxes/variables.py +67 -9
- {foxes-1.3.dist-info → foxes-1.4.dist-info}/METADATA +6 -15
- foxes-1.4.dist-info/RECORD +320 -0
- {foxes-1.3.dist-info → foxes-1.4.dist-info}/WHEEL +1 -1
- tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +2 -3
- tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +1 -1
- tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
- tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +0 -1
- tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +0 -2
- tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +0 -1
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +0 -1
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +0 -1
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +0 -1
- tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +0 -1
- tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
- tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +0 -1
- tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +0 -1
- tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +0 -1
- foxes/output/round.py +0 -10
- foxes/utils/pandas_helpers.py +0 -178
- foxes-1.3.dist-info/RECORD +0 -313
- {foxes-1.3.dist-info → foxes-1.4.dist-info}/entry_points.txt +0 -0
- {foxes-1.3.dist-info → foxes-1.4.dist-info/licenses}/LICENSE +0 -0
- {foxes-1.3.dist-info → foxes-1.4.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
+
from foxes.config import config
|
|
3
4
|
from foxes.core import TurbineInductionModel
|
|
4
5
|
import foxes.variables as FV
|
|
5
6
|
import foxes.constants as FC
|
|
@@ -10,9 +11,6 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
10
11
|
The self-similar induction wake model
|
|
11
12
|
from Troldborg and Meyer Forsting
|
|
12
13
|
|
|
13
|
-
The individual wake effects are superposed linearly,
|
|
14
|
-
without invoking a wake superposition model.
|
|
15
|
-
|
|
16
14
|
Notes
|
|
17
15
|
-----
|
|
18
16
|
References:
|
|
@@ -60,17 +58,29 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
60
58
|
Calculate only the pre-rotor region
|
|
61
59
|
|
|
62
60
|
"""
|
|
63
|
-
super().__init__()
|
|
61
|
+
super().__init__(wind_superposition=superposition)
|
|
64
62
|
self.induction = induction
|
|
65
63
|
self.pre_rotor_only = pre_rotor_only
|
|
66
64
|
self.gamma = gamma
|
|
67
|
-
self._superp_name = superposition
|
|
68
65
|
|
|
69
66
|
def __repr__(self):
|
|
70
67
|
iname = (
|
|
71
68
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
72
69
|
)
|
|
73
|
-
return f"{type(self).__name__}({self.
|
|
70
|
+
return f"{type(self).__name__}({self.wind_superposition}, induction={iname}, gamma={self.gamma})"
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def affects_ws(self):
|
|
74
|
+
"""
|
|
75
|
+
Flag for wind speed wake models
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
dws: bool
|
|
80
|
+
If True, this model affects wind speed
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
return True
|
|
74
84
|
|
|
75
85
|
def sub_models(self):
|
|
76
86
|
"""
|
|
@@ -82,7 +92,7 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
82
92
|
All sub models
|
|
83
93
|
|
|
84
94
|
"""
|
|
85
|
-
return
|
|
95
|
+
return super().sub_models() + [self.induction]
|
|
86
96
|
|
|
87
97
|
def initialize(self, algo, verbosity=0, force=False):
|
|
88
98
|
"""
|
|
@@ -98,7 +108,6 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
98
108
|
Overwrite existing data
|
|
99
109
|
|
|
100
110
|
"""
|
|
101
|
-
self._superp = algo.mbook.wake_superpositions[self._superp_name]
|
|
102
111
|
if isinstance(self.induction, str):
|
|
103
112
|
self.induction = algo.mbook.axial_induction[self.induction]
|
|
104
113
|
super().initialize(algo, verbosity, force)
|
|
@@ -122,10 +131,21 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
122
131
|
-------
|
|
123
132
|
wake_deltas: dict
|
|
124
133
|
Key: variable name, value: The zero filled
|
|
125
|
-
wake deltas, shape: (n_states,
|
|
134
|
+
wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
|
|
126
135
|
|
|
127
136
|
"""
|
|
128
|
-
|
|
137
|
+
if self.has_uv:
|
|
138
|
+
duv = np.zeros(
|
|
139
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
|
|
140
|
+
dtype=config.dtype_double,
|
|
141
|
+
)
|
|
142
|
+
return {FV.UV: duv}
|
|
143
|
+
else:
|
|
144
|
+
dws = np.zeros(
|
|
145
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints),
|
|
146
|
+
dtype=config.dtype_double,
|
|
147
|
+
)
|
|
148
|
+
return {FV.WS: dws}
|
|
129
149
|
|
|
130
150
|
def _mu(self, x_R):
|
|
131
151
|
"""Helper function: define mu (eqn 11 from [1])"""
|
|
@@ -211,6 +231,39 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
211
231
|
x_R = np.round(wake_coos[..., 0] / R, 12)
|
|
212
232
|
r_R = np.linalg.norm(wake_coos[..., 1:3], axis=-1) / R
|
|
213
233
|
|
|
234
|
+
def add_wake(sp_sel, wake_deltas, blockage):
|
|
235
|
+
"""adds to wake deltas"""
|
|
236
|
+
if self.has_uv:
|
|
237
|
+
assert self.has_vector_wind_superp, (
|
|
238
|
+
f"Wake model {self.name}: Missing vector wind superposition, got '{self.wind_superposition}'"
|
|
239
|
+
)
|
|
240
|
+
wdeltas = {FV.WS: blockage}
|
|
241
|
+
self.vec_superp.wdeltas_ws2uv(
|
|
242
|
+
algo, fdata, tdata, downwind_index, wdeltas, sp_sel
|
|
243
|
+
)
|
|
244
|
+
wake_deltas[FV.UV] = self.vec_superp.add_wake_vector(
|
|
245
|
+
algo,
|
|
246
|
+
mdata,
|
|
247
|
+
fdata,
|
|
248
|
+
tdata,
|
|
249
|
+
downwind_index,
|
|
250
|
+
sp_sel,
|
|
251
|
+
wake_deltas[FV.UV],
|
|
252
|
+
wdeltas.pop(FV.UV),
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
self.superp[FV.WS].add_wake(
|
|
256
|
+
algo,
|
|
257
|
+
mdata,
|
|
258
|
+
fdata,
|
|
259
|
+
tdata,
|
|
260
|
+
downwind_index,
|
|
261
|
+
sp_sel,
|
|
262
|
+
FV.WS,
|
|
263
|
+
wake_deltas[FV.WS],
|
|
264
|
+
blockage,
|
|
265
|
+
)
|
|
266
|
+
|
|
214
267
|
# select values
|
|
215
268
|
sp_sel = (ct > 1e-8) & (x_R <= 0) # upstream
|
|
216
269
|
if np.any(sp_sel):
|
|
@@ -218,17 +271,7 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
218
271
|
xr = x_R[sp_sel]
|
|
219
272
|
blockage = self._a(ct[sp_sel], xr) * self._rad_fn(xr, r_R[sp_sel])
|
|
220
273
|
|
|
221
|
-
|
|
222
|
-
algo,
|
|
223
|
-
mdata,
|
|
224
|
-
fdata,
|
|
225
|
-
tdata,
|
|
226
|
-
downwind_index,
|
|
227
|
-
sp_sel,
|
|
228
|
-
FV.WS,
|
|
229
|
-
wake_deltas[FV.WS],
|
|
230
|
-
-blockage,
|
|
231
|
-
)
|
|
274
|
+
add_wake(sp_sel, wake_deltas, -blockage)
|
|
232
275
|
|
|
233
276
|
# set area behind to mirrored value EXCEPT for area behind turbine
|
|
234
277
|
if not self.pre_rotor_only:
|
|
@@ -238,52 +281,6 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
238
281
|
xr = x_R[sp_sel]
|
|
239
282
|
blockage = self._a(ct[sp_sel], -xr) * self._rad_fn(-xr, r_R[sp_sel])
|
|
240
283
|
|
|
241
|
-
|
|
242
|
-
self._superp.add_wake(
|
|
243
|
-
algo,
|
|
244
|
-
mdata,
|
|
245
|
-
fdata,
|
|
246
|
-
tdata,
|
|
247
|
-
downwind_index,
|
|
248
|
-
sp_sel,
|
|
249
|
-
FV.WS,
|
|
250
|
-
wake_deltas[FV.WS],
|
|
251
|
-
blockage,
|
|
252
|
-
)
|
|
284
|
+
add_wake(sp_sel, wake_deltas, blockage)
|
|
253
285
|
|
|
254
286
|
return wake_deltas
|
|
255
|
-
|
|
256
|
-
def finalize_wake_deltas(
|
|
257
|
-
self,
|
|
258
|
-
algo,
|
|
259
|
-
mdata,
|
|
260
|
-
fdata,
|
|
261
|
-
amb_results,
|
|
262
|
-
wake_deltas,
|
|
263
|
-
):
|
|
264
|
-
"""
|
|
265
|
-
Finalize the wake calculation.
|
|
266
|
-
|
|
267
|
-
Modifies wake_deltas on the fly.
|
|
268
|
-
|
|
269
|
-
Parameters
|
|
270
|
-
----------
|
|
271
|
-
algo: foxes.core.Algorithm
|
|
272
|
-
The calculation algorithm
|
|
273
|
-
mdata: foxes.core.MData
|
|
274
|
-
The model data
|
|
275
|
-
fdata: foxes.core.FData
|
|
276
|
-
The farm data
|
|
277
|
-
amb_results: dict
|
|
278
|
-
The ambient results, key: variable name str,
|
|
279
|
-
values: numpy.ndarray with shape
|
|
280
|
-
(n_states, n_targets, n_tpoints)
|
|
281
|
-
wake_deltas: dict
|
|
282
|
-
The wake deltas object at the selected target
|
|
283
|
-
turbines. Key: variable str, value: numpy.ndarray
|
|
284
|
-
with shape (n_states, n_targets, n_tpoints)
|
|
285
|
-
|
|
286
|
-
"""
|
|
287
|
-
wake_deltas[FV.WS] = self._superp.calc_final_wake_delta(
|
|
288
|
-
algo, mdata, fdata, FV.WS, amb_results[FV.WS], wake_deltas[FV.WS]
|
|
289
|
-
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
+
from foxes.config import config
|
|
3
4
|
from foxes.core import TurbineInductionModel
|
|
4
5
|
import foxes.variables as FV
|
|
5
6
|
import foxes.constants as FC
|
|
@@ -45,16 +46,28 @@ class VortexSheet(TurbineInductionModel):
|
|
|
45
46
|
Calculate only the pre-rotor region
|
|
46
47
|
|
|
47
48
|
"""
|
|
48
|
-
super().__init__()
|
|
49
|
+
super().__init__(wind_superposition=superposition)
|
|
49
50
|
self.induction = induction
|
|
50
51
|
self.pre_rotor_only = pre_rotor_only
|
|
51
|
-
self._superp_name = superposition
|
|
52
52
|
|
|
53
53
|
def __repr__(self):
|
|
54
54
|
iname = (
|
|
55
55
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
56
56
|
)
|
|
57
|
-
return f"{type(self).__name__}({self.
|
|
57
|
+
return f"{type(self).__name__}({self.wind_superposition}, induction={iname})"
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def affects_ws(self):
|
|
61
|
+
"""
|
|
62
|
+
Flag for wind speed wake models
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
dws: bool
|
|
67
|
+
If True, this model affects wind speed
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
return True
|
|
58
71
|
|
|
59
72
|
def sub_models(self):
|
|
60
73
|
"""
|
|
@@ -66,7 +79,7 @@ class VortexSheet(TurbineInductionModel):
|
|
|
66
79
|
All sub models
|
|
67
80
|
|
|
68
81
|
"""
|
|
69
|
-
return
|
|
82
|
+
return super().sub_models() + [self.induction]
|
|
70
83
|
|
|
71
84
|
def initialize(self, algo, verbosity=0, force=False):
|
|
72
85
|
"""
|
|
@@ -82,35 +95,44 @@ class VortexSheet(TurbineInductionModel):
|
|
|
82
95
|
Overwrite existing data
|
|
83
96
|
|
|
84
97
|
"""
|
|
85
|
-
self._superp = algo.mbook.wake_superpositions[self._superp_name]
|
|
86
98
|
if isinstance(self.induction, str):
|
|
87
99
|
self.induction = algo.mbook.axial_induction[self.induction]
|
|
88
100
|
super().initialize(algo, verbosity, force)
|
|
89
101
|
|
|
90
102
|
def new_wake_deltas(self, algo, mdata, fdata, tdata):
|
|
91
103
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
They are added on the fly to the wake_deltas dict.
|
|
104
|
+
Creates new empty wake delta arrays.
|
|
95
105
|
|
|
96
106
|
Parameters
|
|
97
107
|
----------
|
|
98
108
|
algo: foxes.core.Algorithm
|
|
99
109
|
The calculation algorithm
|
|
100
|
-
mdata: foxes.core.
|
|
110
|
+
mdata: foxes.core.MData
|
|
101
111
|
The model data
|
|
102
|
-
fdata: foxes.core.
|
|
112
|
+
fdata: foxes.core.FData
|
|
103
113
|
The farm data
|
|
104
|
-
|
|
105
|
-
The
|
|
114
|
+
tdata: foxes.core.TData
|
|
115
|
+
The target point data
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
106
119
|
wake_deltas: dict
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
wake delta applies, values: numpy.ndarray with
|
|
110
|
-
shape (n_states, n_points, ...)
|
|
120
|
+
Key: variable name, value: The zero filled
|
|
121
|
+
wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
|
|
111
122
|
|
|
112
123
|
"""
|
|
113
|
-
|
|
124
|
+
if self.has_uv:
|
|
125
|
+
duv = np.zeros(
|
|
126
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
|
|
127
|
+
dtype=config.dtype_double,
|
|
128
|
+
)
|
|
129
|
+
return {FV.UV: duv}
|
|
130
|
+
else:
|
|
131
|
+
dws = np.zeros(
|
|
132
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints),
|
|
133
|
+
dtype=config.dtype_double,
|
|
134
|
+
)
|
|
135
|
+
return {FV.WS: dws}
|
|
114
136
|
|
|
115
137
|
def contribute(
|
|
116
138
|
self,
|
|
@@ -186,20 +208,39 @@ class VortexSheet(TurbineInductionModel):
|
|
|
186
208
|
R_sel = D[sp_sel] / 2
|
|
187
209
|
xi = r_sph_sel / R_sel
|
|
188
210
|
|
|
211
|
+
def add_wake(sp_sel, wake_deltas, blockage):
|
|
212
|
+
"""adds to wake deltas"""
|
|
213
|
+
if self.has_vector_wind_superp:
|
|
214
|
+
wdeltas = {FV.WS: blockage}
|
|
215
|
+
self.vec_superp.wdeltas_ws2uv(
|
|
216
|
+
algo, fdata, tdata, downwind_index, wdeltas, sp_sel
|
|
217
|
+
)
|
|
218
|
+
wake_deltas[FV.UV] = self.vec_superp.add_wake_vector(
|
|
219
|
+
algo,
|
|
220
|
+
mdata,
|
|
221
|
+
fdata,
|
|
222
|
+
tdata,
|
|
223
|
+
downwind_index,
|
|
224
|
+
sp_sel,
|
|
225
|
+
wake_deltas[FV.UV],
|
|
226
|
+
wdeltas.pop(FV.UV),
|
|
227
|
+
)
|
|
228
|
+
else:
|
|
229
|
+
self.superp[FV.WS].add_wake(
|
|
230
|
+
algo,
|
|
231
|
+
mdata,
|
|
232
|
+
fdata,
|
|
233
|
+
tdata,
|
|
234
|
+
downwind_index,
|
|
235
|
+
sp_sel,
|
|
236
|
+
FV.WS,
|
|
237
|
+
wake_deltas[FV.WS],
|
|
238
|
+
blockage,
|
|
239
|
+
)
|
|
240
|
+
|
|
189
241
|
if np.any(sp_sel):
|
|
190
242
|
blockage = self.induction.ct2a(ct_sel) * (1 + -xi / np.sqrt(1 + xi**2))
|
|
191
|
-
|
|
192
|
-
self._superp.add_wake(
|
|
193
|
-
algo,
|
|
194
|
-
mdata,
|
|
195
|
-
fdata,
|
|
196
|
-
tdata,
|
|
197
|
-
downwind_index,
|
|
198
|
-
sp_sel,
|
|
199
|
-
FV.WS,
|
|
200
|
-
wake_deltas[FV.WS],
|
|
201
|
-
-blockage,
|
|
202
|
-
)
|
|
243
|
+
add_wake(sp_sel, wake_deltas, -blockage)
|
|
203
244
|
|
|
204
245
|
if not self.pre_rotor_only:
|
|
205
246
|
sp_sel = (
|
|
@@ -211,51 +252,6 @@ class VortexSheet(TurbineInductionModel):
|
|
|
211
252
|
xi = r_sph_sel / R_sel
|
|
212
253
|
if np.any(sp_sel):
|
|
213
254
|
blockage = self.induction.ct2a(ct_sel) * (1 + -xi / np.sqrt(1 + xi**2))
|
|
214
|
-
|
|
215
|
-
algo,
|
|
216
|
-
mdata,
|
|
217
|
-
fdata,
|
|
218
|
-
tdata,
|
|
219
|
-
downwind_index,
|
|
220
|
-
sp_sel,
|
|
221
|
-
FV.WS,
|
|
222
|
-
wake_deltas[FV.WS],
|
|
223
|
-
blockage,
|
|
224
|
-
)
|
|
255
|
+
add_wake(sp_sel, wake_deltas, blockage)
|
|
225
256
|
|
|
226
257
|
return wake_deltas
|
|
227
|
-
|
|
228
|
-
def finalize_wake_deltas(
|
|
229
|
-
self,
|
|
230
|
-
algo,
|
|
231
|
-
mdata,
|
|
232
|
-
fdata,
|
|
233
|
-
amb_results,
|
|
234
|
-
wake_deltas,
|
|
235
|
-
):
|
|
236
|
-
"""
|
|
237
|
-
Finalize the wake calculation.
|
|
238
|
-
|
|
239
|
-
Modifies wake_deltas on the fly.
|
|
240
|
-
|
|
241
|
-
Parameters
|
|
242
|
-
----------
|
|
243
|
-
algo: foxes.core.Algorithm
|
|
244
|
-
The calculation algorithm
|
|
245
|
-
mdata: foxes.core.MData
|
|
246
|
-
The model data
|
|
247
|
-
fdata: foxes.core.FData
|
|
248
|
-
The farm data
|
|
249
|
-
amb_results: dict
|
|
250
|
-
The ambient results, key: variable name str,
|
|
251
|
-
values: numpy.ndarray with shape
|
|
252
|
-
(n_states, n_targets, n_tpoints)
|
|
253
|
-
wake_deltas: dict
|
|
254
|
-
The wake deltas object at the selected target
|
|
255
|
-
turbines. Key: variable str, value: numpy.ndarray
|
|
256
|
-
with shape (n_states, n_targets, n_tpoints)
|
|
257
|
-
|
|
258
|
-
"""
|
|
259
|
-
wake_deltas[FV.WS] = self._superp.calc_final_wake_delta(
|
|
260
|
-
algo, mdata, fdata, FV.WS, amb_results[FV.WS], wake_deltas[FV.WS]
|
|
261
|
-
)
|
|
@@ -98,7 +98,9 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
98
98
|
Parameters for the WakeK class
|
|
99
99
|
|
|
100
100
|
"""
|
|
101
|
-
super().__init__(
|
|
101
|
+
super().__init__(
|
|
102
|
+
other_superpositions={FV.TI: superposition}, induction=induction
|
|
103
|
+
)
|
|
102
104
|
|
|
103
105
|
self.a_near = a_near
|
|
104
106
|
self.a_far = a_far
|
|
@@ -115,7 +117,7 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
115
117
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
116
118
|
)
|
|
117
119
|
s = f"{type(self).__name__}"
|
|
118
|
-
s += f"({self.
|
|
120
|
+
s += f"({self.other_superpositions[FV.TI]}, induction={iname}, "
|
|
119
121
|
s += self.wake_k.repr() + ")"
|
|
120
122
|
return s
|
|
121
123
|
|
|
@@ -129,7 +131,7 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
129
131
|
All sub models
|
|
130
132
|
|
|
131
133
|
"""
|
|
132
|
-
return [self.wake_k]
|
|
134
|
+
return super().sub_models() + [self.wake_k]
|
|
133
135
|
|
|
134
136
|
def new_wake_deltas(self, algo, mdata, fdata, tdata):
|
|
135
137
|
"""
|
|
@@ -53,7 +53,9 @@ class IECTIWake(TopHatWakeModel):
|
|
|
53
53
|
Parameters for the WakeK class
|
|
54
54
|
|
|
55
55
|
"""
|
|
56
|
-
super().__init__(
|
|
56
|
+
super().__init__(
|
|
57
|
+
other_superpositions={FV.TI: superposition}, induction=induction
|
|
58
|
+
)
|
|
57
59
|
self.iec_type = iec_type
|
|
58
60
|
self.wake_k = None
|
|
59
61
|
|
|
@@ -62,7 +64,7 @@ class IECTIWake(TopHatWakeModel):
|
|
|
62
64
|
else:
|
|
63
65
|
if "k" in wake_k or "ka" in wake_k or "kb" in wake_k:
|
|
64
66
|
raise KeyError(
|
|
65
|
-
|
|
67
|
+
"Can handle 'opening_angle' or ('k', 'ka', 'kb') parameters, not both"
|
|
66
68
|
)
|
|
67
69
|
self._k = float(np.tan(np.deg2rad(opening_angle / 2.0)))
|
|
68
70
|
|
|
@@ -71,7 +73,7 @@ class IECTIWake(TopHatWakeModel):
|
|
|
71
73
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
72
74
|
)
|
|
73
75
|
s = f"{type(self).__name__}"
|
|
74
|
-
s += f"({self.
|
|
76
|
+
s += f"({self.other_superpositions[FV.TI]}, induction={iname}"
|
|
75
77
|
if self.wake_k is not None:
|
|
76
78
|
s += ", " + self.wake_k.repr()
|
|
77
79
|
s += ")"
|
|
@@ -87,7 +89,7 @@ class IECTIWake(TopHatWakeModel):
|
|
|
87
89
|
All sub models
|
|
88
90
|
|
|
89
91
|
"""
|
|
90
|
-
return [self.wake_k] if self.wake_k is not None else []
|
|
92
|
+
return super().sub_models() + ([self.wake_k] if self.wake_k is not None else [])
|
|
91
93
|
|
|
92
94
|
def new_wake_deltas(self, algo, mdata, fdata, tdata):
|
|
93
95
|
"""
|
|
@@ -2,6 +2,7 @@ import numpy as np
|
|
|
2
2
|
from abc import abstractmethod
|
|
3
3
|
|
|
4
4
|
from foxes.models.wake_models.axisymmetric import AxisymmetricWakeModel
|
|
5
|
+
from foxes.config import config
|
|
5
6
|
import foxes.variables as FV
|
|
6
7
|
import foxes.constants as FC
|
|
7
8
|
|
|
@@ -19,21 +20,21 @@ class TopHatWakeModel(AxisymmetricWakeModel):
|
|
|
19
20
|
|
|
20
21
|
"""
|
|
21
22
|
|
|
22
|
-
def __init__(self,
|
|
23
|
+
def __init__(self, *args, induction="Betz", **kwargs):
|
|
23
24
|
"""
|
|
24
25
|
Constructor.
|
|
25
26
|
|
|
26
27
|
Parameters
|
|
27
28
|
----------
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
value: The wake superposition model name,
|
|
31
|
-
will be looked up in model book
|
|
29
|
+
args: tuple, optional
|
|
30
|
+
Additional parameters for the base class
|
|
32
31
|
induction: foxes.core.AxialInductionModel or str
|
|
33
32
|
The induction model
|
|
33
|
+
kwargs: dict, optional
|
|
34
|
+
Additional parameters for the base class
|
|
34
35
|
|
|
35
36
|
"""
|
|
36
|
-
super().__init__(
|
|
37
|
+
super().__init__(*args, **kwargs)
|
|
37
38
|
self.induction = induction
|
|
38
39
|
|
|
39
40
|
def sub_models(self):
|
|
@@ -46,7 +47,7 @@ class TopHatWakeModel(AxisymmetricWakeModel):
|
|
|
46
47
|
All sub models
|
|
47
48
|
|
|
48
49
|
"""
|
|
49
|
-
return [self.induction]
|
|
50
|
+
return super().sub_models() + [self.induction]
|
|
50
51
|
|
|
51
52
|
def initialize(self, algo, verbosity=0, force=False):
|
|
52
53
|
"""
|
|
@@ -66,6 +67,41 @@ class TopHatWakeModel(AxisymmetricWakeModel):
|
|
|
66
67
|
self.induction = algo.mbook.axial_induction[self.induction]
|
|
67
68
|
super().initialize(algo, verbosity, force)
|
|
68
69
|
|
|
70
|
+
def new_wake_deltas(self, algo, mdata, fdata, tdata):
|
|
71
|
+
"""
|
|
72
|
+
Creates new empty wake delta arrays.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
algo: foxes.core.Algorithm
|
|
77
|
+
The calculation algorithm
|
|
78
|
+
mdata: foxes.core.MData
|
|
79
|
+
The model data
|
|
80
|
+
fdata: foxes.core.FData
|
|
81
|
+
The farm data
|
|
82
|
+
tdata: foxes.core.TData
|
|
83
|
+
The target point data
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
wake_deltas: dict
|
|
88
|
+
Key: variable name, value: The zero filled
|
|
89
|
+
wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
if self.has_uv:
|
|
93
|
+
duv = np.zeros(
|
|
94
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
|
|
95
|
+
dtype=config.dtype_double,
|
|
96
|
+
)
|
|
97
|
+
return {FV.UV: duv}
|
|
98
|
+
else:
|
|
99
|
+
dws = np.zeros(
|
|
100
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints),
|
|
101
|
+
dtype=config.dtype_double,
|
|
102
|
+
)
|
|
103
|
+
return {FV.WS: dws}
|
|
104
|
+
|
|
69
105
|
@abstractmethod
|
|
70
106
|
def calc_wake_radius(
|
|
71
107
|
self,
|
|
@@ -224,4 +260,19 @@ class TopHatWakeModel(AxisymmetricWakeModel):
|
|
|
224
260
|
for v, wdel in cl_del.items():
|
|
225
261
|
wdeltas[v] = np.where(isin, wdel[:, None], 0.0)
|
|
226
262
|
|
|
263
|
+
if self.affects_ws and FV.WS in wdeltas:
|
|
264
|
+
# wake deflection causes wind vector rotation:
|
|
265
|
+
if FC.WDEFL_ROT_ANGLE in tdata:
|
|
266
|
+
dwd_defl = tdata[FC.WDEFL_ROT_ANGLE]
|
|
267
|
+
if FV.WD not in wdeltas:
|
|
268
|
+
wdeltas[FV.WD] = np.zeros_like(wdeltas[FV.WS])
|
|
269
|
+
wdeltas[FV.WD][:] = dwd_defl[st_sel]
|
|
270
|
+
else:
|
|
271
|
+
wdeltas[FV.WD] += dwd_defl[st_sel]
|
|
272
|
+
|
|
273
|
+
# wake deflection causes wind speed reduction:
|
|
274
|
+
if FC.WDEFL_DWS_FACTOR in tdata:
|
|
275
|
+
dws_defl = tdata[FC.WDEFL_DWS_FACTOR]
|
|
276
|
+
wdeltas[FV.WS] *= dws_defl[st_sel]
|
|
277
|
+
|
|
227
278
|
return wdeltas, st_sel
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Wind deficit wake models.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from .jensen import JensenWake
|
|
6
|
-
from .bastankhah14 import Bastankhah2014
|
|
7
|
-
from .bastankhah16 import Bastankhah2016Model
|
|
8
|
-
from .
|
|
5
|
+
from .jensen import JensenWake as JensenWake
|
|
6
|
+
from .bastankhah14 import Bastankhah2014 as Bastankhah2014
|
|
7
|
+
from .bastankhah16 import Bastankhah2016Model as Bastankhah2016Model
|
|
8
|
+
from .bastankhah16 import Bastankhah2016 as Bastankhah2016
|
|
9
|
+
from .turbopark import TurbOParkWake as TurbOParkWake
|
|
10
|
+
from .turbopark import TurbOParkWakeIX as TurbOParkWakeIX
|
|
@@ -22,7 +22,7 @@ class Bastankhah2014(GaussianWakeModel):
|
|
|
22
22
|
----------
|
|
23
23
|
sbeta_factor: float
|
|
24
24
|
Factor multiplying sbeta
|
|
25
|
-
induction: foxes.core.AxialInductionModel
|
|
25
|
+
induction: foxes.core.AxialInductionModel
|
|
26
26
|
The induction model
|
|
27
27
|
wake_k: foxes.core.WakeK
|
|
28
28
|
Handler for the wake growth parameter k
|
|
@@ -31,7 +31,13 @@ class Bastankhah2014(GaussianWakeModel):
|
|
|
31
31
|
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
-
def __init__(
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
superposition,
|
|
37
|
+
sbeta_factor=0.2,
|
|
38
|
+
induction="Madsen",
|
|
39
|
+
**wake_k,
|
|
40
|
+
):
|
|
35
41
|
"""
|
|
36
42
|
Constructor.
|
|
37
43
|
|
|
@@ -47,8 +53,7 @@ class Bastankhah2014(GaussianWakeModel):
|
|
|
47
53
|
Parameters for the WakeK class
|
|
48
54
|
|
|
49
55
|
"""
|
|
50
|
-
super().__init__(
|
|
51
|
-
|
|
56
|
+
super().__init__(wind_superposition=superposition)
|
|
52
57
|
self.sbeta_factor = sbeta_factor
|
|
53
58
|
self.induction = induction
|
|
54
59
|
self.wake_k = WakeK(**wake_k)
|
|
@@ -58,10 +63,23 @@ class Bastankhah2014(GaussianWakeModel):
|
|
|
58
63
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
59
64
|
)
|
|
60
65
|
s = f"{type(self).__name__}"
|
|
61
|
-
s += f"({self.
|
|
66
|
+
s += f"({self.wind_superposition}, induction={iname}, "
|
|
62
67
|
s += self.wake_k.repr() + ")"
|
|
63
68
|
return s
|
|
64
69
|
|
|
70
|
+
@property
|
|
71
|
+
def affects_ws(self):
|
|
72
|
+
"""
|
|
73
|
+
Flag for wind speed wake models
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
dws: bool
|
|
78
|
+
If True, this model affects wind speed
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
return True
|
|
82
|
+
|
|
65
83
|
def sub_models(self):
|
|
66
84
|
"""
|
|
67
85
|
List of all sub-models
|
|
@@ -72,7 +90,7 @@ class Bastankhah2014(GaussianWakeModel):
|
|
|
72
90
|
All sub models
|
|
73
91
|
|
|
74
92
|
"""
|
|
75
|
-
return [self.wake_k, self.induction]
|
|
93
|
+
return super().sub_models() + [self.wake_k, self.induction]
|
|
76
94
|
|
|
77
95
|
def initialize(self, algo, verbosity=0, force=False):
|
|
78
96
|
"""
|
|
@@ -182,7 +200,7 @@ class Bastankhah2014(GaussianWakeModel):
|
|
|
182
200
|
|
|
183
201
|
# calculate amplitude:
|
|
184
202
|
ct_eff = ct / (8 * (sigma / D) ** 2)
|
|
185
|
-
ampld = np.maximum(-2 * self.induction.ct2a(ct_eff), -
|
|
203
|
+
ampld = np.maximum(-2 * self.induction.ct2a(ct_eff), -0.9999)
|
|
186
204
|
|
|
187
205
|
# case no targets:
|
|
188
206
|
else:
|