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
foxes/core/model.py
CHANGED
|
@@ -77,7 +77,7 @@ class Model(ABC):
|
|
|
77
77
|
|
|
78
78
|
Returns
|
|
79
79
|
-------
|
|
80
|
-
bool
|
|
80
|
+
bool:
|
|
81
81
|
True if the model has been initialized.
|
|
82
82
|
|
|
83
83
|
"""
|
|
@@ -424,8 +424,8 @@ class Model(ABC):
|
|
|
424
424
|
# lookup mdata:
|
|
425
425
|
elif s == "m" and mdata is not None and variable in mdata:
|
|
426
426
|
a, d = _filter_dims(mdata)
|
|
427
|
-
|
|
428
|
-
if
|
|
427
|
+
ld = len(d)
|
|
428
|
+
if ld <= len(dims) and d == dims[:ld]:
|
|
429
429
|
out = _match_shape(mdata[variable])
|
|
430
430
|
|
|
431
431
|
# lookup fdata:
|
|
@@ -448,8 +448,8 @@ class Model(ABC):
|
|
|
448
448
|
and variable in tdata
|
|
449
449
|
):
|
|
450
450
|
a, d = _filter_dims(tdata)
|
|
451
|
-
|
|
452
|
-
if
|
|
451
|
+
ld = len(ld)
|
|
452
|
+
if ld <= len(dims) and d == dims[:ld]:
|
|
453
453
|
out = _match_shape(tdata[variable])
|
|
454
454
|
|
|
455
455
|
# lookup wake modelling data:
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
+
import numpy as np
|
|
2
3
|
|
|
3
|
-
from foxes.utils import new_instance
|
|
4
|
+
from foxes.utils import new_instance, wd2uv, uv2wd
|
|
5
|
+
from foxes.config import config
|
|
6
|
+
import foxes.variables as FV
|
|
7
|
+
import foxes.constants as FC
|
|
4
8
|
|
|
5
9
|
from .model import Model
|
|
10
|
+
from .data import TData
|
|
6
11
|
|
|
7
12
|
|
|
8
13
|
class PartialWakesModel(Model):
|
|
@@ -68,6 +73,144 @@ class PartialWakesModel(Model):
|
|
|
68
73
|
"""
|
|
69
74
|
pass
|
|
70
75
|
|
|
76
|
+
def get_initial_tdata(
|
|
77
|
+
self,
|
|
78
|
+
algo,
|
|
79
|
+
mdata,
|
|
80
|
+
fdata,
|
|
81
|
+
amb_rotor_res,
|
|
82
|
+
rotor_weights,
|
|
83
|
+
wmodels,
|
|
84
|
+
):
|
|
85
|
+
"""
|
|
86
|
+
Creates the initial target data object
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
algo: foxes.core.Algorithm
|
|
91
|
+
The calculation algorithm
|
|
92
|
+
mdata: foxes.core.MData
|
|
93
|
+
The model data
|
|
94
|
+
fdata: foxes.core.FData
|
|
95
|
+
The farm data
|
|
96
|
+
amb_rotor_res: dict
|
|
97
|
+
The ambient results at rotor points,
|
|
98
|
+
key: variable name, value: numpy.ndarray
|
|
99
|
+
of shape: (n_states, n_turbines, n_rotor_points)
|
|
100
|
+
rotor_weights: numpy.ndarray
|
|
101
|
+
The rotor point weights, shape: (n_rotor_points,)
|
|
102
|
+
wmodels: list of foxes.core.WakeModel
|
|
103
|
+
The wake models for this partial wake model
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
tdata: foxes.core.TData
|
|
108
|
+
The target point data for the wake points
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
tpoints, tweights = self.get_wake_points(algo, mdata, fdata)
|
|
112
|
+
tdata = TData.from_tpoints(tpoints, tweights)
|
|
113
|
+
|
|
114
|
+
# map wind data:
|
|
115
|
+
if FV.WD in amb_rotor_res or FV.WS in amb_rotor_res:
|
|
116
|
+
assert FV.WD in amb_rotor_res and FV.WS in amb_rotor_res, (
|
|
117
|
+
"Require both wind direction and speed in ambient rotor results."
|
|
118
|
+
)
|
|
119
|
+
uv = wd2uv(amb_rotor_res[FV.WD], amb_rotor_res[FV.WS])
|
|
120
|
+
uv = np.stack(
|
|
121
|
+
[
|
|
122
|
+
self.map_rotor_results(
|
|
123
|
+
algo, mdata, fdata, tdata, FV.U, uv[..., 0], rotor_weights
|
|
124
|
+
),
|
|
125
|
+
self.map_rotor_results(
|
|
126
|
+
algo, mdata, fdata, tdata, FV.V, uv[..., 1], rotor_weights
|
|
127
|
+
),
|
|
128
|
+
],
|
|
129
|
+
axis=-1,
|
|
130
|
+
)
|
|
131
|
+
tdata.add(FV.AMB_WD, uv2wd(uv), dims=(FC.STATE, FC.TARGET, FC.TPOINT))
|
|
132
|
+
tdata.add(
|
|
133
|
+
FV.AMB_WS,
|
|
134
|
+
np.linalg.norm(uv, axis=-1),
|
|
135
|
+
dims=(FC.STATE, FC.TARGET, FC.TPOINT),
|
|
136
|
+
)
|
|
137
|
+
for wmodel in wmodels:
|
|
138
|
+
if wmodel.has_uv:
|
|
139
|
+
tdata.add(
|
|
140
|
+
FV.AMB_UV, uv, dims=(FC.STATE, FC.TARGET, FC.TPOINT, FC.XY)
|
|
141
|
+
)
|
|
142
|
+
break
|
|
143
|
+
|
|
144
|
+
# map rotor point results onto target points:
|
|
145
|
+
for v, d in amb_rotor_res.items():
|
|
146
|
+
if v not in [FV.WS, FV.WD, FV.U, FV.V, FV.UV]:
|
|
147
|
+
w = FV.var2amb.get(v, v)
|
|
148
|
+
tdata.add(
|
|
149
|
+
w,
|
|
150
|
+
self.map_rotor_results(
|
|
151
|
+
algo, mdata, fdata, tdata, v, d, rotor_weights
|
|
152
|
+
),
|
|
153
|
+
dims=(FC.STATE, FC.TARGET, FC.TPOINT),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return tdata
|
|
157
|
+
|
|
158
|
+
def map_rotor_results(
|
|
159
|
+
self,
|
|
160
|
+
algo,
|
|
161
|
+
mdata,
|
|
162
|
+
fdata,
|
|
163
|
+
tdata,
|
|
164
|
+
variable,
|
|
165
|
+
rotor_res,
|
|
166
|
+
rotor_weights,
|
|
167
|
+
):
|
|
168
|
+
"""
|
|
169
|
+
Map ambient rotor point results onto target points.
|
|
170
|
+
|
|
171
|
+
Parameters
|
|
172
|
+
----------
|
|
173
|
+
algo: foxes.core.Algorithm
|
|
174
|
+
The calculation algorithm
|
|
175
|
+
mdata: foxes.core.MData
|
|
176
|
+
The model data
|
|
177
|
+
fdata: foxes.core.FData
|
|
178
|
+
The farm data
|
|
179
|
+
tdata: foxes.core.TData
|
|
180
|
+
The target point data
|
|
181
|
+
variable: str
|
|
182
|
+
The variable name to map
|
|
183
|
+
rotor_res: numpy.ndarray
|
|
184
|
+
The results at rotor points, shape:
|
|
185
|
+
(n_states, n_turbines, n_rotor_points)
|
|
186
|
+
rotor_weights: numpy.ndarray
|
|
187
|
+
The rotor point weights, shape: (n_rotor_points,)
|
|
188
|
+
|
|
189
|
+
Returns
|
|
190
|
+
-------
|
|
191
|
+
res: numpy.ndarray
|
|
192
|
+
The mapped results at target points, shape:
|
|
193
|
+
(n_states, n_targets, n_tpoints)
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
if len(rotor_res.shape) > 2 and rotor_res.shape[:2] == (
|
|
197
|
+
tdata.n_states,
|
|
198
|
+
tdata.n_targets,
|
|
199
|
+
):
|
|
200
|
+
q = np.zeros(
|
|
201
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints),
|
|
202
|
+
dtype=config.dtype_double,
|
|
203
|
+
)
|
|
204
|
+
if rotor_res.shape[2] == 1:
|
|
205
|
+
q[:] = rotor_res
|
|
206
|
+
else:
|
|
207
|
+
q[:] = np.einsum("str,r->st", rotor_res, rotor_weights)[:, :, None]
|
|
208
|
+
return q
|
|
209
|
+
else:
|
|
210
|
+
raise ValueError(
|
|
211
|
+
f"Partial wakes '{self.name}': Incompatible shape '{rotor_res.shape}' for variable '{variable}' in rotor results."
|
|
212
|
+
)
|
|
213
|
+
|
|
71
214
|
def new_wake_deltas(self, algo, mdata, fdata, tdata, wmodel):
|
|
72
215
|
"""
|
|
73
216
|
Creates new initial wake deltas, filled
|
|
@@ -77,11 +220,11 @@ class PartialWakesModel(Model):
|
|
|
77
220
|
----------
|
|
78
221
|
algo: foxes.core.Algorithm
|
|
79
222
|
The calculation algorithm
|
|
80
|
-
mdata: foxes.core.
|
|
223
|
+
mdata: foxes.core.MData
|
|
81
224
|
The model data
|
|
82
|
-
fdata: foxes.core.
|
|
225
|
+
fdata: foxes.core.FData
|
|
83
226
|
The farm data
|
|
84
|
-
tdata: foxes.core.
|
|
227
|
+
tdata: foxes.core.TData
|
|
85
228
|
The target point data
|
|
86
229
|
wmodel: foxes.core.WakeModel
|
|
87
230
|
The wake model
|
|
@@ -140,7 +283,6 @@ class PartialWakesModel(Model):
|
|
|
140
283
|
mdata,
|
|
141
284
|
fdata,
|
|
142
285
|
tdata,
|
|
143
|
-
amb_res,
|
|
144
286
|
rpoint_weights,
|
|
145
287
|
wake_deltas,
|
|
146
288
|
wmodel,
|
|
@@ -162,11 +304,6 @@ class PartialWakesModel(Model):
|
|
|
162
304
|
The farm data
|
|
163
305
|
tdata: foxes.core.Data
|
|
164
306
|
The target point data
|
|
165
|
-
amb_res: dict
|
|
166
|
-
The ambient results at the target points
|
|
167
|
-
of all rotors. Key: variable name, value
|
|
168
|
-
np.ndarray of shape:
|
|
169
|
-
(n_states, n_turbines, n_rotor_points)
|
|
170
307
|
rpoint_weights: numpy.ndarray
|
|
171
308
|
The rotor point weights, shape: (n_rotor_points,)
|
|
172
309
|
wake_deltas: dict
|
foxes/core/point_data_model.py
CHANGED
|
@@ -98,8 +98,7 @@ class PointDataModel(DataCalcModel):
|
|
|
98
98
|
|
|
99
99
|
def run_calculation(self, algo, *data, out_vars, **calc_pars):
|
|
100
100
|
"""
|
|
101
|
-
Starts the model calculation in parallel
|
|
102
|
-
xarray's `apply_ufunc`.
|
|
101
|
+
Starts the model calculation in parallel.
|
|
103
102
|
|
|
104
103
|
Typically this function is called by algorithms.
|
|
105
104
|
|
|
@@ -149,7 +148,7 @@ class PointDataModelList(PointDataModel):
|
|
|
149
148
|
|
|
150
149
|
By using the PointDataModelList the models'
|
|
151
150
|
`calculate` functions are called together
|
|
152
|
-
under one common call
|
|
151
|
+
under one common call by the engine.
|
|
153
152
|
|
|
154
153
|
Attributes
|
|
155
154
|
----------
|
foxes/core/rotor_model.py
CHANGED
|
@@ -60,6 +60,10 @@ class RotorModel(FarmDataModel):
|
|
|
60
60
|
"""
|
|
61
61
|
if self.calc_vars is None:
|
|
62
62
|
vrs = algo.states.output_point_vars(algo)
|
|
63
|
+
assert FV.WEIGHT not in vrs, (
|
|
64
|
+
f"Rotor '{self.name}': States '{algo.states.name}' output_point_vars contain '{FV.WEIGHT}', please remove"
|
|
65
|
+
)
|
|
66
|
+
|
|
63
67
|
if FV.WS in vrs:
|
|
64
68
|
self.calc_vars = [FV.REWS] + [v for v in vrs if v != FV.WS]
|
|
65
69
|
else:
|
|
@@ -72,6 +76,9 @@ class RotorModel(FarmDataModel):
|
|
|
72
76
|
|
|
73
77
|
self.calc_vars = sorted(self.calc_vars)
|
|
74
78
|
|
|
79
|
+
if FV.WEIGHT not in self.calc_vars:
|
|
80
|
+
self.calc_vars.append(FV.WEIGHT)
|
|
81
|
+
|
|
75
82
|
return self.calc_vars
|
|
76
83
|
|
|
77
84
|
@abstractmethod
|
|
@@ -174,9 +181,7 @@ class RotorModel(FarmDataModel):
|
|
|
174
181
|
elif res.shape[1] == 1:
|
|
175
182
|
fdata[v][:, downwind_index] = res[:, 0]
|
|
176
183
|
else:
|
|
177
|
-
|
|
178
|
-
f"Rotor model '{self.name}': downwind_index is not None, but results shape for '{v}' has more than one turbine, {res.shape}"
|
|
179
|
-
)
|
|
184
|
+
fdata[v, downwind_index] = res[:, downwind_index]
|
|
180
185
|
|
|
181
186
|
def eval_rpoint_results(
|
|
182
187
|
self,
|
|
@@ -184,7 +189,7 @@ class RotorModel(FarmDataModel):
|
|
|
184
189
|
mdata,
|
|
185
190
|
fdata,
|
|
186
191
|
tdata,
|
|
187
|
-
|
|
192
|
+
rpoint_weights,
|
|
188
193
|
downwind_index=None,
|
|
189
194
|
copy_to_ambient=False,
|
|
190
195
|
):
|
|
@@ -207,7 +212,7 @@ class RotorModel(FarmDataModel):
|
|
|
207
212
|
The farm data
|
|
208
213
|
tdata: foxes.core.TData
|
|
209
214
|
The target point data
|
|
210
|
-
|
|
215
|
+
rpoint_weights: numpy.ndarray
|
|
211
216
|
The rotor point weights, shape: (n_rpoints,)
|
|
212
217
|
downwind_index: int, optional
|
|
213
218
|
The index in the downwind order
|
|
@@ -233,7 +238,7 @@ class RotorModel(FarmDataModel):
|
|
|
233
238
|
wd = tdata[FV.WD]
|
|
234
239
|
ws = tdata[FV.WS]
|
|
235
240
|
uvp = wd2uv(wd, ws, axis=-1)
|
|
236
|
-
uv = np.einsum("stpd,p->std", uvp,
|
|
241
|
+
uv = np.einsum("stpd,p->std", uvp, rpoint_weights)
|
|
237
242
|
|
|
238
243
|
wd = None
|
|
239
244
|
vdone = []
|
|
@@ -243,7 +248,6 @@ class RotorModel(FarmDataModel):
|
|
|
243
248
|
wd = uv2wd(uv, axis=-1)
|
|
244
249
|
self._set_res(fdata, v, wd, downwind_index)
|
|
245
250
|
vdone.append(v)
|
|
246
|
-
|
|
247
251
|
elif v == FV.WS:
|
|
248
252
|
ws = np.linalg.norm(uv, axis=-1)
|
|
249
253
|
self._set_res(fdata, v, ws, downwind_index)
|
|
@@ -265,7 +269,7 @@ class RotorModel(FarmDataModel):
|
|
|
265
269
|
|
|
266
270
|
for v in self.calc_vars:
|
|
267
271
|
if v == FV.REWS:
|
|
268
|
-
rews = np.maximum(np.einsum("stp,p->st", wsp,
|
|
272
|
+
rews = np.maximum(np.einsum("stp,p->st", wsp, rpoint_weights), 0.0)
|
|
269
273
|
self._set_res(fdata, v, rews, downwind_index)
|
|
270
274
|
del rews
|
|
271
275
|
vdone.append(v)
|
|
@@ -278,12 +282,14 @@ class RotorModel(FarmDataModel):
|
|
|
278
282
|
if uvp.shape[2] > 1:
|
|
279
283
|
rews2 = np.sqrt(
|
|
280
284
|
np.maximum(
|
|
281
|
-
np.einsum(
|
|
285
|
+
np.einsum(
|
|
286
|
+
"stp,p->st", np.sign(wsp) * wsp**2, rpoint_weights
|
|
287
|
+
),
|
|
282
288
|
0.0,
|
|
283
289
|
)
|
|
284
290
|
)
|
|
285
291
|
else:
|
|
286
|
-
rews2 = np.sqrt(np.einsum("stp,p->st", wsp**2,
|
|
292
|
+
rews2 = np.sqrt(np.einsum("stp,p->st", wsp**2, rpoint_weights))
|
|
287
293
|
self._set_res(fdata, v, rews2, downwind_index)
|
|
288
294
|
del rews2
|
|
289
295
|
vdone.append(v)
|
|
@@ -295,10 +301,12 @@ class RotorModel(FarmDataModel):
|
|
|
295
301
|
# turbine axis direction:
|
|
296
302
|
if uvp.shape[2] > 1:
|
|
297
303
|
rews3 = np.maximum(
|
|
298
|
-
np.einsum("stp,p->st", wsp**3,
|
|
304
|
+
np.einsum("stp,p->st", wsp**3, rpoint_weights), 0.0
|
|
299
305
|
) ** (1.0 / 3.0)
|
|
300
306
|
else:
|
|
301
|
-
rews3 = (np.einsum("stp,p->st", wsp**3,
|
|
307
|
+
rews3 = (np.einsum("stp,p->st", wsp**3, rpoint_weights)) ** (
|
|
308
|
+
1.0 / 3.0
|
|
309
|
+
)
|
|
302
310
|
self._set_res(fdata, v, rews3, downwind_index)
|
|
303
311
|
del rews3
|
|
304
312
|
vdone.append(v)
|
|
@@ -307,8 +315,10 @@ class RotorModel(FarmDataModel):
|
|
|
307
315
|
del uvp
|
|
308
316
|
|
|
309
317
|
for v in self.calc_vars:
|
|
310
|
-
if v not in vdone
|
|
311
|
-
|
|
318
|
+
if v not in vdone and (
|
|
319
|
+
fdata[v].shape[1] > 1 or downwind_index is None or downwind_index == 0
|
|
320
|
+
):
|
|
321
|
+
res = np.einsum("stp,p->st", tdata[v], rpoint_weights)
|
|
312
322
|
self._set_res(fdata, v, res, downwind_index)
|
|
313
323
|
if copy_to_ambient and v in FV.var2amb:
|
|
314
324
|
fdata[FV.var2amb[v]] = fdata[v].copy()
|
|
@@ -319,10 +329,8 @@ class RotorModel(FarmDataModel):
|
|
|
319
329
|
mdata,
|
|
320
330
|
fdata,
|
|
321
331
|
rpoints=None,
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
store_rweights=False,
|
|
325
|
-
store_amb_res=False,
|
|
332
|
+
rpoint_weights=None,
|
|
333
|
+
store=False,
|
|
326
334
|
downwind_index=None,
|
|
327
335
|
):
|
|
328
336
|
"""
|
|
@@ -339,16 +347,11 @@ class RotorModel(FarmDataModel):
|
|
|
339
347
|
rpoints: numpy.ndarray, optional
|
|
340
348
|
The rotor points, or None for automatic for
|
|
341
349
|
this rotor. Shape: (n_states, n_turbines, n_rpoints, 3)
|
|
342
|
-
|
|
350
|
+
rpoint_weights: numpy.ndarray, optional
|
|
343
351
|
The rotor point weights, or None for automatic
|
|
344
352
|
for this rotor. Shape: (n_rpoints,)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
store_rweights: bool, optional
|
|
348
|
-
Switch for storing rotor point weights to mdata
|
|
349
|
-
store_amb_res: bool, optional
|
|
350
|
-
Switch for storing ambient rotor point reults as they
|
|
351
|
-
come from the states to mdata
|
|
353
|
+
store: bool, optional
|
|
354
|
+
Flag for storing ambient rotor point results
|
|
352
355
|
downwind_index: int, optional
|
|
353
356
|
Only compute for index in the downwind order
|
|
354
357
|
|
|
@@ -364,18 +367,12 @@ class RotorModel(FarmDataModel):
|
|
|
364
367
|
rpoints = mdata.get(
|
|
365
368
|
FC.ROTOR_POINTS, self.get_rotor_points(algo, mdata, fdata)
|
|
366
369
|
)
|
|
367
|
-
if store_rpoints:
|
|
368
|
-
algo.add_to_chunk_store(FC.ROTOR_POINTS, rpoints, mdata=mdata)
|
|
369
|
-
|
|
370
370
|
if downwind_index is not None:
|
|
371
371
|
rpoints = rpoints[:, downwind_index, None]
|
|
372
|
+
if rpoint_weights is None:
|
|
373
|
+
rpoint_weights = mdata.get_item(FC.TWEIGHTS, self.rotor_point_weights())
|
|
372
374
|
|
|
373
|
-
|
|
374
|
-
weights = mdata.get(FC.TWEIGHTS, self.rotor_point_weights())
|
|
375
|
-
if store_rweights:
|
|
376
|
-
algo.add_to_chunk_store(FC.ROTOR_WEIGHTS, weights, mdata=mdata)
|
|
377
|
-
|
|
378
|
-
tdata = TData.from_tpoints(rpoints, weights)
|
|
375
|
+
tdata = TData.from_tpoints(rpoints, rpoint_weights)
|
|
379
376
|
svars = algo.states.output_point_vars(algo)
|
|
380
377
|
for v in svars:
|
|
381
378
|
tdata.add(
|
|
@@ -386,16 +383,23 @@ class RotorModel(FarmDataModel):
|
|
|
386
383
|
|
|
387
384
|
sres = algo.states.calculate(algo, mdata, fdata, tdata)
|
|
388
385
|
tdata.update(sres)
|
|
386
|
+
if FV.WEIGHT not in tdata:
|
|
387
|
+
raise KeyError(
|
|
388
|
+
f"Rotor '{self.name}': States '{algo.states.name}' failed to provide '{FV.WEIGHT}' in tdata"
|
|
389
|
+
)
|
|
389
390
|
|
|
390
|
-
if
|
|
391
|
-
algo.add_to_chunk_store(FC.
|
|
391
|
+
if store:
|
|
392
|
+
algo.add_to_chunk_store(FC.ROTOR_POINTS, rpoints, mdata=mdata)
|
|
393
|
+
algo.add_to_chunk_store(FC.ROTOR_WEIGHTS, rpoint_weights, mdata=mdata)
|
|
394
|
+
algo.add_to_chunk_store(FC.AMB_ROTOR_RES, sres, mdata=mdata)
|
|
395
|
+
algo.add_to_chunk_store(FC.WEIGHT_RES, tdata[FV.WEIGHT], mdata=mdata)
|
|
392
396
|
|
|
393
397
|
self.eval_rpoint_results(
|
|
394
398
|
algo,
|
|
395
399
|
mdata,
|
|
396
400
|
fdata,
|
|
397
401
|
tdata,
|
|
398
|
-
|
|
402
|
+
rpoint_weights,
|
|
399
403
|
downwind_index,
|
|
400
404
|
copy_to_ambient=True,
|
|
401
405
|
)
|
foxes/core/states.py
CHANGED
|
@@ -2,7 +2,6 @@ from abc import abstractmethod
|
|
|
2
2
|
|
|
3
3
|
from .point_data_model import PointDataModel, PointDataModelList
|
|
4
4
|
from foxes.utils import new_instance
|
|
5
|
-
import foxes.variables as FV
|
|
6
5
|
import foxes.constants as FC
|
|
7
6
|
|
|
8
7
|
|
|
@@ -43,24 +42,6 @@ class States(PointDataModel):
|
|
|
43
42
|
"""
|
|
44
43
|
return list(range(self.size()))
|
|
45
44
|
|
|
46
|
-
@abstractmethod
|
|
47
|
-
def weights(self, algo):
|
|
48
|
-
"""
|
|
49
|
-
The statistical weights of all states.
|
|
50
|
-
|
|
51
|
-
Parameters
|
|
52
|
-
----------
|
|
53
|
-
algo: foxes.core.Algorithm
|
|
54
|
-
The calculation algorithm
|
|
55
|
-
|
|
56
|
-
Returns
|
|
57
|
-
-------
|
|
58
|
-
weights: numpy.ndarray
|
|
59
|
-
The weights, shape: (n_states, n_turbines)
|
|
60
|
-
|
|
61
|
-
"""
|
|
62
|
-
pass
|
|
63
|
-
|
|
64
45
|
def reset(self, algo=None, states_sel=None, states_loc=None, verbosity=0):
|
|
65
46
|
"""
|
|
66
47
|
Reset the states, optionally select states
|
|
@@ -106,19 +87,9 @@ class States(PointDataModel):
|
|
|
106
87
|
if sinds is not None:
|
|
107
88
|
idata["coords"][FC.STATE] = sinds
|
|
108
89
|
|
|
109
|
-
weights = self.weights(algo)
|
|
110
|
-
if len(weights.shape) != 2:
|
|
111
|
-
raise ValueError(
|
|
112
|
-
f"States '{self.name}': Wrong weights dimension, expecing ({FC.STATE}, {FC.TURBINE}), got shape {weights.shape}"
|
|
113
|
-
)
|
|
114
|
-
if weights.shape[1] != algo.n_turbines:
|
|
115
|
-
raise ValueError(
|
|
116
|
-
f"States '{self.name}': Wrong size of second axis dimension '{FC.TURBINE}': Expecting {algo.n_turbines}, got {weights.shape[1]}"
|
|
117
|
-
)
|
|
118
|
-
idata["data_vars"][FV.WEIGHT] = ((FC.STATE, FC.TURBINE), weights)
|
|
119
|
-
|
|
120
90
|
return idata
|
|
121
91
|
|
|
92
|
+
@abstractmethod
|
|
122
93
|
def output_point_vars(self, algo):
|
|
123
94
|
"""
|
|
124
95
|
The variables which are being modified by the model.
|
|
@@ -134,7 +105,7 @@ class States(PointDataModel):
|
|
|
134
105
|
The output variable names
|
|
135
106
|
|
|
136
107
|
"""
|
|
137
|
-
|
|
108
|
+
pass
|
|
138
109
|
|
|
139
110
|
def __add__(self, s):
|
|
140
111
|
if isinstance(s, list):
|
|
@@ -142,7 +113,7 @@ class States(PointDataModel):
|
|
|
142
113
|
elif isinstance(s, ExtendedStates):
|
|
143
114
|
if s.states is not self:
|
|
144
115
|
raise ValueError(
|
|
145
|
-
|
|
116
|
+
"Cannot add extended states, since not based on same states"
|
|
146
117
|
)
|
|
147
118
|
return ExtendedStates(self, s.pmodels.models[1:])
|
|
148
119
|
else:
|
|
@@ -245,23 +216,6 @@ class ExtendedStates(States):
|
|
|
245
216
|
"""
|
|
246
217
|
return self.states.index()
|
|
247
218
|
|
|
248
|
-
def weights(self, algo):
|
|
249
|
-
"""
|
|
250
|
-
The statistical weights of all states.
|
|
251
|
-
|
|
252
|
-
Parameters
|
|
253
|
-
----------
|
|
254
|
-
algo: foxes.core.Algorithm
|
|
255
|
-
The calculation algorithm
|
|
256
|
-
|
|
257
|
-
Returns
|
|
258
|
-
-------
|
|
259
|
-
weights: numpy.ndarray
|
|
260
|
-
The weights, shape: (n_states, n_turbines)
|
|
261
|
-
|
|
262
|
-
"""
|
|
263
|
-
return self.states.weights(algo)
|
|
264
|
-
|
|
265
219
|
def output_point_vars(self, algo):
|
|
266
220
|
"""
|
|
267
221
|
The variables which are being modified by the model.
|
|
@@ -313,7 +267,7 @@ class ExtendedStates(States):
|
|
|
313
267
|
elif isinstance(m, ExtendedStates):
|
|
314
268
|
if m.states is not self.states:
|
|
315
269
|
raise ValueError(
|
|
316
|
-
|
|
270
|
+
"Cannot add extended states, since not based on same states"
|
|
317
271
|
)
|
|
318
272
|
return ExtendedStates(self.states, models + m.pmodels.models[1:])
|
|
319
273
|
else:
|
foxes/core/turbine.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
from copy import deepcopy
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class Turbine:
|
|
@@ -71,7 +72,7 @@ class Turbine:
|
|
|
71
72
|
self.index = index
|
|
72
73
|
self.name = name
|
|
73
74
|
self.xy = np.array(xy)
|
|
74
|
-
self.models = turbine_models
|
|
75
|
+
self.models = deepcopy(turbine_models)
|
|
75
76
|
self.D = D
|
|
76
77
|
self.H = H
|
|
77
78
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
|
|
3
|
+
from foxes.utils import new_instance
|
|
4
|
+
from .model import Model
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class WakeDeflection(Model):
|
|
8
|
+
"""
|
|
9
|
+
Abstract base class for wake deflection models.
|
|
10
|
+
|
|
11
|
+
:group: core
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def has_uv(self):
|
|
17
|
+
"""
|
|
18
|
+
This model uses wind vector data
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
hasuv: bool
|
|
23
|
+
Flag for wind vector data
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def calc_deflection(
|
|
30
|
+
self,
|
|
31
|
+
algo,
|
|
32
|
+
mdata,
|
|
33
|
+
fdata,
|
|
34
|
+
tdata,
|
|
35
|
+
downwind_index,
|
|
36
|
+
coos,
|
|
37
|
+
):
|
|
38
|
+
"""
|
|
39
|
+
Calculates the wake deflection.
|
|
40
|
+
|
|
41
|
+
This function optionally adds FC.WDEFL_ROT_ANGLE or
|
|
42
|
+
FC.WDEFL_DWS_FACTOR to the tdata.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
algo: foxes.core.Algorithm
|
|
47
|
+
The calculation algorithm
|
|
48
|
+
mdata: foxes.core.MData
|
|
49
|
+
The model data
|
|
50
|
+
fdata: foxes.core.FData
|
|
51
|
+
The farm data
|
|
52
|
+
tdata: foxes.core.TData
|
|
53
|
+
The target point data
|
|
54
|
+
downwind_index: int
|
|
55
|
+
The index of the wake causing turbine
|
|
56
|
+
in the downwind order
|
|
57
|
+
coos: numpy.ndarray
|
|
58
|
+
The wake frame coordinates of the evaluation
|
|
59
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
coos: numpy.ndarray
|
|
64
|
+
The wake frame coordinates of the evaluation
|
|
65
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def get_yaw_alpha_seq(
|
|
71
|
+
self,
|
|
72
|
+
algo,
|
|
73
|
+
mdata,
|
|
74
|
+
fdata,
|
|
75
|
+
tdata,
|
|
76
|
+
downwind_index,
|
|
77
|
+
x,
|
|
78
|
+
):
|
|
79
|
+
"""
|
|
80
|
+
Computes sequential wind vector rotation angles.
|
|
81
|
+
|
|
82
|
+
Wind vector rotation angles are computed at the
|
|
83
|
+
current trace points due to a yawed rotor
|
|
84
|
+
for sequential runs.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
algo: foxes.core.Algorithm
|
|
89
|
+
The calculation algorithm
|
|
90
|
+
mdata: foxes.core.MData
|
|
91
|
+
The model data
|
|
92
|
+
fdata: foxes.core.FData
|
|
93
|
+
The farm data
|
|
94
|
+
tdata: foxes.core.TData
|
|
95
|
+
The target point data
|
|
96
|
+
downwind_index: int
|
|
97
|
+
The index of the wake causing turbine
|
|
98
|
+
in the downwind order
|
|
99
|
+
x: numpy.ndarray
|
|
100
|
+
The distance from the wake causing rotor
|
|
101
|
+
for the first n_times subsequent time steps,
|
|
102
|
+
shape: (n_times,)
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
alpha: numpy.ndarray
|
|
107
|
+
The delta WD result at the x locations,
|
|
108
|
+
shape: (n_times,)
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
raise NotImplementedError(
|
|
112
|
+
f"Wake deflection '{self.name}' not implemented for sequential runs"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def new(cls, wframe_type, *args, **kwargs):
|
|
117
|
+
"""
|
|
118
|
+
Run-time wake deflection model factory.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
wframe_type: str
|
|
123
|
+
The selected derived class name
|
|
124
|
+
args: tuple, optional
|
|
125
|
+
Additional parameters for constructor
|
|
126
|
+
kwargs: dict, optional
|
|
127
|
+
Additional parameters for constructor
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
return new_instance(cls, wframe_type, *args, **kwargs)
|