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
@@ -0,0 +1,94 @@
1
+ from foxes.core.wake_deflection import WakeDeflection
2
+
3
+
4
+ class NoDeflection(WakeDeflection):
5
+ """
6
+ Switch of wake deflection
7
+
8
+ :group: models.wake_deflections
9
+
10
+ """
11
+
12
+ def calc_deflection(
13
+ self,
14
+ algo,
15
+ mdata,
16
+ fdata,
17
+ tdata,
18
+ downwind_index,
19
+ coos,
20
+ ):
21
+ """
22
+ Calculates the wake deflection.
23
+
24
+ This function optionally adds FC.WDEFL_ROT_ANGLE or
25
+ FC.WDEFL_DWS_FACTOR to the tdata.
26
+
27
+ Parameters
28
+ ----------
29
+ algo: foxes.core.Algorithm
30
+ The calculation algorithm
31
+ mdata: foxes.core.MData
32
+ The model data
33
+ fdata: foxes.core.FData
34
+ The farm data
35
+ tdata: foxes.core.TData
36
+ The target point data
37
+ downwind_index: int
38
+ The index of the wake causing turbine
39
+ in the downwind order
40
+ coos: numpy.ndarray
41
+ The wake frame coordinates of the evaluation
42
+ points, shape: (n_states, n_targets, n_tpoints, 3)
43
+
44
+ Returns
45
+ -------
46
+ coos: numpy.ndarray
47
+ The wake frame coordinates of the evaluation
48
+ points, shape: (n_states, n_targets, n_tpoints, 3)
49
+
50
+ """
51
+ return coos
52
+
53
+ def get_yaw_alpha_seq(
54
+ self,
55
+ algo,
56
+ mdata,
57
+ fdata,
58
+ tdata,
59
+ downwind_index,
60
+ x,
61
+ ):
62
+ """
63
+ Computes sequential wind vector rotation angles.
64
+
65
+ Wind vector rotation angles are computed at the
66
+ current trace points due to a yawed rotor
67
+ for sequential runs.
68
+
69
+ Parameters
70
+ ----------
71
+ algo: foxes.core.Algorithm
72
+ The calculation algorithm
73
+ mdata: foxes.core.MData
74
+ The model data
75
+ fdata: foxes.core.FData
76
+ The farm data
77
+ tdata: foxes.core.TData
78
+ The target point data
79
+ downwind_index: int
80
+ The index of the wake causing turbine
81
+ in the downwind order
82
+ x: numpy.ndarray
83
+ The distance from the wake causing rotor
84
+ for the first n_times subsequent time steps,
85
+ shape: (n_times,)
86
+
87
+ Returns
88
+ -------
89
+ alpha: numpy.ndarray
90
+ The delta WD result at the x locations,
91
+ shape: (n_times,)
92
+
93
+ """
94
+ return None
@@ -2,10 +2,9 @@
2
2
  Wake frame models.
