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,7 +1,7 @@
1
1
  import numpy as np
2
2
 
3
+ from foxes.config import config
3
4
  from foxes.core import TurbineInductionModel
4
- from foxes.utils import uv2wd, wd2uv, delta_wd
5
5
  import foxes.variables as FV
6
6
  import foxes.constants as FC
7
7
 
@@ -10,9 +10,6 @@ class RankineHalfBody(TurbineInductionModel):
10
10
  """
11
11
  The Rankine half body induction wake model
12
12
 
13
- The individual wake effects are superposed linearly,
14
- without invoking a wake superposition model.
15
-
16
13
  Notes
17
14
  -----
18
15
  Reference:
@@ -30,24 +27,41 @@ class RankineHalfBody(TurbineInductionModel):
30
27
 
31
28
  """
32
29
 
33
- def __init__(self, induction="Madsen"):
30
+ def __init__(self, superposition="vector", induction="Madsen"):
34
31
  """
35
32
  Constructor.
36
33
 
37
34
  Parameters
38
35
  ----------
36
+ superposition: str
37
+ The wind speed deficit superposition.
39
38
  induction: foxes.core.AxialInductionModel or str
40
39
  The induction model
41
40
 
42
41
  """
43
- super().__init__()
42
+ super().__init__(wind_superposition=superposition, other_superpositions={})
44
43
  self.induction = induction
45
44
 
45
+ self._has_uv = True
46
+
46
47
  def __repr__(self):
47
48
  iname = (
48
49
  self.induction if isinstance(self.induction, str) else self.induction.name
49
50
  )
50
- return f"{type(self).__name__}(induction={iname})"
51
+ return f"{type(self).__name__}({self.wind_superposition}, induction={iname})"
52
+
53
+ @property
54
+ def affects_ws(self):
55
+ """
56
+ Flag for wind speed wake models
57
+
58
+ Returns
59
+ -------
60
+ dws: bool
61
+ If True, this model affects wind speed
62
+
63
+ """
64
+ return True
51
65
 
52
66
  def sub_models(self):
53
67
  """
@@ -59,7 +73,7 @@ class RankineHalfBody(TurbineInductionModel):
59
73
  All sub models
60
74
 
61
75
  """
62
- return [self.induction]
76
+ return super().sub_models() + [self.induction]
63
77
 
64
78
  def initialize(self, algo, verbosity=0, force=False):
65
79
  """
@@ -98,15 +112,14 @@ class RankineHalfBody(TurbineInductionModel):
98
112
  -------
99
113
  wake_deltas: dict
100
114
  Key: variable name, value: The zero filled
101
- wake deltas, shape: (n_states, n_turbines, n_rpoints, ...)
115
+ wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
102
116
 
103
117
  """
104
- return {
105
- FV.WS: np.zeros_like(tdata[FC.TARGETS][..., 0]),
106
- FV.WD: np.zeros_like(tdata[FC.TARGETS][..., 0]),
107
- "U": np.zeros_like(tdata[FC.TARGETS][..., 0]),
108
- "V": np.zeros_like(tdata[FC.TARGETS][..., 0]),
109
- }
118
+ duv = np.zeros(
119
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
120
+ dtype=config.dtype_double,
121
+ )
122
+ return {FV.UV: duv}
110
123
 
