foxes 1.0__py3-none-any.whl → 1.1.1__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 (128) hide show
  1. docs/source/conf.py +0 -1
  2. examples/states_lookup_table/run.py +1 -1
  3. examples/timeseries/run.py +11 -4
  4. foxes/algorithms/downwind/downwind.py +18 -13
  5. foxes/algorithms/downwind/models/farm_wakes_calc.py +1 -1
  6. foxes/algorithms/downwind/models/init_farm_data.py +1 -1
  7. foxes/algorithms/downwind/models/point_wakes_calc.py +1 -1
  8. foxes/algorithms/downwind/models/reorder_farm_output.py +1 -1
  9. foxes/algorithms/downwind/models/set_amb_farm_results.py +1 -1
  10. foxes/algorithms/downwind/models/set_amb_point_results.py +1 -1
  11. foxes/algorithms/iterative/iterative.py +1 -1
  12. foxes/algorithms/iterative/models/farm_wakes_calc.py +1 -1
  13. foxes/algorithms/iterative/models/urelax.py +3 -3
  14. foxes/algorithms/sequential/models/plugin.py +4 -4
  15. foxes/algorithms/sequential/models/seq_state.py +1 -1
  16. foxes/constants.py +5 -5
  17. foxes/core/algorithm.py +2 -2
  18. foxes/core/data_calc_model.py +2 -2
  19. foxes/core/engine.py +20 -10
  20. foxes/core/farm_controller.py +3 -3
  21. foxes/core/farm_data_model.py +1 -1
  22. foxes/core/ground_model.py +2 -2
  23. foxes/core/model.py +122 -108
  24. foxes/core/partial_wakes_model.py +1 -1
  25. foxes/core/point_data_model.py +2 -2
  26. foxes/core/states.py +1 -1
  27. foxes/core/turbine_type.py +2 -2
  28. foxes/core/wake_frame.py +8 -30
  29. foxes/core/wake_model.py +3 -2
  30. foxes/core/wake_superposition.py +1 -1
  31. foxes/data/windio/windio_5turbines_timeseries.yaml +9 -15
  32. foxes/engines/__init__.py +1 -0
  33. foxes/engines/dask.py +13 -6
  34. foxes/engines/multiprocess.py +5 -8
  35. foxes/engines/numpy.py +8 -26
  36. foxes/engines/pool.py +10 -24
  37. foxes/engines/ray.py +79 -0
  38. foxes/engines/single.py +3 -1
  39. foxes/input/farm_layout/from_json.py +1 -1
  40. foxes/input/states/__init__.py +1 -0
  41. foxes/input/states/field_data_nc.py +4 -4
  42. foxes/input/states/multi_height.py +4 -4
  43. foxes/input/states/scan_ws.py +1 -1
  44. foxes/input/states/single.py +1 -1
  45. foxes/input/states/slice_data_nc.py +681 -0
  46. foxes/input/states/states_table.py +3 -3
  47. foxes/input/windio/__init__.py +1 -1
  48. foxes/input/windio/read_attributes.py +8 -2
  49. foxes/input/windio/read_fields.py +3 -0
  50. foxes/input/windio/read_outputs.py +8 -2
  51. foxes/input/windio/windio.py +6 -2
  52. foxes/models/farm_models/turbine2farm.py +1 -1
  53. foxes/models/ground_models/wake_mirror.py +2 -2
  54. foxes/models/model_book.py +29 -2
  55. foxes/models/partial_wakes/axiwake.py +3 -3
  56. foxes/models/partial_wakes/top_hat.py +2 -2
  57. foxes/models/point_models/set_uniform_data.py +1 -1
  58. foxes/models/point_models/tke2ti.py +1 -1
  59. foxes/models/point_models/wake_deltas.py +1 -1
  60. foxes/models/rotor_models/grid.py +2 -2
  61. foxes/models/turbine_models/calculator.py +4 -4
  62. foxes/models/turbine_models/kTI_model.py +22 -6
  63. foxes/models/turbine_models/lookup_table.py +3 -2
  64. foxes/models/turbine_types/PCt_file.py +5 -5
  65. foxes/models/turbine_types/PCt_from_two.py +5 -5
  66. foxes/models/turbine_types/TBL_file.py +80 -0
  67. foxes/models/turbine_types/__init__.py +1 -0
  68. foxes/models/turbine_types/lookup.py +5 -5
  69. foxes/models/turbine_types/null_type.py +3 -3
  70. foxes/models/turbine_types/wsrho2PCt_from_two.py +7 -7
  71. foxes/models/turbine_types/wsti2PCt_from_two.py +9 -9
  72. foxes/models/vertical_profiles/__init__.py +1 -1
  73. foxes/models/wake_frames/dynamic_wakes.py +2 -2
  74. foxes/models/wake_frames/farm_order.py +2 -2
  75. foxes/models/wake_frames/rotor_wd.py +2 -2
  76. foxes/models/wake_frames/seq_dynamic_wakes.py +5 -11
  77. foxes/models/wake_frames/streamlines.py +2 -2
  78. foxes/models/wake_frames/timelines.py +2 -2
  79. foxes/models/wake_frames/yawed_wakes.py +3 -3
  80. foxes/models/wake_models/dist_sliced.py +1 -1
  81. foxes/models/wake_models/induction/rankine_half_body.py +1 -1
  82. foxes/models/wake_models/induction/rathmann.py +76 -22
  83. foxes/models/wake_models/induction/self_similar.py +76 -26
  84. foxes/models/wake_models/induction/vortex_sheet.py +84 -46
  85. foxes/models/wake_models/ti/crespo_hernandez.py +6 -4
  86. foxes/models/wake_models/ti/iec_ti.py +7 -5
  87. foxes/models/wake_models/wind/bastankhah14.py +6 -4
  88. foxes/models/wake_models/wind/bastankhah16.py +9 -9
  89. foxes/models/wake_models/wind/jensen.py +3 -2
  90. foxes/models/wake_models/wind/turbopark.py +14 -11
  91. foxes/models/wake_superpositions/ti_linear.py +1 -1
  92. foxes/models/wake_superpositions/ti_max.py +1 -1
  93. foxes/models/wake_superpositions/ti_pow.py +1 -1
  94. foxes/models/wake_superpositions/ti_quadratic.py +1 -1
  95. foxes/models/wake_superpositions/ws_linear.py +8 -7
  96. foxes/models/wake_superpositions/ws_max.py +8 -7
  97. foxes/models/wake_superpositions/ws_pow.py +8 -7
  98. foxes/models/wake_superpositions/ws_product.py +5 -5
  99. foxes/models/wake_superpositions/ws_quadratic.py +8 -7
  100. foxes/output/farm_layout.py +14 -10
  101. foxes/output/farm_results_eval.py +1 -1
  102. foxes/output/grids.py +1 -1
  103. foxes/output/results_writer.py +2 -2
  104. foxes/output/rose_plot.py +3 -3
  105. foxes/output/seq_plugins/seq_flow_ani_plugin.py +2 -2
  106. foxes/output/seq_plugins/seq_wake_debug_plugin.py +2 -2
  107. foxes/output/state_turbine_map.py +1 -1
  108. foxes/utils/abl/neutral.py +2 -2
  109. foxes/utils/abl/stable.py +2 -2
  110. foxes/utils/abl/unstable.py +2 -2
  111. foxes/utils/data_book.py +1 -1
  112. foxes/utils/dict.py +23 -0
  113. foxes/utils/exec_python.py +1 -1
  114. foxes/utils/factory.py +29 -1
  115. foxes/utils/geom2d/circle.py +1 -1
  116. foxes/utils/geom2d/polygon.py +1 -1
  117. foxes/utils/geopandas_utils.py +2 -2
  118. foxes/utils/load.py +2 -2
  119. foxes/utils/pandas_helpers.py +1 -1
  120. foxes/utils/xarray_utils.py +1 -1
  121. foxes/variables.py +3 -3
  122. {foxes-1.0.dist-info → foxes-1.1.1.dist-info}/METADATA +8 -6
  123. {foxes-1.0.dist-info → foxes-1.1.1.dist-info}/RECORD +127 -125
  124. {foxes-1.0.dist-info → foxes-1.1.1.dist-info}/WHEEL +1 -1
  125. foxes/utils/geopandas_helpers.py +0 -294
  126. /examples/{induction_RHB → induction}/run.py +0 -0
  127. {foxes-1.0.dist-info → foxes-1.1.1.dist-info}/LICENSE +0 -0
  128. {foxes-1.0.dist-info → foxes-1.1.1.dist-info}/top_level.txt +0 -0