3
3
  """
4
4
 
5
- from .rotor_wd import RotorWD
6
- from .streamlines import Streamlines2D
7
- from .timelines import Timelines
8
- from .yawed_wakes import YawedWakes
9
- from .farm_order import FarmOrder
10
- from .seq_dynamic_wakes import SeqDynamicWakes
11
- from .dynamic_wakes import DynamicWakes
5
+ from .rotor_wd import RotorWD as RotorWD
6
+ from .streamlines import Streamlines2D as Streamlines2D
7
+ from .timelines import Timelines as Timelines
8
+ from .farm_order import FarmOrder as FarmOrder
9
+ from .seq_dynamic_wakes import SeqDynamicWakes as SeqDynamicWakes
10
+ from .dynamic_wakes import DynamicWakes as DynamicWakes
@@ -161,7 +161,10 @@ class DynamicWakes(WakeFrame):
161
161
  for v in algo.states.output_point_vars(algo)
162
162
  }
163
163
  key = f"{self.DATA}_{downwind_index}"
164
- ukey_fun = lambda fr, to: f"{self.UPDATE}_dw{downwind_index}_from_{fr}_to_{to}"
164
+
165
+ def ukey_fun(fr, to):
166
+ """helper function to create update key"""
167
+ return f"{self.UPDATE}_dw{downwind_index}_from_{fr}_to_{to}"
165
168
 
166
169
  # compute wakes that start within this chunk: x, y, z, length
167
170
  data = algo.get_from_chunk_store(name=key, mdata=mdata, error=False)
@@ -274,7 +277,6 @@ class DynamicWakes(WakeFrame):
274
277
  # compute single state wake propagation:
275
278
  isnan0 = np.isnan(hdata)
276
279
  for si in range(n_states):
277
-
278
280
  s = slice(si, si + 1, None)
279
281
  hmdata = mdata.get_slice(FC.STATE, s)
280
282
  hfdata = fdata.get_slice(FC.STATE, s)
@@ -429,4 +431,11 @@ class DynamicWakes(WakeFrame):
429
431
  (FC.STATE, FC.TARGET, FC.TPOINT),
430
432
  )
431
433
 
432
- return wcoos.reshape(n_states, n_targets, n_tpoints, 3)
434
+ return algo.wake_deflection.calc_deflection(
435
+ algo,
436
+ mdata,
437
+ fdata,
438
+ tdata,
439
+ downwind_index,
440
+ wcoos.reshape(n_states, n_targets, n_tpoints, 3),
441
+ )
@@ -116,7 +116,9 @@ class RotorWD(WakeFrame):
116
116
 
117
117
  coos = np.einsum("stpd,sad->stpa", delta, nax)
118
118
 
119
- return coos
119
+ return algo.wake_deflection.calc_deflection(
120
+ algo, mdata, fdata, tdata, downwind_index, coos
121
+ )
120
122
 
121
123
  def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
122
124
  """
@@ -23,12 +23,14 @@ class SeqDynamicWakes(FarmOrder):
23
23
  dt_min: float, optional
24
24
  The delta t value in minutes,
25
25
  if not from timeseries data
26
+ induction: foxes.core.AxialInductionModel
27
+ The induction model
26
28
 
27
29
  :group: models.wake_frames.sequential
28
30
 
29
31
  """
30
32
 
31
- def __init__(self, cl_ipars={}, dt_min=None, **kwargs):
33
+ def __init__(self, cl_ipars={}, dt_min=None, induction="Madsen", **kwargs):
32
34
  """
33
35
  Constructor.
34
36
 
@@ -40,6 +42,8 @@ class SeqDynamicWakes(FarmOrder):
40
42
  dt_min: float, optional
41
43
  The delta t value in minutes,
42
44
  if not from timeseries data
45
+ induction: foxes.core.AxialInductionModel or str
46
+ The induction model
43
47
  kwargs: dict, optional
44
48
  Additional parameters for the base class
45
49
 
@@ -47,9 +51,25 @@ class SeqDynamicWakes(FarmOrder):
47
51
  super().__init__(**kwargs)
48
52
  self.cl_ipars = cl_ipars
49
53
  self.dt_min = dt_min
54
+ self.induction = induction
50
55
 
51
56
  def __repr__(self):
52
- return f"{type(self).__name__}(dt_min={self.dt_min})"
57
+ iname = (
58
+ self.induction if isinstance(self.induction, str) else self.induction.name
59
+ )
60
+ return f"{type(self).__name__}(dt_min={self.dt_min}, induction={iname})"
61
+
62
+ def sub_models(self):
63
+ """
64
+ List of all sub-models
65
+
66
+ Returns
67
+ -------
68
+ smdls: list of foxes.core.Model
69
+ All sub models
70
+
71
+ """
72
+ return [self.induction]
53
73
 
54
74
  def initialize(self, algo, verbosity=0):
55
75
  """
@@ -63,7 +83,11 @@ class SeqDynamicWakes(FarmOrder):
63
83
  The verbosity level, 0 = silent
64
84
 
65
85
  """
