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
|
@@ -23,54 +23,33 @@ class PartialWakesModel(Model):
|
|
|
23
23
|
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def get_wake_points(self, algo, mdata, fdata):
|
|
27
28
|
"""
|
|
28
|
-
|
|
29
|
+
Get the wake calculation points, and their
|
|
30
|
+
weights.
|
|
29
31
|
|
|
30
32
|
Parameters
|
|
31
33
|
----------
|
|
32
|
-
|
|
33
|
-
The
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"""
|
|
39
|
-
super().__init__()
|
|
40
|
-
|
|
41
|
-
self._wmodels = wake_models
|
|
42
|
-
self._wframe = wake_frame
|
|
43
|
-
|
|
44
|
-
def sub_models(self):
|
|
45
|
-
"""
|
|
46
|
-
List of all sub-models
|
|
34
|
+
algo: foxes.core.Algorithm
|
|
35
|
+
The calculation algorithm
|
|
36
|
+
mdata: foxes.core.Data
|
|
37
|
+
The model data
|
|
38
|
+
fdata: foxes.core.Data
|
|
39
|
+
The farm data
|
|
47
40
|
|
|
48
41
|
Returns
|
|
49
42
|
-------
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def initialize(self, algo, verbosity=0):
|
|
57
|
-
"""
|
|
58
|
-
Initializes the model.
|
|
59
|
-
|
|
60
|
-
Parameters
|
|
61
|
-
----------
|
|
62
|
-
algo: foxes.core.Algorithm
|
|
63
|
-
The calculation algorithm
|
|
64
|
-
verbosity: int
|
|
65
|
-
The verbosity level, 0 = silent
|
|
43
|
+
rpoints: numpy.ndarray
|
|
44
|
+
The wake calculation points, shape:
|
|
45
|
+
(n_states, n_turbines, n_tpoints, 3)
|
|
46
|
+
rweights: numpy.ndarray
|
|
47
|
+
The target point weights, shape: (n_tpoints,)
|
|
66
48
|
|
|
67
49
|
"""
|
|
68
|
-
|
|
69
|
-
self.wake_frame = algo.wake_frame if self._wframe is None else self._wframe
|
|
70
|
-
super().initialize(algo, verbosity)
|
|
50
|
+
pass
|
|
71
51
|
|
|
72
|
-
|
|
73
|
-
def new_wake_deltas(self, algo, mdata, fdata):
|
|
52
|
+
def new_wake_deltas(self, algo, mdata, fdata, tdata, wmodel):
|
|
74
53
|
"""
|
|
75
54
|
Creates new initial wake deltas, filled
|
|
76
55
|
with zeros.
|
|
@@ -83,88 +62,112 @@ class PartialWakesModel(Model):
|
|
|
83
62
|
The model data
|
|
84
63
|
fdata: foxes.core.Data
|
|
85
64
|
The farm data
|
|
65
|
+
tdata: foxes.core.Data
|
|
66
|
+
The target point data
|
|
67
|
+
wmodel: foxes.core.WakeModel
|
|
68
|
+
The wake model
|
|
69
|
+
wpoints: numpy.ndarray
|
|
70
|
+
The wake evaluation points,
|
|
71
|
+
shape: (n_states, n_turbines, n_tpoints, 3)
|
|
86
72
|
|
|
87
73
|
Returns
|
|
88
74
|
-------
|
|
89
75
|
wake_deltas: dict
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
The evaluation point data
|
|
76
|
+
Key: variable name, value: The zero filled
|
|
77
|
+
wake deltas, shape: (n_states, n_turbines, n_tpoints, ...)
|
|
93
78
|
|
|
94
79
|
"""
|
|
95
|
-
|
|
80
|
+
return wmodel.new_wake_deltas(algo, mdata, fdata, tdata)
|
|
96
81
|
|
|
97
|
-
|
|
98
|
-
def contribute_to_wake_deltas(
|
|
82
|
+
def contribute(
|
|
99
83
|
self,
|
|
100
84
|
algo,
|
|
101
85
|
mdata,
|
|
102
86
|
fdata,
|
|
103
|
-
|
|
104
|
-
|
|
87
|
+
tdata,
|
|
88
|
+
downwind_index,
|
|
105
89
|
wake_deltas,
|
|
90
|
+
wmodel,
|
|
106
91
|
):
|
|
107
92
|
"""
|
|
108
|
-
Modifies wake deltas
|
|
109
|
-
specified wake source turbines.
|
|
93
|
+
Modifies wake deltas at target points by
|
|
94
|
+
contributions from the specified wake source turbines.
|
|
110
95
|
|
|
111
96
|
Parameters
|
|
112
97
|
----------
|
|
113
98
|
algo: foxes.core.Algorithm
|
|
114
99
|
The calculation algorithm
|
|
115
|
-
mdata: foxes.core.
|
|
100
|
+
mdata: foxes.core.MData
|
|
116
101
|
The model data
|
|
117
|
-
fdata: foxes.core.
|
|
102
|
+
fdata: foxes.core.FData
|
|
118
103
|
The farm data
|
|
119
|
-
|
|
120
|
-
The
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
wake_deltas:
|
|
125
|
-
The wake deltas
|
|
126
|
-
|
|
104
|
+
tdata: foxes.core.TData
|
|
105
|
+
The target point data
|
|
106
|
+
downwind_index: int
|
|
107
|
+
The index of the wake causing turbine
|
|
108
|
+
in the downwnd order
|
|
109
|
+
wake_deltas: dict
|
|
110
|
+
The wake deltas. Key: variable name,
|
|
111
|
+
value: numpy.ndarray with shape
|
|
112
|
+
(n_states, n_targets, n_tpoints, ...)
|
|
113
|
+
wmodel: foxes.core.WakeModel
|
|
114
|
+
The wake model
|
|
127
115
|
|
|
128
116
|
"""
|
|
129
|
-
|
|
117
|
+
wcoos = algo.wake_frame.get_wake_coos(algo, mdata, fdata, tdata, downwind_index)
|
|
118
|
+
wmodel.contribute(algo, mdata, fdata, tdata, downwind_index, wcoos, wake_deltas)
|
|
130
119
|
|
|
131
120
|
@abstractmethod
|
|
132
|
-
def
|
|
121
|
+
def finalize_wakes(
|
|
133
122
|
self,
|
|
134
123
|
algo,
|
|
135
124
|
mdata,
|
|
136
125
|
fdata,
|
|
137
|
-
|
|
126
|
+
tdata,
|
|
127
|
+
amb_res,
|
|
128
|
+
rpoint_weights,
|
|
138
129
|
wake_deltas,
|
|
139
|
-
|
|
140
|
-
|
|
130
|
+
wmodel,
|
|
131
|
+
downwind_index,
|
|
141
132
|
):
|
|
142
133
|
"""
|
|
143
|
-
Updates the
|
|
144
|
-
|
|
134
|
+
Updates the wake_deltas at the selected target
|
|
135
|
+
downwind index.
|
|
136
|
+
|
|
137
|
+
Modifies wake_deltas on the fly.
|
|
145
138
|
|
|
146
139
|
Parameters
|
|
147
140
|
----------
|
|
148
141
|
algo: foxes.core.Algorithm
|
|
149
142
|
The calculation algorithm
|
|
150
|
-
mdata: foxes.core.
|
|
143
|
+
mdata: foxes.core.MData
|
|
151
144
|
The model data
|
|
152
|
-
fdata: foxes.core.
|
|
145
|
+
fdata: foxes.core.FData
|
|
153
146
|
The farm data
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
147
|
+
tdata: foxes.core.Data
|
|
148
|
+
The target point data
|
|
149
|
+
amb_res: dict
|
|
150
|
+
The ambient results at the target points
|
|
151
|
+
of all rotors. Key: variable name, value
|
|
152
|
+
np.ndarray of shape:
|
|
153
|
+
(n_states, n_turbines, n_rotor_points)
|
|
154
|
+
rpoint_weights: numpy.ndarray
|
|
155
|
+
The rotor point weights, shape: (n_rotor_points,)
|
|
156
|
+
wake_deltas: dict
|
|
157
|
+
The wake deltas. Key: variable name,
|
|
158
|
+
value: np.ndarray of shape
|
|
159
|
+
(n_states, n_turbines, n_tpoints)
|
|
160
|
+
wmodel: foxes.core.WakeModel
|
|
161
|
+
The wake model
|
|
162
|
+
downwind_index: int
|
|
163
|
+
The index in the downwind order
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
final_wake_deltas: dict
|
|
168
|
+
The final wake deltas at the selected downwind
|
|
169
|
+
turbines. Key: variable name, value: np.ndarray
|
|
170
|
+
of shape (n_states, n_rotor_points)
|
|
168
171
|
|
|
169
172
|
"""
|
|
170
173
|
pass
|
foxes/core/point_data_model.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import numpy as np
|
|
1
2
|
from abc import abstractmethod
|
|
2
3
|
|
|
3
4
|
from .data_calc_model import DataCalcModel
|
|
@@ -26,8 +27,33 @@ class PointDataModel(DataCalcModel):
|
|
|
26
27
|
"""
|
|
27
28
|
return []
|
|
28
29
|
|
|
30
|
+
def ensure_variables(self, algo, mdata, fdata, tdata):
|
|
31
|
+
"""
|
|
32
|
+
Add variables to tdata, initialized with NaN
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
algo: foxes.core.Algorithm
|
|
37
|
+
The calculation algorithm
|
|
38
|
+
mdata: foxes.core.Data
|
|
39
|
+
The model data
|
|
40
|
+
fdata: foxes.core.Data
|
|
41
|
+
The farm data
|
|
42
|
+
tdata: foxes.core.Data
|
|
43
|
+
The target point data
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
for v in self.output_point_vars(algo):
|
|
47
|
+
if v not in tdata:
|
|
48
|
+
tdata[v] = np.full(
|
|
49
|
+
(tdata.n_states, tdata.n_targets, tdata.n_tpoints),
|
|
50
|
+
np.nan,
|
|
51
|
+
dtype=FC.DTYPE,
|
|
52
|
+
)
|
|
53
|
+
tdata.dims[v] = (FC.STATE, FC.TARGET, FC.TPOINT)
|
|
54
|
+
|
|
29
55
|
@abstractmethod
|
|
30
|
-
def calculate(self, algo, mdata, fdata,
|
|
56
|
+
def calculate(self, algo, mdata, fdata, tdata):
|
|
31
57
|
""" "
|
|
32
58
|
The main model calculation.
|
|
33
59
|
|
|
@@ -38,18 +64,19 @@ class PointDataModel(DataCalcModel):
|
|
|
38
64
|
----------
|
|
39
65
|
algo: foxes.core.Algorithm
|
|
40
66
|
The calculation algorithm
|
|
41
|
-
mdata: foxes.core.
|
|
67
|
+
mdata: foxes.core.MData
|
|
42
68
|
The model data
|
|
43
|
-
fdata: foxes.core.
|
|
69
|
+
fdata: foxes.core.FData
|
|
44
70
|
The farm data
|
|
45
|
-
|
|
46
|
-
The point data
|
|
71
|
+
tdata: foxes.core.TData
|
|
72
|
+
The target point data
|
|
47
73
|
|
|
48
74
|
Returns
|
|
49
75
|
-------
|
|
50
76
|
results: dict
|
|
51
77
|
The resulting data, keys: output variable str.
|
|
52
|
-
Values: numpy.ndarray with shape
|
|
78
|
+
Values: numpy.ndarray with shape
|
|
79
|
+
(n_states, n_targets, n_tpoints)
|
|
53
80
|
|
|
54
81
|
"""
|
|
55
82
|
pass
|
|
@@ -78,14 +105,19 @@ class PointDataModel(DataCalcModel):
|
|
|
78
105
|
The calculation results
|
|
79
106
|
|
|
80
107
|
"""
|
|
81
|
-
|
|
108
|
+
results = super().run_calculation(
|
|
82
109
|
algo,
|
|
83
110
|
*data,
|
|
84
111
|
out_vars=out_vars,
|
|
85
|
-
loop_dims=[FC.STATE, FC.
|
|
86
|
-
out_core_vars=[FC.VARS],
|
|
112
|
+
loop_dims=[FC.STATE, FC.TARGET],
|
|
113
|
+
out_core_vars=[FC.TPOINT, FC.VARS],
|
|
87
114
|
**calc_pars,
|
|
88
115
|
)
|
|
116
|
+
if results.sizes[FC.TPOINT] != 1:
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"PointDataModel '{self.name}': Expecting dimension '{FC.TPOINT}' of size 1, found {results.sizes[FC.TPOINT]}"
|
|
119
|
+
)
|
|
120
|
+
return results.sel({FC.TPOINT: 0}).rename({FC.TARGET: FC.POINT})
|
|
89
121
|
|
|
90
122
|
def __add__(self, m):
|
|
91
123
|
if isinstance(m, list):
|
|
@@ -170,7 +202,7 @@ class PointDataModelList(PointDataModel):
|
|
|
170
202
|
ovars += m.output_point_vars(algo)
|
|
171
203
|
return list(dict.fromkeys(ovars))
|
|
172
204
|
|
|
173
|
-
def calculate(self, algo, mdata, fdata,
|
|
205
|
+
def calculate(self, algo, mdata, fdata, tdata, parameters=None):
|
|
174
206
|
""" "
|
|
175
207
|
The main model calculation.
|
|
176
208
|
|
|
@@ -181,12 +213,12 @@ class PointDataModelList(PointDataModel):
|
|
|
181
213
|
----------
|
|
182
214
|
algo: foxes.core.Algorithm
|
|
183
215
|
The calculation algorithm
|
|
184
|
-
mdata: foxes.core.
|
|
216
|
+
mdata: foxes.core.MData
|
|
185
217
|
The model data
|
|
186
|
-
fdata: foxes.core.
|
|
218
|
+
fdata: foxes.core.FData
|
|
187
219
|
The farm data
|
|
188
|
-
|
|
189
|
-
The point data
|
|
220
|
+
tdata: foxes.core.TData
|
|
221
|
+
The target point data
|
|
190
222
|
parameters: list of dict, optional
|
|
191
223
|
A list of parameter dicts, one for each model
|
|
192
224
|
|
|
@@ -194,7 +226,8 @@ class PointDataModelList(PointDataModel):
|
|
|
194
226
|
-------
|
|
195
227
|
results: dict
|
|
196
228
|
The resulting data, keys: output variable str.
|
|
197
|
-
Values: numpy.ndarray with shape
|
|
229
|
+
Values: numpy.ndarray with shape
|
|
230
|
+
(n_states, n_targets, n_tpoints)
|
|
198
231
|
|
|
199
232
|
"""
|
|
200
233
|
if parameters is None:
|
|
@@ -209,7 +242,7 @@ class PointDataModelList(PointDataModel):
|
|
|
209
242
|
)
|
|
210
243
|
|
|
211
244
|
for mi, m in enumerate(self.models):
|
|
212
|
-
res = m.calculate(algo, mdata, fdata,
|
|
213
|
-
|
|
245
|
+
res = m.calculate(algo, mdata, fdata, tdata, **parameters[mi])
|
|
246
|
+
tdata.update(res)
|
|
214
247
|
|
|
215
|
-
return {v:
|
|
248
|
+
return {v: tdata[v] for v in self.output_point_vars(algo)}
|
foxes/core/rotor_model.py
CHANGED
|
@@ -6,7 +6,7 @@ import foxes.constants as FC
|
|
|
6
6
|
from .farm_data_model import FarmDataModel
|
|
7
7
|
from foxes.utils import wd2uv, uv2wd, all_subclasses
|
|
8
8
|
|
|
9
|
-
from .data import
|
|
9
|
+
from .data import TData
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class RotorModel(FarmDataModel):
|
|
@@ -41,6 +41,10 @@ class RotorModel(FarmDataModel):
|
|
|
41
41
|
super().__init__()
|
|
42
42
|
self.calc_vars = calc_vars
|
|
43
43
|
|
|
44
|
+
self.RPOINTS = self.var("rpoints")
|
|
45
|
+
self.RWEIGHTS = self.var("rweights")
|
|
46
|
+
self.AMBRES = self.var("amb_res")
|
|
47
|
+
|
|
44
48
|
def output_farm_vars(self, algo):
|
|
45
49
|
"""
|
|
46
50
|
The variables which are being modified by the model.
|
|
@@ -118,9 +122,9 @@ class RotorModel(FarmDataModel):
|
|
|
118
122
|
----------
|
|
119
123
|
algo: foxes.core.Algorithm
|
|
120
124
|
The calculation algorithm
|
|
121
|
-
mdata: foxes.core.
|
|
125
|
+
mdata: foxes.core.MData
|
|
122
126
|
The model data
|
|
123
|
-
fdata: foxes.core.
|
|
127
|
+
fdata: foxes.core.FData
|
|
124
128
|
The farm data
|
|
125
129
|
|
|
126
130
|
Returns
|
|
@@ -152,17 +156,17 @@ class RotorModel(FarmDataModel):
|
|
|
152
156
|
|
|
153
157
|
return points
|
|
154
158
|
|
|
155
|
-
def _set_res(self, fdata, v, res,
|
|
159
|
+
def _set_res(self, fdata, v, res, downwind_index):
|
|
156
160
|
"""
|
|
157
161
|
Helper function for results setting
|
|
158
162
|
"""
|
|
159
|
-
if
|
|
163
|
+
if downwind_index is None:
|
|
160
164
|
fdata[v] = res.copy()
|
|
161
165
|
elif res.shape[1] == 1:
|
|
162
|
-
fdata[v][
|
|
166
|
+
fdata[v][:, downwind_index] = res[:, 0]
|
|
163
167
|
else:
|
|
164
168
|
raise ValueError(
|
|
165
|
-
f"Rotor model '{self.name}':
|
|
169
|
+
f"Rotor model '{self.name}': downwind_index is not None, but results shape for '{v}' has more than one turbine, {res.shape}"
|
|
166
170
|
)
|
|
167
171
|
|
|
168
172
|
def eval_rpoint_results(
|
|
@@ -170,9 +174,9 @@ class RotorModel(FarmDataModel):
|
|
|
170
174
|
algo,
|
|
171
175
|
mdata,
|
|
172
176
|
fdata,
|
|
173
|
-
|
|
177
|
+
tdata,
|
|
174
178
|
weights,
|
|
175
|
-
|
|
179
|
+
downwind_index=None,
|
|
176
180
|
copy_to_ambient=False,
|
|
177
181
|
):
|
|
178
182
|
"""
|
|
@@ -188,32 +192,21 @@ class RotorModel(FarmDataModel):
|
|
|
188
192
|
----------
|
|
189
193
|
algo: foxes.core.Algorithm
|
|
190
194
|
The calculation algorithm
|
|
191
|
-
mdata: foxes.core.
|
|
195
|
+
mdata: foxes.core.MData
|
|
192
196
|
The model data
|
|
193
|
-
fdata: foxes.core.
|
|
197
|
+
fdata: foxes.core.FData
|
|
194
198
|
The farm data
|
|
195
|
-
|
|
196
|
-
The
|
|
197
|
-
Values: numpy.ndarray, shape if `states_turbine`
|
|
198
|
-
is None: (n_states, n_turbines, n_rpoints).
|
|
199
|
-
Else: (n_states, 1, n_rpoints)
|
|
199
|
+
tdata: foxes.core.TData
|
|
200
|
+
The target point data
|
|
200
201
|
weights: numpy.ndarray
|
|
201
202
|
The rotor point weights, shape: (n_rpoints,)
|
|
202
|
-
|
|
203
|
-
The
|
|
204
|
-
copy_to_ambient: bool
|
|
203
|
+
downwind_index: int, optional
|
|
204
|
+
The index in the downwind order
|
|
205
|
+
copy_to_ambient: bool
|
|
205
206
|
If `True`, the fdata results are copied to ambient
|
|
206
207
|
variables after calculation
|
|
207
208
|
|
|
208
209
|
"""
|
|
209
|
-
|
|
210
|
-
n_states = mdata.n_states
|
|
211
|
-
n_turbines = algo.n_turbines
|
|
212
|
-
if states_turbine is not None:
|
|
213
|
-
stsel = (np.arange(n_states), states_turbine)
|
|
214
|
-
else:
|
|
215
|
-
stsel = None
|
|
216
|
-
|
|
217
210
|
uvp = None
|
|
218
211
|
uv = None
|
|
219
212
|
if (
|
|
@@ -224,8 +217,8 @@ class RotorModel(FarmDataModel):
|
|
|
224
217
|
or FV.REWS2 in self.calc_vars
|
|
225
218
|
or FV.REWS3 in self.calc_vars
|
|
226
219
|
):
|
|
227
|
-
wd =
|
|
228
|
-
ws =
|
|
220
|
+
wd = tdata[FV.WD]
|
|
221
|
+
ws = tdata[FV.WS]
|
|
229
222
|
uvp = wd2uv(wd, ws, axis=-1)
|
|
230
223
|
uv = np.einsum("stpd,p->std", uvp, weights)
|
|
231
224
|
|
|
@@ -235,12 +228,12 @@ class RotorModel(FarmDataModel):
|
|
|
235
228
|
if v == FV.WD or v == FV.YAW:
|
|
236
229
|
if wd is None:
|
|
237
230
|
wd = uv2wd(uv, axis=-1)
|
|
238
|
-
self._set_res(fdata, v, wd,
|
|
231
|
+
self._set_res(fdata, v, wd, downwind_index)
|
|
239
232
|
vdone.append(v)
|
|
240
233
|
|
|
241
234
|
elif v == FV.WS:
|
|
242
235
|
ws = np.linalg.norm(uv, axis=-1)
|
|
243
|
-
self._set_res(fdata, v, ws,
|
|
236
|
+
self._set_res(fdata, v, ws, downwind_index)
|
|
244
237
|
del ws
|
|
245
238
|
vdone.append(v)
|
|
246
239
|
del uv, wd
|
|
@@ -250,17 +243,17 @@ class RotorModel(FarmDataModel):
|
|
|
250
243
|
or FV.REWS2 in self.calc_vars
|
|
251
244
|
or FV.REWS3 in self.calc_vars
|
|
252
245
|
):
|
|
253
|
-
if
|
|
246
|
+
if downwind_index is None:
|
|
254
247
|
yaw = fdata[FV.YAW].copy()
|
|
255
248
|
else:
|
|
256
|
-
yaw = fdata[FV.YAW][
|
|
249
|
+
yaw = fdata[FV.YAW][:, downwind_index, None]
|
|
257
250
|
nax = wd2uv(yaw, axis=-1)
|
|
258
251
|
wsp = np.einsum("stpd,std->stp", uvp, nax)
|
|
259
252
|
|
|
260
253
|
for v in self.calc_vars:
|
|
261
254
|
if v == FV.REWS:
|
|
262
255
|
rews = np.maximum(np.einsum("stp,p->st", wsp, weights), 0.0)
|
|
263
|
-
self._set_res(fdata, v, rews,
|
|
256
|
+
self._set_res(fdata, v, rews, downwind_index)
|
|
264
257
|
del rews
|
|
265
258
|
vdone.append(v)
|
|
266
259
|
|
|
@@ -278,7 +271,7 @@ class RotorModel(FarmDataModel):
|
|
|
278
271
|
)
|
|
279
272
|
else:
|
|
280
273
|
rews2 = np.sqrt(np.einsum("stp,p->st", wsp**2, weights))
|
|
281
|
-
self._set_res(fdata, v, rews2,
|
|
274
|
+
self._set_res(fdata, v, rews2, downwind_index)
|
|
282
275
|
del rews2
|
|
283
276
|
vdone.append(v)
|
|
284
277
|
|
|
@@ -293,7 +286,7 @@ class RotorModel(FarmDataModel):
|
|
|
293
286
|
) ** (1.0 / 3.0)
|
|
294
287
|
else:
|
|
295
288
|
rews3 = (np.einsum("stp,p->st", wsp**3, weights)) ** (1.0 / 3.0)
|
|
296
|
-
self._set_res(fdata, v, rews3,
|
|
289
|
+
self._set_res(fdata, v, rews3, downwind_index)
|
|
297
290
|
del rews3
|
|
298
291
|
vdone.append(v)
|
|
299
292
|
|
|
@@ -302,8 +295,8 @@ class RotorModel(FarmDataModel):
|
|
|
302
295
|
|
|
303
296
|
for v in self.calc_vars:
|
|
304
297
|
if v not in vdone:
|
|
305
|
-
res = np.einsum("stp,p->st",
|
|
306
|
-
self._set_res(fdata, v, res,
|
|
298
|
+
res = np.einsum("stp,p->st", tdata[v], weights)
|
|
299
|
+
self._set_res(fdata, v, res, downwind_index)
|
|
307
300
|
if copy_to_ambient and v in FV.var2amb:
|
|
308
301
|
fdata[FV.var2amb[v]] = fdata[v].copy()
|
|
309
302
|
|
|
@@ -317,7 +310,7 @@ class RotorModel(FarmDataModel):
|
|
|
317
310
|
store_rpoints=False,
|
|
318
311
|
store_rweights=False,
|
|
319
312
|
store_amb_res=False,
|
|
320
|
-
|
|
313
|
+
downwind_index=None,
|
|
321
314
|
):
|
|
322
315
|
"""
|
|
323
316
|
Calculate ambient rotor effective results.
|
|
@@ -326,9 +319,9 @@ class RotorModel(FarmDataModel):
|
|
|
326
319
|
----------
|
|
327
320
|
algo: foxes.core.Algorithm
|
|
328
321
|
The calculation algorithm
|
|
329
|
-
mdata: foxes.core.
|
|
322
|
+
mdata: foxes.core.MData
|
|
330
323
|
The model data
|
|
331
|
-
fdata: foxes.core.
|
|
324
|
+
fdata: foxes.core.FData
|
|
332
325
|
The farm data
|
|
333
326
|
rpoints: numpy.ndarray, optional
|
|
334
327
|
The rotor points, or None for automatic for
|
|
@@ -343,8 +336,8 @@ class RotorModel(FarmDataModel):
|
|
|
343
336
|
store_amb_res: bool, optional
|
|
344
337
|
Switch for storing ambient rotor point reults as they
|
|
345
338
|
come from the states to mdata
|
|
346
|
-
|
|
347
|
-
|
|
339
|
+
downwind_index: int, optional
|
|
340
|
+
Only compute for index in the downwind order
|
|
348
341
|
|
|
349
342
|
Returns
|
|
350
343
|
-------
|
|
@@ -355,56 +348,45 @@ class RotorModel(FarmDataModel):
|
|
|
355
348
|
"""
|
|
356
349
|
|
|
357
350
|
if rpoints is None:
|
|
358
|
-
rpoints = mdata.get(
|
|
351
|
+
rpoints = mdata.get(self.RPOINTS, self.get_rotor_points(algo, mdata, fdata))
|
|
359
352
|
if store_rpoints:
|
|
360
|
-
mdata[
|
|
361
|
-
mdata.dims[
|
|
362
|
-
self.data_to_store(
|
|
353
|
+
mdata[self.RPOINTS] = rpoints
|
|
354
|
+
mdata.dims[self.RPOINTS] = (FC.STATE, FC.TURBINE, FC.TPOINT, FC.XYH)
|
|
355
|
+
self.data_to_store(self.RPOINTS, algo, mdata)
|
|
363
356
|
|
|
364
|
-
if
|
|
365
|
-
|
|
366
|
-
stsel = (np.arange(n_states), states_turbine)
|
|
367
|
-
rpoints = rpoints[stsel][:, None]
|
|
368
|
-
n_states, n_turbines, n_rpoints, __ = rpoints.shape
|
|
369
|
-
n_points = n_turbines * n_rpoints
|
|
357
|
+
if downwind_index is not None:
|
|
358
|
+
rpoints = rpoints[:, downwind_index, None]
|
|
370
359
|
|
|
371
360
|
if weights is None:
|
|
372
|
-
weights = mdata.get(FC.
|
|
361
|
+
weights = mdata.get(FC.TWEIGHTS, self.rotor_point_weights())
|
|
373
362
|
if store_rweights:
|
|
374
|
-
mdata[
|
|
375
|
-
mdata.dims[
|
|
376
|
-
self.data_to_store(
|
|
363
|
+
mdata[self.RWEIGHTS] = weights
|
|
364
|
+
mdata.dims[self.RWEIGHTS] = (FC.TPOINT,)
|
|
365
|
+
self.data_to_store(self.RWEIGHTS, algo, mdata)
|
|
377
366
|
|
|
367
|
+
tdata = TData.from_tpoints(rpoints, weights)
|
|
378
368
|
svars = algo.states.output_point_vars(algo)
|
|
379
|
-
points = rpoints.reshape(n_states, n_points, 3)
|
|
380
|
-
pdata = {FC.POINTS: points}
|
|
381
|
-
pdims = {FC.POINTS: (FC.STATE, FC.POINT, FC.XYH)}
|
|
382
|
-
pdata.update(
|
|
383
|
-
{v: np.full((n_states, n_points), np.nan, dtype=FC.DTYPE) for v in svars}
|
|
384
|
-
)
|
|
385
|
-
pdims.update({v: (FC.STATE, FC.POINT) for v in svars})
|
|
386
|
-
pdata = Data(pdata, pdims, loop_dims=[FC.STATE, FC.POINT])
|
|
387
|
-
del pdims, points
|
|
388
|
-
|
|
389
|
-
sres = algo.states.calculate(algo, mdata, fdata, pdata)
|
|
390
|
-
pdata.update(sres)
|
|
391
|
-
del sres
|
|
392
|
-
|
|
393
|
-
rpoint_results = {}
|
|
394
369
|
for v in svars:
|
|
395
|
-
|
|
370
|
+
tdata.add(
|
|
371
|
+
v,
|
|
372
|
+
data=np.full_like(rpoints[..., 0], np.nan),
|
|
373
|
+
dims=(FC.STATE, FC.TARGET, FC.TPOINT),
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
sres = algo.states.calculate(algo, mdata, fdata, tdata)
|
|
377
|
+
tdata.update(sres)
|
|
396
378
|
|
|
397
379
|
if store_amb_res:
|
|
398
|
-
mdata[
|
|
399
|
-
self.data_to_store(
|
|
380
|
+
mdata[self.AMBRES] = sres.copy()
|
|
381
|
+
self.data_to_store(self.AMBRES, algo, mdata)
|
|
400
382
|
|
|
401
383
|
self.eval_rpoint_results(
|
|
402
384
|
algo,
|
|
403
385
|
mdata,
|
|
404
386
|
fdata,
|
|
405
|
-
|
|
387
|
+
tdata,
|
|
406
388
|
weights,
|
|
407
|
-
|
|
389
|
+
downwind_index,
|
|
408
390
|
copy_to_ambient=True,
|
|
409
391
|
)
|
|
410
392
|
|
foxes/core/states.py
CHANGED
|
@@ -245,7 +245,7 @@ class ExtendedStates(States):
|
|
|
245
245
|
"""
|
|
246
246
|
return self.states.output_point_vars(algo)
|
|
247
247
|
|
|
248
|
-
def calculate(self, algo, mdata, fdata,
|
|
248
|
+
def calculate(self, algo, mdata, fdata, tdata):
|
|
249
249
|
""" "
|
|
250
250
|
The main model calculation.
|
|
251
251
|
|
|
@@ -256,12 +256,12 @@ class ExtendedStates(States):
|
|
|
256
256
|
----------
|
|
257
257
|
algo: foxes.core.Algorithm
|
|
258
258
|
The calculation algorithm
|
|
259
|
-
mdata: foxes.core.
|
|
259
|
+
mdata: foxes.core.MData
|
|
260
260
|
The model data
|
|
261
|
-
fdata: foxes.core.
|
|
261
|
+
fdata: foxes.core.FData
|
|
262
262
|
The farm data
|
|
263
|
-
|
|
264
|
-
The point data
|
|
263
|
+
tdata: foxes.core.TData
|
|
264
|
+
The target point data
|
|
265
265
|
|
|
266
266
|
Returns
|
|
267
267
|
-------
|
|
@@ -270,7 +270,7 @@ class ExtendedStates(States):
|
|
|
270
270
|
Values: numpy.ndarray with shape (n_states, n_points)
|
|
271
271
|
|
|
272
272
|
"""
|
|
273
|
-
return self.pmodels.calculate(algo, mdata, fdata,
|
|
273
|
+
return self.pmodels.calculate(algo, mdata, fdata, tdata)
|
|
274
274
|
|
|
275
275
|
def __add__(self, m):
|
|
276
276
|
models = self.pmodels.models[1:]
|