foxes 0.8.2__py3-none-any.whl → 1.0__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_RHB/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 +183 -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 +232 -101
- foxes/algorithms/downwind/models/farm_wakes_calc.py +11 -6
- foxes/algorithms/downwind/models/init_farm_data.py +1 -1
- foxes/algorithms/downwind/models/point_wakes_calc.py +5 -6
- foxes/algorithms/downwind/models/reorder_farm_output.py +0 -1
- foxes/algorithms/downwind/models/set_amb_point_results.py +4 -2
- foxes/algorithms/iterative/iterative.py +73 -33
- foxes/algorithms/iterative/models/farm_wakes_calc.py +11 -6
- foxes/algorithms/sequential/models/plugin.py +1 -1
- foxes/algorithms/sequential/sequential.py +126 -255
- foxes/constants.py +17 -2
- foxes/core/__init__.py +1 -0
- foxes/core/algorithm.py +631 -146
- foxes/core/data.py +252 -20
- foxes/core/data_calc_model.py +13 -289
- foxes/core/engine.py +630 -0
- foxes/core/farm_controller.py +37 -9
- foxes/core/farm_data_model.py +15 -0
- foxes/core/model.py +133 -80
- foxes/core/point_data_model.py +15 -0
- foxes/core/rotor_model.py +27 -21
- foxes/core/states.py +16 -0
- foxes/core/turbine_type.py +28 -0
- foxes/core/wake_frame.py +22 -4
- foxes/core/wake_model.py +2 -3
- foxes/data/windio/windio_5turbines_timeseries.yaml +23 -1
- foxes/engines/__init__.py +16 -0
- foxes/engines/dask.py +975 -0
- foxes/engines/default.py +75 -0
- foxes/engines/futures.py +72 -0
- foxes/engines/mpi.py +38 -0
- foxes/engines/multiprocess.py +74 -0
- foxes/engines/numpy.py +185 -0
- foxes/engines/pool.py +263 -0
- foxes/engines/single.py +139 -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 +1 -1
- 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 +6 -0
- foxes/input/states/create/random_abl_states.py +1 -1
- foxes/input/states/field_data_nc.py +157 -32
- foxes/input/states/multi_height.py +127 -13
- foxes/input/states/one_point_flow.py +577 -0
- foxes/input/states/scan_ws.py +73 -2
- foxes/input/states/states_table.py +204 -35
- foxes/input/windio/__init__.py +1 -1
- foxes/input/windio/get_states.py +44 -23
- foxes/input/windio/read_attributes.py +41 -16
- foxes/input/windio/read_farm.py +116 -102
- foxes/input/windio/read_fields.py +13 -6
- foxes/input/windio/read_outputs.py +63 -22
- foxes/input/windio/runner.py +31 -17
- foxes/input/windio/windio.py +36 -22
- foxes/models/ground_models/wake_mirror.py +8 -4
- foxes/models/model_book.py +29 -18
- foxes/models/partial_wakes/rotor_points.py +3 -3
- foxes/models/rotor_models/centre.py +4 -0
- foxes/models/rotor_models/grid.py +22 -23
- foxes/models/rotor_models/levels.py +4 -5
- foxes/models/turbine_models/calculator.py +0 -2
- foxes/models/turbine_models/lookup_table.py +27 -2
- 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 +24 -0
- foxes/models/turbine_types/PCt_from_two.py +24 -0
- foxes/models/turbine_types/__init__.py +1 -0
- foxes/models/turbine_types/lookup.py +316 -0
- foxes/models/turbine_types/null_type.py +50 -0
- foxes/models/turbine_types/wsrho2PCt_from_two.py +24 -0
- foxes/models/turbine_types/wsti2PCt_from_two.py +24 -0
- 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 +23 -3
- foxes/models/wake_frames/rotor_wd.py +4 -2
- foxes/models/wake_frames/seq_dynamic_wakes.py +56 -63
- foxes/models/wake_frames/streamlines.py +19 -20
- foxes/models/wake_frames/timelines.py +328 -127
- foxes/models/wake_frames/yawed_wakes.py +4 -1
- foxes/models/wake_models/dist_sliced.py +1 -3
- foxes/models/wake_models/induction/rankine_half_body.py +4 -4
- foxes/models/wake_models/induction/rathmann.py +2 -2
- foxes/models/wake_models/induction/self_similar.py +2 -2
- foxes/models/wake_models/induction/vortex_sheet.py +2 -2
- foxes/models/wake_models/ti/iec_ti.py +34 -17
- foxes/models/wake_models/top_hat.py +1 -1
- foxes/models/wake_models/wind/bastankhah14.py +2 -2
- foxes/models/wake_models/wind/bastankhah16.py +8 -7
- foxes/models/wake_models/wind/jensen.py +1 -1
- foxes/models/wake_models/wind/turbopark.py +2 -2
- foxes/output/__init__.py +4 -1
- foxes/output/farm_layout.py +2 -2
- foxes/output/flow_plots_2d/__init__.py +0 -1
- foxes/output/flow_plots_2d/flow_plots.py +70 -30
- foxes/output/grids.py +91 -21
- foxes/output/seq_plugins/__init__.py +2 -0
- foxes/output/{flow_plots_2d → seq_plugins}/seq_flow_ani_plugin.py +62 -20
- foxes/output/seq_plugins/seq_wake_debug_plugin.py +145 -0
- foxes/output/slice_data.py +131 -111
- foxes/output/state_turbine_map.py +18 -13
- foxes/output/state_turbine_table.py +19 -19
- foxes/utils/__init__.py +1 -1
- foxes/utils/dev_utils.py +42 -0
- foxes/utils/dict.py +1 -1
- foxes/utils/factory.py +147 -52
- foxes/utils/pandas_helpers.py +4 -3
- foxes/utils/wind_dir.py +0 -2
- foxes/utils/xarray_utils.py +23 -13
- foxes/variables.py +37 -0
- {foxes-0.8.2.dist-info → foxes-1.0.dist-info}/METADATA +71 -33
- foxes-1.0.dist-info/RECORD +307 -0
- {foxes-0.8.2.dist-info → foxes-1.0.dist-info}/WHEEL +1 -1
- foxes-1.0.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/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.0.dist-info}/LICENSE +0 -0
|
@@ -44,15 +44,23 @@ class SetFarmVars(TurbineModel):
|
|
|
44
44
|
The data, shape: (n_states, n_turbines)
|
|
45
45
|
|
|
46
46
|
"""
|
|
47
|
+
if self.initialized:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Model '{self.name}': Cannot add_var after initialization"
|
|
50
|
+
)
|
|
51
|
+
if self.running:
|
|
52
|
+
raise ValueError(f"Model '{self.name}': Cannot add_var while running")
|
|
47
53
|
self.vars.append(var)
|
|
48
|
-
self.
|
|
54
|
+
self.__vdata.append(np.asarray(data, dtype=FC.DTYPE))
|
|
49
55
|
|
|
50
56
|
def reset(self):
|
|
51
57
|
"""
|
|
52
58
|
Remove all variables.
|
|
53
59
|
"""
|
|
60
|
+
if self.running:
|
|
61
|
+
raise ValueError(f"Model '{self.name}': Cannot reset while running")
|
|
54
62
|
self.vars = []
|
|
55
|
-
self.
|
|
63
|
+
self.__vdata = []
|
|
56
64
|
|
|
57
65
|
def output_farm_vars(self, algo):
|
|
58
66
|
"""
|
|
@@ -98,7 +106,7 @@ class SetFarmVars(TurbineModel):
|
|
|
98
106
|
|
|
99
107
|
for i, v in enumerate(self.vars):
|
|
100
108
|
data = np.full((algo.n_states, algo.n_turbines), np.nan, dtype=FC.DTYPE)
|
|
101
|
-
vdata = self.
|
|
109
|
+
vdata = self.__vdata[i]
|
|
102
110
|
|
|
103
111
|
# handle special case of call during vectorized optimization:
|
|
104
112
|
if (
|
|
@@ -110,14 +118,105 @@ class SetFarmVars(TurbineModel):
|
|
|
110
118
|
n_ost = algo.states.states.size()
|
|
111
119
|
n_trb = algo.n_turbines
|
|
112
120
|
vdata = np.zeros((n_pop, n_ost, n_trb), dtype=FC.DTYPE)
|
|
113
|
-
vdata[:] = self.
|
|
121
|
+
vdata[:] = self.__vdata[i][None, :]
|
|
114
122
|
vdata = vdata.reshape(n_pop * n_ost, n_trb)
|
|
115
123
|
|
|
116
124
|
data[:] = vdata
|
|
117
125
|
idata["data_vars"][self.var(v)] = ((FC.STATE, FC.TURBINE), data)
|
|
118
126
|
|
|
127
|
+
# special case of turbine positions:
|
|
128
|
+
if v in [FV.X, FV.Y]:
|
|
129
|
+
i = [FV.X, FV.Y].index(v)
|
|
130
|
+
for ti in range(algo.n_turbines):
|
|
131
|
+
t = algo.farm.turbines[ti]
|
|
132
|
+
if len(t.xy.shape) == 1:
|
|
133
|
+
xy = np.zeros((algo.n_states, 2), dtype=FC.DTYPE)
|
|
134
|
+
xy[:] = t.xy[None, :]
|
|
135
|
+
t.xy = xy
|
|
136
|
+
t.xy[:, i] = np.where(
|
|
137
|
+
np.isnan(data[:, ti]), t.xy[:, i], data[:, ti]
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# special case of rotor diameter and hub height:
|
|
141
|
+
if v in [FV.D, FV.H]:
|
|
142
|
+
for ti in range(algo.n_turbines):
|
|
143
|
+
t = algo.farm.turbines[ti]
|
|
144
|
+
x = np.zeros(algo.n_states, dtype=FC.DTYPE)
|
|
145
|
+
if v == FV.D:
|
|
146
|
+
x[:] = t.D
|
|
147
|
+
t.D = x
|
|
148
|
+
else:
|
|
149
|
+
x[:] = t.H
|
|
150
|
+
t.H = x
|
|
151
|
+
x[:] = np.where(np.isnan(data[:, ti]), x, data[:, ti])
|
|
152
|
+
|
|
119
153
|
return idata
|
|
120
154
|
|
|
155
|
+
def set_running(
|
|
156
|
+
self,
|
|
157
|
+
algo,
|
|
158
|
+
data_stash,
|
|
159
|
+
sel=None,
|
|
160
|
+
isel=None,
|
|
161
|
+
verbosity=0,
|
|
162
|
+
):
|
|
163
|
+
"""
|
|
164
|
+
Sets this model status to running, and moves
|
|
165
|
+
all large data to stash.
|
|
166
|
+
|
|
167
|
+
The stashed data will be returned by the
|
|
168
|
+
unset_running() function after running calculations.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
algo: foxes.core.Algorithm
|
|
173
|
+
The calculation algorithm
|
|
174
|
+
data_stash: dict
|
|
175
|
+
Large data stash, this function adds data here.
|
|
176
|
+
Key: model name. Value: dict, large model data
|
|
177
|
+
sel: dict, optional
|
|
178
|
+
The subset selection dictionary
|
|
179
|
+
isel: dict, optional
|
|
180
|
+
The index subset selection dictionary
|
|
181
|
+
verbosity: int
|
|
182
|
+
The verbosity level, 0 = silent
|
|
183
|
+
|
|
184
|
+
"""
|
|
185
|
+
super().set_running(algo, data_stash, sel, isel, verbosity)
|
|
186
|
+
|
|
187
|
+
data_stash[self.name]["vdata"] = self.__vdata
|
|
188
|
+
del self.__vdata
|
|
189
|
+
|
|
190
|
+
def unset_running(
|
|
191
|
+
self,
|
|
192
|
+
algo,
|
|
193
|
+
data_stash,
|
|
194
|
+
sel=None,
|
|
195
|
+
isel=None,
|
|
196
|
+
verbosity=0,
|
|
197
|
+
):
|
|
198
|
+
"""
|
|
199
|
+
Sets this model status to not running, recovering large data
|
|
200
|
+
from stash
|
|
201
|
+
|
|
202
|
+
Parameters
|
|
203
|
+
----------
|
|
204
|
+
algo: foxes.core.Algorithm
|
|
205
|
+
The calculation algorithm
|
|
206
|
+
data_stash: dict
|
|
207
|
+
Large data stash, this function adds data here.
|
|
208
|
+
Key: model name. Value: dict, large model data
|
|
209
|
+
sel: dict, optional
|
|
210
|
+
The subset selection dictionary
|
|
211
|
+
isel: dict, optional
|
|
212
|
+
The index subset selection dictionary
|
|
213
|
+
verbosity: int
|
|
214
|
+
The verbosity level, 0 = silent
|
|
215
|
+
|
|
216
|
+
"""
|
|
217
|
+
super().unset_running(algo, data_stash, sel, isel, verbosity)
|
|
218
|
+
self.__vdata = data_stash[self.name].pop("vdata")
|
|
219
|
+
|
|
121
220
|
def calculate(self, algo, mdata, fdata, st_sel):
|
|
122
221
|
"""
|
|
123
222
|
The main model calculation.
|
|
@@ -159,36 +258,6 @@ class SetFarmVars(TurbineModel):
|
|
|
159
258
|
hsel = ~np.isnan(data)
|
|
160
259
|
tsel = bsel & hsel
|
|
161
260
|
|
|
162
|
-
# special case of turbine positions:
|
|
163
|
-
if v in [FV.X, FV.Y]:
|
|
164
|
-
i = [FV.X, FV.Y].index(v)
|
|
165
|
-
for ti in np.where(tsel)[1]:
|
|
166
|
-
t = algo.farm.turbines[ti]
|
|
167
|
-
if len(t.xy.shape) == 1:
|
|
168
|
-
xy = np.zeros((algo.n_states, 2), dtype=FC.DTYPE)
|
|
169
|
-
xy[:] = t.xy[None, :]
|
|
170
|
-
t.xy = xy
|
|
171
|
-
i0 = fdata.states_i0()
|
|
172
|
-
hsel = tsel[:, ti]
|
|
173
|
-
ssel = i0 + np.where(hsel)[0]
|
|
174
|
-
t.xy[ssel, i] = data[hsel, ti]
|
|
175
|
-
|
|
176
|
-
# special case of rotor diameter and hub height:
|
|
177
|
-
if v in [FV.D, FV.H]:
|
|
178
|
-
for ti in np.where(tsel)[1]:
|
|
179
|
-
t = algo.farm.turbines[ti]
|
|
180
|
-
x = np.zeros(algo.n_states, dtype=FC.DTYPE)
|
|
181
|
-
if v == FV.D:
|
|
182
|
-
x[:] = t.D
|
|
183
|
-
t.D = x
|
|
184
|
-
else:
|
|
185
|
-
x[:] = t.H
|
|
186
|
-
t.H = x
|
|
187
|
-
i0 = fdata.states_i0()
|
|
188
|
-
hsel = tsel[:, ti]
|
|
189
|
-
ssel = i0 + np.where(hsel)[0]
|
|
190
|
-
x[ssel] = data[hsel, ti]
|
|
191
|
-
|
|
192
261
|
fdata[v][tsel] = data[tsel]
|
|
193
262
|
|
|
194
263
|
return {v: fdata[v] for v in self.vars}
|
|
@@ -106,6 +106,30 @@ class PCtFile(TurbineType):
|
|
|
106
106
|
a += f", var_ws_ct={self.WSCT}, var_ws_P={self.WSP}"
|
|
107
107
|
return f"{type(self).__name__}({a})"
|
|
108
108
|
|
|
109
|
+
def needs_rews2(self):
|
|
110
|
+
"""
|
|
111
|
+
Returns flag for requirering REWS2 variable
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
flag: bool
|
|
116
|
+
True if REWS2 is required
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
return self.WSCT == FV.REWS2 or self.WSP == FV.REWS2
|
|
120
|
+
|
|
121
|
+
def needs_rews3(self):
|
|
122
|
+
"""
|
|
123
|
+
Returns flag for requirering REWS3 variable
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
flag: bool
|
|
128
|
+
True if REWS3 is required
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
return self.WSCT == FV.REWS3 or self.WSP == FV.REWS3
|
|
132
|
+
|
|
109
133
|
def output_farm_vars(self, algo):
|
|
110
134
|
"""
|
|
111
135
|
The variables which are being modified by the model.
|
|
@@ -126,6 +126,30 @@ class PCtFromTwo(TurbineType):
|
|
|
126
126
|
a += f", var_ws_ct={self.WSCT}, var_ws_P={self.WSP}"
|
|
127
127
|
return f"{type(self).__name__}({a})"
|
|
128
128
|
|
|
129
|
+
def needs_rews2(self):
|
|
130
|
+
"""
|
|
131
|
+
Returns flag for requirering REWS2 variable
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
flag: bool
|
|
136
|
+
True if REWS2 is required
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
return self.WSCT == FV.REWS2 or self.WSP == FV.REWS2
|
|
140
|
+
|
|
141
|
+
def needs_rews3(self):
|
|
142
|
+
"""
|
|
143
|
+
Returns flag for requirering REWS3 variable
|
|
144
|
+
|
|
145
|
+
Returns
|
|
146
|
+
-------
|
|
147
|
+
flag: bool
|
|
148
|
+
True if REWS3 is required
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
return self.WSCT == FV.REWS3 or self.WSP == FV.REWS3
|
|
152
|
+
|
|
129
153
|
def output_farm_vars(self, algo):
|
|
130
154
|
"""
|
|
131
155
|
The variables which are being modified by the model.
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
from foxes.core import TurbineType, FData
|
|
5
|
+
from foxes.data import parse_Pct_file_name
|
|
6
|
+
from foxes.models.turbine_models import LookupTable
|
|
7
|
+
import foxes.variables as FV
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FromLookupTable(TurbineType):
|
|
11
|
+
"""
|
|
12
|
+
Calculate power and ct by interpolating
|
|
13
|
+
by using a lookup-table
|
|
14
|
+
|
|
15
|
+
Attributes
|
|
16
|
+
----------
|
|
17
|
+
source: str or pandas.DataFrame
|
|
18
|
+
The file path, static name, or data
|
|
19
|
+
rho: float
|
|
20
|
+
The air densitiy for which the data is valid
|
|
21
|
+
or None for no correction
|
|
22
|
+
WSCT: str
|
|
23
|
+
The wind speed variable for ct lookup
|
|
24
|
+
WSP: str
|
|
25
|
+
The wind speed variable for power lookup
|
|
26
|
+
rpars: dict, optional
|
|
27
|
+
Parameters for pandas file reading
|
|
28
|
+
|
|
29
|
+
:group: models.turbine_types
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
data_source,
|
|
36
|
+
input_vars,
|
|
37
|
+
varmap={},
|
|
38
|
+
lookup_pars={},
|
|
39
|
+
rho=None,
|
|
40
|
+
p_ct=1.0,
|
|
41
|
+
p_P=1.88,
|
|
42
|
+
var_ws_ct=FV.REWS2,
|
|
43
|
+
var_ws_P=FV.REWS3,
|
|
44
|
+
pd_file_read_pars={},
|
|
45
|
+
interpn_args={},
|
|
46
|
+
**parameters,
|
|
47
|
+
):
|
|
48
|
+
"""
|
|
49
|
+
Constructor.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
data_source: str or pandas.DataFrame
|
|
54
|
+
The file path, static name, or data
|
|
55
|
+
input_vars: list of str
|
|
56
|
+
The foxes input variables
|
|
57
|
+
varmap: dict
|
|
58
|
+
Mapping from foxes variable names
|
|
59
|
+
to column names in the data_source
|
|
60
|
+
lookup_pars: dict
|
|
61
|
+
Additional parameters for the LookupTable model
|
|
62
|
+
rho: float, optional
|
|
63
|
+
The air densitiy for which the data is valid
|
|
64
|
+
or None for no correction
|
|
65
|
+
p_ct: float
|
|
66
|
+
The exponent for yaw dependency of ct
|
|
67
|
+
p_P: float
|
|
68
|
+
The exponent for yaw dependency of P
|
|
69
|
+
var_ws_ct: str
|
|
70
|
+
The wind speed variable for ct lookup
|
|
71
|
+
var_ws_P: str
|
|
72
|
+
The wind speed variable for power lookup
|
|
73
|
+
pd_file_read_pars: dict
|
|
74
|
+
Parameters for pandas file reading
|
|
75
|
+
interpn_args: dict
|
|
76
|
+
Parameters for scipy intern or interp1d
|
|
77
|
+
parameters: dict, optional
|
|
78
|
+
Additional parameters for TurbineType class
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
if not isinstance(data_source, pd.DataFrame):
|
|
82
|
+
pars = parse_Pct_file_name(data_source)
|
|
83
|
+
pars.update(parameters)
|
|
84
|
+
else:
|
|
85
|
+
pars = parameters
|
|
86
|
+
|
|
87
|
+
super().__init__(**pars)
|
|
88
|
+
|
|
89
|
+
self.source = data_source
|
|
90
|
+
self.rho = rho
|
|
91
|
+
self.p_ct = p_ct
|
|
92
|
+
self.p_P = p_P
|
|
93
|
+
self.WSCT = var_ws_ct
|
|
94
|
+
self.WSP = var_ws_P
|
|
95
|
+
self.rpars = pd_file_read_pars
|
|
96
|
+
|
|
97
|
+
if FV.REWS not in input_vars or len(
|
|
98
|
+
set(input_vars).intersection([FV.WS, FV.REWS2, FV.REWS3])
|
|
99
|
+
):
|
|
100
|
+
raise KeyError(
|
|
101
|
+
f"Turbine type '{self.name}': Expecting '{FV.REWS}' as wind speed variable in inputv_vars, got {input_vars}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
iargs = dict(bounds_error=False, fill_value=0)
|
|
105
|
+
iargs.update(interpn_args)
|
|
106
|
+
self._lookup = LookupTable(
|
|
107
|
+
data_source=data_source,
|
|
108
|
+
input_vars=input_vars,
|
|
109
|
+
output_vars=[FV.P, FV.CT],
|
|
110
|
+
varmap=varmap,
|
|
111
|
+
interpn_args=iargs,
|
|
112
|
+
**lookup_pars,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def __repr__(self):
|
|
116
|
+
a = f"D={self.D}, H={self.H}, P_nominal={self.P_nominal}, P_unit={self.P_unit}, rho={self.rho}"
|
|
117
|
+
a += f", var_ws_ct={self.WSCT}, var_ws_P={self.WSP}"
|
|
118
|
+
return f"{type(self).__name__}({a})"
|
|
119
|
+
|
|
120
|
+
def needs_rews2(self):
|
|
121
|
+
"""
|
|
122
|
+
Returns flag for requirering REWS2 variable
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
flag: bool
|
|
127
|
+
True if REWS2 is required
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
return self.WSCT == FV.REWS2 or self.WSP == FV.REWS2
|
|
131
|
+
|
|
132
|
+
def needs_rews3(self):
|
|
133
|
+
"""
|
|
134
|
+
Returns flag for requirering REWS3 variable
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
flag: bool
|
|
139
|
+
True if REWS3 is required
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
return self.WSCT == FV.REWS3 or self.WSP == FV.REWS3
|
|
143
|
+
|
|
144
|
+
def sub_models(self):
|
|
145
|
+
"""
|
|
146
|
+
List of all sub-models
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
smdls: list of foxes.core.Model
|
|
151
|
+
All sub models
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
return [self._lookup]
|
|
155
|
+
|
|
156
|
+
def output_farm_vars(self, algo):
|
|
157
|
+
"""
|
|
158
|
+
The variables which are being modified by the model.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
algo: foxes.core.Algorithm
|
|
163
|
+
The calculation algorithm
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
output_vars: list of str
|
|
168
|
+
The output variable names
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
return [FV.P, FV.CT]
|
|
172
|
+
|
|
173
|
+
def initialize(self, algo, verbosity=0, force=False):
|
|
174
|
+
"""
|
|
175
|
+
Initializes the model.
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
algo: foxes.core.Algorithm
|
|
180
|
+
The calculation algorithm
|
|
181
|
+
verbosity: int
|
|
182
|
+
The verbosity level, 0 = silent
|
|
183
|
+
force: bool
|
|
184
|
+
Overwrite existing data
|
|
185
|
+
|
|
186
|
+
"""
|
|
187
|
+
super().initialize(algo, verbosity, force)
|
|
188
|
+
if self.P_nominal is None:
|
|
189
|
+
col_P = self._lookup.varmap.get(FV.P, FV.P)
|
|
190
|
+
self.P_nominal = np.max(self._lookup._data[col_P].to_numpy())
|
|
191
|
+
|
|
192
|
+
def modify_cutin(
|
|
193
|
+
self,
|
|
194
|
+
modify_ct,
|
|
195
|
+
modify_P,
|
|
196
|
+
steps=20,
|
|
197
|
+
iterations=100,
|
|
198
|
+
a=0.55,
|
|
199
|
+
b=0.55,
|
|
200
|
+
):
|
|
201
|
+
"""
|
|
202
|
+
Modify the data such that a discontinuity
|
|
203
|
+
at cutin wind speed is avoided
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
variable: str
|
|
208
|
+
The target variable
|
|
209
|
+
modify_ct: bool
|
|
210
|
+
Flag for modification of the ct curve
|
|
211
|
+
modify_P: bool
|
|
212
|
+
Flag for modification of the power curve
|
|
213
|
+
steps: int
|
|
214
|
+
The number of wind speed steps between 0 and
|
|
215
|
+
the cutin wind speed
|
|
216
|
+
iterations: int
|
|
217
|
+
The number of iterations
|
|
218
|
+
a: float
|
|
219
|
+
Coefficient for iterative mixing
|
|
220
|
+
b: float
|
|
221
|
+
Coefficient for iterative mixing
|
|
222
|
+
|
|
223
|
+
"""
|
|
224
|
+
if modify_ct or modify_P:
|
|
225
|
+
raise NotImplementedError
|
|
226
|
+
|
|
227
|
+
else:
|
|
228
|
+
super().modify_cutin(modify_ct, modify_P)
|
|
229
|
+
|
|
230
|
+
def calculate(self, algo, mdata, fdata, st_sel):
|
|
231
|
+
""" "
|
|
232
|
+
The main model calculation.
|
|
233
|
+
|
|
234
|
+
This function is executed on a single chunk of data,
|
|
235
|
+
all computations should be based on numpy arrays.
|
|
236
|
+
|
|
237
|
+
Parameters
|
|
238
|
+
----------
|
|
239
|
+
algo: foxes.core.Algorithm
|
|
240
|
+
The calculation algorithm
|
|
241
|
+
mdata: foxes.core.MData
|
|
242
|
+
The model data
|
|
243
|
+
fdata: foxes.core.FData
|
|
244
|
+
The farm data
|
|
245
|
+
st_sel: numpy.ndarray of bool
|
|
246
|
+
The state-turbine selection,
|
|
247
|
+
shape: (n_states, n_turbines)
|
|
248
|
+
|
|
249
|
+
Returns
|
|
250
|
+
-------
|
|
251
|
+
results: dict
|
|
252
|
+
The resulting data, keys: output variable str.
|
|
253
|
+
Values: numpy.ndarray with shape (n_states, n_turbines)
|
|
254
|
+
|
|
255
|
+
"""
|
|
256
|
+
# prepare data for lookup:
|
|
257
|
+
input_vars = self._lookup.input_vars
|
|
258
|
+
fdata_lookup = FData(
|
|
259
|
+
data={v: fdata[v] for v in input_vars},
|
|
260
|
+
dims={v: fdata.dims[v] for v in input_vars},
|
|
261
|
+
loop_dims=fdata.loop_dims,
|
|
262
|
+
)
|
|
263
|
+
for v in self.output_farm_vars(algo):
|
|
264
|
+
fdata_lookup.add(v, fdata[v], fdata.dims[v])
|
|
265
|
+
|
|
266
|
+
rews2 = None
|
|
267
|
+
rews3 = None
|
|
268
|
+
if self.WSP != FV.REWS or self.WSCT != FV.REWS:
|
|
269
|
+
rews2 = fdata[self.WSCT].copy()
|
|
270
|
+
rews3 = fdata[self.WSP].copy()
|
|
271
|
+
|
|
272
|
+
# apply air density correction:
|
|
273
|
+
if self.rho is not None:
|
|
274
|
+
if rews2 is None:
|
|
275
|
+
rews2 = fdata[self.WSCT].copy()
|
|
276
|
+
rews3 = fdata[self.WSP].copy()
|
|
277
|
+
|
|
278
|
+
# correct wind speed by air density, such
|
|
279
|
+
# that in the partial load region the
|
|
280
|
+
# correct value is reconstructed:
|
|
281
|
+
rho = fdata[FV.RHO][st_sel]
|
|
282
|
+
rews3[st_sel] *= (self.rho / rho) ** (1.0 / 3.0)
|
|
283
|
+
del rho
|
|
284
|
+
|
|
285
|
+
# in yawed case, calc yaw corrected wind speed:
|
|
286
|
+
if FV.YAWM in fdata and (self.p_P is not None or self.p_ct is not None):
|
|
287
|
+
if rews2 is None:
|
|
288
|
+
rews2 = fdata[self.WSCT].copy()
|
|
289
|
+
rews3 = fdata[self.WSP].copy()
|
|
290
|
+
|
|
291
|
+
# calculate corrected wind speed wsc,
|
|
292
|
+
# gives ws**3 * cos**p_P in partial load region
|
|
293
|
+
# and smoothly deals with full load region:
|
|
294
|
+
yawm = fdata[FV.YAWM][st_sel]
|
|
295
|
+
if np.any(np.isnan(yawm)):
|
|
296
|
+
raise ValueError(
|
|
297
|
+
f"{self.name}: Found NaN values for variable '{FV.YAWM}'. Maybe change order in turbine_models?"
|
|
298
|
+
)
|
|
299
|
+
cosm = np.cos(yawm / 180 * np.pi)
|
|
300
|
+
if self.p_ct is not None:
|
|
301
|
+
rews2[st_sel] *= (cosm**self.p_ct) ** 0.5
|
|
302
|
+
if self.p_P is not None:
|
|
303
|
+
rews3[st_sel] *= (cosm**self.p_P) ** (1.0 / 3.0)
|
|
304
|
+
del yawm, cosm
|
|
305
|
+
|
|
306
|
+
# run lookup:
|
|
307
|
+
if rews2 is None:
|
|
308
|
+
out = self._lookup.calculate(algo, mdata, fdata_lookup, st_sel)
|
|
309
|
+
else:
|
|
310
|
+
fdata_lookup[FV.REWS] = rews2
|
|
311
|
+
ct = self._lookup.calculate(algo, mdata, fdata_lookup, st_sel)[FV.CT]
|
|
312
|
+
fdata_lookup[FV.REWS] = rews3
|
|
313
|
+
out = self._lookup.calculate(algo, mdata, fdata_lookup, st_sel)
|
|
314
|
+
out[FV.CT] = ct
|
|
315
|
+
|
|
316
|
+
return out
|
|
@@ -9,6 +9,56 @@ class NullType(TurbineType):
|
|
|
9
9
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
*args,
|
|
15
|
+
needs_rews2=False,
|
|
16
|
+
needs_rews3=False,
|
|
17
|
+
**kwargs,
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
Constructor.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
args: tuple, optional
|
|
25
|
+
Additional parameters for TurbineType class
|
|
26
|
+
needs_rews2: bool
|
|
27
|
+
Flag for runs that require the REWS2 variable
|
|
28
|
+
needs_rews3: bool
|
|
29
|
+
Flag for runs that require the REWS3 variable
|
|
30
|
+
kwargs: dict, optional
|
|
31
|
+
Additional parameters for TurbineType class
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
super().__init__(*args, **kwargs)
|
|
35
|
+
self._rews2 = needs_rews2
|
|
36
|
+
self._rews3 = needs_rews3
|
|
37
|
+
|
|
38
|
+
def needs_rews2(self):
|
|
39
|
+
"""
|
|
40
|
+
Returns flag for requirering REWS2 variable
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
flag: bool
|
|
45
|
+
True if REWS2 is required
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
return self._rews2
|
|
49
|
+
|
|
50
|
+
def needs_rews3(self):
|
|
51
|
+
"""
|
|
52
|
+
Returns flag for requirering REWS3 variable
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
flag: bool
|
|
57
|
+
True if REWS3 is required
|
|
58
|
+
|
|
59
|
+
"""
|
|
60
|
+
return self._rews3
|
|
61
|
+
|
|
12
62
|
def output_farm_vars(self, algo):
|
|
13
63
|
"""
|
|
14
64
|
The variables which are being modified by the model.
|
|
@@ -115,6 +115,30 @@ class WsRho2PCtFromTwo(TurbineType):
|
|
|
115
115
|
self._P = None
|
|
116
116
|
self._ct = None
|
|
117
117
|
|
|
118
|
+
def needs_rews2(self):
|
|
119
|
+
"""
|
|
120
|
+
Returns flag for requirering REWS2 variable
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
flag: bool
|
|
125
|
+
True if REWS2 is required
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
return self.WSCT == FV.REWS2 or self.WSP == FV.REWS2
|
|
129
|
+
|
|
130
|
+
def needs_rews3(self):
|
|
131
|
+
"""
|
|
132
|
+
Returns flag for requirering REWS3 variable
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
flag: bool
|
|
137
|
+
True if REWS3 is required
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
return self.WSCT == FV.REWS3 or self.WSP == FV.REWS3
|
|
141
|
+
|
|
118
142
|
def output_farm_vars(self, algo):
|
|
119
143
|
"""
|
|
120
144
|
The variables which are being modified by the model.
|
|
@@ -127,6 +127,30 @@ class WsTI2PCtFromTwo(TurbineType):
|
|
|
127
127
|
a = f"D={self.D}, H={self.H}, P_nominal={self.P_nominal}, P_unit={self.P_unit}, rho={self.rho}"
|
|
128
128
|
return f"{type(self).__name__}({a})"
|
|
129
129
|
|
|
130
|
+
def needs_rews2(self):
|
|
131
|
+
"""
|
|
132
|
+
Returns flag for requirering REWS2 variable
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
flag: bool
|
|
137
|
+
True if REWS2 is required
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
return self.WSCT == FV.REWS2 or self.WSP == FV.REWS2
|
|
141
|
+
|
|
142
|
+
def needs_rews3(self):
|
|
143
|
+
"""
|
|
144
|
+
Returns flag for requirering REWS3 variable
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
flag: bool
|
|
149
|
+
True if REWS3 is required
|
|
150
|
+
|
|
151
|
+
"""
|
|
152
|
+
return self.WSCT == FV.REWS3 or self.WSP == FV.REWS3
|
|
153
|
+
|
|
130
154
|
def output_farm_vars(self, algo):
|
|
131
155
|
"""
|
|
132
156
|
The variables which are being modified by the model.
|