foxes 1.3__py3-none-any.whl → 1.5__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 (228) 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/field_data_nc/run.py +1 -1
  7. examples/induction/run.py +3 -3
  8. examples/multi_height/run.py +1 -1
  9. examples/power_mask/run.py +2 -2
  10. examples/quickstart/run.py +0 -1
  11. examples/random_timeseries/run.py +3 -4
  12. examples/scan_row/run.py +3 -3
  13. examples/sequential/run.py +33 -10
  14. examples/single_state/run.py +3 -4
  15. examples/states_lookup_table/run.py +3 -3
  16. examples/streamline_wakes/run.py +29 -6
  17. examples/tab_file/run.py +3 -3
  18. examples/timelines/run.py +29 -5
  19. examples/timeseries/run.py +3 -3
  20. examples/timeseries_slurm/run.py +3 -3
  21. examples/wind_rose/run.py +3 -3
  22. examples/yawed_wake/run.py +19 -9
  23. foxes/__init__.py +21 -17
  24. foxes/algorithms/__init__.py +6 -6
  25. foxes/algorithms/downwind/__init__.py +2 -2
  26. foxes/algorithms/downwind/downwind.py +49 -17
  27. foxes/algorithms/downwind/models/__init__.py +6 -6
  28. foxes/algorithms/downwind/models/farm_wakes_calc.py +11 -9
  29. foxes/algorithms/downwind/models/init_farm_data.py +58 -29
  30. foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
  31. foxes/algorithms/downwind/models/set_amb_farm_results.py +1 -1
  32. foxes/algorithms/downwind/models/set_amb_point_results.py +6 -6
  33. foxes/algorithms/iterative/__init__.py +7 -3
  34. foxes/algorithms/iterative/iterative.py +1 -2
  35. foxes/algorithms/iterative/models/__init__.py +7 -3
  36. foxes/algorithms/iterative/models/farm_wakes_calc.py +9 -5
  37. foxes/algorithms/sequential/__init__.py +3 -3
  38. foxes/algorithms/sequential/models/__init__.py +2 -2
  39. foxes/algorithms/sequential/sequential.py +3 -4
  40. foxes/config/__init__.py +5 -1
  41. foxes/constants.py +16 -0
  42. foxes/core/__init__.py +45 -22
  43. foxes/core/algorithm.py +5 -6
  44. foxes/core/data.py +94 -22
  45. foxes/core/data_calc_model.py +4 -2
  46. foxes/core/engine.py +42 -53
  47. foxes/core/farm_controller.py +2 -2
  48. foxes/core/farm_data_model.py +16 -13
  49. foxes/core/ground_model.py +4 -13
  50. foxes/core/model.py +24 -6
  51. foxes/core/partial_wakes_model.py +147 -10
  52. foxes/core/point_data_model.py +21 -17
  53. foxes/core/rotor_model.py +4 -3
  54. foxes/core/states.py +2 -3
  55. foxes/core/turbine.py +2 -1
  56. foxes/core/wake_deflection.py +130 -0
  57. foxes/core/wake_model.py +222 -9
  58. foxes/core/wake_superposition.py +122 -4
  59. foxes/core/wind_farm.py +6 -6
  60. foxes/data/__init__.py +7 -2
  61. foxes/data/states/point_cloud_100.nc +0 -0
  62. foxes/data/states/weibull_cloud_4.nc +0 -0
  63. foxes/data/states/weibull_grid.nc +0 -0
  64. foxes/data/states/weibull_sectors_12.csv +13 -0
  65. foxes/data/states/weibull_sectors_12.nc +0 -0
  66. foxes/engines/__init__.py +14 -15
  67. foxes/engines/dask.py +42 -20
  68. foxes/engines/default.py +2 -2
  69. foxes/engines/numpy.py +11 -13
  70. foxes/engines/pool.py +20 -11
  71. foxes/engines/single.py +8 -6
  72. foxes/input/__init__.py +3 -3
  73. foxes/input/farm_layout/__init__.py +9 -8
  74. foxes/input/farm_layout/from_arrays.py +68 -0
  75. foxes/input/farm_layout/from_csv.py +1 -1
  76. foxes/input/farm_layout/ring.py +0 -1
  77. foxes/input/states/__init__.py +28 -12
  78. foxes/input/states/create/__init__.py +3 -2
  79. foxes/input/states/dataset_states.py +710 -0
  80. foxes/input/states/field_data.py +531 -0
  81. foxes/input/states/multi_height.py +11 -6
  82. foxes/input/states/one_point_flow.py +1 -4
  83. foxes/input/states/point_cloud_data.py +618 -0
  84. foxes/input/states/scan.py +2 -0
  85. foxes/input/states/single.py +3 -1
  86. foxes/input/states/states_table.py +23 -30
  87. foxes/input/states/weibull_sectors.py +330 -0
  88. foxes/input/states/wrg_states.py +8 -6
  89. foxes/input/yaml/__init__.py +9 -3
  90. foxes/input/yaml/dict.py +42 -41
  91. foxes/input/yaml/windio/__init__.py +10 -5
  92. foxes/input/yaml/windio/read_attributes.py +42 -29
  93. foxes/input/yaml/windio/read_farm.py +17 -15
  94. foxes/input/yaml/windio/read_fields.py +4 -2
  95. foxes/input/yaml/windio/read_outputs.py +25 -15
  96. foxes/input/yaml/windio/read_site.py +172 -11
  97. foxes/input/yaml/windio/windio.py +23 -11
  98. foxes/input/yaml/yaml.py +1 -0
  99. foxes/models/__init__.py +15 -14
  100. foxes/models/axial_induction/__init__.py +2 -2
  101. foxes/models/farm_controllers/__init__.py +1 -1
  102. foxes/models/farm_models/__init__.py +1 -1
  103. foxes/models/ground_models/__init__.py +3 -2
  104. foxes/models/ground_models/wake_mirror.py +3 -3
  105. foxes/models/model_book.py +190 -63
  106. foxes/models/partial_wakes/__init__.py +6 -6
  107. foxes/models/partial_wakes/axiwake.py +30 -5
  108. foxes/models/partial_wakes/centre.py +47 -0
  109. foxes/models/partial_wakes/rotor_points.py +41 -11
  110. foxes/models/partial_wakes/segregated.py +2 -25
  111. foxes/models/partial_wakes/top_hat.py +27 -2
  112. foxes/models/point_models/__init__.py +4 -4
  113. foxes/models/rotor_models/__init__.py +4 -3
  114. foxes/models/rotor_models/centre.py +1 -1
  115. foxes/models/rotor_models/direct_infusion.py +241 -0
  116. foxes/models/turbine_models/__init__.py +11 -11
  117. foxes/models/turbine_models/calculator.py +16 -3
  118. foxes/models/turbine_models/kTI_model.py +1 -0
  119. foxes/models/turbine_models/lookup_table.py +2 -0
  120. foxes/models/turbine_models/power_mask.py +1 -0
  121. foxes/models/turbine_models/rotor_centre_calc.py +2 -0
  122. foxes/models/turbine_models/sector_management.py +1 -0
  123. foxes/models/turbine_models/set_farm_vars.py +3 -9
  124. foxes/models/turbine_models/table_factors.py +2 -0
  125. foxes/models/turbine_models/thrust2ct.py +1 -0
  126. foxes/models/turbine_models/yaw2yawm.py +2 -0
  127. foxes/models/turbine_models/yawm2yaw.py +2 -0
  128. foxes/models/turbine_types/PCt_file.py +2 -6
  129. foxes/models/turbine_types/PCt_from_two.py +1 -2
  130. foxes/models/turbine_types/__init__.py +10 -9
  131. foxes/models/turbine_types/calculator_type.py +123 -0
  132. foxes/models/turbine_types/null_type.py +1 -0
  133. foxes/models/turbine_types/wsrho2PCt_from_two.py +2 -0
  134. foxes/models/turbine_types/wsti2PCt_from_two.py +3 -1
  135. foxes/models/vertical_profiles/__init__.py +7 -7
  136. foxes/models/wake_deflections/__init__.py +3 -0
  137. foxes/models/{wake_frames/yawed_wakes.py → wake_deflections/bastankhah2016.py} +32 -111
  138. foxes/models/wake_deflections/jimenez.py +277 -0
  139. foxes/models/wake_deflections/no_deflection.py +94 -0
  140. foxes/models/wake_frames/__init__.py +6 -7
  141. foxes/models/wake_frames/dynamic_wakes.py +12 -3
  142. foxes/models/wake_frames/rotor_wd.py +3 -1
  143. foxes/models/wake_frames/seq_dynamic_wakes.py +41 -7
  144. foxes/models/wake_frames/streamlines.py +8 -6
  145. foxes/models/wake_frames/timelines.py +9 -3
  146. foxes/models/wake_models/__init__.py +7 -7
  147. foxes/models/wake_models/dist_sliced.py +50 -84
  148. foxes/models/wake_models/gaussian.py +20 -0
  149. foxes/models/wake_models/induction/__init__.py +5 -5
  150. foxes/models/wake_models/induction/rankine_half_body.py +30 -71
  151. foxes/models/wake_models/induction/rathmann.py +65 -64
  152. foxes/models/wake_models/induction/self_similar.py +65 -68
  153. foxes/models/wake_models/induction/self_similar2020.py +0 -3
  154. foxes/models/wake_models/induction/vortex_sheet.py +71 -75
  155. foxes/models/wake_models/ti/__init__.py +2 -2
  156. foxes/models/wake_models/ti/crespo_hernandez.py +5 -3
  157. foxes/models/wake_models/ti/iec_ti.py +6 -4
  158. foxes/models/wake_models/top_hat.py +58 -7
  159. foxes/models/wake_models/wind/__init__.py +6 -4
  160. foxes/models/wake_models/wind/bastankhah14.py +25 -7
  161. foxes/models/wake_models/wind/bastankhah16.py +35 -3
  162. foxes/models/wake_models/wind/jensen.py +15 -2
  163. foxes/models/wake_models/wind/turbopark.py +28 -2
  164. foxes/models/wake_superpositions/__init__.py +18 -9
  165. foxes/models/wake_superpositions/ti_linear.py +4 -4
  166. foxes/models/wake_superpositions/ti_max.py +4 -4
  167. foxes/models/wake_superpositions/ti_pow.py +4 -4
  168. foxes/models/wake_superpositions/ti_quadratic.py +4 -4
  169. foxes/models/wake_superpositions/wind_vector.py +257 -0
  170. foxes/models/wake_superpositions/ws_linear.py +9 -10
  171. foxes/models/wake_superpositions/ws_max.py +8 -8
  172. foxes/models/wake_superpositions/ws_pow.py +8 -8
  173. foxes/models/wake_superpositions/ws_product.py +4 -4
  174. foxes/models/wake_superpositions/ws_quadratic.py +8 -8
  175. foxes/output/__init__.py +21 -19
  176. foxes/output/farm_layout.py +4 -2
  177. foxes/output/farm_results_eval.py +19 -16
  178. foxes/output/flow_plots_2d/__init__.py +2 -2
  179. foxes/output/flow_plots_2d/flow_plots.py +18 -0
  180. foxes/output/flow_plots_2d/get_fig.py +5 -2
  181. foxes/output/output.py +6 -1
  182. foxes/output/results_writer.py +1 -1
  183. foxes/output/rose_plot.py +13 -3
  184. foxes/output/rotor_point_plots.py +3 -0
  185. foxes/output/seq_plugins/__init__.py +2 -2
  186. foxes/output/seq_plugins/seq_flow_ani_plugin.py +0 -3
  187. foxes/output/seq_plugins/seq_wake_debug_plugin.py +0 -1
  188. foxes/output/state_turbine_map.py +3 -0
  189. foxes/output/turbine_type_curves.py +10 -8
  190. foxes/utils/__init__.py +37 -19
  191. foxes/utils/abl/__init__.py +4 -4
  192. foxes/utils/cubic_roots.py +1 -1
  193. foxes/utils/data_book.py +4 -3
  194. foxes/utils/dict.py +49 -37
  195. foxes/utils/exec_python.py +5 -5
  196. foxes/utils/factory.py +3 -5
  197. foxes/utils/geom2d/__init__.py +7 -5
  198. foxes/utils/geopandas_utils.py +2 -2
  199. foxes/utils/pandas_utils.py +4 -3
  200. foxes/utils/tab_files.py +0 -1
  201. foxes/utils/weibull.py +28 -0
  202. foxes/utils/wrg_utils.py +3 -1
  203. foxes/utils/xarray_utils.py +9 -2
  204. foxes/variables.py +67 -9
  205. {foxes-1.3.dist-info → foxes-1.5.dist-info}/METADATA +34 -63
  206. foxes-1.5.dist-info/RECORD +328 -0
  207. {foxes-1.3.dist-info → foxes-1.5.dist-info}/WHEEL +1 -1
  208. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +2 -3
  209. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +1 -1
  210. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
  211. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +0 -1
  212. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +0 -2
  213. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +0 -1
  214. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +0 -1
  215. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +0 -1
  216. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +0 -1
  217. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +0 -1
  218. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
  219. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +0 -1
  220. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +0 -1
  221. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +0 -1
  222. foxes/input/states/field_data_nc.py +0 -847
  223. foxes/output/round.py +0 -10
  224. foxes/utils/pandas_helpers.py +0 -178
  225. foxes-1.3.dist-info/RECORD +0 -313
  226. {foxes-1.3.dist-info → foxes-1.5.dist-info}/entry_points.txt +0 -0
  227. {foxes-1.3.dist-info → foxes-1.5.dist-info/licenses}/LICENSE +0 -0
  228. {foxes-1.3.dist-info → foxes-1.5.dist-info}/top_level.txt +0 -0