@@ -37,11 +37,11 @@ class WsTI2PCtFromTwo(TurbineType):
37
37
  rpars_ct: dict, optional
38
38
  Parameters for pandas ct file reading
39
39
  ipars_P: dict, optional
40
- Parameters for scipy.interpolate.interpn()
40
+ Parameters for scipy.interpolate.interpn
41
41
  ipars_ct: dict, optional
42
- Parameters for scipy.interpolate.interpn()
42
+ Parameters for scipy.interpolate.interpn
43
43
  rho: float
44
- The air densitiy for which the data is valid
44
+ The air density for which the data is valid
45
45
  or None for no correction
46
46
 
47
47
  :group: models.turbine_types
@@ -73,7 +73,7 @@ class WsTI2PCtFromTwo(TurbineType):
73
73
  data_source_ct: str or pandas.DataFrame
74
74
  The file path for the ct curve, static name, or data
75
75
  rho: float, optional
76
- The air densitiy for which the data is valid
76
+ The air density for which the data is valid
77
77
  or None for no correction
78
78
  p_ct: float
79
79
  The exponent for yaw dependency of ct
@@ -88,9 +88,9 @@ class WsTI2PCtFromTwo(TurbineType):
88
88
  pd_file_read_pars_ct: dict
89
89
  Parameters for pandas ct file reading
