foxes 0.6.1__py3-none-any.whl → 0.7__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.
- foxes/VERSION +1 -1
- foxes/algorithms/downwind/downwind.py +131 -65
- foxes/algorithms/downwind/models/__init__.py +2 -1
- foxes/algorithms/downwind/models/farm_wakes_calc.py +87 -55
- foxes/algorithms/downwind/models/init_farm_data.py +134 -0
- foxes/algorithms/downwind/models/point_wakes_calc.py +54 -65
- foxes/algorithms/downwind/models/{calc_order.py → reorder_farm_output.py} +28 -26
- foxes/algorithms/iterative/iterative.py +100 -51
- foxes/algorithms/iterative/models/convergence.py +3 -3
- foxes/algorithms/iterative/models/farm_wakes_calc.py +55 -48
- foxes/algorithms/sequential/models/seq_state.py +7 -6
- foxes/algorithms/sequential/sequential.py +81 -44
- foxes/constants.py +33 -18
- foxes/core/__init__.py +2 -2
- foxes/core/algorithm.py +31 -12
- foxes/core/data.py +335 -41
- foxes/core/data_calc_model.py +27 -23
- foxes/core/farm_controller.py +27 -28
- foxes/core/farm_data_model.py +26 -4
- foxes/core/model.py +186 -129
- foxes/core/partial_wakes_model.py +84 -81
- foxes/core/point_data_model.py +51 -18
- foxes/core/rotor_model.py +59 -77
- foxes/core/states.py +6 -6
- foxes/core/turbine_model.py +4 -4
- foxes/core/turbine_type.py +24 -0
- foxes/core/vertical_profile.py +3 -3
- foxes/core/wake_frame.py +91 -50
- foxes/core/wake_model.py +74 -43
- foxes/core/wake_superposition.py +29 -26
- foxes/input/farm_layout/__init__.py +1 -0
- foxes/input/farm_layout/from_random.py +49 -0
- foxes/input/states/__init__.py +1 -1
- foxes/input/states/create/__init__.py +1 -0
- foxes/input/states/create/random_abl_states.py +6 -2
- foxes/input/states/create/random_timeseries.py +56 -0
- foxes/input/states/field_data_nc.py +12 -8
- foxes/input/states/multi_height.py +24 -14
- foxes/input/states/scan_ws.py +13 -17
- foxes/input/states/single.py +28 -20
- foxes/input/states/states_table.py +22 -18
- foxes/models/axial_induction_models/betz.py +1 -1
- foxes/models/farm_models/turbine2farm.py +2 -2
- foxes/models/model_book.py +40 -14
- foxes/models/partial_wakes/__init__.py +2 -2
- foxes/models/partial_wakes/axiwake.py +73 -200
- foxes/models/partial_wakes/centre.py +40 -0
- foxes/models/partial_wakes/grid.py +7 -63
- foxes/models/partial_wakes/rotor_points.py +53 -147
- foxes/models/partial_wakes/segregated.py +158 -0
- foxes/models/partial_wakes/top_hat.py +88 -196
- foxes/models/point_models/set_uniform_data.py +4 -4
- foxes/models/point_models/tke2ti.py +4 -4
- foxes/models/point_models/wake_deltas.py +4 -4
- foxes/models/rotor_models/centre.py +15 -19
- foxes/models/rotor_models/grid.py +2 -1
- foxes/models/rotor_models/levels.py +2 -1
- foxes/models/turbine_models/__init__.py +0 -1
- foxes/models/turbine_models/calculator.py +11 -7
- foxes/models/turbine_models/kTI_model.py +13 -11
- foxes/models/turbine_models/lookup_table.py +22 -9
- foxes/models/turbine_models/power_mask.py +81 -51
- foxes/models/turbine_models/rotor_centre_calc.py +17 -20
- foxes/models/turbine_models/sector_management.py +5 -6
- foxes/models/turbine_models/set_farm_vars.py +49 -20
- foxes/models/turbine_models/table_factors.py +5 -5
- foxes/models/turbine_models/thrust2ct.py +9 -5
- foxes/models/turbine_models/yaw2yawm.py +7 -13
- foxes/models/turbine_models/yawm2yaw.py +7 -11
- foxes/models/turbine_types/PCt_file.py +84 -3
- foxes/models/turbine_types/PCt_from_two.py +7 -3
- foxes/models/turbine_types/null_type.py +2 -2
- foxes/models/turbine_types/wsrho2PCt_from_two.py +2 -2
- foxes/models/turbine_types/wsti2PCt_from_two.py +6 -2
- foxes/models/wake_frames/farm_order.py +26 -22
- foxes/models/wake_frames/rotor_wd.py +32 -31
- foxes/models/wake_frames/seq_dynamic_wakes.py +112 -64
- foxes/models/wake_frames/streamlines.py +51 -47
- foxes/models/wake_frames/timelines.py +59 -47
- foxes/models/wake_frames/yawed_wakes.py +63 -40
- foxes/models/wake_models/axisymmetric.py +31 -35
- foxes/models/wake_models/dist_sliced.py +50 -56
- foxes/models/wake_models/gaussian.py +33 -35
- foxes/models/wake_models/induction/rankine_half_body.py +79 -87
- foxes/models/wake_models/induction/rathmann.py +56 -63
- foxes/models/wake_models/induction/self_similar.py +59 -62
- foxes/models/wake_models/ti/crespo_hernandez.py +83 -74
- foxes/models/wake_models/ti/iec_ti.py +65 -75
- foxes/models/wake_models/top_hat.py +60 -69
- foxes/models/wake_models/wake_mirror.py +49 -54
- foxes/models/wake_models/wind/bastankhah14.py +44 -66
- foxes/models/wake_models/wind/bastankhah16.py +84 -111
- foxes/models/wake_models/wind/jensen.py +67 -89
- foxes/models/wake_models/wind/turbopark.py +93 -133
- foxes/models/wake_superpositions/ti_linear.py +33 -27
- foxes/models/wake_superpositions/ti_max.py +33 -27
- foxes/models/wake_superpositions/ti_pow.py +35 -27
- foxes/models/wake_superpositions/ti_quadratic.py +33 -27
- foxes/models/wake_superpositions/ws_linear.py +39 -32
- foxes/models/wake_superpositions/ws_max.py +40 -33
- foxes/models/wake_superpositions/ws_pow.py +39 -32
- foxes/models/wake_superpositions/ws_product.py +35 -28
- foxes/models/wake_superpositions/ws_quadratic.py +39 -32
- foxes/opt/constraints/min_dist.py +1 -1
- foxes/opt/objectives/farm_vars.py +1 -1
- foxes/opt/problems/layout/farm_layout.py +38 -97
- foxes/output/__init__.py +1 -0
- foxes/output/farm_results_eval.py +1 -1
- foxes/output/flow_plots_2d/flow_plots.py +2 -0
- foxes/output/flow_plots_2d/get_fig.py +2 -0
- foxes/output/grids.py +1 -1
- foxes/output/rose_plot.py +3 -3
- foxes/output/rotor_point_plots.py +117 -0
- foxes/output/turbine_type_curves.py +2 -2
- foxes/utils/__init__.py +2 -1
- foxes/utils/load.py +29 -0
- foxes/utils/random_xy.py +56 -0
- foxes/utils/runners/runners.py +13 -1
- foxes/utils/windrose_plot.py +1 -1
- foxes/variables.py +10 -0
- {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/METADATA +13 -7
- {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/RECORD +126 -122
- {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/WHEEL +1 -1
- foxes/models/partial_wakes/distsliced.py +0 -322
- foxes/models/partial_wakes/mapped.py +0 -252
- foxes/models/turbine_models/set_XYHD.py +0 -130
- {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/LICENSE +0 -0
- {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/top_level.txt +0 -0
- {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/zip-safe +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from foxes.core import FarmDataModel, TData
|
|
4
|
+
import foxes.variables as FV
|
|
5
|
+
import foxes.constants as FC
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InitFarmData(FarmDataModel):
|
|
9
|
+
"""
|
|
10
|
+
Sets basic turbine data and applies downwind order
|
|
11
|
+
|
|
12
|
+
:group: algorithms.downwind.models
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""
|
|
18
|
+
Constructor.
|
|
19
|
+
"""
|
|
20
|
+
super().__init__(pre_rotor=True)
|
|
21
|
+
|
|
22
|
+
def output_farm_vars(self, algo):
|
|
23
|
+
"""
|
|
24
|
+
The variables which are being modified by the model.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
algo: foxes.core.Algorithm
|
|
29
|
+
The calculation algorithm
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
output_vars: list of str
|
|
34
|
+
The output variable names
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
return [
|
|
38
|
+
FV.X,
|
|
39
|
+
FV.Y,
|
|
40
|
+
FV.H,
|
|
41
|
+
FV.D,
|
|
42
|
+
FV.WD,
|
|
43
|
+
FV.YAW,
|
|
44
|
+
FV.ORDER,
|
|
45
|
+
FV.WEIGHT,
|
|
46
|
+
FV.ORDER_SSEL,
|
|
47
|
+
FV.ORDER_INV,
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
def calculate(self, algo, mdata, fdata):
|
|
51
|
+
""" "
|
|
52
|
+
The main model calculation.
|
|
53
|
+
|
|
54
|
+
This function is executed on a single chunk of data,
|
|
55
|
+
all computations should be based on numpy arrays.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
algo: foxes.core.Algorithm
|
|
60
|
+
The calculation algorithm
|
|
61
|
+
mdata: foxes.core.Data
|
|
62
|
+
The model data
|
|
63
|
+
fdata: foxes.core.Data
|
|
64
|
+
The farm data
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
results: dict
|
|
69
|
+
The resulting data, keys: output variable str.
|
|
70
|
+
Values: numpy.ndarray with shape (n_states, n_turbines)
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
# prepare:
|
|
74
|
+
n_states = fdata.n_states
|
|
75
|
+
n_turbines = algo.n_turbines
|
|
76
|
+
|
|
77
|
+
# define FV.TXYH as vector [X, Y, H]:
|
|
78
|
+
fdata[FV.TXYH] = np.full((n_states, n_turbines, 3), np.nan, dtype=FC.DTYPE)
|
|
79
|
+
fdata.dims[FV.TXYH] = (FC.STATE, FC.TURBINE, FC.XYH)
|
|
80
|
+
for i, v in enumerate([FV.X, FV.Y, FV.H]):
|
|
81
|
+
fdata[v] = fdata[FV.TXYH][..., i]
|
|
82
|
+
fdata.dims[v] = (FC.STATE, FC.TURBINE)
|
|
83
|
+
|
|
84
|
+
# set X, Y, H, D:
|
|
85
|
+
fdata[FV.D] = np.zeros((n_states, n_turbines), dtype=FC.DTYPE)
|
|
86
|
+
for ti, t in enumerate(algo.farm.turbines):
|
|
87
|
+
|
|
88
|
+
if len(t.xy.shape) == 1:
|
|
89
|
+
fdata[FV.TXYH][:, ti, :2] = t.xy[None, :]
|
|
90
|
+
else:
|
|
91
|
+
i0 = fdata.states_i0()
|
|
92
|
+
s = np.s_[i0 : i0 + fdata.n_states]
|
|
93
|
+
fdata[FV.TXYH][:, ti, :2] = t.xy[s]
|
|
94
|
+
|
|
95
|
+
H = t.H
|
|
96
|
+
if H is None:
|
|
97
|
+
H = algo.farm_controller.turbine_types[ti].H
|
|
98
|
+
fdata[FV.TXYH][:, ti, 2] = H
|
|
99
|
+
|
|
100
|
+
D = t.D
|
|
101
|
+
if D is None:
|
|
102
|
+
D = algo.farm_controller.turbine_types[ti].D
|
|
103
|
+
fdata[FV.D][:, ti] = D
|
|
104
|
+
|
|
105
|
+
# calc WD and YAW at rotor centres:
|
|
106
|
+
tdata = TData.from_points(points=fdata[FV.TXYH])
|
|
107
|
+
sres = algo.states.calculate(algo, mdata, fdata, tdata)
|
|
108
|
+
fdata[FV.WD] = sres[FV.WD][:, :, 0]
|
|
109
|
+
del tdata, sres
|
|
110
|
+
|
|
111
|
+
# calculate and inverse:
|
|
112
|
+
order = algo.wake_frame.calc_order(algo, mdata, fdata)
|
|
113
|
+
ssel = np.zeros_like(order)
|
|
114
|
+
ssel[:] = np.arange(n_states)[:, None]
|
|
115
|
+
fdata[FV.ORDER] = order
|
|
116
|
+
fdata[FV.ORDER_SSEL] = ssel
|
|
117
|
+
fdata[FV.ORDER_INV] = np.zeros_like(order)
|
|
118
|
+
fdata[FV.ORDER_INV][ssel, order] = np.arange(n_turbines)[None, :]
|
|
119
|
+
|
|
120
|
+
# apply downwind order to all data:
|
|
121
|
+
fdata[FV.TXYH] = fdata[FV.TXYH][ssel, order]
|
|
122
|
+
for i, v in enumerate([FV.X, FV.Y, FV.H]):
|
|
123
|
+
fdata[v] = fdata[FV.TXYH][..., i]
|
|
124
|
+
for v in [FV.D, FV.WD, FV.WEIGHT]:
|
|
125
|
+
if np.any(fdata[v] != fdata[v][0, 0, None, None]):
|
|
126
|
+
fdata[v] = fdata[v][ssel, order]
|
|
127
|
+
fdata[FV.YAW] = fdata[FV.WD].copy()
|
|
128
|
+
for k in mdata.keys():
|
|
129
|
+
if tuple(mdata.dims[k][:2]) == (FC.STATE, FC.TURBINE) and np.any(
|
|
130
|
+
mdata[k] != mdata[k][0, 0, None, None]
|
|
131
|
+
):
|
|
132
|
+
mdata[k] = mdata[k][ssel, order]
|
|
133
|
+
|
|
134
|
+
return {v: fdata[v] for v in self.output_farm_vars(algo)}
|
|
@@ -16,7 +16,7 @@ class PointWakesCalculation(PointDataModel):
|
|
|
16
16
|
emodels_cpars: list of dict
|
|
17
17
|
The calculation parameters for extra models
|
|
18
18
|
wake_models: list of foxes.core.WakeModel
|
|
19
|
-
The wake models
|
|
19
|
+
The wake models to be used
|
|
20
20
|
|
|
21
21
|
:group: algorithms.downwind.models
|
|
22
22
|
|
|
@@ -33,7 +33,7 @@ class PointWakesCalculation(PointDataModel):
|
|
|
33
33
|
emodels_cpars: list of dict, optional
|
|
34
34
|
The calculation parameters for extra models
|
|
35
35
|
wake_models: list of foxes.core.WakeModel, optional
|
|
36
|
-
|
|
36
|
+
Specific wake models to be used
|
|
37
37
|
|
|
38
38
|
"""
|
|
39
39
|
super().__init__()
|
|
@@ -86,14 +86,14 @@ class PointWakesCalculation(PointDataModel):
|
|
|
86
86
|
"""
|
|
87
87
|
return self.pvars
|
|
88
88
|
|
|
89
|
-
def
|
|
89
|
+
def contribute(
|
|
90
90
|
self,
|
|
91
91
|
algo,
|
|
92
92
|
mdata,
|
|
93
93
|
fdata,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
tdata,
|
|
95
|
+
downwind_index,
|
|
96
|
+
wmodel,
|
|
97
97
|
wdeltas,
|
|
98
98
|
):
|
|
99
99
|
"""
|
|
@@ -107,30 +107,23 @@ class PointWakesCalculation(PointDataModel):
|
|
|
107
107
|
The model data
|
|
108
108
|
fdata: foxes.core.Data
|
|
109
109
|
The farm data
|
|
110
|
-
|
|
111
|
-
The point data
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
The wake models
|
|
110
|
+
tdata: foxes.core.Data
|
|
111
|
+
The target point data
|
|
112
|
+
downwind_index: int
|
|
113
|
+
The index in the downwind order
|
|
114
|
+
wmodel: foxes.core.WakeModel
|
|
115
|
+
The wake model
|
|
117
116
|
wdeltas: dict
|
|
118
117
|
The wake deltas, are being modified ob the fly.
|
|
119
118
|
Key: Variable name str, for which the
|
|
120
119
|
wake delta applies, values: numpy.ndarray with
|
|
121
|
-
shape (n_states,
|
|
120
|
+
shape (n_states, n_targets, n_tpoints, ...)
|
|
122
121
|
|
|
123
122
|
"""
|
|
124
|
-
wcoos = algo.wake_frame.get_wake_coos(
|
|
125
|
-
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
for w in wmodels:
|
|
129
|
-
w.contribute_to_wake_deltas(
|
|
130
|
-
algo, mdata, fdata, pdata, states_source_turbine, wcoos, wdeltas
|
|
131
|
-
)
|
|
123
|
+
wcoos = algo.wake_frame.get_wake_coos(algo, mdata, fdata, tdata, downwind_index)
|
|
124
|
+
wmodel.contribute(algo, mdata, fdata, tdata, downwind_index, wcoos, wdeltas)
|
|
132
125
|
|
|
133
|
-
def calculate(self, algo, mdata, fdata,
|
|
126
|
+
def calculate(self, algo, mdata, fdata, tdata, downwind_index=None):
|
|
134
127
|
""" "
|
|
135
128
|
The main model calculation.
|
|
136
129
|
|
|
@@ -145,55 +138,51 @@ class PointWakesCalculation(PointDataModel):
|
|
|
145
138
|
The model data
|
|
146
139
|
fdata: foxes.core.Data
|
|
147
140
|
The farm data
|
|
148
|
-
|
|
149
|
-
The point data
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Default: include all turbines
|
|
141
|
+
tdata: foxes.core.Data
|
|
142
|
+
The target point data
|
|
143
|
+
downwind_index: int
|
|
144
|
+
The index in the downwind order of the wake
|
|
145
|
+
causing turbine
|
|
154
146
|
|
|
155
147
|
Returns
|
|
156
|
-
-------
|
|
157
148
|
results: dict
|
|
158
149
|
The resulting data, keys: output variable str.
|
|
159
|
-
Values: numpy.ndarray with shape
|
|
150
|
+
Values: numpy.ndarray with shape
|
|
151
|
+
(n_states, n_targets, n_tpoints)
|
|
160
152
|
|
|
161
153
|
"""
|
|
162
154
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
self.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
for v in self.pvars:
|
|
193
|
-
if v in wdeltas:
|
|
194
|
-
pdata[v] = amb_res[v] + wdeltas[v]
|
|
155
|
+
res = {}
|
|
156
|
+
wmodels = (
|
|
157
|
+
algo.wake_models.values() if self.wake_models is None else self.wake_models
|
|
158
|
+
)
|
|
159
|
+
for wmodel in wmodels:
|
|
160
|
+
wdeltas = wmodel.new_wake_deltas(algo, mdata, fdata, tdata)
|
|
161
|
+
if len(set(self.pvars).intersection(wdeltas.keys())):
|
|
162
|
+
|
|
163
|
+
if downwind_index is None:
|
|
164
|
+
for oi in range(fdata.n_turbines):
|
|
165
|
+
self.contribute(algo, mdata, fdata, tdata, oi, wmodel, wdeltas)
|
|
166
|
+
|
|
167
|
+
else:
|
|
168
|
+
self.contribute(
|
|
169
|
+
algo, mdata, fdata, tdata, downwind_index, wmodel, wdeltas
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
for v in self.pvars:
|
|
173
|
+
if v not in res and v in tdata:
|
|
174
|
+
res[v] = tdata[v].copy()
|
|
175
|
+
|
|
176
|
+
wmodel.finalize_wake_deltas(algo, mdata, fdata, res, wdeltas)
|
|
177
|
+
|
|
178
|
+
for v in res.keys():
|
|
179
|
+
if v in wdeltas:
|
|
180
|
+
res[v] += wdeltas[v]
|
|
181
|
+
|
|
182
|
+
for v in res.keys():
|
|
183
|
+
tdata[v] = res[v]
|
|
195
184
|
|
|
196
185
|
if self.emodels is not None:
|
|
197
|
-
self.emodels.calculate(algo, mdata, fdata,
|
|
186
|
+
self.emodels.calculate(algo, mdata, fdata, tdata, self.emodels_cpars)
|
|
198
187
|
|
|
199
|
-
return {v:
|
|
188
|
+
return {v: tdata[v] for v in self.pvars}
|
|
@@ -1,42 +1,34 @@
|
|
|
1
|
-
import
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
2
3
|
from foxes.core import FarmDataModel
|
|
4
|
+
import foxes.variables as FV
|
|
3
5
|
|
|
4
6
|
|
|
5
|
-
class
|
|
7
|
+
class ReorderFarmOutput(FarmDataModel):
|
|
6
8
|
"""
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
Reorders final results to state-turbine dimensions
|
|
10
|
+
|
|
11
|
+
Attributes
|
|
12
|
+
----------
|
|
13
|
+
outputs: list of str, optional
|
|
14
|
+
The output variables, or None for defaults
|
|
9
15
|
|
|
10
16
|
:group: algorithms.downwind.models
|
|
11
17
|
|
|
12
18
|
"""
|
|
13
19
|
|
|
14
|
-
def
|
|
20
|
+
def __init__(self, outputs):
|
|
15
21
|
"""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Returns
|
|
19
|
-
-------
|
|
20
|
-
smdls: list of foxes.core.Model
|
|
21
|
-
Names of all sub models
|
|
22
|
-
|
|
23
|
-
"""
|
|
24
|
-
return [self._wframe]
|
|
25
|
-
|
|
26
|
-
def initialize(self, algo, verbosity=0):
|
|
27
|
-
"""
|
|
28
|
-
Initializes the model.
|
|
22
|
+
Constructor
|
|
29
23
|
|
|
30
24
|
Parameters
|
|
31
25
|
----------
|
|
32
|
-
|
|
33
|
-
The
|
|
34
|
-
verbosity: int
|
|
35
|
-
The verbosity level, 0 = silent
|
|
26
|
+
outputs: list of str, optional
|
|
27
|
+
The output variables, or None for defaults
|
|
36
28
|
|
|
37
29
|
"""
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
super().__init__(pre_rotor=False)
|
|
31
|
+
self.outputs = outputs
|
|
40
32
|
|
|
41
33
|
def output_farm_vars(self, algo):
|
|
42
34
|
"""
|
|
@@ -53,7 +45,7 @@ class CalcOrder(FarmDataModel):
|
|
|
53
45
|
The output variable names
|
|
54
46
|
|
|
55
47
|
"""
|
|
56
|
-
return
|
|
48
|
+
return self.outputs if self.outputs is not None else algo.farm_vars
|
|
57
49
|
|
|
58
50
|
def calculate(self, algo, mdata, fdata):
|
|
59
51
|
""" "
|
|
@@ -78,4 +70,14 @@ class CalcOrder(FarmDataModel):
|
|
|
78
70
|
Values: numpy.ndarray with shape (n_states, n_turbines)
|
|
79
71
|
|
|
80
72
|
"""
|
|
81
|
-
|
|
73
|
+
ssel = fdata[FV.ORDER_SSEL]
|
|
74
|
+
order_inv = fdata[FV.ORDER_INV]
|
|
75
|
+
|
|
76
|
+
out = {}
|
|
77
|
+
for v in self.output_farm_vars(algo):
|
|
78
|
+
if v != FV.ORDER and np.any(fdata[v] != fdata[v][0, 0, None, None]):
|
|
79
|
+
out[v] = fdata[v][ssel, order_inv]
|
|
80
|
+
else:
|
|
81
|
+
out[v] = fdata[v]
|
|
82
|
+
|
|
83
|
+
return out
|
|
@@ -44,7 +44,7 @@ class Iterative(Downwind):
|
|
|
44
44
|
except AttributeError:
|
|
45
45
|
return super().get_model(name)
|
|
46
46
|
|
|
47
|
-
def __init__(self, *args, max_it=None, conv_crit=
|
|
47
|
+
def __init__(self, *args, max_it=None, conv_crit="default", mod_cutin={}, **kwargs):
|
|
48
48
|
"""
|
|
49
49
|
Constructor.
|
|
50
50
|
|
|
@@ -56,6 +56,8 @@ class Iterative(Downwind):
|
|
|
56
56
|
The maximal number of iterations
|
|
57
57
|
conv_crit: foxes.algorithms.iterative.ConvCrit, optional
|
|
58
58
|
The convergence criteria
|
|
59
|
+
mod_cutin: dict, optional
|
|
60
|
+
Parameters for cutin modification
|
|
59
61
|
kwargs: dict, optional
|
|
60
62
|
Keyword arguments for Downwind
|
|
61
63
|
|
|
@@ -64,15 +66,16 @@ class Iterative(Downwind):
|
|
|
64
66
|
|
|
65
67
|
self.max_it = 2 * self.farm.n_turbines if max_it is None else max_it
|
|
66
68
|
self.conv_crit = (
|
|
67
|
-
self.get_model("DefaultConv")() if conv_crit
|
|
69
|
+
self.get_model("DefaultConv")() if conv_crit == "default" else conv_crit
|
|
68
70
|
)
|
|
69
71
|
self.prev_farm_results = None
|
|
70
72
|
self._it = None
|
|
71
73
|
self._mlist = None
|
|
72
74
|
self._reamb = False
|
|
73
|
-
self._urelax =
|
|
74
|
-
|
|
75
|
-
)
|
|
75
|
+
self._urelax = None
|
|
76
|
+
|
|
77
|
+
self._mod_cutin = dict(modify_ct=True, modify_P=False)
|
|
78
|
+
self._mod_cutin.update(mod_cutin)
|
|
76
79
|
|
|
77
80
|
def set_urelax(self, entry_point, **urel):
|
|
78
81
|
"""
|
|
@@ -89,8 +92,25 @@ class Iterative(Downwind):
|
|
|
89
92
|
"""
|
|
90
93
|
if self.initialized:
|
|
91
94
|
raise ValueError(f"Attempt to set_urelax after initialization")
|
|
95
|
+
if self._urelax is None:
|
|
96
|
+
self._urelax = Dict(
|
|
97
|
+
first={},
|
|
98
|
+
pre_rotor={},
|
|
99
|
+
post_rotor={},
|
|
100
|
+
pre_wake={},
|
|
101
|
+
last={},
|
|
102
|
+
)
|
|
92
103
|
self._urelax[entry_point].update(urel)
|
|
93
104
|
|
|
105
|
+
def initialize(self):
|
|
106
|
+
"""
|
|
107
|
+
Initializes the algorithm.
|
|
108
|
+
"""
|
|
109
|
+
super().initialize()
|
|
110
|
+
if len(self._mod_cutin):
|
|
111
|
+
for t in self.farm_controller.turbine_types:
|
|
112
|
+
t.modify_cutin(**self._mod_cutin)
|
|
113
|
+
|
|
94
114
|
@property
|
|
95
115
|
def urelax(self):
|
|
96
116
|
"""
|
|
@@ -119,46 +139,50 @@ class Iterative(Downwind):
|
|
|
119
139
|
|
|
120
140
|
def _collect_farm_models(
|
|
121
141
|
self,
|
|
142
|
+
outputs,
|
|
122
143
|
calc_parameters,
|
|
123
144
|
ambient,
|
|
124
145
|
):
|
|
125
146
|
"""
|
|
126
147
|
Helper function that creates model list
|
|
127
148
|
"""
|
|
128
|
-
|
|
129
149
|
if self._it == 0:
|
|
130
150
|
self._mlist0, self._calc_pars0 = super()._collect_farm_models(
|
|
131
|
-
|
|
151
|
+
outputs=False,
|
|
152
|
+
calc_parameters=calc_parameters,
|
|
132
153
|
ambient=ambient,
|
|
133
154
|
)
|
|
134
155
|
|
|
135
156
|
n = 0
|
|
136
|
-
if
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
157
|
+
if self._urelax is not None:
|
|
158
|
+
if len(self._urelax["first"]):
|
|
159
|
+
self._mlist0.insert(0, mdls.URelax(**self._urelax["first"]))
|
|
160
|
+
self._calc_pars0.insert(0, {})
|
|
161
|
+
self._reamb = True
|
|
162
|
+
n += 1
|
|
163
|
+
|
|
164
|
+
if len(self._urelax["pre_rotor"]):
|
|
165
|
+
self._mlist0.insert(2 + n, mdls.URelax(**self._urelax["pre_rotor"]))
|
|
166
|
+
self._calc_pars0.insert(2 + n, {})
|
|
167
|
+
self._reamb = True
|
|
168
|
+
n += 1
|
|
169
|
+
|
|
170
|
+
if len(self._urelax["post_rotor"]):
|
|
171
|
+
self._mlist0.insert(
|
|
172
|
+
4 + n, mdls.URelax(**self._urelax["post_rotor"])
|
|
173
|
+
)
|
|
174
|
+
self._calc_pars0.insert(4 + n, {})
|
|
175
|
+
self._reamb = True
|
|
176
|
+
n += 1
|
|
177
|
+
|
|
178
|
+
if len(self._urelax["pre_wake"]):
|
|
179
|
+
self._mlist0.models[5 + n].urelax = mdls.URelax(
|
|
180
|
+
**self._urelax["pre_wake"]
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if len(self._urelax["last"]):
|
|
184
|
+
self._mlist0.append(mdls.URelax(**self._urelax["last"]))
|
|
185
|
+
self._calc_pars0.append({})
|
|
162
186
|
|
|
163
187
|
return self._mlist0, self._calc_pars0
|
|
164
188
|
|
|
@@ -170,19 +194,34 @@ class Iterative(Downwind):
|
|
|
170
194
|
calc_pars = []
|
|
171
195
|
mlist = FarmDataModelList(models=[])
|
|
172
196
|
|
|
173
|
-
#
|
|
174
|
-
|
|
175
|
-
if len(self._urelax["pre_wake"]):
|
|
176
|
-
urelax = mdls.URelax(**self._urelax["pre_wake"])
|
|
197
|
+
# do not rotate back from downwind order:
|
|
198
|
+
if not self._final_run:
|
|
177
199
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
200
|
+
# add under-relaxation during wake calculation:
|
|
201
|
+
urelax = None
|
|
202
|
+
if self._urelax is not None and len(self._urelax["pre_wake"]):
|
|
203
|
+
urelax = mdls.URelax(**self._urelax["pre_wake"])
|
|
181
204
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
205
|
+
# add model that calculates wake effects:
|
|
206
|
+
mlist.models.append(
|
|
207
|
+
self.get_model("FarmWakesCalculation")(urelax=urelax)
|
|
208
|
+
)
|
|
209
|
+
calc_pars.append(calc_parameters.get(mlist.models[-1].name, {}))
|
|
210
|
+
|
|
211
|
+
# add under-relaxation:
|
|
212
|
+
if self._urelax is not None and len(self._urelax["last"]):
|
|
213
|
+
mlist.append(mdls.URelax(**self._urelax["last"]))
|
|
214
|
+
calc_pars.append({})
|
|
215
|
+
|
|
216
|
+
# rotate back from downwind order:
|
|
217
|
+
else:
|
|
218
|
+
|
|
219
|
+
# add model that calculates wake effects:
|
|
220
|
+
# mlist.models.append(self.get_model("FarmWakesCalculation")(urelax=None))
|
|
221
|
+
# calc_pars.append(calc_parameters.get(mlist.models[-1].name, {}))
|
|
222
|
+
|
|
223
|
+
mlist.models.append(self.get_model("ReorderFarmOutput")(outputs))
|
|
224
|
+
calc_pars.append(calc_parameters.get(mlist.models[-1].name, {}))
|
|
186
225
|
|
|
187
226
|
return mlist, calc_pars
|
|
188
227
|
|
|
@@ -218,23 +257,33 @@ class Iterative(Downwind):
|
|
|
218
257
|
dimensions (state, turbine)
|
|
219
258
|
|
|
220
259
|
"""
|
|
260
|
+
outputs = kwargs.pop("outputs", self.DEFAULT_FARM_OUTPUTS)
|
|
261
|
+
outputs = list(set(outputs + [FV.ORDER_SSEL, FV.ORDER_INV, FV.WEIGHT]))
|
|
262
|
+
|
|
221
263
|
fres = None
|
|
222
264
|
self._it = -1
|
|
265
|
+
self._final_run = False
|
|
223
266
|
while self._it < self.max_it:
|
|
224
267
|
self._it += 1
|
|
225
268
|
|
|
226
269
|
self.print(f"\nAlgorithm {self.name}: Iteration {self._it}\n", vlim=0)
|
|
227
270
|
|
|
228
271
|
self.prev_farm_results = fres
|
|
229
|
-
fres = super().calc_farm(finalize=False, **kwargs)
|
|
272
|
+
fres = super().calc_farm(outputs=None, finalize=False, **kwargs)
|
|
230
273
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
274
|
+
if self.conv_crit is not None:
|
|
275
|
+
conv = self.conv_crit.check_converged(
|
|
276
|
+
self, self.prev_farm_results, fres, verbosity=self.verbosity + 1
|
|
277
|
+
)
|
|
234
278
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
279
|
+
if conv:
|
|
280
|
+
self.print(
|
|
281
|
+
f"\nAlgorithm {self.name}: Convergence reached.\n", vlim=0
|
|
282
|
+
)
|
|
283
|
+
self.print("Starting final run")
|
|
284
|
+
self._final_run = True
|
|
285
|
+
fres = super().calc_farm(outputs=outputs, finalize=False, **kwargs)
|
|
286
|
+
break
|
|
238
287
|
|
|
239
288
|
if self._it == 0:
|
|
240
289
|
self.verbosity -= 1
|