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
@@ -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
@@ -231,15 +235,78 @@ class ModelBook:
231
235
  hints={"n2": "(Number of points in square grid)"},
232
236
  )
233
237
 
238
+ self.wake_deflections = FDict(
239
+ name="wake_deflections",
240
+ no_deflection=fm.wake_deflections.NoDeflection(),
241
+ Bastankhah2016=fm.wake_deflections.Bastankhah2016Deflection(),
242
+ Jimenez=fm.wake_deflections.JimenezDeflection(rotate=True),
243
+ JimenezProj=fm.wake_deflections.JimenezDeflection(rotate=False),
244
+ JimenezPath=fm.wake_deflections.JimenezDeflection(rotate=None),
245
+ )
246
+
247
+ self.wake_deflections.add_factory(
248
+ fm.wake_deflections.JimenezDeflection,
249
+ "Jimenez_b<beta>",
250
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
251
+ hints={"beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)"},
252
+ kwargs=dict(rotate=True),
253
+ )
254
+ self.wake_deflections.add_factory(
255
+ fm.wake_deflections.JimenezDeflection,
256
+ "Jimenez_b<beta>_dx<dx>",
257
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
258
+ dx=lambda dx: float(dx),
259
+ var2arg={"dx": "step_x"},
260
+ hints={
261
+ "beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)",
262
+ "dx": "(The step size in m for integration along wake path, e.g. 10)",
263
+ },
264
+ kwargs=dict(rotate=True),
265
+ )
266
+ self.wake_deflections.add_factory(
267
+ fm.wake_deflections.JimenezDeflection,
268
+ "JimenezProj_b<beta>",
269
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
270
+ hints={"beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)"},
271
+ kwargs=dict(rotate=False),
272
+ )
273
+ self.wake_deflections.add_factory(
274
+ fm.wake_deflections.JimenezDeflection,
275
+ "JimenezProj_b<beta>_dx<dx>",
276
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
277
+ dx=lambda dx: float(dx),
278
+ var2arg={"dx": "step_x"},
279
+ hints={
280
+ "beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)",
281
+ "dx": "(The step size in m for integration along wake path, e.g. 10)",
282
+ },
283
+ kwargs=dict(rotate=False),
284
+ )
285
+ self.wake_deflections.add_factory(
286
+ fm.wake_deflections.JimenezDeflection,
287
+ "JimenezPath_b<beta>",
288
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
289
+ hints={"beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)"},
290
+ kwargs=dict(rotate=None),
291
+ )
292
+ self.wake_deflections.add_factory(
293
+ fm.wake_deflections.JimenezDeflection,
294
+ "JimenezPath_b<beta>_dx<dx>",
295
+ beta=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
296
+ dx=lambda dx: float(dx),
297
+ var2arg={"dx": "step_x"},
298
+ hints={
299
+ "beta": "(The Jimenez beta coefficient, e.g. 01 for 0.1)",
300
+ "dx": "(The step size in m for integration along wake path, e.g. 10)",
301
+ },
302
+ kwargs=dict(rotate=None),
303
+ )
304
+
234
305
  self.wake_frames = FDict(
235
306
  name="wake_frames",
236
- rotor_wd=fm.wake_frames.RotorWD(var_wd=FV.WD),
307
+ rotor_wd=fm.wake_frames.RotorWD(),
237
308
  rotor_wd_farmo=fm.wake_frames.FarmOrder(),
238
309
  )
239
- self.wake_frames.add_k_factory(
240
- fm.wake_frames.YawedWakes,
241
- "yawed_[wake_k]",
242
- )
243
310
  self.wake_frames.add_factory(
244
311
  fm.wake_frames.Streamlines2D,
245
312
  "streamlines_<step>",
@@ -336,6 +403,8 @@ class ModelBook:
336
403
  ti_cubic=fm.wake_superpositions.TIPow(pow=3, superp_to_amb="quadratic"),
337
404
  ti_quartic=fm.wake_superpositions.TIPow(pow=4, superp_to_amb="quadratic"),
338
405
  ti_max=fm.wake_superpositions.TIMax(superp_to_amb="quadratic"),
406
+ vector=fm.wake_superpositions.WindVectorLinear(scale_amb=False),
407
+ vector_amb=fm.wake_superpositions.WindVectorLinear(scale_amb=True),
339
408
  )
