foxes 1.3__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 (190) 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 +0 -1
  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 +21 -17
  23. foxes/algorithms/__init__.py +6 -6
  24. foxes/algorithms/downwind/__init__.py +2 -2
  25. foxes/algorithms/downwind/downwind.py +44 -12
  26. foxes/algorithms/downwind/models/__init__.py +6 -6
  27. foxes/algorithms/downwind/models/farm_wakes_calc.py +11 -9
  28. foxes/algorithms/downwind/models/init_farm_data.py +0 -1
  29. foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
  30. foxes/algorithms/downwind/models/set_amb_point_results.py +6 -6
  31. foxes/algorithms/iterative/__init__.py +7 -3
  32. foxes/algorithms/iterative/iterative.py +1 -2
  33. foxes/algorithms/iterative/models/__init__.py +7 -3
  34. foxes/algorithms/iterative/models/farm_wakes_calc.py +9 -5
  35. foxes/algorithms/sequential/__init__.py +3 -3
  36. foxes/algorithms/sequential/models/__init__.py +2 -2
  37. foxes/algorithms/sequential/sequential.py +3 -4
  38. foxes/config/__init__.py +5 -1
  39. foxes/constants.py +16 -0
  40. foxes/core/__init__.py +45 -22
  41. foxes/core/algorithm.py +0 -1
  42. foxes/core/data.py +19 -18
  43. foxes/core/engine.py +9 -13
  44. foxes/core/farm_controller.py +2 -2
  45. foxes/core/ground_model.py +4 -13
  46. foxes/core/model.py +5 -5
  47. foxes/core/partial_wakes_model.py +147 -10
  48. foxes/core/point_data_model.py +2 -3
  49. foxes/core/rotor_model.py +3 -3
  50. foxes/core/states.py +2 -3
  51. foxes/core/turbine.py +2 -1
  52. foxes/core/wake_deflection.py +130 -0
  53. foxes/core/wake_model.py +222 -9
  54. foxes/core/wake_superposition.py +122 -4
  55. foxes/core/wind_farm.py +6 -6
  56. foxes/data/__init__.py +7 -2
  57. foxes/data/states/weibull_sectors_12.csv +13 -0
  58. foxes/data/states/weibull_sectors_12.nc +0 -0
  59. foxes/engines/__init__.py +14 -15
  60. foxes/engines/dask.py +39 -14
  61. foxes/engines/numpy.py +0 -3
  62. foxes/input/__init__.py +3 -3
  63. foxes/input/farm_layout/__init__.py +8 -8
  64. foxes/input/farm_layout/from_csv.py +1 -1
  65. foxes/input/farm_layout/ring.py +0 -1
  66. foxes/input/states/__init__.py +22 -12
  67. foxes/input/states/create/__init__.py +3 -2
  68. foxes/input/states/field_data_nc.py +10 -24
  69. foxes/input/states/multi_height.py +9 -6
  70. foxes/input/states/one_point_flow.py +0 -4
  71. foxes/input/states/single.py +1 -1
  72. foxes/input/states/states_table.py +10 -7
  73. foxes/input/states/weibull_sectors.py +225 -0
  74. foxes/input/states/wrg_states.py +7 -5
  75. foxes/input/yaml/__init__.py +9 -3
  76. foxes/input/yaml/dict.py +19 -19
  77. foxes/input/yaml/windio/__init__.py +10 -5
  78. foxes/input/yaml/windio/read_attributes.py +2 -2
  79. foxes/input/yaml/windio/read_farm.py +5 -5
  80. foxes/input/yaml/windio/read_fields.py +4 -2
  81. foxes/input/yaml/windio/read_site.py +52 -0
  82. foxes/input/yaml/windio/windio.py +1 -1
  83. foxes/models/__init__.py +15 -14
  84. foxes/models/axial_induction/__init__.py +2 -2
  85. foxes/models/farm_controllers/__init__.py +1 -1
  86. foxes/models/farm_models/__init__.py +1 -1
  87. foxes/models/ground_models/__init__.py +3 -2
  88. foxes/models/ground_models/wake_mirror.py +3 -3
  89. foxes/models/model_book.py +175 -49
  90. foxes/models/partial_wakes/__init__.py +6 -6
  91. foxes/models/partial_wakes/axiwake.py +30 -5
  92. foxes/models/partial_wakes/centre.py +47 -0
  93. foxes/models/partial_wakes/rotor_points.py +41 -11
  94. foxes/models/partial_wakes/segregated.py +2 -25
  95. foxes/models/partial_wakes/top_hat.py +27 -2
  96. foxes/models/point_models/__init__.py +4 -4
  97. foxes/models/rotor_models/__init__.py +3 -3
  98. foxes/models/turbine_models/__init__.py +11 -11
  99. foxes/models/turbine_models/set_farm_vars.py +0 -1
  100. foxes/models/turbine_types/PCt_file.py +0 -2
  101. foxes/models/turbine_types/PCt_from_two.py +0 -2
  102. foxes/models/turbine_types/__init__.py +9 -9
  103. foxes/models/vertical_profiles/__init__.py +7 -7
  104. foxes/models/wake_deflections/__init__.py +3 -0
  105. foxes/models/{wake_frames/yawed_wakes.py → wake_deflections/bastankhah2016.py} +32 -111
  106. foxes/models/wake_deflections/jimenez.py +277 -0
  107. foxes/models/wake_deflections/no_deflection.py +94 -0
  108. foxes/models/wake_frames/__init__.py +6 -7
  109. foxes/models/wake_frames/dynamic_wakes.py +12 -3
  110. foxes/models/wake_frames/rotor_wd.py +3 -1
  111. foxes/models/wake_frames/seq_dynamic_wakes.py +41 -7
  112. foxes/models/wake_frames/streamlines.py +8 -6
  113. foxes/models/wake_frames/timelines.py +9 -3
  114. foxes/models/wake_models/__init__.py +7 -7
  115. foxes/models/wake_models/dist_sliced.py +50 -84
  116. foxes/models/wake_models/gaussian.py +20 -0
  117. foxes/models/wake_models/induction/__init__.py +5 -5
  118. foxes/models/wake_models/induction/rankine_half_body.py +30 -71
  119. foxes/models/wake_models/induction/rathmann.py +65 -64
  120. foxes/models/wake_models/induction/self_similar.py +65 -68
  121. foxes/models/wake_models/induction/self_similar2020.py +0 -3
  122. foxes/models/wake_models/induction/vortex_sheet.py +71 -75
  123. foxes/models/wake_models/ti/__init__.py +2 -2
  124. foxes/models/wake_models/ti/crespo_hernandez.py +5 -3
  125. foxes/models/wake_models/ti/iec_ti.py +6 -4
  126. foxes/models/wake_models/top_hat.py +58 -7
  127. foxes/models/wake_models/wind/__init__.py +6 -4
  128. foxes/models/wake_models/wind/bastankhah14.py +25 -7
  129. foxes/models/wake_models/wind/bastankhah16.py +35 -3
  130. foxes/models/wake_models/wind/jensen.py +15 -2
  131. foxes/models/wake_models/wind/turbopark.py +28 -2
  132. foxes/models/wake_superpositions/__init__.py +18 -9
  133. foxes/models/wake_superpositions/ti_linear.py +4 -4
  134. foxes/models/wake_superpositions/ti_max.py +4 -4
  135. foxes/models/wake_superpositions/ti_pow.py +4 -4
  136. foxes/models/wake_superpositions/ti_quadratic.py +4 -4
  137. foxes/models/wake_superpositions/wind_vector.py +257 -0
  138. foxes/models/wake_superpositions/ws_linear.py +9 -10
  139. foxes/models/wake_superpositions/ws_max.py +8 -8
  140. foxes/models/wake_superpositions/ws_pow.py +8 -8
  141. foxes/models/wake_superpositions/ws_product.py +4 -4
  142. foxes/models/wake_superpositions/ws_quadratic.py +8 -8
  143. foxes/output/__init__.py +21 -19
  144. foxes/output/farm_layout.py +2 -2
  145. foxes/output/farm_results_eval.py +15 -15
  146. foxes/output/flow_plots_2d/__init__.py +2 -2
  147. foxes/output/flow_plots_2d/get_fig.py +4 -2
  148. foxes/output/rose_plot.py +3 -3
  149. foxes/output/seq_plugins/__init__.py +2 -2
  150. foxes/output/seq_plugins/seq_flow_ani_plugin.py +0 -3
  151. foxes/output/seq_plugins/seq_wake_debug_plugin.py +0 -1
  152. foxes/output/turbine_type_curves.py +7 -8
  153. foxes/utils/__init__.py +37 -19
  154. foxes/utils/abl/__init__.py +4 -4
  155. foxes/utils/cubic_roots.py +1 -1
  156. foxes/utils/data_book.py +4 -3
  157. foxes/utils/dict.py +3 -3
  158. foxes/utils/exec_python.py +5 -5
  159. foxes/utils/factory.py +1 -3
  160. foxes/utils/geom2d/__init__.py +7 -5
  161. foxes/utils/geopandas_utils.py +2 -2
  162. foxes/utils/pandas_utils.py +4 -3
  163. foxes/utils/tab_files.py +0 -1
  164. foxes/utils/weibull.py +28 -0
  165. foxes/utils/wrg_utils.py +3 -1
  166. foxes/utils/xarray_utils.py +9 -2
  167. foxes/variables.py +67 -9
  168. {foxes-1.3.dist-info → foxes-1.4.dist-info}/METADATA +6 -15
  169. foxes-1.4.dist-info/RECORD +320 -0
  170. {foxes-1.3.dist-info → foxes-1.4.dist-info}/WHEEL +1 -1
  171. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +2 -3
  172. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +1 -1
  173. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
  174. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +0 -1
  175. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +0 -2
  176. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +0 -1
  177. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +0 -1
  178. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +0 -1
  179. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +0 -1
  180. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +0 -1
  181. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
  182. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +0 -1
  183. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +0 -1
  184. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +0 -1
  185. foxes/output/round.py +0 -10
  186. foxes/utils/pandas_helpers.py +0 -178
  187. foxes-1.3.dist-info/RECORD +0 -313
  188. {foxes-1.3.dist-info → foxes-1.4.dist-info}/entry_points.txt +0 -0
  189. {foxes-1.3.dist-info → foxes-1.4.dist-info/licenses}/LICENSE +0 -0
  190. {foxes-1.3.dist-info → foxes-1.4.dist-info}/top_level.txt +0 -0