@@ -17,6 +17,7 @@ from foxes.core import (
17
17
  AxialInductionModel,
18
18
  TurbineInductionModel,
19
19
  GroundModel,
20
+ WakeDeflection,
20
21
  )
21
22
 
22
23
 
@@ -62,6 +63,9 @@ class ModelBook:
62
63
  ground_models: foxes.utils.FDict
63
64
  The ground models. Keys: model name str,
64
65
  values: foxes.core.GroundModel
66
+ wake_deflections: foxes.utils.FDict
67
+ The wake deflection models. Keys: model name str,
68
+ values: foxes.core.WakeDeflection
65
69
  sources: foxes.utils.FDict
66
70
  All sources dict
67
71
  base_classes: foxes.utils.FDict
@@ -81,12 +85,13 @@ class ModelBook:
81
85
  Path to power/ct curve file, for creation
82
86
  of default turbine type model
83
87
  """
84
- self.point_models = FDict(name="point_models")
88
+ self.point_models = FDict(_name="point_models")
85
89
  self.point_models["tke2ti"] = fm.point_models.TKE2TI()
86
90
  self.point_models["ustar2ti"] = fm.point_models.Ustar2TI()
87
91
 
88
- self.rotor_models = FDict(name="rotor_models")
92
+ self.rotor_models = FDict(_name="rotor_models")
89
93
  self.rotor_models["centre"] = fm.rotor_models.CentreRotor()
94
+ self.rotor_models["direct_mdata"] = fm.rotor_models.DirectMDataInfusion()
90
95
 
91
96
  def _n2n(n2):
92
97
  n2 = float(n2)
@@ -128,7 +133,7 @@ class ModelBook:
128
133
  hints={"n": "(Number of vertical levels)"},
129
134
  )
130
135
 
131
- self.turbine_types = FDict(name="turbine_types")
136
+ self.turbine_types = FDict(_name="turbine_types")
132
137
  self.turbine_types["null_type"] = fm.turbine_types.NullType()
133
138
  self.turbine_types["NREL5MW"] = fm.turbine_types.PCtFile(
134
139
  "NREL-5MW-D126-H90.csv", rho=1.225
@@ -146,7 +151,7 @@ class ModelBook:
146
151
  self.turbine_types["Pct"] = fm.turbine_types.PCtFile(Pct_file)
147
152
 
148
153
  self.turbine_models = FDict(
149
- name="turbine_models",
154
+ _name="turbine_models",
150
155
  kTI=fm.turbine_models.kTI(),
151
156
  kTI_amb=fm.turbine_models.kTI(ti_var=FV.AMB_TI),
152
157
  thrust2ct=fm.turbine_models.Thrust2Ct(),
@@ -199,7 +204,7 @@ class ModelBook:
199
204
  )
200
205
 
201
206
  self.farm_models = FDict(
202
- name="farm_models",
207
+ _name="farm_models",
203
208
  **{
204
209
  f"farm_{mname}": fm.farm_models.Turbine2FarmModel(m)
205
210
  for mname, m in self.turbine_models.items()
@@ -207,12 +212,12 @@ class ModelBook:
207
212
  )
208
213
 
209
214
  self.farm_controllers = FDict(
210
- name="farm_controllers",
215
+ _name="farm_controllers",
211
216
  basic_ctrl=fm.farm_controllers.BasicFarmController(),
212
217
  )
213
218
 
214
219
  self.partial_wakes = FDict(
215
- name="partial_wakes",
220
+ _name="partial_wakes",
216
221
  rotor_points=fm.partial_wakes.RotorPoints(),
217
222
  top_hat=fm.partial_wakes.PartialTopHat(),
218
223
  centre=fm.partial_wakes.PartialCentre(),
@@ -231,15 +236,78 @@ class ModelBook:
231
236
  hints={"n2": "(Number of points in square grid)"},
232
237
  )
233
238
 
239
+ self.wake_deflections = FDict(
240
+ _name="wake_deflections",
241
+ no_deflection=fm.wake_deflections.NoDeflection(),
242
+ Bastankhah2016=fm.wake_deflections.Bastankhah2016Deflection(),
243
+ Jimenez=fm.wake_deflections.JimenezDeflection(rotate=True),
244
+ JimenezProj=fm.wake_deflections.JimenezDeflection(rotate=False),
245
+ JimenezPath=fm.wake_deflections.JimenezDeflection(rotate=None),
246
+ )
247
+
248
+ self.wake_deflections.add_factory(
249
+ fm.wake_deflections.JimenezDeflection,
250
+ "Jimenez_b<beta>",
251
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
252
+ hints={"beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)"},
253
+ kwargs=dict(rotate=True),
254
+ )
255
+ self.wake_deflections.add_factory(
256
+ fm.wake_deflections.JimenezDeflection,
257
+ "Jimenez_b<beta>_dx<dx>",
258
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
259
+ dx=lambda dx: float(dx),
260
+ var2arg={"dx": "step_x"},
261
+ hints={
262
+ "beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)",
263
+ "dx": "(The step size in m for integration along wake path, e.g. 10)",
264
+ },
265
+ kwargs=dict(rotate=True),
266
+ )
267
+ self.wake_deflections.add_factory(
268
+ fm.wake_deflections.JimenezDeflection,
269
+ "JimenezProj_b<beta>",
270
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
271
+ hints={"beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)"},
272
+ kwargs=dict(rotate=False),
273
+ )
274
+ self.wake_deflections.add_factory(
275
+ fm.wake_deflections.JimenezDeflection,
276
+ "JimenezProj_b<beta>_dx<dx>",
277
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
278
+ dx=lambda dx: float(dx),
279
+ var2arg={"dx": "step_x"},
280
+ hints={
281
+ "beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)",
282
+ "dx": "(The step size in m for integration along wake path, e.g. 10)",
283
+ },
284
+ kwargs=dict(rotate=False),
285
+ )
286
+ self.wake_deflections.add_factory(
287
+ fm.wake_deflections.JimenezDeflection,
288
+ "JimenezPath_b<beta>",
289
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
290
+ hints={"beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)"},
291
+ kwargs=dict(rotate=None),
292
+ )
293
+ self.wake_deflections.add_factory(
294
+ fm.wake_deflections.JimenezDeflection,
295
+ "JimenezPath_b<beta>_dx<dx>",
296
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
297
+ dx=lambda dx: float(dx),
298
+ var2arg={"dx": "step_x"},
299
+ hints={
300
+ "beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)",
301
+ "dx": "(The step size in m for integration along wake path, e.g. 10)",
302
+ },
303
+ kwargs=dict(rotate=None),
304
+ )
305
+
234
306
  self.wake_frames = FDict(
235
- name="wake_frames",
236
- rotor_wd=fm.wake_frames.RotorWD(var_wd=FV.WD),
307
+ _name="wake_frames",
308
+ rotor_wd=fm.wake_frames.RotorWD(),
237
309
  rotor_wd_farmo=fm.wake_frames.FarmOrder(),
238
310
  )
239
- self.wake_frames.add_k_factory(
240
- fm.wake_frames.YawedWakes,
241
- "yawed_[wake_k]",
242
- )
243
311
  self.wake_frames.add_factory(
244
312
  fm.wake_frames.Streamlines2D,
245
313
  "streamlines_<step>",
@@ -296,7 +364,7 @@ class ModelBook:
296
364
  )
297
365
 
298
366
  self.wake_superpositions = FDict(
299
- name="wake_superpositions",
367
+ _name="wake_superpositions",
300
368
  ws_linear=fm.wake_superpositions.WSLinear(scale_amb=False),
301
369
  ws_linear_lim=fm.wake_superpositions.WSLinear(
302
370
  scale_amb=False, lim_low=1e-4
@@ -336,88 +404,128 @@ class ModelBook:
336
404
  ti_cubic=fm.wake_superpositions.TIPow(pow=3, superp_to_amb="quadratic"),
337
405
  ti_quartic=fm.wake_superpositions.TIPow(pow=4, superp_to_amb="quadratic"),
338
406
  ti_max=fm.wake_superpositions.TIMax(superp_to_amb="quadratic"),
407
+ vector=fm.wake_superpositions.WindVectorLinear(scale_amb=False),
408
+ vector_amb=fm.wake_superpositions.WindVectorLinear(scale_amb=True),
339
409
  )
340
410
 
341
- self.axial_induction = FDict(name="induction_models")
411
+ self.axial_induction = FDict(_name="induction_models")
342
412
  self.axial_induction["Betz"] = fm.axial_induction.BetzAxialInduction()
343
413
  self.axial_induction["Madsen"] = fm.axial_induction.MadsenAxialInduction()
344
414
 
345
- self.wake_models = FDict(name="wake_models")
415
+ self.wake_models = FDict(_name="wake_models")
346
416
 
347
417
  self.wake_models.add_k_factory(
348
418
  fm.wake_models.wind.JensenWake,
349
419
  "Jensen_<superposition>_[wake_k]",
350
420
  kwargs=dict(induction="Betz"),
351
- superposition=lambda s: f"ws_{s}",
352
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
421
+ superposition=lambda s: f"ws_{s}"
422
+ if f"ws_{s}" in self.wake_superpositions
423
+ else s,
424
+ hints={
425
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
426
+ },
353
427
  )
354
428
 
355
429
  self.wake_models.add_k_factory(
356
430
  fm.wake_models.wind.Bastankhah2014,
357
431
  "Bastankhah2014_<superposition>_[wake_k]",
358
432
  kwargs=dict(sbeta_factor=0.2, induction="Madsen"),
359
- superposition=lambda s: f"ws_{s}",
360
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
433
+ superposition=lambda s: f"ws_{s}"
434
+ if f"ws_{s}" in self.wake_superpositions
435
+ else s,
436
+ hints={
437
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
438
+ },
361
439
  )
362
440
  self.wake_models.add_k_factory(
363
441
  fm.wake_models.wind.Bastankhah2014,
364
442
  "Bastankhah2014B_<superposition>_[wake_k]",
365
443
  kwargs=dict(sbeta_factor=0.2, induction="Betz"),
366
- superposition=lambda s: f"ws_{s}",
367
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
444
+ superposition=lambda s: f"ws_{s}"
445
+ if f"ws_{s}" in self.wake_superpositions
446
+ else s,
447
+ hints={
448
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
449
+ },
368
450
  )
369
451
  self.wake_models.add_k_factory(
370
452
  fm.wake_models.wind.Bastankhah2014,
371
453
  "Bastankhah025_<superposition>_[wake_k]",
372
454
  kwargs=dict(sbeta_factor=0.25, induction="Madsen"),
373
- superposition=lambda s: f"ws_{s}",
374
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
455
+ superposition=lambda s: f"ws_{s}"
456
+ if f"ws_{s}" in self.wake_superpositions
457
+ else s,
458
+ hints={
459
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
460
+ },
375
461
  )
376
462
  self.wake_models.add_k_factory(
377
463
  fm.wake_models.wind.Bastankhah2014,
378
464
  "Bastankhah025B_<superposition>_[wake_k]",
379
465
  kwargs=dict(sbeta_factor=0.25, induction="Betz"),
380
- superposition=lambda s: f"ws_{s}",
381
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
466
+ superposition=lambda s: f"ws_{s}"
467
+ if f"ws_{s}" in self.wake_superpositions
468
+ else s,
469
+ hints={
470
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
471
+ },
382
472
  )
383
473
 
384
474
  self.wake_models.add_k_factory(
385
475
  fm.wake_models.wind.Bastankhah2016,
386
476
  "Bastankhah2016_<superposition>_[wake_k]",
387
477
  kwargs=dict(induction="Madsen"),
388
- superposition=lambda s: f"ws_{s}",
389
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
478
+ superposition=lambda s: f"ws_{s}"
479
+ if f"ws_{s}" in self.wake_superpositions
480
+ else s,
481
+ hints={
482
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
483
+ },
390
484
  )
391
485
  self.wake_models.add_k_factory(
392
486
  fm.wake_models.wind.Bastankhah2016,
393
487
  "Bastankhah2016B_<superposition>_[wake_k]",
394
488
  kwargs=dict(induction="Betz"),
395
- superposition=lambda s: f"ws_{s}",
396
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
489
+ superposition=lambda s: f"ws_{s}"
490
+ if f"ws_{s}" in self.wake_superpositions
491
+ else s,
492
+ hints={
493
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
494
+ },
397
495
  )
398
496
 
399
497
  self.wake_models.add_k_factory(
400
498
  fm.wake_models.wind.TurbOParkWake,
401
499
  "TurbOPark_<superposition>_[wake_k]",
402
500
  kwargs=dict(induction="Madsen"),
403
- superposition=lambda s: f"ws_{s}",
404
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
501
+ superposition=lambda s: f"ws_{s}"
502
+ if f"ws_{s}" in self.wake_superpositions
503
+ else s,
504
+ hints={
505
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
506
+ },
405
507
  )
406
508
  self.wake_models.add_k_factory(
407
509
  fm.wake_models.wind.TurbOParkWake,
408
510
  "TurbOParkB_<superposition>_[wake_k]",
409
511
  kwargs=dict(induction="Betz"),
410
- superposition=lambda s: f"ws_{s}",
411
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
512
+ superposition=lambda s: f"ws_{s}"
513
+ if f"ws_{s}" in self.wake_superpositions
514
+ else s,
515
+ hints={
516
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
517
+ },
412
518
  )
413
519
 
414
520
  self.wake_models.add_k_factory(
415
521
  fm.wake_models.wind.TurbOParkWakeIX,
416
522
  "TurbOParkIX_<superposition>_[wake_k]_dx<dx>",
417
- superposition=lambda s: f"ws_{s}",
523
+ superposition=lambda s: f"ws_{s}"
524
+ if f"ws_{s}" in self.wake_superpositions
525
+ else s,
418
526
  dx=lambda x: float(x),
419
527
  hints={
420
- "superposition": "(Superposition, e.g. linear for ws_linear)",
528
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)",
421
529
  "dx": "(Integration step in m)",
422
530
  },
423
531
  )
@@ -460,42 +568,56 @@ class ModelBook:
460
568
  hints={"superposition": "(Superposition, e.g. linear for ti_linear)"},
461
569
  )
462
570
 
463
- self.wake_models[f"RHB"] = fm.wake_models.induction.RankineHalfBody()
571
+ self.wake_models["RHB"] = fm.wake_models.induction.RankineHalfBody()
464
572
 
465
- self.wake_models[f"VortexSheet"] = fm.wake_models.induction.VortexSheet()
573
+ self.wake_models["VortexSheet"] = fm.wake_models.induction.VortexSheet()
466
574
  self.wake_models.add_factory(
467
575
  fm.wake_models.induction.VortexSheet,
468
576
  "VortexSheet_<superposition>",
469
- superposition=lambda s: f"ws_{s}",
470
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
577
+ superposition=lambda s: f"ws_{s}"
578
+ if f"ws_{s}" in self.wake_superpositions
579
+ else s,
580
+ hints={
581
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
582
+ },
471
583
  )
472
584
 
473
- self.wake_models[f"Rathmann"] = fm.wake_models.induction.Rathmann()
585
+ self.wake_models["Rathmann"] = fm.wake_models.induction.Rathmann()
474
586
  self.wake_models.add_factory(
475
587
  fm.wake_models.induction.Rathmann,
476
588
  "Rathmann_<superposition>",
477
- superposition=lambda s: f"ws_{s}",
478
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
589
+ superposition=lambda s: f"ws_{s}"
590
+ if f"ws_{s}" in self.wake_superpositions
591
+ else s,
592
+ hints={
593
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
594
+ },
479
595
  )
480
596
 
481
- self.wake_models[f"SelfSimilar"] = fm.wake_models.induction.SelfSimilar()
482
- self.wake_models[f"SelfSimilar2020"] = (
483
- fm.wake_models.induction.SelfSimilar2020()
484
- )
597
+ self.wake_models["SelfSimilar"] = fm.wake_models.induction.SelfSimilar()
598
+ self.wake_models["SelfSimilar2020"] = fm.wake_models.induction.SelfSimilar2020()
485
599
  self.wake_models.add_factory(
486
600
  fm.wake_models.induction.SelfSimilar,
487
601
  "SelfSimilar_<superposition>",
488
- superposition=lambda s: f"ws_{s}",
489
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
602
+ superposition=lambda s: f"ws_{s}"
603
+ if f"ws_{s}" in self.wake_superpositions
604
+ else s,
605
+ hints={
606
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
607
+ },
490
608
  )
491
609
  self.wake_models.add_factory(
492
610
  fm.wake_models.induction.SelfSimilar2020,
493
611
  "SelfSimilar2020_<superposition>",
494
- superposition=lambda s: f"ws_{s}",
495
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
612
+ superposition=lambda s: f"ws_{s}"
613
+ if f"ws_{s}" in self.wake_superpositions
614
+ else s,
615
+ hints={
616
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
617
+ },
496
618
  )
497
619
 
498
- self.ground_models = FDict(name="ground_models")
620
+ self.ground_models = FDict(_name="ground_models")
499
621
  self.ground_models["no_ground"] = fm.ground_models.NoGround()
500
622
  self.ground_models["ground_mirror"] = fm.ground_models.GroundMirror()
501
623
  self.ground_models.add_factory(
@@ -508,7 +630,7 @@ class ModelBook:
508
630
  )
509
631
 
510
632
  self.sources = FDict(
511
- name="sources",
633
+ _name="sources",
512
634
  point_models=self.point_models,
513
635
  rotor_models=self.rotor_models,
514
636
  turbine_types=self.turbine_types,
@@ -517,13 +639,14 @@ class ModelBook:
517
639
  farm_controllers=self.farm_controllers,
518
640
  partial_wakes=self.partial_wakes,
519
641
  wake_frames=self.wake_frames,
642
+ wake_deflections=self.wake_deflections,
520
643
  wake_superpositions=self.wake_superpositions,
521
644
  wake_models=self.wake_models,
522
645
  axial_induction=self.axial_induction,
523
646
  ground_models=self.ground_models,
524
647
  )
525
648
  self.base_classes = FDict(
526
- name="base_classes",
649
+ _name="base_classes",
527
650
  point_models=PointDataModel,
528
651
  rotor_models=RotorModel,
529
652
  turbine_types=TurbineType,
@@ -532,6 +655,7 @@ class ModelBook:
532
655
  farm_controllers=FarmController,
533
656
  partial_wakes=PartialWakesModel,
534
657
  wake_frames=WakeFrame,
658
+ wake_deflections=WakeDeflection,
535
659
  wake_superpositions=WakeSuperposition,
536
660
  wake_models=WakeModel,
537
661
  axial_induction=AxialInductionModel,
@@ -563,16 +687,19 @@ class ModelBook:
563
687
  if subset is None or k in subset:
564
688
  print(k)
565
689
  print("-" * len(k))
566
- if len(ms):
567
- for mname in sorted(list(ms.keys())):
568
- if search is None or search in mname:
569
- print(f"{mname}: {ms[mname]}")
570
- if isinstance(ms, FDict):
571
- for f in ms.factories:
572
- if search is None or search in f.name_template:
690
+ any = False
691
+ for mname in sorted(list(ms.keys())):
692
+ if search is None or search in mname:
693
+ print(f"{mname}: {ms[mname]}")
694
+ any = True
695
+ if isinstance(ms, FDict):
696
+ for f in ms.factories:
697
+ if search is None or search in f.name_template:
698
+ if any:
573
699
  print()
574
- print(f)
575
- else:
700
+ print(f)
701
+ any = True
702
+ if not any:
576
703
  print("(none)")
577
704
  print()
578
705
 
@@ -2,9 +2,9 @@
2
2
  Partial wake models.
3
3
  """
