foxes 1.3__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 +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 +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 +21 -17
- foxes/algorithms/__init__.py +6 -6
- foxes/algorithms/downwind/__init__.py +2 -2
- foxes/algorithms/downwind/downwind.py +44 -12
- 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 +0 -1
- foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
- 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 +0 -1
- foxes/core/data.py +19 -18
- foxes/core/engine.py +9 -13
- foxes/core/farm_controller.py +2 -2
- 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 +3 -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/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 -12
- foxes/input/states/create/__init__.py +3 -2
- foxes/input/states/field_data_nc.py +10 -24
- foxes/input/states/multi_height.py +9 -6
- foxes/input/states/one_point_flow.py +0 -4
- foxes/input/states/single.py +1 -1
- foxes/input/states/states_table.py +10 -7
- foxes/input/states/weibull_sectors.py +225 -0
- foxes/input/states/wrg_states.py +7 -5
- 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 +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 +3 -3
- 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 +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 +2 -2
- foxes/output/farm_results_eval.py +15 -15
- foxes/output/flow_plots_2d/__init__.py +2 -2
- foxes/output/flow_plots_2d/get_fig.py +4 -2
- foxes/output/rose_plot.py +3 -3
- 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/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.3.dist-info → foxes-1.4.dist-info}/METADATA +6 -15
- foxes-1.4.dist-info/RECORD +320 -0
- {foxes-1.3.dist-info → foxes-1.4.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/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.4.dist-info}/entry_points.txt +0 -0
- {foxes-1.3.dist-info → foxes-1.4.dist-info/licenses}/LICENSE +0 -0
- {foxes-1.3.dist-info → foxes-1.4.dist-info}/top_level.txt +0 -0
foxes/core/data.py
CHANGED
|
@@ -28,9 +28,9 @@ class Data(Dict):
|
|
|
28
28
|
|
|
29
29
|
def __init__(
|
|
30
30
|
self,
|
|
31
|
-
data,
|
|
32
|
-
dims,
|
|
33
|
-
loop_dims,
|
|
31
|
+
data={},
|
|
32
|
+
dims={},
|
|
33
|
+
loop_dims=[FC.STATE],
|
|
34
34
|
states_i0=None,
|
|
35
35
|
name="data",
|
|
36
36
|
):
|
|
@@ -153,13 +153,13 @@ class Data(Dict):
|
|
|
153
153
|
# remove axes of size 1, added by dask for extra loop dimensions:
|
|
154
154
|
if dims is not None:
|
|
155
155
|
if len(dims) != len(data.shape):
|
|
156
|
-
for li,
|
|
157
|
-
if data.shape[li] == 1 and (len(dims) < li + 1 or dims[li] !=
|
|
156
|
+
for li, ld in enumerate(self.loop_dims):
|
|
157
|
+
if data.shape[li] == 1 and (len(dims) < li + 1 or dims[li] != ld):
|
|
158
158
|
self[name] = np.squeeze(data, axis=li)
|
|
159
159
|
for ci, c in enumerate(dims):
|
|
160
160
|
if c not in self.sizes or self.sizes[c] == 1:
|
|
161
161
|
self.sizes[c] = self[name].shape[ci]
|
|
162
|
-
elif self[name].shape[ci] == 1:
|
|
162
|
+
elif c != FC.TARGET and self[name].shape[ci] == 1:
|
|
163
163
|
pass
|
|
164
164
|
elif self.sizes[c] != self[name].shape[ci]:
|
|
165
165
|
raise ValueError(
|
|
@@ -240,9 +240,14 @@ class Data(Dict):
|
|
|
240
240
|
)
|
|
241
241
|
else:
|
|
242
242
|
states_i0 = None
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
)
|
|
243
|
+
|
|
244
|
+
cls = type(self)
|
|
245
|
+
if issubclass(cls, Data):
|
|
246
|
+
return cls(data, dims, name=name, states_i0=states_i0)
|
|
247
|
+
else:
|
|
248
|
+
return cls(
|
|
249
|
+
data, dims, loop_dims=self.loop_dims, name=name, states_i0=states_i0
|
|
250
|
+
)
|
|
246
251
|
|
|
247
252
|
@classmethod
|
|
248
253
|
def from_dataset(cls, ds, *args, callback=None, s_states=None, copy=True, **kwargs):
|
|
@@ -360,7 +365,7 @@ class FData(Data):
|
|
|
360
365
|
Arguments for the base class
|
|
361
366
|
|
|
362
367
|
"""
|
|
363
|
-
super().__init__(*args, name=name, **kwargs)
|
|
368
|
+
super().__init__(*args, loop_dims=[FC.STATE], name=name, **kwargs)
|
|
364
369
|
|
|
365
370
|
def _run_entry_checks(self, name, data, dims):
|
|
366
371
|
"""Run entry checks on new data"""
|
|
@@ -453,7 +458,7 @@ class TData(Data):
|
|
|
453
458
|
Arguments for the base class
|
|
454
459
|
|
|
455
460
|
"""
|
|
456
|
-
super().__init__(*args, name=name, **kwargs)
|
|
461
|
+
super().__init__(*args, loop_dims=[FC.STATE, FC.TARGET], name=name, **kwargs)
|
|
457
462
|
|
|
458
463
|
def _run_entry_checks(self, name, data, dims):
|
|
459
464
|
"""Run entry checks on new data"""
|
|
@@ -635,9 +640,7 @@ class TData(Data):
|
|
|
635
640
|
for v in variables:
|
|
636
641
|
data[v] = np.full_like(points[:, :, None, 0], np.nan)
|
|
637
642
|
dims[v] = (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
638
|
-
return cls(
|
|
639
|
-
data=data, dims=dims, loop_dims=[FC.STATE, FC.TARGET], name=name, **kwargs
|
|
640
|
-
)
|
|
643
|
+
return cls(data=data, dims=dims, name=name, **kwargs)
|
|
641
644
|
|
|
642
645
|
@classmethod
|
|
643
646
|
def from_tpoints(
|
|
@@ -694,9 +697,7 @@ class TData(Data):
|
|
|
694
697
|
for v in variables:
|
|
695
698
|
data[v] = np.full_like(tpoints[..., 0], np.nan)
|
|
696
699
|
dims[v] = (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
697
|
-
return cls(
|
|
698
|
-
data=data, dims=dims, loop_dims=[FC.STATE, FC.TARGET], name=name, **kwargs
|
|
699
|
-
)
|
|
700
|
+
return cls(data=data, dims=dims, name=name, **kwargs)
|
|
700
701
|
|
|
701
702
|
@classmethod
|
|
702
703
|
def from_dataset(
|
|
@@ -764,7 +765,7 @@ class TData(Data):
|
|
|
764
765
|
FC.TPOINT,
|
|
765
766
|
):
|
|
766
767
|
raise ValueError(
|
|
767
|
-
f"Expecting coordinates '{
|
|
768
|
+
f"Expecting coordinates '{(FC.STATE, FC.TARGET, FC.TPOINT)}' at positions 0-2 for data variable '{v}', got {dims[v]}"
|
|
768
769
|
)
|
|
769
770
|
else:
|
|
770
771
|
data[v] = d[:, s_targets]
|
foxes/core/engine.py
CHANGED
|
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
|
|
|
4
4
|
from tqdm import tqdm
|
|
5
5
|
from xarray import Dataset
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from .data import MData, FData, TData
|
|
8
8
|
from foxes.utils import new_instance
|
|
9
9
|
from foxes.config import config
|
|
10
10
|
import foxes.constants as FC
|
|
@@ -73,7 +73,7 @@ class Engine(ABC):
|
|
|
73
73
|
|
|
74
74
|
def __enter__(self):
|
|
75
75
|
if self.__entered:
|
|
76
|
-
raise ValueError(
|
|
76
|
+
raise ValueError("Enter called for already entered engine")
|
|
77
77
|
self.__entered = True
|
|
78
78
|
if not self.initialized:
|
|
79
79
|
self.initialize()
|
|
@@ -81,7 +81,7 @@ class Engine(ABC):
|
|
|
81
81
|
|
|
82
82
|
def __exit__(self, *exit_args):
|
|
83
83
|
if not self.__entered:
|
|
84
|
-
raise ValueError(
|
|
84
|
+
raise ValueError("Exit called for not entered engine")
|
|
85
85
|
self.__entered = False
|
|
86
86
|
if self.initialized:
|
|
87
87
|
self.finalize(*exit_args)
|
|
@@ -127,7 +127,6 @@ class Engine(ABC):
|
|
|
127
127
|
raise ValueError(
|
|
128
128
|
f"Cannot initialize engine '{type(self).__name__}', since engine already set to '{type(get_engine()).__name__}'"
|
|
129
129
|
)
|
|
130
|
-
global __global_engine_data__
|
|
131
130
|
__global_engine_data__["engine"] = self
|
|
132
131
|
self.__initialized = True
|
|
133
132
|
|
|
@@ -148,7 +147,6 @@ class Engine(ABC):
|
|
|
148
147
|
if self.entered:
|
|
149
148
|
self.__exit__(type, value, traceback)
|
|
150
149
|
elif self.initialized:
|
|
151
|
-
global __global_engine_data__
|
|
152
150
|
__global_engine_data__["engine"] = None
|
|
153
151
|
self.__initialized = False
|
|
154
152
|
|
|
@@ -304,9 +302,9 @@ class Engine(ABC):
|
|
|
304
302
|
chunk_sizes_targets[-extra:] += 1
|
|
305
303
|
|
|
306
304
|
s = np.sum(chunk_sizes_targets)
|
|
307
|
-
assert (
|
|
308
|
-
s
|
|
309
|
-
)
|
|
305
|
+
assert s == n_targets, (
|
|
306
|
+
f"Targets count mismatch: Expecting {n_targets}, chunks sum is {s}. Chunks: {[int(c) for c in chunk_sizes_targets]}"
|
|
307
|
+
)
|
|
310
308
|
|
|
311
309
|
chunk_sizes_states = np.full(n_chunks_states, chunk_size_states)
|
|
312
310
|
extra = n_states - n_chunks_states * chunk_size_states
|
|
@@ -314,9 +312,9 @@ class Engine(ABC):
|
|
|
314
312
|
chunk_sizes_states[-extra:] += 1
|
|
315
313
|
|
|
316
314
|
s = np.sum(chunk_sizes_states)
|
|
317
|
-
assert (
|
|
318
|
-
s
|
|
319
|
-
)
|
|
315
|
+
assert s == n_states, (
|
|
316
|
+
f"States count mismatch: Expecting {n_states}, chunks sum is {s}. Chunks: {[int(c) for c in chunk_sizes_states]}"
|
|
317
|
+
)
|
|
320
318
|
|
|
321
319
|
return chunk_sizes_states, chunk_sizes_targets
|
|
322
320
|
|
|
@@ -389,7 +387,6 @@ class Engine(ABC):
|
|
|
389
387
|
mdata=mdata,
|
|
390
388
|
s_states=s_states,
|
|
391
389
|
callback=cb,
|
|
392
|
-
loop_dims=[FC.STATE],
|
|
393
390
|
states_i0=i0_states,
|
|
394
391
|
copy=True,
|
|
395
392
|
)
|
|
@@ -413,7 +410,6 @@ class Engine(ABC):
|
|
|
413
410
|
s_states=s_states,
|
|
414
411
|
s_targets=s_targets,
|
|
415
412
|
callback=cb,
|
|
416
|
-
loop_dims=[FC.STATE, FC.TARGET],
|
|
417
413
|
states_i0=i0_states,
|
|
418
414
|
copy=True,
|
|
419
415
|
)
|
foxes/core/farm_controller.py
CHANGED
|
@@ -171,7 +171,7 @@ class FarmController(FarmDataModel):
|
|
|
171
171
|
for ti, t in enumerate(algo.farm.turbines):
|
|
172
172
|
if tmis[ti] != len(models[ti]):
|
|
173
173
|
raise ValueError(
|
|
174
|
-
f"Turbine {ti}, {t.name}: Could not find turbine model order that includes all {mtype} turbine models, missing {t.models[tmis[ti]:]}"
|
|
174
|
+
f"Turbine {ti}, {t.name}: Could not find turbine model order that includes all {mtype} turbine models, missing {t.models[tmis[ti] :]}"
|
|
175
175
|
)
|
|
176
176
|
|
|
177
177
|
return [m.name for m in tmodels], tmsels
|
|
@@ -253,7 +253,7 @@ class FarmController(FarmDataModel):
|
|
|
253
253
|
prer = m.pre_rotor
|
|
254
254
|
elif not prer and m.pre_rotor:
|
|
255
255
|
raise ValueError(
|
|
256
|
-
f"Turbine {ti}, {t.name}: Model is classified as pre-rotor, but following the post-rotor model '{t.models[mi-1]}'"
|
|
256
|
+
f"Turbine {ti}, {t.name}: Model is classified as pre-rotor, but following the post-rotor model '{t.models[mi - 1]}'"
|
|
257
257
|
)
|
|
258
258
|
if m.pre_rotor:
|
|
259
259
|
prer_models[ti].append(m)
|
foxes/core/ground_model.py
CHANGED
|
@@ -91,7 +91,6 @@ class GroundModel(Model):
|
|
|
91
91
|
mdata,
|
|
92
92
|
fdata,
|
|
93
93
|
tdata,
|
|
94
|
-
amb_res,
|
|
95
94
|
rpoint_weights,
|
|
96
95
|
wake_deltas,
|
|
97
96
|
wmodel,
|
|
@@ -114,11 +113,6 @@ class GroundModel(Model):
|
|
|
114
113
|
The farm data
|
|
115
114
|
tdata: foxes.core.Data
|
|
116
115
|
The target point data
|
|
117
|
-
amb_res: dict
|
|
118
|
-
The ambient results at the target points
|
|
119
|
-
of all rotors. Key: variable name, value
|
|
120
|
-
np.ndarray of shape:
|
|
121
|
-
(n_states, n_turbines, n_rotor_points)
|
|
122
116
|
rpoint_weights: numpy.ndarray
|
|
123
117
|
The rotor point weights, shape: (n_rotor_points,)
|
|
124
118
|
wake_deltas: dict
|
|
@@ -143,7 +137,6 @@ class GroundModel(Model):
|
|
|
143
137
|
mdata,
|
|
144
138
|
fdata,
|
|
145
139
|
tdata,
|
|
146
|
-
amb_res,
|
|
147
140
|
rpoint_weights,
|
|
148
141
|
wake_deltas,
|
|
149
142
|
wmodel,
|
|
@@ -226,7 +219,7 @@ class GroundModel(Model):
|
|
|
226
219
|
algo,
|
|
227
220
|
mdata,
|
|
228
221
|
fdata,
|
|
229
|
-
|
|
222
|
+
tdata,
|
|
230
223
|
wake_deltas,
|
|
231
224
|
wmodel,
|
|
232
225
|
):
|
|
@@ -243,17 +236,15 @@ class GroundModel(Model):
|
|
|
243
236
|
The model data
|
|
244
237
|
fdata: foxes.core.FData
|
|
245
238
|
The farm data
|
|
246
|
-
|
|
247
|
-
The
|
|
248
|
-
values: numpy.ndarray with shape
|
|
249
|
-
(n_states, n_targets, n_tpoints)
|
|
239
|
+
tdata: foxes.core.TData
|
|
240
|
+
The target point data
|
|
250
241
|
wake_deltas: dict
|
|
251
242
|
The wake deltas object at the selected target
|
|
252
243
|
turbines. Key: variable str, value: numpy.ndarray
|
|
253
244
|
with shape (n_states, n_targets, n_tpoints)
|
|
254
245
|
|
|
255
246
|
"""
|
|
256
|
-
wmodel.finalize_wake_deltas(algo, mdata, fdata,
|
|
247
|
+
wmodel.finalize_wake_deltas(algo, mdata, fdata, tdata, wake_deltas)
|
|
257
248
|
|
|
258
249
|
@classmethod
|
|
259
250
|
def new(cls, ground_type, *args, **kwargs):
|
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,9 +60,9 @@ class RotorModel(FarmDataModel):
|
|
|
60
60
|
"""
|
|
61
61
|
if self.calc_vars is None:
|
|
62
62
|
vrs = algo.states.output_point_vars(algo)
|
|
63
|
-
assert (
|
|
64
|
-
FV.WEIGHT
|
|
65
|
-
)
|
|
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
66
|
|
|
67
67
|
if FV.WS in vrs:
|
|
68
68
|
self.calc_vars = [FV.REWS] + [v for v in vrs if v != FV.WS]
|
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
|
|
|
@@ -114,7 +113,7 @@ class States(PointDataModel):
|
|
|
114
113
|
elif isinstance(s, ExtendedStates):
|
|
115
114
|
if s.states is not self:
|
|
116
115
|
raise ValueError(
|
|
117
|
-
|
|
116
|
+
"Cannot add extended states, since not based on same states"
|
|
118
117
|
)
|
|
119
118
|
return ExtendedStates(self, s.pmodels.models[1:])
|
|
120
119
|
else:
|
|
@@ -268,7 +267,7 @@ class ExtendedStates(States):
|
|
|
268
267
|
elif isinstance(m, ExtendedStates):
|
|
269
268
|
if m.states is not self.states:
|
|
270
269
|
raise ValueError(
|
|
271
|
-
|
|
270
|
+
"Cannot add extended states, since not based on same states"
|
|
272
271
|
)
|
|
273
272
|
return ExtendedStates(self.states, models + m.pmodels.models[1:])
|
|
274
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)
|