@@ -447,7 +447,7 @@ class Bastankhah2016(DistSlicedWakeModel):
447
447
  Parameters for the WakeK class
448
448
 
449
449
  """
450
- super().__init__(superpositions={FV.WS: superposition})
450
+ super().__init__(wind_superposition=superposition)
451
451
 
452
452
  self.model = None
453
453
  self.alpha = alpha
@@ -460,10 +460,23 @@ class Bastankhah2016(DistSlicedWakeModel):
460
460
  def __repr__(self):
461
461
  iname = self.induction
462
462
  s = f"{type(self).__name__}"
463
- s += f"({self.superpositions[FV.WS]}, induction={iname}, "
463
+ s += f"({self.wind_superposition}, induction={iname}, "
464
464
  s += self.wake_k.repr() + ")"
465
465
  return s
466
466
 
467
+ @property
468
+ def affects_ws(self):
469
+ """
470
+ Flag for wind speed wake models
471
+
472
+ Returns
473
+ -------
474
+ dws: bool
475
+ If True, this model affects wind speed
476
+
477
+ """
478
+ return True
479
+
467
480
  def sub_models(self):
468
481
  """
469
482
  List of all sub-models
@@ -474,7 +487,7 @@ class Bastankhah2016(DistSlicedWakeModel):
474
487
  Names of all sub models