340
409
 
341
410
  self.axial_induction = FDict(name="induction_models")
@@ -348,76 +417,114 @@ class ModelBook:
348
417
  fm.wake_models.wind.JensenWake,
349
418
  "Jensen_<superposition>_[wake_k]",
350
419
  kwargs=dict(induction="Betz"),
351
- superposition=lambda s: f"ws_{s}",
352
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
420
+ superposition=lambda s: f"ws_{s}"
421
+ if f"ws_{s}" in self.wake_superpositions
422
+ else s,
423
+ hints={
424
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
425
+ },
353
426
  )
354
427
 
355
428
  self.wake_models.add_k_factory(
356
429
  fm.wake_models.wind.Bastankhah2014,
357
430
  "Bastankhah2014_<superposition>_[wake_k]",
358
431
  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)"},
432
+ superposition=lambda s: f"ws_{s}"
433
+ if f"ws_{s}" in self.wake_superpositions
434
+ else s,
435
+ hints={
436
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
437
+ },
361
438
  )
362
439
  self.wake_models.add_k_factory(
363
440
  fm.wake_models.wind.Bastankhah2014,
364
441
  "Bastankhah2014B_<superposition>_[wake_k]",
365
442
  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)"},
443
+ superposition=lambda s: f"ws_{s}"
444
+ if f"ws_{s}" in self.wake_superpositions
445
+ else s,
446
+ hints={
447
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
448
+ },
368
449
  )
369
450
  self.wake_models.add_k_factory(
370
451
  fm.wake_models.wind.Bastankhah2014,
371
452
  "Bastankhah025_<superposition>_[wake_k]",
372
453
  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)"},
454
+ superposition=lambda s: f"ws_{s}"
455
+ if f"ws_{s}" in self.wake_superpositions
456
+ else s,
457
+ hints={
458
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
459
+ },
375
460
  )
376
461
  self.wake_models.add_k_factory(
377
462
  fm.wake_models.wind.Bastankhah2014,
378
463
  "Bastankhah025B_<superposition>_[wake_k]",
379
464
  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)"},
465
+ superposition=lambda s: f"ws_{s}"
466
+ if f"ws_{s}" in self.wake_superpositions
467
+ else s,
468
+ hints={
469
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
470
+ },
382
471
  )
383
472
 
384
473
  self.wake_models.add_k_factory(
385
474
  fm.wake_models.wind.Bastankhah2016,
386
475
  "Bastankhah2016_<superposition>_[wake_k]",
387
476
  kwargs=dict(induction="Madsen"),
388
- superposition=lambda s: f"ws_{s}",
389
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
477
+ superposition=lambda s: f"ws_{s}"
478
+ if f"ws_{s}" in self.wake_superpositions
479
+ else s,
480
+ hints={
481
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
482
+ },
390
483
  )
391
484
  self.wake_models.add_k_factory(
392
485
  fm.wake_models.wind.Bastankhah2016,
393
486
  "Bastankhah2016B_<superposition>_[wake_k]",
394
487
  kwargs=dict(induction="Betz"),
395
- superposition=lambda s: f"ws_{s}",
396
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
488
+ superposition=lambda s: f"ws_{s}"
489
+ if f"ws_{s}" in self.wake_superpositions
490
+ else s,
491
+ hints={
492
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
493
+ },
397
494
  )
398
495
 
399
496
  self.wake_models.add_k_factory(
400
497
  fm.wake_models.wind.TurbOParkWake,
401
498
  "TurbOPark_<superposition>_[wake_k]",
402
499
  kwargs=dict(induction="Madsen"),
403
- superposition=lambda s: f"ws_{s}",
404
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
500
+ superposition=lambda s: f"ws_{s}"
501
+ if f"ws_{s}" in self.wake_superpositions
502
+ else s,
503
+ hints={
504
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
505
+ },
405
506
  )