86
+ if isinstance(self.induction, str):
87
+ self.induction = algo.mbook.axial_induction[self.induction]
88
+
66
89
  super().initialize(algo, verbosity)
90
+
67
91
  if not isinstance(algo, Sequential):
68
92
  raise TypeError(
69
93
  f"Incompatible algorithm type {type(algo).__name__}, expecting {Sequential.__name__}"
@@ -168,7 +192,6 @@ class SeqDynamicWakes(FarmOrder):
168
192
  N = counter + 1
169
193
 
170
194
  if np.isnan(self._traces_l[counter, downwind_index]):
171
-
172
195
  # new wake starts at turbine:
173
196
  self._traces_p[counter, downwind_index][:] = fdata[FV.TXYH][
174
197
  0, downwind_index
@@ -186,12 +209,26 @@ class SeqDynamicWakes(FarmOrder):
186
209
 
187
210
  # compute wind vectors at wake traces:
188
211
  # TODO: dz from U_z is missing here
189
- hpdata = TData.from_points(points=self._traces_p[None, :N, downwind_index])
190
- res = algo.states.calculate(algo, mdata, fdata, hpdata)
191
- self._traces_v[:N, downwind_index, :2] = wd2uv(
192
- res[FV.WD][0, :, 0], res[FV.WS][0, :, 0]
212
+ svrs = algo.states.output_point_vars(algo)
213
+ hpdata = TData.from_points(
214
+ points=self._traces_p[None, :N, downwind_index], variables=svrs
193
215
  )
194
- del hpdata, res
216
+ res = algo.states.calculate(algo, mdata, fdata, hpdata)
217
+ wd = res[FV.WD][0, :, 0]
218
+ if FV.YAWM in fdata:
219
+ wddef = algo.wake_deflection.get_yaw_alpha_seq(
220
+ algo,
221
+ mdata,
222
+ fdata,
223
+ hpdata,
224
+ downwind_index,
225
+ self._traces_l[:N, downwind_index],
226
+ )
227
+ if wddef is not None:
228
+ wd += wddef
229
+ del wddef
230
+ self._traces_v[:N, downwind_index, :2] = wd2uv(wd, res[FV.WS][0, :, 0])
231
+ del hpdata, res, svrs, wd
195
232
 
196
233
  # find nearest wake point:
197
234
  dists = cdist(points[0], self._traces_p[:N, downwind_index])
@@ -67,7 +67,6 @@ class Streamlines2D(WakeFrame):
67
67
  # calc data: x, y, z, wd
68
68
  data = np.zeros((n_states, n_turbines, N, 4), dtype=config.dtype_double)
69
69
  for i in range(N):
70
-
71
70
  # set streamline start point data (rotor centre):
72
71
  if i == 0:
73
72
  data[:, :, i, :3] = fdata[FV.TXYH]
@@ -75,7 +74,6 @@ class Streamlines2D(WakeFrame):
75
74
 
76
75
  # compute next step:
77
76
  else:
78
-
79
77
  # calculate next point:
80
78
  xyz = data[:, :, i - 1, :3]
81
79
  n = wd2uv(data[:, :, i - 1, 3])
@@ -198,9 +196,9 @@ class Streamlines2D(WakeFrame):
198
196
  # n_states, n_turbines_source, n_turbines_target
199
197
  coosx = np.zeros((n_states, n_turbines, n_turbines), dtype=config.dtype_double)
200
198
  for ti in range(n_turbines):
201
- coosx[:, ti, :] = self.get_wake_coos(algo, mdata, fdata, tdata, ti)[
202
- :, :, 0, 0
203
- ]
199
+ coosx[:, ti, :] = self._calc_coos(
200
+ algo, mdata, fdata, tdata[FC.TARGETS], ti
201
+ )[:, :, 0, 0]
204
202
 
205
203
  # derive turbine order:
206
204
  # TODO: Remove loop over states
@@ -242,7 +240,11 @@ class Streamlines2D(WakeFrame):
242
240
  points, shape: (n_states, n_targets, n_tpoints, 3)
243
241
 
244
242
  """
245
- return self._calc_coos(algo, mdata, fdata, tdata[FC.TARGETS], downwind_index)
243
+ coos = self._calc_coos(algo, mdata, fdata, tdata[FC.TARGETS], downwind_index)
244
+
245
+ return algo.wake_deflection.calc_deflection(
246
+ algo, mdata, fdata, tdata, downwind_index, coos
247
+ )
246
248
 
247
249
  def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
248
250
  """
@@ -92,7 +92,7 @@ class Timelines(WakeFrame):
92
92
  del mdict, mdims, data
93
93
 
94
94
  # prepare fdata:
95
- fdata = FData({}, {}, loop_dims=[FC.STATE])
95
+ fdata = FData()
96
96
 
97
97
  # prepare tdata:
98
98
  n_states = states.size()
@@ -105,8 +105,8 @@ class Timelines(WakeFrame):
105
105
 
106
106
  # calculate all heights:
107
107
  self.timelines_data = {"dxy": (("height", FC.STATE, "dir"), [])}
108
+ weight_data = None
108
109
  for h in heights:
109
-
110
110
  if verbosity > 0:
111
111
  print(f" Height: {h} m")
112
112
 
@@ -118,6 +118,13 @@ class Timelines(WakeFrame):
118
118
  )
119
119
 
120
120
  res = states.calculate(algo, mdata, fdata, tdata)
121
+
122
+ if weight_data is None:
123
+ weight_data = ((FC.STATE,), tdata[FV.WEIGHT][:, 0, 0])
124
+ elif not np.all(tdata[FV.WEIGHT] == weight_data[1]):
125
+ raise AssertionError(
126
+ f"States '{self.name}': weight data mismatch between heights"
127
+ )
121
128
  del tdata
122
129
 
123
130
  uv = wd2uv(res[FV.WD], res[FV.WS])[:, 0, 0, :2]
@@ -163,6 +170,8 @@ class Timelines(WakeFrame):
163
170
  },
164
171
  )