475
488
 
476
489
  """
477
- return [self.wake_k, self.model]
490
+ return super().sub_models() + [self.wake_k, self.model]
478
491
 
479
492
  def initialize(self, algo, verbosity=0, force=False):
480
493
  """
@@ -621,4 +634,23 @@ class Bastankhah2016(DistSlicedWakeModel):
621
634
  * np.exp(-0.5 * (z / sigma_z) ** 2)
622
635
  )
623
636
 
637
+ # wake deflection causes wind vector rotation:
638
+ if FC.WDEFL_ROT_ANGLE in tdata:
639
+ dwd_defl = tdata.pop(FC.WDEFL_ROT_ANGLE)
640
+ if FV.WD not in wdeltas:
641
+ wdeltas[FV.WD] = np.zeros_like(wdeltas[FV.WS])
642
+ wdeltas[FV.WD][:] = dwd_defl[st_sel]
643
+ else:
644
+ wdeltas[FV.WD] += dwd_defl[st_sel]
645
+
646
+ # wake deflection causes wind speed reduction:
647
+ if FC.WDEFL_DWS_FACTOR in tdata:
648
+ dws_defl = tdata.pop(FC.WDEFL_DWS_FACTOR)
649
+ if FV.WS not in wdeltas:
650
+ raise AssertionError(
651
+ f"Wake model '{self.name}': Expecting '{FV.WS}' in wdeltas, found {list(wdeltas.keys())}"
652
+ )
653
+ else:
654
+ wdeltas[FV.WS] *= dws_defl[st_sel]
655
+
624
656
  return wdeltas, st_sel
@@ -31,7 +31,7 @@ class JensenWake(TopHatWakeModel):
31
31
  Parameters for the WakeK class
32
32
 
33
33
  """
34
- super().__init__(superpositions={FV.WS: superposition}, induction=induction)
34
+ super().__init__(wind_superposition=superposition, induction=induction)
35
35
  self.wake_k = WakeK(**wake_k)
36
36
 
37
37
  def __repr__(self):
@@ -39,10 +39,23 @@ class JensenWake(TopHatWakeModel):
39
39
  self.induction if isinstance(self.induction, str) else self.induction.name
40
40
  )
41
41
  s = f"{type(self).__name__}"
42
- s += f"({self.superpositions[FV.WS]}, induction={iname}, "
42
+ s += f"({self.wind_superposition}, induction={iname}, "
43
43
  s += self.wake_k.repr() + ")"
44
44
  return s
45
45
 
46
+ @property
47
+ def affects_ws(self):
48
+ """
49
+ Flag for wind speed wake models
50
+
51
+ Returns
52
+ -------
53
+ dws: bool
54
+ If True, this model affects wind speed
55
+
56
+ """
57
+ return True
58
+
46
59
  def calc_wake_radius(
47
60
  self,
48
61
  algo,
@@ -63,7 +63,7 @@ class TurbOParkWake(GaussianWakeModel):
63
63
  Parameters for the WakeK class
64
64
 
65
65
  """