406
507
  self.wake_models.add_k_factory(
407
508
  fm.wake_models.wind.TurbOParkWake,
408
509
  "TurbOParkB_<superposition>_[wake_k]",
409
510
  kwargs=dict(induction="Betz"),
410
- superposition=lambda s: f"ws_{s}",
411
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
511
+ superposition=lambda s: f"ws_{s}"
512
+ if f"ws_{s}" in self.wake_superpositions
513
+ else s,
514
+ hints={
515
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
516
+ },
412
517
  )
413
518
 
414
519
  self.wake_models.add_k_factory(
415
520
  fm.wake_models.wind.TurbOParkWakeIX,
416
521
  "TurbOParkIX_<superposition>_[wake_k]_dx<dx>",
417
- superposition=lambda s: f"ws_{s}",
522
+ superposition=lambda s: f"ws_{s}"
523
+ if f"ws_{s}" in self.wake_superpositions
524
+ else s,
418
525
  dx=lambda x: float(x),
419
526
  hints={
420
- "superposition": "(Superposition, e.g. linear for ws_linear)",
527
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)",
421
528
  "dx": "(Integration step in m)",
422
529
  },
423
530
  )
@@ -460,39 +567,53 @@ class ModelBook:
460
567
  hints={"superposition": "(Superposition, e.g. linear for ti_linear)"},
461
568
  )
462
569
 
463
- self.wake_models[f"RHB"] = fm.wake_models.induction.RankineHalfBody()
570
+ self.wake_models["RHB"] = fm.wake_models.induction.RankineHalfBody()
464
571
 
465
- self.wake_models[f"VortexSheet"] = fm.wake_models.induction.VortexSheet()
572
+ self.wake_models["VortexSheet"] = fm.wake_models.induction.VortexSheet()
466
573
  self.wake_models.add_factory(
467
574
  fm.wake_models.induction.VortexSheet,
468
575
  "VortexSheet_<superposition>",
469
- superposition=lambda s: f"ws_{s}",
470
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
576
+ superposition=lambda s: f"ws_{s}"
577
+ if f"ws_{s}" in self.wake_superpositions
578
+ else s,
579
+ hints={
580
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
581
+ },
471
582
  )
472
583
 
473
- self.wake_models[f"Rathmann"] = fm.wake_models.induction.Rathmann()
584
+ self.wake_models["Rathmann"] = fm.wake_models.induction.Rathmann()
474
585
  self.wake_models.add_factory(
475
586
  fm.wake_models.induction.Rathmann,
476
587
  "Rathmann_<superposition>",
477
- superposition=lambda s: f"ws_{s}",
478
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
588
+ superposition=lambda s: f"ws_{s}"
589
+ if f"ws_{s}" in self.wake_superpositions
590
+ else s,
591
+ hints={
592
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
593
+ },
479
594
  )
480
595
 
481
- self.wake_models[f"SelfSimilar"] = fm.wake_models.induction.SelfSimilar()
482
- self.wake_models[f"SelfSimilar2020"] = (
483
- fm.wake_models.induction.SelfSimilar2020()
484
- )
596
+ self.wake_models["SelfSimilar"] = fm.wake_models.induction.SelfSimilar()
597
+ self.wake_models["SelfSimilar2020"] = fm.wake_models.induction.SelfSimilar2020()
485
598
  self.wake_models.add_factory(
486
599
  fm.wake_models.induction.SelfSimilar,
487
600
  "SelfSimilar_<superposition>",
488
- superposition=lambda s: f"ws_{s}",
489
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
601
+ superposition=lambda s: f"ws_{s}"
602
+ if f"ws_{s}" in self.wake_superpositions
603
+ else s,
604
+ hints={
605
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
606
+ },
490
607
  )