4
4
 
5
- from .centre import PartialCentre
6
- from .rotor_points import RotorPoints
7
- from .top_hat import PartialTopHat
8
- from .axiwake import PartialAxiwake
9
- from .segregated import PartialSegregated
10
- from .grid import PartialGrid
5
+ from .centre import PartialCentre as PartialCentre
6
+ from .rotor_points import RotorPoints as RotorPoints
7
+ from .top_hat import PartialTopHat as PartialTopHat
8
+ from .axiwake import PartialAxiwake as PartialAxiwake
9
+ from .segregated import PartialSegregated as PartialSegregated
10
+ from .grid import PartialGrid as PartialGrid
@@ -101,8 +101,6 @@ class PartialAxiwake(PartialCentre):
101
101
  The wake deltas. Key: variable name,
102
102
  value: numpy.ndarray with shape
103
103
  (n_states, n_targets, n_tpoints, ...)
104
- wmodel: foxes.core.WakeModel
105
- The wake model
106
104
 
107
105
  """
108
106
  # check:
@@ -199,16 +197,43 @@ class PartialAxiwake(PartialCentre):
199
197
  algo, mdata, fdata, tdata, downwind_index, x, r
200
198
  )
201
199
 
202
- for v, wdel in wdeltas.items():
203
- d = np.einsum("sn,sn->s", wdel, weights[st_sel])
200
+ # run superposition models:
201
+ if wmodel.affects_ws and wmodel.has_uv:
202
+ assert wmodel.has_vector_wind_superp, (
203
+ f"{self.name}: Expecting vector wind superposition in wake model '{wmodel.name}', got '{wmodel.wind_superposition}'"
204
+ )
205
+ if FV.WS in wdeltas or FV.UV in wdeltas:
206
+ if FV.UV not in wdeltas:
207
+ wmodel.vec_superp.wdeltas_ws2uv(
208
+ algo, fdata, tdata, downwind_index, wdeltas, st_sel
209
+ )
210
+ duv = np.einsum("snd,sn->sd", wdeltas.pop(FV.UV), weights[st_sel])
211
+ wake_deltas[FV.UV] = wmodel.vec_superp.add_wake_vector(
212
+ algo,
213
+ mdata,
214
+ fdata,
215
+ tdata,
216
+ downwind_index,
217
+ st_sel,
218
+ wake_deltas[FV.UV],
219
+ duv[:, None],
220
+ )
221
+ del duv
222
+ for v in [FV.WS, FV.WD, FV.UV]:
223
+ if v in wdeltas:
224
+ del wdeltas[v]
204
225
 
226
+ for v, wdel in wdeltas.items():
205
227
  try:
206
228
  superp = wmodel.superp[v]
207
229
  except KeyError:
230
+ s = {v: m.name for v, m in wmodel.superp.items()}
208
231
  raise KeyError(
209
- f"Model '{self.name}': Missing wake superposition entry for variable '{v}' in wake model '{wmodel.name}', found {sorted(list(wmodel.superp.keys()))}"
232
+ f"Model '{self.name}': Missing wake superposition entry for variable '{v}' in wake model '{wmodel.name}', found {s}"
210
233
  )
211
234
 
235
+ d = np.einsum("sn,sn->s", wdel, weights[st_sel])
236
+
212
237
  wake_deltas[v] = superp.add_wake(
213
238
  algo,
214
239
  mdata,
@@ -39,3 +39,50 @@ class PartialCentre(RotorPoints):
39
39
 
40
40
  """
