foxes 1.2.5__py3-none-any.whl → 1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of foxes might be problematic. Click here for more details.
- docs/source/conf.py +3 -3
- examples/abl_states/run.py +2 -2
- examples/compare_rotors_pwakes/run.py +1 -1
- examples/compare_wakes/run.py +1 -2
- examples/dyn_wakes/run.py +29 -6
- examples/induction/run.py +3 -3
- examples/multi_height/run.py +1 -1
- examples/power_mask/run.py +2 -2
- examples/quickstart/run.py +16 -0
- examples/random_timeseries/run.py +3 -4
- examples/scan_row/run.py +3 -3
- examples/sequential/run.py +33 -10
- examples/single_state/run.py +3 -4
- examples/states_lookup_table/run.py +3 -3
- examples/streamline_wakes/run.py +27 -4
- examples/tab_file/run.py +3 -3
- examples/timelines/run.py +29 -5
- examples/timeseries/run.py +3 -3
- examples/timeseries_slurm/run.py +3 -3
- examples/wind_rose/run.py +3 -3
- examples/yawed_wake/run.py +16 -8
- foxes/__init__.py +22 -18
- foxes/algorithms/__init__.py +6 -6
- foxes/algorithms/downwind/__init__.py +2 -2
- foxes/algorithms/downwind/downwind.py +53 -27
- foxes/algorithms/downwind/models/__init__.py +6 -6
- foxes/algorithms/downwind/models/farm_wakes_calc.py +22 -14
- foxes/algorithms/downwind/models/init_farm_data.py +4 -5
- foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
- foxes/algorithms/downwind/models/reorder_farm_output.py +5 -1
- foxes/algorithms/downwind/models/set_amb_point_results.py +7 -7
- foxes/algorithms/iterative/__init__.py +7 -3
- foxes/algorithms/iterative/iterative.py +1 -2
- foxes/algorithms/iterative/models/__init__.py +7 -3
- foxes/algorithms/iterative/models/farm_wakes_calc.py +15 -8
- foxes/algorithms/sequential/__init__.py +3 -3
- foxes/algorithms/sequential/models/__init__.py +2 -2
- foxes/algorithms/sequential/models/seq_state.py +0 -18
- foxes/algorithms/sequential/sequential.py +8 -22
- foxes/config/__init__.py +5 -1
- foxes/constants.py +22 -0
- foxes/core/__init__.py +45 -22
- foxes/core/algorithm.py +0 -1
- foxes/core/data.py +56 -29
- foxes/core/engine.py +28 -14
- foxes/core/farm_controller.py +2 -2
- foxes/core/farm_data_model.py +1 -0
- foxes/core/ground_model.py +4 -13
- foxes/core/model.py +5 -5
- foxes/core/partial_wakes_model.py +147 -10
- foxes/core/point_data_model.py +2 -3
- foxes/core/rotor_model.py +42 -38
- foxes/core/states.py +4 -50
- foxes/core/turbine.py +2 -1
- foxes/core/wake_deflection.py +130 -0
- foxes/core/wake_model.py +222 -9
- foxes/core/wake_superposition.py +122 -4
- foxes/core/wind_farm.py +6 -6
- foxes/data/__init__.py +7 -2
- foxes/data/states/weibull_sectors_12.csv +13 -0
- foxes/data/states/weibull_sectors_12.nc +0 -0
- foxes/engines/__init__.py +14 -15
- foxes/engines/dask.py +39 -14
- foxes/engines/numpy.py +0 -3
- foxes/input/__init__.py +3 -3
- foxes/input/farm_layout/__init__.py +8 -8
- foxes/input/farm_layout/from_csv.py +1 -1
- foxes/input/farm_layout/ring.py +0 -1
- foxes/input/states/__init__.py +22 -11
- foxes/input/states/create/__init__.py +3 -2
- foxes/input/states/field_data_nc.py +48 -84
- foxes/input/states/multi_height.py +40 -60
- foxes/input/states/one_point_flow.py +22 -25
- foxes/input/states/scan.py +6 -19
- foxes/input/states/single.py +6 -18
- foxes/input/states/states_table.py +25 -44
- foxes/input/states/weibull_sectors.py +225 -0
- foxes/input/states/wrg_states.py +151 -37
- foxes/input/yaml/__init__.py +9 -3
- foxes/input/yaml/dict.py +19 -19
- foxes/input/yaml/windio/__init__.py +10 -5
- foxes/input/yaml/windio/read_attributes.py +2 -2
- foxes/input/yaml/windio/read_farm.py +5 -5
- foxes/input/yaml/windio/read_fields.py +4 -2
- foxes/input/yaml/windio/read_site.py +52 -0
- foxes/input/yaml/windio/windio.py +1 -1
- foxes/models/__init__.py +15 -14
- foxes/models/axial_induction/__init__.py +2 -2
- foxes/models/farm_controllers/__init__.py +1 -1
- foxes/models/farm_models/__init__.py +1 -1
- foxes/models/ground_models/__init__.py +3 -2
- foxes/models/ground_models/wake_mirror.py +3 -3
- foxes/models/model_book.py +175 -49
- foxes/models/partial_wakes/__init__.py +6 -6
- foxes/models/partial_wakes/axiwake.py +30 -5
- foxes/models/partial_wakes/centre.py +47 -0
- foxes/models/partial_wakes/rotor_points.py +45 -9
- foxes/models/partial_wakes/segregated.py +2 -20
- foxes/models/partial_wakes/top_hat.py +27 -2
- foxes/models/point_models/__init__.py +4 -4
- foxes/models/rotor_models/__init__.py +3 -3
- foxes/models/rotor_models/centre.py +6 -4
- foxes/models/turbine_models/__init__.py +11 -11
- foxes/models/turbine_models/set_farm_vars.py +0 -1
- foxes/models/turbine_types/PCt_file.py +0 -2
- foxes/models/turbine_types/PCt_from_two.py +0 -2
- foxes/models/turbine_types/__init__.py +9 -9
- foxes/models/vertical_profiles/__init__.py +7 -7
- foxes/models/wake_deflections/__init__.py +3 -0
- foxes/models/{wake_frames/yawed_wakes.py → wake_deflections/bastankhah2016.py} +32 -111
- foxes/models/wake_deflections/jimenez.py +277 -0
- foxes/models/wake_deflections/no_deflection.py +94 -0
- foxes/models/wake_frames/__init__.py +6 -7
- foxes/models/wake_frames/dynamic_wakes.py +12 -3
- foxes/models/wake_frames/rotor_wd.py +3 -1
- foxes/models/wake_frames/seq_dynamic_wakes.py +45 -8
- foxes/models/wake_frames/streamlines.py +8 -6
- foxes/models/wake_frames/timelines.py +19 -3
- foxes/models/wake_models/__init__.py +7 -7
- foxes/models/wake_models/dist_sliced.py +50 -84
- foxes/models/wake_models/gaussian.py +20 -0
- foxes/models/wake_models/induction/__init__.py +5 -5
- foxes/models/wake_models/induction/rankine_half_body.py +30 -71
- foxes/models/wake_models/induction/rathmann.py +65 -64
- foxes/models/wake_models/induction/self_similar.py +65 -68
- foxes/models/wake_models/induction/self_similar2020.py +0 -3
- foxes/models/wake_models/induction/vortex_sheet.py +71 -75
- foxes/models/wake_models/ti/__init__.py +2 -2
- foxes/models/wake_models/ti/crespo_hernandez.py +5 -3
- foxes/models/wake_models/ti/iec_ti.py +6 -4
- foxes/models/wake_models/top_hat.py +58 -7
- foxes/models/wake_models/wind/__init__.py +6 -4
- foxes/models/wake_models/wind/bastankhah14.py +25 -7
- foxes/models/wake_models/wind/bastankhah16.py +35 -3
- foxes/models/wake_models/wind/jensen.py +15 -2
- foxes/models/wake_models/wind/turbopark.py +28 -2
- foxes/models/wake_superpositions/__init__.py +18 -9
- foxes/models/wake_superpositions/ti_linear.py +4 -4
- foxes/models/wake_superpositions/ti_max.py +4 -4
- foxes/models/wake_superpositions/ti_pow.py +4 -4
- foxes/models/wake_superpositions/ti_quadratic.py +4 -4
- foxes/models/wake_superpositions/wind_vector.py +257 -0
- foxes/models/wake_superpositions/ws_linear.py +9 -10
- foxes/models/wake_superpositions/ws_max.py +8 -8
- foxes/models/wake_superpositions/ws_pow.py +8 -8
- foxes/models/wake_superpositions/ws_product.py +4 -4
- foxes/models/wake_superpositions/ws_quadratic.py +8 -8
- foxes/output/__init__.py +21 -19
- foxes/output/farm_layout.py +14 -6
- foxes/output/farm_results_eval.py +51 -27
- foxes/output/flow_plots_2d/__init__.py +2 -2
- foxes/output/flow_plots_2d/get_fig.py +4 -2
- foxes/output/rose_plot.py +23 -5
- foxes/output/seq_plugins/__init__.py +2 -2
- foxes/output/seq_plugins/seq_flow_ani_plugin.py +0 -3
- foxes/output/seq_plugins/seq_wake_debug_plugin.py +0 -1
- foxes/output/slice_data.py +16 -19
- foxes/output/turbine_type_curves.py +7 -8
- foxes/utils/__init__.py +37 -19
- foxes/utils/abl/__init__.py +4 -4
- foxes/utils/cubic_roots.py +1 -1
- foxes/utils/data_book.py +4 -3
- foxes/utils/dict.py +3 -3
- foxes/utils/exec_python.py +5 -5
- foxes/utils/factory.py +1 -3
- foxes/utils/geom2d/__init__.py +7 -5
- foxes/utils/geopandas_utils.py +2 -2
- foxes/utils/pandas_utils.py +4 -3
- foxes/utils/tab_files.py +0 -1
- foxes/utils/weibull.py +28 -0
- foxes/utils/wrg_utils.py +3 -1
- foxes/utils/xarray_utils.py +9 -2
- foxes/variables.py +67 -9
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/METADATA +14 -21
- foxes-1.4.dist-info/RECORD +320 -0
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/WHEEL +1 -1
- tests/0_consistency/iterative/test_iterative.py +2 -3
- tests/0_consistency/partial_wakes/test_partial_wakes.py +2 -2
- tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +2 -3
- tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +48 -56
- tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
- tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +33 -36
- tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +0 -1
- tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +0 -2
- tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +3 -3
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +3 -4
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +3 -4
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +3 -4
- tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +3 -4
- tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
- tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +3 -3
- tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +3 -3
- tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +0 -1
- tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +3 -4
- tests/3_examples/test_examples.py +3 -2
- foxes/output/round.py +0 -10
- foxes/utils/pandas_helpers.py +0 -178
- foxes-1.2.5.dist-info/RECORD +0 -312
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/entry_points.txt +0 -0
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info/licenses}/LICENSE +0 -0
- {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from foxes.core.wake_deflection import WakeDeflection
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NoDeflection(WakeDeflection):
|
|
5
|
+
"""
|
|
6
|
+
Switch of wake deflection
|
|
7
|
+
|
|
8
|
+
:group: models.wake_deflections
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def calc_deflection(
|
|
13
|
+
self,
|
|
14
|
+
algo,
|
|
15
|
+
mdata,
|
|
16
|
+
fdata,
|
|
17
|
+
tdata,
|
|
18
|
+
downwind_index,
|
|
19
|
+
coos,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Calculates the wake deflection.
|
|
23
|
+
|
|
24
|
+
This function optionally adds FC.WDEFL_ROT_ANGLE or
|
|
25
|
+
FC.WDEFL_DWS_FACTOR to the tdata.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
algo: foxes.core.Algorithm
|
|
30
|
+
The calculation algorithm
|
|
31
|
+
mdata: foxes.core.MData
|
|
32
|
+
The model data
|
|
33
|
+
fdata: foxes.core.FData
|
|
34
|
+
The farm data
|
|
35
|
+
tdata: foxes.core.TData
|
|
36
|
+
The target point data
|
|
37
|
+
downwind_index: int
|
|
38
|
+
The index of the wake causing turbine
|
|
39
|
+
in the downwind order
|
|
40
|
+
coos: numpy.ndarray
|
|
41
|
+
The wake frame coordinates of the evaluation
|
|
42
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
coos: numpy.ndarray
|
|
47
|
+
The wake frame coordinates of the evaluation
|
|
48
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
return coos
|
|
52
|
+
|
|
53
|
+
def get_yaw_alpha_seq(
|
|
54
|
+
self,
|
|
55
|
+
algo,
|
|
56
|
+
mdata,
|
|
57
|
+
fdata,
|
|
58
|
+
tdata,
|
|
59
|
+
downwind_index,
|
|
60
|
+
x,
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Computes sequential wind vector rotation angles.
|
|
64
|
+
|
|
65
|
+
Wind vector rotation angles are computed at the
|
|
66
|
+
current trace points due to a yawed rotor
|
|
67
|
+
for sequential runs.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
algo: foxes.core.Algorithm
|
|
72
|
+
The calculation algorithm
|
|
73
|
+
mdata: foxes.core.MData
|
|
74
|
+
The model data
|
|
75
|
+
fdata: foxes.core.FData
|
|
76
|
+
The farm data
|
|
77
|
+
tdata: foxes.core.TData
|
|
78
|
+
The target point data
|
|
79
|
+
downwind_index: int
|
|
80
|
+
The index of the wake causing turbine
|
|
81
|
+
in the downwind order
|
|
82
|
+
x: numpy.ndarray
|
|
83
|
+
The distance from the wake causing rotor
|
|
84
|
+
for the first n_times subsequent time steps,
|
|
85
|
+
shape: (n_times,)
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
alpha: numpy.ndarray
|
|
90
|
+
The delta WD result at the x locations,
|
|
91
|
+
shape: (n_times,)
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
return None
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
Wake frame models.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from .rotor_wd import RotorWD
|
|
6
|
-
from .streamlines import Streamlines2D
|
|
7
|
-
from .timelines import Timelines
|
|
8
|
-
from .
|
|
9
|
-
from .
|
|
10
|
-
from .
|
|
11
|
-
from .dynamic_wakes import DynamicWakes
|
|
5
|
+
from .rotor_wd import RotorWD as RotorWD
|
|
6
|
+
from .streamlines import Streamlines2D as Streamlines2D
|
|
7
|
+
from .timelines import Timelines as Timelines
|
|
8
|
+
from .farm_order import FarmOrder as FarmOrder
|
|
9
|
+
from .seq_dynamic_wakes import SeqDynamicWakes as SeqDynamicWakes
|
|
10
|
+
from .dynamic_wakes import DynamicWakes as DynamicWakes
|
|
@@ -161,7 +161,10 @@ class DynamicWakes(WakeFrame):
|
|
|
161
161
|
for v in algo.states.output_point_vars(algo)
|
|
162
162
|
}
|
|
163
163
|
key = f"{self.DATA}_{downwind_index}"
|
|
164
|
-
|
|
164
|
+
|
|
165
|
+
def ukey_fun(fr, to):
|
|
166
|
+
"""helper function to create update key"""
|
|
167
|
+
return f"{self.UPDATE}_dw{downwind_index}_from_{fr}_to_{to}"
|
|
165
168
|
|
|
166
169
|
# compute wakes that start within this chunk: x, y, z, length
|
|
167
170
|
data = algo.get_from_chunk_store(name=key, mdata=mdata, error=False)
|
|
@@ -274,7 +277,6 @@ class DynamicWakes(WakeFrame):
|
|
|
274
277
|
# compute single state wake propagation:
|
|
275
278
|
isnan0 = np.isnan(hdata)
|
|
276
279
|
for si in range(n_states):
|
|
277
|
-
|
|
278
280
|
s = slice(si, si + 1, None)
|
|
279
281
|
hmdata = mdata.get_slice(FC.STATE, s)
|
|
280
282
|
hfdata = fdata.get_slice(FC.STATE, s)
|
|
@@ -429,4 +431,11 @@ class DynamicWakes(WakeFrame):
|
|
|
429
431
|
(FC.STATE, FC.TARGET, FC.TPOINT),
|
|
430
432
|
)
|
|
431
433
|
|
|
432
|
-
return
|
|
434
|
+
return algo.wake_deflection.calc_deflection(
|
|
435
|
+
algo,
|
|
436
|
+
mdata,
|
|
437
|
+
fdata,
|
|
438
|
+
tdata,
|
|
439
|
+
downwind_index,
|
|
440
|
+
wcoos.reshape(n_states, n_targets, n_tpoints, 3),
|
|
441
|
+
)
|
|
@@ -116,7 +116,9 @@ class RotorWD(WakeFrame):
|
|
|
116
116
|
|
|
117
117
|
coos = np.einsum("stpd,sad->stpa", delta, nax)
|
|
118
118
|
|
|
119
|
-
return
|
|
119
|
+
return algo.wake_deflection.calc_deflection(
|
|
120
|
+
algo, mdata, fdata, tdata, downwind_index, coos
|
|
121
|
+
)
|
|
120
122
|
|
|
121
123
|
def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
|
|
122
124
|
"""
|
|
@@ -23,12 +23,14 @@ class SeqDynamicWakes(FarmOrder):
|
|
|
23
23
|
dt_min: float, optional
|
|
24
24
|
The delta t value in minutes,
|
|
25
25
|
if not from timeseries data
|
|
26
|
+
induction: foxes.core.AxialInductionModel
|
|
27
|
+
The induction model
|
|
26
28
|
|
|
27
29
|
:group: models.wake_frames.sequential
|
|
28
30
|
|
|
29
31
|
"""
|
|
30
32
|
|
|
31
|
-
def __init__(self, cl_ipars={}, dt_min=None, **kwargs):
|
|
33
|
+
def __init__(self, cl_ipars={}, dt_min=None, induction="Madsen", **kwargs):
|
|
32
34
|
"""
|
|
33
35
|
Constructor.
|
|
34
36
|
|
|
@@ -40,6 +42,8 @@ class SeqDynamicWakes(FarmOrder):
|
|
|
40
42
|
dt_min: float, optional
|
|
41
43
|
The delta t value in minutes,
|
|
42
44
|
if not from timeseries data
|
|
45
|
+
induction: foxes.core.AxialInductionModel or str
|
|
46
|
+
The induction model
|
|
43
47
|
kwargs: dict, optional
|
|
44
48
|
Additional parameters for the base class
|
|
45
49
|
|
|
@@ -47,9 +51,25 @@ class SeqDynamicWakes(FarmOrder):
|
|
|
47
51
|
super().__init__(**kwargs)
|
|
48
52
|
self.cl_ipars = cl_ipars
|
|
49
53
|
self.dt_min = dt_min
|
|
54
|
+
self.induction = induction
|
|
50
55
|
|
|
51
56
|
def __repr__(self):
|
|
52
|
-
|
|
57
|
+
iname = (
|
|
58
|
+
self.induction if isinstance(self.induction, str) else self.induction.name
|
|
59
|
+
)
|
|
60
|
+
return f"{type(self).__name__}(dt_min={self.dt_min}, induction={iname})"
|
|
61
|
+
|
|
62
|
+
def sub_models(self):
|
|
63
|
+
"""
|
|
64
|
+
List of all sub-models
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
smdls: list of foxes.core.Model
|
|
69
|
+
All sub models
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
return [self.induction]
|
|
53
73
|
|
|
54
74
|
def initialize(self, algo, verbosity=0):
|
|
55
75
|
"""
|
|
@@ -63,7 +83,11 @@ class SeqDynamicWakes(FarmOrder):
|
|
|
63
83
|
The verbosity level, 0 = silent
|
|
64
84
|
|
|
65
85
|
"""
|
|
86
|
+
if isinstance(self.induction, str):
|
|
87
|
+
self.induction = algo.mbook.axial_induction[self.induction]
|
|
88
|
+
|
|
66
89
|
super().initialize(algo, verbosity)
|
|
90
|
+
|
|
67
91
|
if not isinstance(algo, Sequential):
|
|
68
92
|
raise TypeError(
|
|
69
93
|
f"Incompatible algorithm type {type(algo).__name__}, expecting {Sequential.__name__}"
|
|
@@ -168,7 +192,6 @@ class SeqDynamicWakes(FarmOrder):
|
|
|
168
192
|
N = counter + 1
|
|
169
193
|
|
|
170
194
|
if np.isnan(self._traces_l[counter, downwind_index]):
|
|
171
|
-
|
|
172
195
|
# new wake starts at turbine:
|
|
173
196
|
self._traces_p[counter, downwind_index][:] = fdata[FV.TXYH][
|
|
174
197
|
0, downwind_index
|
|
@@ -186,12 +209,26 @@ class SeqDynamicWakes(FarmOrder):
|
|
|
186
209
|
|
|
187
210
|
# compute wind vectors at wake traces:
|
|
188
211
|
# TODO: dz from U_z is missing here
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
res[FV.WD][0, :, 0], res[FV.WS][0, :, 0]
|
|
212
|
+
svrs = algo.states.output_point_vars(algo)
|
|
213
|
+
hpdata = TData.from_points(
|
|
214
|
+
points=self._traces_p[None, :N, downwind_index], variables=svrs
|
|
193
215
|
)
|
|
194
|
-
|
|
216
|
+
res = algo.states.calculate(algo, mdata, fdata, hpdata)
|
|
217
|
+
wd = res[FV.WD][0, :, 0]
|
|
218
|
+
if FV.YAWM in fdata:
|
|
219
|
+
wddef = algo.wake_deflection.get_yaw_alpha_seq(
|
|
220
|
+
algo,
|
|
221
|
+
mdata,
|
|
222
|
+
fdata,
|
|
223
|
+
hpdata,
|
|
224
|
+
downwind_index,
|
|
225
|
+
self._traces_l[:N, downwind_index],
|
|
226
|
+
)
|
|
227
|
+
if wddef is not None:
|
|
228
|
+
wd += wddef
|
|
229
|
+
del wddef
|
|
230
|
+
self._traces_v[:N, downwind_index, :2] = wd2uv(wd, res[FV.WS][0, :, 0])
|
|
231
|
+
del hpdata, res, svrs, wd
|
|
195
232
|
|
|
196
233
|
# find nearest wake point:
|
|
197
234
|
dists = cdist(points[0], self._traces_p[:N, downwind_index])
|
|
@@ -67,7 +67,6 @@ class Streamlines2D(WakeFrame):
|
|
|
67
67
|
# calc data: x, y, z, wd
|
|
68
68
|
data = np.zeros((n_states, n_turbines, N, 4), dtype=config.dtype_double)
|
|
69
69
|
for i in range(N):
|
|
70
|
-
|
|
71
70
|
# set streamline start point data (rotor centre):
|
|
72
71
|
if i == 0:
|
|
73
72
|
data[:, :, i, :3] = fdata[FV.TXYH]
|
|
@@ -75,7 +74,6 @@ class Streamlines2D(WakeFrame):
|
|
|
75
74
|
|
|
76
75
|
# compute next step:
|
|
77
76
|
else:
|
|
78
|
-
|
|
79
77
|
# calculate next point:
|
|
80
78
|
xyz = data[:, :, i - 1, :3]
|
|
81
79
|
n = wd2uv(data[:, :, i - 1, 3])
|
|
@@ -198,9 +196,9 @@ class Streamlines2D(WakeFrame):
|
|
|
198
196
|
# n_states, n_turbines_source, n_turbines_target
|
|
199
197
|
coosx = np.zeros((n_states, n_turbines, n_turbines), dtype=config.dtype_double)
|
|
200
198
|
for ti in range(n_turbines):
|
|
201
|
-
coosx[:, ti, :] = self.
|
|
202
|
-
|
|
203
|
-
]
|
|
199
|
+
coosx[:, ti, :] = self._calc_coos(
|
|
200
|
+
algo, mdata, fdata, tdata[FC.TARGETS], ti
|
|
201
|
+
)[:, :, 0, 0]
|
|
204
202
|
|
|
205
203
|
# derive turbine order:
|
|
206
204
|
# TODO: Remove loop over states
|
|
@@ -242,7 +240,11 @@ class Streamlines2D(WakeFrame):
|
|
|
242
240
|
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
243
241
|
|
|
244
242
|
"""
|
|
245
|
-
|
|
243
|
+
coos = self._calc_coos(algo, mdata, fdata, tdata[FC.TARGETS], downwind_index)
|
|
244
|
+
|
|
245
|
+
return algo.wake_deflection.calc_deflection(
|
|
246
|
+
algo, mdata, fdata, tdata, downwind_index, coos
|
|
247
|
+
)
|
|
246
248
|
|
|
247
249
|
def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
|
|
248
250
|
"""
|
|
@@ -92,7 +92,7 @@ class Timelines(WakeFrame):
|
|
|
92
92
|
del mdict, mdims, data
|
|
93
93
|
|
|
94
94
|
# prepare fdata:
|
|
95
|
-
fdata = FData(
|
|
95
|
+
fdata = FData()
|
|
96
96
|
|
|
97
97
|
# prepare tdata:
|
|
98
98
|
n_states = states.size()
|
|
@@ -105,8 +105,8 @@ class Timelines(WakeFrame):
|
|
|
105
105
|
|
|
106
106
|
# calculate all heights:
|
|
107
107
|
self.timelines_data = {"dxy": (("height", FC.STATE, "dir"), [])}
|
|
108
|
+
weight_data = None
|
|
108
109
|
for h in heights:
|
|
109
|
-
|
|
110
110
|
if verbosity > 0:
|
|
111
111
|
print(f" Height: {h} m")
|
|
112
112
|
|
|
@@ -118,6 +118,13 @@ class Timelines(WakeFrame):
|
|
|
118
118
|
)
|
|
119
119
|
|
|
120
120
|
res = states.calculate(algo, mdata, fdata, tdata)
|
|
121
|
+
|
|
122
|
+
if weight_data is None:
|
|
123
|
+
weight_data = ((FC.STATE,), tdata[FV.WEIGHT][:, 0, 0])
|
|
124
|
+
elif not np.all(tdata[FV.WEIGHT] == weight_data[1]):
|
|
125
|
+
raise AssertionError(
|
|
126
|
+
f"States '{self.name}': weight data mismatch between heights"
|
|
127
|
+
)
|
|
121
128
|
del tdata
|
|
122
129
|
|
|
123
130
|
uv = wd2uv(res[FV.WD], res[FV.WS])[:, 0, 0, :2]
|
|
@@ -163,6 +170,8 @@ class Timelines(WakeFrame):
|
|
|
163
170
|
},
|
|
164
171
|
)
|
|
165
172
|
|
|
173
|
+
return weight_data
|
|
174
|
+
|
|
166
175
|
def initialize(self, algo, verbosity=0):
|
|
167
176
|
"""
|
|
168
177
|
Initializes the model.
|
|
@@ -420,7 +429,14 @@ class Timelines(WakeFrame):
|
|
|
420
429
|
(FC.STATE, FC.TARGET, FC.TPOINT),
|
|
421
430
|
)
|
|
422
431
|
|
|
423
|
-
return
|
|
432
|
+
return algo.wake_deflection.calc_deflection(
|
|
433
|
+
algo,
|
|
434
|
+
mdata,
|
|
435
|
+
fdata,
|
|
436
|
+
tdata,
|
|
437
|
+
downwind_index,
|
|
438
|
+
wcoos.reshape(n_states, n_targets, n_tpoints, 3),
|
|
439
|
+
)
|
|
424
440
|
|
|
425
441
|
def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
|
|
426
442
|
"""
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Wake models.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from .dist_sliced import DistSlicedWakeModel
|
|
6
|
-
from .axisymmetric import AxisymmetricWakeModel
|
|
7
|
-
from .top_hat import TopHatWakeModel
|
|
8
|
-
from .gaussian import GaussianWakeModel
|
|
5
|
+
from .dist_sliced import DistSlicedWakeModel as DistSlicedWakeModel
|
|
6
|
+
from .axisymmetric import AxisymmetricWakeModel as AxisymmetricWakeModel
|
|
7
|
+
from .top_hat import TopHatWakeModel as TopHatWakeModel
|
|
8
|
+
from .gaussian import GaussianWakeModel as GaussianWakeModel
|
|
9
9
|
|
|
10
|
-
from . import wind
|
|
11
|
-
from . import ti
|
|
12
|
-
from . import induction
|
|
10
|
+
from . import wind as wind
|
|
11
|
+
from . import ti as ti
|
|
12
|
+
from . import induction as induction
|
|
@@ -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
|