491
608
  self.wake_models.add_factory(
492
609
  fm.wake_models.induction.SelfSimilar2020,
493
610
  "SelfSimilar2020_<superposition>",
494
- superposition=lambda s: f"ws_{s}",
495
- hints={"superposition": "(Superposition, e.g. linear for ws_linear)"},
611
+ superposition=lambda s: f"ws_{s}"
612
+ if f"ws_{s}" in self.wake_superpositions
613
+ else s,
614
+ hints={
615
+ "superposition": "(Superposition, e.g. linear for ws_linear, or vector)"
616
+ },
496
617
  )
497
618
 
498
619
  self.ground_models = FDict(name="ground_models")
@@ -517,6 +638,7 @@ class ModelBook:
517
638
  farm_controllers=self.farm_controllers,
518
639
  partial_wakes=self.partial_wakes,
519
640
  wake_frames=self.wake_frames,
641
+ wake_deflections=self.wake_deflections,
520
642
  wake_superpositions=self.wake_superpositions,
521
643
  wake_models=self.wake_models,
522
644
  axial_induction=self.axial_induction,
@@ -532,6 +654,7 @@ class ModelBook:
532
654
  farm_controllers=FarmController,
533
655
  partial_wakes=PartialWakesModel,
534
656
  wake_frames=WakeFrame,
657
+ wake_deflections=WakeDeflection,
535
658
  wake_superpositions=WakeSuperposition,
536
659
  wake_models=WakeModel,
537
660
  axial_induction=AxialInductionModel,
@@ -563,16 +686,19 @@ class ModelBook:
563
686
  if subset is None or k in subset:
564
687
  print(k)
565
688
  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:
689
+ any = False
690
+ for mname in sorted(list(ms.keys())):
691
+ if search is None or search in mname:
692
+ print(f"{mname}: {ms[mname]}")
693
+ any = True
694
+ if isinstance(ms, FDict):
695
+ for f in ms.factories:
696
+ if search is None or search in f.name_template:
697
+ if any:
573
698
  print()
574
- print(f)
575
- else:
699
+ print(f)
700
+ any = True
701
+ if not any:
576
702
  print("(none)")
577
703
  print()
578
704
 
@@ -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,8 +125,10 @@ class RotorPoints(PartialWakesModel):
91
125
  of shape (n_states, n_rotor_points)
92
126
 
93
127
  """
94
- ares = {v: d[:, downwind_index, None] for v, d in amb_res.items()}
95
- wdel = {v: d[:, downwind_index, None].copy() for v, d in wake_deltas.items()}
96
- wmodel.finalize_wake_deltas(algo, mdata, fdata, ares, wdel)
128
+ wdel = {
129
+ v: d[:, downwind_index, None].copy() if d.shape[1] > 1 else d[:, 0, None]
130
+ for v, d in wake_deltas.items()
131
+ }
132
+ wmodel.finalize_wake_deltas(algo, mdata, fdata, tdata, wdel)
97
133
 
98
134
  return {v: d[:, 0] for v, d in wdel.items()}
@@ -87,7 +87,6 @@ class PartialSegregated(PartialWakesModel):
87
87
  mdata,
88
88
  fdata,
89
89
  tdata,
90
- amb_res,
91
90
  rpoint_weights,
92
91
  wake_deltas,
93
92
  wmodel,
@@ -109,11 +108,6 @@ class PartialSegregated(PartialWakesModel):
109
108
  The farm data
110
109
  tdata: foxes.core.Data
111
110
  The target point data
112
- amb_res: dict
113
- The ambient results at the target points
114
- of all rotors. Key: variable name, value
115
- np.ndarray of shape:
116
- (n_states, n_turbines, n_rotor_points)
117
111
  rpoint_weights: numpy.ndarray
118
112
  The rotor point weights, shape: (n_rotor_points,)
119
113
  wake_deltas: dict
@@ -138,20 +132,8 @@ class PartialSegregated(PartialWakesModel):
138
132
  gweights = tdata[FC.TWEIGHTS]
139
133
 
140
134
  wdel = {v: d[:, downwind_index, None].copy() for v, d in wake_deltas.items()}
141
-
142
- if n_rotor_points == tdata.n_tpoints:
143
- ares = {v: d[:, downwind_index, None] for v, d in amb_res.items()}
144
- else:
145
- ares = {}
146
- for v, d in amb_res.items():
147
- ares[v] = np.zeros(
148
- (n_states, 1, tdata.n_tpoints), dtype=config.dtype_double
149
- )
150
- ares[v][:] = np.einsum("sp,p->s", d[:, downwind_index], rpoint_weights)[
151
- :, None, None
152
- ]
153
-
154
- wmodel.finalize_wake_deltas(algo, mdata, fdata, ares, wdel)
135
+ htdata = tdata.get_slice([FC.TURBINE], np.s_[downwind_index])
136
+ wmodel.finalize_wake_deltas(algo, mdata, fdata, htdata, wdel)
155
137
 
156
138
  for v in wdel.keys():
157
139
  hdel = np.zeros((n_states, n_rotor_points), dtype=config.dtype_double)
@@ -126,8 +126,6 @@ class PartialTopHat(PartialCentre):
126
126
  The wake deltas. Key: variable name,
127
127
  value: numpy.ndarray with shape
128
128
  (n_states, n_targets, n_tpoints, ...)
129
- wmodel: foxes.core.WakeModel
130
- The wake model
131
129
 
132
130
  """