66
- super().__init__(superpositions={FV.WS: superposition})
66
+ super().__init__(wind_superposition=superposition)
67
67
 
68
68
  self.sbeta_factor = sbeta_factor
69
69
  self.c1 = c1
@@ -76,10 +76,23 @@ class TurbOParkWake(GaussianWakeModel):
76
76
  self.induction if isinstance(self.induction, str) else self.induction.name
77
77
  )
78
78
  s = f"{type(self).__name__}"
79
- s += f"({self.superpositions[FV.WS]}, induction={iname}, "
79
+ s += f"({self.wind_superposition}, induction={iname}, "
80
80
  s += self.wake_k.repr() + ")"
81
81
  return s
82
82
 
83
+ @property
84
+ def affects_ws(self):
85
+ """
86
+ Flag for wind speed wake models
87
+
88
+ Returns
89
+ -------
90
+ dws: bool
91
+ If True, this model affects wind speed
92
+
93
+ """
94
+ return True
95
+
83
96
  def sub_models(self):
84
97
  """
85
98
  List of all sub-models
@@ -329,6 +342,19 @@ class TurbOParkWakeIX(GaussianWakeModel):
329
342
  s += self.wake_k.repr() + ")"
330
343
  return s
331
344
 
345
+ @property
346
+ def affects_ws(self):
347
+ """
348
+ Flag for wind speed wake models
349
+
350
+ Returns
351
+ -------
352
+ dws: bool
353
+ If True, this model affects wind speed
354
+
355
+ """
356
+ return True
357
+
332
358
  def sub_models(self):
333
359
  """
334
360
  List of all sub-models
@@ -2,12 +2,21 @@
2
2
  Wake superposition models.
3
3
  """
4
4
 
5
- from .ws_linear import WSLinear, WSLinearLocal
6
- from .ws_quadratic import WSQuadratic, WSQuadraticLocal
7
- from .ws_pow import WSPow, WSPowLocal
8
- from .ws_max import WSMax, WSMaxLocal
9
- from .ws_product import WSProduct
10
- from .ti_linear import TILinear
11
- from .ti_quadratic import TIQuadratic
12
- from .ti_pow import TIPow
13
- from .ti_max import TIMax
5
+ from .ws_linear import WSLinear as WSLinear
6
+ from .ws_linear import WSLinearLocal as WSLinearLocal
7
+
8
+ from .ws_pow import WSPow as WSPow
9
+ from .ws_pow import WSPowLocal as WSPowLocal
10
+
11
+ from .ws_max import WSMax as WSMax
12
+ from .ws_max import WSMaxLocal as WSMaxLocal
13
+
14
+ from .ws_quadratic import WSQuadratic as WSQuadratic
15
+ from .ws_quadratic import WSQuadraticLocal as WSQuadraticLocal
16
+
17
+ from .wind_vector import WindVectorLinear as WindVectorLinear
18
+ from .ws_product import WSProduct as WSProduct
19
+ from .ti_linear import TILinear as TILinear
20
+ from .ti_quadratic import TIQuadratic as TIQuadratic
21
+ from .ti_pow import TIPow as TIPow
22
+ from .ti_max import TIMax as TIMax
@@ -95,8 +95,8 @@ class TILinear(WakeSuperposition):
95
95
  algo,
96
96
  mdata,
97
97
  fdata,
98
+ tdata,
98
99
  variable,
99
- amb_results,
100
100
  wake_delta,
101
101
  ):
102
102
  """
@@ -111,11 +111,10 @@ class TILinear(WakeSuperposition):
111
111
  The model data
112
112
  fdata: foxes.core.FData
113
113
  The farm data
114
+ tdata: foxes.core.TData
115
+ The target point data
114
116
  variable: str
115
117
  The variable name for which the wake deltas applies
116
- amb_results: numpy.ndarray
117
- The ambient results at targets,
118
- shape: (n_states, n_targets, n_tpoints)
119
118
  wake_delta: numpy.ndarray
120
119
  The wake deltas at targets, shape:
121
120
  (n_states, n_targets, n_tpoints)
@@ -134,6 +133,7 @@ class TILinear(WakeSuperposition):
134
133
 
135
134
  # quadratic superposition to ambient:
136
135
  elif self.superp_to_amb == "quadratic":
