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
@@ -77,7 +77,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
77
77
  return FV.REWS if wse == 1 else (FV.REWS2 if wse == 2 else FV.REWS3)
78
78
 
79
79
  _print(f" Creating model '{tname}'", level=3)
80
- _print(f" Turbine type class: PCtFomTwo", level=3)
80
+ _print(" Turbine type class: PCtFomTwo", level=3)
81
81
  mbook.turbine_types[tname] = TurbineType.new(
82
82
  ttype_type="PCtFromTwo",
83
83
  data_source_P=data_P,
@@ -111,7 +111,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
111
111
  data_ct = pd.DataFrame(data={"ws": ws_ct, "ct": ct})
112
112
 
113
113
  _print(f" Creating model '{tname}'", level=3)
114
- _print(f" Turbine type class: CpCtFromTwo", level=3)
114
+ _print(" Turbine type class: CpCtFromTwo", level=3)
115
115
  mbook.turbine_types[tname] = TurbineType.new(
116
116
  ttype_type="CpCtFromTwo",
117
117
  data_source_cp=data_cp,
@@ -126,7 +126,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
126
126
  _print(" ", mbook.turbine_types[tname], level=3)
127
127
 
128
128
  else:
129
- raise KeyError(f"Expecting either 'power_curve' or 'Cp_curve'")
129
+ raise KeyError("Expecting either 'power_curve' or 'Cp_curve'")
130
130
 
131
131
  return ttypes
132
132
 
@@ -157,7 +157,7 @@ def read_layout(lname, ldict, farm, ttypes, verbosity=1):
157
157
  cdict = Dict(ldict["coordinates"], name="coordinates")
158
158
  tmap = ldict.get_item("turbine_types", None)
159
159
  if verbosity > 2:
160
- print(f" Turbine type map:", tmap)
160
+ print(" Turbine type map:", tmap)
161
161
  for i, xy in enumerate(zip(cdict["x"], cdict["y"])):
162
162
  tt = ttypes[tmap[i] if tmap is not None else 0]
163
163
  farm.add_turbine(
@@ -211,7 +211,7 @@ def read_farm(wio_dict, mbook, verbosity):
211
211
  if isinstance(wfarm, dict):
212
212
  layouts = Dict(wfarm, name=wio_farm.name + ".layouts")
213
213
  else:
214
- layouts = {str(i): l for i, l in enumerate(wfarm)}
214
+ layouts = {str(i): lf for i, lf in enumerate(wfarm)}
215
215
  layouts = Dict(layouts, name=wio_farm.name + ".layouts")
216
216
  if verbosity > 2:
217
217
  print(" Reading layouts")
@@ -22,6 +22,8 @@ wio2foxes = {
22
22
  "LMO": FV.MOL,
23
23
  "z0": FV.Z0,
24
24
  "reference_height": FV.H,
25
+ "weibull_a": FV.WEIBULL_A,
26
+ "weibull_k": FV.WEIBULL_k,
25
27
  }
26
28
 
27
29
  """ Mapping from foxes to windio variables
@@ -134,8 +136,6 @@ def read_wind_resource_field(
134
136
 
135
137
  """
136
138
  if name in [
137
- "weibull_a",
138
- "weibull_k",
139
139
  "potential_temperature",
140
140
  "friction_velocity",
141
141
  "k",
@@ -178,6 +178,8 @@ def read_wind_resource_field(
178
178
  "LMO",
179
179
  "z0",
180
180
  "reference_height",
181
+ "weibull_a",
182
+ "weibull_k",
181
183
  ] and _read_multi_dimensional_data(name, wio_data, fields, dims):
182
184
  return True
183
185
 
@@ -159,6 +159,55 @@ def _get_MultiHeightNCTimeseries(
159
159
  return False
160
160
 
161
161
 
162
+ def _get_WeibullSectors(
163
+ coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
164
+ ):
165
+ """Try to generate Weibull sector parameters
166
+ :group: input.yaml.windio
167
+ """
168
+ if (
169
+ FV.WEIBULL_A in fields
170
+ and FV.WEIBULL_k in fields
171
+ and "sector_probability" in fields
172
+ and len(dims[FV.WEIBULL_A]) == 1
173
+ and len(dims[FV.WEIBULL_k]) == 1
174
+ and len(dims["sector_probability"]) == 1
175
+ ):
176
+ if verbosity > 2:
177
+ print(" selecting class 'WeibullSectors'")
178
+
179
+ data = {}
180
+ fix = {}
181
+ c = dims[FV.WEIBULL_A][0]
182
+ for v, d in fields.items():
183
+ if dims[v] == (c,):
184
+ data[v] = d
185
+ elif len(dims[v]) == 0:
186
+ fix[v] = d
187
+ elif verbosity > 2:
188
+ print(f" ignoring field '{v}' with dims {dims[v]}")
189
+ fix.update({v: d for v, d in fixval.items() if v not in data})
190
+
191
+ if FV.WD in coords:
192
+ data[FV.WD] = coords[FV.WD]
193
+
194
+ sdata = pd.DataFrame(index=range(len(fields[FV.WEIBULL_A])), data=data)
195
+ sdata.index.name = "sector"
196
+ states_dict.update(
197
+ dict(
198
+ states_type="WeibullSectors",
199
+ data_source=sdata,
200
+ ws_bins=coords.get(FV.WS, np.arange(30)),
201
+ output_vars=ovars,
202
+ var2ncvar={FV.WEIGHT: "sector_probability"},
203
+ fixed_vars=fix,
204
+ profiles=profiles,
205
+ )
206
+ )
207
+ return True
208
+ return False
209
+
210
+
162
211
  def get_states(coords, fields, dims, verbosity=1):
163
212
  """
164
213
  Reads states parameters from windio input
@@ -200,6 +249,9 @@ def get_states(coords, fields, dims, verbosity=1):
200
249
  or _get_MultiHeightNCTimeseries(
201
250
  coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
202
251
  )
252
+ or _get_WeibullSectors(
253
+ coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
254
+ )
203
255
  ):
204
256
  return States.new(**states_dict)
205
257
  else:
@@ -43,7 +43,7 @@ def read_windio(wio_dict, verbosity=1):
43
43
  if not isinstance(wio_dict, Dict):
44
44
  wio_dict = Dict(wio_dict, name="windio")
45
45
 
46
- _print(f"Reading windio data")
46
+ _print("Reading windio data")
47
47
  _print(" Name:", wio_dict.pop_item("name", None))
48
48
  _print(" Contents:", [k for k in wio_dict.keys()])
49
49
 
foxes/models/__init__.py CHANGED
@@ -2,18 +2,19 @@
2
2
  Model collection.
3
3
  """
4
4
 
5
- from . import turbine_types
6
- from . import rotor_models
7
- from . import turbine_models
8
- from . import farm_models
9
- from . import partial_wakes
10
- from . import wake_frames
11
- from . import wake_models
12
- from . import wake_superpositions
13
- from . import farm_controllers
14
- from . import vertical_profiles
15
- from . import point_models
16
- from . import axial_induction
17
- from . import ground_models
5
+ from . import turbine_types as turbine_types
6
+ from . import rotor_models as rotor_models
7
+ from . import turbine_models as turbine_models
8
+ from . import farm_models as farm_models
9
+ from . import partial_wakes as partial_wakes
10
+ from . import wake_frames as wake_frames
11
+ from . import wake_models as wake_models
12
+ from . import wake_deflections as wake_deflections
13
+ from . import wake_superpositions as wake_superpositions
14
+ from . import farm_controllers as farm_controllers
15
+ from . import vertical_profiles as vertical_profiles
16
+ from . import point_models as point_models
17
+ from . import axial_induction as axial_induction
18
+ from . import ground_models as ground_models
18
19
 
19
- from .model_book import ModelBook
20
+ from .model_book import ModelBook as ModelBook
@@ -1,2 +1,2 @@
1
- from .betz import BetzAxialInduction
2
- from .madsen import MadsenAxialInduction
1
+ from .betz import BetzAxialInduction as BetzAxialInduction
2
+ from .madsen import MadsenAxialInduction as MadsenAxialInduction
@@ -2,4 +2,4 @@
2
2
  Farm controller models.
3
3
  """
4
4
 
5
- from .basic import BasicFarmController
5
+ from .basic import BasicFarmController as BasicFarmController
@@ -2,4 +2,4 @@
2
2
  Farm models.
3
3
  """
4
4
 
5
- from .turbine2farm import Turbine2FarmModel
5
+ from .turbine2farm import Turbine2FarmModel as Turbine2FarmModel
@@ -1,2 +1,3 @@
1
- from .no_ground import NoGround
2
- from .wake_mirror import WakeMirror, GroundMirror
1
+ from .no_ground import NoGround as NoGround
2
+ from .wake_mirror import WakeMirror as WakeMirror
3
+ from .wake_mirror import GroundMirror as GroundMirror
@@ -75,13 +75,14 @@ class WakeMirror(GroundModel):
75
75
  # assert(np.all(fdata[FV.H]==fdata[FV.TXYH[..., 2]]))
76
76
 
77
77
  # contribution from main wake:
78
- wcoos = algo.wake_frame.get_wake_coos(algo, mdata, fdata, tdata, downwind_index)
78
+ wcoos = algo.wake_frame.get_wake_coos(
79
+ algo, mdata, fdata, tdata, downwind_index, wmodel
80
+ )
79
81
  wmodel.contribute(algo, mdata, fdata, tdata, downwind_index, wcoos, wake_deltas)
80
82
 
81
83
  # contribution from mirrors:
82
84
  tdata[FC.TARGETS] = tdata[FC.TARGETS].copy() # making sure this is no ref
83
85
  for h in self.heights:
84
-
85
86
  fdata[FV.TXYH][:, downwind_index, 2] = hh + 2 * (h - hh)
86
87
 
87
88
  pwake.contribute(
@@ -136,7 +137,6 @@ class WakeMirror(GroundModel):
136
137
  # contribution from mirrors:
137
138
  tdata[FC.TARGETS] = tdata[FC.TARGETS].copy() # making sure this is no ref
138
139
  for h in self.heights:
139
-
140
140
  fdata[FV.TXYH][:, downwind_index, 2] = hh + 2 * (h - hh)
141
141
 
142
142
  wcoos = algo.wake_frame.get_wake_coos(
@@ -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,