165
172
 
173
+ return weight_data
174
+
166
175
  def initialize(self, algo, verbosity=0):
167
176
  """
168
177
  Initializes the model.
@@ -420,7 +429,14 @@ class Timelines(WakeFrame):
420
429
  (FC.STATE, FC.TARGET, FC.TPOINT),
421
430
  )
422
431
 
423
- return wcoos.reshape(n_states, n_targets, n_tpoints, 3)
432
+ return algo.wake_deflection.calc_deflection(
433
+ algo,
434
+ mdata,
435
+ fdata,
436
+ tdata,
437
+ downwind_index,
438
+ wcoos.reshape(n_states, n_targets, n_tpoints, 3),
439
+ )
424
440
 
425
441
  def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
426
442
  """
@@ -2,11 +2,11 @@
2
2
  Wake models.
3
3
  """
4
4
 
5
- from .dist_sliced import DistSlicedWakeModel
6
- from .axisymmetric import AxisymmetricWakeModel
7
- from .top_hat import TopHatWakeModel
8
- from .gaussian import GaussianWakeModel
5
+ from .dist_sliced import DistSlicedWakeModel as DistSlicedWakeModel
6
+ from .axisymmetric import AxisymmetricWakeModel as AxisymmetricWakeModel
7
+ from .top_hat import TopHatWakeModel as TopHatWakeModel
8
+ from .gaussian import GaussianWakeModel as GaussianWakeModel
9
9
 
10
- from . import wind
11
- from . import ti
12
- from . import induction
10
+ from . import wind as wind
11
+ from . import ti as ti
12
+ from . import induction as induction
@@ -1,9 +1,12 @@
1
1
  from abc import abstractmethod
2
+ import numpy as np
2
3
 
3
- from foxes.core import WakeModel
4
+ from foxes.core import SingleTurbineWakeModel
5
+ from foxes.config import config
6
+ import foxes.variables as FV
4
7
 
5
8
 
6
- class DistSlicedWakeModel(WakeModel):
9
+ class DistSlicedWakeModel(SingleTurbineWakeModel):
7
10
  """