136
+ amb_results = tdata[FV.var2amb[variable]]
137
137
  return np.sqrt(wake_delta**2 + amb_results**2) - amb_results
138
138
 
139
139
  # unknown ti delta:
@@ -95,8 +95,8 @@ class TIMax(WakeSuperposition):
95
95
  algo,
96
96
  mdata,
97
97
  fdata,
98
+ tdata,
98
99
  variable,
99
- amb_results,
100
100
  wake_delta,
101
101
  ):
102
102
  """
@@ -111,11 +111,10 @@ class TIMax(WakeSuperposition):
111
111
  The model data
112
112
  fdata: foxes.core.FData
113
113
  The farm data
114
+ tdata: foxes.core.TData
115
+ The target point data
114
116
  variable: str
115
117
  The variable name for which the wake deltas applies
116
- amb_results: numpy.ndarray
117
- The ambient results at targets,
118
- shape: (n_states, n_targets, n_tpoints)
119
118
  wake_delta: numpy.ndarray
120
119
  The wake deltas at targets, shape:
121
120
  (n_states, n_targets, n_tpoints)
@@ -134,6 +133,7 @@ class TIMax(WakeSuperposition):
134
133
 
135
134
  # quadratic superposition to ambient:
136
135
  elif self.superp_to_amb == "quadratic":
136
+ amb_results = tdata[FV.var2amb[variable]]
137
137
  return np.sqrt(wake_delta**2 + amb_results**2) - amb_results
138
138
 
139
139
  # unknown ti delta:
@@ -102,8 +102,8 @@ class TIPow(WakeSuperposition):
102
102
  algo,
103
103
  mdata,
104
104
  fdata,
105
+ tdata,
105
106
  variable,
106
- amb_results,
107
107
  wake_delta,
108
108
  ):
109
109
  """
@@ -118,11 +118,10 @@ class TIPow(WakeSuperposition):
118
118
  The model data
119
119
  fdata: foxes.core.FData
120
120
  The farm data
121
+ tdata: foxes.core.TData
122
+ The target point data
121
123
  variable: str
122
124
  The variable name for which the wake deltas applies
123
- amb_results: numpy.ndarray
124
- The ambient results at targets,
125
- shape: (n_states, n_targets, n_tpoints)
126
125
  wake_delta: numpy.ndarray
127
126
  The wake deltas at targets, shape:
128
127
  (n_states, n_targets, n_tpoints)
@@ -141,6 +140,7 @@ class TIPow(WakeSuperposition):
141
140
 
142
141
  # quadratic superposition to ambient:
143
142
  elif self.superp_to_amb == "quadratic":
143
+ amb_results = tdata[FV.var2amb[variable]]
144
144
  return np.sqrt(wake_delta ** (2 / self.pow) + amb_results**2) - amb_results
145
145
 
146
146
  # unknown ti delta:
@@ -95,8 +95,8 @@ class TIQuadratic(WakeSuperposition):
95
95
  algo,
96
96
  mdata,
97
97
  fdata,
98
+ tdata,
98
99
  variable,
99
- amb_results,
100
100
  wake_delta,
101
101
  ):
