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
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.interpolate import interpn
|
|
3
|
+
|
|
4
|
+
from foxes.core import States
|
|
5
|
+
from foxes.utils import uv2wd
|
|
6
|
+
from foxes.models.wake_frames.timelines import Timelines
|
|
7
|
+
import foxes.variables as FV
|
|
8
|
+
import foxes.constants as FC
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OnePointFlowStates(States):
|
|
12
|
+
"""
|
|
13
|
+
Time-evolving states based on horizontally
|
|
14
|
+
homogeneous timeseries data
|
|
15
|
+
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
ref_xy: list of float
|
|
19
|
+
The [x, y] or [x, y, z] coordinates of the base states.
|
|
20
|
+
If [x, y, z] then z will serve as height
|
|
21
|
+
tl_heights: list of float
|
|
22
|
+
The heights at which timelines will be calculated
|
|
23
|
+
dt_min: float
|
|
24
|
+
The delta t value in minutes,
|
|
25
|
+
if not from timeseries data
|
|
26
|
+
intp_pars: dict
|
|
27
|
+
Parameters for height interpolation with
|
|
28
|
+
scipy.interpolate.interpn
|
|
29
|
+
|
|
30
|
+
:group: input.states
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
ref_xy,
|
|
37
|
+
*base_states_args,
|
|
38
|
+
base_states=None,
|
|
39
|
+
tl_heights=None,
|
|
40
|
+
dt_min=None,
|
|
41
|
+
**base_states_kwargs,
|
|
42
|
+
):
|
|
43
|
+
"""
|
|
44
|
+
Constructor.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
ref_xy: list of float
|
|
49
|
+
The [x, y] or [x, y, z] coordinates of the base states.
|
|
50
|
+
If [x, y, z] then z will serve as height
|
|
51
|
+
base_states_args: tuple, optional
|
|
52
|
+
Arguments for creating the base states from
|
|
53
|
+
States.new(), if not given as base_states
|
|
54
|
+
base_states: foxes.core.States, optional
|
|
55
|
+
The base states, representing horizontally
|
|
56
|
+
homogeneous inflow
|
|
57
|
+
tl_heights: list of float, optional
|
|
58
|
+
The heights at which timelines will be calculated
|
|
59
|
+
dt_min: float, optional
|
|
60
|
+
The delta t value in minutes,
|
|
61
|
+
if not from timeseries data
|
|
62
|
+
base_states_kwargs: dict, optional
|
|
63
|
+
Arguments for creating the base states from
|
|
64
|
+
States.new(), if not given as base_states
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
super().__init__()
|
|
68
|
+
self.ref_xy = np.array(ref_xy, dtype=FC.DTYPE)
|
|
69
|
+
self.heights = tl_heights
|
|
70
|
+
self.base_states = base_states
|
|
71
|
+
self.dt_min = dt_min
|
|
72
|
+
|
|
73
|
+
self.intp_pars = {"fill_value": None}
|
|
74
|
+
if "bounds_error" in base_states_kwargs:
|
|
75
|
+
self.intp_pars["bounds_error"] = base_states_kwargs["bounds_error"]
|
|
76
|
+
|
|
77
|
+
if base_states is not None and len(base_states_kwargs):
|
|
78
|
+
raise KeyError(
|
|
79
|
+
f"Base states of type '{type(base_states).__name__}' were given, cannot handle base_state_pars {list(base_states_kwargs.keys())}"
|
|
80
|
+
)
|
|
81
|
+
elif base_states is not None and len(base_states_args):
|
|
82
|
+
raise KeyError(
|
|
83
|
+
f"Base states of type '{type(base_states).__name__}' were given, cannot handle base_states_args of types {[type(a).__name__ for a in base_states_args]}"
|
|
84
|
+
)
|
|
85
|
+
elif base_states is None:
|
|
86
|
+
self.base_states = States.new(*base_states_args, **base_states_kwargs)
|
|
87
|
+
|
|
88
|
+
def __repr__(self):
|
|
89
|
+
return f"{type(self).__name__}(base={type(self.base_states).__name__}, heights={self.heights}, dt_min={self.dt_min})"
|
|
90
|
+
|
|
91
|
+
def sub_models(self):
|
|
92
|
+
"""
|
|
93
|
+
List of all sub-models
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
smdls: list of foxes.core.Model
|
|
98
|
+
All sub models
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
return [self.base_states]
|
|
102
|
+
|
|
103
|
+
def initialize(self, algo, verbosity=0):
|
|
104
|
+
"""
|
|
105
|
+
Initializes the model.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
algo: foxes.core.Algorithm
|
|
110
|
+
The calculation algorithm
|
|
111
|
+
verbosity: int
|
|
112
|
+
The verbosity level, 0 = silent
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
super().initialize(algo, verbosity)
|
|
116
|
+
|
|
117
|
+
# find heights:
|
|
118
|
+
if self.heights is None:
|
|
119
|
+
if hasattr(self.base_states, "heights"):
|
|
120
|
+
self.heights = self.base_states.heights
|
|
121
|
+
elif len(self.ref_xy) > 2:
|
|
122
|
+
self.heights = [self.ref_xy[2]]
|
|
123
|
+
else:
|
|
124
|
+
raise KeyError(
|
|
125
|
+
f"Cannot find 'heights' in base states of type '{type(self.base_states).__name__}', missing either `ref_xy` of type [x, y, z], or explicit value list via parameter 'tl_heights'"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# pre-calc data:
|
|
129
|
+
Timelines._precalc_data(
|
|
130
|
+
self, algo, self.base_states, self.heights, verbosity, needs_res=True
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def size(self):
|
|
134
|
+
"""
|
|
135
|
+
The total number of states.
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
int:
|
|
140
|
+
The total number of states
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
return self.base_states.size()
|
|
144
|
+
|
|
145
|
+
def index(self):
|
|
146
|
+
"""
|
|
147
|
+
The index list
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
indices: array_like
|
|
152
|
+
The index labels of states, or None for default integers
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
return self.base_states.index()
|
|
156
|
+
|
|
157
|
+
def output_point_vars(self, algo):
|
|
158
|
+
"""
|
|
159
|
+
The variables which are being modified by the model.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
algo: foxes.core.Algorithm
|
|
164
|
+
The calculation algorithm
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
output_vars: list of str
|
|
169
|
+
The output variable names
|
|
170
|
+
|
|
171
|
+
"""
|
|
172
|
+
return self.base_states.output_point_vars(algo)
|
|
173
|
+
|
|
174
|
+
def weights(self, algo):
|
|
175
|
+
"""
|
|
176
|
+
The statistical weights of all states.
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
algo: foxes.core.Algorithm
|
|
181
|
+
The calculation algorithm
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
weights: numpy.ndarray
|
|
186
|
+
The weights, shape: (n_states, n_turbines)
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
return self.base_states.weights(algo)
|
|
190
|
+
|
|
191
|
+
def set_running(
|
|
192
|
+
self,
|
|
193
|
+
algo,
|
|
194
|
+
data_stash,
|
|
195
|
+
sel=None,
|
|
196
|
+
isel=None,
|
|
197
|
+
verbosity=0,
|
|
198
|
+
):
|
|
199
|
+
"""
|
|
200
|
+
Sets this model status to running, and moves
|
|
201
|
+
all large data to stash.
|
|
202
|
+
|
|
203
|
+
The stashed data will be returned by the
|
|
204
|
+
unset_running() function after running calculations.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
algo: foxes.core.Algorithm
|
|
209
|
+
The calculation algorithm
|
|
210
|
+
data_stash: dict
|
|
211
|
+
Large data stash, this function adds data here.
|
|
212
|
+
Key: model name. Value: dict, large model data
|
|
213
|
+
sel: dict, optional
|
|
214
|
+
The subset selection dictionary
|
|
215
|
+
isel: dict, optional
|
|
216
|
+
The index subset selection dictionary
|
|
217
|
+
verbosity: int
|
|
218
|
+
The verbosity level, 0 = silent
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
super().set_running(algo, data_stash, sel, isel, verbosity)
|
|
222
|
+
|
|
223
|
+
if sel is not None or isel is not None:
|
|
224
|
+
data_stash[self.name]["data"] = self.timelines_data
|
|
225
|
+
|
|
226
|
+
if isel is not None:
|
|
227
|
+
self.timelines_data = self.timelines_data.isel(isel)
|
|
228
|
+
if sel is not None:
|
|
229
|
+
self.timelines_data = self.timelines_data.sel(sel)
|
|
230
|
+
|
|
231
|
+
def unset_running(
|
|
232
|
+
self,
|
|
233
|
+
algo,
|
|
234
|
+
data_stash,
|
|
235
|
+
sel=None,
|
|
236
|
+
isel=None,
|
|
237
|
+
verbosity=0,
|
|
238
|
+
):
|
|
239
|
+
"""
|
|
240
|
+
Sets this model status to not running, recovering large data
|
|
241
|
+
from stash
|
|
242
|
+
|
|
243
|
+
Parameters
|
|
244
|
+
----------
|
|
245
|
+
algo: foxes.core.Algorithm
|
|
246
|
+
The calculation algorithm
|
|
247
|
+
data_stash: dict
|
|
248
|
+
Large data stash, this function adds data here.
|
|
249
|
+
Key: model name. Value: dict, large model data
|
|
250
|
+
sel: dict, optional
|
|
251
|
+
The subset selection dictionary
|
|
252
|
+
isel: dict, optional
|
|
253
|
+
The index subset selection dictionary
|
|
254
|
+
verbosity: int
|
|
255
|
+
The verbosity level, 0 = silent
|
|
256
|
+
|
|
257
|
+
"""
|
|
258
|
+
super().unset_running(algo, data_stash, sel, isel, verbosity)
|
|
259
|
+
|
|
260
|
+
data = data_stash[self.name]
|
|
261
|
+
if "data" in data:
|
|
262
|
+
self.timelines_data = data.pop("data")
|
|
263
|
+
|
|
264
|
+
def calc_states_indices(self, algo, mdata, points, hi, ref_xy):
|
|
265
|
+
|
|
266
|
+
n_states, n_points = points.shape[:2]
|
|
267
|
+
dxy = self.timelines_data["dxy"].to_numpy()[hi]
|
|
268
|
+
|
|
269
|
+
i0 = mdata.states_i0(counter=True)
|
|
270
|
+
trace_p = points[:, :, :2] - ref_xy[:, :, :2]
|
|
271
|
+
trace_si = np.zeros((n_states, n_points), dtype=FC.ITYPE)
|
|
272
|
+
trace_si[:] = i0 + np.arange(n_states)[:, None]
|
|
273
|
+
coeffs = np.full((n_states, n_points), np.nan, dtype=FC.DTYPE)
|
|
274
|
+
|
|
275
|
+
# flake8: noqa: F821
|
|
276
|
+
def _eval_trace(sel, hdxy=None, hdxy0=None, trs=None):
|
|
277
|
+
"""Helper function that updates trace_done"""
|
|
278
|
+
nonlocal coeffs
|
|
279
|
+
|
|
280
|
+
# project onto local x direction:
|
|
281
|
+
hdxy0 = dxy[trace_si[sel]] if hdxy0 is None else hdxy0
|
|
282
|
+
nx = hdxy0 / np.linalg.norm(hdxy0, axis=-1)[..., None]
|
|
283
|
+
projx = np.einsum("...d,...d->...", trace_p[sel], nx)
|
|
284
|
+
|
|
285
|
+
# check for local points:
|
|
286
|
+
if hdxy is None:
|
|
287
|
+
seld = (projx >= 1e-10) & (projx <= 1e-10)
|
|
288
|
+
if np.any(seld):
|
|
289
|
+
coeffs[sel] = np.where(seld, -1, coeffs[sel])
|
|
290
|
+
|
|
291
|
+
# check for vicinity to reference plane:
|
|
292
|
+
else:
|
|
293
|
+
lx = np.einsum("...d,...d->...", hdxy, nx)
|
|
294
|
+
seld = ((lx < 0) & (projx >= lx) & (projx <= 0)) | (
|
|
295
|
+
(lx > 0) & (projx >= 0) & (projx <= lx)
|
|
296
|
+
)
|
|
297
|
+
if np.any(seld):
|
|
298
|
+
w = projx / np.abs(lx)
|
|
299
|
+
coeffs[sel] = np.where(seld, w, coeffs[sel])
|
|
300
|
+
|
|
301
|
+
# step backwards in time, until projection onto axis is negative:
|
|
302
|
+
_eval_trace(sel=np.s_[:])
|
|
303
|
+
sel = np.isnan(coeffs)
|
|
304
|
+
tshift = 0
|
|
305
|
+
while np.any(sel):
|
|
306
|
+
|
|
307
|
+
trs = trace_si[sel]
|
|
308
|
+
hdxy = -dxy[trs]
|
|
309
|
+
trace_p[sel] += hdxy
|
|
310
|
+
|
|
311
|
+
_eval_trace(sel, hdxy=hdxy, hdxy0=dxy[trs - tshift])
|
|
312
|
+
|
|
313
|
+
tshift -= 1
|
|
314
|
+
sel0 = np.isnan(coeffs)
|
|
315
|
+
trace_si[sel0 & sel] -= 1
|
|
316
|
+
sel = sel0 & (trace_si >= 0)
|
|
317
|
+
|
|
318
|
+
del trs, sel0, hdxy
|
|
319
|
+
|
|
320
|
+
# step forwards in time, until projection onto axis is positive:
|
|
321
|
+
sel = np.isnan(coeffs)
|
|
322
|
+
if np.any(sel):
|
|
323
|
+
trace_p = np.where(
|
|
324
|
+
sel[:, :, None], points[:, :, :2] - ref_xy[:, :, :2], trace_p
|
|
325
|
+
)
|
|
326
|
+
trace_si = np.where(sel, i0 + np.arange(n_states)[:, None] + 1, trace_si)
|
|
327
|
+
|
|
328
|
+
sel &= trace_si < algo.n_states
|
|
329
|
+
tshift = 1
|
|
330
|
+
while np.any(sel):
|
|
331
|
+
|
|
332
|
+
trs = trace_si[sel]
|
|
333
|
+
hdxy = dxy[trs]
|
|
334
|
+
trace_p[sel] += hdxy
|
|
335
|
+
|
|
336
|
+
_eval_trace(sel, hdxy=hdxy, hdxy0=dxy[trs - tshift])
|
|
337
|
+
|
|
338
|
+
tshift += 1
|
|
339
|
+
sel0 = np.isnan(coeffs)
|
|
340
|
+
trace_si[sel0 & sel] += 1
|
|
341
|
+
sel = sel0 & (trace_si < algo.n_states)
|
|
342
|
+
|
|
343
|
+
del trs, sel0, hdxy
|
|
344
|
+
|
|
345
|
+
return trace_si, coeffs
|
|
346
|
+
|
|
347
|
+
def calculate(self, algo, mdata, fdata, tdata):
|
|
348
|
+
"""
|
|
349
|
+
The main model calculation.
|
|
350
|
+
|
|
351
|
+
This function is executed on a single chunk of data,
|
|
352
|
+
all computations should be based on numpy arrays.
|
|
353
|
+
|
|
354
|
+
Parameters
|
|
355
|
+
----------
|
|
356
|
+
algo: foxes.core.Algorithm
|
|
357
|
+
The calculation algorithm
|
|
358
|
+
mdata: foxes.core.MData
|
|
359
|
+
The model data
|
|
360
|
+
fdata: foxes.core.FData
|
|
361
|
+
The farm data
|
|
362
|
+
tdata: foxes.core.TData
|
|
363
|
+
The target point data
|
|
364
|
+
|
|
365
|
+
Returns
|
|
366
|
+
-------
|
|
367
|
+
results: dict
|
|
368
|
+
The resulting data, keys: output variable str.
|
|
369
|
+
Values: numpy.ndarray with shape
|
|
370
|
+
(n_states, n_targets, n_tpoints)
|
|
371
|
+
|
|
372
|
+
"""
|
|
373
|
+
# prepare:
|
|
374
|
+
targets = tdata[FC.TARGETS]
|
|
375
|
+
n_states, n_targets, n_tpoints = targets.shape[:3]
|
|
376
|
+
n_points = n_targets * n_tpoints
|
|
377
|
+
points = targets.reshape(n_states, n_points, 3)
|
|
378
|
+
heights = self.timelines_data["height"].to_numpy()
|
|
379
|
+
n_heights = len(heights)
|
|
380
|
+
|
|
381
|
+
# compute states indices for all requested points:
|
|
382
|
+
trace_si = []
|
|
383
|
+
coeffs = []
|
|
384
|
+
for hi in range(n_heights):
|
|
385
|
+
s, c = self.calc_states_indices(
|
|
386
|
+
algo, mdata, points, hi, self.ref_xy[None, None, :]
|
|
387
|
+
)
|
|
388
|
+
trace_si.append(s)
|
|
389
|
+
coeffs.append(c)
|
|
390
|
+
del s, c
|
|
391
|
+
|
|
392
|
+
# flake8: noqa: F821
|
|
393
|
+
def _interp_time(hi, v):
|
|
394
|
+
"""Helper function for interpolation bewteen states"""
|
|
395
|
+
|
|
396
|
+
sts = trace_si[hi]
|
|
397
|
+
cfs = coeffs[hi]
|
|
398
|
+
data = self.timelines_data[v].to_numpy()[hi]
|
|
399
|
+
out = np.zeros(sts.shape, dtype=FC.DTYPE)
|
|
400
|
+
|
|
401
|
+
sel_low = sts < 0
|
|
402
|
+
if np.any(sel_low):
|
|
403
|
+
out[sel_low] = data[0]
|
|
404
|
+
|
|
405
|
+
sel_hi = sts >= algo.n_states
|
|
406
|
+
if np.any(sel_hi):
|
|
407
|
+
out[sel_hi] = data[algo.n_states - 1]
|
|
408
|
+
|
|
409
|
+
sel = (~sel_low) & (~sel_hi) & (cfs <= 0)
|
|
410
|
+
if np.any(sel):
|
|
411
|
+
s = sts[sel]
|
|
412
|
+
c = -cfs[sel]
|
|
413
|
+
out[sel] = c * data[s] + (1 - c) * data[s - 1]
|
|
414
|
+
|
|
415
|
+
sel = (~sel_low) & (~sel_hi) & (cfs > 0)
|
|
416
|
+
if np.any(sel):
|
|
417
|
+
s = sts[sel]
|
|
418
|
+
c = cfs[sel]
|
|
419
|
+
out[sel] = c * data[s - 1] + (1 - c) * data[s]
|
|
420
|
+
|
|
421
|
+
return out
|
|
422
|
+
|
|
423
|
+
# interpolate to heights:
|
|
424
|
+
if n_heights > 1:
|
|
425
|
+
|
|
426
|
+
ar_states = np.arange(n_states)
|
|
427
|
+
ar_points = np.arange(n_points)
|
|
428
|
+
|
|
429
|
+
crds = (heights, ar_states, ar_points)
|
|
430
|
+
|
|
431
|
+
data = {
|
|
432
|
+
v: np.stack([_interp_time(hi, v) for hi in range(n_heights)], axis=0)
|
|
433
|
+
for v in self.timelines_data.data_vars.keys()
|
|
434
|
+
if v != "dxy"
|
|
435
|
+
}
|
|
436
|
+
vres = list(data.keys())
|
|
437
|
+
data = np.stack(list(data.values()), axis=-1)
|
|
438
|
+
|
|
439
|
+
eval = np.zeros((n_states, n_points, 3), dtype=FC.DTYPE)
|
|
440
|
+
eval[:, :, 0] = points[:, :, 2]
|
|
441
|
+
eval[:, :, 1] = ar_states[:, None]
|
|
442
|
+
eval[:, :, 2] = ar_points[None, :]
|
|
443
|
+
|
|
444
|
+
try:
|
|
445
|
+
ires = interpn(crds, data, eval, **self.intp_pars)
|
|
446
|
+
except ValueError as e:
|
|
447
|
+
print(f"\nStates '{self.name}': Interpolation error")
|
|
448
|
+
print("INPUT VARS: (heights, states, points)")
|
|
449
|
+
print(
|
|
450
|
+
"DATA BOUNDS:",
|
|
451
|
+
[float(np.min(d)) for d in crds],
|
|
452
|
+
[float(np.max(d)) for d in crds],
|
|
453
|
+
)
|
|
454
|
+
print(
|
|
455
|
+
"EVAL BOUNDS:",
|
|
456
|
+
[float(np.min(p)) for p in eval.T],
|
|
457
|
+
[float(np.max(p)) for p in eval.T],
|
|
458
|
+
)
|
|
459
|
+
print(
|
|
460
|
+
"\nMaybe you want to try the option 'bounds_error=False'? This will extrapolate the data.\n"
|
|
461
|
+
)
|
|
462
|
+
raise e
|
|
463
|
+
del crds, eval, data, ar_points, ar_states
|
|
464
|
+
|
|
465
|
+
results = {}
|
|
466
|
+
for v in self.output_point_vars(algo):
|
|
467
|
+
if v not in [FV.WS, FV.WD]:
|
|
468
|
+
results[v] = ires[:, :, vres.index(v)]
|
|
469
|
+
elif v not in results:
|
|
470
|
+
uv = np.stack(
|
|
471
|
+
[ires[:, :, vres.index("U")], ires[:, :, vres.index("V")]],
|
|
472
|
+
axis=-1,
|
|
473
|
+
)
|
|
474
|
+
results = {FV.WD: uv2wd(uv), FV.WS: np.linalg.norm(uv, axis=-1)}
|
|
475
|
+
del uv
|
|
476
|
+
|
|
477
|
+
# no dependence on height:
|
|
478
|
+
else:
|
|
479
|
+
results = {}
|
|
480
|
+
for v in self.output_point_vars(algo):
|
|
481
|
+
if v not in [FV.WS, FV.WD]:
|
|
482
|
+
results[v] = _interp_time(hi, v)
|
|
483
|
+
elif v not in results:
|
|
484
|
+
uv = np.stack(
|
|
485
|
+
[_interp_time(hi, "U"), _interp_time(hi, "V")], axis=-1
|
|
486
|
+
)
|
|
487
|
+
results = {FV.WD: uv2wd(uv), FV.WS: np.linalg.norm(uv, axis=-1)}
|
|
488
|
+
del uv
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
v: d.reshape(n_states, n_targets, n_tpoints) for v, d in results.items()
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
class OnePointFlowTimeseries(OnePointFlowStates):
|
|
496
|
+
"""
|
|
497
|
+
Inhomogeneous inflow from homogeneous timeseries data
|
|
498
|
+
at one point
|
|
499
|
+
|
|
500
|
+
:group: input.states
|
|
501
|
+
|
|
502
|
+
"""
|
|
503
|
+
|
|
504
|
+
def __init__(self, ref_xy, *args, tl_heights=None, **kwargs):
|
|
505
|
+
"""
|
|
506
|
+
Constructor.
|
|
507
|
+
|
|
508
|
+
Parameters
|
|
509
|
+
----------
|
|
510
|
+
ref_xy: list of float
|
|
511
|
+
The [x, y] or [x, y, z] coordinates of the base states.
|
|
512
|
+
If [x, y, z] then z will serve as height
|
|
513
|
+
args: tuple, optional
|
|
514
|
+
Parameters for the base class
|
|
515
|
+
tl_heights: list of float, optional
|
|
516
|
+
The heights at which timelines will be calculated
|
|
517
|
+
kwargs: dict, optional
|
|
518
|
+
Parameters for the base class
|
|
519
|
+
|
|
520
|
+
"""
|
|
521
|
+
if tl_heights is None and len(ref_xy) < 3:
|
|
522
|
+
tl_heights = [100.0]
|
|
523
|
+
super().__init__(
|
|
524
|
+
ref_xy,
|
|
525
|
+
*args,
|
|
526
|
+
tl_heights=tl_heights,
|
|
527
|
+
states_type="Timeseries",
|
|
528
|
+
**kwargs,
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
class OnePointFlowMultiHeightTimeseries(OnePointFlowStates):
|
|
533
|
+
"""
|
|
534
|
+
Inhomogeneous inflow from height dependent homogeneous
|
|
535
|
+
timeseries data at one point
|
|
536
|
+
|
|
537
|
+
:group: input.states
|
|
538
|
+
|
|
539
|
+
"""
|
|
540
|
+
|
|
541
|
+
def __init__(self, *args, **kwargs):
|
|
542
|
+
"""
|
|
543
|
+
Constructor.
|
|
544
|
+
|
|
545
|
+
Parameters
|
|
546
|
+
----------
|
|
547
|
+
args: tuple, optional
|
|
548
|
+
Parameters for the base class
|
|
549
|
+
kwargs: dict, optional
|
|
550
|
+
Parameters for the base class
|
|
551
|
+
|
|
552
|
+
"""
|
|
553
|
+
super().__init__(*args, states_type="MultiHeightTimeseries", **kwargs)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class OnePointFlowMultiHeightNCTimeseries(OnePointFlowStates):
|
|
557
|
+
"""
|
|
558
|
+
Inhomogeneous inflow from height dependent homogeneous
|
|
559
|
+
timeseries data at one point based on NetCDF input
|
|
560
|
+
|
|
561
|
+
:group: input.states
|
|
562
|
+
|
|
563
|
+
"""
|
|
564
|
+
|
|
565
|
+
def __init__(self, *args, **kwargs):
|
|
566
|
+
"""
|
|
567
|
+
Constructor.
|
|
568
|
+
|
|
569
|
+
Parameters
|
|
570
|
+
----------
|
|
571
|
+
args: tuple, optional
|
|
572
|
+
Parameters for the base class
|
|
573
|
+
kwargs: dict, optional
|
|
574
|
+
Parameters for the base class
|
|
575
|
+
|
|
576
|
+
"""
|
|
577
|
+
super().__init__(*args, states_type="MultiHeightNCTimeseries", **kwargs)
|
foxes/input/states/scan_ws.py
CHANGED
|
@@ -40,7 +40,7 @@ class ScanWS(States):
|
|
|
40
40
|
"""
|
|
41
41
|
super().__init__()
|
|
42
42
|
|
|
43
|
-
self.
|
|
43
|
+
self.__wsl = np.array(ws_list)
|
|
44
44
|
self.N = len(ws_list)
|
|
45
45
|
self.wd = wd
|
|
46
46
|
self.ti = ti
|
|
@@ -72,10 +72,81 @@ class ScanWS(States):
|
|
|
72
72
|
self.WS = self.var(FV.WS)
|
|
73
73
|
|
|
74
74
|
idata = super().load_data(algo, verbosity)
|
|
75
|
-
idata["data_vars"][self.WS] = ((FC.STATE,), self.
|
|
75
|
+
idata["data_vars"][self.WS] = ((FC.STATE,), self.__wsl)
|
|
76
76
|
|
|
77
77
|
return idata
|
|
78
78
|
|
|
79
|
+
def set_running(
|
|
80
|
+
self,
|
|
81
|
+
algo,
|
|
82
|
+
data_stash,
|
|
83
|
+
sel=None,
|
|
84
|
+
isel=None,
|
|
85
|
+
verbosity=0,
|
|
86
|
+
):
|
|
87
|
+
"""
|
|
88
|
+
Sets this model status to running, and moves
|
|
89
|
+
all large data to stash.
|
|
90
|
+
|
|
91
|
+
The stashed data will be returned by the
|
|
92
|
+
unset_running() function after running calculations.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
algo: foxes.core.Algorithm
|
|
97
|
+
The calculation algorithm
|
|
98
|
+
data_stash: dict
|
|
99
|
+
Large data stash, this function adds data here.
|
|
100
|
+
Key: model name. Value: dict, large model data
|
|
101
|
+
sel: dict, optional
|
|
102
|
+
The subset selection dictionary
|
|
103
|
+
isel: dict, optional
|
|
104
|
+
The index subset selection dictionary
|
|
105
|
+
verbosity: int
|
|
106
|
+
The verbosity level, 0 = silent
|
|
107
|
+
|
|
108
|
+
"""
|
|
109
|
+
super().set_running(algo, data_stash, sel, isel, verbosity)
|
|
110
|
+
|
|
111
|
+
data_stash[self.name].update(
|
|
112
|
+
dict(
|
|
113
|
+
wsl=self.__wsl,
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
del self.__wsl
|
|
117
|
+
|
|
118
|
+
def unset_running(
|
|
119
|
+
self,
|
|
120
|
+
algo,
|
|
121
|
+
data_stash,
|
|
122
|
+
sel=None,
|
|
123
|
+
isel=None,
|
|
124
|
+
verbosity=0,
|
|
125
|
+
):
|
|
126
|
+
"""
|
|
127
|
+
Sets this model status to not running, recovering large data
|
|
128
|
+
from stash
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
algo: foxes.core.Algorithm
|
|
133
|
+
The calculation algorithm
|
|
134
|
+
data_stash: dict
|
|
135
|
+
Large data stash, this function adds data here.
|
|
136
|
+
Key: model name. Value: dict, large model data
|
|
137
|
+
sel: dict, optional
|
|
138
|
+
The subset selection dictionary
|
|
139
|
+
isel: dict, optional
|
|
140
|
+
The index subset selection dictionary
|
|
141
|
+
verbosity: int
|
|
142
|
+
The verbosity level, 0 = silent
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
super().unset_running(algo, data_stash, sel, isel, verbosity)
|
|
146
|
+
|
|
147
|
+
data = data_stash[self.name]
|
|
148
|
+
self.__wsl = data.pop("wsl")
|
|
149
|
+
|
|
79
150
|
def size(self):
|
|
80
151
|
"""
|
|
81
152
|
The total number of states.
|
|
@@ -130,7 +201,7 @@ class ScanWS(States):
|
|
|
130
201
|
return np.full((self.N, algo.n_turbines), 1.0 / self.N, dtype=FC.DTYPE)
|
|
131
202
|
|
|
132
203
|
def calculate(self, algo, mdata, fdata, tdata):
|
|
133
|
-
"""
|
|
204
|
+
"""
|
|
134
205
|
The main model calculation.
|
|
135
206
|
|
|
136
207
|
This function is executed on a single chunk of data,
|
foxes/input/states/single.py
CHANGED
|
@@ -166,7 +166,7 @@ class SingleStateStates(States):
|
|
|
166
166
|
return np.ones((1, algo.n_turbines), dtype=FC.DTYPE)
|
|
167
167
|
|
|
168
168
|
def calculate(self, algo, mdata, fdata, tdata):
|
|
169
|
-
"""
|
|
169
|
+
"""
|
|
170
170
|
The main model calculation.
|
|
171
171
|
|
|
172
172
|
This function is executed on a single chunk of data,
|