foxes 0.8.2__py3-none-any.whl → 1.1.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.
- docs/source/conf.py +353 -0
- examples/abl_states/run.py +160 -0
- examples/compare_rotors_pwakes/run.py +217 -0
- examples/compare_wakes/run.py +241 -0
- examples/dyn_wakes/run.py +311 -0
- examples/field_data_nc/run.py +121 -0
- examples/induction/run.py +201 -0
- examples/multi_height/run.py +113 -0
- examples/power_mask/run.py +249 -0
- examples/random_timeseries/run.py +210 -0
- examples/scan_row/run.py +193 -0
- examples/sector_management/run.py +162 -0
- examples/sequential/run.py +209 -0
- examples/single_state/run.py +201 -0
- examples/states_lookup_table/run.py +137 -0
- examples/streamline_wakes/run.py +138 -0
- examples/tab_file/run.py +142 -0
- examples/timelines/run.py +267 -0
- examples/timeseries/run.py +190 -0
- examples/timeseries_slurm/run.py +185 -0
- examples/wind_rose/run.py +141 -0
- examples/windio/run.py +29 -0
- examples/yawed_wake/run.py +196 -0
- foxes/__init__.py +4 -8
- foxes/algorithms/__init__.py +1 -1
- foxes/algorithms/downwind/downwind.py +247 -111
- foxes/algorithms/downwind/models/farm_wakes_calc.py +12 -7
- foxes/algorithms/downwind/models/init_farm_data.py +2 -2
- foxes/algorithms/downwind/models/point_wakes_calc.py +6 -7
- foxes/algorithms/downwind/models/reorder_farm_output.py +1 -2
- foxes/algorithms/downwind/models/set_amb_farm_results.py +1 -1
- foxes/algorithms/downwind/models/set_amb_point_results.py +5 -3
- foxes/algorithms/iterative/iterative.py +74 -34
- foxes/algorithms/iterative/models/farm_wakes_calc.py +12 -7
- foxes/algorithms/iterative/models/urelax.py +3 -3
- foxes/algorithms/sequential/models/plugin.py +5 -5
- foxes/algorithms/sequential/models/seq_state.py +1 -1
- foxes/algorithms/sequential/sequential.py +126 -255
- foxes/constants.py +22 -7
- foxes/core/__init__.py +1 -0
- foxes/core/algorithm.py +632 -147
- foxes/core/data.py +252 -20
- foxes/core/data_calc_model.py +15 -291
- foxes/core/engine.py +640 -0
- foxes/core/farm_controller.py +38 -10
- foxes/core/farm_data_model.py +16 -1
- foxes/core/ground_model.py +2 -2
- foxes/core/model.py +249 -182
- foxes/core/partial_wakes_model.py +1 -1
- foxes/core/point_data_model.py +17 -2
- foxes/core/rotor_model.py +27 -21
- foxes/core/states.py +17 -1
- foxes/core/turbine_type.py +28 -0
- foxes/core/wake_frame.py +30 -34
- foxes/core/wake_model.py +5 -5
- foxes/core/wake_superposition.py +1 -1
- foxes/data/windio/windio_5turbines_timeseries.yaml +31 -15
- foxes/engines/__init__.py +17 -0
- foxes/engines/dask.py +982 -0
- foxes/engines/default.py +75 -0
- foxes/engines/futures.py +72 -0
- foxes/engines/mpi.py +38 -0
- foxes/engines/multiprocess.py +71 -0
- foxes/engines/numpy.py +167 -0
- foxes/engines/pool.py +249 -0
- foxes/engines/ray.py +79 -0
- foxes/engines/single.py +141 -0
- foxes/input/farm_layout/__init__.py +1 -0
- foxes/input/farm_layout/from_csv.py +4 -0
- foxes/input/farm_layout/from_json.py +2 -2
- foxes/input/farm_layout/grid.py +2 -2
- foxes/input/farm_layout/ring.py +65 -0
- foxes/input/farm_layout/row.py +2 -2
- foxes/input/states/__init__.py +7 -0
- foxes/input/states/create/random_abl_states.py +1 -1
- foxes/input/states/field_data_nc.py +158 -33
- foxes/input/states/multi_height.py +128 -14
- foxes/input/states/one_point_flow.py +577 -0
- foxes/input/states/scan_ws.py +74 -3
- foxes/input/states/single.py +1 -1
- foxes/input/states/slice_data_nc.py +681 -0
- foxes/input/states/states_table.py +204 -35
- foxes/input/windio/__init__.py +2 -2
- foxes/input/windio/get_states.py +44 -23
- foxes/input/windio/read_attributes.py +48 -17
- foxes/input/windio/read_farm.py +116 -102
- foxes/input/windio/read_fields.py +16 -6
- foxes/input/windio/read_outputs.py +71 -24
- foxes/input/windio/runner.py +31 -17
- foxes/input/windio/windio.py +41 -23
- foxes/models/farm_models/turbine2farm.py +1 -1
- foxes/models/ground_models/wake_mirror.py +10 -6
- foxes/models/model_book.py +58 -20
- foxes/models/partial_wakes/axiwake.py +3 -3
- foxes/models/partial_wakes/rotor_points.py +3 -3
- foxes/models/partial_wakes/top_hat.py +2 -2
- foxes/models/point_models/set_uniform_data.py +1 -1
- foxes/models/point_models/tke2ti.py +1 -1
- foxes/models/point_models/wake_deltas.py +1 -1
- foxes/models/rotor_models/centre.py +4 -0
- foxes/models/rotor_models/grid.py +24 -25
- foxes/models/rotor_models/levels.py +4 -5
- foxes/models/turbine_models/calculator.py +4 -6
- foxes/models/turbine_models/kTI_model.py +22 -6
- foxes/models/turbine_models/lookup_table.py +30 -4
- foxes/models/turbine_models/rotor_centre_calc.py +4 -3
- foxes/models/turbine_models/set_farm_vars.py +103 -34
- foxes/models/turbine_types/PCt_file.py +27 -3
- foxes/models/turbine_types/PCt_from_two.py +27 -3
- foxes/models/turbine_types/TBL_file.py +80 -0
- foxes/models/turbine_types/__init__.py +2 -0
- foxes/models/turbine_types/lookup.py +316 -0
- foxes/models/turbine_types/null_type.py +51 -1
- foxes/models/turbine_types/wsrho2PCt_from_two.py +29 -5
- foxes/models/turbine_types/wsti2PCt_from_two.py +31 -7
- foxes/models/vertical_profiles/__init__.py +1 -1
- foxes/models/vertical_profiles/data_profile.py +1 -1
- foxes/models/wake_frames/__init__.py +1 -0
- foxes/models/wake_frames/dynamic_wakes.py +424 -0
- foxes/models/wake_frames/farm_order.py +25 -5
- foxes/models/wake_frames/rotor_wd.py +6 -4
- foxes/models/wake_frames/seq_dynamic_wakes.py +61 -74
- foxes/models/wake_frames/streamlines.py +21 -22
- foxes/models/wake_frames/timelines.py +330 -129
- foxes/models/wake_frames/yawed_wakes.py +7 -4
- foxes/models/wake_models/dist_sliced.py +2 -4
- foxes/models/wake_models/induction/rankine_half_body.py +5 -5
- foxes/models/wake_models/induction/rathmann.py +78 -24
- foxes/models/wake_models/induction/self_similar.py +78 -28
- foxes/models/wake_models/induction/vortex_sheet.py +86 -48
- foxes/models/wake_models/ti/crespo_hernandez.py +6 -4
- foxes/models/wake_models/ti/iec_ti.py +40 -21
- foxes/models/wake_models/top_hat.py +1 -1
- foxes/models/wake_models/wind/bastankhah14.py +8 -6
- foxes/models/wake_models/wind/bastankhah16.py +17 -16
- foxes/models/wake_models/wind/jensen.py +4 -3
- foxes/models/wake_models/wind/turbopark.py +16 -13
- foxes/models/wake_superpositions/ti_linear.py +1 -1
- foxes/models/wake_superpositions/ti_max.py +1 -1
- foxes/models/wake_superpositions/ti_pow.py +1 -1
- foxes/models/wake_superpositions/ti_quadratic.py +1 -1
- foxes/models/wake_superpositions/ws_linear.py +8 -7
- foxes/models/wake_superpositions/ws_max.py +8 -7
- foxes/models/wake_superpositions/ws_pow.py +8 -7
- foxes/models/wake_superpositions/ws_product.py +5 -5
- foxes/models/wake_superpositions/ws_quadratic.py +8 -7
- foxes/output/__init__.py +4 -1
- foxes/output/farm_layout.py +16 -12
- foxes/output/farm_results_eval.py +1 -1
- foxes/output/flow_plots_2d/__init__.py +0 -1
- foxes/output/flow_plots_2d/flow_plots.py +70 -30
- foxes/output/grids.py +92 -22
- foxes/output/results_writer.py +2 -2
- foxes/output/rose_plot.py +3 -3
- foxes/output/seq_plugins/__init__.py +2 -0
- foxes/output/{flow_plots_2d → seq_plugins}/seq_flow_ani_plugin.py +64 -22
- foxes/output/seq_plugins/seq_wake_debug_plugin.py +145 -0
- foxes/output/slice_data.py +131 -111
- foxes/output/state_turbine_map.py +19 -14
- foxes/output/state_turbine_table.py +19 -19
- foxes/utils/__init__.py +1 -1
- foxes/utils/abl/neutral.py +2 -2
- foxes/utils/abl/stable.py +2 -2
- foxes/utils/abl/unstable.py +2 -2
- foxes/utils/data_book.py +1 -1
- foxes/utils/dev_utils.py +42 -0
- foxes/utils/dict.py +24 -1
- foxes/utils/exec_python.py +1 -1
- foxes/utils/factory.py +176 -53
- foxes/utils/geom2d/circle.py +1 -1
- foxes/utils/geom2d/polygon.py +1 -1
- foxes/utils/geopandas_utils.py +2 -2
- foxes/utils/load.py +2 -2
- foxes/utils/pandas_helpers.py +3 -2
- foxes/utils/wind_dir.py +0 -2
- foxes/utils/xarray_utils.py +24 -14
- foxes/variables.py +39 -2
- {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/METADATA +75 -33
- foxes-1.1.0.2.dist-info/RECORD +309 -0
- {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/WHEEL +1 -1
- foxes-1.1.0.2.dist-info/top_level.txt +4 -0
- tests/0_consistency/iterative/test_iterative.py +92 -0
- tests/0_consistency/partial_wakes/test_partial_wakes.py +90 -0
- tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +85 -0
- tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +103 -0
- tests/1_verification/flappy_0_6/abl_states/flappy/run.py +85 -0
- tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +87 -0
- tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +82 -0
- tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +82 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_centre/flappy/run.py +92 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +93 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/flappy/run.py +92 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +96 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/flappy/run.py +94 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +122 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/flappy/run.py +94 -0
- tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +122 -0
- tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/flappy/run.py +92 -0
- tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +93 -0
- tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +85 -0
- tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +130 -0
- tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/flappy/run.py +96 -0
- tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +116 -0
- tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +93 -0
- tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +99 -0
- tests/3_examples/test_examples.py +34 -0
- foxes/VERSION +0 -1
- foxes/output/flow_plots_2d.py +0 -0
- foxes/utils/geopandas_helpers.py +0 -294
- foxes/utils/runners/__init__.py +0 -1
- foxes/utils/runners/runners.py +0 -280
- foxes-0.8.2.dist-info/RECORD +0 -247
- foxes-0.8.2.dist-info/top_level.txt +0 -1
- foxes-0.8.2.dist-info/zip-safe +0 -1
- {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/LICENSE +0 -0
foxes/core/model.py
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from abc import ABC
|
|
3
3
|
from itertools import count
|
|
4
|
-
from copy import deepcopy
|
|
5
4
|
|
|
6
5
|
import foxes.constants as FC
|
|
7
|
-
from .data import Data
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
class Model(ABC):
|
|
@@ -35,8 +33,8 @@ class Model(ABC):
|
|
|
35
33
|
if self._id > 0:
|
|
36
34
|
self.name += f"_instance{self._id}"
|
|
37
35
|
|
|
38
|
-
self._store = {}
|
|
39
36
|
self.__initialized = False
|
|
37
|
+
self.__running = False
|
|
40
38
|
|
|
41
39
|
def __repr__(self):
|
|
42
40
|
return f"{type(self).__name__}()"
|
|
@@ -119,6 +117,10 @@ class Model(ABC):
|
|
|
119
117
|
and `coords`, a dict with entries `dim_name_str -> dim_array`
|
|
120
118
|
|
|
121
119
|
"""
|
|
120
|
+
if self.initialized:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
f"Model '{self.name}': Cannot call load_data after initialization"
|
|
123
|
+
)
|
|
122
124
|
return {"coords": {}, "data_vars": {}}
|
|
123
125
|
|
|
124
126
|
def initialize(self, algo, verbosity=0, force=False):
|
|
@@ -135,6 +137,8 @@ class Model(ABC):
|
|
|
135
137
|
Overwrite existing data
|
|
136
138
|
|
|
137
139
|
"""
|
|
140
|
+
if self.running:
|
|
141
|
+
raise ValueError(f"Model '{self.name}': Cannot initialize while running")
|
|
138
142
|
if not self.initialized:
|
|
139
143
|
pr = False
|
|
140
144
|
for m in self.sub_models():
|
|
@@ -152,6 +156,103 @@ class Model(ABC):
|
|
|
152
156
|
|
|
153
157
|
self.__initialized = True
|
|
154
158
|
|
|
159
|
+
@property
|
|
160
|
+
def running(self):
|
|
161
|
+
"""
|
|
162
|
+
Flag for currently running models
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
flag: bool
|
|
167
|
+
True if currently running
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
return self.__running
|
|
171
|
+
|
|
172
|
+
def set_running(
|
|
173
|
+
self,
|
|
174
|
+
algo,
|
|
175
|
+
data_stash,
|
|
176
|
+
sel=None,
|
|
177
|
+
isel=None,
|
|
178
|
+
verbosity=0,
|
|
179
|
+
):
|
|
180
|
+
"""
|
|
181
|
+
Sets this model status to running, and moves
|
|
182
|
+
all large data to stash.
|
|
183
|
+
|
|
184
|
+
The stashed data will be returned by the
|
|
185
|
+
unset_running() function after running calculations.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
algo: foxes.core.Algorithm
|
|
190
|
+
The calculation algorithm
|
|
191
|
+
data_stash: dict
|
|
192
|
+
Large data stash, this function adds data here.
|
|
193
|
+
Key: model name. Value: dict, large model data
|
|
194
|
+
sel: dict, optional
|
|
195
|
+
The subset selection dictionary
|
|
196
|
+
isel: dict, optional
|
|
197
|
+
The index subset selection dictionary
|
|
198
|
+
verbosity: int
|
|
199
|
+
The verbosity level, 0 = silent
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
if self.running:
|
|
203
|
+
raise ValueError(
|
|
204
|
+
f"Model '{self.name}': Cannot call set_running while running"
|
|
205
|
+
)
|
|
206
|
+
for m in self.sub_models():
|
|
207
|
+
if not m.running:
|
|
208
|
+
m.set_running(algo, data_stash, sel, isel, verbosity=verbosity)
|
|
209
|
+
|
|
210
|
+
if verbosity > 0:
|
|
211
|
+
print(f"Model '{self.name}': running")
|
|
212
|
+
if self.name not in data_stash:
|
|
213
|
+
data_stash[self.name] = {}
|
|
214
|
+
|
|
215
|
+
self.__running = True
|
|
216
|
+
|
|
217
|
+
def unset_running(
|
|
218
|
+
self,
|
|
219
|
+
algo,
|
|
220
|
+
data_stash,
|
|
221
|
+
sel=None,
|
|
222
|
+
isel=None,
|
|
223
|
+
verbosity=0,
|
|
224
|
+
):
|
|
225
|
+
"""
|
|
226
|
+
Sets this model status to not running, recovering large data
|
|
227
|
+
from stash
|
|
228
|
+
|
|
229
|
+
Parameters
|
|
230
|
+
----------
|
|
231
|
+
algo: foxes.core.Algorithm
|
|
232
|
+
The calculation algorithm
|
|
233
|
+
data_stash: dict
|
|
234
|
+
Large data stash, this function adds data here.
|
|
235
|
+
Key: model name. Value: dict, large model data
|
|
236
|
+
sel: dict, optional
|
|
237
|
+
The subset selection dictionary
|
|
238
|
+
isel: dict, optional
|
|
239
|
+
The index subset selection dictionary
|
|
240
|
+
verbosity: int
|
|
241
|
+
The verbosity level, 0 = silent
|
|
242
|
+
|
|
243
|
+
"""
|
|
244
|
+
if not self.running:
|
|
245
|
+
raise ValueError(
|
|
246
|
+
f"Model '{self.name}': Cannot call unset_running when not running"
|
|
247
|
+
)
|
|
248
|
+
for m in self.sub_models():
|
|
249
|
+
if m.running:
|
|
250
|
+
m.unset_running(algo, data_stash, sel, isel, verbosity=verbosity)
|
|
251
|
+
|
|
252
|
+
if verbosity > 0:
|
|
253
|
+
print(f"Model '{self.name}': not running")
|
|
254
|
+
self.__running = False
|
|
255
|
+
|
|
155
256
|
def finalize(self, algo, verbosity=0):
|
|
156
257
|
"""
|
|
157
258
|
Finalizes the model.
|
|
@@ -164,6 +265,8 @@ class Model(ABC):
|
|
|
164
265
|
The verbosity level, 0 = silent
|
|
165
266
|
|
|
166
267
|
"""
|
|
268
|
+
if self.running:
|
|
269
|
+
raise ValueError(f"Model '{self.name}': Cannot finalize while running")
|
|
167
270
|
if self.initialized:
|
|
168
271
|
pr = False
|
|
169
272
|
for m in self.sub_models():
|
|
@@ -178,7 +281,6 @@ class Model(ABC):
|
|
|
178
281
|
print(f"Finalizing model '{self.name}'")
|
|
179
282
|
algo.del_model_data(self)
|
|
180
283
|
|
|
181
|
-
self._store = {}
|
|
182
284
|
self.__initialized = False
|
|
183
285
|
|
|
184
286
|
def get_data(
|
|
@@ -194,6 +296,7 @@ class Model(ABC):
|
|
|
194
296
|
accept_nan=True,
|
|
195
297
|
algo=None,
|
|
196
298
|
upcast=False,
|
|
299
|
+
selection=None,
|
|
197
300
|
):
|
|
198
301
|
"""
|
|
199
302
|
Getter for a data entry in the model object
|
|
@@ -233,6 +336,9 @@ class Model(ABC):
|
|
|
233
336
|
upcast: bool
|
|
234
337
|
Flag for ensuring targets dimension,
|
|
235
338
|
otherwise dimension 1 is entered
|
|
339
|
+
selection: numpy.ndarray, optional
|
|
340
|
+
Apply this selection to the result,
|
|
341
|
+
state-turbine, state-target, or state-target-tpoint
|
|
236
342
|
|
|
237
343
|
"""
|
|
238
344
|
|
|
@@ -241,7 +347,7 @@ class Model(ABC):
|
|
|
241
347
|
for s in sources:
|
|
242
348
|
try:
|
|
243
349
|
if a == "states_i0":
|
|
244
|
-
out = s.states_i0(counter=True
|
|
350
|
+
out = s.states_i0(counter=True)
|
|
245
351
|
if out is not None:
|
|
246
352
|
return out
|
|
247
353
|
else:
|
|
@@ -256,6 +362,10 @@ class Model(ABC):
|
|
|
256
362
|
|
|
257
363
|
n_states = _geta("n_states")
|
|
258
364
|
if target == FC.STATE_TURBINE:
|
|
365
|
+
if downwind_index is not None:
|
|
366
|
+
raise ValueError(
|
|
367
|
+
f"Target '{target}' is incompatible with downwind_index (here {downwind_index})"
|
|
368
|
+
)
|
|
259
369
|
n_turbines = _geta("n_turbines")
|
|
260
370
|
dims = (FC.STATE, FC.TURBINE)
|
|
261
371
|
shp = (n_states, n_turbines)
|
|
@@ -273,42 +383,49 @@ class Model(ABC):
|
|
|
273
383
|
f"Model '{self.name}': Wrong parameter 'target = {target}'. Choices: {FC.STATE_TURBINE}, {FC.STATE_TARGET}, {FC.STATE_TARGET_TPOINT}"
|
|
274
384
|
)
|
|
275
385
|
|
|
386
|
+
def _match_shape(a):
|
|
387
|
+
out = np.asarray(a)
|
|
388
|
+
if len(out.shape) < len(shp):
|
|
389
|
+
for i, s in enumerate(shp):
|
|
390
|
+
if i >= len(out.shape):
|
|
391
|
+
out = out[..., None]
|
|
392
|
+
elif a.shape[i] not in (1, s):
|
|
393
|
+
raise ValueError(
|
|
394
|
+
f"Shape mismatch for '{variable}': Got {out.shape}, expecting {shp}"
|
|
395
|
+
)
|
|
396
|
+
elif len(out.shape) > len(shp):
|
|
397
|
+
raise ValueError(
|
|
398
|
+
f"Shape mismatch for '{variable}': Got {out.shape}, expecting {shp}"
|
|
399
|
+
)
|
|
400
|
+
return out
|
|
401
|
+
|
|
402
|
+
def _filter_dims(source):
|
|
403
|
+
a = source[variable]
|
|
404
|
+
a_dims = tuple(source.dims[variable])
|
|
405
|
+
if downwind_index is None or FC.TURBINE not in a_dims:
|
|
406
|
+
d = a_dims
|
|
407
|
+
else:
|
|
408
|
+
slc = tuple(
|
|
409
|
+
[downwind_index if dd == FC.TURBINE else np.s_[:] for dd in a_dims]
|
|
410
|
+
)
|
|
411
|
+
a = a[slc]
|
|
412
|
+
d = tuple([dd for dd in a_dims if dd != FC.TURBINE])
|
|
413
|
+
return a, d
|
|
414
|
+
|
|
276
415
|
out = None
|
|
277
|
-
out_dims = None
|
|
278
416
|
for s in lookup:
|
|
279
417
|
# lookup self:
|
|
280
418
|
if s == "s" and hasattr(self, variable):
|
|
281
419
|
a = getattr(self, variable)
|
|
282
420
|
if a is not None:
|
|
283
|
-
|
|
284
|
-
out = a
|
|
285
|
-
out_dims = None
|
|
286
|
-
elif target == FC.STATE_TURBINE:
|
|
287
|
-
out = np.full((n_states, n_turbines), np.nan, dtype=FC.DTYPE)
|
|
288
|
-
out[:] = a
|
|
289
|
-
out_dims = (FC.STATE, FC.TURBINE)
|
|
290
|
-
elif target == FC.STATE_TARGET:
|
|
291
|
-
out = np.full((n_states, n_targets), np.nan, dtype=FC.DTYPE)
|
|
292
|
-
out[:] = a
|
|
293
|
-
out_dims = (FC.STATE, FC.TARGET)
|
|
294
|
-
elif target == FC.STATE_TARGET_TPOINT:
|
|
295
|
-
out = np.full(
|
|
296
|
-
(n_states, n_targets, n_tpoints), np.nan, dtype=FC.DTYPE
|
|
297
|
-
)
|
|
298
|
-
out[:] = a
|
|
299
|
-
out_dims = (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
300
|
-
else:
|
|
301
|
-
raise NotImplementedError
|
|
421
|
+
out = _match_shape(a)
|
|
302
422
|
|
|
303
423
|
# lookup mdata:
|
|
304
|
-
elif
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
and
|
|
308
|
-
|
|
309
|
-
):
|
|
310
|
-
out = mdata[variable]
|
|
311
|
-
out_dims = dims
|
|
424
|
+
elif s == "m" and mdata is not None and variable in mdata:
|
|
425
|
+
a, d = _filter_dims(mdata)
|
|
426
|
+
l = len(d)
|
|
427
|
+
if l <= len(dims) and d == dims[:l]:
|
|
428
|
+
out = _match_shape(mdata[variable])
|
|
312
429
|
|
|
313
430
|
# lookup fdata:
|
|
314
431
|
elif (
|
|
@@ -317,18 +434,22 @@ class Model(ABC):
|
|
|
317
434
|
and variable in fdata
|
|
318
435
|
and tuple(fdata.dims[variable]) == (FC.STATE, FC.TURBINE)
|
|
319
436
|
):
|
|
320
|
-
|
|
321
|
-
|
|
437
|
+
if target == FC.STATE_TURBINE:
|
|
438
|
+
out = fdata[variable]
|
|
439
|
+
elif downwind_index is not None:
|
|
440
|
+
out = _match_shape(fdata[variable][:, downwind_index])
|
|
322
441
|
|
|
323
442
|
# lookup pdata:
|
|
324
443
|
elif (
|
|
325
444
|
s == "t"
|
|
445
|
+
and target != FC.STATE_TURBINE
|
|
326
446
|
and tdata is not None
|
|
327
447
|
and variable in tdata
|
|
328
|
-
and tuple(tdata.dims[variable]) == (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
329
448
|
):
|
|
330
|
-
|
|
331
|
-
|
|
449
|
+
a, d = _filter_dims(tdata)
|
|
450
|
+
l = len(d)
|
|
451
|
+
if l <= len(dims) and d == dims[:l]:
|
|
452
|
+
out = _match_shape(tdata[variable])
|
|
332
453
|
|
|
333
454
|
# lookup wake modelling data:
|
|
334
455
|
elif (
|
|
@@ -340,78 +461,27 @@ class Model(ABC):
|
|
|
340
461
|
and downwind_index is not None
|
|
341
462
|
and algo is not None
|
|
342
463
|
):
|
|
343
|
-
out
|
|
344
|
-
algo
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
464
|
+
out = _match_shape(
|
|
465
|
+
algo.wake_frame.get_wake_modelling_data(
|
|
466
|
+
algo,
|
|
467
|
+
variable,
|
|
468
|
+
downwind_index,
|
|
469
|
+
fdata,
|
|
470
|
+
tdata=tdata,
|
|
471
|
+
target=target,
|
|
472
|
+
)
|
|
351
473
|
)
|
|
352
474
|
|
|
353
475
|
if out is not None:
|
|
354
476
|
break
|
|
355
477
|
|
|
356
|
-
#
|
|
357
|
-
if
|
|
358
|
-
if
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
out = np.zeros(shp, dtype=FC.DTYPE)
|
|
362
|
-
out[:] = out0
|
|
363
|
-
out_dims = dims
|
|
364
|
-
del out0
|
|
365
|
-
else:
|
|
366
|
-
out_dims = tuple([1 for _ in dims])
|
|
367
|
-
|
|
368
|
-
elif out_dims == (FC.STATE, FC.TURBINE):
|
|
369
|
-
if downwind_index is None:
|
|
370
|
-
raise KeyError(
|
|
371
|
-
f"Require downwind_index for target {target} and out dims {out_dims}"
|
|
372
|
-
)
|
|
373
|
-
out0 = out[:, downwind_index, None]
|
|
374
|
-
if len(dims) == 3:
|
|
375
|
-
out0 = out0[:, :, None]
|
|
376
|
-
if upcast:
|
|
377
|
-
out = np.zeros(shp, dtype=FC.DTYPE)
|
|
378
|
-
out[:] = out0
|
|
379
|
-
out_dims = dims
|
|
380
|
-
else:
|
|
381
|
-
out = out0
|
|
382
|
-
out_dims = (FC.STATE, 1) if len(dims) == 2 else (FC.STATE, 1, 1)
|
|
383
|
-
del out0
|
|
384
|
-
|
|
385
|
-
elif out_dims == (FC.STATE, 1):
|
|
386
|
-
out0 = out
|
|
387
|
-
if len(dims) == 3:
|
|
388
|
-
out0 = out0[:, :, None]
|
|
389
|
-
out_dims = (FC.STATE, 1, 1)
|
|
390
|
-
if upcast:
|
|
391
|
-
out = np.zeros(shp, dtype=FC.DTYPE)
|
|
392
|
-
out[:] = out0
|
|
393
|
-
out_dims = dims
|
|
394
|
-
else:
|
|
395
|
-
out = out0
|
|
396
|
-
del out0
|
|
397
|
-
|
|
398
|
-
elif out_dims == (FC.STATE, 1, 1):
|
|
399
|
-
out0 = out
|
|
400
|
-
if len(dims) == 2:
|
|
401
|
-
out0 = out0[:, :, 0]
|
|
402
|
-
out_dims = (FC.STATE, 1)
|
|
403
|
-
if upcast:
|
|
404
|
-
out = np.zeros(shp, dtype=FC.DTYPE)
|
|
405
|
-
out[:] = out0
|
|
406
|
-
out_dims = dims
|
|
407
|
-
else:
|
|
408
|
-
out = out0
|
|
409
|
-
del out0
|
|
410
|
-
|
|
411
|
-
else:
|
|
412
|
-
raise NotImplementedError(
|
|
413
|
-
f"No casting implemented for target {target} and out dims {out_dims} fo upcast {upcast}"
|
|
478
|
+
# check for None:
|
|
479
|
+
if out is None:
|
|
480
|
+
if not accept_none:
|
|
481
|
+
raise ValueError(
|
|
482
|
+
f"Model '{self.name}': Variable '{variable}' is requested but not found."
|
|
414
483
|
)
|
|
484
|
+
return out
|
|
415
485
|
|
|
416
486
|
# data from other chunks, only with iterations:
|
|
417
487
|
if (
|
|
@@ -421,10 +491,14 @@ class Model(ABC):
|
|
|
421
491
|
and tdata is not None
|
|
422
492
|
and FC.STATES_SEL in tdata
|
|
423
493
|
):
|
|
424
|
-
if
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
494
|
+
if out.shape != shp:
|
|
495
|
+
# upcast to dims:
|
|
496
|
+
tmp = np.zeros(shp, dtype=out.dtype)
|
|
497
|
+
tmp[:] = out
|
|
498
|
+
out = tmp
|
|
499
|
+
del tmp
|
|
500
|
+
else:
|
|
501
|
+
out = out.copy()
|
|
428
502
|
if downwind_index is None:
|
|
429
503
|
raise KeyError(
|
|
430
504
|
f"Model '{self.name}': Require downwind_index for obtaining results from previous iteration"
|
|
@@ -438,7 +512,12 @@ class Model(ABC):
|
|
|
438
512
|
f"Model '{self.name}': Iteration data found for variable '{variable}', requiring algo"
|
|
439
513
|
)
|
|
440
514
|
|
|
441
|
-
|
|
515
|
+
from foxes.algorithms.sequential import Sequential
|
|
516
|
+
|
|
517
|
+
if isinstance(algo, Sequential):
|
|
518
|
+
i0 = algo.states.counter
|
|
519
|
+
else:
|
|
520
|
+
i0 = _geta("states_i0")
|
|
442
521
|
sts = tdata[FC.STATES_SEL]
|
|
443
522
|
if target == FC.STATE_TARGET and tdata.n_tpoints != 1:
|
|
444
523
|
# find the mean index and round it to nearest integer:
|
|
@@ -446,27 +525,31 @@ class Model(ABC):
|
|
|
446
525
|
sts = (sts + 0.5).astype(FC.ITYPE)
|
|
447
526
|
sel = sts < i0
|
|
448
527
|
if np.any(sel):
|
|
449
|
-
if not hasattr(algo, "
|
|
528
|
+
if not hasattr(algo, "farm_results_downwind"):
|
|
450
529
|
raise KeyError(
|
|
451
530
|
f"Model '{self.name}': Iteration data found for variable '{variable}', requiring iterative algorithm"
|
|
452
531
|
)
|
|
453
|
-
prev_fres = getattr(algo, "
|
|
532
|
+
prev_fres = getattr(algo, "farm_results_downwind")
|
|
454
533
|
if prev_fres is not None:
|
|
455
534
|
prev_data = prev_fres[variable].to_numpy()[sts[sel], downwind_index]
|
|
456
535
|
if target == FC.STATE_TARGET:
|
|
457
536
|
out[sel[:, :, 0]] = prev_data
|
|
458
537
|
else:
|
|
459
538
|
out[sel] = prev_data
|
|
460
|
-
del
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
539
|
+
del prev_data
|
|
540
|
+
del prev_fres
|
|
541
|
+
if np.any(~sel):
|
|
542
|
+
sts = sts[~sel] - i0
|
|
543
|
+
sel_data = fdata[variable][sts, downwind_index]
|
|
544
|
+
if target == FC.STATE_TARGET:
|
|
545
|
+
out[~sel[:, :, 0]] = sel_data
|
|
546
|
+
else:
|
|
547
|
+
out[~sel] = sel_data
|
|
548
|
+
del sel_data
|
|
549
|
+
del sel, sts
|
|
467
550
|
|
|
468
551
|
# check for nan:
|
|
469
|
-
|
|
552
|
+
if not accept_nan:
|
|
470
553
|
try:
|
|
471
554
|
if np.all(np.isnan(np.atleast_1d(out))):
|
|
472
555
|
raise ValueError(
|
|
@@ -475,68 +558,52 @@ class Model(ABC):
|
|
|
475
558
|
except TypeError:
|
|
476
559
|
pass
|
|
477
560
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
ret_dims: bool
|
|
519
|
-
Return dimensions
|
|
520
|
-
safe: bool
|
|
521
|
-
Return None instead of error if
|
|
522
|
-
not found
|
|
523
|
-
|
|
524
|
-
Returns
|
|
525
|
-
-------
|
|
526
|
-
data: numpy.ndarray
|
|
527
|
-
The data
|
|
528
|
-
dims: tuple of dims, optional
|
|
529
|
-
The data dimensions
|
|
561
|
+
# apply selection:
|
|
562
|
+
if selection is not None:
|
|
563
|
+
|
|
564
|
+
def _upcast_sel(sel_shape):
|
|
565
|
+
chp = []
|
|
566
|
+
for i, s in enumerate(out.shape):
|
|
567
|
+
if i < len(sel_shape) and sel_shape[i] > 1:
|
|
568
|
+
if sel_shape[i] != shp[i]:
|
|
569
|
+
raise ValueError(
|
|
570
|
+
f"Incompatible selection shape {sel_shape} for output shape {shp[i]}"
|
|
571
|
+
)
|
|
572
|
+
chp.append(shp[i])
|
|
573
|
+
else:
|
|
574
|
+
chp.append(s)
|
|
575
|
+
chp = tuple(chp)
|
|
576
|
+
eshp = list(shp[len(sel_shape) :])
|
|
577
|
+
if chp != out.shape:
|
|
578
|
+
nout = np.zeros(chp, dtype=out.dtype)
|
|
579
|
+
nout[:] = out
|
|
580
|
+
return nout, eshp
|
|
581
|
+
return out, eshp
|
|
582
|
+
|
|
583
|
+
if isinstance(selection, np.ndarray) and selection.dtype == bool:
|
|
584
|
+
if len(selection.shape) > len(out.shape):
|
|
585
|
+
raise ValueError(
|
|
586
|
+
f"Expecting selection of shape {out.shape}, got {selection.shape}"
|
|
587
|
+
)
|
|
588
|
+
out, eshp = _upcast_sel(selection.shape)
|
|
589
|
+
elif isinstance(selection, (tuple, list)):
|
|
590
|
+
if len(selection) > len(out.shape):
|
|
591
|
+
raise ValueError(
|
|
592
|
+
f"Selection is tuple/list of length {len(selection)}, expecting <= {len(out.shape)} "
|
|
593
|
+
)
|
|
594
|
+
out, eshp = _upcast_sel(shp[: len(selection)])
|
|
595
|
+
else:
|
|
596
|
+
raise TypeError(
|
|
597
|
+
f"Expecting selection of type np.ndarray (bool), or tuple, or list. Got {type(selection).__name__}"
|
|
598
|
+
)
|
|
599
|
+
out = out[selection]
|
|
600
|
+
shp = tuple([len(out)] + list(eshp))
|
|
530
601
|
|
|
531
|
-
|
|
532
|
-
if
|
|
533
|
-
|
|
602
|
+
# apply upcast:
|
|
603
|
+
if upcast and out.shape != shp:
|
|
604
|
+
tmp = np.zeros(shp, dtype=out.dtype)
|
|
605
|
+
tmp[:] = out
|
|
606
|
+
out = tmp
|
|
607
|
+
del tmp
|
|
534
608
|
|
|
535
|
-
|
|
536
|
-
if not safe or (i0 in self._store and name in self._store[i0]):
|
|
537
|
-
if ret_dims:
|
|
538
|
-
return self._store[i0][name], self._store[i0].dims[name]
|
|
539
|
-
else:
|
|
540
|
-
return self._store[i0][name]
|
|
541
|
-
else:
|
|
542
|
-
return (None, None) if ret_dims else None
|
|
609
|
+
return out
|
|
@@ -121,7 +121,7 @@ class PartialWakesModel(Model):
|
|
|
121
121
|
The target point data
|
|
122
122
|
downwind_index: int
|
|
123
123
|
The index of the wake causing turbine
|
|
124
|
-
in the
|
|
124
|
+
in the downwind order
|
|
125
125
|
wake_deltas: dict
|
|
126
126
|
The wake deltas. Key: variable name,
|
|
127
127
|
value: numpy.ndarray with shape
|
foxes/core/point_data_model.py
CHANGED
|
@@ -27,6 +27,18 @@ class PointDataModel(DataCalcModel):
|
|
|
27
27
|
"""
|
|
28
28
|
return []
|
|
29
29
|
|
|
30
|
+
def output_coords(self):
|
|
31
|
+
"""
|
|
32
|
+
Gets the coordinates of all output arrays
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
dims: tuple of str
|
|
37
|
+
The coordinates of all output arrays
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
return (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
41
|
+
|
|
30
42
|
def ensure_variables(self, algo, mdata, fdata, tdata):
|
|
31
43
|
"""
|
|
32
44
|
Add variables to tdata, initialized with NaN
|
|
@@ -54,7 +66,7 @@ class PointDataModel(DataCalcModel):
|
|
|
54
66
|
|
|
55
67
|
@abstractmethod
|
|
56
68
|
def calculate(self, algo, mdata, fdata, tdata):
|
|
57
|
-
"""
|
|
69
|
+
"""
|
|
58
70
|
The main model calculation.
|
|
59
71
|
|
|
60
72
|
This function is executed on a single chunk of data,
|
|
@@ -158,6 +170,9 @@ class PointDataModelList(PointDataModel):
|
|
|
158
170
|
super().__init__()
|
|
159
171
|
self.models = models
|
|
160
172
|
|
|
173
|
+
def __repr__(self):
|
|
174
|
+
return f"{type(self).__name__}({[m.name for m in self.models]})"
|
|
175
|
+
|
|
161
176
|
def append(self, model):
|
|
162
177
|
"""
|
|
163
178
|
Add a model to the list
|
|
@@ -203,7 +218,7 @@ class PointDataModelList(PointDataModel):
|
|
|
203
218
|
return list(dict.fromkeys(ovars))
|
|
204
219
|
|
|
205
220
|
def calculate(self, algo, mdata, fdata, tdata, parameters=None):
|
|
206
|
-
"""
|
|
221
|
+
"""
|
|
207
222
|
The main model calculation.
|
|
208
223
|
|
|
209
224
|
This function is executed on a single chunk of data,
|