foxes 1.3__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.

Files changed (190) hide show
  1. docs/source/conf.py +3 -3
  2. examples/abl_states/run.py +2 -2
  3. examples/compare_rotors_pwakes/run.py +1 -1
  4. examples/compare_wakes/run.py +1 -2
  5. examples/dyn_wakes/run.py +29 -6
  6. examples/induction/run.py +3 -3
  7. examples/multi_height/run.py +1 -1
  8. examples/power_mask/run.py +2 -2
  9. examples/quickstart/run.py +0 -1
  10. examples/random_timeseries/run.py +3 -4
  11. examples/scan_row/run.py +3 -3
  12. examples/sequential/run.py +33 -10
  13. examples/single_state/run.py +3 -4
  14. examples/states_lookup_table/run.py +3 -3
  15. examples/streamline_wakes/run.py +27 -4
  16. examples/tab_file/run.py +3 -3
  17. examples/timelines/run.py +29 -5
  18. examples/timeseries/run.py +3 -3
  19. examples/timeseries_slurm/run.py +3 -3
  20. examples/wind_rose/run.py +3 -3
  21. examples/yawed_wake/run.py +16 -8
  22. foxes/__init__.py +21 -17
  23. foxes/algorithms/__init__.py +6 -6
  24. foxes/algorithms/downwind/__init__.py +2 -2
  25. foxes/algorithms/downwind/downwind.py +44 -12
  26. foxes/algorithms/downwind/models/__init__.py +6 -6
  27. foxes/algorithms/downwind/models/farm_wakes_calc.py +11 -9
  28. foxes/algorithms/downwind/models/init_farm_data.py +0 -1
  29. foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
  30. foxes/algorithms/downwind/models/set_amb_point_results.py +6 -6
  31. foxes/algorithms/iterative/__init__.py +7 -3
  32. foxes/algorithms/iterative/iterative.py +1 -2
  33. foxes/algorithms/iterative/models/__init__.py +7 -3
  34. foxes/algorithms/iterative/models/farm_wakes_calc.py +9 -5
  35. foxes/algorithms/sequential/__init__.py +3 -3
  36. foxes/algorithms/sequential/models/__init__.py +2 -2
  37. foxes/algorithms/sequential/sequential.py +3 -4
  38. foxes/config/__init__.py +5 -1
  39. foxes/constants.py +16 -0
  40. foxes/core/__init__.py +45 -22
  41. foxes/core/algorithm.py +0 -1
  42. foxes/core/data.py +19 -18
  43. foxes/core/engine.py +9 -13
  44. foxes/core/farm_controller.py +2 -2
  45. foxes/core/ground_model.py +4 -13
  46. foxes/core/model.py +5 -5
  47. foxes/core/partial_wakes_model.py +147 -10
  48. foxes/core/point_data_model.py +2 -3
  49. foxes/core/rotor_model.py +3 -3
  50. foxes/core/states.py +2 -3
  51. foxes/core/turbine.py +2 -1
  52. foxes/core/wake_deflection.py +130 -0
  53. foxes/core/wake_model.py +222 -9
  54. foxes/core/wake_superposition.py +122 -4
  55. foxes/core/wind_farm.py +6 -6
  56. foxes/data/__init__.py +7 -2
  57. foxes/data/states/weibull_sectors_12.csv +13 -0
  58. foxes/data/states/weibull_sectors_12.nc +0 -0
  59. foxes/engines/__init__.py +14 -15
  60. foxes/engines/dask.py +39 -14
  61. foxes/engines/numpy.py +0 -3
  62. foxes/input/__init__.py +3 -3
  63. foxes/input/farm_layout/__init__.py +8 -8
  64. foxes/input/farm_layout/from_csv.py +1 -1
  65. foxes/input/farm_layout/ring.py +0 -1
  66. foxes/input/states/__init__.py +22 -12
  67. foxes/input/states/create/__init__.py +3 -2
  68. foxes/input/states/field_data_nc.py +10 -24
  69. foxes/input/states/multi_height.py +9 -6
  70. foxes/input/states/one_point_flow.py +0 -4
  71. foxes/input/states/single.py +1 -1
  72. foxes/input/states/states_table.py +10 -7
  73. foxes/input/states/weibull_sectors.py +225 -0
  74. foxes/input/states/wrg_states.py +7 -5
  75. foxes/input/yaml/__init__.py +9 -3
  76. foxes/input/yaml/dict.py +19 -19
  77. foxes/input/yaml/windio/__init__.py +10 -5
  78. foxes/input/yaml/windio/read_attributes.py +2 -2
  79. foxes/input/yaml/windio/read_farm.py +5 -5
  80. foxes/input/yaml/windio/read_fields.py +4 -2
  81. foxes/input/yaml/windio/read_site.py +52 -0
  82. foxes/input/yaml/windio/windio.py +1 -1
  83. foxes/models/__init__.py +15 -14
  84. foxes/models/axial_induction/__init__.py +2 -2
  85. foxes/models/farm_controllers/__init__.py +1 -1
  86. foxes/models/farm_models/__init__.py +1 -1
  87. foxes/models/ground_models/__init__.py +3 -2
  88. foxes/models/ground_models/wake_mirror.py +3 -3
  89. foxes/models/model_book.py +175 -49
  90. foxes/models/partial_wakes/__init__.py +6 -6
  91. foxes/models/partial_wakes/axiwake.py +30 -5
  92. foxes/models/partial_wakes/centre.py +47 -0
  93. foxes/models/partial_wakes/rotor_points.py +41 -11
  94. foxes/models/partial_wakes/segregated.py +2 -25
  95. foxes/models/partial_wakes/top_hat.py +27 -2
  96. foxes/models/point_models/__init__.py +4 -4
  97. foxes/models/rotor_models/__init__.py +3 -3
  98. foxes/models/turbine_models/__init__.py +11 -11
  99. foxes/models/turbine_models/set_farm_vars.py +0 -1
  100. foxes/models/turbine_types/PCt_file.py +0 -2
  101. foxes/models/turbine_types/PCt_from_two.py +0 -2
  102. foxes/models/turbine_types/__init__.py +9 -9
  103. foxes/models/vertical_profiles/__init__.py +7 -7
  104. foxes/models/wake_deflections/__init__.py +3 -0
  105. foxes/models/{wake_frames/yawed_wakes.py → wake_deflections/bastankhah2016.py} +32 -111
  106. foxes/models/wake_deflections/jimenez.py +277 -0
  107. foxes/models/wake_deflections/no_deflection.py +94 -0
  108. foxes/models/wake_frames/__init__.py +6 -7
  109. foxes/models/wake_frames/dynamic_wakes.py +12 -3
  110. foxes/models/wake_frames/rotor_wd.py +3 -1
  111. foxes/models/wake_frames/seq_dynamic_wakes.py +41 -7
  112. foxes/models/wake_frames/streamlines.py +8 -6
  113. foxes/models/wake_frames/timelines.py +9 -3
  114. foxes/models/wake_models/__init__.py +7 -7
  115. foxes/models/wake_models/dist_sliced.py +50 -84
  116. foxes/models/wake_models/gaussian.py +20 -0
  117. foxes/models/wake_models/induction/__init__.py +5 -5
  118. foxes/models/wake_models/induction/rankine_half_body.py +30 -71
  119. foxes/models/wake_models/induction/rathmann.py +65 -64
  120. foxes/models/wake_models/induction/self_similar.py +65 -68
  121. foxes/models/wake_models/induction/self_similar2020.py +0 -3
  122. foxes/models/wake_models/induction/vortex_sheet.py +71 -75
  123. foxes/models/wake_models/ti/__init__.py +2 -2
  124. foxes/models/wake_models/ti/crespo_hernandez.py +5 -3
  125. foxes/models/wake_models/ti/iec_ti.py +6 -4
  126. foxes/models/wake_models/top_hat.py +58 -7
  127. foxes/models/wake_models/wind/__init__.py +6 -4
  128. foxes/models/wake_models/wind/bastankhah14.py +25 -7
  129. foxes/models/wake_models/wind/bastankhah16.py +35 -3
  130. foxes/models/wake_models/wind/jensen.py +15 -2
  131. foxes/models/wake_models/wind/turbopark.py +28 -2
  132. foxes/models/wake_superpositions/__init__.py +18 -9
  133. foxes/models/wake_superpositions/ti_linear.py +4 -4
  134. foxes/models/wake_superpositions/ti_max.py +4 -4
  135. foxes/models/wake_superpositions/ti_pow.py +4 -4
  136. foxes/models/wake_superpositions/ti_quadratic.py +4 -4
  137. foxes/models/wake_superpositions/wind_vector.py +257 -0
  138. foxes/models/wake_superpositions/ws_linear.py +9 -10
  139. foxes/models/wake_superpositions/ws_max.py +8 -8
  140. foxes/models/wake_superpositions/ws_pow.py +8 -8
  141. foxes/models/wake_superpositions/ws_product.py +4 -4
  142. foxes/models/wake_superpositions/ws_quadratic.py +8 -8
  143. foxes/output/__init__.py +21 -19
  144. foxes/output/farm_layout.py +2 -2
  145. foxes/output/farm_results_eval.py +15 -15
  146. foxes/output/flow_plots_2d/__init__.py +2 -2
  147. foxes/output/flow_plots_2d/get_fig.py +4 -2
  148. foxes/output/rose_plot.py +3 -3
  149. foxes/output/seq_plugins/__init__.py +2 -2
  150. foxes/output/seq_plugins/seq_flow_ani_plugin.py +0 -3
  151. foxes/output/seq_plugins/seq_wake_debug_plugin.py +0 -1
  152. foxes/output/turbine_type_curves.py +7 -8
  153. foxes/utils/__init__.py +37 -19
  154. foxes/utils/abl/__init__.py +4 -4
  155. foxes/utils/cubic_roots.py +1 -1
  156. foxes/utils/data_book.py +4 -3
  157. foxes/utils/dict.py +3 -3
  158. foxes/utils/exec_python.py +5 -5
  159. foxes/utils/factory.py +1 -3
  160. foxes/utils/geom2d/__init__.py +7 -5
  161. foxes/utils/geopandas_utils.py +2 -2
  162. foxes/utils/pandas_utils.py +4 -3
  163. foxes/utils/tab_files.py +0 -1
  164. foxes/utils/weibull.py +28 -0
  165. foxes/utils/wrg_utils.py +3 -1
  166. foxes/utils/xarray_utils.py +9 -2
  167. foxes/variables.py +67 -9
  168. {foxes-1.3.dist-info → foxes-1.4.dist-info}/METADATA +6 -15
  169. foxes-1.4.dist-info/RECORD +320 -0
  170. {foxes-1.3.dist-info → foxes-1.4.dist-info}/WHEEL +1 -1
  171. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +2 -3
  172. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +1 -1
  173. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
  174. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +0 -1
  175. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +0 -2
  176. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +0 -1
  177. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +0 -1
  178. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +0 -1
  179. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +0 -1
  180. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +0 -1
  181. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
  182. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +0 -1
  183. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +0 -1
  184. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +0 -1
  185. foxes/output/round.py +0 -10
  186. foxes/utils/pandas_helpers.py +0 -178
  187. foxes-1.3.dist-info/RECORD +0 -313
  188. {foxes-1.3.dist-info → foxes-1.4.dist-info}/entry_points.txt +0 -0
  189. {foxes-1.3.dist-info → foxes-1.4.dist-info/licenses}/LICENSE +0 -0
  190. {foxes-1.3.dist-info → foxes-1.4.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  import numpy as np
2
2
 
3
+ from foxes.config import config
3
4
  from foxes.core import TurbineInductionModel
4
5
  import foxes.variables as FV
5
6
  import foxes.constants as FC
@@ -10,9 +11,6 @@ class SelfSimilar(TurbineInductionModel):
10
11
  The self-similar induction wake model
11
12
  from Troldborg and Meyer Forsting
12
13
 
13
- The individual wake effects are superposed linearly,
14
- without invoking a wake superposition model.
15
-
16
14
  Notes
17
15
  -----
18
16
  References:
@@ -60,17 +58,29 @@ class SelfSimilar(TurbineInductionModel):
60
58
  Calculate only the pre-rotor region
61
59
 
62
60
  """
63
- super().__init__()
61
+ super().__init__(wind_superposition=superposition)
64
62
  self.induction = induction
65
63
  self.pre_rotor_only = pre_rotor_only
66
64
  self.gamma = gamma
67
- self._superp_name = superposition
68
65
 
69
66
  def __repr__(self):
70
67
  iname = (
71
68
  self.induction if isinstance(self.induction, str) else self.induction.name
72
69
  )
73
- return f"{type(self).__name__}({self._superp_name}, induction={iname}, gamma={self.gamma})"
70
+ return f"{type(self).__name__}({self.wind_superposition}, induction={iname}, gamma={self.gamma})"
71
+
72
+ @property
73
+ def affects_ws(self):
74
+ """
75
+ Flag for wind speed wake models
76
+
77
+ Returns
78
+ -------
79
+ dws: bool
80
+ If True, this model affects wind speed
81
+
82
+ """
83
+ return True
74
84
 
75
85
  def sub_models(self):
76
86
  """
@@ -82,7 +92,7 @@ class SelfSimilar(TurbineInductionModel):
82
92
  All sub models
83
93
 
84
94
  """
85
- return [self._superp, self.induction]
95
+ return super().sub_models() + [self.induction]
86
96
 
87
97
  def initialize(self, algo, verbosity=0, force=False):
88
98
  """
@@ -98,7 +108,6 @@ class SelfSimilar(TurbineInductionModel):
98
108
  Overwrite existing data
99
109
 
100
110
  """
101
- self._superp = algo.mbook.wake_superpositions[self._superp_name]
102
111
  if isinstance(self.induction, str):
103
112
  self.induction = algo.mbook.axial_induction[self.induction]
104
113
  super().initialize(algo, verbosity, force)
@@ -122,10 +131,21 @@ class SelfSimilar(TurbineInductionModel):
122
131
  -------
123
132
  wake_deltas: dict
124
133
  Key: variable name, value: The zero filled
125
- wake deltas, shape: (n_states, n_turbines, n_rpoints, ...)
134
+ wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
126
135
 
127
136
  """
128
- return {FV.WS: np.zeros_like(tdata[FC.TARGETS][..., 0])}
137
+ if self.has_uv:
138
+ duv = np.zeros(
139
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
140
+ dtype=config.dtype_double,
141
+ )
142
+ return {FV.UV: duv}
143
+ else:
144
+ dws = np.zeros(
145
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
146
+ dtype=config.dtype_double,
147
+ )
148
+ return {FV.WS: dws}
129
149
 
130
150
  def _mu(self, x_R):
131
151
  """Helper function: define mu (eqn 11 from [1])"""
@@ -211,6 +231,39 @@ class SelfSimilar(TurbineInductionModel):
211
231
  x_R = np.round(wake_coos[..., 0] / R, 12)
212
232
  r_R = np.linalg.norm(wake_coos[..., 1:3], axis=-1) / R
213
233
 
234
+ def add_wake(sp_sel, wake_deltas, blockage):
235
+ """adds to wake deltas"""
236
+ if self.has_uv:
237
+ assert self.has_vector_wind_superp, (
238
+ f"Wake model {self.name}: Missing vector wind superposition, got '{self.wind_superposition}'"
239
+ )
240
+ wdeltas = {FV.WS: blockage}
241
+ self.vec_superp.wdeltas_ws2uv(
242
+ algo, fdata, tdata, downwind_index, wdeltas, sp_sel
243
+ )
244
+ wake_deltas[FV.UV] = self.vec_superp.add_wake_vector(
245
+ algo,
246
+ mdata,
247
+ fdata,
248
+ tdata,
249
+ downwind_index,
250
+ sp_sel,
251
+ wake_deltas[FV.UV],
252
+ wdeltas.pop(FV.UV),
253
+ )
254
+ else:
255
+ self.superp[FV.WS].add_wake(
256
+ algo,
257
+ mdata,
258
+ fdata,
259
+ tdata,
260
+ downwind_index,
261
+ sp_sel,
262
+ FV.WS,
263
+ wake_deltas[FV.WS],
264
+ blockage,
265
+ )
266
+
214
267
  # select values
215
268
  sp_sel = (ct > 1e-8) & (x_R <= 0) # upstream
216
269
  if np.any(sp_sel):
@@ -218,17 +271,7 @@ class SelfSimilar(TurbineInductionModel):
218
271
  xr = x_R[sp_sel]
219
272
  blockage = self._a(ct[sp_sel], xr) * self._rad_fn(xr, r_R[sp_sel])
220
273
 
221
- self._superp.add_wake(
222
- algo,
223
- mdata,
224
- fdata,
225
- tdata,
226
- downwind_index,
227
- sp_sel,
228
- FV.WS,
229
- wake_deltas[FV.WS],
230
- -blockage,
231
- )
274
+ add_wake(sp_sel, wake_deltas, -blockage)
232
275
 
233
276
  # set area behind to mirrored value EXCEPT for area behind turbine
234
277
  if not self.pre_rotor_only:
@@ -238,52 +281,6 @@ class SelfSimilar(TurbineInductionModel):
238
281
  xr = x_R[sp_sel]
239
282
  blockage = self._a(ct[sp_sel], -xr) * self._rad_fn(-xr, r_R[sp_sel])
240
283
 
241
- # wdelta[sp_sel] += blockage
242
- self._superp.add_wake(
243
- algo,
244
- mdata,
245
- fdata,
246
- tdata,
247
- downwind_index,
248
- sp_sel,
249
- FV.WS,
250
- wake_deltas[FV.WS],
251
- blockage,
252
- )
284
+ add_wake(sp_sel, wake_deltas, blockage)
253
285
 
254
286
  return wake_deltas
255
-
256
- def finalize_wake_deltas(
257
- self,
258
- algo,
259
- mdata,
260
- fdata,
261
- amb_results,
262
- wake_deltas,
263
- ):
264
- """
265
- Finalize the wake calculation.
266
-
267
- Modifies wake_deltas on the fly.
268
-
269
- Parameters
270
- ----------
271
- algo: foxes.core.Algorithm
272
- The calculation algorithm
273
- mdata: foxes.core.MData
274
- The model data
275
- fdata: foxes.core.FData
276
- The farm data
277
- amb_results: dict
278
- The ambient results, key: variable name str,
279
- values: numpy.ndarray with shape
280
- (n_states, n_targets, n_tpoints)
281
- wake_deltas: dict
282
- The wake deltas object at the selected target
283
- turbines. Key: variable str, value: numpy.ndarray
284
- with shape (n_states, n_targets, n_tpoints)
285
-
286
- """
287
- wake_deltas[FV.WS] = self._superp.calc_final_wake_delta(
288
- algo, mdata, fdata, FV.WS, amb_results[FV.WS], wake_deltas[FV.WS]
289
- )
@@ -8,9 +8,6 @@ class SelfSimilar2020(SelfSimilar):
8
8
  The self-similar 2020 induction wake model
9
9
  from Troldborg and Meyer Forsting
10
10
 
11
- The individual wake effects are superposed linearly,
12
- without invoking a wake superposition model.
13
-
14
11
  Notes
15
12
  -----
16
13
  References:
@@ -1,5 +1,6 @@
1
1
  import numpy as np
2
2
 
3
+ from foxes.config import config
3
4
  from foxes.core import TurbineInductionModel
4
5
  import foxes.variables as FV
5
6
  import foxes.constants as FC
@@ -45,16 +46,28 @@ class VortexSheet(TurbineInductionModel):
45
46
  Calculate only the pre-rotor region
46
47
 
47
48
  """
48
- super().__init__()
49
+ super().__init__(wind_superposition=superposition)
49
50
  self.induction = induction
50
51
  self.pre_rotor_only = pre_rotor_only
51
- self._superp_name = superposition
52
52
 
53
53
  def __repr__(self):
54
54
  iname = (
55
55
  self.induction if isinstance(self.induction, str) else self.induction.name
56
56
  )
57
- return f"{type(self).__name__}({self._superp_name}, induction={iname})"
57
+ return f"{type(self).__name__}({self.wind_superposition}, induction={iname})"
58
+
59
+ @property
60
+ def affects_ws(self):
61
+ """
62
+ Flag for wind speed wake models
63
+
64
+ Returns
65
+ -------
66
+ dws: bool
67
+ If True, this model affects wind speed
68
+
69
+ """
70
+ return True
58
71
 
59
72
  def sub_models(self):
60
73
  """
@@ -66,7 +79,7 @@ class VortexSheet(TurbineInductionModel):
66
79
  All sub models
67
80
 
68
81
  """
69
- return [self._superp, self.induction]
82
+ return super().sub_models() + [self.induction]
70
83
 
71
84
  def initialize(self, algo, verbosity=0, force=False):
72
85
  """
@@ -82,35 +95,44 @@ class VortexSheet(TurbineInductionModel):
82
95
  Overwrite existing data
83
96
 
84
97
  """
85
- self._superp = algo.mbook.wake_superpositions[self._superp_name]
86
98
  if isinstance(self.induction, str):
87
99
  self.induction = algo.mbook.axial_induction[self.induction]
88
100
  super().initialize(algo, verbosity, force)
89
101
 
90
102
  def new_wake_deltas(self, algo, mdata, fdata, tdata):
91
103
  """
92
- Initialize wake delta storage.
93
-
94
- They are added on the fly to the wake_deltas dict.
104
+ Creates new empty wake delta arrays.
95
105
 
96
106
  Parameters
97
107
  ----------
98
108
  algo: foxes.core.Algorithm
99
109
  The calculation algorithm
100
- mdata: foxes.core.Data
110
+ mdata: foxes.core.MData
101
111
  The model data
102
- fdata: foxes.core.Data
112
+ fdata: foxes.core.FData
103
113
  The farm data
104
- pdata: foxes.core.Data
105
- The evaluation point data
114
+ tdata: foxes.core.TData
115
+ The target point data
116
+
117
+ Returns
118
+ -------
106
119
  wake_deltas: dict
107
- The wake deltas storage, add wake deltas
108
- on the fly. Keys: Variable name str, for which the
109
- wake delta applies, values: numpy.ndarray with
110
- shape (n_states, n_points, ...)
120
+ Key: variable name, value: The zero filled
121
+ wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
111
122
 
112
123
  """
113
- return {FV.WS: np.zeros_like(tdata[FC.TARGETS][..., 0])}
124
+ if self.has_uv:
125
+ duv = np.zeros(
126
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
127
+ dtype=config.dtype_double,
128
+ )
129
+ return {FV.UV: duv}
130
+ else:
131
+ dws = np.zeros(
132
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
133
+ dtype=config.dtype_double,
134
+ )
135
+ return {FV.WS: dws}
114
136
 
115
137
  def contribute(
116
138
  self,
@@ -186,20 +208,39 @@ class VortexSheet(TurbineInductionModel):
186
208
  R_sel = D[sp_sel] / 2
187
209
  xi = r_sph_sel / R_sel
188
210
 
211
+ def add_wake(sp_sel, wake_deltas, blockage):
212
+ """adds to wake deltas"""
213
+ if self.has_vector_wind_superp:
214
+ wdeltas = {FV.WS: blockage}
215
+ self.vec_superp.wdeltas_ws2uv(
216
+ algo, fdata, tdata, downwind_index, wdeltas, sp_sel
217
+ )
218
+ wake_deltas[FV.UV] = self.vec_superp.add_wake_vector(
219
+ algo,
220
+ mdata,
221
+ fdata,
222
+ tdata,
223
+ downwind_index,
224
+ sp_sel,
225
+ wake_deltas[FV.UV],
226
+ wdeltas.pop(FV.UV),
227
+ )
228
+ else:
229
+ self.superp[FV.WS].add_wake(
230
+ algo,
231
+ mdata,
232
+ fdata,
233
+ tdata,
234
+ downwind_index,
235
+ sp_sel,
236
+ FV.WS,
237
+ wake_deltas[FV.WS],
238
+ blockage,
239
+ )
240
+
189
241
  if np.any(sp_sel):
190
242
  blockage = self.induction.ct2a(ct_sel) * (1 + -xi / np.sqrt(1 + xi**2))
191
-
192
- self._superp.add_wake(
193
- algo,
194
- mdata,
195
- fdata,
196
- tdata,
197
- downwind_index,
198
- sp_sel,
199
- FV.WS,
200
- wake_deltas[FV.WS],
201
- -blockage,
202
- )
243
+ add_wake(sp_sel, wake_deltas, -blockage)
203
244
 
204
245
  if not self.pre_rotor_only:
205
246
  sp_sel = (
@@ -211,51 +252,6 @@ class VortexSheet(TurbineInductionModel):
211
252
  xi = r_sph_sel / R_sel
212
253
  if np.any(sp_sel):
213
254
  blockage = self.induction.ct2a(ct_sel) * (1 + -xi / np.sqrt(1 + xi**2))
214
- self._superp.add_wake(
215
- algo,
216
- mdata,
217
- fdata,
218
- tdata,
219
- downwind_index,
220
- sp_sel,
221
- FV.WS,
222
- wake_deltas[FV.WS],
223
- blockage,
224
- )
255
+ add_wake(sp_sel, wake_deltas, blockage)
225
256
 
226
257
  return wake_deltas
227
-
228
- def finalize_wake_deltas(
229
- self,
230
- algo,
231
- mdata,
232
- fdata,
233
- amb_results,
234
- wake_deltas,
235
- ):
236
- """
237
- Finalize the wake calculation.
238
-
239
- Modifies wake_deltas on the fly.
240
-
241
- Parameters
242
- ----------
243
- algo: foxes.core.Algorithm
244
- The calculation algorithm
245
- mdata: foxes.core.MData
246
- The model data
247
- fdata: foxes.core.FData
248
- The farm data
249
- amb_results: dict
250
- The ambient results, key: variable name str,
251
- values: numpy.ndarray with shape
252
- (n_states, n_targets, n_tpoints)
253
- wake_deltas: dict
254
- The wake deltas object at the selected target
255
- turbines. Key: variable str, value: numpy.ndarray
256
- with shape (n_states, n_targets, n_tpoints)
257
-
258
- """
259
- wake_deltas[FV.WS] = self._superp.calc_final_wake_delta(
260
- algo, mdata, fdata, FV.WS, amb_results[FV.WS], wake_deltas[FV.WS]
261
- )
@@ -2,5 +2,5 @@
2
2
  Turbulence intensity wake models.
3
3
  """
4
4
 
5
- from .crespo_hernandez import CrespoHernandezTIWake
6
- from .iec_ti import IECTIWake
5
+ from .crespo_hernandez import CrespoHernandezTIWake as CrespoHernandezTIWake
6
+ from .iec_ti import IECTIWake as IECTIWake
@@ -98,7 +98,9 @@ class CrespoHernandezTIWake(TopHatWakeModel):
98
98
  Parameters for the WakeK class
99
99
 
100
100
  """
101
- super().__init__(superpositions={FV.TI: superposition}, induction=induction)
101
+ super().__init__(
102
+ other_superpositions={FV.TI: superposition}, induction=induction
103
+ )
102
104
 
103
105
  self.a_near = a_near
104
106
  self.a_far = a_far
@@ -115,7 +117,7 @@ class CrespoHernandezTIWake(TopHatWakeModel):
115
117
  self.induction if isinstance(self.induction, str) else self.induction.name
116
118
  )
117
119
  s = f"{type(self).__name__}"
118
- s += f"({self.superpositions[FV.TI]}, induction={iname}, "
120
+ s += f"({self.other_superpositions[FV.TI]}, induction={iname}, "
119
121
  s += self.wake_k.repr() + ")"
120
122
  return s
121
123
 
@@ -129,7 +131,7 @@ class CrespoHernandezTIWake(TopHatWakeModel):
129
131
  All sub models
130
132
 
131
133
  """
132
- return [self.wake_k]
134
+ return super().sub_models() + [self.wake_k]
133
135
 
134
136
  def new_wake_deltas(self, algo, mdata, fdata, tdata):
135
137
  """
@@ -53,7 +53,9 @@ class IECTIWake(TopHatWakeModel):
53
53
  Parameters for the WakeK class
54
54
 
55
55
  """
56
- super().__init__(superpositions={FV.TI: superposition}, induction=induction)
56
+ super().__init__(
57
+ other_superpositions={FV.TI: superposition}, induction=induction
58
+ )
57
59
  self.iec_type = iec_type
58
60
  self.wake_k = None
59
61
 
@@ -62,7 +64,7 @@ class IECTIWake(TopHatWakeModel):
62
64
  else:
63
65
  if "k" in wake_k or "ka" in wake_k or "kb" in wake_k:
64
66
  raise KeyError(
65
- f"Can handle 'opening_angle' or ('k', 'ka', 'kb') parameters, not both"
67
+ "Can handle 'opening_angle' or ('k', 'ka', 'kb') parameters, not both"
66
68
  )
67
69
  self._k = float(np.tan(np.deg2rad(opening_angle / 2.0)))
68
70
 
@@ -71,7 +73,7 @@ class IECTIWake(TopHatWakeModel):
71
73
  self.induction if isinstance(self.induction, str) else self.induction.name
72
74
  )
