foxes 1.2.5__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 +16 -0
- 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 +22 -18
- foxes/algorithms/__init__.py +6 -6
- foxes/algorithms/downwind/__init__.py +2 -2
- foxes/algorithms/downwind/downwind.py +53 -27
- foxes/algorithms/downwind/models/__init__.py +6 -6
- foxes/algorithms/downwind/models/farm_wakes_calc.py +22 -14
- foxes/algorithms/downwind/models/init_farm_data.py +4 -5
- foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
- foxes/algorithms/downwind/models/reorder_farm_output.py +5 -1
- foxes/algorithms/downwind/models/set_amb_point_results.py +7 -7
- 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 +15 -8
- foxes/algorithms/sequential/__init__.py +3 -3
- foxes/algorithms/sequential/models/__init__.py +2 -2
- foxes/algorithms/sequential/models/seq_state.py +0 -18
- foxes/algorithms/sequential/sequential.py +8 -22
- foxes/config/__init__.py +5 -1
- foxes/constants.py +22 -0
- foxes/core/__init__.py +45 -22
- foxes/core/algorithm.py +0 -1
- foxes/core/data.py +56 -29
- foxes/core/engine.py +28 -14
- foxes/core/farm_controller.py +2 -2
- foxes/core/farm_data_model.py +1 -0
- 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 +42 -38
- foxes/core/states.py +4 -50
- 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 -11
- foxes/input/states/create/__init__.py +3 -2
- foxes/input/states/field_data_nc.py +48 -84
- foxes/input/states/multi_height.py +40 -60
- foxes/input/states/one_point_flow.py +22 -25
- foxes/input/states/scan.py +6 -19
- foxes/input/states/single.py +6 -18
- foxes/input/states/states_table.py +25 -44
- foxes/input/states/weibull_sectors.py +225 -0
- foxes/input/states/wrg_states.py +151 -37
- 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 +45 -9
- foxes/models/partial_wakes/segregated.py +2 -20
- 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/rotor_models/centre.py +6 -4
- 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 +45 -8
- foxes/models/wake_frames/streamlines.py +8 -6
- foxes/models/wake_frames/timelines.py +19 -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 +14 -6
- foxes/output/farm_results_eval.py +51 -27
- foxes/output/flow_plots_2d/__init__.py +2 -2
- foxes/output/flow_plots_2d/get_fig.py +4 -2
- foxes/output/rose_plot.py +23 -5
- 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/slice_data.py +16 -19
- 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.2.5.dist-info → foxes-1.4.dist-info}/METADATA +14 -21
- foxes-1.4.dist-info/RECORD +320 -0
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/WHEEL +1 -1
- tests/0_consistency/iterative/test_iterative.py +2 -3
- tests/0_consistency/partial_wakes/test_partial_wakes.py +2 -2
- 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 +48 -56
- tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
- tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +33 -36
- 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 +3 -3
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +3 -4
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +3 -4
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +3 -4
- tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +3 -4
- tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
- tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +3 -3
- tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +3 -3
- 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 +3 -4
- tests/3_examples/test_examples.py +3 -2
- foxes/output/round.py +0 -10
- foxes/utils/pandas_helpers.py +0 -178
- foxes-1.2.5.dist-info/RECORD +0 -312
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/entry_points.txt +0 -0
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info/licenses}/LICENSE +0 -0
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
+
from foxes.config import config
|
|
3
4
|
from foxes.core import TurbineInductionModel
|
|
4
|
-
from foxes.utils import uv2wd, wd2uv, delta_wd
|
|
5
5
|
import foxes.variables as FV
|
|
6
6
|
import foxes.constants as FC
|
|
7
7
|
|
|
@@ -10,9 +10,6 @@ class RankineHalfBody(TurbineInductionModel):
|
|
|
10
10
|
"""
|
|
11
11
|
The Rankine half body induction wake model
|
|
12
12
|
|
|
13
|
-
The individual wake effects are superposed linearly,
|
|
14
|
-
without invoking a wake superposition model.
|
|
15
|
-
|
|
16
13
|
Notes
|
|
17
14
|
-----
|
|
18
15
|
Reference:
|
|
@@ -30,24 +27,41 @@ class RankineHalfBody(TurbineInductionModel):
|
|
|
30
27
|
|
|
31
28
|
"""
|
|
32
29
|
|
|
33
|
-
def __init__(self, induction="Madsen"):
|
|
30
|
+
def __init__(self, superposition="vector", induction="Madsen"):
|
|
34
31
|
"""
|
|
35
32
|
Constructor.
|
|
36
33
|
|
|
37
34
|
Parameters
|
|
38
35
|
----------
|
|
36
|
+
superposition: str
|
|
37
|
+
The wind speed deficit superposition.
|
|
39
38
|
induction: foxes.core.AxialInductionModel or str
|
|
40
39
|
The induction model
|
|
41
40
|
|
|
42
41
|
"""
|
|
43
|
-
super().__init__()
|
|
42
|
+
super().__init__(wind_superposition=superposition, other_superpositions={})
|
|
44
43
|
self.induction = induction
|
|
45
44
|
|
|
45
|
+
self._has_uv = True
|
|
46
|
+
|
|
46
47
|
def __repr__(self):
|
|
47
48
|
iname = (
|
|
48
49
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
49
50
|
)
|
|
50
|
-
return f"{type(self).__name__}(induction={iname})"
|
|
51
|
+
return f"{type(self).__name__}({self.wind_superposition}, induction={iname})"
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def affects_ws(self):
|
|
55
|
+
"""
|
|
56
|
+
Flag for wind speed wake models
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
dws: bool
|
|
61
|
+
If True, this model affects wind speed
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
return True
|
|
51
65
|
|
|
52
66
|
def sub_models(self):
|
|
53
67
|
"""
|
|
@@ -59,7 +73,7 @@ class RankineHalfBody(TurbineInductionModel):
|
|
|
59
73
|
All sub models
|
|
60
74
|
|
|
61
75
|
"""
|
|
62
|
-
return [self.induction]
|
|
76
|
+
return super().sub_models() + [self.induction]
|
|
63
77
|
|
|
64
78
|
def initialize(self, algo, verbosity=0, force=False):
|
|
65
79
|
"""
|
|
@@ -98,15 +112,14 @@ class RankineHalfBody(TurbineInductionModel):
|
|
|
98
112
|
-------
|
|
99
113
|
wake_deltas: dict
|
|
100
114
|
Key: variable name, value: The zero filled
|
|
101
|
-
wake deltas, shape: (n_states,
|
|
115
|
+
wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
|
|
102
116
|
|
|
103
117
|
"""
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
118
|
+
duv = np.zeros(
|
|
119
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
|
|
120
|
+
dtype=config.dtype_double,
|
|
121
|
+
)
|
|
122
|
+
return {FV.UV: duv}
|
|
110
123
|
|
|
111
124
|
def contribute(
|
|
112
125
|
self,
|
|
@@ -205,8 +218,7 @@ class RankineHalfBody(TurbineInductionModel):
|
|
|
205
218
|
|
|
206
219
|
# calc velocity components
|
|
207
220
|
vel_factor = m[st_sel] / (4 * np.linalg.norm(xyz, axis=-1) ** 3)
|
|
208
|
-
wake_deltas[
|
|
209
|
-
wake_deltas["V"][st_sel] += vel_factor * xyz[:, 1]
|
|
221
|
+
wake_deltas[FV.UV][st_sel] += vel_factor[:, None] * xyz[:, :2]
|
|
210
222
|
|
|
211
223
|
# set values inside body shape
|
|
212
224
|
st_sel = (ct > 1e-8) & (RHB_shape >= -1) & (x >= xs) & (x <= 0)
|
|
@@ -217,57 +229,4 @@ class RankineHalfBody(TurbineInductionModel):
|
|
|
217
229
|
|
|
218
230
|
# calc velocity components
|
|
219
231
|
vel_factor = m[st_sel] / (4 * np.linalg.norm(xyz, axis=-1) ** 3)
|
|
220
|
-
wake_deltas[
|
|
221
|
-
|
|
222
|
-
def finalize_wake_deltas(
|
|
223
|
-
self,
|
|
224
|
-
algo,
|
|
225
|
-
mdata,
|
|
226
|
-
fdata,
|
|
227
|
-
amb_results,
|
|
228
|
-
wake_deltas,
|
|
229
|
-
):
|
|
230
|
-
"""
|
|
231
|
-
Finalize the wake calculation.
|
|
232
|
-
|
|
233
|
-
Modifies wake_deltas on the fly.
|
|
234
|
-
|
|
235
|
-
Parameters
|
|
236
|
-
----------
|
|
237
|
-
algo: foxes.core.Algorithm
|
|
238
|
-
The calculation algorithm
|
|
239
|
-
mdata: foxes.core.MData
|
|
240
|
-
The model data
|
|
241
|
-
fdata: foxes.core.FData
|
|
242
|
-
The farm data
|
|
243
|
-
amb_results: dict
|
|
244
|
-
The ambient results, key: variable name str,
|
|
245
|
-
values: numpy.ndarray with shape
|
|
246
|
-
(n_states, n_targets, n_tpoints)
|
|
247
|
-
wake_deltas: dict
|
|
248
|
-
The wake deltas object at the selected target
|
|
249
|
-
turbines. Key: variable str, value: numpy.ndarray
|
|
250
|
-
with shape (n_states, n_targets, n_tpoints)
|
|
251
|
-
|
|
252
|
-
"""
|
|
253
|
-
# calc ambient wind vector:
|
|
254
|
-
ws0 = amb_results[FV.WS]
|
|
255
|
-
nx = wd2uv(amb_results[FV.WD])
|
|
256
|
-
wind_vec = nx * ws0[:, :, :, None]
|
|
257
|
-
|
|
258
|
-
# wake deltas are in wake frame, rotate back to global frame:
|
|
259
|
-
ny = np.stack((-nx[..., 1], nx[..., 0]), axis=-1)
|
|
260
|
-
delta_uv = (
|
|
261
|
-
wake_deltas["U"][:, :, :, None] * nx + wake_deltas["V"][:, :, :, None] * ny
|
|
262
|
-
)
|
|
263
|
-
del ws0, nx, ny
|
|
264
|
-
|
|
265
|
-
# add ambient result to wake deltas:
|
|
266
|
-
wind_vec += delta_uv
|
|
267
|
-
del delta_uv
|
|
268
|
-
|
|
269
|
-
# deduce WS and WD deltas:
|
|
270
|
-
new_wd = uv2wd(wind_vec)
|
|
271
|
-
new_ws = np.linalg.norm(wind_vec, axis=-1)
|
|
272
|
-
wake_deltas[FV.WS] += new_ws - amb_results[FV.WS]
|
|
273
|
-
wake_deltas[FV.WD] += delta_wd(amb_results[FV.WD], new_wd)
|
|
232
|
+
wake_deltas[FV.UV][st_sel, 0] += vel_factor * xyz[:, 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
|
|
@@ -50,16 +51,28 @@ class Rathmann(TurbineInductionModel):
|
|
|
50
51
|
Calculate only the pre-rotor region
|
|
51
52
|
|
|
52
53
|
"""
|
|
53
|
-
super().__init__()
|
|
54
|
+
super().__init__(wind_superposition=superposition)
|
|
54
55
|
self.induction = induction
|
|
55
56
|
self.pre_rotor_only = pre_rotor_only
|
|
56
|
-
self._superp_name = superposition
|
|
57
57
|
|
|
58
58
|
def __repr__(self):
|
|
59
59
|
iname = (
|
|
60
60
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
61
61
|
)
|
|
62
|
-
return f"{type(self).__name__}({self.
|
|
62
|
+
return f"{type(self).__name__}({self.wind_superposition}, induction={iname})"
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def affects_ws(self):
|
|
66
|
+
"""
|
|
67
|
+
Flag for wind speed wake models
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
dws: bool
|
|
72
|
+
If True, this model affects wind speed
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
return True
|
|
63
76
|
|
|
64
77
|
def sub_models(self):
|
|
65
78
|
"""
|
|
@@ -71,7 +84,7 @@ class Rathmann(TurbineInductionModel):
|
|
|
71
84
|
All sub models
|
|
72
85
|
|
|
73
86
|
"""
|
|
74
|
-
return
|
|
87
|
+
return super().sub_models() + [self.induction]
|
|
75
88
|
|
|
76
89
|
def initialize(self, algo, verbosity=0, force=False):
|
|
77
90
|
"""
|
|
@@ -87,7 +100,6 @@ class Rathmann(TurbineInductionModel):
|
|
|
87
100
|
Overwrite existing data
|
|
88
101
|
|
|
89
102
|
"""
|
|
90
|
-
self._superp = algo.mbook.wake_superpositions[self._superp_name]
|
|
91
103
|
if isinstance(self.induction, str):
|
|
92
104
|
self.induction = algo.mbook.axial_induction[self.induction]
|
|
93
105
|
super().initialize(algo, verbosity, force)
|
|
@@ -111,10 +123,21 @@ class Rathmann(TurbineInductionModel):
|
|
|
111
123
|
-------
|
|
112
124
|
wake_deltas: dict
|
|
113
125
|
Key: variable name, value: The zero filled
|
|
114
|
-
wake deltas, shape: (n_states,
|
|
126
|
+
wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
|
|
115
127
|
|
|
116
128
|
"""
|
|
117
|
-
|
|
129
|
+
if self.has_uv:
|
|
130
|
+
duv = np.zeros(
|
|
131
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
|
|
132
|
+
dtype=config.dtype_double,
|
|
133
|
+
)
|
|
134
|
+
return {FV.UV: duv}
|
|
135
|
+
else:
|
|
136
|
+
dws = np.zeros(
|
|
137
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints),
|
|
138
|
+
dtype=config.dtype_double,
|
|
139
|
+
)
|
|
140
|
+
return {FV.WS: dws}
|
|
118
141
|
|
|
119
142
|
def contribute(
|
|
120
143
|
self,
|
|
@@ -195,6 +218,39 @@ class Rathmann(TurbineInductionModel):
|
|
|
195
218
|
sin_beta = 1 / np.sqrt(x_R**2 + r_R**2 + 1) # eqn 19
|
|
196
219
|
return sin_alpha * sin_beta * (1 + x_R**2)
|
|
197
220
|
|
|
221
|
+
def add_wake(sp_sel, wake_deltas, blockage):
|
|
222
|
+
"""adds to wake deltas"""
|
|
223
|
+
if self.has_uv:
|
|
224
|
+
assert self.has_vector_wind_superp, (
|
|
225
|
+
f"Wake model {self.name}: Missing vector wind superposition, got '{self.wind_superposition}'"
|
|
226
|
+
)
|
|
227
|
+
wdeltas = {FV.WS: blockage}
|
|
228
|
+
self.vec_superp.wdeltas_ws2uv(
|
|
229
|
+
algo, fdata, tdata, downwind_index, wdeltas, sp_sel
|
|
230
|
+
)
|
|
231
|
+
wake_deltas[FV.UV] = self.vec_superp.add_wake_vector(
|
|
232
|
+
algo,
|
|
233
|
+
mdata,
|
|
234
|
+
fdata,
|
|
235
|
+
tdata,
|
|
236
|
+
downwind_index,
|
|
237
|
+
sp_sel,
|
|
238
|
+
wake_deltas[FV.UV],
|
|
239
|
+
wdeltas.pop(FV.UV),
|
|
240
|
+
)
|
|
241
|
+
else:
|
|
242
|
+
self.superp[FV.WS].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
|
+
)
|
|
253
|
+
|
|
198
254
|
# ws delta in front of rotor
|
|
199
255
|
sp_sel = (ct > 1e-8) & (x_R <= 0)
|
|
200
256
|
if np.any(sp_sel):
|
|
@@ -202,17 +258,7 @@ class Rathmann(TurbineInductionModel):
|
|
|
202
258
|
a = self.induction.ct2a(ct[sp_sel])
|
|
203
259
|
blockage = a * mu(xr) * G(xr, r_R[sp_sel]) # eqn 10
|
|
204
260
|
|
|
205
|
-
|
|
206
|
-
algo,
|
|
207
|
-
mdata,
|
|
208
|
-
fdata,
|
|
209
|
-
tdata,
|
|
210
|
-
downwind_index,
|
|
211
|
-
sp_sel,
|
|
212
|
-
FV.WS,
|
|
213
|
-
wake_deltas[FV.WS],
|
|
214
|
-
-blockage,
|
|
215
|
-
)
|
|
261
|
+
add_wake(sp_sel, wake_deltas, -blockage)
|
|
216
262
|
|
|
217
263
|
# ws delta behind rotor
|
|
218
264
|
if not self.pre_rotor_only:
|
|
@@ -223,51 +269,6 @@ class Rathmann(TurbineInductionModel):
|
|
|
223
269
|
a = self.induction.ct2a(ct[sp_sel])
|
|
224
270
|
blockage = a * mu(-xr) * G(-xr, r_R[sp_sel]) # eqn 10
|
|
225
271
|
|
|
226
|
-
|
|
227
|
-
algo,
|
|
228
|
-
mdata,
|
|
229
|
-
fdata,
|
|
230
|
-
tdata,
|
|
231
|
-
downwind_index,
|
|
232
|
-
sp_sel,
|
|
233
|
-
FV.WS,
|
|
234
|
-
wake_deltas[FV.WS],
|
|
235
|
-
blockage,
|
|
236
|
-
)
|
|
272
|
+
add_wake(sp_sel, wake_deltas, blockage)
|
|
237
273
|
|
|
238
274
|
return wake_deltas
|
|
239
|
-
|
|
240
|
-
def finalize_wake_deltas(
|
|
241
|
-
self,
|
|
242
|
-
algo,
|
|
243
|
-
mdata,
|
|
244
|
-
fdata,
|
|
245
|
-
amb_results,
|
|
246
|
-
wake_deltas,
|
|
247
|
-
):
|
|
248
|
-
"""
|
|
249
|
-
Finalize the wake calculation.
|
|
250
|
-
|
|
251
|
-
Modifies wake_deltas on the fly.
|
|
252
|
-
|
|
253
|
-
Parameters
|
|
254
|
-
----------
|
|
255
|
-
algo: foxes.core.Algorithm
|
|
256
|
-
The calculation algorithm
|
|
257
|
-
mdata: foxes.core.MData
|
|
258
|
-
The model data
|
|
259
|
-
fdata: foxes.core.FData
|
|
260
|
-
The farm data
|
|
261
|
-
amb_results: dict
|
|
262
|
-
The ambient results, key: variable name str,
|
|
263
|
-
values: numpy.ndarray with shape
|
|
264
|
-
(n_states, n_targets, n_tpoints)
|
|
265
|
-
wake_deltas: dict
|
|
266
|
-
The wake deltas object at the selected target
|
|
267
|
-
turbines. Key: variable str, value: numpy.ndarray
|
|
268
|
-
with shape (n_states, n_targets, n_tpoints)
|
|
269
|
-
|
|
270
|
-
"""
|
|
271
|
-
wake_deltas[FV.WS] = self._superp.calc_final_wake_delta(
|
|
272
|
-
algo, mdata, fdata, FV.WS, amb_results[FV.WS], wake_deltas[FV.WS]
|
|
273
|
-
)
|
|
@@ -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
|
-
)
|