foxes 1.2.5__py3-none-any.whl → 1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of foxes might be problematic. Click here for more details.

Files changed (201) 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 +16 -0
  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 +22 -18
  23. foxes/algorithms/__init__.py +6 -6
  24. foxes/algorithms/downwind/__init__.py +2 -2
  25. foxes/algorithms/downwind/downwind.py +53 -27
  26. foxes/algorithms/downwind/models/__init__.py +6 -6
  27. foxes/algorithms/downwind/models/farm_wakes_calc.py +22 -14
  28. foxes/algorithms/downwind/models/init_farm_data.py +4 -5
  29. foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
  30. foxes/algorithms/downwind/models/reorder_farm_output.py +5 -1
  31. foxes/algorithms/downwind/models/set_amb_point_results.py +7 -7
  32. foxes/algorithms/iterative/__init__.py +7 -3
  33. foxes/algorithms/iterative/iterative.py +1 -2
  34. foxes/algorithms/iterative/models/__init__.py +7 -3
  35. foxes/algorithms/iterative/models/farm_wakes_calc.py +15 -8
  36. foxes/algorithms/sequential/__init__.py +3 -3
  37. foxes/algorithms/sequential/models/__init__.py +2 -2
  38. foxes/algorithms/sequential/models/seq_state.py +0 -18
  39. foxes/algorithms/sequential/sequential.py +8 -22
  40. foxes/config/__init__.py +5 -1
  41. foxes/constants.py +22 -0
  42. foxes/core/__init__.py +45 -22
  43. foxes/core/algorithm.py +0 -1
  44. foxes/core/data.py +56 -29
  45. foxes/core/engine.py +28 -14
  46. foxes/core/farm_controller.py +2 -2
  47. foxes/core/farm_data_model.py +1 -0
  48. foxes/core/ground_model.py +4 -13
  49. foxes/core/model.py +5 -5
  50. foxes/core/partial_wakes_model.py +147 -10
  51. foxes/core/point_data_model.py +2 -3
  52. foxes/core/rotor_model.py +42 -38
  53. foxes/core/states.py +4 -50
  54. foxes/core/turbine.py +2 -1
  55. foxes/core/wake_deflection.py +130 -0
  56. foxes/core/wake_model.py +222 -9
  57. foxes/core/wake_superposition.py +122 -4
  58. foxes/core/wind_farm.py +6 -6
  59. foxes/data/__init__.py +7 -2
  60. foxes/data/states/weibull_sectors_12.csv +13 -0
  61. foxes/data/states/weibull_sectors_12.nc +0 -0
  62. foxes/engines/__init__.py +14 -15
  63. foxes/engines/dask.py +39 -14
  64. foxes/engines/numpy.py +0 -3
  65. foxes/input/__init__.py +3 -3
  66. foxes/input/farm_layout/__init__.py +8 -8
  67. foxes/input/farm_layout/from_csv.py +1 -1
  68. foxes/input/farm_layout/ring.py +0 -1
  69. foxes/input/states/__init__.py +22 -11
  70. foxes/input/states/create/__init__.py +3 -2
  71. foxes/input/states/field_data_nc.py +48 -84
  72. foxes/input/states/multi_height.py +40 -60
  73. foxes/input/states/one_point_flow.py +22 -25
  74. foxes/input/states/scan.py +6 -19
  75. foxes/input/states/single.py +6 -18
  76. foxes/input/states/states_table.py +25 -44
  77. foxes/input/states/weibull_sectors.py +225 -0
  78. foxes/input/states/wrg_states.py +151 -37
  79. foxes/input/yaml/__init__.py +9 -3
  80. foxes/input/yaml/dict.py +19 -19
  81. foxes/input/yaml/windio/__init__.py +10 -5
  82. foxes/input/yaml/windio/read_attributes.py +2 -2
  83. foxes/input/yaml/windio/read_farm.py +5 -5
  84. foxes/input/yaml/windio/read_fields.py +4 -2
  85. foxes/input/yaml/windio/read_site.py +52 -0
  86. foxes/input/yaml/windio/windio.py +1 -1
  87. foxes/models/__init__.py +15 -14
  88. foxes/models/axial_induction/__init__.py +2 -2
  89. foxes/models/farm_controllers/__init__.py +1 -1
  90. foxes/models/farm_models/__init__.py +1 -1
  91. foxes/models/ground_models/__init__.py +3 -2
  92. foxes/models/ground_models/wake_mirror.py +3 -3
  93. foxes/models/model_book.py +175 -49
  94. foxes/models/partial_wakes/__init__.py +6 -6
  95. foxes/models/partial_wakes/axiwake.py +30 -5
  96. foxes/models/partial_wakes/centre.py +47 -0
  97. foxes/models/partial_wakes/rotor_points.py +45 -9
  98. foxes/models/partial_wakes/segregated.py +2 -20
  99. foxes/models/partial_wakes/top_hat.py +27 -2
  100. foxes/models/point_models/__init__.py +4 -4
  101. foxes/models/rotor_models/__init__.py +3 -3
  102. foxes/models/rotor_models/centre.py +6 -4
  103. foxes/models/turbine_models/__init__.py +11 -11
  104. foxes/models/turbine_models/set_farm_vars.py +0 -1
  105. foxes/models/turbine_types/PCt_file.py +0 -2
  106. foxes/models/turbine_types/PCt_from_two.py +0 -2
  107. foxes/models/turbine_types/__init__.py +9 -9
  108. foxes/models/vertical_profiles/__init__.py +7 -7
  109. foxes/models/wake_deflections/__init__.py +3 -0
  110. foxes/models/{wake_frames/yawed_wakes.py → wake_deflections/bastankhah2016.py} +32 -111
  111. foxes/models/wake_deflections/jimenez.py +277 -0
  112. foxes/models/wake_deflections/no_deflection.py +94 -0
  113. foxes/models/wake_frames/__init__.py +6 -7
  114. foxes/models/wake_frames/dynamic_wakes.py +12 -3
  115. foxes/models/wake_frames/rotor_wd.py +3 -1
  116. foxes/models/wake_frames/seq_dynamic_wakes.py +45 -8
  117. foxes/models/wake_frames/streamlines.py +8 -6
  118. foxes/models/wake_frames/timelines.py +19 -3
  119. foxes/models/wake_models/__init__.py +7 -7
  120. foxes/models/wake_models/dist_sliced.py +50 -84
  121. foxes/models/wake_models/gaussian.py +20 -0
  122. foxes/models/wake_models/induction/__init__.py +5 -5
  123. foxes/models/wake_models/induction/rankine_half_body.py +30 -71
  124. foxes/models/wake_models/induction/rathmann.py +65 -64
  125. foxes/models/wake_models/induction/self_similar.py +65 -68
  126. foxes/models/wake_models/induction/self_similar2020.py +0 -3
  127. foxes/models/wake_models/induction/vortex_sheet.py +71 -75
  128. foxes/models/wake_models/ti/__init__.py +2 -2
  129. foxes/models/wake_models/ti/crespo_hernandez.py +5 -3
  130. foxes/models/wake_models/ti/iec_ti.py +6 -4
  131. foxes/models/wake_models/top_hat.py +58 -7
  132. foxes/models/wake_models/wind/__init__.py +6 -4
  133. foxes/models/wake_models/wind/bastankhah14.py +25 -7
  134. foxes/models/wake_models/wind/bastankhah16.py +35 -3
  135. foxes/models/wake_models/wind/jensen.py +15 -2
  136. foxes/models/wake_models/wind/turbopark.py +28 -2
  137. foxes/models/wake_superpositions/__init__.py +18 -9
  138. foxes/models/wake_superpositions/ti_linear.py +4 -4
  139. foxes/models/wake_superpositions/ti_max.py +4 -4
  140. foxes/models/wake_superpositions/ti_pow.py +4 -4
  141. foxes/models/wake_superpositions/ti_quadratic.py +4 -4
  142. foxes/models/wake_superpositions/wind_vector.py +257 -0
  143. foxes/models/wake_superpositions/ws_linear.py +9 -10
  144. foxes/models/wake_superpositions/ws_max.py +8 -8
  145. foxes/models/wake_superpositions/ws_pow.py +8 -8
  146. foxes/models/wake_superpositions/ws_product.py +4 -4
  147. foxes/models/wake_superpositions/ws_quadratic.py +8 -8
  148. foxes/output/__init__.py +21 -19
  149. foxes/output/farm_layout.py +14 -6
  150. foxes/output/farm_results_eval.py +51 -27
  151. foxes/output/flow_plots_2d/__init__.py +2 -2
  152. foxes/output/flow_plots_2d/get_fig.py +4 -2
  153. foxes/output/rose_plot.py +23 -5
  154. foxes/output/seq_plugins/__init__.py +2 -2
  155. foxes/output/seq_plugins/seq_flow_ani_plugin.py +0 -3
  156. foxes/output/seq_plugins/seq_wake_debug_plugin.py +0 -1
  157. foxes/output/slice_data.py +16 -19
  158. foxes/output/turbine_type_curves.py +7 -8
  159. foxes/utils/__init__.py +37 -19
  160. foxes/utils/abl/__init__.py +4 -4
  161. foxes/utils/cubic_roots.py +1 -1
  162. foxes/utils/data_book.py +4 -3
  163. foxes/utils/dict.py +3 -3
  164. foxes/utils/exec_python.py +5 -5
  165. foxes/utils/factory.py +1 -3
  166. foxes/utils/geom2d/__init__.py +7 -5
  167. foxes/utils/geopandas_utils.py +2 -2
  168. foxes/utils/pandas_utils.py +4 -3
  169. foxes/utils/tab_files.py +0 -1
  170. foxes/utils/weibull.py +28 -0
  171. foxes/utils/wrg_utils.py +3 -1
  172. foxes/utils/xarray_utils.py +9 -2
  173. foxes/variables.py +67 -9
  174. {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/METADATA +14 -21
  175. foxes-1.4.dist-info/RECORD +320 -0
  176. {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/WHEEL +1 -1
  177. tests/0_consistency/iterative/test_iterative.py +2 -3
  178. tests/0_consistency/partial_wakes/test_partial_wakes.py +2 -2
  179. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +2 -3
  180. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +48 -56
  181. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
  182. tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +33 -36
  183. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +0 -1
  184. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +0 -2
  185. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +3 -3
  186. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +3 -4
  187. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +3 -4
  188. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +3 -4
  189. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +3 -4
  190. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
  191. tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +3 -3
  192. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +3 -3
  193. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +0 -1
  194. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +3 -4
  195. tests/3_examples/test_examples.py +3 -2
  196. foxes/output/round.py +0 -10
  197. foxes/utils/pandas_helpers.py +0 -178
  198. foxes-1.2.5.dist-info/RECORD +0 -312
  199. {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/entry_points.txt +0 -0
  200. {foxes-1.2.5.dist-info → foxes-1.4.dist-info/licenses}/LICENSE +0 -0
  201. {foxes-1.2.5.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
@@ -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:
@@ -447,7 +447,7 @@ class Bastankhah2016(DistSlicedWakeModel):
447
447
  Parameters for the WakeK class
448
448
 
449
449
  """
450
- super().__init__(superpositions={FV.WS: superposition})
450
+ super().__init__(wind_superposition=superposition)
451
451
 
452
452
  self.model = None
453
453
  self.alpha = alpha
@@ -460,10 +460,23 @@ class Bastankhah2016(DistSlicedWakeModel):
460
460
  def __repr__(self):
461
461
  iname = self.induction
462
462
  s = f"{type(self).__name__}"
463
- s += f"({self.superpositions[FV.WS]}, induction={iname}, "
463
+ s += f"({self.wind_superposition}, induction={iname}, "
464
464
  s += self.wake_k.repr() + ")"
465
465
  return s
466
466
 
467
+ @property
468
+ def affects_ws(self):
469
+ """
470
+ Flag for wind speed wake models
471
+
472
+ Returns
473
+ -------
474
+ dws: bool
475
+ If True, this model affects wind speed
476
+
477
+ """
478
+ return True
479
+
467
480
  def sub_models(self):
468
481
  """
469
482
  List of all sub-models
@@ -474,7 +487,7 @@ class Bastankhah2016(DistSlicedWakeModel):
474
487
  Names of all sub models
475
488
 
476
489
  """
477
- return [self.wake_k, self.model]
490
+ return super().sub_models() + [self.wake_k, self.model]
478
491
 
479
492
  def initialize(self, algo, verbosity=0, force=False):
480
493
  """
@@ -621,4 +634,23 @@ class Bastankhah2016(DistSlicedWakeModel):
621
634
  * np.exp(-0.5 * (z / sigma_z) ** 2)
622
635
  )
623
636
 
637
+ # wake deflection causes wind vector rotation:
638
+ if FC.WDEFL_ROT_ANGLE in tdata:
639
+ dwd_defl = tdata.pop(FC.WDEFL_ROT_ANGLE)
640
+ if FV.WD not in wdeltas:
641
+ wdeltas[FV.WD] = np.zeros_like(wdeltas[FV.WS])
642
+ wdeltas[FV.WD][:] = dwd_defl[st_sel]
643
+ else:
644
+ wdeltas[FV.WD] += dwd_defl[st_sel]
645
+
646
+ # wake deflection causes wind speed reduction:
647
+ if FC.WDEFL_DWS_FACTOR in tdata:
648
+ dws_defl = tdata.pop(FC.WDEFL_DWS_FACTOR)
649
+ if FV.WS not in wdeltas:
650
+ raise AssertionError(
651
+ f"Wake model '{self.name}': Expecting '{FV.WS}' in wdeltas, found {list(wdeltas.keys())}"
652
+ )
653
+ else:
654
+ wdeltas[FV.WS] *= dws_defl[st_sel]
655
+
624
656
  return wdeltas, st_sel
@@ -31,7 +31,7 @@ class JensenWake(TopHatWakeModel):
31
31
  Parameters for the WakeK class
32
32
 
33
33
  """
34
- super().__init__(superpositions={FV.WS: superposition}, induction=induction)
34
+ super().__init__(wind_superposition=superposition, induction=induction)
35
35
  self.wake_k = WakeK(**wake_k)
36
36
 
37
37
  def __repr__(self):
@@ -39,10 +39,23 @@ class JensenWake(TopHatWakeModel):
39
39
  self.induction if isinstance(self.induction, str) else self.induction.name
40
40
  )
41
41
  s = f"{type(self).__name__}"
42
- s += f"({self.superpositions[FV.WS]}, induction={iname}, "
42
+ s += f"({self.wind_superposition}, induction={iname}, "
43
43
  s += self.wake_k.repr() + ")"
44
44
  return s
45
45
 
46
+ @property
47
+ def affects_ws(self):
48
+ """
49
+ Flag for wind speed wake models
50
+
51
+ Returns
52
+ -------
53
+ dws: bool
54
+ If True, this model affects wind speed
55
+
56
+ """
57
+ return True
58
+
46
59
  def calc_wake_radius(
47
60
  self,
48
61
  algo,
@@ -63,7 +63,7 @@ class TurbOParkWake(GaussianWakeModel):
63
63
  Parameters for the WakeK class
64
64
 
65
65
  """
66
- super().__init__(superpositions={FV.WS: superposition})
66
+ super().__init__(wind_superposition=superposition)
67
67
 
68
68
  self.sbeta_factor = sbeta_factor
69
69
  self.c1 = c1
@@ -76,10 +76,23 @@ class TurbOParkWake(GaussianWakeModel):
76
76
  self.induction if isinstance(self.induction, str) else self.induction.name
77
77
  )
78
78
  s = f"{type(self).__name__}"
79
- s += f"({self.superpositions[FV.WS]}, induction={iname}, "
79
+ s += f"({self.wind_superposition}, induction={iname}, "
80
80
  s += self.wake_k.repr() + ")"
81
81
  return s
82
82
 
83
+ @property
84
+ def affects_ws(self):
85
+ """
86
+ Flag for wind speed wake models
87
+
88
+ Returns
89
+ -------
90
+ dws: bool
91
+ If True, this model affects wind speed
92
+
93
+ """
94
+ return True
95
+
83
96
  def sub_models(self):
84
97
  """
85
98
  List of all sub-models
@@ -329,6 +342,19 @@ class TurbOParkWakeIX(GaussianWakeModel):
329
342
  s += self.wake_k.repr() + ")"
330
343
  return s
331
344
 
345
+ @property
346
+ def affects_ws(self):
347
+ """
348
+ Flag for wind speed wake models
349
+
350
+ Returns
351
+ -------
352
+ dws: bool
353
+ If True, this model affects wind speed
354
+
355
+ """
356
+ return True
357
+
332
358
  def sub_models(self):
333
359
  """
334
360
  List of all sub-models
@@ -2,12 +2,21 @@
2
2
  Wake superposition models.
3
3
  """
4
4
 
5
- from .ws_linear import WSLinear, WSLinearLocal
6
- from .ws_quadratic import WSQuadratic, WSQuadraticLocal
7
- from .ws_pow import WSPow, WSPowLocal
8
- from .ws_max import WSMax, WSMaxLocal
9
- from .ws_product import WSProduct
10
- from .ti_linear import TILinear
11
- from .ti_quadratic import TIQuadratic
12
- from .ti_pow import TIPow
13
- from .ti_max import TIMax
5
+ from .ws_linear import WSLinear as WSLinear
6
+ from .ws_linear import WSLinearLocal as WSLinearLocal
7
+
8
+ from .ws_pow import WSPow as WSPow
9
+ from .ws_pow import WSPowLocal as WSPowLocal
10
+
11
+ from .ws_max import WSMax as WSMax
12
+ from .ws_max import WSMaxLocal as WSMaxLocal
13
+
14
+ from .ws_quadratic import WSQuadratic as WSQuadratic
15
+ from .ws_quadratic import WSQuadraticLocal as WSQuadraticLocal
16
+
17
+ from .wind_vector import WindVectorLinear as WindVectorLinear
18
+ from .ws_product import WSProduct as WSProduct
19
+ from .ti_linear import TILinear as TILinear
20
+ from .ti_quadratic import TIQuadratic as TIQuadratic
21
+ from .ti_pow import TIPow as TIPow
22
+ from .ti_max import TIMax as TIMax