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,9 +1,12 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
+
import numpy as np
|
|
2
3
|
|
|
3
|
-
from foxes.core import
|
|
4
|
+
from foxes.core import SingleTurbineWakeModel
|
|
5
|
+
from foxes.config import config
|
|
6
|
+
import foxes.variables as FV
|
|
4
7
|
|
|
5
8
|
|
|
6
|
-
class DistSlicedWakeModel(
|
|
9
|
+
class DistSlicedWakeModel(SingleTurbineWakeModel):
|
|
7
10
|
"""
|
|
8
11
|
Abstract base class for wake models for which
|
|
9
12
|
the x-denpendency can be separated from the
|
|
@@ -12,65 +15,44 @@ class DistSlicedWakeModel(WakeModel):
|
|
|
12
15
|
The multi-yz ability is used by the `PartialDistSlicedWake`
|
|
13
16
|
partial wakes model.
|
|
14
17
|
|
|
15
|
-
Attributes
|
|
16
|
-
----------
|
|
17
|
-
superpositions: dict
|
|
18
|
-
The superpositions. Key: variable name str,
|
|
19
|
-
value: The wake superposition model name,
|
|
20
|
-
will be looked up in model book
|
|
21
|
-
superp: dict
|
|
22
|
-
The superposition dict, key: variable name str,
|
|
23
|
-
value: `foxes.core.WakeSuperposition`
|
|
24
|
-
|
|
25
18
|
:group: models.wake_models
|
|
26
19
|
|
|
27
20
|
"""
|
|
28
21
|
|
|
29
|
-
def
|
|
22
|
+
def new_wake_deltas(self, algo, mdata, fdata, tdata):
|
|
30
23
|
"""
|
|
31
|
-
|
|
24
|
+
Creates new empty wake delta arrays.
|
|
32
25
|
|
|
33
26
|
Parameters
|
|
34
27
|
----------
|
|
35
|
-
|
|
36
|
-
The
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def sub_models(self):
|
|
45
|
-
"""
|
|
46
|
-
List of all sub-models
|
|
28
|
+
algo: foxes.core.Algorithm
|
|
29
|
+
The calculation algorithm
|
|
30
|
+
mdata: foxes.core.MData
|
|
31
|
+
The model data
|
|
32
|
+
fdata: foxes.core.FData
|
|
33
|
+
The farm data
|
|
34
|
+
tdata: foxes.core.TData
|
|
35
|
+
The target point data
|
|
47
36
|
|
|
48
37
|
Returns
|
|
49
38
|
-------
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"""
|
|
54
|
-
return list(self.superp.values())
|
|
55
|
-
|
|
56
|
-
def initialize(self, algo, verbosity=0, force=False):
|
|
57
|
-
"""
|
|
58
|
-
Initializes the model.
|
|
59
|
-
|
|
60
|
-
Parameters
|
|
61
|
-
----------
|
|
62
|
-
algo: foxes.core.Algorithm
|
|
63
|
-
The calculation algorithm
|
|
64
|
-
verbosity: int
|
|
65
|
-
The verbosity level, 0 = silent
|
|
66
|
-
force: bool
|
|
67
|
-
Overwrite existing data
|
|
39
|
+
wake_deltas: dict
|
|
40
|
+
Key: variable name, value: The zero filled
|
|
41
|
+
wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
|
|
68
42
|
|
|
69
43
|
"""
|
|
70
|
-
self.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
44
|
+
if self.has_uv:
|
|
45
|
+
duv = np.zeros(
|
|
46
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
|
|
47
|
+
dtype=config.dtype_double,
|
|
48
|
+
)
|
|
49
|
+
return {FV.UV: duv}
|
|
50
|
+
else:
|
|
51
|
+
dws = np.zeros(
|
|
52
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints),
|
|
53
|
+
dtype=config.dtype_double,
|
|
54
|
+
)
|
|
55
|
+
return {FV.WS: dws}
|
|
74
56
|
|
|
75
57
|
@abstractmethod
|
|
76
58
|
def calc_wakes_x_yz(
|
|
@@ -159,6 +141,26 @@ class DistSlicedWakeModel(WakeModel):
|
|
|
159
141
|
algo, mdata, fdata, tdata, downwind_index, x, yz
|
|
160
142
|
)
|
|
161
143
|
|
|
144
|
+
if self.affects_ws and self.has_uv:
|
|
145
|
+
assert self.has_vector_wind_superp, (
|
|
146
|
+
f"Wake model {self.name}: Missing vector wind superposition, got '{self.wind_superposition}'"
|
|
147
|
+
)
|
|
148
|
+
if FV.UV in wdeltas or FV.WS in wdeltas:
|
|
149
|
+
if FV.UV not in wdeltas:
|
|
150
|
+
self.vec_superp.wdeltas_ws2uv(
|
|
151
|
+
algo, fdata, tdata, downwind_index, wdeltas, st_sel
|
|
152
|
+
)
|
|
153
|
+
wake_deltas[FV.UV] = self.vec_superp.add_wake_vector(
|
|
154
|
+
algo,
|
|
155
|
+
mdata,
|
|
156
|
+
fdata,
|
|
157
|
+
tdata,
|
|
158
|
+
downwind_index,
|
|
159
|
+
st_sel,
|
|
160
|
+
wake_deltas[FV.UV],
|
|
161
|
+
wdeltas.pop(FV.UV),
|
|
162
|
+
)
|
|
163
|
+
|
|
162
164
|
for v, hdel in wdeltas.items():
|
|
163
165
|
try:
|
|
164
166
|
superp = self.superp[v]
|
|
@@ -178,39 +180,3 @@ class DistSlicedWakeModel(WakeModel):
|
|
|
178
180
|
wake_deltas[v],
|
|
179
181
|
hdel,
|
|
180
182
|
)
|
|
181
|
-
|
|
182
|
-
def finalize_wake_deltas(
|
|
183
|
-
self,
|
|
184
|
-
algo,
|
|
185
|
-
mdata,
|
|
186
|
-
fdata,
|
|
187
|
-
amb_results,
|
|
188
|
-
wake_deltas,
|
|
189
|
-
):
|
|
190
|
-
"""
|
|
191
|
-
Finalize the wake calculation.
|
|
192
|
-
|
|
193
|
-
Modifies wake_deltas on the fly.
|
|
194
|
-
|
|
195
|
-
Parameters
|
|
196
|
-
----------
|
|
197
|
-
algo: foxes.core.Algorithm
|
|
198
|
-
The calculation algorithm
|
|
199
|
-
mdata: foxes.core.MData
|
|
200
|
-
The model data
|
|
201
|
-
fdata: foxes.core.FData
|
|
202
|
-
The farm data
|
|
203
|
-
amb_results: dict
|
|
204
|
-
The ambient results, key: variable name str,
|
|
205
|
-
values: numpy.ndarray with shape
|
|
206
|
-
(n_states, n_targets, n_tpoints)
|
|
207
|
-
wake_deltas: dict
|
|
208
|
-
The wake deltas object at the selected target
|
|
209
|
-
turbines. Key: variable str, value: numpy.ndarray
|
|
210
|
-
with shape (n_states, n_targets, n_tpoints)
|
|
211
|
-
|
|
212
|
-
"""
|
|
213
|
-
for v, s in self.superp.items():
|
|
214
|
-
wake_deltas[v] = s.calc_final_wake_delta(
|
|
215
|
-
algo, mdata, fdata, v, amb_results[v], wake_deltas[v]
|
|
216
|
-
)
|
|
@@ -2,6 +2,8 @@ import numpy as np
|
|
|
2
2
|
from abc import abstractmethod
|
|
3
3
|
|
|
4
4
|
from foxes.models.wake_models.axisymmetric import AxisymmetricWakeModel
|
|
5
|
+
import foxes.variables as FV
|
|
6
|
+
import foxes.constants as FC
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
class GaussianWakeModel(AxisymmetricWakeModel):
|
|
@@ -94,13 +96,31 @@ class GaussianWakeModel(AxisymmetricWakeModel):
|
|
|
94
96
|
is non-zero, shape: (n_states, n_targets)
|
|
95
97
|
|
|
96
98
|
"""
|
|
99
|
+
# compute amplitude and sigma:
|
|
97
100
|
amsi, st_sel = self.calc_amplitude_sigma(
|
|
98
101
|
algo, mdata, fdata, tdata, downwind_index, x
|
|
99
102
|
)
|
|
103
|
+
|
|
104
|
+
# evaluate the Gaussian function:
|
|
100
105
|
wdeltas = {}
|
|
101
106
|
rsel = r[st_sel]
|
|
102
107
|
for v in amsi.keys():
|
|
103
108
|
ampld, sigma = amsi[v]
|
|
104
109
|
wdeltas[v] = ampld[:, None] * np.exp(-0.5 * (rsel / sigma[:, None]) ** 2)
|
|
105
110
|
|
|
111
|
+
if self.affects_ws and FV.WS in wdeltas:
|
|
112
|
+
# wake deflection causes wind vector rotation:
|
|
113
|
+
if FC.WDEFL_ROT_ANGLE in tdata:
|
|
114
|
+
dwd_defl = tdata.pop(FC.WDEFL_ROT_ANGLE)
|
|
115
|
+
if FV.WD not in wdeltas:
|
|
116
|
+
wdeltas[FV.WD] = np.zeros_like(wdeltas[FV.WS])
|
|
117
|
+
wdeltas[FV.WD][:] = dwd_defl[st_sel]
|
|
118
|
+
else:
|
|
119
|
+
wdeltas[FV.WD] += dwd_defl[st_sel]
|
|
120
|
+
|
|
121
|
+
# wake deflection causes wind speed reduction:
|
|
122
|
+
if FC.WDEFL_DWS_FACTOR in tdata:
|
|
123
|
+
dws_defl = tdata.pop(FC.WDEFL_DWS_FACTOR)
|
|
124
|
+
wdeltas[FV.WS] *= dws_defl[st_sel]
|
|
125
|
+
|
|
106
126
|
return wdeltas, st_sel
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Induction wake models
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from .rankine_half_body import RankineHalfBody
|
|
6
|
-
from .rathmann import Rathmann
|
|
7
|
-
from .self_similar import SelfSimilar
|
|
8
|
-
from .self_similar2020 import SelfSimilar2020
|
|
9
|
-
from .vortex_sheet import VortexSheet
|
|
5
|
+
from .rankine_half_body import RankineHalfBody as RankineHalfBody
|
|
6
|
+
from .rathmann import Rathmann as Rathmann
|
|
7
|
+
from .self_similar import SelfSimilar as SelfSimilar
|
|
8
|
+
from .self_similar2020 import SelfSimilar2020 as SelfSimilar2020
|
|
9
|
+
from .vortex_sheet import VortexSheet as VortexSheet
|
|
@@ -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
|
-
)
|