41
41
  return fdata[FV.TXYH][:, :, None], np.ones(1, dtype=config.dtype_double)
42
+
43
+ def map_rotor_results(
44
+ self,
45
+ algo,
46
+ mdata,
47
+ fdata,
48
+ tdata,
49
+ variable,
50
+ rotor_res,
51
+ rotor_weights,
52
+ ):
53
+ """
54
+ Map ambient rotor point results onto target points.
55
+
56
+ Parameters
57
+ ----------
58
+ algo: foxes.core.Algorithm
59
+ The calculation algorithm
60
+ mdata: foxes.core.MData
61
+ The model data
62
+ fdata: foxes.core.FData
63
+ The farm data
64
+ tdata: foxes.core.TData
65
+ The target point data
66
+ variable: str
67
+ The variable name to map
68
+ rotor_res: numpy.ndarray
69
+ The results at rotor points, shape:
70
+ (n_states, n_turbines, n_rotor_points)
71
+ rotor_weights: numpy.ndarray
72
+ The rotor point weights, shape: (n_rotor_points,)
73
+
74
+ Returns
75
+ -------
76
+ res: numpy.ndarray
77
+ The mapped results at target points, shape:
78
+ (n_states, n_targets, n_tpoints)
79
+
80
+ """
81
+ if rotor_res.shape[2] > 1:
82
+ return np.einsum(
83
+ "str,r->st",
84
+ rotor_res,
85
+ rotor_weights,
86
+ )[:, :, None]
87
+ else:
88
+ return rotor_res
@@ -39,13 +39,52 @@ class RotorPoints(PartialWakesModel):
39
39
  algo.get_from_chunk_store(FC.ROTOR_WEIGHTS, mdata=mdata),