111
124
  def contribute(
112
125
  self,
@@ -205,8 +218,7 @@ class RankineHalfBody(TurbineInductionModel):
205
218
 
206
219
  # calc velocity components
207
220
  vel_factor = m[st_sel] / (4 * np.linalg.norm(xyz, axis=-1) ** 3)
208
- wake_deltas["U"][st_sel] += vel_factor * xyz[:, 0]
209
- wake_deltas["V"][st_sel] += vel_factor * xyz[:, 1]
221
+ wake_deltas[FV.UV][st_sel] += vel_factor[:, None] * xyz[:, :2]
210
222
 
211
223
  # set values inside body shape
212
224
  st_sel = (ct > 1e-8) & (RHB_shape >= -1) & (x >= xs) & (x <= 0)
@@ -217,57 +229,4 @@ class RankineHalfBody(TurbineInductionModel):
217
229
 
218
230
  # calc velocity components
219
231
  vel_factor = m[st_sel] / (4 * np.linalg.norm(xyz, axis=-1) ** 3)
220
- wake_deltas["U"][st_sel] += vel_factor * xyz[:, 0]
221
-
222
- def finalize_wake_deltas(
223
- self,
224
- algo,
225
- mdata,
226
- fdata,
227
- amb_results,
228
- wake_deltas,
229
- ):
230
- """
231
- Finalize the wake calculation.
232
-
233
- Modifies wake_deltas on the fly.
234
-
235
- Parameters
236
- ----------
237
- algo: foxes.core.Algorithm
238
- The calculation algorithm
239
- mdata: foxes.core.MData
240
- The model data
241
- fdata: foxes.core.FData
242
- The farm data
243
- amb_results: dict
244
- The ambient results, key: variable name str,
245
- values: numpy.ndarray with shape
246
- (n_states, n_targets, n_tpoints)
247
- wake_deltas: dict
248
- The wake deltas object at the selected target
249
- turbines. Key: variable str, value: numpy.ndarray
250
- with shape (n_states, n_targets, n_tpoints)
251
-
252
- """
253
- # calc ambient wind vector:
254
- ws0 = amb_results[FV.WS]
255
- nx = wd2uv(amb_results[FV.WD])
256
- wind_vec = nx * ws0[:, :, :, None]
257
-
258
- # wake deltas are in wake frame, rotate back to global frame:
259
- ny = np.stack((-nx[..., 1], nx[..., 0]), axis=-1)
260
- delta_uv = (
261
- wake_deltas["U"][:, :, :, None] * nx + wake_deltas["V"][:, :, :, None] * ny
262
- )
263
- del ws0, nx, ny
264
-
265
- # add ambient result to wake deltas:
266
- wind_vec += delta_uv
267
- del delta_uv
268
-
269
- # deduce WS and WD deltas:
270
- new_wd = uv2wd(wind_vec)
271
- new_ws = np.linalg.norm(wind_vec, axis=-1)
272
- wake_deltas[FV.WS] += new_ws - amb_results[FV.WS]
273
- wake_deltas[FV.WD] += delta_wd(amb_results[FV.WD], new_wd)
232
+ wake_deltas[FV.UV][st_sel, 0] += vel_factor * xyz[:, 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
@@ -50,16 +51,28 @@ class Rathmann(TurbineInductionModel):
50
51
  Calculate only the pre-rotor region
51
52
 
52
53
  """
53
- super().__init__()
54
+ super().__init__(wind_superposition=superposition)
54
55
  self.induction = induction
55
56
  self.pre_rotor_only = pre_rotor_only
56
- self._superp_name = superposition
57
57
 
58
58
  def __repr__(self):
59
59
  iname = (
60
60
  self.induction if isinstance(self.induction, str) else self.induction.name
61
61
  )
62
- return f"{type(self).__name__}({self._superp_name}, induction={iname})"
62
+ return f"{type(self).__name__}({self.wind_superposition}, induction={iname})"
63
+
64
+ @property
65
+ def affects_ws(self):
66
+ """
67
+ Flag for wind speed wake models
68
+
69
+ Returns
70
+ -------
71
+ dws: bool
72
+ If True, this model affects wind speed
73
+
74
+ """
75
+ return True
63
76
 
64
77
  def sub_models(self):
65
78
  """
@@ -71,7 +84,7 @@ class Rathmann(TurbineInductionModel):
71
84
  All sub models
72
85
 
73
86
  """
74
- return [self._superp, self.induction]
87
+ return super().sub_models() + [self.induction]
75
88
 
76
89
  def initialize(self, algo, verbosity=0, force=False):
77
90
  """
@@ -87,7 +100,6 @@ class Rathmann(TurbineInductionModel):
87
100
  Overwrite existing data
88
101
 
89
102
  """
90
- self._superp = algo.mbook.wake_superpositions[self._superp_name]
91
103
  if isinstance(self.induction, str):
92
104
  self.induction = algo.mbook.axial_induction[self.induction]
93
105
  super().initialize(algo, verbosity, force)
@@ -111,10 +123,21 @@ class Rathmann(TurbineInductionModel):
111
123
  -------
112
124
  wake_deltas: dict
113
125
  Key: variable name, value: The zero filled
114
- wake deltas, shape: (n_states, n_turbines, n_rpoints, ...)
126
+ wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
115
127
 
116
128
  """
117
- return {FV.WS: np.zeros_like(tdata[FC.TARGETS][..., 0])}
129
+ if self.has_uv:
130
+ duv = np.zeros(
131
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
132
+ dtype=config.dtype_double,
133
+ )
134
+ return {FV.UV: duv}
135
+ else:
136
+ dws = np.zeros(
137
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
138
+ dtype=config.dtype_double,
139
+ )
140
+ return {FV.WS: dws}
118
141
 
119
142
  def contribute(
120
143
  self,
@@ -195,6 +218,39 @@ class Rathmann(TurbineInductionModel):
195
218
  sin_beta = 1 / np.sqrt(x_R**2 + r_R**2 + 1) # eqn 19
196
219
  return sin_alpha * sin_beta * (1 + x_R**2)
197
220
 
221
+ def add_wake(sp_sel, wake_deltas, blockage):
222
+ """adds to wake deltas"""
223
+ if self.has_uv:
224
+ assert self.has_vector_wind_superp, (
225
+ f"Wake model {self.name}: Missing vector wind superposition, got '{self.wind_superposition}'"
226
+ )
227
+ wdeltas = {FV.WS: blockage}
228
+ self.vec_superp.wdeltas_ws2uv(
229
+ algo, fdata, tdata, downwind_index, wdeltas, sp_sel
230
+ )
231
+ wake_deltas[FV.UV] = self.vec_superp.add_wake_vector(
232
+ algo,
233
+ mdata,
234
+ fdata,
235
+ tdata,
236
+ downwind_index,
237
+ sp_sel,
238
+ wake_deltas[FV.UV],
239
+ wdeltas.pop(FV.UV),
240
+ )
241
+ else:
242
+ self.superp[FV.WS].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
+ )
253
+
198
254
  # ws delta in front of rotor
199
255
  sp_sel = (ct > 1e-8) & (x_R <= 0)
200
256
  if np.any(sp_sel):
@@ -202,17 +258,7 @@ class Rathmann(TurbineInductionModel):
202
258
  a = self.induction.ct2a(ct[sp_sel])
203
259
  blockage = a * mu(xr) * G(xr, r_R[sp_sel]) # eqn 10
204
260
 
205
- self._superp.add_wake(
206
- algo,
207
- mdata,
208
- fdata,
209
- tdata,
210
- downwind_index,
211
- sp_sel,
212
- FV.WS,
213
- wake_deltas[FV.WS],
214
- -blockage,
215
- )
261
+ add_wake(sp_sel, wake_deltas, -blockage)
216
262
 
217
263
  # ws delta behind rotor
218
264
  if not self.pre_rotor_only:
@@ -223,51 +269,6 @@ class Rathmann(TurbineInductionModel):
223
269
  a = self.induction.ct2a(ct[sp_sel])
224
270
  blockage = a * mu(-xr) * G(-xr, r_R[sp_sel]) # eqn 10
225
271
 
226
- self._superp.add_wake(
227
- algo,
228
- mdata,
229
- fdata,
230
- tdata,
231
- downwind_index,
232
- sp_sel,
233
- FV.WS,
234
- wake_deltas[FV.WS],
235
- blockage,
236
- )
272
+ add_wake(sp_sel, wake_deltas, blockage)
237
273
 
238
274
  return wake_deltas
239
-
240
- def finalize_wake_deltas(
241
- self,
242
- algo,
243
- mdata,
244
- fdata,
245
- amb_results,
246
- wake_deltas,
247
- ):
248
- """
249
- Finalize the wake calculation.
250
-
251
- Modifies wake_deltas on the fly.
252
-
253
- Parameters
254
- ----------
255
- algo: foxes.core.Algorithm
256
- The calculation algorithm
257
- mdata: foxes.core.MData
258
- The model data
259
- fdata: foxes.core.FData
260
- The farm data
261
- amb_results: dict
262
- The ambient results, key: variable name str,
263
- values: numpy.ndarray with shape
264
- (n_states, n_targets, n_tpoints)
265
- wake_deltas: dict
266
- The wake deltas object at the selected target
267
- turbines. Key: variable str, value: numpy.ndarray
268
- with shape (n_states, n_targets, n_tpoints)
269
-
270
- """
271
- wake_deltas[FV.WS] = self._superp.calc_final_wake_delta(
272
- algo, mdata, fdata, FV.WS, amb_results[FV.WS], wake_deltas[FV.WS]
273
- )
@@ -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: