foxes 0.8.2__py3-none-any.whl → 1.1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of foxes might be problematic. Click here for more details.
- docs/source/conf.py +353 -0
- examples/abl_states/run.py +160 -0
- examples/compare_rotors_pwakes/run.py +217 -0
- examples/compare_wakes/run.py +241 -0
- examples/dyn_wakes/run.py +311 -0
- examples/field_data_nc/run.py +121 -0
- examples/induction/run.py +201 -0
- examples/multi_height/run.py +113 -0
- examples/power_mask/run.py +249 -0
- examples/random_timeseries/run.py +210 -0
- examples/scan_row/run.py +193 -0
- examples/sector_management/run.py +162 -0
- examples/sequential/run.py +209 -0
- examples/single_state/run.py +201 -0
- examples/states_lookup_table/run.py +137 -0
- examples/streamline_wakes/run.py +138 -0
- examples/tab_file/run.py +142 -0
- examples/timelines/run.py +267 -0
- examples/timeseries/run.py +190 -0
- examples/timeseries_slurm/run.py +185 -0
- examples/wind_rose/run.py +141 -0
- examples/windio/run.py +29 -0
- examples/yawed_wake/run.py +196 -0
- foxes/__init__.py +4 -8
- foxes/algorithms/__init__.py +1 -1
- foxes/algorithms/downwind/downwind.py +247 -111
- foxes/algorithms/downwind/models/farm_wakes_calc.py +12 -7
- foxes/algorithms/downwind/models/init_farm_data.py +2 -2
- foxes/algorithms/downwind/models/point_wakes_calc.py +6 -7
- foxes/algorithms/downwind/models/reorder_farm_output.py +1 -2
- foxes/algorithms/downwind/models/set_amb_farm_results.py +1 -1
- foxes/algorithms/downwind/models/set_amb_point_results.py +5 -3
- foxes/algorithms/iterative/iterative.py +74 -34
- foxes/algorithms/iterative/models/farm_wakes_calc.py +12 -7
- foxes/algorithms/iterative/models/urelax.py +3 -3
- foxes/algorithms/sequential/models/plugin.py +5 -5
- foxes/algorithms/sequential/models/seq_state.py +1 -1
- foxes/algorithms/sequential/sequential.py +126 -255
- foxes/constants.py +22 -7
- foxes/core/__init__.py +1 -0
- foxes/core/algorithm.py +632 -147
- foxes/core/data.py +252 -20
- foxes/core/data_calc_model.py +15 -291
- foxes/core/engine.py +640 -0
- foxes/core/farm_controller.py +38 -10
- foxes/core/farm_data_model.py +16 -1
- foxes/core/ground_model.py +2 -2
- foxes/core/model.py +249 -182
- foxes/core/partial_wakes_model.py +1 -1
- foxes/core/point_data_model.py +17 -2
- foxes/core/rotor_model.py +27 -21
- foxes/core/states.py +17 -1
- foxes/core/turbine_type.py +28 -0
- foxes/core/wake_frame.py +30 -34
- foxes/core/wake_model.py +5 -5
- foxes/core/wake_superposition.py +1 -1
- foxes/data/windio/windio_5turbines_timeseries.yaml +31 -15
- foxes/engines/__init__.py +17 -0
- foxes/engines/dask.py +982 -0
- foxes/engines/default.py +75 -0
- foxes/engines/futures.py +72 -0
- foxes/engines/mpi.py +38 -0
- foxes/engines/multiprocess.py +71 -0
- foxes/engines/numpy.py +167 -0
- foxes/engines/pool.py +249 -0
- foxes/engines/ray.py +79 -0
- foxes/engines/single.py +141 -0
- foxes/input/farm_layout/__init__.py +1 -0
- foxes/input/farm_layout/from_csv.py +4 -0
- foxes/input/farm_layout/from_json.py +2 -2
- foxes/input/farm_layout/grid.py +2 -2
- foxes/input/farm_layout/ring.py +65 -0
- foxes/input/farm_layout/row.py +2 -2
- foxes/input/states/__init__.py +7 -0
- foxes/input/states/create/random_abl_states.py +1 -1
- foxes/input/states/field_data_nc.py +158 -33
- foxes/input/states/multi_height.py +128 -14
- foxes/input/states/one_point_flow.py +577 -0
- foxes/input/states/scan_ws.py +74 -3
- foxes/input/states/single.py +1 -1
- foxes/input/states/slice_data_nc.py +681 -0
- foxes/input/states/states_table.py +204 -35
- foxes/input/windio/__init__.py +2 -2
- foxes/input/windio/get_states.py +44 -23
- foxes/input/windio/read_attributes.py +48 -17
- foxes/input/windio/read_farm.py +116 -102
- foxes/input/windio/read_fields.py +16 -6
- foxes/input/windio/read_outputs.py +71 -24
- foxes/input/windio/runner.py +31 -17
- foxes/input/windio/windio.py +41 -23
- foxes/models/farm_models/turbine2farm.py +1 -1
- foxes/models/ground_models/wake_mirror.py +10 -6
- foxes/models/model_book.py +58 -20
- foxes/models/partial_wakes/axiwake.py +3 -3
- foxes/models/partial_wakes/rotor_points.py +3 -3
- foxes/models/partial_wakes/top_hat.py +2 -2
- foxes/models/point_models/set_uniform_data.py +1 -1
- foxes/models/point_models/tke2ti.py +1 -1
- foxes/models/point_models/wake_deltas.py +1 -1
- foxes/models/rotor_models/centre.py +4 -0
- foxes/models/rotor_models/grid.py +24 -25
- foxes/models/rotor_models/levels.py +4 -5
- foxes/models/turbine_models/calculator.py +4 -6
- foxes/models/turbine_models/kTI_model.py +22 -6
- foxes/models/turbine_models/lookup_table.py +30 -4
- foxes/models/turbine_models/rotor_centre_calc.py +4 -3
- foxes/models/turbine_models/set_farm_vars.py +103 -34
- foxes/models/turbine_types/PCt_file.py +27 -3
- foxes/models/turbine_types/PCt_from_two.py +27 -3
- foxes/models/turbine_types/TBL_file.py +80 -0
- foxes/models/turbine_types/__init__.py +2 -0
- foxes/models/turbine_types/lookup.py +316 -0
- foxes/models/turbine_types/null_type.py +51 -1
- foxes/models/turbine_types/wsrho2PCt_from_two.py +29 -5
- foxes/models/turbine_types/wsti2PCt_from_two.py +31 -7
- foxes/models/vertical_profiles/__init__.py +1 -1
- foxes/models/vertical_profiles/data_profile.py +1 -1
- foxes/models/wake_frames/__init__.py +1 -0
- foxes/models/wake_frames/dynamic_wakes.py +424 -0
- foxes/models/wake_frames/farm_order.py +25 -5
- foxes/models/wake_frames/rotor_wd.py +6 -4
- foxes/models/wake_frames/seq_dynamic_wakes.py +61 -74
- foxes/models/wake_frames/streamlines.py +21 -22
- foxes/models/wake_frames/timelines.py +330 -129
- foxes/models/wake_frames/yawed_wakes.py +7 -4
- foxes/models/wake_models/dist_sliced.py +2 -4
- foxes/models/wake_models/induction/rankine_half_body.py +5 -5
- foxes/models/wake_models/induction/rathmann.py +78 -24
- foxes/models/wake_models/induction/self_similar.py +78 -28
- foxes/models/wake_models/induction/vortex_sheet.py +86 -48
- foxes/models/wake_models/ti/crespo_hernandez.py +6 -4
- foxes/models/wake_models/ti/iec_ti.py +40 -21
- foxes/models/wake_models/top_hat.py +1 -1
- foxes/models/wake_models/wind/bastankhah14.py +8 -6
- foxes/models/wake_models/wind/bastankhah16.py +17 -16
- foxes/models/wake_models/wind/jensen.py +4 -3
- foxes/models/wake_models/wind/turbopark.py +16 -13
- foxes/models/wake_superpositions/ti_linear.py +1 -1
- foxes/models/wake_superpositions/ti_max.py +1 -1
- foxes/models/wake_superpositions/ti_pow.py +1 -1
- foxes/models/wake_superpositions/ti_quadratic.py +1 -1
- foxes/models/wake_superpositions/ws_linear.py +8 -7
- foxes/models/wake_superpositions/ws_max.py +8 -7
- foxes/models/wake_superpositions/ws_pow.py +8 -7
- foxes/models/wake_superpositions/ws_product.py +5 -5
- foxes/models/wake_superpositions/ws_quadratic.py +8 -7
- foxes/output/__init__.py +4 -1
- foxes/output/farm_layout.py +16 -12
- foxes/output/farm_results_eval.py +1 -1
- foxes/output/flow_plots_2d/__init__.py +0 -1
- foxes/output/flow_plots_2d/flow_plots.py +70 -30
- foxes/output/grids.py +92 -22
- foxes/output/results_writer.py +2 -2
- foxes/output/rose_plot.py +3 -3
- foxes/output/seq_plugins/__init__.py +2 -0
- foxes/output/{flow_plots_2d → seq_plugins}/seq_flow_ani_plugin.py +64 -22
- foxes/output/seq_plugins/seq_wake_debug_plugin.py +145 -0
- foxes/output/slice_data.py +131 -111
- foxes/output/state_turbine_map.py +19 -14
- foxes/output/state_turbine_table.py +19 -19
- foxes/utils/__init__.py +1 -1
- foxes/utils/abl/neutral.py +2 -2
- foxes/utils/abl/stable.py +2 -2
- foxes/utils/abl/unstable.py +2 -2
- foxes/utils/data_book.py +1 -1
- foxes/utils/dev_utils.py +42 -0
- foxes/utils/dict.py +24 -1
- foxes/utils/exec_python.py +1 -1
- foxes/utils/factory.py +176 -53
- foxes/utils/geom2d/circle.py +1 -1
- foxes/utils/geom2d/polygon.py +1 -1
- foxes/utils/geopandas_utils.py +2 -2
- foxes/utils/load.py +2 -2
- foxes/utils/pandas_helpers.py +3 -2
- foxes/utils/wind_dir.py +0 -2
- foxes/utils/xarray_utils.py +24 -14
- foxes/variables.py +39 -2
- {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/METADATA +75 -33
- foxes-1.1.0.2.dist-info/RECORD +309 -0
- {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/WHEEL +1 -1
- foxes-1.1.0.2.dist-info/top_level.txt +4 -0
- tests/0_consistency/iterative/test_iterative.py +92 -0
- tests/0_consistency/partial_wakes/test_partial_wakes.py +90 -0
- tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +85 -0
- tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +103 -0
- tests/1_verification/flappy_0_6/abl_states/flappy/run.py +85 -0
- tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +87 -0
- tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +82 -0
- tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +82 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_centre/flappy/run.py +92 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +93 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/flappy/run.py +92 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +96 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/flappy/run.py +94 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +122 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/flappy/run.py +94 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +122 -0
- tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/flappy/run.py +92 -0
- tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +93 -0
- tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +85 -0
- tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +130 -0
- tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/flappy/run.py +96 -0
- tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +116 -0
- tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +93 -0
- tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +99 -0
- tests/3_examples/test_examples.py +34 -0
- foxes/VERSION +0 -1
- foxes/output/flow_plots_2d.py +0 -0
- foxes/utils/geopandas_helpers.py +0 -294
- foxes/utils/runners/__init__.py +0 -1
- foxes/utils/runners/runners.py +0 -280
- foxes-0.8.2.dist-info/RECORD +0 -247
- foxes-0.8.2.dist-info/top_level.txt +0 -1
- foxes-0.8.2.dist-info/zip-safe +0 -1
- {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/LICENSE +0 -0
|
@@ -31,27 +31,35 @@ class Rathmann(TurbineInductionModel):
|
|
|
31
31
|
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
-
def __init__(
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
superposition="ws_linear",
|
|
37
|
+
induction="Madsen",
|
|
38
|
+
pre_rotor_only=False,
|
|
39
|
+
):
|
|
35
40
|
"""
|
|
36
41
|
Constructor.
|
|
37
42
|
|
|
38
43
|
Parameters
|
|
39
44
|
----------
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
superposition: str
|
|
46
|
+
The wind speed superposition
|
|
42
47
|
induction: foxes.core.AxialInductionModel or str
|
|
43
48
|
The induction model
|
|
49
|
+
pre_rotor_only: bool
|
|
50
|
+
Calculate only the pre-rotor region
|
|
44
51
|
|
|
45
52
|
"""
|
|
46
53
|
super().__init__()
|
|
47
54
|
self.induction = induction
|
|
48
55
|
self.pre_rotor_only = pre_rotor_only
|
|
56
|
+
self._superp_name = superposition
|
|
49
57
|
|
|
50
58
|
def __repr__(self):
|
|
51
59
|
iname = (
|
|
52
60
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
53
61
|
)
|
|
54
|
-
return f"{type(self).__name__}(induction={iname})"
|
|
62
|
+
return f"{type(self).__name__}({self._superp_name}, induction={iname})"
|
|
55
63
|
|
|
56
64
|
def sub_models(self):
|
|
57
65
|
"""
|
|
@@ -63,7 +71,7 @@ class Rathmann(TurbineInductionModel):
|
|
|
63
71
|
All sub models
|
|
64
72
|
|
|
65
73
|
"""
|
|
66
|
-
return [self.induction]
|
|
74
|
+
return [self._superp, self.induction]
|
|
67
75
|
|
|
68
76
|
def initialize(self, algo, verbosity=0, force=False):
|
|
69
77
|
"""
|
|
@@ -79,6 +87,7 @@ class Rathmann(TurbineInductionModel):
|
|
|
79
87
|
Overwrite existing data
|
|
80
88
|
|
|
81
89
|
"""
|
|
90
|
+
self._superp = algo.mbook.wake_superpositions[self._superp_name]
|
|
82
91
|
if isinstance(self.induction, str):
|
|
83
92
|
self.induction = algo.mbook.axial_induction[self.induction]
|
|
84
93
|
super().initialize(algo, verbosity, force)
|
|
@@ -133,7 +142,7 @@ class Rathmann(TurbineInductionModel):
|
|
|
133
142
|
The target point data
|
|
134
143
|
downwind_index: int
|
|
135
144
|
The index of the wake causing turbine
|
|
136
|
-
in the
|
|
145
|
+
in the downwind order
|
|
137
146
|
wake_coos: numpy.ndarray
|
|
138
147
|
The wake frame coordinates of the evaluation
|
|
139
148
|
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
@@ -155,18 +164,6 @@ class Rathmann(TurbineInductionModel):
|
|
|
155
164
|
downwind_index=downwind_index,
|
|
156
165
|
)
|
|
157
166
|
|
|
158
|
-
# get ws:
|
|
159
|
-
ws = self.get_data(
|
|
160
|
-
FV.REWS,
|
|
161
|
-
FC.STATE_TARGET_TPOINT,
|
|
162
|
-
lookup="w",
|
|
163
|
-
algo=algo,
|
|
164
|
-
fdata=fdata,
|
|
165
|
-
tdata=tdata,
|
|
166
|
-
upcast=True,
|
|
167
|
-
downwind_index=downwind_index,
|
|
168
|
-
)
|
|
169
|
-
|
|
170
167
|
# get D
|
|
171
168
|
R = 0.5 * self.get_data(
|
|
172
169
|
FV.D,
|
|
@@ -199,21 +196,78 @@ class Rathmann(TurbineInductionModel):
|
|
|
199
196
|
return sin_alpha * sin_beta * (1 + x_R**2)
|
|
200
197
|
|
|
201
198
|
# ws delta in front of rotor
|
|
202
|
-
sp_sel = (ct >
|
|
199
|
+
sp_sel = (ct > 1e-8) & (x_R <= 0)
|
|
203
200
|
if np.any(sp_sel):
|
|
204
201
|
xr = x_R[sp_sel]
|
|
205
202
|
a = self.induction.ct2a(ct[sp_sel])
|
|
206
|
-
blockage =
|
|
207
|
-
|
|
203
|
+
blockage = a * mu(xr) * G(xr, r_R[sp_sel]) # eqn 10
|
|
204
|
+
|
|
205
|
+
self._superp.add_wake(
|
|
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
|
+
)
|
|
208
216
|
|
|
209
217
|
# ws delta behind rotor
|
|
210
218
|
if not self.pre_rotor_only:
|
|
211
219
|
# mirror -blockage in rotor plane
|
|
212
|
-
sp_sel = (ct >
|
|
220
|
+
sp_sel = (ct > 1e-8) & (x_R > 0) & (r_R > 1)
|
|
213
221
|
if np.any(sp_sel):
|
|
214
222
|
xr = x_R[sp_sel]
|
|
215
223
|
a = self.induction.ct2a(ct[sp_sel])
|
|
216
|
-
blockage =
|
|
217
|
-
|
|
224
|
+
blockage = a * mu(-xr) * G(-xr, r_R[sp_sel]) # eqn 10
|
|
225
|
+
|
|
226
|
+
self._superp.add_wake(
|
|
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
|
+
)
|
|
218
237
|
|
|
219
238
|
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
|
+
)
|
|
@@ -38,30 +38,39 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
38
38
|
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
|
-
def __init__(
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
superposition="ws_linear",
|
|
44
|
+
induction="Madsen",
|
|
45
|
+
gamma=1.1,
|
|
46
|
+
pre_rotor_only=False,
|
|
47
|
+
):
|
|
42
48
|
"""
|
|
43
49
|
Constructor.
|
|
44
50
|
|
|
45
51
|
Parameters
|
|
46
52
|
----------
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
superposition: str
|
|
54
|
+
The wind speed superposition.
|
|
49
55
|
induction: foxes.core.AxialInductionModel or str
|
|
50
56
|
The induction model
|
|
51
57
|
gamma: float, default=1.1
|
|
52
58
|
The parameter that multiplies Ct in the ct2a calculation
|
|
59
|
+
pre_rotor_only: bool
|
|
60
|
+
Calculate only the pre-rotor region
|
|
53
61
|
|
|
54
62
|
"""
|
|
55
63
|
super().__init__()
|
|
56
64
|
self.induction = induction
|
|
57
65
|
self.pre_rotor_only = pre_rotor_only
|
|
58
66
|
self.gamma = gamma
|
|
67
|
+
self._superp_name = superposition
|
|
59
68
|
|
|
60
69
|
def __repr__(self):
|
|
61
70
|
iname = (
|
|
62
71
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
63
72
|
)
|
|
64
|
-
return f"{type(self).__name__}(
|
|
73
|
+
return f"{type(self).__name__}({self._superp_name}, induction={iname}, gamma={self.gamma})"
|
|
65
74
|
|
|
66
75
|
def sub_models(self):
|
|
67
76
|
"""
|
|
@@ -73,7 +82,7 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
73
82
|
All sub models
|
|
74
83
|
|
|
75
84
|
"""
|
|
76
|
-
return [self.induction]
|
|
85
|
+
return [self._superp, self.induction]
|
|
77
86
|
|
|
78
87
|
def initialize(self, algo, verbosity=0, force=False):
|
|
79
88
|
"""
|
|
@@ -89,6 +98,7 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
89
98
|
Overwrite existing data
|
|
90
99
|
|
|
91
100
|
"""
|
|
101
|
+
self._superp = algo.mbook.wake_superpositions[self._superp_name]
|
|
92
102
|
if isinstance(self.induction, str):
|
|
93
103
|
self.induction = algo.mbook.axial_induction[self.induction]
|
|
94
104
|
super().initialize(algo, verbosity, force)
|
|
@@ -163,7 +173,7 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
163
173
|
The target point data
|
|
164
174
|
downwind_index: int
|
|
165
175
|
The index of the wake causing turbine
|
|
166
|
-
in the
|
|
176
|
+
in the downwind order
|
|
167
177
|
wake_coos: numpy.ndarray
|
|
168
178
|
The wake frame coordinates of the evaluation
|
|
169
179
|
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
@@ -185,18 +195,6 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
185
195
|
downwind_index=downwind_index,
|
|
186
196
|
)
|
|
187
197
|
|
|
188
|
-
# get ws
|
|
189
|
-
ws = self.get_data(
|
|
190
|
-
FV.REWS,
|
|
191
|
-
FC.STATE_TARGET_TPOINT,
|
|
192
|
-
lookup="w",
|
|
193
|
-
algo=algo,
|
|
194
|
-
fdata=fdata,
|
|
195
|
-
tdata=tdata,
|
|
196
|
-
upcast=True,
|
|
197
|
-
downwind_index=downwind_index,
|
|
198
|
-
)
|
|
199
|
-
|
|
200
198
|
# get R
|
|
201
199
|
R = 0.5 * self.get_data(
|
|
202
200
|
FV.D,
|
|
@@ -214,26 +212,78 @@ class SelfSimilar(TurbineInductionModel):
|
|
|
214
212
|
r_R = np.linalg.norm(wake_coos[..., 1:3], axis=-1) / R
|
|
215
213
|
|
|
216
214
|
# select values
|
|
217
|
-
sp_sel = (ct >
|
|
215
|
+
sp_sel = (ct > 1e-8) & (x_R <= 0) # upstream
|
|
218
216
|
if np.any(sp_sel):
|
|
219
217
|
# velocity eqn 10 from [1]
|
|
220
218
|
xr = x_R[sp_sel]
|
|
221
|
-
blockage = (
|
|
222
|
-
|
|
219
|
+
blockage = self._a(ct[sp_sel], xr) * self._rad_fn(xr, r_R[sp_sel])
|
|
220
|
+
|
|
221
|
+
self._superp.add_wake(
|
|
222
|
+
algo,
|
|
223
|
+
mdata,
|
|
224
|
+
fdata,
|
|
225
|
+
tdata,
|
|
226
|
+
downwind_index,
|
|
227
|
+
sp_sel,
|
|
228
|
+
FV.WS,
|
|
229
|
+
wake_deltas[FV.WS],
|
|
230
|
+
-blockage,
|
|
223
231
|
)
|
|
224
|
-
wake_deltas[FV.WS][sp_sel] -= blockage
|
|
225
232
|
|
|
226
233
|
# set area behind to mirrored value EXCEPT for area behind turbine
|
|
227
234
|
if not self.pre_rotor_only:
|
|
228
|
-
sp_sel = (ct >
|
|
235
|
+
sp_sel = (ct > 1e-8) & (x_R > 0) & (r_R > 1)
|
|
229
236
|
if np.any(sp_sel):
|
|
230
237
|
# velocity eqn 10 from [1]
|
|
231
238
|
xr = x_R[sp_sel]
|
|
232
|
-
blockage = (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
blockage = self._a(ct[sp_sel], -xr) * self._rad_fn(-xr, r_R[sp_sel])
|
|
240
|
+
|
|
241
|
+
# wdelta[sp_sel] += blockage
|
|
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,
|
|
236
252
|
)
|
|
237
|
-
wake_deltas[FV.WS][sp_sel] += blockage
|
|
238
253
|
|
|
239
254
|
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
|
+
)
|
|
@@ -10,7 +10,10 @@ class VortexSheet(TurbineInductionModel):
|
|
|
10
10
|
"""
|
|
11
11
|
The Vortex Sheet model implemented with a radial dependency
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Notes
|
|
14
|
+
-----
|
|
15
|
+
Reference:
|
|
16
|
+
Medici, D., et al. "The upstream flow of a wind turbine: blockage effect." Wind Energy 14.5 (2011): 691-697.
|
|
14
17
|
https://doi.org/10.1002/we.451
|
|
15
18
|
|
|
16
19
|
Attributes
|
|
@@ -24,29 +27,35 @@ class VortexSheet(TurbineInductionModel):
|
|
|
24
27
|
|
|
25
28
|
"""
|
|
26
29
|
|
|
27
|
-
def __init__(
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
superposition="ws_linear",
|
|
33
|
+
induction="Madsen",
|
|
34
|
+
pre_rotor_only=False,
|
|
35
|
+
):
|
|
28
36
|
"""
|
|
29
37
|
Constructor.
|
|
30
38
|
|
|
31
39
|
Parameters
|
|
32
40
|
----------
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
superposition: str
|
|
42
|
+
The wind speed superposition
|
|
35
43
|
induction: foxes.core.AxialInductionModel or str
|
|
36
44
|
The induction model
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
pre_rotor_only: bool
|
|
46
|
+
Calculate only the pre-rotor region
|
|
39
47
|
|
|
40
48
|
"""
|
|
41
49
|
super().__init__()
|
|
42
50
|
self.induction = induction
|
|
43
51
|
self.pre_rotor_only = pre_rotor_only
|
|
52
|
+
self._superp_name = superposition
|
|
44
53
|
|
|
45
54
|
def __repr__(self):
|
|
46
55
|
iname = (
|
|
47
56
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
48
57
|
)
|
|
49
|
-
return f"{type(self).__name__}, induction={iname})"
|
|
58
|
+
return f"{type(self).__name__}({self._superp_name}, induction={iname})"
|
|
50
59
|
|
|
51
60
|
def sub_models(self):
|
|
52
61
|
"""
|
|
@@ -58,7 +67,7 @@ class VortexSheet(TurbineInductionModel):
|
|
|
58
67
|
All sub models
|
|
59
68
|
|
|
60
69
|
"""
|
|
61
|
-
return [self.induction]
|
|
70
|
+
return [self._superp, self.induction]
|
|
62
71
|
|
|
63
72
|
def initialize(self, algo, verbosity=0, force=False):
|
|
64
73
|
"""
|
|
@@ -74,6 +83,7 @@ class VortexSheet(TurbineInductionModel):
|
|
|
74
83
|
Overwrite existing data
|
|
75
84
|
|
|
76
85
|
"""
|
|
86
|
+
self._superp = algo.mbook.wake_superpositions[self._superp_name]
|
|
77
87
|
if isinstance(self.induction, str):
|
|
78
88
|
self.induction = algo.mbook.axial_induction[self.induction]
|
|
79
89
|
super().initialize(algo, verbosity, force)
|
|
@@ -129,7 +139,7 @@ class VortexSheet(TurbineInductionModel):
|
|
|
129
139
|
The target point data
|
|
130
140
|
downwind_index: int
|
|
131
141
|
The index of the wake causing turbine
|
|
132
|
-
in the
|
|
142
|
+
in the downwind order
|
|
133
143
|
wake_coos: numpy.ndarray
|
|
134
144
|
The wake frame coordinates of the evaluation
|
|
135
145
|
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
@@ -159,18 +169,6 @@ class VortexSheet(TurbineInductionModel):
|
|
|
159
169
|
downwind_index=downwind_index,
|
|
160
170
|
)
|
|
161
171
|
|
|
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
172
|
# get D
|
|
175
173
|
D = self.get_data(
|
|
176
174
|
FV.D,
|
|
@@ -183,45 +181,85 @@ class VortexSheet(TurbineInductionModel):
|
|
|
183
181
|
downwind_index=downwind_index,
|
|
184
182
|
)
|
|
185
183
|
|
|
186
|
-
sp_sel = (ct >
|
|
187
|
-
ws_sel = ws[sp_sel]
|
|
184
|
+
sp_sel = (ct > 1e-8) & (x <= 0)
|
|
188
185
|
ct_sel = ct[sp_sel]
|
|
189
186
|
r_sph_sel = r_sph[sp_sel]
|
|
190
187
|
D_sel = D[sp_sel]
|
|
191
188
|
|
|
192
189
|
if np.any(sp_sel):
|
|
193
|
-
blockage = (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
190
|
+
blockage = self.induction.ct2a(ct_sel) * (
|
|
191
|
+
(1 + 2 * r_sph_sel / D_sel) * (1 + (2 * r_sph_sel / D_sel) ** 2)
|
|
192
|
+
) ** (-0.5)
|
|
193
|
+
|
|
194
|
+
self._superp.add_wake(
|
|
195
|
+
algo,
|
|
196
|
+
mdata,
|
|
197
|
+
fdata,
|
|
198
|
+
tdata,
|
|
199
|
+
downwind_index,
|
|
200
|
+
sp_sel,
|
|
201
|
+
FV.WS,
|
|
202
|
+
wake_deltas[FV.WS],
|
|
203
|
+
-blockage,
|
|
204
|
+
)
|
|
203
205
|
|
|
204
206
|
if not self.pre_rotor_only:
|
|
205
207
|
sp_sel = (
|
|
206
|
-
(ct >
|
|
208
|
+
(ct > 1e-8) & (x > 0) & (r > D / 2)
|
|
207
209
|
) # mirror in rotor plane and inverse blockage, but not directly behind rotor
|
|
208
|
-
ws_sel = ws[sp_sel]
|
|
209
210
|
ct_sel = ct[sp_sel]
|
|
210
211
|
r_sph_sel = r_sph[sp_sel]
|
|
211
212
|
D_sel = D[sp_sel]
|
|
212
213
|
if np.any(sp_sel):
|
|
213
|
-
blockage = (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
214
|
+
blockage = self.induction.ct2a(ct_sel) * (
|
|
215
|
+
(1 + 2 * r_sph_sel / D_sel) * (1 + (2 * r_sph_sel / D_sel) ** 2)
|
|
216
|
+
) ** (-0.5)
|
|
217
|
+
|
|
218
|
+
self._superp.add_wake(
|
|
219
|
+
algo,
|
|
220
|
+
mdata,
|
|
221
|
+
fdata,
|
|
222
|
+
tdata,
|
|
223
|
+
downwind_index,
|
|
224
|
+
sp_sel,
|
|
225
|
+
FV.WS,
|
|
226
|
+
wake_deltas[FV.WS],
|
|
227
|
+
blockage,
|
|
228
|
+
)
|
|
226
229
|
|
|
227
230
|
return wake_deltas
|
|
231
|
+
|
|
232
|
+
def finalize_wake_deltas(
|
|
233
|
+
self,
|
|
234
|
+
algo,
|
|
235
|
+
mdata,
|
|
236
|
+
fdata,
|
|
237
|
+
amb_results,
|
|
238
|
+
wake_deltas,
|
|
239
|
+
):
|
|
240
|
+
"""
|
|
241
|
+
Finalize the wake calculation.
|
|
242
|
+
|
|
243
|
+
Modifies wake_deltas on the fly.
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
algo: foxes.core.Algorithm
|
|
248
|
+
The calculation algorithm
|
|
249
|
+
mdata: foxes.core.MData
|
|
250
|
+
The model data
|
|
251
|
+
fdata: foxes.core.FData
|
|
252
|
+
The farm data
|
|
253
|
+
amb_results: dict
|
|
254
|
+
The ambient results, key: variable name str,
|
|
255
|
+
values: numpy.ndarray with shape
|
|
256
|
+
(n_states, n_targets, n_tpoints)
|
|
257
|
+
wake_deltas: dict
|
|
258
|
+
The wake deltas object at the selected target
|
|
259
|
+
turbines. Key: variable str, value: numpy.ndarray
|
|
260
|
+
with shape (n_states, n_targets, n_tpoints)
|
|
261
|
+
|
|
262
|
+
"""
|
|
263
|
+
wake_deltas[FV.WS] = self._superp.calc_final_wake_delta(
|
|
264
|
+
algo, mdata, fdata, FV.WS, amb_results[FV.WS], wake_deltas[FV.WS]
|
|
265
|
+
)
|
|
@@ -278,8 +278,9 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
278
278
|
fdata=fdata,
|
|
279
279
|
tdata=tdata,
|
|
280
280
|
downwind_index=downwind_index,
|
|
281
|
-
upcast=
|
|
282
|
-
|
|
281
|
+
upcast=False,
|
|
282
|
+
selection=st_sel,
|
|
283
|
+
)
|
|
283
284
|
|
|
284
285
|
# get TI:
|
|
285
286
|
ti = self.get_data(
|
|
@@ -290,8 +291,9 @@ class CrespoHernandezTIWake(TopHatWakeModel):
|
|
|
290
291
|
fdata=fdata,
|
|
291
292
|
tdata=tdata,
|
|
292
293
|
downwind_index=downwind_index,
|
|
293
|
-
upcast=
|
|
294
|
-
|
|
294
|
+
upcast=False,
|
|
295
|
+
selection=st_sel,
|
|
296
|
+
)
|
|
295
297
|
|
|
296
298
|
# calculate induction factor:
|
|
297
299
|
twoa = 2 * self.induction.ct2a(ct)
|
|
@@ -54,24 +54,27 @@ class IECTIWake(TopHatWakeModel):
|
|
|
54
54
|
|
|
55
55
|
"""
|
|
56
56
|
super().__init__(superpositions={FV.TI: superposition}, induction=induction)
|
|
57
|
+
self.iec_type = iec_type
|
|
58
|
+
self.wake_k = None
|
|
57
59
|
|
|
58
|
-
if opening_angle is
|
|
60
|
+
if opening_angle is None:
|
|
61
|
+
self.wake_k = WakeK(**wake_k)
|
|
62
|
+
else:
|
|
59
63
|
if "k" in wake_k or "ka" in wake_k or "kb" in wake_k:
|
|
60
64
|
raise KeyError(
|
|
61
65
|
f"Can handle 'opening_angle' or ('k', 'ka', 'kb') parameters, not both"
|
|
62
66
|
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
self.iec_type = iec_type
|
|
66
|
-
self.wake_k = WakeK(**wake_k)
|
|
67
|
+
self._k = float(np.tan(np.deg2rad(opening_angle / 2.0)))
|
|
67
68
|
|
|
68
69
|
def __repr__(self):
|
|
69
70
|
iname = (
|
|
70
71
|
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
71
72
|
)
|
|
72
73
|
s = f"{type(self).__name__}"
|
|
73
|
-
s += f"({self.superpositions[FV.TI]}, induction={iname}
|
|
74
|
-
|
|
74
|
+
s += f"({self.superpositions[FV.TI]}, induction={iname}"
|
|
75
|
+
if self.wake_k is not None:
|
|
76
|
+
s += ", " + self.wake_k.repr()
|
|
77
|
+
s += ")"
|
|
75
78
|
return s
|
|
76
79
|
|
|
77
80
|
def sub_models(self):
|
|
@@ -84,7 +87,7 @@ class IECTIWake(TopHatWakeModel):
|
|
|
84
87
|
All sub models
|
|
85
88
|
|
|
86
89
|
"""
|
|
87
|
-
return [self.wake_k]
|
|
90
|
+
return [self.wake_k] if self.wake_k is not None else []
|
|
88
91
|
|
|
89
92
|
def new_wake_deltas(self, algo, mdata, fdata, tdata):
|
|
90
93
|
"""
|
|
@@ -147,15 +150,29 @@ class IECTIWake(TopHatWakeModel):
|
|
|
147
150
|
The wake radii, shape: (n_states, n_targets)
|
|
148
151
|
|
|
149
152
|
"""
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
if self.wake_k is None:
|
|
154
|
+
return self._k * x
|
|
155
|
+
else:
|
|
156
|
+
D = self.get_data(
|
|
157
|
+
FV.D,
|
|
158
|
+
FC.STATE_TARGET,
|
|
159
|
+
lookup="w",
|
|
160
|
+
algo=algo,
|
|
161
|
+
fdata=fdata,
|
|
162
|
+
tdata=tdata,
|
|
163
|
+
downwind_index=downwind_index,
|
|
164
|
+
upcast=True,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
k = self.wake_k(
|
|
168
|
+
FC.STATE_TARGET,
|
|
169
|
+
algo=algo,
|
|
170
|
+
fdata=fdata,
|
|
171
|
+
tdata=tdata,
|
|
172
|
+
upcast=False,
|
|
173
|
+
downwind_index=downwind_index,
|
|
174
|
+
)
|
|
175
|
+
return D / 2 + k * x
|
|
159
176
|
|
|
160
177
|
def calc_centreline(
|
|
161
178
|
self,
|
|
@@ -211,8 +228,9 @@ class IECTIWake(TopHatWakeModel):
|
|
|
211
228
|
fdata=fdata,
|
|
212
229
|
tdata=tdata,
|
|
213
230
|
downwind_index=downwind_index,
|
|
214
|
-
upcast=
|
|
215
|
-
|
|
231
|
+
upcast=False,
|
|
232
|
+
selection=st_sel,
|
|
233
|
+
)
|
|
216
234
|
|
|
217
235
|
# get ws:
|
|
218
236
|
ws = self.get_data(
|
|
@@ -223,8 +241,9 @@ class IECTIWake(TopHatWakeModel):
|
|
|
223
241
|
fdata=fdata,
|
|
224
242
|
tdata=tdata,
|
|
225
243
|
downwind_index=downwind_index,
|
|
226
|
-
upcast=
|
|
227
|
-
|
|
244
|
+
upcast=False,
|
|
245
|
+
selection=st_sel,
|
|
246
|
+
)
|
|
228
247
|
|
|
229
248
|
# calculate wind deficit:
|
|
230
249
|
if self.iec_type == "2005":
|
|
@@ -209,7 +209,7 @@ class TopHatWakeModel(AxisymmetricWakeModel):
|
|
|
209
209
|
wake_r = self.calc_wake_radius(algo, mdata, fdata, tdata, downwind_index, x, ct)
|
|
210
210
|
|
|
211
211
|
wdeltas = {}
|
|
212
|
-
st_sel = (
|
|
212
|
+
st_sel = (x > 1e-8) & (ct > 1e-8) & np.any(r < wake_r[:, :, None], axis=2)
|
|
213
213
|
if np.any(st_sel):
|
|
214
214
|
x = x[st_sel]
|
|
215
215
|
r = r[st_sel]
|