102
102
  """
@@ -111,11 +111,10 @@ class TIQuadratic(WakeSuperposition):
111
111
  The model data
112
112
  fdata: foxes.core.FData
113
113
  The farm data
114
+ tdata: foxes.core.TData
115
+ The target point data
114
116
  variable: str
115
117
  The variable name for which the wake deltas applies
116
- amb_results: numpy.ndarray
117
- The ambient results at targets,
118
- shape: (n_states, n_targets, n_tpoints)
119
118
  wake_delta: numpy.ndarray
120
119
  The wake deltas at targets, shape:
121
120
  (n_states, n_targets, n_tpoints)
@@ -134,6 +133,7 @@ class TIQuadratic(WakeSuperposition):
134
133
 
135
134
  # quadratic superposition to ambient:
136
135
  elif self.superp_to_amb == "quadratic":
136
+ amb_results = tdata[FV.var2amb[variable]]
137
137
  return np.sqrt(wake_delta + amb_results**2) - amb_results
138
138
 
139
139
  # unknown ti delta:
@@ -0,0 +1,257 @@
1
+ import numpy as np
2
+
3
+ from foxes.core import WindVectorWakeSuperposition
4
+ from foxes.utils import wd2uv, uv2wd, delta_wd
5
+ import foxes.variables as FV
6
+ import foxes.constants as FC
7
+
8
+
9
+ class WindVectorLinear(WindVectorWakeSuperposition):
10
+ """
11
+ Linear superposition of wind deficit vector results
12
+
13
+ Attributes
14
+ ----------
15
+ scale_amb: bool
16
+ Flag for scaling wind deficit with ambient wind speed
17
+ instead of waked wind speed
18
+
19
+ :group: models.wake_superpositions
20
+
21
+ """
22
+
23
+ def __init__(self, scale_amb=False):
24
+ """
25
+ Constructor.
26
+
27
+ Parameters
28
+ ----------
29
+ scale_amb: bool
30
+ Flag for scaling wind deficit with ambient wind speed
31
+ instead of waked wind speed
32
+
33
+ """
34
+ super().__init__()
35
+ self.scale_amb = scale_amb
36
+
37
+ def __repr__(self):
38
+ a = f"scale_amb={self.scale_amb}"
39
+ return f"{type(self).__name__}({a})"
40
+
41
+ def input_farm_vars(self, algo):
42
+ """
43
+ The variables which are needed for running
44
+ the model.
45
+
46
+ Parameters
47
+ ----------
48
+ algo: foxes.core.Algorithm
49
+ The calculation algorithm
50
+
51
+ Returns
52
+ -------
53
+ input_vars: list of str
54
+ The input variable names
55
+
56
+ """
57
+ return [FV.AMB_REWS] if self.scale_amb else [FV.REWS]
58
+
59
+ def wdeltas_ws2uv(self, algo, fdata, tdata, downwind_index, wdeltas, st_sel):
60
+ """
61
+ Transform results from wind speed to wind vector data
62
+
63
+ Parameters
64
+ ----------
65
+ algo: foxes.core.Algorithm
66
+ The calculation algorithm
67
+ fdata: foxes.core.FData
68
+ The farm data
69
+ tdata: foxes.core.TData
70
+ The target point data
71
+ downwind_index: int
72
+ The index of the wake causing turbine
73
+ in the downwind order
74
+ wdeltas: dict
75
+ The wake deltas. Key: variable name str,
76
+ value: numpy.ndarray, shape: (n_st_sel, n_tpoints)
77
+ st_sel: numpy.ndarray of bool
78
+ The state-target selection, for which the wake
79
+ is non-zero, shape: (n_states, n_targets)
80
+
81
+ Returns
82
+ -------
83
+ wdeltas: dict
84
+ The wake deltas. Key: variable name str,
85
+ value: numpy.ndarray, now respecting has_uv flag
86
+
87
+ """
88
+ if FV.AMB_UV not in tdata:
89
+ tdata[FV.AMB_UV] = wd2uv(tdata[FV.AMB_WD], tdata[FV.AMB_WS])
90
+ if FV.UV not in wdeltas:
91
+ assert FV.WS in wdeltas, (
92
+ f"{self.name}: Expecting '{FV.WS}' in wdeltas, got {list(wdeltas.keys())}"
93
+ )
94
+ scale = self.get_data(
95
+ FV.AMB_REWS if self.scale_amb else FV.REWS,
96
+ FC.STATE_TARGET_TPOINT,
97
+ lookup="w",
98
+ algo=algo,
99
+ fdata=fdata,
100
+ tdata=tdata,
101
+ downwind_index=downwind_index,
102
+ upcast=False,
103
+ selection=st_sel,
104
+ )
105
+ ws0 = tdata[FV.AMB_WS][st_sel]
106
+ wd0 = tdata[FV.AMB_WD][st_sel]
107
+ dws = scale * wdeltas.pop(FV.WS)
108
+ dwd = wdeltas.pop(FV.WD, 0)
109
+ wdeltas[FV.UV] = wd2uv(wd0 + dwd, ws0 + dws) - tdata[FV.AMB_UV][st_sel]
110
+
111
+ return wdeltas
112
+
113
+ def wdeltas_uv2ws(self, algo, fdata, tdata, downwind_index, wdeltas, st_sel):
114
+ """
115
+ Transform results from wind vector to wind speed data
116
+
117
+ Parameters
118
+ ----------
119
+ algo: foxes.core.Algorithm
120
+ The calculation algorithm
121
+ fdata: foxes.core.FData
122
+ The farm data
123
+ tdata: foxes.core.TData
124
+ The target point data
125
+ downwind_index: int
126
+ The index of the wake causing turbine
127
+ in the downwind order
128
+ wdeltas: dict
129
+ The wake deltas. Key: variable name str,
130
+ value: numpy.ndarray, shape: (n_st_sel, n_tpoints)
131
+ st_sel: numpy.ndarray of bool
132
+ The state-target selection, for which the wake
133
+ is non-zero, shape: (n_states, n_targets)
134
+
135
+ Returns
136
+ -------
137
+ wdeltas: dict
138
+ The wake deltas. Key: variable name str,
139
+ value: numpy.ndarray, now respecting has_uv flag
140
+
141
+ """
142
+ if FV.UV in wdeltas:
143
+ scale = self.get_data(
144
+ FV.AMB_REWS if self.scale_amb else FV.REWS,
145
+ FC.STATE_TARGET_TPOINT,
146
+ lookup="w",
147
+ algo=algo,
148
+ fdata=fdata,
149
+ tdata=tdata,
150
+ downwind_index=downwind_index,
151
+ upcast=False,
152
+ selection=st_sel,
153
+ )
154
+ ws0 = tdata[FV.AMB_WS][st_sel]
155
+ wd0 = tdata[FV.AMB_WD][st_sel]
156
+ uv = tdata[FV.AMB_UV][st_sel] + wdeltas.pop(FV.UV)
157
+ wdeltas[FV.WD] = delta_wd(wd0, uv2wd(uv))
158
+ wdeltas[FV.WS] = (np.linalg.norm(uv, axis=-1) - ws0) / scale
159
+
160
+ return wdeltas
161
+
162
+ def add_wake_vector(
163
+ self,
164
+ algo,
165
+ mdata,
166
+ fdata,
167
+ tdata,
168
+ downwind_index,
169
+ st_sel,
170
+ wake_delta_uv,
171
+ wake_model_result_uv,
172
+ ):
173
+ """
174
+ Add a wake delta vector to previous wake deltas,
175
+ at rotor points.
176
+
177
+ Parameters
178
+ ----------
179
+ algo: foxes.core.Algorithm
180
+ The calculation algorithm
181
+ mdata: foxes.core.MData
182
+ The model data
183
+ fdata: foxes.core.FData
184
+ The farm data
185
+ tdata: foxes.core.TData
186
+ The target point data
187
+ downwind_index: int
188
+ The index of the wake causing turbine
189
+ in the downwind order
190
+ st_sel: numpy.ndarray of bool
191
+ The selection of targets, shape: (n_states, n_targets)
192
+ wake_delta_uv: numpy.ndarray
193
+ The original wind vector wake deltas, shape:
194
+ (n_states, n_targets, n_tpoints, 2)
195
+ wake_model_result_uv: numpy.ndarray
196
+ The new wind vector wake deltas of the selected rotors,
197
+ shape: (n_st_sel, n_tpoints, 2, ...)
198
+
199
+ Returns
200
+ -------
201
+ wdelta_uv: numpy.ndarray
202
+ The updated wind vector wake deltas, shape:
203
+ (n_states, n_targets, n_tpoints, ...)
204
+
205
+ """
206
+
207
+ if np.any(st_sel):
208
+ wake_delta_uv[st_sel] += wake_model_result_uv
209
+
210
+ return wake_delta_uv
211
+
212
+ def calc_final_wake_delta_uv(
213
+ self,
214
+ algo,
215
+ mdata,
216
+ fdata,
217
+ tdata,
218
+ wake_delta_uv,
219
+ ):
220
+ """
221
+ Calculate the final wind vector wake delta after adding all
222
+ contributions.
223
+
224
+ Parameters
225
+ ----------
226
+ algo: foxes.core.Algorithm
227
+ The calculation algorithm
228
+ mdata: foxes.core.MData
229
+ The model data
230
+ fdata: foxes.core.FData
231
+ The farm data
232
+ tdata: foxes.core.TData
233
+ The target point data
234
+ wake_delta_uv: numpy.ndarray
235
+ The original wind vector wake deltas, shape:
236
+ (n_states, n_targets, n_tpoints, 2)
237
+
238
+ Returns
239
+ -------
240
+ final_wake_delta_ws: numpy.ndarray
241
+ The final wind speed wake delta, which will be added to
242
+ the ambient results by simple plus operation. Shape:
243
+ (n_states, n_targets, n_tpoints)
244
+ final_wake_delta_wd: numpy.ndarray
245
+ The final wind direction wake delta, which will be added to
246
+ the ambient results by simple plus operation. Shape:
247
+ (n_states, n_targets, n_tpoints)
248
+
249
+ """
250
+ if FV.AMB_UV not in tdata:
251
+ tdata[FV.AMB_UV] = wd2uv(tdata[FV.AMB_WD], tdata[FV.AMB_WS])
252
+
253
+ uv = tdata[FV.AMB_UV] + wake_delta_uv
254
+ dwd = delta_wd(tdata[FV.AMB_WD], uv2wd(uv))
255
+ dws = np.linalg.norm(uv, axis=-1) - tdata[FV.AMB_WS]
256
+
257
+ return dws, dwd
@@ -140,8 +140,8 @@ class WSLinear(WakeSuperposition):
140
140
  algo,
141
141
  mdata,
142
142
  fdata,
143
+ tdata,
143
144
  variable,
144
- amb_results,
145
145
  wake_delta,
146
146
  ):
147
147
  """
