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
foxes/input/states/wrg_states.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
from scipy.interpolate import interpn
|
|
2
3
|
|
|
3
4
|
from foxes.core.states import States
|
|
4
5
|
from foxes.config import config, get_input_path
|
|
5
|
-
from foxes.utils
|
|
6
|
+
from foxes.utils import ReaderWRG, weibull_weights
|
|
6
7
|
from foxes.data import STATES
|
|
7
8
|
import foxes.variables as FV
|
|
8
9
|
import foxes.constants as FC
|
|
@@ -25,6 +26,8 @@ class WRGStates(States):
|
|
|
25
26
|
bounds_extra_space: float or str
|
|
26
27
|
The extra space, either float in m,
|
|
27
28
|
or str for units of D, e.g. '2.5D'
|
|
29
|
+
interpn_pars: dict
|
|
30
|
+
Additional parameters for scipy.interpolate.interpn
|
|
28
31
|
|
|
29
32
|
:group: input.states
|
|
30
33
|
|
|
@@ -36,7 +39,7 @@ class WRGStates(States):
|
|
|
36
39
|
ws_bins,
|
|
37
40
|
fixed_vars={},
|
|
38
41
|
bounds_extra_space="1D",
|
|
39
|
-
**
|
|
42
|
+
**interpn_pars,
|
|
40
43
|
):
|
|
41
44
|
"""
|
|
42
45
|
Constructor
|
|
@@ -54,15 +57,16 @@ class WRGStates(States):
|
|
|
54
57
|
bounds_extra_space: float or str, optional
|
|
55
58
|
The extra space, either float in m,
|
|
56
59
|
or str for units of D, e.g. '2.5D'
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
interpn_pars: dict, optional
|
|
61
|
+
Additional parameters for scipy.interpolate.interpn
|
|
59
62
|
|
|
60
63
|
"""
|
|
61
|
-
super().__init__(
|
|
64
|
+
super().__init__()
|
|
62
65
|
self.wrg_fname = wrg_fname
|
|
63
66
|
self.ws_bins = np.asarray(ws_bins)
|
|
64
67
|
self.fixed_vars = fixed_vars
|
|
65
68
|
self.bounds_extra_space = bounds_extra_space
|
|
69
|
+
self.interpn_pars = interpn_pars
|
|
66
70
|
|
|
67
71
|
def load_data(self, algo, verbosity=0):
|
|
68
72
|
"""
|
|
@@ -100,11 +104,14 @@ class WRGStates(States):
|
|
|
100
104
|
elif verbosity:
|
|
101
105
|
print(f"States '{self.name}': Reading file {fpath}")
|
|
102
106
|
wrg = ReaderWRG(fpath)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
p0 = np.array([wrg.x0, wrg.y0], dtype=config.dtype_double)
|
|
108
|
+
nx = wrg.nx
|
|
109
|
+
ny = wrg.ny
|
|
110
|
+
ns = wrg.n_sectors
|
|
111
|
+
res = wrg.resolution
|
|
112
|
+
p1 = p0 + np.array([nx * res, ny * res])
|
|
113
|
+
if verbosity > 0:
|
|
114
|
+
print(f"States '{self.name}': Data bounds {p0} - {p1}")
|
|
108
115
|
|
|
109
116
|
# find bounds:
|
|
110
117
|
if self.bounds_extra_space is not None:
|
|
@@ -112,48 +119,56 @@ class WRGStates(States):
|
|
|
112
119
|
extra_space=self.bounds_extra_space, algo=algo
|
|
113
120
|
)
|
|
114
121
|
if verbosity > 0:
|
|
115
|
-
print(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
xy_min -= self._p0
|
|
119
|
-
xy_max -= self._p0
|
|
120
|
-
ij_min = np.asarray(xy_min / self._res, dtype=config.dtype_int)
|
|
121
|
-
ij_max = np.asarray(xy_max / self._res, dtype=config.dtype_int) + 1
|
|
122
|
+
print(f"States '{self.name}': Farm bounds {xy_min} - {xy_max}")
|
|
123
|
+
ij_min = np.asarray((xy_min - p0) / res, dtype=config.dtype_int)
|
|
124
|
+
ij_max = np.asarray((xy_max - p0) / res, dtype=config.dtype_int) + 1
|
|
122
125
|
sx = slice(ij_min[0], ij_max[0])
|
|
123
126
|
sy = slice(ij_min[1], ij_max[1])
|
|
124
127
|
else:
|
|
125
128
|
sx = np.s_[:]
|
|
126
129
|
sy = np.s_[:]
|
|
130
|
+
self._x = p0[0] + np.arange(nx) * res
|
|
131
|
+
self._x = self._x[sx]
|
|
132
|
+
self._y = p0[1] + np.arange(ny) * res
|
|
133
|
+
self._y = self._y[sy]
|
|
134
|
+
if len(self._x) < 2 or len(self._y) < 2:
|
|
135
|
+
raise ValueError("No overlap between data bounds and farm bounds")
|
|
136
|
+
p0[0] = np.min(self._x)
|
|
137
|
+
p0[1] = np.min(self._y)
|
|
138
|
+
p1[0] = np.max(self._x)
|
|
139
|
+
p1[1] = np.max(self._y)
|
|
140
|
+
if verbosity > 0:
|
|
141
|
+
print(f"States '{self.name}': New bounds {p0} - {p1}")
|
|
127
142
|
|
|
128
143
|
# store data:
|
|
129
144
|
A = []
|
|
130
145
|
k = []
|
|
131
|
-
|
|
132
|
-
for s in range(
|
|
133
|
-
A.append(wrg.data[f"
|
|
134
|
-
k.append(wrg.data[f"
|
|
135
|
-
|
|
136
|
-
wrg.data[f"fs_{s}"].to_numpy().reshape(self._ny, self._nx)[sy, sx]
|
|
137
|
-
)
|
|
146
|
+
f = []
|
|
147
|
+
for s in range(ns):
|
|
148
|
+
A.append(wrg.data[f"As_{s}"].to_numpy().reshape(ny, nx)[sy, sx])
|
|
149
|
+
k.append(wrg.data[f"Ks_{s}"].to_numpy().reshape(ny, nx)[sy, sx])
|
|
150
|
+
f.append(wrg.data[f"fs_{s}"].to_numpy().reshape(ny, nx)[sy, sx])
|
|
138
151
|
del wrg
|
|
139
152
|
A = np.stack(A, axis=0).T
|
|
140
153
|
k = np.stack(k, axis=0).T
|
|
141
|
-
|
|
142
|
-
self._data = np.stack([A, k,
|
|
154
|
+
f = np.stack(f, axis=0).T
|
|
155
|
+
self._data = np.stack([A, k, f], axis=-1) # (x, y, wd, AKfs)
|
|
143
156
|
|
|
144
157
|
# store ws and wd:
|
|
145
|
-
self.
|
|
146
|
-
self.
|
|
158
|
+
self.VARS = self.var("VARS")
|
|
159
|
+
self.DATA = self.var("DATA")
|
|
160
|
+
self._wds = np.arange(0.0, 360.0, 360 / ns)
|
|
147
161
|
self._wsd = self.ws_bins[1:] - self.ws_bins[:-1]
|
|
148
162
|
self._wss = 0.5 * (self.ws_bins[:-1] + self.ws_bins[1:])
|
|
149
|
-
self._N = len(self._wss) *
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
163
|
+
self._N = len(self._wss) * ns
|
|
164
|
+
data = np.zeros((len(self._wss), ns, 3), dtype=config.dtype_double)
|
|
165
|
+
data[..., 0] = self._wss[:, None]
|
|
166
|
+
data[..., 1] = self._wds[None, :]
|
|
167
|
+
data[..., 2] = self._wsd[:, None]
|
|
168
|
+
data = data.reshape(self._N, 3)
|
|
154
169
|
idata = super().load_data(algo, verbosity)
|
|
155
|
-
idata
|
|
156
|
-
idata
|
|
170
|
+
idata["coords"][self.VARS] = ["ws", "wd", "dws"]
|
|
171
|
+
idata["data_vars"][self.DATA] = ((FC.STATE, self.VARS), data)
|
|
157
172
|
|
|
158
173
|
return idata
|
|
159
174
|
|
|
@@ -184,6 +199,105 @@ class WRGStates(States):
|
|
|
184
199
|
The output variable names
|
|
185
200
|
|
|
186
201
|
"""
|
|
187
|
-
ovars = set([FV.WS, FV.WD
|
|
188
|
-
ovars.update(self.fixed_vars.
|
|
202
|
+
ovars = set([FV.WS, FV.WD])
|
|
203
|
+
ovars.update(self.fixed_vars.keys())
|
|
189
204
|
return list(ovars)
|
|
205
|
+
|
|
206
|
+
def calculate(self, algo, mdata, fdata, tdata):
|
|
207
|
+
"""
|
|
208
|
+
The main model calculation.
|
|
209
|
+
|
|
210
|
+
This function is executed on a single chunk of data,
|
|
211
|
+
all computations should be based on numpy arrays.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
algo: foxes.core.Algorithm
|
|
216
|
+
The calculation algorithm
|
|
217
|
+
mdata: foxes.core.MData
|
|
218
|
+
The model data
|
|
219
|
+
fdata: foxes.core.FData
|
|
220
|
+
The farm data
|
|
221
|
+
tdata: foxes.core.TData
|
|
222
|
+
The target point data
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
results: dict
|
|
227
|
+
The resulting data, keys: output variable str.
|
|
228
|
+
Values: numpy.ndarray with shape
|
|
229
|
+
(n_states, n_targets, n_tpoints)
|
|
230
|
+
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
# prepare:
|
|
234
|
+
n_states = tdata.n_states
|
|
235
|
+
n_targets = tdata.n_targets
|
|
236
|
+
n_tpoints = tdata.n_tpoints
|
|
237
|
+
n_pts = n_states * n_targets * n_tpoints
|
|
238
|
+
points = tdata[FC.TARGETS]
|
|
239
|
+
ws = mdata[self.DATA][:, 0]
|
|
240
|
+
wd = mdata[self.DATA][:, 1]
|
|
241
|
+
wsd = mdata[self.DATA][:, 2]
|
|
242
|
+
|
|
243
|
+
out = {}
|
|
244
|
+
|
|
245
|
+
out[FV.WS] = tdata[FV.WS]
|
|
246
|
+
out[FV.WS][:] = ws[:, None, None]
|
|
247
|
+
|
|
248
|
+
out[FV.WD] = tdata[FV.WD]
|
|
249
|
+
out[FV.WD][:] = wd[:, None, None]
|
|
250
|
+
|
|
251
|
+
for v, d in self.fixed_vars.items():
|
|
252
|
+
out[v] = tdata[v]
|
|
253
|
+
out[v][:] = d
|
|
254
|
+
|
|
255
|
+
# interpolate A, k, f from x, y, wd
|
|
256
|
+
z = points[..., 2].copy()
|
|
257
|
+
points[..., 2] = wd[:, None, None]
|
|
258
|
+
pts = points.reshape(n_pts, 3)
|
|
259
|
+
gvars = (self._x, self._y, self._wds)
|
|
260
|
+
try:
|
|
261
|
+
ipars = dict(bounds_error=True, fill_value=None)
|
|
262
|
+
ipars.update(self.interpn_pars)
|
|
263
|
+
data = interpn(gvars, self._data, pts, **ipars).reshape(
|
|
264
|
+
n_states, n_targets, n_tpoints, 3
|
|
265
|
+
)
|
|
266
|
+
except ValueError as e:
|
|
267
|
+
print(f"\nStates '{self.name}': Interpolation error")
|
|
268
|
+
print("INPUT VARS: (x, y, wd)")
|
|
269
|
+
print(
|
|
270
|
+
"DATA BOUNDS:",
|
|
271
|
+
[float(np.min(d)) for d in gvars],
|
|
272
|
+
[float(np.max(d)) for d in gvars],
|
|
273
|
+
)
|
|
274
|
+
print(
|
|
275
|
+
"EVAL BOUNDS:",
|
|
276
|
+
[float(np.min(p)) for p in pts.T],
|
|
277
|
+
[float(np.max(p)) for p in pts.T],
|
|
278
|
+
)
|
|
279
|
+
print(
|
|
280
|
+
"\nMaybe you want to try the option 'bounds_error=False'? This will extrapolate the data.\n"
|
|
281
|
+
)
|
|
282
|
+
raise e
|
|
283
|
+
|
|
284
|
+
A = data[..., 0]
|
|
285
|
+
k = data[..., 1]
|
|
286
|
+
f = data[..., 2]
|
|
287
|
+
points[..., 2] = z
|
|
288
|
+
del data, gvars, pts, z, wd
|
|
289
|
+
|
|
290
|
+
tdata.add(
|
|
291
|
+
FV.WEIGHT,
|
|
292
|
+
f,
|
|
293
|
+
dims=(FC.STATE, FC.TARGET, FC.TPOINT),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
tdata[FV.WEIGHT] *= weibull_weights(
|
|
297
|
+
ws=out[FV.WS],
|
|
298
|
+
ws_deltas=wsd[:, None, None],
|
|
299
|
+
A=A,
|
|
300
|
+
k=k,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
return out
|
foxes/input/yaml/__init__.py
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
from .dict import read_dict
|
|
2
|
-
from .
|
|
3
|
-
from .
|
|
1
|
+
from .dict import read_dict as read_dict
|
|
2
|
+
from .dict import run_dict as run_dict
|
|
3
|
+
from .dict import run_outputs as run_outputs
|
|
4
|
+
from .dict import run_obj_function as run_obj_function
|
|
5
|
+
from .dict import get_output_obj as get_output_obj
|
|
6
|
+
|
|
7
|
+
from .yaml import foxes_yaml as foxes_yaml
|
|
8
|
+
from .windio.windio import read_windio as read_windio
|
|
9
|
+
from .windio.windio import foxes_windio as foxes_windio
|
foxes/input/yaml/dict.py
CHANGED
|
@@ -75,13 +75,13 @@ def read_dict(
|
|
|
75
75
|
print(*args, **kwargs)
|
|
76
76
|
|
|
77
77
|
# set working directory:
|
|
78
|
-
|
|
78
|
+
ld = 0
|
|
79
79
|
for c, d in zip(
|
|
80
80
|
[FC.WORK_DIR, FC.INPUT_DIR, FC.OUTPUT_DIR], [work_dir, input_dir, output_dir]
|
|
81
81
|
):
|
|
82
82
|
if d is not None:
|
|
83
83
|
config[c] = d
|
|
84
|
-
|
|
84
|
+
ld = max(ld, len(str(d)))
|
|
85
85
|
_print("\n--------------------- Reading foxes parameter dict ---------------------")
|
|
86
86
|
_print("Working directory :", config.work_dir)
|
|
87
87
|
_print("Input directory :", config.input_dir)
|
|
@@ -95,9 +95,9 @@ def read_dict(
|
|
|
95
95
|
else:
|
|
96
96
|
states = algo.states
|
|
97
97
|
else:
|
|
98
|
-
assert (
|
|
99
|
-
algo
|
|
100
|
-
)
|
|
98
|
+
assert algo is None, (
|
|
99
|
+
"Cannot handle both the algo and the states argument, please drop one"
|
|
100
|
+
)
|
|
101
101
|
|
|
102
102
|
# create model book:
|
|
103
103
|
if mbook is None:
|
|
@@ -119,9 +119,9 @@ def read_dict(
|
|
|
119
119
|
else:
|
|
120
120
|
mbook = algo.mbook
|
|
121
121
|
else:
|
|
122
|
-
assert (
|
|
123
|
-
algo
|
|
124
|
-
)
|
|
122
|
+
assert algo is None, (
|
|
123
|
+
"Cannot handle both the algo and the mbook argument, please drop one"
|
|
124
|
+
)
|
|
125
125
|
|
|
126
126
|
# create farm:
|
|
127
127
|
if farm is None:
|
|
@@ -129,8 +129,8 @@ def read_dict(
|
|
|
129
129
|
_print("Creating wind farm")
|
|
130
130
|
fdict = idict.get_item("wind_farm")
|
|
131
131
|
lyts = [
|
|
132
|
-
Dict(
|
|
133
|
-
for i,
|
|
132
|
+
Dict(lo, name=f"{fdict.name}.layout{i}")
|
|
133
|
+
for i, lo in enumerate(fdict.pop_item("layouts"))
|
|
134
134
|
]
|
|
135
135
|
farm = WindFarm(**fdict)
|
|
136
136
|
for lyt in lyts:
|
|
@@ -141,9 +141,9 @@ def read_dict(
|
|
|
141
141
|
else:
|
|
142
142
|
farm = algo.farm
|
|
143
143
|
else:
|
|
144
|
-
assert (
|
|
145
|
-
algo
|
|
146
|
-
)
|
|
144
|
+
assert algo is None, (
|
|
145
|
+
"Cannot handle both the algo and the farm argument, please drop one"
|
|
146
|
+
)
|
|
147
147
|
|
|
148
148
|
# create engine:
|
|
149
149
|
engine = None
|
|
@@ -321,12 +321,12 @@ def run_obj_function(
|
|
|
321
321
|
|
|
322
322
|
def _set_label(rlabels, k, r):
|
|
323
323
|
if k not in ["", "none", "None", "_", "__"]:
|
|
324
|
-
assert (
|
|
325
|
-
|
|
326
|
-
)
|
|
327
|
-
assert (
|
|
328
|
-
"
|
|
329
|
-
)
|
|
324
|
+
assert k[0] == "$", (
|
|
325
|
+
f"Output {i} of type '{ocls}', function '{fname}': result labels must start with '$', got '{k}'"
|
|
326
|
+
)
|
|
327
|
+
assert "[" not in k and "]" not in k and "," not in k, (
|
|
328
|
+
f"Output {i} of type '{ocls}', function '{fname}': result labels cannot contain '[' or ']' or comma, got '{k}'"
|
|
329
|
+
)
|
|
330
330
|
_print(f" result label {k}: {type(r).__name__}")
|
|
331
331
|
rlabels[k] = r
|
|
332
332
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
from .windio import foxes_windio
|
|
2
|
-
from .
|
|
3
|
-
|
|
4
|
-
from .read_farm import read_layout
|
|
5
|
-
from .
|
|
1
|
+
from .windio import foxes_windio as foxes_windio
|
|
2
|
+
from .windio import read_windio as read_windio
|
|
3
|
+
|
|
4
|
+
from .read_farm import read_layout as read_layout
|
|
5
|
+
from .read_farm import read_turbine_types as read_turbine_types
|
|
6
|
+
from .read_farm import read_farm as read_farm
|
|
7
|
+
|
|
8
|
+
from .read_site import read_site as read_site
|
|
9
|
+
from .read_fields import read_wind_resource_field as read_wind_resource_field
|
|
10
|
+
from .read_attributes import read_attributes as read_attributes
|
|
@@ -202,7 +202,7 @@ def _read_rotor_averaging(rotor_averaging, algo_dict, verbosity):
|
|
|
202
202
|
elif background_averaging in ["none", "None", None]:
|
|
203
203
|
algo_dict["rotor_model"] = None
|
|
204
204
|
elif background_averaging == "grid":
|
|
205
|
-
algo_dict["rotor_model"] = f"grid{nx*ny}"
|
|
205
|
+
algo_dict["rotor_model"] = f"grid{nx * ny}"
|
|
206
206
|
else:
|
|
207
207
|
algo_dict["rotor_model"] = background_averaging
|
|
208
208
|
|
|
@@ -215,7 +215,7 @@ def _read_rotor_averaging(rotor_averaging, algo_dict, verbosity):
|
|
|
215
215
|
algo_dict["partial_wakes"] = "rotor_points"
|
|
216
216
|
else:
|
|
217
217
|
if grid == "grid":
|
|
218
|
-
algo_dict["partial_wakes"] = f"grid{nx*ny}"
|
|
218
|
+
algo_dict["partial_wakes"] = f"grid{nx * ny}"
|
|
219
219
|
else:
|
|
220
220
|
algo_dict["partial_wakes"] = grid
|
|
221
221
|
else:
|
|
@@ -77,7 +77,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
|
|
|
77
77
|
return FV.REWS if wse == 1 else (FV.REWS2 if wse == 2 else FV.REWS3)
|
|
78
78
|
|
|
79
79
|
_print(f" Creating model '{tname}'", level=3)
|
|
80
|
-
_print(
|
|
80
|
+
_print(" Turbine type class: PCtFomTwo", level=3)
|
|
81
81
|
mbook.turbine_types[tname] = TurbineType.new(
|
|
82
82
|
ttype_type="PCtFromTwo",
|
|
83
83
|
data_source_P=data_P,
|
|
@@ -111,7 +111,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
|
|
|
111
111
|
data_ct = pd.DataFrame(data={"ws": ws_ct, "ct": ct})
|
|
112
112
|
|
|
113
113
|
_print(f" Creating model '{tname}'", level=3)
|
|
114
|
-
_print(
|
|
114
|
+
_print(" Turbine type class: CpCtFromTwo", level=3)
|
|
115
115
|
mbook.turbine_types[tname] = TurbineType.new(
|
|
116
116
|
ttype_type="CpCtFromTwo",
|
|
117
117
|
data_source_cp=data_cp,
|
|
@@ -126,7 +126,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
|
|
|
126
126
|
_print(" ", mbook.turbine_types[tname], level=3)
|
|
127
127
|
|
|
128
128
|
else:
|
|
129
|
-
raise KeyError(
|
|
129
|
+
raise KeyError("Expecting either 'power_curve' or 'Cp_curve'")
|
|
130
130
|
|
|
131
131
|
return ttypes
|
|
132
132
|
|
|
@@ -157,7 +157,7 @@ def read_layout(lname, ldict, farm, ttypes, verbosity=1):
|
|
|
157
157
|
cdict = Dict(ldict["coordinates"], name="coordinates")
|
|
158
158
|
tmap = ldict.get_item("turbine_types", None)
|
|
159
159
|
if verbosity > 2:
|
|
160
|
-
print(
|
|
160
|
+
print(" Turbine type map:", tmap)
|
|
161
161
|
for i, xy in enumerate(zip(cdict["x"], cdict["y"])):
|
|
162
162
|
tt = ttypes[tmap[i] if tmap is not None else 0]
|
|
163
163
|
farm.add_turbine(
|
|
@@ -211,7 +211,7 @@ def read_farm(wio_dict, mbook, verbosity):
|
|
|
211
211
|
if isinstance(wfarm, dict):
|
|
212
212
|
layouts = Dict(wfarm, name=wio_farm.name + ".layouts")
|
|
213
213
|
else:
|
|
214
|
-
layouts = {str(i):
|
|
214
|
+
layouts = {str(i): lf for i, lf in enumerate(wfarm)}
|
|
215
215
|
layouts = Dict(layouts, name=wio_farm.name + ".layouts")
|
|
216
216
|
if verbosity > 2:
|
|
217
217
|
print(" Reading layouts")
|
|
@@ -22,6 +22,8 @@ wio2foxes = {
|
|
|
22
22
|
"LMO": FV.MOL,
|
|
23
23
|
"z0": FV.Z0,
|
|
24
24
|
"reference_height": FV.H,
|
|
25
|
+
"weibull_a": FV.WEIBULL_A,
|
|
26
|
+
"weibull_k": FV.WEIBULL_k,
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
""" Mapping from foxes to windio variables
|
|
@@ -134,8 +136,6 @@ def read_wind_resource_field(
|
|
|
134
136
|
|
|
135
137
|
"""
|
|
136
138
|
if name in [
|
|
137
|
-
"weibull_a",
|
|
138
|
-
"weibull_k",
|
|
139
139
|
"potential_temperature",
|
|
140
140
|
"friction_velocity",
|
|
141
141
|
"k",
|
|
@@ -178,6 +178,8 @@ def read_wind_resource_field(
|
|
|
178
178
|
"LMO",
|
|
179
179
|
"z0",
|
|
180
180
|
"reference_height",
|
|
181
|
+
"weibull_a",
|
|
182
|
+
"weibull_k",
|
|
181
183
|
] and _read_multi_dimensional_data(name, wio_data, fields, dims):
|
|
182
184
|
return True
|
|
183
185
|
|
|
@@ -159,6 +159,55 @@ def _get_MultiHeightNCTimeseries(
|
|
|
159
159
|
return False
|
|
160
160
|
|
|
161
161
|
|
|
162
|
+
def _get_WeibullSectors(
|
|
163
|
+
coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
|
|
164
|
+
):
|
|
165
|
+
"""Try to generate Weibull sector parameters
|
|
166
|
+
:group: input.yaml.windio
|
|
167
|
+
"""
|
|
168
|
+
if (
|
|
169
|
+
FV.WEIBULL_A in fields
|
|
170
|
+
and FV.WEIBULL_k in fields
|
|
171
|
+
and "sector_probability" in fields
|
|
172
|
+
and len(dims[FV.WEIBULL_A]) == 1
|
|
173
|
+
and len(dims[FV.WEIBULL_k]) == 1
|
|
174
|
+
and len(dims["sector_probability"]) == 1
|
|
175
|
+
):
|
|
176
|
+
if verbosity > 2:
|
|
177
|
+
print(" selecting class 'WeibullSectors'")
|
|
178
|
+
|
|
179
|
+
data = {}
|
|
180
|
+
fix = {}
|
|
181
|
+
c = dims[FV.WEIBULL_A][0]
|
|
182
|
+
for v, d in fields.items():
|
|
183
|
+
if dims[v] == (c,):
|
|
184
|
+
data[v] = d
|
|
185
|
+
elif len(dims[v]) == 0:
|
|
186
|
+
fix[v] = d
|
|
187
|
+
elif verbosity > 2:
|
|
188
|
+
print(f" ignoring field '{v}' with dims {dims[v]}")
|
|
189
|
+
fix.update({v: d for v, d in fixval.items() if v not in data})
|
|
190
|
+
|
|
191
|
+
if FV.WD in coords:
|
|
192
|
+
data[FV.WD] = coords[FV.WD]
|
|
193
|
+
|
|
194
|
+
sdata = pd.DataFrame(index=range(len(fields[FV.WEIBULL_A])), data=data)
|
|
195
|
+
sdata.index.name = "sector"
|
|
196
|
+
states_dict.update(
|
|
197
|
+
dict(
|
|
198
|
+
states_type="WeibullSectors",
|
|
199
|
+
data_source=sdata,
|
|
200
|
+
ws_bins=coords.get(FV.WS, np.arange(30)),
|
|
201
|
+
output_vars=ovars,
|
|
202
|
+
var2ncvar={FV.WEIGHT: "sector_probability"},
|
|
203
|
+
fixed_vars=fix,
|
|
204
|
+
profiles=profiles,
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
return True
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
|
|
162
211
|
def get_states(coords, fields, dims, verbosity=1):
|
|
163
212
|
"""
|
|
164
213
|
Reads states parameters from windio input
|
|
@@ -200,6 +249,9 @@ def get_states(coords, fields, dims, verbosity=1):
|
|
|
200
249
|
or _get_MultiHeightNCTimeseries(
|
|
201
250
|
coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
|
|
202
251
|
)
|
|
252
|
+
or _get_WeibullSectors(
|
|
253
|
+
coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
|
|
254
|
+
)
|
|
203
255
|
):
|
|
204
256
|
return States.new(**states_dict)
|
|
205
257
|
else:
|
|
@@ -43,7 +43,7 @@ def read_windio(wio_dict, verbosity=1):
|
|
|
43
43
|
if not isinstance(wio_dict, Dict):
|
|
44
44
|
wio_dict = Dict(wio_dict, name="windio")
|
|
45
45
|
|
|
46
|
-
_print(
|
|
46
|
+
_print("Reading windio data")
|
|
47
47
|
_print(" Name:", wio_dict.pop_item("name", None))
|
|
48
48
|
_print(" Contents:", [k for k in wio_dict.keys()])
|
|
49
49
|
|
foxes/models/__init__.py
CHANGED
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
Model collection.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from . import turbine_types
|
|
6
|
-
from . import rotor_models
|
|
7
|
-
from . import turbine_models
|
|
8
|
-
from . import farm_models
|
|
9
|
-
from . import partial_wakes
|
|
10
|
-
from . import wake_frames
|
|
11
|
-
from . import wake_models
|
|
12
|
-
from . import
|
|
13
|
-
from . import
|
|
14
|
-
from . import
|
|
15
|
-
from . import
|
|
16
|
-
from . import
|
|
17
|
-
from . import
|
|
5
|
+
from . import turbine_types as turbine_types
|
|
6
|
+
from . import rotor_models as rotor_models
|
|
7
|
+
from . import turbine_models as turbine_models
|
|
8
|
+
from . import farm_models as farm_models
|
|
9
|
+
from . import partial_wakes as partial_wakes
|
|
10
|
+
from . import wake_frames as wake_frames
|
|
11
|
+
from . import wake_models as wake_models
|
|
12
|
+
from . import wake_deflections as wake_deflections
|
|
13
|
+
from . import wake_superpositions as wake_superpositions
|
|
14
|
+
from . import farm_controllers as farm_controllers
|
|
15
|
+
from . import vertical_profiles as vertical_profiles
|
|
16
|
+
from . import point_models as point_models
|
|
17
|
+
from . import axial_induction as axial_induction
|
|
18
|
+
from . import ground_models as ground_models
|
|
18
19
|
|
|
19
|
-
from .model_book import ModelBook
|
|
20
|
+
from .model_book import ModelBook as ModelBook
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
from .betz import BetzAxialInduction
|
|
2
|
-
from .madsen import MadsenAxialInduction
|
|
1
|
+
from .betz import BetzAxialInduction as BetzAxialInduction
|
|
2
|
+
from .madsen import MadsenAxialInduction as MadsenAxialInduction
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
from .no_ground import NoGround
|
|
2
|
-
from .wake_mirror import WakeMirror
|
|
1
|
+
from .no_ground import NoGround as NoGround
|
|
2
|
+
from .wake_mirror import WakeMirror as WakeMirror
|
|
3
|
+
from .wake_mirror import GroundMirror as GroundMirror
|
|
@@ -75,13 +75,14 @@ class WakeMirror(GroundModel):
|
|
|
75
75
|
# assert(np.all(fdata[FV.H]==fdata[FV.TXYH[..., 2]]))
|
|
76
76
|
|
|
77
77
|
# contribution from main wake:
|
|
78
|
-
wcoos = algo.wake_frame.get_wake_coos(
|
|
78
|
+
wcoos = algo.wake_frame.get_wake_coos(
|
|
79
|
+
algo, mdata, fdata, tdata, downwind_index, wmodel
|
|
80
|
+
)
|
|
79
81
|
wmodel.contribute(algo, mdata, fdata, tdata, downwind_index, wcoos, wake_deltas)
|
|
80
82
|
|
|
81
83
|
# contribution from mirrors:
|
|
82
84
|
tdata[FC.TARGETS] = tdata[FC.TARGETS].copy() # making sure this is no ref
|
|
83
85
|
for h in self.heights:
|
|
84
|
-
|
|
85
86
|
fdata[FV.TXYH][:, downwind_index, 2] = hh + 2 * (h - hh)
|
|
86
87
|
|
|
87
88
|
pwake.contribute(
|
|
@@ -136,7 +137,6 @@ class WakeMirror(GroundModel):
|
|
|
136
137
|
# contribution from mirrors:
|
|
137
138
|
tdata[FC.TARGETS] = tdata[FC.TARGETS].copy() # making sure this is no ref
|
|
138
139
|
for h in self.heights:
|
|
139
|
-
|
|
140
140
|
fdata[FV.TXYH][:, downwind_index, 2] = hh + 2 * (h - hh)
|
|
141
141
|
|
|
142
142
|
wcoos = algo.wake_frame.get_wake_coos(
|