90
90
  interpn_pars_P: dict, optional
91
- Parameters for scipy.interpolate.interpn()
91
+ Parameters for scipy.interpolate.interpn
92
92
  interpn_pars_ct: dict, optional
93
- Parameters for scipy.interpolate.interpn()
93
+ Parameters for scipy.interpolate.interpn
94
94
  parameters: dict, optional
95
95
  Additional parameters for TurbineType class
96
96
 
@@ -129,7 +129,7 @@ class WsTI2PCtFromTwo(TurbineType):
129
129
 
130
130
  def needs_rews2(self):
131
131
  """
132
- Returns flag for requirering REWS2 variable
132
+ Returns flag for requiring REWS2 variable
133
133
 
134
134
  Returns
135
135
  -------
@@ -141,7 +141,7 @@ class WsTI2PCtFromTwo(TurbineType):
141
141
 
142
142
  def needs_rews3(self):
143
143
  """
144
- Returns flag for requirering REWS3 variable
144
+ Returns flag for requiring REWS3 variable
145
145
 
146
146
  Returns
147
147
  -------
@@ -241,7 +241,7 @@ class WsTI2PCtFromTwo(TurbineType):
241
241
  print()
242
242
 
243
243
  def calculate(self, algo, mdata, fdata, st_sel):
244
- """ "
244
+ """
245
245
  The main model calculation.
246
246
 
247
247
  This function is executed on a single chunk of data,
@@ -1,5 +1,5 @@
1
1
  """
2
- Verctical profile models.
2
+ Vertical profile models.
3
3
  """
4
4
 
5
5
  from .uniform import UniformProfile
@@ -120,7 +120,7 @@ class DynamicWakes(WakeFrame):
120
120
  self.UPDATE = self.var("update")
121
121
 
122
122
  def calc_order(self, algo, mdata, fdata):
123
- """ "
123
+ """
124
124
  Calculates the order of turbine evaluation.
125
125
 
126
126
  This function is executed on a single chunk of data,
@@ -347,7 +347,7 @@ class DynamicWakes(WakeFrame):
347
347
  The target point data
348
348
  downwind_index: int
349
349
  The index of the wake causing turbine
350
- in the downwnd order
350
+ in the downwind order
351
351
 
352
352
  Returns
353
353
  -------
@@ -69,7 +69,7 @@ class FarmOrder(WakeFrame):
69
69
  return [self.base_frame]