@@ -156,11 +156,10 @@ class WSLinear(WakeSuperposition):
156
156
  The model data
157
157
  fdata: foxes.core.FData
158
158
  The farm data
159
+ tdata: foxes.core.TData
160
+ The target point data
159
161
  variable: str
160
162
  The variable name for which the wake deltas applies
161
- amb_results: numpy.ndarray
162
- The ambient results at targets,
163
- shape: (n_states, n_targets, n_tpoints)
164
163
  wake_delta: numpy.ndarray
165
164
  The wake deltas at targets, shape:
166
165
  (n_states, n_targets, n_tpoints)
@@ -175,9 +174,9 @@ class WSLinear(WakeSuperposition):
175
174
  """
176
175
  w = wake_delta
177
176
  if self.lim_low is not None:
178
- w = np.maximum(w, self.lim_low - amb_results)
177
+ w = np.maximum(w, self.lim_low - tdata[FV.var2amb[variable]])
179
178
  if self.lim_high is not None:
180
- w = np.minimum(w, self.lim_high - amb_results)
179
+ w = np.minimum(w, self.lim_high - tdata[FV.var2amb[variable]])
181
180
  return w
182
181
 
183
182
 
@@ -296,8 +295,8 @@ class WSLinearLocal(WakeSuperposition):
296
295
  algo,
297
296
  mdata,
298
297
  fdata,
298
+ tdata,
299
299
  variable,
300
- amb_results,
301
300
  wake_delta,
302
301
  ):
303
302
  """
