foxes 0.6.2__py3-none-any.whl → 0.7.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of foxes might be problematic. Click here for more details.
- foxes/VERSION +1 -1
- foxes/algorithms/downwind/downwind.py +131 -65
- foxes/algorithms/downwind/models/__init__.py +2 -1
- foxes/algorithms/downwind/models/farm_wakes_calc.py +87 -55
- foxes/algorithms/downwind/models/init_farm_data.py +134 -0
- foxes/algorithms/downwind/models/point_wakes_calc.py +54 -65
- foxes/algorithms/downwind/models/{calc_order.py → reorder_farm_output.py} +28 -26
- foxes/algorithms/iterative/iterative.py +100 -51
- foxes/algorithms/iterative/models/convergence.py +3 -3
- foxes/algorithms/iterative/models/farm_wakes_calc.py +55 -48
- foxes/algorithms/sequential/models/seq_state.py +7 -6
- foxes/algorithms/sequential/sequential.py +81 -44
- foxes/constants.py +33 -18
- foxes/core/__init__.py +2 -2
- foxes/core/algorithm.py +31 -12
- foxes/core/data.py +335 -41
- foxes/core/data_calc_model.py +27 -23
- foxes/core/farm_controller.py +27 -28
- foxes/core/farm_data_model.py +26 -4
- foxes/core/model.py +186 -129
- foxes/core/partial_wakes_model.py +84 -81
- foxes/core/point_data_model.py +51 -18
- foxes/core/rotor_model.py +59 -77
- foxes/core/states.py +6 -6
- foxes/core/turbine_model.py +4 -4
- foxes/core/turbine_type.py +24 -0
- foxes/core/vertical_profile.py +3 -3
- foxes/core/wake_frame.py +91 -50
- foxes/core/wake_model.py +74 -43
- foxes/core/wake_superposition.py +29 -26
- foxes/input/farm_layout/from_random.py +10 -9
- foxes/input/states/create/random_timeseries.py +17 -19
- foxes/input/states/field_data_nc.py +12 -8
- foxes/input/states/multi_height.py +24 -14
- foxes/input/states/scan_ws.py +13 -17
- foxes/input/states/single.py +28 -20
- foxes/input/states/states_table.py +22 -18
- foxes/models/axial_induction_models/betz.py +1 -1
- foxes/models/farm_models/turbine2farm.py +2 -2
- foxes/models/model_book.py +39 -14
- foxes/models/partial_wakes/__init__.py +2 -3
- foxes/models/partial_wakes/axiwake.py +73 -200
- foxes/models/partial_wakes/centre.py +11 -79
- foxes/models/partial_wakes/grid.py +7 -63
- foxes/models/partial_wakes/rotor_points.py +53 -147
- foxes/models/partial_wakes/segregated.py +158 -0
- foxes/models/partial_wakes/top_hat.py +88 -196
- foxes/models/point_models/set_uniform_data.py +4 -4
- foxes/models/point_models/tke2ti.py +4 -4
- foxes/models/point_models/wake_deltas.py +4 -4
- foxes/models/rotor_models/centre.py +15 -19
- foxes/models/rotor_models/grid.py +2 -1
- foxes/models/rotor_models/levels.py +2 -1
- foxes/models/turbine_models/__init__.py +0 -1
- foxes/models/turbine_models/calculator.py +11 -7
- foxes/models/turbine_models/kTI_model.py +13 -11
- foxes/models/turbine_models/lookup_table.py +22 -9
- foxes/models/turbine_models/power_mask.py +81 -51
- foxes/models/turbine_models/rotor_centre_calc.py +17 -20
- foxes/models/turbine_models/sector_management.py +5 -6
- foxes/models/turbine_models/set_farm_vars.py +49 -20
- foxes/models/turbine_models/table_factors.py +5 -5
- foxes/models/turbine_models/thrust2ct.py +9 -5
- foxes/models/turbine_models/yaw2yawm.py +7 -13
- foxes/models/turbine_models/yawm2yaw.py +7 -11
- foxes/models/turbine_types/PCt_file.py +84 -3
- foxes/models/turbine_types/PCt_from_two.py +7 -3
- foxes/models/turbine_types/null_type.py +2 -2
- foxes/models/turbine_types/wsrho2PCt_from_two.py +2 -2
- foxes/models/turbine_types/wsti2PCt_from_two.py +6 -2
- foxes/models/wake_frames/farm_order.py +26 -22
- foxes/models/wake_frames/rotor_wd.py +32 -31
- foxes/models/wake_frames/seq_dynamic_wakes.py +112 -64
- foxes/models/wake_frames/streamlines.py +51 -47
- foxes/models/wake_frames/timelines.py +59 -47
- foxes/models/wake_frames/yawed_wakes.py +63 -40
- foxes/models/wake_models/axisymmetric.py +31 -35
- foxes/models/wake_models/dist_sliced.py +50 -56
- foxes/models/wake_models/gaussian.py +33 -35
- foxes/models/wake_models/induction/rankine_half_body.py +79 -87
- foxes/models/wake_models/induction/rathmann.py +56 -63
- foxes/models/wake_models/induction/self_similar.py +59 -62
- foxes/models/wake_models/ti/crespo_hernandez.py +83 -74
- foxes/models/wake_models/ti/iec_ti.py +65 -75
- foxes/models/wake_models/top_hat.py +60 -69
- foxes/models/wake_models/wake_mirror.py +49 -54
- foxes/models/wake_models/wind/bastankhah14.py +44 -66
- foxes/models/wake_models/wind/bastankhah16.py +84 -111
- foxes/models/wake_models/wind/jensen.py +67 -89
- foxes/models/wake_models/wind/turbopark.py +93 -133
- foxes/models/wake_superpositions/ti_linear.py +33 -27
- foxes/models/wake_superpositions/ti_max.py +33 -27
- foxes/models/wake_superpositions/ti_pow.py +35 -27
- foxes/models/wake_superpositions/ti_quadratic.py +33 -27
- foxes/models/wake_superpositions/ws_linear.py +39 -32
- foxes/models/wake_superpositions/ws_max.py +40 -33
- foxes/models/wake_superpositions/ws_pow.py +39 -32
- foxes/models/wake_superpositions/ws_product.py +35 -28
- foxes/models/wake_superpositions/ws_quadratic.py +39 -32
- foxes/opt/problems/layout/farm_layout.py +38 -97
- foxes/output/__init__.py +1 -0
- foxes/output/flow_plots_2d/flow_plots.py +2 -0
- foxes/output/rose_plot.py +3 -3
- foxes/output/rotor_point_plots.py +117 -0
- foxes/output/turbine_type_curves.py +2 -2
- foxes/utils/__init__.py +1 -1
- foxes/utils/load.py +29 -0
- foxes/utils/random_xy.py +11 -10
- foxes/utils/runners/runners.py +3 -4
- foxes/utils/windrose_plot.py +1 -1
- foxes/variables.py +10 -0
- {foxes-0.6.2.dist-info → foxes-0.7.0.2.dist-info}/METADATA +13 -7
- {foxes-0.6.2.dist-info → foxes-0.7.0.2.dist-info}/RECORD +117 -117
- foxes/models/partial_wakes/distsliced.py +0 -322
- foxes/models/partial_wakes/mapped.py +0 -252
- foxes/models/turbine_models/set_XYHD.py +0 -130
- {foxes-0.6.2.dist-info → foxes-0.7.0.2.dist-info}/LICENSE +0 -0
- {foxes-0.6.2.dist-info → foxes-0.7.0.2.dist-info}/WHEEL +0 -0
- {foxes-0.6.2.dist-info → foxes-0.7.0.2.dist-info}/top_level.txt +0 -0
- {foxes-0.6.2.dist-info → foxes-0.7.0.2.dist-info}/zip-safe +0 -0
|
@@ -3,7 +3,7 @@ from scipy.spatial.distance import cdist
|
|
|
3
3
|
|
|
4
4
|
from foxes.core import WakeFrame
|
|
5
5
|
from foxes.utils import wd2uv
|
|
6
|
-
from foxes.core.data import
|
|
6
|
+
from foxes.core.data import TData
|
|
7
7
|
import foxes.variables as FV
|
|
8
8
|
import foxes.constants as FC
|
|
9
9
|
from foxes.algorithms import Sequential
|
|
@@ -44,6 +44,9 @@ class SeqDynamicWakes(WakeFrame):
|
|
|
44
44
|
self.cl_ipars = cl_ipars
|
|
45
45
|
self.dt_min = dt_min
|
|
46
46
|
|
|
47
|
+
def __repr__(self):
|
|
48
|
+
return f"{type(self).__name__}(dt_min={self.dt_min})"
|
|
49
|
+
|
|
47
50
|
def initialize(self, algo, verbosity=0):
|
|
48
51
|
"""
|
|
49
52
|
Initializes the model.
|
|
@@ -98,9 +101,9 @@ class SeqDynamicWakes(WakeFrame):
|
|
|
98
101
|
----------
|
|
99
102
|
algo: foxes.core.Algorithm
|
|
100
103
|
The calculation algorithm
|
|
101
|
-
mdata: foxes.core.
|
|
104
|
+
mdata: foxes.core.MData
|
|
102
105
|
The model data
|
|
103
|
-
fdata: foxes.core.
|
|
106
|
+
fdata: foxes.core.FData
|
|
104
107
|
The farm data
|
|
105
108
|
|
|
106
109
|
Returns
|
|
@@ -109,19 +112,18 @@ class SeqDynamicWakes(WakeFrame):
|
|
|
109
112
|
The turbine order, shape: (n_states, n_turbines)
|
|
110
113
|
|
|
111
114
|
"""
|
|
112
|
-
|
|
113
115
|
# prepare:
|
|
114
116
|
n_states = fdata.n_states
|
|
115
117
|
n_turbines = algo.n_turbines
|
|
116
|
-
|
|
118
|
+
tdata = TData.from_points(points=fdata[FV.TXYH])
|
|
117
119
|
|
|
118
120
|
# calculate streamline x coordinates for turbines rotor centre points:
|
|
119
121
|
# n_states, n_turbines_source, n_turbines_target
|
|
120
122
|
coosx = np.zeros((n_states, n_turbines, n_turbines), dtype=FC.DTYPE)
|
|
121
123
|
for ti in range(n_turbines):
|
|
122
|
-
coosx[:, ti, :] = self.get_wake_coos(
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
coosx[:, ti, :] = self.get_wake_coos(algo, mdata, fdata, tdata, ti)[
|
|
125
|
+
:, :, 0, 0
|
|
126
|
+
]
|
|
125
127
|
|
|
126
128
|
# derive turbine order:
|
|
127
129
|
# TODO: Remove loop over states
|
|
@@ -131,94 +133,112 @@ class SeqDynamicWakes(WakeFrame):
|
|
|
131
133
|
|
|
132
134
|
return order
|
|
133
135
|
|
|
134
|
-
def get_wake_coos(
|
|
136
|
+
def get_wake_coos(
|
|
137
|
+
self,
|
|
138
|
+
algo,
|
|
139
|
+
mdata,
|
|
140
|
+
fdata,
|
|
141
|
+
tdata,
|
|
142
|
+
downwind_index,
|
|
143
|
+
):
|
|
135
144
|
"""
|
|
136
|
-
Calculate wake coordinates.
|
|
145
|
+
Calculate wake coordinates of rotor points.
|
|
137
146
|
|
|
138
147
|
Parameters
|
|
139
148
|
----------
|
|
140
149
|
algo: foxes.core.Algorithm
|
|
141
150
|
The calculation algorithm
|
|
142
|
-
mdata: foxes.core.
|
|
151
|
+
mdata: foxes.core.MData
|
|
143
152
|
The model data
|
|
144
|
-
fdata: foxes.core.
|
|
153
|
+
fdata: foxes.core.FData
|
|
145
154
|
The farm data
|
|
146
|
-
|
|
147
|
-
The
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
155
|
+
tdata: foxes.core.TData
|
|
156
|
+
The target point data
|
|
157
|
+
downwind_index: int
|
|
158
|
+
The index of the wake causing turbine
|
|
159
|
+
in the downwnd order
|
|
151
160
|
|
|
152
161
|
Returns
|
|
153
162
|
-------
|
|
154
163
|
wake_coos: numpy.ndarray
|
|
155
164
|
The wake frame coordinates of the evaluation
|
|
156
|
-
points, shape: (n_states,
|
|
165
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
157
166
|
|
|
158
167
|
"""
|
|
159
|
-
|
|
160
168
|
# prepare:
|
|
161
169
|
n_states = 1
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
170
|
+
n_targets = tdata.n_targets
|
|
171
|
+
n_tpoints = tdata.n_tpoints
|
|
172
|
+
n_points = n_targets * n_tpoints
|
|
173
|
+
points = tdata[FC.TARGETS].reshape(n_states, n_points, 3)
|
|
166
174
|
counter = algo.states.counter
|
|
167
175
|
N = counter + 1
|
|
168
176
|
|
|
169
177
|
# new wake starts at turbine:
|
|
170
|
-
self._traces_p[counter,
|
|
171
|
-
self._traces_l[counter,
|
|
178
|
+
self._traces_p[counter, downwind_index] = fdata[FV.TXYH][0, downwind_index]
|
|
179
|
+
self._traces_l[counter, downwind_index] = 0
|
|
172
180
|
|
|
173
181
|
# transport wakes that originate from previous time steps:
|
|
174
182
|
if counter > 0:
|
|
175
|
-
dxyz = self._traces_v[:counter,
|
|
176
|
-
self._traces_p[:counter,
|
|
177
|
-
self._traces_l[:counter,
|
|
183
|
+
dxyz = self._traces_v[:counter, downwind_index] * self._dt[:counter, None]
|
|
184
|
+
self._traces_p[:counter, downwind_index] += dxyz
|
|
185
|
+
self._traces_l[:counter, downwind_index] += np.linalg.norm(dxyz, axis=-1)
|
|
178
186
|
|
|
179
187
|
# compute wind vectors at wake traces:
|
|
180
188
|
# TODO: dz from U_z is missing here
|
|
181
189
|
hpdata = {
|
|
182
|
-
v: np.zeros((1, N), dtype=FC.DTYPE)
|
|
190
|
+
v: np.zeros((1, N, 1), dtype=FC.DTYPE)
|
|
183
191
|
for v in algo.states.output_point_vars(algo)
|
|
184
192
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
193
|
+
hpdims = {v: (FC.STATE, FC.TARGET, FC.TPOINT) for v in hpdata.keys()}
|
|
194
|
+
hpdata = TData.from_points(
|
|
195
|
+
points=self._traces_p[None, :N, downwind_index],
|
|
196
|
+
data=hpdata,
|
|
197
|
+
dims=hpdims,
|
|
198
|
+
)
|
|
189
199
|
res = algo.states.calculate(algo, mdata, fdata, hpdata)
|
|
190
|
-
self._traces_v[:N,
|
|
200
|
+
self._traces_v[:N, downwind_index, :2] = wd2uv(
|
|
201
|
+
res[FV.WD][0, :, 0], res[FV.WS][0, :, 0]
|
|
202
|
+
)
|
|
191
203
|
del hpdata, hpdims, res
|
|
192
204
|
|
|
193
205
|
# project:
|
|
194
|
-
dists = cdist(points[0], self._traces_p[:N,
|
|
206
|
+
dists = cdist(points[0], self._traces_p[:N, downwind_index])
|
|
195
207
|
tri = np.argmin(dists, axis=1)
|
|
196
208
|
del dists
|
|
197
209
|
wcoos = np.full((n_states, n_points, 3), 1e20, dtype=FC.DTYPE)
|
|
198
|
-
wcoos[0, :, 2] = points[0, :, 2] - fdata[FV.TXYH][
|
|
199
|
-
delp = points[0, :, :2] - self._traces_p[tri,
|
|
200
|
-
nx = self._traces_v[tri,
|
|
210
|
+
wcoos[0, :, 2] = points[0, :, 2] - fdata[FV.TXYH][:, downwind_index][0, None, 2]
|
|
211
|
+
delp = points[0, :, :2] - self._traces_p[tri, downwind_index, :2]
|
|
212
|
+
nx = self._traces_v[tri, downwind_index, :2]
|
|
201
213
|
nx /= np.linalg.norm(nx, axis=1)[:, None]
|
|
202
214
|
ny = np.concatenate([-nx[:, 1, None], nx[:, 0, None]], axis=1)
|
|
203
|
-
wcoos[0, :, 0] =
|
|
215
|
+
wcoos[0, :, 0] = (
|
|
216
|
+
np.einsum("pd,pd->p", delp, nx) + self._traces_l[tri, downwind_index]
|
|
217
|
+
)
|
|
204
218
|
wcoos[0, :, 1] = np.einsum("pd,pd->p", delp, ny)
|
|
205
219
|
|
|
206
220
|
# turbines that cause wake:
|
|
207
|
-
|
|
221
|
+
tdata[FC.STATE_SOURCE_ORDERI] = downwind_index
|
|
208
222
|
|
|
209
223
|
# states that cause wake for each target point:
|
|
210
|
-
|
|
224
|
+
tdata.add(
|
|
225
|
+
FC.STATES_SEL,
|
|
226
|
+
tri[None, :].reshape(n_states, n_targets, n_tpoints),
|
|
227
|
+
(FC.STATE, FC.TARGET, FC.TPOINT),
|
|
228
|
+
)
|
|
211
229
|
|
|
212
|
-
return wcoos
|
|
230
|
+
return wcoos.reshape(n_states, n_targets, n_tpoints, 3)
|
|
213
231
|
|
|
214
232
|
def get_wake_modelling_data(
|
|
215
233
|
self,
|
|
216
234
|
algo,
|
|
217
235
|
variable,
|
|
218
|
-
|
|
236
|
+
downwind_index,
|
|
219
237
|
fdata,
|
|
220
|
-
|
|
238
|
+
tdata,
|
|
239
|
+
target,
|
|
221
240
|
states0=None,
|
|
241
|
+
upcast=False,
|
|
222
242
|
):
|
|
223
243
|
"""
|
|
224
244
|
Return data that is required for computing the
|
|
@@ -230,35 +250,65 @@ class SeqDynamicWakes(WakeFrame):
|
|
|
230
250
|
The algorithm, needed for data from previous iteration
|
|
231
251
|
variable: str
|
|
232
252
|
The variable, serves as data key
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
fdata: foxes.core.Data
|
|
253
|
+
downwind_index: int, optional
|
|
254
|
+
The index in the downwind order
|
|
255
|
+
fdata: foxes.core.FData
|
|
237
256
|
The farm data
|
|
238
|
-
|
|
239
|
-
The
|
|
257
|
+
tdata: foxes.core.TData
|
|
258
|
+
The target point data
|
|
259
|
+
target: str, optional
|
|
260
|
+
The dimensions identifier for the output,
|
|
261
|
+
FC.STATE_TARGET, FC.STATE_TARGET_TPOINT
|
|
240
262
|
states0: numpy.ndarray, optional
|
|
241
263
|
The states of wake creation
|
|
264
|
+
upcast: bool
|
|
265
|
+
Flag for ensuring targets dimension,
|
|
266
|
+
otherwise dimension 1 is entered
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
data: numpy.ndarray
|
|
271
|
+
Data for wake modelling, shape:
|
|
272
|
+
(n_states, n_turbines) or (n_states, n_target)
|
|
273
|
+
dims: tuple
|
|
274
|
+
The data dimensions
|
|
242
275
|
|
|
243
276
|
"""
|
|
244
|
-
if states0 is None:
|
|
277
|
+
if states0 is None and FC.STATE_SOURCE_ORDERI in tdata:
|
|
245
278
|
# from previous iteration:
|
|
246
|
-
if
|
|
279
|
+
if downwind_index != tdata[FC.STATE_SOURCE_ORDERI]:
|
|
247
280
|
raise ValueError(
|
|
248
|
-
f"Model '{self.name}': Mismatch of '
|
|
281
|
+
f"Model '{self.name}': Mismatch of '{FC.STATE_SOURCE_ORDERI}'. Expected {tdata[FC.STATE_SOURCE_ORDERI]}, got {downwind_index}"
|
|
249
282
|
)
|
|
250
283
|
|
|
251
|
-
|
|
252
|
-
|
|
284
|
+
n_states = 1
|
|
285
|
+
n_targets = tdata.n_targets
|
|
286
|
+
n_tpoints = tdata.n_tpoints
|
|
287
|
+
n_points = n_targets * n_tpoints
|
|
253
288
|
|
|
254
|
-
|
|
289
|
+
s = tdata[FC.STATES_SEL][0].reshape(n_points)
|
|
290
|
+
data = algo.farm_results[variable].to_numpy()
|
|
291
|
+
data = data[s, downwind_index].reshape(n_states, n_targets, n_tpoints)
|
|
292
|
+
|
|
293
|
+
if target == FC.STATE_TARGET:
|
|
294
|
+
if n_tpoints == 1:
|
|
295
|
+
data = data[:, :, 0]
|
|
296
|
+
else:
|
|
297
|
+
data = np.einsum("stp,p->st", data, tdata[FC.TWEIGHTS])
|
|
298
|
+
return data, (FC.STATE, FC.TARGET)
|
|
299
|
+
elif target == FC.STATE_TARGET_TPOINT:
|
|
300
|
+
return data, (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
301
|
+
else:
|
|
302
|
+
raise ValueError(
|
|
303
|
+
f"Cannot handle target '{target}', choices are {FC.STATE_TARGET}, {FC.STATE_TARGET_TPOINT}"
|
|
304
|
+
)
|
|
255
305
|
|
|
256
306
|
else:
|
|
257
307
|
return super().get_wake_modelling_data(
|
|
258
|
-
algo, variable,
|
|
308
|
+
algo, variable, downwind_index, fdata, tdata, target, states0, upcast
|
|
259
309
|
)
|
|
260
310
|
|
|
261
|
-
def get_centreline_points(self, algo, mdata, fdata,
|
|
311
|
+
def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
|
|
262
312
|
"""
|
|
263
313
|
Gets the points along the centreline for given
|
|
264
314
|
values of x.
|
|
@@ -267,13 +317,12 @@ class SeqDynamicWakes(WakeFrame):
|
|
|
267
317
|
----------
|
|
268
318
|
algo: foxes.core.Algorithm
|
|
269
319
|
The calculation algorithm
|
|
270
|
-
mdata: foxes.core.
|
|
320
|
+
mdata: foxes.core.MData
|
|
271
321
|
The model data
|
|
272
|
-
fdata: foxes.core.
|
|
322
|
+
fdata: foxes.core.FData
|
|
273
323
|
The farm data
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
wake causing turbine. Shape: (n_states,)
|
|
324
|
+
downwind_index: int
|
|
325
|
+
The index in the downwind order
|
|
277
326
|
x: numpy.ndarray
|
|
278
327
|
The wake frame x coordinates, shape: (n_states, n_points)
|
|
279
328
|
|
|
@@ -283,5 +332,4 @@ class SeqDynamicWakes(WakeFrame):
|
|
|
283
332
|
The centreline points, shape: (n_states, n_points, 3)
|
|
284
333
|
|
|
285
334
|
"""
|
|
286
|
-
|
|
287
335
|
raise NotImplementedError
|
|
@@ -3,7 +3,7 @@ from scipy.interpolate import interpn
|
|
|
3
3
|
|
|
4
4
|
from foxes.core import WakeFrame
|
|
5
5
|
from foxes.utils import wd2uv
|
|
6
|
-
from foxes.core.data import
|
|
6
|
+
from foxes.core.data import TData
|
|
7
7
|
import foxes.variables as FV
|
|
8
8
|
import foxes.constants as FC
|
|
9
9
|
|
|
@@ -47,9 +47,11 @@ class Streamlines2D(WakeFrame):
|
|
|
47
47
|
self.cl_ipars = cl_ipars
|
|
48
48
|
|
|
49
49
|
self.DATA = self.var("DATA")
|
|
50
|
+
self.STEPS = self.var("STEPS")
|
|
51
|
+
self.SDAT = self.var("SDAT")
|
|
50
52
|
|
|
51
53
|
def __repr__(self):
|
|
52
|
-
return
|
|
54
|
+
return f"{type(self).__name__}(step={self.step})"
|
|
53
55
|
|
|
54
56
|
def _calc_streamlines(self, algo, mdata, fdata):
|
|
55
57
|
"""
|
|
@@ -80,19 +82,17 @@ class Streamlines2D(WakeFrame):
|
|
|
80
82
|
|
|
81
83
|
# calculate next tangential vector:
|
|
82
84
|
svars = algo.states.output_point_vars(algo)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
v: np.full((n_states, n_turbines), np.nan, dtype=FC.DTYPE)
|
|
85
|
+
tdata = TData.from_points(
|
|
86
|
+
data[:, :, i, :3],
|
|
87
|
+
data={
|
|
88
|
+
v: np.full((n_states, n_turbines, 1), np.nan, dtype=FC.DTYPE)
|
|
88
89
|
for v in svars
|
|
89
|
-
}
|
|
90
|
+
},
|
|
91
|
+
dims={v: (FC.STATE, FC.TARGET, FC.TPOINT) for v in svars},
|
|
90
92
|
)
|
|
91
|
-
|
|
92
|
-
pdata = Data(pdata, pdims, loop_dims=[FC.STATE, FC.POINT])
|
|
93
|
-
data[:, :, i, 3] = algo.states.calculate(algo, mdata, fdata, pdata)[
|
|
93
|
+
data[:, :, i, 3] = algo.states.calculate(algo, mdata, fdata, tdata)[
|
|
94
94
|
FV.WD
|
|
95
|
-
]
|
|
95
|
+
][:, :, 0]
|
|
96
96
|
|
|
97
97
|
sel = np.isnan(data[:, :, i, 3])
|
|
98
98
|
if np.any(sel):
|
|
@@ -108,9 +108,9 @@ class Streamlines2D(WakeFrame):
|
|
|
108
108
|
----------
|
|
109
109
|
algo: foxes.core.Algorithm
|
|
110
110
|
The calculation algorithm
|
|
111
|
-
mdata: foxes.core.
|
|
111
|
+
mdata: foxes.core.MData
|
|
112
112
|
The model data
|
|
113
|
-
fdata: foxes.core.
|
|
113
|
+
fdata: foxes.core.FData
|
|
114
114
|
The farm data
|
|
115
115
|
|
|
116
116
|
Returns
|
|
@@ -125,22 +125,23 @@ class Streamlines2D(WakeFrame):
|
|
|
125
125
|
mdata[self.DATA][:, :, 0, :3] == fdata[FV.TXYH]
|
|
126
126
|
):
|
|
127
127
|
mdata[self.DATA] = self._calc_streamlines(algo, mdata, fdata)
|
|
128
|
+
mdata.dims[self.DATA] = (FC.STATE, FC.TURBINE, self.STEPS, self.SDAT)
|
|
128
129
|
|
|
129
130
|
return mdata[self.DATA]
|
|
130
131
|
|
|
131
|
-
def _calc_coos(self, algo, mdata, fdata,
|
|
132
|
+
def _calc_coos(self, algo, mdata, fdata, targets, downwind_index):
|
|
132
133
|
"""
|
|
133
134
|
Helper function, calculates streamline coordinates
|
|
134
135
|
for given points and given turbine
|
|
135
136
|
"""
|
|
136
137
|
|
|
137
138
|
# prepare:
|
|
138
|
-
n_states =
|
|
139
|
-
n_points =
|
|
140
|
-
|
|
139
|
+
n_states, n_targets, n_tpoints = targets.shape[:3]
|
|
140
|
+
n_points = n_targets * n_tpoints
|
|
141
|
+
points = targets.reshape(n_states, n_points, 3)
|
|
141
142
|
|
|
142
143
|
# find nearest streamline points:
|
|
143
|
-
data = self.get_streamline_data(algo, mdata, fdata)[
|
|
144
|
+
data = self.get_streamline_data(algo, mdata, fdata)[:, downwind_index]
|
|
144
145
|
dists = np.linalg.norm(points[:, :, None, :2] - data[:, None, :, :2], axis=-1)
|
|
145
146
|
selp = np.argmin(dists, axis=2)
|
|
146
147
|
data = np.take_along_axis(data[:, None], selp[:, :, None, None], axis=2)[
|
|
@@ -158,7 +159,7 @@ class Streamlines2D(WakeFrame):
|
|
|
158
159
|
coos[:, :, 1] = np.einsum("spd,spd->sp", delta, ny)
|
|
159
160
|
coos[:, :, 2] = points[:, :, 2] - data[:, :, 2]
|
|
160
161
|
|
|
161
|
-
return coos
|
|
162
|
+
return coos.reshape(n_states, n_targets, n_tpoints, 3)
|
|
162
163
|
|
|
163
164
|
def calc_order(self, algo, mdata, fdata):
|
|
164
165
|
""" "
|
|
@@ -171,9 +172,9 @@ class Streamlines2D(WakeFrame):
|
|
|
171
172
|
----------
|
|
172
173
|
algo: foxes.core.Algorithm
|
|
173
174
|
The calculation algorithm
|
|
174
|
-
mdata: foxes.core.
|
|
175
|
+
mdata: foxes.core.MData
|
|
175
176
|
The model data
|
|
176
|
-
fdata: foxes.core.
|
|
177
|
+
fdata: foxes.core.FData
|
|
177
178
|
The farm data
|
|
178
179
|
|
|
179
180
|
Returns
|
|
@@ -185,15 +186,15 @@ class Streamlines2D(WakeFrame):
|
|
|
185
186
|
# prepare:
|
|
186
187
|
n_states = fdata.n_states
|
|
187
188
|
n_turbines = algo.n_turbines
|
|
188
|
-
|
|
189
|
+
tdata = TData.from_points(points=fdata[FV.TXYH])
|
|
189
190
|
|
|
190
191
|
# calculate streamline x coordinates for turbines rotor centre points:
|
|
191
192
|
# n_states, n_turbines_source, n_turbines_target
|
|
192
193
|
coosx = np.zeros((n_states, n_turbines, n_turbines), dtype=FC.DTYPE)
|
|
193
194
|
for ti in range(n_turbines):
|
|
194
|
-
coosx[:, ti, :] = self.get_wake_coos(
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
coosx[:, ti, :] = self.get_wake_coos(algo, mdata, fdata, tdata, ti)[
|
|
196
|
+
:, :, 0, 0
|
|
197
|
+
]
|
|
197
198
|
|
|
198
199
|
# derive turbine order:
|
|
199
200
|
# TODO: Remove loop over states
|
|
@@ -203,36 +204,41 @@ class Streamlines2D(WakeFrame):
|
|
|
203
204
|
|
|
204
205
|
return order
|
|
205
206
|
|
|
206
|
-
def get_wake_coos(
|
|
207
|
+
def get_wake_coos(
|
|
208
|
+
self,
|
|
209
|
+
algo,
|
|
210
|
+
mdata,
|
|
211
|
+
fdata,
|
|
212
|
+
tdata,
|
|
213
|
+
downwind_index,
|
|
214
|
+
):
|
|
207
215
|
"""
|
|
208
|
-
Calculate wake coordinates.
|
|
216
|
+
Calculate wake coordinates of rotor points.
|
|
209
217
|
|
|
210
218
|
Parameters
|
|
211
219
|
----------
|
|
212
220
|
algo: foxes.core.Algorithm
|
|
213
221
|
The calculation algorithm
|
|
214
|
-
mdata: foxes.core.
|
|
222
|
+
mdata: foxes.core.MData
|
|
215
223
|
The model data
|
|
216
|
-
fdata: foxes.core.
|
|
224
|
+
fdata: foxes.core.FData
|
|
217
225
|
The farm data
|
|
218
|
-
|
|
219
|
-
The
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
226
|
+
tdata: foxes.core.TData
|
|
227
|
+
The target point data
|
|
228
|
+
downwind_index: int
|
|
229
|
+
The index of the wake causing turbine
|
|
230
|
+
in the downwnd order
|
|
223
231
|
|
|
224
232
|
Returns
|
|
225
233
|
-------
|
|
226
234
|
wake_coos: numpy.ndarray
|
|
227
235
|
The wake frame coordinates of the evaluation
|
|
228
|
-
points, shape: (n_states,
|
|
236
|
+
points, shape: (n_states, n_targets, n_tpoints, 3)
|
|
229
237
|
|
|
230
238
|
"""
|
|
231
|
-
return self._calc_coos(
|
|
232
|
-
algo, mdata, fdata, pdata[FC.POINTS], states_source_turbine
|
|
233
|
-
)
|
|
239
|
+
return self._calc_coos(algo, mdata, fdata, tdata[FC.TARGETS], downwind_index)
|
|
234
240
|
|
|
235
|
-
def get_centreline_points(self, algo, mdata, fdata,
|
|
241
|
+
def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
|
|
236
242
|
"""
|
|
237
243
|
Gets the points along the centreline for given
|
|
238
244
|
values of x.
|
|
@@ -241,13 +247,12 @@ class Streamlines2D(WakeFrame):
|
|
|
241
247
|
----------
|
|
242
248
|
algo: foxes.core.Algorithm
|
|
243
249
|
The calculation algorithm
|
|
244
|
-
mdata: foxes.core.
|
|
250
|
+
mdata: foxes.core.MData
|
|
245
251
|
The model data
|
|
246
|
-
fdata: foxes.core.
|
|
252
|
+
fdata: foxes.core.FData
|
|
247
253
|
The farm data
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
wake causing turbine. Shape: (n_states,)
|
|
254
|
+
downwind_index: int
|
|
255
|
+
The index in the downwind order
|
|
251
256
|
x: numpy.ndarray
|
|
252
257
|
The wake frame x coordinates, shape: (n_states, n_points)
|
|
253
258
|
|
|
@@ -263,8 +268,7 @@ class Streamlines2D(WakeFrame):
|
|
|
263
268
|
|
|
264
269
|
# get streamline points:
|
|
265
270
|
n_states, n_points = x.shape
|
|
266
|
-
|
|
267
|
-
data = self.get_streamline_data(algo, mdata, fdata)[st_sel]
|
|
271
|
+
data = self.get_streamline_data(algo, mdata, fdata)[:, downwind_index]
|
|
268
272
|
spts = data[:, :, :3]
|
|
269
273
|
n_spts = spts.shape[1]
|
|
270
274
|
xs = self.step * np.arange(n_spts)
|