70
70
 
71
71
  def calc_order(self, algo, mdata, fdata):
72
- """ "
72
+ """
73
73
  Calculates the order of turbine evaluation.
74
74
 
75
75
  This function is executed on a single chunk of data,
@@ -118,7 +118,7 @@ class FarmOrder(WakeFrame):
118
118
  The target point data
119
119
  downwind_index: int
120
120
  The index of the wake causing turbine
121
- in the downwnd order
121
+ in the downwind order
122
122
 
123
123
  Returns
124
124
  -------
@@ -36,7 +36,7 @@ class RotorWD(WakeFrame):
36
36
  self.var_wd = var_wd
37
37
 
38
38
  def calc_order(self, algo, mdata, fdata):
39
- """ "
39
+ """
40
40
  Calculates the order of turbine evaluation.
41
41
 
42
42
  This function is executed on a single chunk of data,
@@ -86,7 +86,7 @@ class RotorWD(WakeFrame):
86
86
  The target point data
87
87
  downwind_index: int
88
88
  The index of the wake causing turbine
89
- in the downwnd order
89
+ in the downwind order
90
90
 
91
91
  Returns
92
92
  -------
@@ -96,7 +96,7 @@ class SeqDynamicWakes(FarmOrder):
96
96
  )
97
97
 
98
98
  def calc_order(self, algo, mdata, fdata):
99
- """ "
99
+ """
100
100
  Calculates the order of turbine evaluation.
101
101
 
102
102
  This function is executed on a single chunk of data,
@@ -142,7 +142,7 @@ class SeqDynamicWakes(FarmOrder):
142
142
  The target point data
143
143
  downwind_index: int
144
144
  The index of the wake causing turbine
145
- in the downwnd order
145
+ in the downwind order
146
146
 
147
147
  Returns
148
148
  -------
@@ -230,7 +230,6 @@ class SeqDynamicWakes(FarmOrder):
230
230
  tdata,
231
231
  target,
232
232
  states0=None,
233
- upcast=False,
234
233
  ):
235
234
  """
236
235
  Return data that is required for computing the
@@ -253,17 +252,12 @@ class SeqDynamicWakes(FarmOrder):
253
252
  FC.STATE_TARGET, FC.STATE_TARGET_TPOINT
254
253
  states0: numpy.ndarray, optional
255
254
  The states of wake creation
256
- upcast: bool
257
- Flag for ensuring targets dimension,
258
- otherwise dimension 1 is entered
259
255
 
260
256
  Returns
261
257
  -------
262
258
  data: numpy.ndarray
263
259
  Data for wake modelling, shape:
264
260
  (n_states, n_turbines) or (n_states, n_target)
265
- dims: tuple
266
- The data dimensions
267
261
 
268
262
  """
269
263
  if states0 is None and FC.STATE_SOURCE_ORDERI in tdata:
@@ -288,9 +282,9 @@ class SeqDynamicWakes(FarmOrder):
288
282
  data = data[:, :, 0]
289
283
  else:
290
284
  data = np.einsum("stp,p->st", data, tdata[FC.TWEIGHTS])
291
- return data, (FC.STATE, FC.TARGET)
285
+ return data
292
286
  elif target == FC.STATE_TARGET_TPOINT:
293
- return data, (FC.STATE, FC.TARGET, FC.TPOINT)
287
+ return data
294
288
  else:
295
289
  raise ValueError(
296
290
  f"Cannot handle target '{target}', choices are {FC.STATE_TARGET}, {FC.STATE_TARGET_TPOINT}"
@@ -298,7 +292,7 @@ class SeqDynamicWakes(FarmOrder):
298
292
 
299
293
  else:
300
294
  return super().get_wake_modelling_data(
301
- algo, variable, downwind_index, fdata, tdata, target, states0, upcast
295
+ algo, variable, downwind_index, fdata, tdata, target, states0
302
296
  )
303
297
 
304
298
  def get_centreline_points(self, algo, mdata, fdata, downwind_index, x):
@@ -165,7 +165,7 @@ class Streamlines2D(WakeFrame):
165
165
  return coos.reshape(n_states, n_targets, n_tpoints, 3)
166
166
 
167
167
  def calc_order(self, algo, mdata, fdata):
168
- """ "
168
+ """
169
169
  Calculates the order of turbine evaluation.
170
170
 
171
171
  This function is executed on a single chunk of data,
@@ -230,7 +230,7 @@ class Streamlines2D(WakeFrame):
230
230
  The target point data
231
231
  downwind_index: int
232
232
  The index of the wake causing turbine
233
- in the downwnd order
233
+ in the downwind order
234
234
 
235
235
  Returns
236
236
  -------
@@ -264,7 +264,7 @@ class Timelines(WakeFrame):
264
264
  self.timelines_data = data.pop("data")
265
265
 
266
266
  def calc_order(self, algo, mdata, fdata):
267
- """ "
267
+ """
268
268
  Calculates the order of turbine evaluation.
269
269
 
270
270
  This function is executed on a single chunk of data,
@@ -312,7 +312,7 @@ class Timelines(WakeFrame):
312
312
  The target point data
313
313
  downwind_index: int
314
314
  The index of the wake causing turbine
315
- in the downwnd order
315
+ in the downwind order
316
316
 
317
317
  Returns
318
318
  -------
@@ -137,7 +137,7 @@ class YawedWakes(WakeFrame):
137
137
  super().initialize(algo, verbosity, force)
138
138
 
139
139
  def calc_order(self, algo, mdata, fdata):
140
- """ "
140
+ """
141
141
  Calculates the order of turbine evaluation.
142
142
 
143
143
  This function is executed on a single chunk of data,
@@ -177,7 +177,7 @@ class YawedWakes(WakeFrame):
177
177
  downwind_index=downwind_index,
178
178
  accept_nan=False,
179
179
  )
180
- gamma *= np.pi / 180
180
+ gamma = gamma * np.pi / 180
181
181
 
182
182
  # get k:
183
183
  k = self.wake_k(
@@ -248,7 +248,7 @@ class YawedWakes(WakeFrame):
248
248
  The target point data
249
249
  downwind_index: int
250
250
  The index of the wake causing turbine
251
- in the downwnd order
251
+ in the downwind order
252
252
 
253
253
  Returns
254
254
  -------
@@ -142,7 +142,7 @@ class DistSlicedWakeModel(WakeModel):
142
142
  The target point data
143
143
  downwind_index: int
144
144
  The index of the wake causing turbine
145
- in the downwnd order
145
+ in the downwind order
146
146
  wake_coos: numpy.ndarray
147
147
  The wake frame coordinates of the evaluation
148
148
  points, shape: (n_states, n_targets, n_tpoints, 3)
@@ -134,7 +134,7 @@ class RankineHalfBody(TurbineInductionModel):
134
134
  The target point data
135
135
  downwind_index: int
136
136
  The index of the wake causing turbine
137
- in the downwnd order
137
+ in the downwind order
138
138
  wake_coos: numpy.ndarray
139
139
  The wake frame coordinates of the evaluation
140
140
  points, shape: (n_states, n_targets, n_tpoints, 3)
@@ -31,27 +31,35 @@ class Rathmann(TurbineInductionModel):
31
31
 
32
32
  """
33
33
 
34
- def __init__(self, pre_rotor_only=False, induction="Madsen"):
34
+ def __init__(
35
+ self,
36
+ superposition="ws_linear",
37
+ induction="Madsen",
38
+ pre_rotor_only=False,
39
+ ):
35
40
  """
36
41
  Constructor.
37
42
 
38
43
  Parameters
39
44
  ----------
40
- pre_rotor_only: bool
41
- Calculate only the pre-rotor region
45
+ superposition: str
46
+ The wind speed superposition
42
47
  induction: foxes.core.AxialInductionModel or str
43
48
  The induction model
49
+ pre_rotor_only: bool
50
+ Calculate only the pre-rotor region
44
51
 
45
52
  """
46
53
  super().__init__()
47
54
  self.induction = induction
48
55
  self.pre_rotor_only = pre_rotor_only
56
+ self._superp_name = superposition
49
57
 
50
58
  def __repr__(self):
51
59
  iname = (
52
60
  self.induction if isinstance(self.induction, str) else self.induction.name
53
61
  )
54
- return f"{type(self).__name__}(induction={iname})"
62
+ return f"{type(self).__name__}({self._superp_name}, induction={iname})"
55
63
 
56
64
  def sub_models(self):
57
65
  """
@@ -63,7 +71,7 @@ class Rathmann(TurbineInductionModel):
63
71
  All sub models
64
72
 
65
73
  """
66
- return [self.induction]
74
+ return [self._superp, self.induction]
67
75
 
68
76
  def initialize(self, algo, verbosity=0, force=False):
69
77
  """
@@ -79,6 +87,7 @@ class Rathmann(TurbineInductionModel):
79
87
  Overwrite existing data
80
88
 
81
89
  """
90
+ self._superp = algo.mbook.wake_superpositions[self._superp_name]
82
91
  if isinstance(self.induction, str):
83
92
  self.induction = algo.mbook.axial_induction[self.induction]
84
93
  super().initialize(algo, verbosity, force)
@@ -133,7 +142,7 @@ class Rathmann(TurbineInductionModel):
133
142
  The target point data
134
143
  downwind_index: int
135
144
  The index of the wake causing turbine
136
- in the downwnd order
145
+ in the downwind order
137
146
  wake_coos: numpy.ndarray
138
147
  The wake frame coordinates of the evaluation
139
148
  points, shape: (n_states, n_targets, n_tpoints, 3)
@@ -155,18 +164,6 @@ class Rathmann(TurbineInductionModel):
155
164
  downwind_index=downwind_index,
156
165
  )
157
166
 
158
- # get ws:
159
- ws = self.get_data(
160
- FV.REWS,
161
- FC.STATE_TARGET_TPOINT,
162
- lookup="w",
163
- algo=algo,
164
- fdata=fdata,
165
- tdata=tdata,
166
- upcast=True,
167
- downwind_index=downwind_index,
168
- )
169
-
170
167
  # get D
171
168
  R = 0.5 * self.get_data(
172
169
  FV.D,
@@ -203,8 +200,19 @@ class Rathmann(TurbineInductionModel):
203
200
  if np.any(sp_sel):
204
201
  xr = x_R[sp_sel]
205
202
  a = self.induction.ct2a(ct[sp_sel])
206
- blockage = ws[sp_sel] * a * mu(xr) * G(xr, r_R[sp_sel]) # eqn 10
207
- wake_deltas[FV.WS][sp_sel] += -blockage
203
+ blockage = a * mu(xr) * G(xr, r_R[sp_sel]) # eqn 10
204
+
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
+ )
208
216
 
209
217
  # ws delta behind rotor
210
218
  if not self.pre_rotor_only:
@@ -213,7 +221,53 @@ class Rathmann(TurbineInductionModel):
213
221
  if np.any(sp_sel):
214
222
  xr = x_R[sp_sel]
215
223
  a = self.induction.ct2a(ct[sp_sel])
216
- blockage = ws[sp_sel] * a * mu(-xr) * G(-xr, r_R[sp_sel]) # eqn 10
217
- wake_deltas[FV.WS][sp_sel] += blockage
224
+ blockage = a * mu(-xr) * G(-xr, r_R[sp_sel]) # eqn 10
225
+
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
+ )
218
237
 
219
238
  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
+ )
@@ -38,30 +38,39 @@ class SelfSimilar(TurbineInductionModel):
38
38
 
39
39
  """
40
40
 
41
- def __init__(self, pre_rotor_only=False, induction="Madsen", gamma=1.1):
41
+ def __init__(
42
+ self,
43
+ superposition="ws_linear",
44
+ induction="Madsen",
45
+ gamma=1.1,
46
+ pre_rotor_only=False,
47
+ ):
42
48
  """
43
49
  Constructor.
44
50
 
45
51
  Parameters
46
52
  ----------
47
- pre_rotor_only: bool
48
- Calculate only the pre-rotor region
53
+ superposition: str
54
+ The wind speed superposition.
49
55
  induction: foxes.core.AxialInductionModel or str
50
56
  The induction model
51
57
  gamma: float, default=1.1
52
58
  The parameter that multiplies Ct in the ct2a calculation
59
+ pre_rotor_only: bool
60
+ Calculate only the pre-rotor region
53
61
 
54
62
  """
55
63
  super().__init__()
56
64
  self.induction = induction
57
65
  self.pre_rotor_only = pre_rotor_only
58
66
  self.gamma = gamma
67
+ self._superp_name = superposition
59
68
 
60
69
  def __repr__(self):
61
70
  iname = (
62
71
  self.induction if isinstance(self.induction, str) else self.induction.name
63
72
  )
64
- return f"{type(self).__name__}(gamma={self.gamma}, induction={iname})"
73
+ return f"{type(self).__name__}({self._superp_name}, induction={iname}, gamma={self.gamma})"
65
74
 
66
75
  def sub_models(self):
67
76
  """
@@ -73,7 +82,7 @@ class SelfSimilar(TurbineInductionModel):
73
82
  All sub models
74
83
 
75
84
  """
76
- return [self.induction]
85
+ return [self._superp, self.induction]
77
86
 
78
87
  def initialize(self, algo, verbosity=0, force=False):
79
88
  """
@@ -89,6 +98,7 @@ class SelfSimilar(TurbineInductionModel):
89
98
  Overwrite existing data
90
99
 
91
100
  """
101
+ self._superp = algo.mbook.wake_superpositions[self._superp_name]
92
102
  if isinstance(self.induction, str):
93
103
  self.induction = algo.mbook.axial_induction[self.induction]
94
104
  super().initialize(algo, verbosity, force)
@@ -163,7 +173,7 @@ class SelfSimilar(TurbineInductionModel):
163
173
  The target point data
164
174
  downwind_index: int
165
175
  The index of the wake causing turbine
166
- in the downwnd order
176
+ in the downwind order
167
177
  wake_coos: numpy.ndarray
168
178
  The wake frame coordinates of the evaluation
169
179
  points, shape: (n_states, n_targets, n_tpoints, 3)
@@ -185,18 +195,6 @@ class SelfSimilar(TurbineInductionModel):
185
195
  downwind_index=downwind_index,
186
196
  )