@@ -312,11 +311,10 @@ class WSLinearLocal(WakeSuperposition):
312
311
  The model data
313
312
  fdata: foxes.core.FData
314
313
  The farm data
314
+ tdata: foxes.core.TData
315
+ The target point data
315
316
  variable: str
316
317
  The variable name for which the wake deltas applies
317
- amb_results: numpy.ndarray
318
- The ambient results at targets,
319
- shape: (n_states, n_targets, n_tpoints)
320
318
  wake_delta: numpy.ndarray
321
319
  The wake deltas at targets, shape:
322
320
  (n_states, n_targets, n_tpoints)
@@ -329,6 +327,7 @@ class WSLinearLocal(WakeSuperposition):
329
327
  (n_states, n_targets, n_tpoints)
330
328
 
331
329
  """
330
+ amb_results = tdata[FV.var2amb[variable]]
332
331
  w = wake_delta * amb_results
333
332
  if self.lim_low is not None:
334
333
  w = np.maximum(w, self.lim_low - amb_results)
@@ -143,8 +143,8 @@ class WSMax(WakeSuperposition):
143
143
  algo,
144
144
  mdata,
145
145
  fdata,
146
+ tdata,
146
147
  variable,
147
- amb_results,
148
148
  wake_delta,
149
149
  ):
150
150
  """
@@ -159,11 +159,10 @@ class WSMax(WakeSuperposition):
159
159
  The model data
160
160
  fdata: foxes.core.FData
161
161
  The farm data
162
+ tdata: foxes.core.TData
163
+ The target point data
162
164
  variable: str
163
165
  The variable name for which the wake deltas applies
164
- amb_results: numpy.ndarray
165
- The ambient results at targets,
166
- shape: (n_states, n_targets, n_tpoints)
167
166
  wake_delta: numpy.ndarray
168
167
  The wake deltas at targets, shape:
169
168
  (n_states, n_targets, n_tpoints)
@@ -176,6 +175,7 @@ class WSMax(WakeSuperposition):
176
175
  (n_states, n_targets, n_tpoints)
177
176
 
178
177
  """
178
+ amb_results = tdata[FV.var2amb[variable]]
179
179
  w = -wake_delta
180
180
  if self.lim_low is not None:
181
181
  w = np.maximum(w, self.lim_low - amb_results)
@@ -302,8 +302,8 @@ class WSMaxLocal(WakeSuperposition):
302
302
  algo,
303
303
  mdata,
304
304
  fdata,
305
+ tdata,
305
306
  variable,
306
- amb_results,
307
307
  wake_delta,
308
308
  ):
309
309
  """
@@ -318,11 +318,10 @@ class WSMaxLocal(WakeSuperposition):
318
318
  The model data
319
319
  fdata: foxes.core.FData
320
320
  The farm data
321
+ tdata: foxes.core.TData
322
+ The target point data
321
323
  variable: str
322
324
  The variable name for which the wake deltas applies
323
- amb_results: numpy.ndarray
324
- The ambient results at targets,
325
- shape: (n_states, n_targets, n_tpoints)
326
325
  wake_delta: numpy.ndarray
327
326
  The wake deltas at targets, shape:
328
327
  (n_states, n_targets, n_tpoints)
@@ -335,6 +334,7 @@ class WSMaxLocal(WakeSuperposition):
335
334
  (n_states, n_targets, n_tpoints)
336
335
 
337
336
  """
337
+ amb_results = tdata[FV.var2amb[variable]]
338
338
  w = -wake_delta * amb_results
339
339
  if self.lim_low is not None:
340
340
  w = np.maximum(w, self.lim_low - amb_results)