8
11
  Abstract base class for wake models for which
9
12
  the x-denpendency can be separated from the
@@ -12,65 +15,44 @@ class DistSlicedWakeModel(WakeModel):
12
15
  The multi-yz ability is used by the `PartialDistSlicedWake`
13
16
  partial wakes model.
14
17
 
15
- Attributes
16
- ----------
17
- superpositions: dict
18
- The superpositions. Key: variable name str,
19
- value: The wake superposition model name,
20
- will be looked up in model book
21
- superp: dict
22
- The superposition dict, key: variable name str,
23
- value: `foxes.core.WakeSuperposition`
24
-
25
18
  :group: models.wake_models
26
19
 
27
20
  """
28
21
 
29
- def __init__(self, superpositions):
22
+ def new_wake_deltas(self, algo, mdata, fdata, tdata):
30
23
  """
31
- Constructor.
24
+ Creates new empty wake delta arrays.
32
25
 
33
26
  Parameters
34
27
  ----------
35
- superpositions: dict
36
- The superpositions. Key: variable name str,
37
- value: The wake superposition model name,
38
- will be looked up in model book
39
-
40
- """
41
- super().__init__()
42
- self.superpositions = superpositions
43
-
44
- def sub_models(self):
45
- """
46
- List of all sub-models
28
+ algo: foxes.core.Algorithm
29
+ The calculation algorithm
30
+ mdata: foxes.core.MData
31
+ The model data
32
+ fdata: foxes.core.FData
33
+ The farm data
34
+ tdata: foxes.core.TData
35
+ The target point data
47
36
 
48
37
  Returns
49
38
  -------
50
- smdls: list of foxes.core.Model
51
- Names of all sub models
52
-
53
- """
54
- return list(self.superp.values())
55
-
56
- def initialize(self, algo, verbosity=0, force=False):
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
66
- force: bool
67
- Overwrite existing data
39
+ wake_deltas: dict
40
+ Key: variable name, value: The zero filled
41
+ wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
68
42
 
69
43
  """
70
- self.superp = {
71
- v: algo.mbook.wake_superpositions[s] for v, s in self.superpositions.items()
72
- }
73
- super().initialize(algo, verbosity, force)
44
+ if self.has_uv:
45
+ duv = np.zeros(
46
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints, 2),
47
+ dtype=config.dtype_double,
48
+ )
49
+ return {FV.UV: duv}
50
+ else:
51
+ dws = np.zeros(
52
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
53
+ dtype=config.dtype_double,
54
+ )
55
+ return {FV.WS: dws}
74
56
 
75
57
  @abstractmethod
76
58
  def calc_wakes_x_yz(
@@ -159,6 +141,26 @@ class DistSlicedWakeModel(WakeModel):
159
141
  algo, mdata, fdata, tdata, downwind_index, x, yz
160
142
  )
161
143
 
144
+ if self.affects_ws and self.has_uv:
145
+ assert self.has_vector_wind_superp, (
146
+ f"Wake model {self.name}: Missing vector wind superposition, got '{self.wind_superposition}'"
147
+ )
148
+ if FV.UV in wdeltas or FV.WS in wdeltas:
149
+ if FV.UV not in wdeltas:
150
+ self.vec_superp.wdeltas_ws2uv(
151
+ algo, fdata, tdata, downwind_index, wdeltas, st_sel
152
+ )
153
+ wake_deltas[FV.UV] = self.vec_superp.add_wake_vector(
154
+ algo,
155
+ mdata,
156
+ fdata,
157
+ tdata,
158
+ downwind_index,
159
+ st_sel,
160
+ wake_deltas[FV.UV],
161
+ wdeltas.pop(FV.UV),
162
+ )
163
+
162
164
  for v, hdel in wdeltas.items():
163
165
  try:
164
166
  superp = self.superp[v]
@@ -178,39 +180,3 @@ class DistSlicedWakeModel(WakeModel):
178
180
  wake_deltas[v],