40
40
  )
41
41
 
42
+ def map_rotor_results(
43
+ self,
44
+ algo,
45
+ mdata,
46
+ fdata,
47
+ tdata,
48
+ variable,
49
+ rotor_res,
50
+ rotor_weights,
51
+ ):
52
+ """
53
+ Map ambient rotor point results onto target points.
54
+
55
+ Parameters
56
+ ----------
57
+ algo: foxes.core.Algorithm
58
+ The calculation algorithm
59
+ mdata: foxes.core.MData
60
+ The model data
61
+ fdata: foxes.core.FData
62
+ The farm data
63
+ tdata: foxes.core.TData
64
+ The target point data
65
+ variable: str
66
+ The variable name to map
67
+ rotor_res: numpy.ndarray
68
+ The results at rotor points, shape:
69
+ (n_states, n_turbines, n_rotor_points)
70
+ rotor_weights: numpy.ndarray
71
+ The rotor point weights, shape: (n_rotor_points,)
72
+
73
+ Returns
74
+ -------
75
+ res: numpy.ndarray
76
+ The mapped results at target points, shape:
77
+ (n_states, n_targets, n_tpoints)
78
+
79
+ """
80
+ return rotor_res
81
+
42
82
  def finalize_wakes(
43
83
  self,
44
84
  algo,
45
85
  mdata,
46
86
  fdata,
47
87
  tdata,
48
- amb_res,
49
88
  rpoint_weights,
50
89
  wake_deltas,
51
90
  wmodel,
@@ -67,11 +106,6 @@ class RotorPoints(PartialWakesModel):
67
106
  The farm data
68
107
  tdata: foxes.core.Data
69
108
  The target point data
70
- amb_res: dict
71
- The ambient results at the target points
72
- of all rotors. Key: variable name, value
73
- np.ndarray of shape:
74
- (n_states, n_turbines, n_rotor_points)
75
109
  rpoint_weights: numpy.ndarray
76
110
  The rotor point weights, shape: (n_rotor_points,)
77
111
  wake_deltas: dict
@@ -91,14 +125,10 @@ class RotorPoints(PartialWakesModel):
91
125
  of shape (n_states, n_rotor_points)
92
126
 
93
127
  """
94
- ares = {
95
- v: d[:, downwind_index, None] if d.shape[1] > 1 else d[:, 0, None]
96
- for v, d in amb_res.items()
97
- }
98
128
  wdel = {
99
129
  v: d[:, downwind_index, None].copy() if d.shape[1] > 1 else d[:, 0, None]
100
130
  for v, d in wake_deltas.items()
101
131
  }
102
- wmodel.finalize_wake_deltas(algo, mdata, fdata, ares, wdel)
132
+ wmodel.finalize_wake_deltas(algo, mdata, fdata, tdata, wdel)
103
133
 
104
134
  return {v: d[:, 0] for v, d in wdel.items()}