73
75
  s = f"{type(self).__name__}"
74
- s += f"({self.superpositions[FV.TI]}, induction={iname}"
76
+ s += f"({self.other_superpositions[FV.TI]}, induction={iname}"
75
77
  if self.wake_k is not None:
76
78
  s += ", " + self.wake_k.repr()
77
79
  s += ")"
@@ -87,7 +89,7 @@ class IECTIWake(TopHatWakeModel):
87
89
  All sub models
88
90
 
89
91
  """
90
- return [self.wake_k] if self.wake_k is not None else []
92
+ return super().sub_models() + ([self.wake_k] if self.wake_k is not None else [])
91
93
 
92
94
  def new_wake_deltas(self, algo, mdata, fdata, tdata):
93
95
  """
@@ -2,6 +2,7 @@ import numpy as np
2
2
  from abc import abstractmethod
3
3
 
4
4
  from foxes.models.wake_models.axisymmetric import AxisymmetricWakeModel
5
+ from foxes.config import config
5
6
  import foxes.variables as FV
6
7
  import foxes.constants as FC
7
8
 
@@ -19,21 +20,21 @@ class TopHatWakeModel(AxisymmetricWakeModel):
19
20
 
20
21
  """
21
22
 
22
- def __init__(self, superpositions, induction="Betz"):
23
+ def __init__(self, *args, induction="Betz", **kwargs):
23
24
  """