187
197
 
188
- # get ws
189
- ws = self.get_data(
190
- FV.REWS,
191
- FC.STATE_TARGET_TPOINT,
192
- lookup="w",
193
- algo=algo,
194
- fdata=fdata,
195
- tdata=tdata,
196
- upcast=True,
197
- downwind_index=downwind_index,
198
- )
199
-
200
198
  # get R
201
199
  R = 0.5 * self.get_data(
202
200
  FV.D,
@@ -218,10 +216,19 @@ class SelfSimilar(TurbineInductionModel):
218
216
  if np.any(sp_sel):
219
217
  # velocity eqn 10 from [1]
220
218
  xr = x_R[sp_sel]
221
- blockage = (
222
- ws[sp_sel] * self._a(ct[sp_sel], xr) * self._rad_fn(xr, r_R[sp_sel])
219
+ blockage = self._a(ct[sp_sel], xr) * self._rad_fn(xr, r_R[sp_sel])
220
+
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,
223
231
  )
224
- wake_deltas[FV.WS][sp_sel] -= blockage
225
232
 
226
233
  # set area behind to mirrored value EXCEPT for area behind turbine
227
234
  if not self.pre_rotor_only:
@@ -229,11 +236,54 @@ class SelfSimilar(TurbineInductionModel):
229
236
  if np.any(sp_sel):
230
237
  # velocity eqn 10 from [1]
231
238
  xr = x_R[sp_sel]
232
- blockage = (
233
- ws[sp_sel]
234
- * self._a(ct[sp_sel], -xr)
235
- * self._rad_fn(-xr, r_R[sp_sel])
239
+ blockage = self._a(ct[sp_sel], -xr) * self._rad_fn(-xr, r_R[sp_sel])
240
+
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,
236
252
  )
237
- wake_deltas[FV.WS][sp_sel] += blockage
238
253
 
239
254
  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
+ )