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
|
@@ -30,7 +30,6 @@ class SeqState(States):
|
|
|
30
30
|
|
|
31
31
|
# updated by SequentialIter:
|
|
32
32
|
self._size = states.size()
|
|
33
|
-
self._weight = None
|
|
34
33
|
self._indx = None
|
|
35
34
|
self._counter = None
|
|
36
35
|
|
|
@@ -100,23 +99,6 @@ class SeqState(States):
|
|
|
100
99
|
"""
|
|
101
100
|
return self._counter
|
|
102
101
|
|
|
103
|
-
def weights(self, algo):
|
|
104
|
-
"""
|
|
105
|
-
The statistical weights of all states.
|
|
106
|
-
|
|
107
|
-
Parameters
|
|
108
|
-
----------
|
|
109
|
-
algo: foxes.core.Algorithm
|
|
110
|
-
The calculation algorithm
|
|
111
|
-
|
|
112
|
-
Returns
|
|
113
|
-
-------
|
|
114
|
-
weights: numpy.ndarray
|
|
115
|
-
The weights, shape: (n_states, n_turbines)
|
|
116
|
-
|
|
117
|
-
"""
|
|
118
|
-
return self._weight[None, :] if self._size == 1 else self.states.weights(algo)
|
|
119
|
-
|
|
120
102
|
def output_point_vars(self, algo):
|
|
121
103
|
"""
|
|
122
104
|
The variables which are being modified by the model.
|
|
@@ -139,7 +139,6 @@ class Sequential(Iterative):
|
|
|
139
139
|
self.print_deco("calc_farm")
|
|
140
140
|
|
|
141
141
|
self._inds = self.states0.index()
|
|
142
|
-
self._weights = self.states0.weights(self)
|
|
143
142
|
self._i = 0
|
|
144
143
|
self._counter = 0
|
|
145
144
|
|
|
@@ -155,15 +154,18 @@ class Sequential(Iterative):
|
|
|
155
154
|
if self._verbo0 > 0:
|
|
156
155
|
print("\nInput data:\n")
|
|
157
156
|
print(self._model_data)
|
|
158
|
-
print(
|
|
157
|
+
print("\nOutput farm variables:", ", ".join(self.farm_vars))
|
|
159
158
|
print()
|
|
160
159
|
|
|
160
|
+
sts = self._model_data[FC.STATE].to_numpy()
|
|
161
161
|
self._farm_results = Dataset(
|
|
162
|
-
coords={FC.STATE:
|
|
162
|
+
coords={FC.STATE: sts},
|
|
163
163
|
data_vars={
|
|
164
164
|
v: (
|
|
165
165
|
(FC.STATE, FC.TURBINE),
|
|
166
|
-
np.
|
|
166
|
+
np.zeros(
|
|
167
|
+
(len(sts), self.n_turbines), dtype=config.dtype_double
|
|
168
|
+
),
|
|
167
169
|
)
|
|
168
170
|
for v in self.farm_vars
|
|
169
171
|
},
|
|
@@ -186,12 +188,10 @@ class Sequential(Iterative):
|
|
|
186
188
|
"""Run calculation for current step, then iterate to next"""
|
|
187
189
|
|
|
188
190
|
if self._i < len(self._inds):
|
|
189
|
-
|
|
190
191
|
self._counter = self._i
|
|
191
192
|
self.states._counter = self._i
|
|
192
193
|
self.states._size = 1
|
|
193
194
|
self.states._indx = self._inds[self._i]
|
|
194
|
-
self.states._weight = self._weights[self._i]
|
|
195
195
|
|
|
196
196
|
if self._verbo0 > 0:
|
|
197
197
|
print(f"{self.name}: Running state {self.states.index()[0]}")
|
|
@@ -257,7 +257,6 @@ class Sequential(Iterative):
|
|
|
257
257
|
self.states._counter = None
|
|
258
258
|
self.states._size = len(self._inds)
|
|
259
259
|
self.states._indx = self._inds
|
|
260
|
-
self.states._weight = self._weights
|
|
261
260
|
|
|
262
261
|
for p in self.plugins:
|
|
263
262
|
p.finalize(self)
|
|
@@ -323,19 +322,6 @@ class Sequential(Iterative):
|
|
|
323
322
|
"""
|
|
324
323
|
return self.counter if counter else self.index
|
|
325
324
|
|
|
326
|
-
@property
|
|
327
|
-
def weight(self):
|
|
328
|
-
"""
|
|
329
|
-
The current weight array
|
|
330
|
-
|
|
331
|
-
Returns
|
|
332
|
-
-------
|
|
333
|
-
w: numpy.ndarray
|
|
334
|
-
The current weight array, shape: (n_turbines,)
|
|
335
|
-
|
|
336
|
-
"""
|
|
337
|
-
return self.states._weight if self.iterating else None
|
|
338
|
-
|
|
339
325
|
@property
|
|
340
326
|
def farm_results(self):
|
|
341
327
|
"""
|
|
@@ -414,7 +400,7 @@ class Sequential(Iterative):
|
|
|
414
400
|
|
|
415
401
|
"""
|
|
416
402
|
if not self.iterating:
|
|
417
|
-
raise ValueError(
|
|
403
|
+
raise ValueError("calc_farm call is only allowed during iterations")
|
|
418
404
|
return self.cur_farm_results
|
|
419
405
|
|
|
420
406
|
def calc_points(
|
|
@@ -446,6 +432,6 @@ class Sequential(Iterative):
|
|
|
446
432
|
|
|
447
433
|
"""
|
|
448
434
|
if not self.iterating:
|
|
449
|
-
raise ValueError(
|
|
435
|
+
raise ValueError("calc_points call is only allowed during iterations")
|
|
450
436
|
|
|
451
437
|
return super().calc_points(farm_results, points, finalize=False, **kwargs)
|
foxes/config/__init__.py
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
from .config import Config
|
|
1
|
+
from .config import Config as Config
|
|
2
|
+
from .config import config as config
|
|
3
|
+
from .config import get_path as get_path
|
|
4
|
+
from .config import get_input_path as get_input_path
|
|
5
|
+
from .config import get_output_path as get_output_path
|
foxes/constants.py
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
XY = "xy"
|
|
2
|
+
""" The vector (x, y)
|
|
3
|
+
:group: foxes.variables
|
|
4
|
+
"""
|
|
5
|
+
|
|
1
6
|
XYH = "xyh"
|
|
2
7
|
""" The vector (x, y, height)
|
|
3
8
|
:group: foxes.variables
|
|
@@ -73,11 +78,17 @@ ROTOR_WEIGHTS = "rotor_weights"
|
|
|
73
78
|
""" Identifier for rotor point weights
|
|
74
79
|
:group: foxes.constants
|
|
75
80
|
"""
|
|
81
|
+
|
|
76
82
|
AMB_ROTOR_RES = "amb_rotor_res"
|
|
77
83
|
""" Identifier for ambient rotor point results
|
|
78
84
|
:group: foxes.constants
|
|
79
85
|
"""
|
|
80
86
|
|
|
87
|
+
WEIGHT_RES = "weight_res"
|
|
88
|
+
""" Identifier for weights results at rotor points
|
|
89
|
+
:group: foxes.constants
|
|
90
|
+
"""
|
|
91
|
+
|
|
81
92
|
|
|
82
93
|
VARS = "vars"
|
|
83
94
|
""" Variables identifier
|
|
@@ -140,6 +151,17 @@ BLOCK_CONVERGENCE = "block_convergence"
|
|
|
140
151
|
"""
|
|
141
152
|
|
|
142
153
|
|
|
154
|
+
WDEFL_ROT_ANGLE = "wake_deflection_rotation_angle"
|
|
155
|
+
"""Identifier for the wake deflection rotation angle data
|
|
156
|
+
:group: foxes.constants
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
WDEFL_DWS_FACTOR = "wake_deflection_deltaws_factor"
|
|
160
|
+
"""Identifier for the wake deflection delta wind speed factor data
|
|
161
|
+
:group: foxes.constants
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
|
|
143
165
|
KAPPA = 0.41
|
|
144
166
|
""" The Von Karman constant
|
|
145
167
|
:group: foxes.constants
|
foxes/core/__init__.py
CHANGED
|
@@ -2,25 +2,48 @@
|
|
|
2
2
|
Abstract classes and core functionality.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from .
|
|
6
|
-
from .
|
|
7
|
-
from .
|
|
8
|
-
from .
|
|
9
|
-
from .
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
17
|
-
from .
|
|
18
|
-
from .
|
|
19
|
-
from .
|
|
20
|
-
from .
|
|
21
|
-
|
|
22
|
-
from .
|
|
23
|
-
from .
|
|
24
|
-
from .
|
|
25
|
-
from .
|
|
26
|
-
|
|
5
|
+
from .model import Model as Model
|
|
6
|
+
from .data_calc_model import DataCalcModel as DataCalcModel
|
|
7
|
+
from .wind_farm import WindFarm as WindFarm
|
|
8
|
+
from .algorithm import Algorithm as Algorithm
|
|
9
|
+
from .rotor_model import RotorModel as RotorModel
|
|
10
|
+
from .farm_model import FarmModel as FarmModel
|
|
11
|
+
from .turbine_model import TurbineModel as TurbineModel
|
|
12
|
+
from .turbine_type import TurbineType as TurbineType
|
|
13
|
+
from .farm_controller import FarmController as FarmController
|
|
14
|
+
from .turbine import Turbine as Turbine
|
|
15
|
+
from .partial_wakes_model import PartialWakesModel as PartialWakesModel
|
|
16
|
+
from .wake_frame import WakeFrame as WakeFrame
|
|
17
|
+
from .wake_deflection import WakeDeflection as WakeDeflection
|
|
18
|
+
from .vertical_profile import VerticalProfile as VerticalProfile
|
|
19
|
+
from .axial_induction_model import AxialInductionModel as AxialInductionModel
|
|
20
|
+
from .ground_model import GroundModel as GroundModel
|
|
21
|
+
|
|
22
|
+
from .data import Data as Data
|
|
23
|
+
from .data import MData as MData
|
|
24
|
+
from .data import FData as FData
|
|
25
|
+
from .data import TData as TData
|
|
26
|
+
|
|
27
|
+
from .engine import Engine as Engine
|
|
28
|
+
from .engine import get_engine as get_engine
|
|
29
|
+
from .engine import has_engine as has_engine
|
|
30
|
+
from .engine import reset_engine as reset_engine
|
|
31
|
+
|
|
32
|
+
from .states import States as States
|
|
33
|
+
from .states import ExtendedStates as ExtendedStates
|
|
34
|
+
|
|
35
|
+
from .farm_data_model import FarmDataModel as FarmDataModel
|
|
36
|
+
from .farm_data_model import FarmDataModelList as FarmDataModelList
|
|
37
|
+
|
|
38
|
+
from .point_data_model import PointDataModel as PointDataModel
|
|
39
|
+
from .point_data_model import PointDataModelList as PointDataModelList
|
|
40
|
+
|
|
41
|
+
from .wake_model import WakeModel as WakeModel
|
|
42
|
+
from .wake_model import SingleTurbineWakeModel as SingleTurbineWakeModel
|
|
43
|
+
from .wake_model import TurbineInductionModel as TurbineInductionModel
|
|
44
|
+
from .wake_model import WakeK as WakeK
|
|
45
|
+
|
|
46
|
+
from .wake_superposition import WakeSuperposition as WakeSuperposition
|
|
47
|
+
from .wake_superposition import (
|
|
48
|
+
WindVectorWakeSuperposition as WindVectorWakeSuperposition,
|
|
49
|
+
)
|
foxes/core/algorithm.py
CHANGED
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,14 @@ 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
|
-
|
|
160
159
|
for ci, c in enumerate(dims):
|
|
161
|
-
if c not in self.sizes:
|
|
160
|
+
if c not in self.sizes or self.sizes[c] == 1:
|
|
162
161
|
self.sizes[c] = self[name].shape[ci]
|
|
162
|
+
elif c != FC.TARGET and self[name].shape[ci] == 1:
|
|
163
|
+
pass
|
|
163
164
|
elif self.sizes[c] != self[name].shape[ci]:
|
|
164
165
|
raise ValueError(
|
|
165
166
|
f"Inconsistent size for data entry '{name}', dimension '{c}': Expecting {self.sizes[c]}, found {self[name].shape[ci]} in shape {self[name].shape}"
|
|
@@ -239,9 +240,14 @@ class Data(Dict):
|
|
|
239
240
|
)
|
|
240
241
|
else:
|
|
241
242
|
states_i0 = None
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
)
|
|
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
|
+
)
|
|
245
251
|
|
|
246
252
|
@classmethod
|
|
247
253
|
def from_dataset(cls, ds, *args, callback=None, s_states=None, copy=True, **kwargs):
|
|
@@ -288,12 +294,16 @@ class Data(Dict):
|
|
|
288
294
|
raise ValueError(
|
|
289
295
|
f"Expecting coordinate '{FC.STATE}' at position 0 for data variable '{v}', got {d.dims}"
|
|
290
296
|
)
|
|
291
|
-
n_states =
|
|
297
|
+
n_states = d.shape[0]
|
|
292
298
|
s = np.s_[:] if s_states is None else s_states
|
|
293
299
|
data[v] = d.to_numpy()[s].copy() if copy else d.to_numpy()[s]
|
|
300
|
+
dims[v] = d.dims
|
|
301
|
+
if v == FV.WEIGHT and d.dims == (FC.STATE,):
|
|
302
|
+
data[v] = data[v][:, None]
|
|
303
|
+
dims[v] = (FC.STATE, FC.TURBINE)
|
|
294
304
|
else:
|
|
295
305
|
data[v] = d.to_numpy().copy() if copy else d.to_numpy()
|
|
296
|
-
|
|
306
|
+
dims[v] = d.dims
|
|
297
307
|
|
|
298
308
|
if callback is not None:
|
|
299
309
|
callback(data, dims)
|
|
@@ -355,15 +365,14 @@ class FData(Data):
|
|
|
355
365
|
Arguments for the base class
|
|
356
366
|
|
|
357
367
|
"""
|
|
358
|
-
super().__init__(*args, name=name, **kwargs)
|
|
368
|
+
super().__init__(*args, loop_dims=[FC.STATE], name=name, **kwargs)
|
|
359
369
|
|
|
360
370
|
def _run_entry_checks(self, name, data, dims):
|
|
361
371
|
"""Run entry checks on new data"""
|
|
362
372
|
super()._run_entry_checks(name, data, dims)
|
|
363
373
|
data = self[name]
|
|
364
374
|
dims = self.dims[name]
|
|
365
|
-
|
|
366
|
-
if name not in self.sizes and name not in FC.TNAME:
|
|
375
|
+
if name not in self.sizes and name not in [FC.TNAME, FV.WEIGHT]:
|
|
367
376
|
dms = (FC.STATE, FC.TURBINE)
|
|
368
377
|
shp = (self.n_states, self.n_turbines)
|
|
369
378
|
if len(data.shape) < 2:
|
|
@@ -418,8 +427,6 @@ class FData(Data):
|
|
|
418
427
|
if FC.STATE not in data:
|
|
419
428
|
data[FC.STATE] = mdata[FC.STATE]
|
|
420
429
|
dims[FC.STATE] = mdata.dims[FC.STATE]
|
|
421
|
-
data[FV.WEIGHT] = mdata[FV.WEIGHT]
|
|
422
|
-
dims[FV.WEIGHT] = mdata.dims[FV.WEIGHT]
|
|
423
430
|
if callback is not None:
|
|
424
431
|
callback(data, dims)
|
|
425
432
|
|
|
@@ -451,7 +458,7 @@ class TData(Data):
|
|
|
451
458
|
Arguments for the base class
|
|
452
459
|
|
|
453
460
|
"""
|
|
454
|
-
super().__init__(*args, name=name, **kwargs)
|
|
461
|
+
super().__init__(*args, loop_dims=[FC.STATE, FC.TARGET], name=name, **kwargs)
|
|
455
462
|
|
|
456
463
|
def _run_entry_checks(self, name, data, dims):
|
|
457
464
|
"""Run entry checks on new data"""
|
|
@@ -587,8 +594,9 @@ class TData(Data):
|
|
|
587
594
|
def from_points(
|
|
588
595
|
cls,
|
|
589
596
|
points,
|
|
590
|
-
data=
|
|
591
|
-
dims=
|
|
597
|
+
data=None,
|
|
598
|
+
dims=None,
|
|
599
|
+
variables=None,
|
|
592
600
|
name="tdata",
|
|
593
601
|
**kwargs,
|
|
594
602
|
):
|
|
@@ -599,11 +607,14 @@ class TData(Data):
|
|
|
599
607
|
----------
|
|
600
608
|
points: np.ndarray
|
|
601
609
|
The points, shape: (n_states, n_points, 3)
|
|
602
|
-
data: dict
|
|
610
|
+
data: dict, optional
|
|
603
611
|
The initial data to be stored
|
|
604
|
-
dims: dict
|
|
612
|
+
dims: dict, optional
|
|
605
613
|
The dimensions tuples, same or subset
|
|
606
614
|
of data keys
|
|
615
|
+
variables: list of str
|
|
616
|
+
Add default empty variables with NaN values
|
|
617
|
+
and shape (n_states, n_targets, n_tpoints)
|
|
607
618
|
name: str
|
|
608
619
|
The data container name
|
|
609
620
|
kwargs: dict, optional
|
|
@@ -619,19 +630,26 @@ class TData(Data):
|
|
|
619
630
|
raise ValueError(
|
|
620
631
|
f"Expecting points shape (n_states, n_points, 3), got {points.shape}"
|
|
621
632
|
)
|
|
633
|
+
data = {} if data is None else data
|
|
634
|
+
dims = {} if dims is None else dims
|
|
622
635
|
data[FC.TARGETS] = points[:, :, None, :]
|
|
623
636
|
dims[FC.TARGETS] = (FC.STATE, FC.TARGET, FC.TPOINT, FC.XYH)
|
|
624
637
|
data[FC.TWEIGHTS] = np.array([1], dtype=config.dtype_double)
|
|
625
638
|
dims[FC.TWEIGHTS] = (FC.TPOINT,)
|
|
626
|
-
|
|
639
|
+
if variables is not None:
|
|
640
|
+
for v in variables:
|
|
641
|
+
data[v] = np.full_like(points[:, :, None, 0], np.nan)
|
|
642
|
+
dims[v] = (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
643
|
+
return cls(data=data, dims=dims, name=name, **kwargs)
|
|
627
644
|
|
|
628
645
|
@classmethod
|
|
629
646
|
def from_tpoints(
|
|
630
647
|
cls,
|
|
631
648
|
tpoints,
|
|
632
649
|
tweights,
|
|
633
|
-
data=
|
|
634
|
-
dims=
|
|
650
|
+
data=None,
|
|
651
|
+
dims=None,
|
|
652
|
+
variables=None,
|
|
635
653
|
name="tdata",
|
|
636
654
|
**kwargs,
|
|
637
655
|
):
|
|
@@ -646,11 +664,14 @@ class TData(Data):
|
|
|
646
664
|
tweights: np.ndarray, optional
|
|
647
665
|
The target point weights, shape:
|
|
648
666
|
(n_tpoints,)
|
|
649
|
-
data: dict
|
|
667
|
+
data: dict, optional
|
|
650
668
|
The initial data to be stored
|
|
651
|
-
dims: dict
|
|
669
|
+
dims: dict, optional
|
|
652
670
|
The dimensions tuples, same or subset
|
|
653
671
|
of data keys
|
|
672
|
+
variables: list of str
|
|
673
|
+
Add default empty variables with NaN values
|
|
674
|
+
and shape (n_states, n_targets, n_tpoints)
|
|
654
675
|
name: str
|
|
655
676
|
The data container name
|
|
656
677
|
kwargs: dict, optional
|
|
@@ -666,11 +687,17 @@ class TData(Data):
|
|
|
666
687
|
raise ValueError(
|
|
667
688
|
f"Expecting tpoints shape (n_states, n_targets, n_tpoints, 3), got {tpoints.shape}"
|
|
668
689
|
)
|
|
690
|
+
data = {} if data is None else data
|
|
691
|
+
dims = {} if dims is None else dims
|
|
669
692
|
data[FC.TARGETS] = tpoints
|
|
670
693
|
dims[FC.TARGETS] = (FC.STATE, FC.TARGET, FC.TPOINT, FC.XYH)
|
|
671
694
|
data[FC.TWEIGHTS] = tweights
|
|
672
695
|
dims[FC.TWEIGHTS] = (FC.TPOINT,)
|
|
673
|
-
|
|
696
|
+
if variables is not None:
|
|
697
|
+
for v in variables:
|
|
698
|
+
data[v] = np.full_like(tpoints[..., 0], np.nan)
|
|
699
|
+
dims[v] = (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
700
|
+
return cls(data=data, dims=dims, name=name, **kwargs)
|
|
674
701
|
|
|
675
702
|
@classmethod
|
|
676
703
|
def from_dataset(
|
|
@@ -738,7 +765,7 @@ class TData(Data):
|
|
|
738
765
|
FC.TPOINT,
|
|
739
766
|
):
|
|
740
767
|
raise ValueError(
|
|
741
|
-
f"Expecting coordinates '{
|
|
768
|
+
f"Expecting coordinates '{(FC.STATE, FC.TARGET, FC.TPOINT)}' at positions 0-2 for data variable '{v}', got {dims[v]}"
|
|
742
769
|
)
|
|
743
770
|
else:
|
|
744
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
|
)
|
|
@@ -515,9 +511,27 @@ class Engine(ABC):
|
|
|
515
511
|
if FC.STATE in out_coords and FC.STATE in model_data.coords:
|
|
516
512
|
coords[FC.STATE] = model_data[FC.STATE].to_numpy()
|
|
517
513
|
|
|
514
|
+
# reducing weights dimensions:
|
|
515
|
+
dvars = {}
|
|
516
|
+
for v, (dims, d) in data_vars.items():
|
|
517
|
+
if (
|
|
518
|
+
dims == (FC.STATE, FC.TURBINE)
|
|
519
|
+
and d.shape[1] == 1
|
|
520
|
+
and algo.n_turbines > 1
|
|
521
|
+
):
|
|
522
|
+
dvars[v] = ((FC.STATE,), d[:, 0])
|
|
523
|
+
elif (
|
|
524
|
+
dims == (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
525
|
+
and goal_data.sizes[FC.TARGET] > n_chunks_targets
|
|
526
|
+
and d.shape[1:] == (n_chunks_targets, 1)
|
|
527
|
+
):
|
|
528
|
+
dvars[v] = ((FC.STATE,), d[:, 0, 0])
|
|
529
|
+
else:
|
|
530
|
+
dvars[v] = (dims, d)
|
|
531
|
+
|
|
518
532
|
return Dataset(
|
|
519
533
|
coords=coords,
|
|
520
|
-
data_vars=
|
|
534
|
+
data_vars=dvars,
|
|
521
535
|
)
|
|
522
536
|
|
|
523
537
|
@abstractmethod
|
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/farm_data_model.py
CHANGED
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):
|