133
131
  self.check_wmodel(wmodel, error=True)
@@ -182,6 +180,33 @@ class PartialTopHat(PartialCentre):
182
180
 
183
181
  weights = calc_area(D / 2, wr, R) / (np.pi * (D / 2) ** 2)
184
182
 
183
+ # run superposition models:
184
+ if wmodel.affects_ws and wmodel.has_uv:
185
+ assert wmodel.has_vector_wind_superp, (
186
+ f"{self.name}: Expecting vector wind superposition in wake model '{wmodel.name}', got '{wmodel.wind_superposition}'"
187
+ )
188
+ if FV.UV in clw:
189
+ duv = clw.pop(FV.UV)
190
+ else:
191
+ clwe = {v: d[:, None] for v, d in clw.items()}
192
+ wmodel.vec_superp.wdeltas_ws2uv(
193
+ algo, fdata, tdata, downwind_index, clwe, st_sel
194
+ )
195
+ duv = np.einsum("sd,s->sd", clwe.pop(FV.UV)[:, 0], weights)
196
+ del clwe, clw[FV.WS]
197
+ if FV.WD in clw:
198
+ del clw[FV.WD]
199
+ wake_deltas[FV.UV] = wmodel.vec_superp.add_wake_vector(
200
+ algo,
201
+ mdata,
202
+ fdata,
203
+ tdata,
204
+ downwind_index,
205
+ st_sel,
206
+ wake_deltas[FV.UV],
207
+ duv[:, None],
208
+ )
209
+
185
210
  for v, d in clw.items():
186
211
  try:
187
212
  superp = wmodel.superp[v]
@@ -2,7 +2,7 @@
2
2
  Point models.
3
3
  """
4
4
 
5
- from .wake_deltas import WakeDeltas
6
- from .set_uniform_data import SetUniformData
7
- from .tke2ti import TKE2TI
8
- from .ustar2ti import Ustar2TI
5
+ from .wake_deltas import WakeDeltas as WakeDeltas
6
+ from .set_uniform_data import SetUniformData as SetUniformData
7
+ from .tke2ti import TKE2TI as TKE2TI
8
+ from .ustar2ti import Ustar2TI as Ustar2TI
@@ -2,6 +2,6 @@
2
2
  Rotor models.
3
3
  """
4
4
 
5
- from .centre import CentreRotor
6
- from .grid import GridRotor
7
- from .levels import LevelRotor
5
+ from .centre import CentreRotor as CentreRotor
6
+ from .grid import GridRotor as GridRotor
7
+ from .levels import LevelRotor as LevelRotor