179
181
  hdel,
180
182
  )
181
-
182
- def finalize_wake_deltas(
183
- self,
184
- algo,
185
- mdata,
186
- fdata,
187
- amb_results,
188
- wake_deltas,
189
- ):
190
- """
191
- Finalize the wake calculation.
192
-
193
- Modifies wake_deltas on the fly.
194
-
195
- Parameters
196
- ----------
197
- algo: foxes.core.Algorithm
198
- The calculation algorithm
199
- mdata: foxes.core.MData
200
- The model data
201
- fdata: foxes.core.FData
202
- The farm data
203
- amb_results: dict
204
- The ambient results, key: variable name str,
205
- values: numpy.ndarray with shape
206
- (n_states, n_targets, n_tpoints)
207
- wake_deltas: dict
208
- The wake deltas object at the selected target
209
- turbines. Key: variable str, value: numpy.ndarray
210
- with shape (n_states, n_targets, n_tpoints)
211
-
212
- """
213
- for v, s in self.superp.items():
214
- wake_deltas[v] = s.calc_final_wake_delta(
215
- algo, mdata, fdata, v, amb_results[v], wake_deltas[v]
216
- )
@@ -2,6 +2,8 @@ import numpy as np
2
2
  from abc import abstractmethod
3
3
 
4
4
  from foxes.models.wake_models.axisymmetric import AxisymmetricWakeModel
5
+ import foxes.variables as FV
6
+ import foxes.constants as FC
5
7
 
6
8
 
7
9
  class GaussianWakeModel(AxisymmetricWakeModel):
@@ -94,13 +96,31 @@ class GaussianWakeModel(AxisymmetricWakeModel):
94
96
  is non-zero, shape: (n_states, n_targets)
95
97
 
96
98
  """
99
+ # compute amplitude and sigma:
97
100
  amsi, st_sel = self.calc_amplitude_sigma(
98
101
  algo, mdata, fdata, tdata, downwind_index, x
99
102
  )
103
+
104
+ # evaluate the Gaussian function:
100
105
  wdeltas = {}
101
106
  rsel = r[st_sel]
102
107
  for v in amsi.keys():
103
108
  ampld, sigma = amsi[v]
104
109
  wdeltas[v] = ampld[:, None] * np.exp(-0.5 * (rsel / sigma[:, None]) ** 2)
105
110
 
111
+ if self.affects_ws and FV.WS in wdeltas:
112
+ # wake deflection causes wind vector rotation:
113
+ if FC.WDEFL_ROT_ANGLE in tdata:
114
+ dwd_defl = tdata.pop(FC.WDEFL_ROT_ANGLE)
115
+ if FV.WD not in wdeltas:
116
+ wdeltas[FV.WD] = np.zeros_like(wdeltas[FV.WS])
117
+ wdeltas[FV.WD][:] = dwd_defl[st_sel]
118
+ else:
119
+ wdeltas[FV.WD] += dwd_defl[st_sel]
120
+
121
+ # wake deflection causes wind speed reduction:
122
+ if FC.WDEFL_DWS_FACTOR in tdata:
123
+ dws_defl = tdata.pop(FC.WDEFL_DWS_FACTOR)
124
+ wdeltas[FV.WS] *= dws_defl[st_sel]
125
+
106
126
  return wdeltas, st_sel
@@ -2,8 +2,8 @@
2
2
  Induction wake models
3
3
  """
4
4
 
5
- from .rankine_half_body import RankineHalfBody
6
- from .rathmann import Rathmann
7
- from .self_similar import SelfSimilar
8
- from .self_similar2020 import SelfSimilar2020
9
- from .vortex_sheet import VortexSheet
5
+ from .rankine_half_body import RankineHalfBody as RankineHalfBody
6
+ from .rathmann import Rathmann as Rathmann
7
+ from .self_similar import SelfSimilar as SelfSimilar
8
+ from .self_similar2020 import SelfSimilar2020 as SelfSimilar2020
9
+ from .vortex_sheet import VortexSheet as VortexSheet