24
25
  Constructor.
25
26
 
26
27
  Parameters
27
28
  ----------
28
- superpositions: dict
29
- The superpositions. Key: variable name str,
30
- value: The wake superposition model name,
31
- will be looked up in model book
29
+ args: tuple, optional
30
+ Additional parameters for the base class
32
31
  induction: foxes.core.AxialInductionModel or str
33
32
  The induction model
33
+ kwargs: dict, optional
34
+ Additional parameters for the base class
34
35
 
35
36
  """
36
- super().__init__(superpositions)
37
+ super().__init__(*args, **kwargs)
37
38
  self.induction = induction
38
39
 
39
40
  def sub_models(self):
@@ -46,7 +47,7 @@ class TopHatWakeModel(AxisymmetricWakeModel):
46
47
  All sub models
47
48
 
48
49
  """
49
- return [self.induction]
50
+ return super().sub_models() + [self.induction]
50
51
 
51
52
  def initialize(self, algo, verbosity=0, force=False):
52
53
  """
@@ -66,6 +67,41 @@ class TopHatWakeModel(AxisymmetricWakeModel):
66
67
  self.induction = algo.mbook.axial_induction[self.induction]
67
68
  super().initialize(algo, verbosity, force)
68
69
 
70
+ def new_wake_deltas(self, algo, mdata, fdata, tdata):
71
+ """
72
+ Creates new empty wake delta arrays.
73
+
74
+ Parameters
75
+ ----------
76
+ algo: foxes.core.Algorithm
77
+ The calculation algorithm
78
+ mdata: foxes.core.MData
79
+ The model data
80
+ fdata: foxes.core.FData
81
+ The farm data
82
+ tdata: foxes.core.TData
83
+ The target point data
84
+
85
+ Returns
86
+ -------
87
+ wake_deltas: dict
88
+ Key: variable name, value: The zero filled
89
+ wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
90
+
91
+ """
92
+ if self.has_uv:
93
+ duv = np.zeros(
94
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
95
+ dtype=config.dtype_double,
96
+ )
97
+ return {FV.UV: duv}
98
+ else:
99
+ dws = np.zeros(
100
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
101
+ dtype=config.dtype_double,
102
+ )
103
+ return {FV.WS: dws}
104
+
69
105
  @abstractmethod
70
106
  def calc_wake_radius(
71
107
  self,
@@ -224,4 +260,19 @@ class TopHatWakeModel(AxisymmetricWakeModel):
224
260
  for v, wdel in cl_del.items():
225
261
  wdeltas[v] = np.where(isin, wdel[:, None], 0.0)
226
262
 
263
+ if self.affects_ws and FV.WS in wdeltas:
264
+ # wake deflection causes wind vector rotation:
265
+ if FC.WDEFL_ROT_ANGLE in tdata:
266
+ dwd_defl = tdata[FC.WDEFL_ROT_ANGLE]
267
+ if FV.WD not in wdeltas:
268
+ wdeltas[FV.WD] = np.zeros_like(wdeltas[FV.WS])
269
+ wdeltas[FV.WD][:] = dwd_defl[st_sel]
270
+ else:
271
+ wdeltas[FV.WD] += dwd_defl[st_sel]
272
+
273
+ # wake deflection causes wind speed reduction:
274
+ if FC.WDEFL_DWS_FACTOR in tdata:
275
+ dws_defl = tdata[FC.WDEFL_DWS_FACTOR]
276
+ wdeltas[FV.WS] *= dws_defl[st_sel]
277
+
227
278
  return wdeltas, st_sel
@@ -2,7 +2,9 @@
2
2
  Wind deficit wake models.
3
3
  """
4
4
 
5
- from .jensen import JensenWake
6
- from .bastankhah14 import Bastankhah2014
7
- from .bastankhah16 import Bastankhah2016Model, Bastankhah2016
8
- from .turbopark import TurbOParkWake, TurbOParkWakeIX
5
+ from .jensen import JensenWake as JensenWake
6
+ from .bastankhah14 import Bastankhah2014 as Bastankhah2014
7
+ from .bastankhah16 import Bastankhah2016Model as Bastankhah2016Model
8
+ from .bastankhah16 import Bastankhah2016 as Bastankhah2016
9
+ from .turbopark import TurbOParkWake as TurbOParkWake
10
+ from .turbopark import TurbOParkWakeIX as TurbOParkWakeIX
@@ -22,7 +22,7 @@ class Bastankhah2014(GaussianWakeModel):
22
22
  ----------
23
23
  sbeta_factor: float
24
24
  Factor multiplying sbeta
25
- induction: foxes.core.AxialInductionModel or str
25
+ induction: foxes.core.AxialInductionModel
26
26
  The induction model
27
27
  wake_k: foxes.core.WakeK
28
28
  Handler for the wake growth parameter k
@@ -31,7 +31,13 @@ class Bastankhah2014(GaussianWakeModel):
31
31
 
32
32
  """
33
33
 
34
- def __init__(self, superposition, sbeta_factor=0.2, induction="Madsen", **wake_k):
34
+ def __init__(
35
+ self,
36
+ superposition,
37
+ sbeta_factor=0.2,
38
+ induction="Madsen",
39
+ **wake_k,
40
+ ):
35
41
  """
36
42
  Constructor.
37
43
 
@@ -47,8 +53,7 @@ class Bastankhah2014(GaussianWakeModel):
47
53
  Parameters for the WakeK class
48
54
 
49
55
  """
50
- super().__init__(superpositions={FV.WS: superposition})
51
-
56
+ super().__init__(wind_superposition=superposition)
52
57
  self.sbeta_factor = sbeta_factor
53
58
  self.induction = induction
54
59
  self.wake_k = WakeK(**wake_k)
@@ -58,10 +63,23 @@ class Bastankhah2014(GaussianWakeModel):
58
63
  self.induction if isinstance(self.induction, str) else self.induction.name
59
64
  )
60
65
  s = f"{type(self).__name__}"
61
- s += f"({self.superpositions[FV.WS]}, induction={iname}, "
66
+ s += f"({self.wind_superposition}, induction={iname}, "
62
67
  s += self.wake_k.repr() + ")"
63
68
  return s
64
69
 
70
+ @property
71
+ def affects_ws(self):
72
+ """
73
+ Flag for wind speed wake models
74
+
75
+ Returns
76
+ -------
77
+ dws: bool
78
+ If True, this model affects wind speed
79
+
80
+ """
81
+ return True
82
+
65
83
  def sub_models(self):
66
84
  """
67
85
  List of all sub-models
@@ -72,7 +90,7 @@ class Bastankhah2014(GaussianWakeModel):
72
90
  All sub models
73
91
 
74
92
  """
75
- return [self.wake_k, self.induction]
93
+ return super().sub_models() + [self.wake_k, self.induction]
76
94
 
77
95
  def initialize(self, algo, verbosity=0, force=False):
78
96
  """
@@ -182,7 +200,7 @@ class Bastankhah2014(GaussianWakeModel):
182
200
 
183
201
  # calculate amplitude:
184
202
  ct_eff = ct / (8 * (sigma / D) ** 2)
185
- ampld = np.maximum(-2 * self.induction.ct2a(ct_eff), -1)
203
+ ampld = np.maximum(-2 * self.induction.ct2a(ct_eff), -0.9999)
186
204
 
187
205
  # case no targets:
188
206
  else: