foxes 1.3__py3-none-any.whl → 1.5__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/field_data_nc/run.py +1 -1
- 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 +29 -6
- 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 +19 -9
- foxes/__init__.py +21 -17
- foxes/algorithms/__init__.py +6 -6
- foxes/algorithms/downwind/__init__.py +2 -2
- foxes/algorithms/downwind/downwind.py +49 -17
- 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 +58 -29
- foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
- foxes/algorithms/downwind/models/set_amb_farm_results.py +1 -1
- 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 +5 -6
- foxes/core/data.py +94 -22
- foxes/core/data_calc_model.py +4 -2
- foxes/core/engine.py +42 -53
- foxes/core/farm_controller.py +2 -2
- foxes/core/farm_data_model.py +16 -13
- foxes/core/ground_model.py +4 -13
- foxes/core/model.py +24 -6
- foxes/core/partial_wakes_model.py +147 -10
- foxes/core/point_data_model.py +21 -17
- foxes/core/rotor_model.py +4 -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/point_cloud_100.nc +0 -0
- foxes/data/states/weibull_cloud_4.nc +0 -0
- foxes/data/states/weibull_grid.nc +0 -0
- 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 +42 -20
- foxes/engines/default.py +2 -2
- foxes/engines/numpy.py +11 -13
- foxes/engines/pool.py +20 -11
- foxes/engines/single.py +8 -6
- foxes/input/__init__.py +3 -3
- foxes/input/farm_layout/__init__.py +9 -8
- foxes/input/farm_layout/from_arrays.py +68 -0
- foxes/input/farm_layout/from_csv.py +1 -1
- foxes/input/farm_layout/ring.py +0 -1
- foxes/input/states/__init__.py +28 -12
- foxes/input/states/create/__init__.py +3 -2
- foxes/input/states/dataset_states.py +710 -0
- foxes/input/states/field_data.py +531 -0
- foxes/input/states/multi_height.py +11 -6
- foxes/input/states/one_point_flow.py +1 -4
- foxes/input/states/point_cloud_data.py +618 -0
- foxes/input/states/scan.py +2 -0
- foxes/input/states/single.py +3 -1
- foxes/input/states/states_table.py +23 -30
- foxes/input/states/weibull_sectors.py +330 -0
- foxes/input/states/wrg_states.py +8 -6
- foxes/input/yaml/__init__.py +9 -3
- foxes/input/yaml/dict.py +42 -41
- foxes/input/yaml/windio/__init__.py +10 -5
- foxes/input/yaml/windio/read_attributes.py +42 -29
- foxes/input/yaml/windio/read_farm.py +17 -15
- foxes/input/yaml/windio/read_fields.py +4 -2
- foxes/input/yaml/windio/read_outputs.py +25 -15
- foxes/input/yaml/windio/read_site.py +172 -11
- foxes/input/yaml/windio/windio.py +23 -11
- foxes/input/yaml/yaml.py +1 -0
- 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 +190 -63
- 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 +4 -3
- foxes/models/rotor_models/centre.py +1 -1
- foxes/models/rotor_models/direct_infusion.py +241 -0
- foxes/models/turbine_models/__init__.py +11 -11
- foxes/models/turbine_models/calculator.py +16 -3
- foxes/models/turbine_models/kTI_model.py +1 -0
- foxes/models/turbine_models/lookup_table.py +2 -0
- foxes/models/turbine_models/power_mask.py +1 -0
- foxes/models/turbine_models/rotor_centre_calc.py +2 -0
- foxes/models/turbine_models/sector_management.py +1 -0
- foxes/models/turbine_models/set_farm_vars.py +3 -9
- foxes/models/turbine_models/table_factors.py +2 -0
- foxes/models/turbine_models/thrust2ct.py +1 -0
- foxes/models/turbine_models/yaw2yawm.py +2 -0
- foxes/models/turbine_models/yawm2yaw.py +2 -0
- foxes/models/turbine_types/PCt_file.py +2 -6
- foxes/models/turbine_types/PCt_from_two.py +1 -2
- foxes/models/turbine_types/__init__.py +10 -9
- foxes/models/turbine_types/calculator_type.py +123 -0
- foxes/models/turbine_types/null_type.py +1 -0
- foxes/models/turbine_types/wsrho2PCt_from_two.py +2 -0
- foxes/models/turbine_types/wsti2PCt_from_two.py +3 -1
- 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 +4 -2
- foxes/output/farm_results_eval.py +19 -16
- foxes/output/flow_plots_2d/__init__.py +2 -2
- foxes/output/flow_plots_2d/flow_plots.py +18 -0
- foxes/output/flow_plots_2d/get_fig.py +5 -2
- foxes/output/output.py +6 -1
- foxes/output/results_writer.py +1 -1
- foxes/output/rose_plot.py +13 -3
- foxes/output/rotor_point_plots.py +3 -0
- 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/state_turbine_map.py +3 -0
- foxes/output/turbine_type_curves.py +10 -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 +49 -37
- foxes/utils/exec_python.py +5 -5
- foxes/utils/factory.py +3 -5
- 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.5.dist-info}/METADATA +34 -63
- foxes-1.5.dist-info/RECORD +328 -0
- {foxes-1.3.dist-info → foxes-1.5.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/input/states/field_data_nc.py +0 -847
- 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.5.dist-info}/entry_points.txt +0 -0
- {foxes-1.3.dist-info → foxes-1.5.dist-info/licenses}/LICENSE +0 -0
- {foxes-1.3.dist-info → foxes-1.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from foxes.core import TurbineType
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CalculatorType(TurbineType):
|
|
5
|
+
"""
|
|
6
|
+
Direct data infusion by a user function.
|
|
7
|
+
|
|
8
|
+
:group: models.turbine_types
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
func,
|
|
15
|
+
out_vars,
|
|
16
|
+
*args,
|
|
17
|
+
needs_rews2=False,
|
|
18
|
+
needs_rews3=False,
|
|
19
|
+
**kwargs,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Constructor.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
func: callable
|
|
27
|
+
The function to calculate farm variables, should have the signature:
|
|
28
|
+
f(algo, mdata, fdata, st_sel) -> dict, where the keys are
|
|
29
|
+
output variable names and the values are numpy.ndarrays
|
|
30
|
+
with shape (n_states, n_turbines).
|
|
31
|
+
|
|
32
|
+
Beware that the turbine ordering in fdata is in downwind order,
|
|
33
|
+
hence external data X of shape (n_states, n_turbines) in farm order
|
|
34
|
+
needs to be reordered by X[ssel, order] with
|
|
35
|
+
ssel = fdata[FV.ORDER_SSEL], order = fdata[FV.ORDER]
|
|
36
|
+
before using it in combination with fdata variables.
|
|
37
|
+
out_vars: list of str
|
|
38
|
+
The output variables of the function
|
|
39
|
+
args: tuple, optional
|
|
40
|
+
Additional parameters for TurbineType class
|
|
41
|
+
needs_rews2: bool
|
|
42
|
+
Flag for runs that require the REWS2 variable
|
|
43
|
+
needs_rews3: bool
|
|
44
|
+
Flag for runs that require the REWS3 variable
|
|
45
|
+
kwargs: dict, optional
|
|
46
|
+
Additional parameters for TurbineType class
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
super().__init__(*args, **kwargs)
|
|
50
|
+
self._func = func
|
|
51
|
+
self._ovars = out_vars
|
|
52
|
+
self._rews2 = needs_rews2
|
|
53
|
+
self._rews3 = needs_rews3
|
|
54
|
+
|
|
55
|
+
def needs_rews2(self):
|
|
56
|
+
"""
|
|
57
|
+
Returns flag for requiring REWS2 variable
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
flag: bool
|
|
62
|
+
True if REWS2 is required
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
return self._rews2
|
|
66
|
+
|
|
67
|
+
def needs_rews3(self):
|
|
68
|
+
"""
|
|
69
|
+
Returns flag for requiring REWS3 variable
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
flag: bool
|
|
74
|
+
True if REWS3 is required
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
return self._rews3
|
|
78
|
+
|
|
79
|
+
def output_farm_vars(self, algo):
|
|
80
|
+
"""
|
|
81
|
+
The variables which are being modified by the model.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
algo: foxes.core.Algorithm
|
|
86
|
+
The calculation algorithm
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
output_vars: list of str
|
|
91
|
+
The output variable names
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
return self._ovars
|
|
95
|
+
|
|
96
|
+
def calculate(self, algo, mdata, fdata, st_sel):
|
|
97
|
+
"""
|
|
98
|
+
The main model calculation.
|
|
99
|
+
|
|
100
|
+
This function is executed on a single chunk of data,
|
|
101
|
+
all computations should be based on numpy arrays.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
algo: foxes.core.Algorithm
|
|
106
|
+
The calculation algorithm
|
|
107
|
+
mdata: foxes.core.MData
|
|
108
|
+
The model data
|
|
109
|
+
fdata: foxes.core.FData
|
|
110
|
+
The farm data
|
|
111
|
+
st_sel: numpy.ndarray of bool
|
|
112
|
+
The state-turbine selection,
|
|
113
|
+
shape: (n_states, n_turbines)
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
results: dict
|
|
118
|
+
The resulting data, keys: output variable str.
|
|
119
|
+
Values: numpy.ndarray with shape (n_states, n_turbines)
|
|
120
|
+
|
|
121
|
+
"""
|
|
122
|
+
self.ensure_output_vars(algo, fdata)
|
|
123
|
+
return self._func(algo, mdata, fdata, st_sel)
|
|
@@ -5,8 +5,8 @@ from scipy.interpolate import interpn
|
|
|
5
5
|
from foxes.core import TurbineType
|
|
6
6
|
from foxes.utils import PandasFileHelper
|
|
7
7
|
from foxes.data import PCTCURVE, parse_Pct_two_files
|
|
8
|
-
import foxes.variables as FV
|
|
9
8
|
from foxes.config import config, get_input_path
|
|
9
|
+
import foxes.variables as FV
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class WsTI2PCtFromTwo(TurbineType):
|
|
@@ -274,6 +274,8 @@ class WsTI2PCtFromTwo(TurbineType):
|
|
|
274
274
|
Values: numpy.ndarray with shape (n_states, n_turbines)
|
|
275
275
|
|
|
276
276
|
"""
|
|
277
|
+
# prepare:
|
|
278
|
+
self.ensure_output_vars(algo, fdata)
|
|
277
279
|
|
|
278
280
|
# calculate P:
|
|
279
281
|
st_sel_P = (
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Vertical profile models.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from .uniform import UniformProfile
|
|
6
|
-
from .abl_log_neutral_ws import ABLLogNeutralWsProfile
|
|
7
|
-
from .abl_log_stable_ws import ABLLogStableWsProfile
|
|
8
|
-
from .abl_log_unstable_ws import ABLLogUnstableWsProfile
|
|
9
|
-
from .abl_log_ws import ABLLogWsProfile
|
|
10
|
-
from .sheared_ws import ShearedProfile
|
|
11
|
-
from .data_profile import DataProfile
|
|
5
|
+
from .uniform import UniformProfile as UniformProfile
|
|
6
|
+
from .abl_log_neutral_ws import ABLLogNeutralWsProfile as ABLLogNeutralWsProfile
|
|
7
|
+
from .abl_log_stable_ws import ABLLogStableWsProfile as ABLLogStableWsProfile
|
|
8
|
+
from .abl_log_unstable_ws import ABLLogUnstableWsProfile as ABLLogUnstableWsProfile
|
|
9
|
+
from .abl_log_ws import ABLLogWsProfile as ABLLogWsProfile
|
|
10
|
+
from .sheared_ws import ShearedProfile as ShearedProfile
|
|
11
|
+
from .data_profile import DataProfile as DataProfile
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
-
from foxes.
|
|
3
|
+
from foxes.config import config
|
|
4
|
+
from foxes.core.wake_deflection import WakeDeflection
|
|
5
|
+
from foxes.core.wake_model import WakeK
|
|
4
6
|
from foxes.models.wake_models.wind.bastankhah16 import (
|
|
5
7
|
Bastankhah2016Model,
|
|
6
8
|
Bastankhah2016,
|
|
7
9
|
)
|
|
8
|
-
from foxes.config import config
|
|
9
|
-
import foxes.variables as FV
|
|
10
10
|
import foxes.constants as FC
|
|
11
|
-
|
|
12
|
-
from .rotor_wd import RotorWD
|
|
11
|
+
import foxes.variables as FV
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
class
|
|
14
|
+
class Bastankhah2016Deflection(WakeDeflection):
|
|
16
15
|
"""
|
|
17
16
|
Bend the wakes for yawed turbines, based on the
|
|
18
17
|
Bastankhah 2016 wake model
|
|
@@ -28,25 +27,26 @@ class YawedWakes(WakeFrame):
|
|
|
28
27
|
----------
|
|
29
28
|
model: Bastankhah2016Model
|
|
30
29
|
The model for computing common data
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
:
|
|
30
|
+
alpha: float
|
|
31
|
+
model parameter used to determine onset of far wake region,
|
|
32
|
+
if not found in wake model
|
|
33
|
+
beta: float
|
|
34
|
+
model parameter used to determine onset of far wake region,
|
|
35
|
+
if not found in wake model
|
|
36
|
+
wake_k: dict
|
|
37
|
+
Parameters for the WakeK class, if not found in wake model
|
|
38
|
+
induction: foxes.core.AxialInductionModel
|
|
39
|
+
The induction model, if not found in wake model
|
|
40
|
+
|
|
41
|
+
:group: models.wake_deflections
|
|
40
42
|
|
|
41
43
|
"""
|
|
42
44
|
|
|
43
45
|
def __init__(
|
|
44
46
|
self,
|
|
45
|
-
base_frame=RotorWD(),
|
|
46
47
|
alpha=0.58,
|
|
47
48
|
beta=0.07,
|
|
48
49
|
induction="Madsen",
|
|
49
|
-
max_length_km=30,
|
|
50
50
|
**wake_k,
|
|
51
51
|
):
|
|
52
52
|
"""
|
|
@@ -54,8 +54,6 @@ class YawedWakes(WakeFrame):
|
|
|
54
54
|
|
|
55
55
|
Parameters
|
|
56
56
|
----------
|
|
57
|
-
base_frame: foxes.core.WakeFrame
|
|
58
|
-
The wake frame from which to start
|
|
59
57
|
alpha: float
|
|
60
58
|
model parameter used to determine onset of far wake region,
|
|
61
59
|
if not found in wake model
|
|
@@ -66,13 +64,10 @@ class YawedWakes(WakeFrame):
|
|
|
66
64
|
The induction model, if not found in wake model
|
|
67
65
|
wake_k: dict, optional
|
|
68
66
|
Parameters for the WakeK class, if not found in wake model
|
|
69
|
-
max_length_km: float
|
|
70
|
-
The maximal wake length in km
|
|
71
67
|
|
|
72
68
|
"""
|
|
73
|
-
super().__init__(
|
|
69
|
+
super().__init__()
|
|
74
70
|
|
|
75
|
-
self.base_frame = base_frame
|
|
76
71
|
self.model = None
|
|
77
72
|
self.alpha = alpha
|
|
78
73
|
self.beta = beta
|
|
@@ -98,7 +93,7 @@ class YawedWakes(WakeFrame):
|
|
|
98
93
|
Names of all sub models
|
|
99
94
|
|
|
100
95
|
"""
|
|
101
|
-
return [self.wake_k, self.
|
|
96
|
+
return [self.wake_k, self.model]
|
|
102
97
|
|
|
103
98
|
def initialize(self, algo, verbosity=0, force=False):
|
|
104
99
|
"""
|
|
@@ -138,30 +133,6 @@ class YawedWakes(WakeFrame):
|
|
|
138
133
|
|
|
139
134
|
super().initialize(algo, verbosity, force)
|
|
140
135
|
|
|
141
|
-
def calc_order(self, algo, mdata, fdata):
|
|
142
|
-
"""
|
|
143
|
-
Calculates the order of turbine evaluation.
|
|
144
|
-
|
|
145
|
-
This function is executed on a single chunk of data,
|
|
146
|
-
all computations should be based on numpy arrays.
|
|
147
|
-
|
|
148
|
-
Parameters
|
|
149
|
-
----------
|
|
150
|
-
algo: foxes.core.Algorithm
|
|
151
|
-
The calculation algorithm
|
|
152
|
-
mdata: foxes.core.MData
|
|
153
|
-
The model data
|
|
154
|
-
fdata: foxes.core.FData
|
|
155
|
-
The farm data
|
|
156
|
-
|
|
157
|
-
Returns
|
|
158
|
-
-------
|
|
159
|
-
order: numpy.ndarray
|
|
160
|
-
The turbine order, shape: (n_states, n_turbines)
|
|
161
|
-
|
|
162
|
-
"""
|
|
163
|
-
return self.base_frame.calc_order(algo, mdata, fdata)
|
|
164
|
-
|
|
165
136
|
def _update_y(self, algo, mdata, fdata, tdata, downwind_index, x, y):
|
|
166
137
|
"""
|
|
167
138
|
Helper function for y deflection
|
|
@@ -227,16 +198,20 @@ class YawedWakes(WakeFrame):
|
|
|
227
198
|
# apply deflection:
|
|
228
199
|
y[st_sel] -= ydef
|
|
229
200
|
|
|
230
|
-
def
|
|
201
|
+
def calc_deflection(
|
|
231
202
|
self,
|
|
232
203
|
algo,
|
|
233
204
|
mdata,
|
|
234
205
|
fdata,
|
|
235
206
|
tdata,
|
|
236
207
|
downwind_index,
|
|
208
|
+
coos,
|
|
237
209
|
):
|
|
238
210
|
"""
|
|
239
|
-
|
|
211
|
+
Calculates the wake deflection.
|
|
212
|
+
|
|
213
|
+
This function optionally adds FC.WDEFL_ROT_ANGLE or
|
|
214
|
+
FC.WDEFL_DWS_FACTOR to the tdata.
|
|
240
215
|
|
|
241
216
|
Parameters
|
|
242
217
|
----------
|
|
@@ -251,79 +226,25 @@ class YawedWakes(WakeFrame):
|
|
|
251
226
|
downwind_index: int
|
|
252
227
|
The index of the wake causing turbine
|
|
253
228
|
in the downwind order
|
|
229
|
+
coos: numpy.ndarray
|
|
230
|
+
The wake frame coordinates of the evaluation
|
|
231
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
254
232
|
|
|
255
233
|
Returns
|
|
256
234
|
-------
|
|
257
|
-
|
|
235
|
+
coos: numpy.ndarray
|
|
258
236
|
The wake frame coordinates of the evaluation
|
|
259
237
|
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
260
238
|
|
|
261
239
|
"""
|
|
262
|
-
# get unyawed results:
|
|
263
|
-
xyz = self.base_frame.get_wake_coos(
|
|
264
|
-
algo,
|
|
265
|
-
mdata,
|
|
266
|
-
fdata,
|
|
267
|
-
tdata,
|
|
268
|
-
downwind_index,
|
|
269
|
-
)
|
|
270
240
|
|
|
271
241
|
# take rotor average:
|
|
272
|
-
xy = np.einsum("stpd,p->std",
|
|
242
|
+
xy = np.einsum("stpd,p->std", coos[..., :2], tdata[FC.TWEIGHTS])
|
|
273
243
|
x = xy[:, :, 0]
|
|
274
244
|
y = xy[:, :, 1]
|
|
275
245
|
|
|
276
246
|
# apply deflection:
|
|
277
247
|
self._update_y(algo, mdata, fdata, tdata, downwind_index, x, y)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return xyz
|
|
281
|
-
|
|
282
|
-
def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
|
|
283
|
-
"""
|
|
284
|
-
Gets the points along the centreline for given
|
|
285
|
-
values of x.
|
|
286
|
-
|
|
287
|
-
Parameters
|
|
288
|
-
----------
|
|
289
|
-
algo: foxes.core.Algorithm
|
|
290
|
-
The calculation algorithm
|
|
291
|
-
mdata: foxes.core.MData
|
|
292
|
-
The model data
|
|
293
|
-
fdata: foxes.core.FData
|
|
294
|
-
The farm data
|
|
295
|
-
downwind_index: int
|
|
296
|
-
The index in the downwind order
|
|
297
|
-
x: numpy.ndarray
|
|
298
|
-
The wake frame x coordinates, shape: (n_states, n_points)
|
|
299
|
-
|
|
300
|
-
Returns
|
|
301
|
-
-------
|
|
302
|
-
points: numpy.ndarray
|
|
303
|
-
The centreline points, shape: (n_states, n_points, 3)
|
|
304
|
-
|
|
305
|
-
"""
|
|
306
|
-
points = self.base_frame.get_centreline_points(
|
|
307
|
-
algo, mdata, fdata, downwind_index, x
|
|
308
|
-
)
|
|
309
|
-
tdata = TData.from_points(points)
|
|
310
|
-
|
|
311
|
-
nx = np.zeros_like(points)
|
|
312
|
-
nx[:, 0] = points[:, 1] - points[:, 0]
|
|
313
|
-
nx[:, -1] = points[:, -1] - points[:, -2]
|
|
314
|
-
nx[:, 1:-1] = 0.5 * (points[:, 1:-1] - points[:, :-2]) + 0.5 * (
|
|
315
|
-
points[:, 2:] - points[:, 1:-1]
|
|
316
|
-
)
|
|
317
|
-
nx /= np.linalg.norm(nx, axis=-1)[:, :, None]
|
|
318
|
-
|
|
319
|
-
nz = np.zeros_like(nx)
|
|
320
|
-
nz[:, :, 2] = 1
|
|
321
|
-
ny = np.cross(nz, nx, axis=-1)
|
|
322
|
-
del nx, nz
|
|
323
|
-
|
|
324
|
-
y = np.zeros_like(x)
|
|
325
|
-
self._update_y(algo, mdata, fdata, tdata, downwind_index, x, y)
|
|
326
|
-
|
|
327
|
-
points += y[:, :, None] * ny
|
|
248
|
+
coos[..., 1] = y[:, :, None]
|
|
328
249
|
|
|
329
|
-
return
|
|
250
|
+
return coos
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from foxes.core.wake_deflection import WakeDeflection
|
|
4
|
+
from foxes.algorithms import Sequential
|
|
5
|
+
import foxes.constants as FC
|
|
6
|
+
import foxes.variables as FV
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class JimenezDeflection(WakeDeflection):
|
|
10
|
+
"""
|
|
11
|
+
Yawed rotor wake defection according to the Jimenez model
|
|
12
|
+
|
|
13
|
+
Notes
|
|
14
|
+
-----
|
|
15
|
+
Reference:
|
|
16
|
+
Jiménez, Á., Crespo, A. and Migoya, E. (2010), Application of a LES technique to characterize
|
|
17
|
+
the wake deflection of a wind turbine in yaw. Wind Energ., 13: 559-572. doi:10.1002/we.380
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Attributes
|
|
21
|
+
----------
|
|
22
|
+
rotate: bool
|
|
23
|
+
If True, rotate local wind vector at evaluation points.
|
|
24
|
+
If False, multiply wind speed with cos(angle) instead.
|
|
25
|
+
If None, do not modify the wind vector, only the path.
|
|
26
|
+
beta: float
|
|
27
|
+
The beta coefficient of the Jimenez model
|
|
28
|
+
step_x: float
|
|
29
|
+
The x step in m for integration
|
|
30
|
+
|
|
31
|
+
:group: models.wake_deflections
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, rotate=True, beta=0.1, step_x=10.0):
|
|
36
|
+
"""
|
|
37
|
+
Constructor.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
rotate: bool, optional
|
|
42
|
+
If True, rotate local wind vector at evaluation points.
|
|
43
|
+
If False, multiply wind speed with cos(angle) instead.
|
|
44
|
+
If None, do not modify the wind vector, only the path.
|
|
45
|
+
beta: float
|
|
46
|
+
The beta coefficient of the Jimenez model
|
|
47
|
+
step_x: float
|
|
48
|
+
The x step in m for integration
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
super().__init__()
|
|
52
|
+
self.rotate = rotate
|
|
53
|
+
self.beta = beta
|
|
54
|
+
self.step_x = step_x
|
|
55
|
+
|
|
56
|
+
def __repr__(self):
|
|
57
|
+
s = f"{type(self).__name__}("
|
|
58
|
+
s += f"rotate={self.rotate}, beta={self.beta}, step_x={self.step_x}"
|
|
59
|
+
s += ")"
|
|
60
|
+
return s
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def has_uv(self):
|
|
64
|
+
"""
|
|
65
|
+
This model uses wind vector data
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
hasuv: bool
|
|
70
|
+
Flag for wind vector data
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
return self.rotate is not None and self.rotate
|
|
74
|
+
|
|
75
|
+
def calc_deflection(
|
|
76
|
+
self,
|
|
77
|
+
algo,
|
|
78
|
+
mdata,
|
|
79
|
+
fdata,
|
|
80
|
+
tdata,
|
|
81
|
+
downwind_index,
|
|
82
|
+
coos,
|
|
83
|
+
):
|
|
84
|
+
"""
|
|
85
|
+
Calculates the wake deflection.
|
|
86
|
+
|
|
87
|
+
This function optionally adds FC.WDEFL_ROT_ANGLE or
|
|
88
|
+
FC.WDEFL_DWS_FACTOR to the tdata.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
algo: foxes.core.Algorithm
|
|
93
|
+
The calculation algorithm
|
|
94
|
+
mdata: foxes.core.MData
|
|
95
|
+
The model data
|
|
96
|
+
fdata: foxes.core.FData
|
|
97
|
+
The farm data
|
|
98
|
+
tdata: foxes.core.TData
|
|
99
|
+
The target point data
|
|
100
|
+
downwind_index: int
|
|
101
|
+
The index of the wake causing turbine
|
|
102
|
+
in the downwind order
|
|
103
|
+
coos: numpy.ndarray
|
|
104
|
+
The wake frame coordinates of the evaluation
|
|
105
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
coos: numpy.ndarray
|
|
110
|
+
The wake frame coordinates of the evaluation
|
|
111
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
if FV.YAWM not in fdata:
|
|
116
|
+
return coos
|
|
117
|
+
|
|
118
|
+
# take rotor average:
|
|
119
|
+
xyz = np.einsum("stpd,p->std", coos, tdata[FC.TWEIGHTS])
|
|
120
|
+
x = xyz[:, :, 0]
|
|
121
|
+
y = xyz[:, :, 1]
|
|
122
|
+
z = xyz[:, :, 2]
|
|
123
|
+
|
|
124
|
+
# get ct:
|
|
125
|
+
ct = self.get_data(
|
|
126
|
+
FV.CT,
|
|
127
|
+
FC.STATE_TARGET,
|
|
128
|
+
lookup="w",
|
|
129
|
+
algo=algo,
|
|
130
|
+
fdata=fdata,
|
|
131
|
+
tdata=tdata,
|
|
132
|
+
downwind_index=downwind_index,
|
|
133
|
+
upcast=True,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# get gamma:
|
|
137
|
+
gamma = self.get_data(
|
|
138
|
+
FV.YAWM,
|
|
139
|
+
FC.STATE_TARGET,
|
|
140
|
+
lookup="w",
|
|
141
|
+
algo=algo,
|
|
142
|
+
fdata=fdata,
|
|
143
|
+
tdata=tdata,
|
|
144
|
+
downwind_index=downwind_index,
|
|
145
|
+
upcast=True,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
sel = (x > 1e-8) & (x < 1e10) & (ct > 1e-8) & (np.abs(gamma) > 1e-8)
|
|
149
|
+
delwd = np.zeros_like(coos[..., 0])
|
|
150
|
+
n_sel = np.sum(sel)
|
|
151
|
+
if n_sel > 0:
|
|
152
|
+
# apply selection:
|
|
153
|
+
gamma = np.deg2rad(gamma[sel])
|
|
154
|
+
ct = ct[sel]
|
|
155
|
+
x = x[sel]
|
|
156
|
+
|
|
157
|
+
# get rotor diameter:
|
|
158
|
+
D = self.get_data(
|
|
159
|
+
FV.D,
|
|
160
|
+
FC.STATE_TARGET,
|
|
161
|
+
lookup="w",
|
|
162
|
+
algo=algo,
|
|
163
|
+
fdata=fdata,
|
|
164
|
+
tdata=tdata,
|
|
165
|
+
downwind_index=downwind_index,
|
|
166
|
+
upcast=True,
|
|
167
|
+
selection=sel,
|
|
168
|
+
)[:, None]
|
|
169
|
+
|
|
170
|
+
# define x path:
|
|
171
|
+
xmax = np.max(x)
|
|
172
|
+
n_x = int(xmax / self.step_x)
|
|
173
|
+
if xmax > n_x * self.step_x:
|
|
174
|
+
n_x += 1
|
|
175
|
+
delx = np.arange(n_x + 1) * self.step_x
|
|
176
|
+
delx = np.minimum(delx[None, :], x[:, None])
|
|
177
|
+
dx = delx[:, 1:] - delx[:, :-1]
|
|
178
|
+
delx = delx[:, :-1]
|
|
179
|
+
|
|
180
|
+
# integrate deflection of y along the x path:
|
|
181
|
+
alpha0 = (
|
|
182
|
+
-(np.cos(gamma[:, None]) ** 2)
|
|
183
|
+
* np.sin(gamma[:, None])
|
|
184
|
+
* ct[:, None]
|
|
185
|
+
/ 2
|
|
186
|
+
)
|
|
187
|
+
y[sel] += np.sum(
|
|
188
|
+
np.tan(alpha0 / (1 + self.beta * delx / D) ** 2) * dx, axis=-1
|
|
189
|
+
)
|
|
190
|
+
del delx, dx
|
|
191
|
+
coos[..., 1] = y[:, :, None]
|
|
192
|
+
|
|
193
|
+
# calculate wind vector modification at evaluation points:
|
|
194
|
+
if self.rotate is not None:
|
|
195
|
+
# delta wd at evaluation points, if within wake radius:
|
|
196
|
+
r2 = (y[sel, None] ** 2 + z[sel, None] ** 2) / D**2
|
|
197
|
+
WD2 = (1 + self.beta * x[:, None] / D) ** 2
|
|
198
|
+
delwd[sel] = np.where(r2 <= WD2 / 4, alpha0 / WD2, 0)
|
|
199
|
+
|
|
200
|
+
if self.rotate:
|
|
201
|
+
tdata[FC.WDEFL_ROT_ANGLE] = np.rad2deg(delwd)
|
|
202
|
+
else:
|
|
203
|
+
tdata[FC.WDEFL_DWS_FACTOR] = np.cos(delwd)
|
|
204
|
+
|
|
205
|
+
return coos
|
|
206
|
+
|
|
207
|
+
def get_yaw_alpha_seq(
|
|
208
|
+
self,
|
|
209
|
+
algo,
|
|
210
|
+
mdata,
|
|
211
|
+
fdata,
|
|
212
|
+
tdata,
|
|
213
|
+
downwind_index,
|
|
214
|
+
x,
|
|
215
|
+
):
|
|
216
|
+
"""
|
|
217
|
+
Computes sequential wind vector rotation angles.
|
|
218
|
+
|
|
219
|
+
Wind vector rotation angles are computed at the
|
|
220
|
+
current trace points due to a yawed rotor
|
|
221
|
+
for sequential runs.
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
algo: foxes.core.Algorithm
|
|
226
|
+
The calculation algorithm
|
|
227
|
+
mdata: foxes.core.MData
|
|
228
|
+
The model data
|
|
229
|
+
fdata: foxes.core.FData
|
|
230
|
+
The farm data
|
|
231
|
+
tdata: foxes.core.TData
|
|
232
|
+
The target point data
|
|
233
|
+
downwind_index: int
|
|
234
|
+
The index of the wake causing turbine
|
|
235
|
+
in the downwind order
|
|
236
|
+
x: numpy.ndarray
|
|
237
|
+
The distance from the wake causing rotor
|
|
238
|
+
for the first n_times subsequent time steps,
|
|
239
|
+
shape: (n_times,)
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
alpha: numpy.ndarray
|
|
244
|
+
The delta WD result at the x locations,
|
|
245
|
+
shape: (n_times,)
|
|
246
|
+
|
|
247
|
+
"""
|
|
248
|
+
assert isinstance(algo, Sequential), (
|
|
249
|
+
f"Wake deflection '{self.name}' requires Sequential algorithm, got '{type(algo).__name__}'"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
n_times = len(x)
|
|
253
|
+
|
|
254
|
+
def _get_data(var):
|
|
255
|
+
data = algo.farm_results_downwind[var].to_numpy()[:n_times, downwind_index]
|
|
256
|
+
data[-1] = fdata[var][0, downwind_index]
|
|
257
|
+
return data
|
|
258
|
+
|
|
259
|
+
gamma = _get_data(FV.YAWM)
|
|
260
|
+
ct = _get_data(FV.CT)
|
|
261
|
+
alpha = np.zeros_like(gamma)
|
|
262
|
+
|
|
263
|
+
sel = (ct > 1e-8) & (np.abs(gamma) > 1e-8)
|
|
264
|
+
if np.any(sel):
|
|
265
|
+
D = _get_data(FV.D)[sel]
|
|
266
|
+
gamma = np.deg2rad(gamma[sel])
|
|
267
|
+
ct = ct[sel]
|
|
268
|
+
|
|
269
|
+
alpha[sel] = np.rad2deg(
|
|
270
|
+
-(np.cos(gamma) ** 2)
|
|
271
|
+
* np.sin(gamma)
|
|
272
|
+
* ct
|
|
273
|
+
/ 2
|
|
274
|
+
/ (1 + self.beta * x / D) ** 2
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
return alpha
|