foxes 1.3__py3-none-any.whl → 1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of foxes might be problematic. Click here for more details.

Files changed (228) hide show
  1. docs/source/conf.py +3 -3
  2. examples/abl_states/run.py +2 -2
  3. examples/compare_rotors_pwakes/run.py +1 -1
  4. examples/compare_wakes/run.py +1 -2
  5. examples/dyn_wakes/run.py +29 -6
  6. examples/field_data_nc/run.py +1 -1
  7. examples/induction/run.py +3 -3
  8. examples/multi_height/run.py +1 -1
  9. examples/power_mask/run.py +2 -2
  10. examples/quickstart/run.py +0 -1
  11. examples/random_timeseries/run.py +3 -4
  12. examples/scan_row/run.py +3 -3
  13. examples/sequential/run.py +33 -10
  14. examples/single_state/run.py +3 -4
  15. examples/states_lookup_table/run.py +3 -3
  16. examples/streamline_wakes/run.py +29 -6
  17. examples/tab_file/run.py +3 -3
  18. examples/timelines/run.py +29 -5
  19. examples/timeseries/run.py +3 -3
  20. examples/timeseries_slurm/run.py +3 -3
  21. examples/wind_rose/run.py +3 -3
  22. examples/yawed_wake/run.py +19 -9
  23. foxes/__init__.py +21 -17
  24. foxes/algorithms/__init__.py +6 -6
  25. foxes/algorithms/downwind/__init__.py +2 -2
  26. foxes/algorithms/downwind/downwind.py +49 -17
  27. foxes/algorithms/downwind/models/__init__.py +6 -6
  28. foxes/algorithms/downwind/models/farm_wakes_calc.py +11 -9
  29. foxes/algorithms/downwind/models/init_farm_data.py +58 -29
  30. foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
  31. foxes/algorithms/downwind/models/set_amb_farm_results.py +1 -1
  32. foxes/algorithms/downwind/models/set_amb_point_results.py +6 -6
  33. foxes/algorithms/iterative/__init__.py +7 -3
  34. foxes/algorithms/iterative/iterative.py +1 -2
  35. foxes/algorithms/iterative/models/__init__.py +7 -3
  36. foxes/algorithms/iterative/models/farm_wakes_calc.py +9 -5
  37. foxes/algorithms/sequential/__init__.py +3 -3
  38. foxes/algorithms/sequential/models/__init__.py +2 -2
  39. foxes/algorithms/sequential/sequential.py +3 -4
  40. foxes/config/__init__.py +5 -1
  41. foxes/constants.py +16 -0
  42. foxes/core/__init__.py +45 -22
  43. foxes/core/algorithm.py +5 -6
  44. foxes/core/data.py +94 -22
  45. foxes/core/data_calc_model.py +4 -2
  46. foxes/core/engine.py +42 -53
  47. foxes/core/farm_controller.py +2 -2
  48. foxes/core/farm_data_model.py +16 -13
  49. foxes/core/ground_model.py +4 -13
  50. foxes/core/model.py +24 -6
  51. foxes/core/partial_wakes_model.py +147 -10
  52. foxes/core/point_data_model.py +21 -17
  53. foxes/core/rotor_model.py +4 -3
  54. foxes/core/states.py +2 -3
  55. foxes/core/turbine.py +2 -1
  56. foxes/core/wake_deflection.py +130 -0
  57. foxes/core/wake_model.py +222 -9
  58. foxes/core/wake_superposition.py +122 -4
  59. foxes/core/wind_farm.py +6 -6
  60. foxes/data/__init__.py +7 -2
  61. foxes/data/states/point_cloud_100.nc +0 -0
  62. foxes/data/states/weibull_cloud_4.nc +0 -0
  63. foxes/data/states/weibull_grid.nc +0 -0
  64. foxes/data/states/weibull_sectors_12.csv +13 -0
  65. foxes/data/states/weibull_sectors_12.nc +0 -0
  66. foxes/engines/__init__.py +14 -15
  67. foxes/engines/dask.py +42 -20
  68. foxes/engines/default.py +2 -2
  69. foxes/engines/numpy.py +11 -13
  70. foxes/engines/pool.py +20 -11
  71. foxes/engines/single.py +8 -6
  72. foxes/input/__init__.py +3 -3
  73. foxes/input/farm_layout/__init__.py +9 -8
  74. foxes/input/farm_layout/from_arrays.py +68 -0
  75. foxes/input/farm_layout/from_csv.py +1 -1
  76. foxes/input/farm_layout/ring.py +0 -1
  77. foxes/input/states/__init__.py +28 -12
  78. foxes/input/states/create/__init__.py +3 -2
  79. foxes/input/states/dataset_states.py +710 -0
  80. foxes/input/states/field_data.py +531 -0
  81. foxes/input/states/multi_height.py +11 -6
  82. foxes/input/states/one_point_flow.py +1 -4
  83. foxes/input/states/point_cloud_data.py +618 -0
  84. foxes/input/states/scan.py +2 -0
  85. foxes/input/states/single.py +3 -1
  86. foxes/input/states/states_table.py +23 -30
  87. foxes/input/states/weibull_sectors.py +330 -0
  88. foxes/input/states/wrg_states.py +8 -6
  89. foxes/input/yaml/__init__.py +9 -3
  90. foxes/input/yaml/dict.py +42 -41
  91. foxes/input/yaml/windio/__init__.py +10 -5
  92. foxes/input/yaml/windio/read_attributes.py +42 -29
  93. foxes/input/yaml/windio/read_farm.py +17 -15
  94. foxes/input/yaml/windio/read_fields.py +4 -2
  95. foxes/input/yaml/windio/read_outputs.py +25 -15
  96. foxes/input/yaml/windio/read_site.py +172 -11
  97. foxes/input/yaml/windio/windio.py +23 -11
  98. foxes/input/yaml/yaml.py +1 -0
  99. foxes/models/__init__.py +15 -14
  100. foxes/models/axial_induction/__init__.py +2 -2
  101. foxes/models/farm_controllers/__init__.py +1 -1
  102. foxes/models/farm_models/__init__.py +1 -1
  103. foxes/models/ground_models/__init__.py +3 -2
  104. foxes/models/ground_models/wake_mirror.py +3 -3
  105. foxes/models/model_book.py +190 -63
  106. foxes/models/partial_wakes/__init__.py +6 -6
  107. foxes/models/partial_wakes/axiwake.py +30 -5
  108. foxes/models/partial_wakes/centre.py +47 -0
  109. foxes/models/partial_wakes/rotor_points.py +41 -11
  110. foxes/models/partial_wakes/segregated.py +2 -25
  111. foxes/models/partial_wakes/top_hat.py +27 -2
  112. foxes/models/point_models/__init__.py +4 -4
  113. foxes/models/rotor_models/__init__.py +4 -3
  114. foxes/models/rotor_models/centre.py +1 -1
  115. foxes/models/rotor_models/direct_infusion.py +241 -0
  116. foxes/models/turbine_models/__init__.py +11 -11
  117. foxes/models/turbine_models/calculator.py +16 -3
  118. foxes/models/turbine_models/kTI_model.py +1 -0
  119. foxes/models/turbine_models/lookup_table.py +2 -0
  120. foxes/models/turbine_models/power_mask.py +1 -0
  121. foxes/models/turbine_models/rotor_centre_calc.py +2 -0
  122. foxes/models/turbine_models/sector_management.py +1 -0
  123. foxes/models/turbine_models/set_farm_vars.py +3 -9
  124. foxes/models/turbine_models/table_factors.py +2 -0
  125. foxes/models/turbine_models/thrust2ct.py +1 -0
  126. foxes/models/turbine_models/yaw2yawm.py +2 -0
  127. foxes/models/turbine_models/yawm2yaw.py +2 -0
  128. foxes/models/turbine_types/PCt_file.py +2 -6
  129. foxes/models/turbine_types/PCt_from_two.py +1 -2
  130. foxes/models/turbine_types/__init__.py +10 -9
  131. foxes/models/turbine_types/calculator_type.py +123 -0
  132. foxes/models/turbine_types/null_type.py +1 -0
  133. foxes/models/turbine_types/wsrho2PCt_from_two.py +2 -0
  134. foxes/models/turbine_types/wsti2PCt_from_two.py +3 -1
  135. foxes/models/vertical_profiles/__init__.py +7 -7
  136. foxes/models/wake_deflections/__init__.py +3 -0
  137. foxes/models/{wake_frames/yawed_wakes.py → wake_deflections/bastankhah2016.py} +32 -111
  138. foxes/models/wake_deflections/jimenez.py +277 -0
  139. foxes/models/wake_deflections/no_deflection.py +94 -0
  140. foxes/models/wake_frames/__init__.py +6 -7
  141. foxes/models/wake_frames/dynamic_wakes.py +12 -3
  142. foxes/models/wake_frames/rotor_wd.py +3 -1
  143. foxes/models/wake_frames/seq_dynamic_wakes.py +41 -7
  144. foxes/models/wake_frames/streamlines.py +8 -6
  145. foxes/models/wake_frames/timelines.py +9 -3
  146. foxes/models/wake_models/__init__.py +7 -7
  147. foxes/models/wake_models/dist_sliced.py +50 -84
  148. foxes/models/wake_models/gaussian.py +20 -0
  149. foxes/models/wake_models/induction/__init__.py +5 -5
  150. foxes/models/wake_models/induction/rankine_half_body.py +30 -71
  151. foxes/models/wake_models/induction/rathmann.py +65 -64
  152. foxes/models/wake_models/induction/self_similar.py +65 -68
  153. foxes/models/wake_models/induction/self_similar2020.py +0 -3
  154. foxes/models/wake_models/induction/vortex_sheet.py +71 -75
  155. foxes/models/wake_models/ti/__init__.py +2 -2
  156. foxes/models/wake_models/ti/crespo_hernandez.py +5 -3
  157. foxes/models/wake_models/ti/iec_ti.py +6 -4
  158. foxes/models/wake_models/top_hat.py +58 -7
  159. foxes/models/wake_models/wind/__init__.py +6 -4
  160. foxes/models/wake_models/wind/bastankhah14.py +25 -7
  161. foxes/models/wake_models/wind/bastankhah16.py +35 -3
  162. foxes/models/wake_models/wind/jensen.py +15 -2
  163. foxes/models/wake_models/wind/turbopark.py +28 -2
  164. foxes/models/wake_superpositions/__init__.py +18 -9
  165. foxes/models/wake_superpositions/ti_linear.py +4 -4
  166. foxes/models/wake_superpositions/ti_max.py +4 -4
  167. foxes/models/wake_superpositions/ti_pow.py +4 -4
  168. foxes/models/wake_superpositions/ti_quadratic.py +4 -4
  169. foxes/models/wake_superpositions/wind_vector.py +257 -0
  170. foxes/models/wake_superpositions/ws_linear.py +9 -10
  171. foxes/models/wake_superpositions/ws_max.py +8 -8
  172. foxes/models/wake_superpositions/ws_pow.py +8 -8
  173. foxes/models/wake_superpositions/ws_product.py +4 -4
  174. foxes/models/wake_superpositions/ws_quadratic.py +8 -8
  175. foxes/output/__init__.py +21 -19
  176. foxes/output/farm_layout.py +4 -2
  177. foxes/output/farm_results_eval.py +19 -16
  178. foxes/output/flow_plots_2d/__init__.py +2 -2
  179. foxes/output/flow_plots_2d/flow_plots.py +18 -0
  180. foxes/output/flow_plots_2d/get_fig.py +5 -2
  181. foxes/output/output.py +6 -1
  182. foxes/output/results_writer.py +1 -1
  183. foxes/output/rose_plot.py +13 -3
  184. foxes/output/rotor_point_plots.py +3 -0
  185. foxes/output/seq_plugins/__init__.py +2 -2
  186. foxes/output/seq_plugins/seq_flow_ani_plugin.py +0 -3
  187. foxes/output/seq_plugins/seq_wake_debug_plugin.py +0 -1
  188. foxes/output/state_turbine_map.py +3 -0
  189. foxes/output/turbine_type_curves.py +10 -8
  190. foxes/utils/__init__.py +37 -19
  191. foxes/utils/abl/__init__.py +4 -4
  192. foxes/utils/cubic_roots.py +1 -1
  193. foxes/utils/data_book.py +4 -3
  194. foxes/utils/dict.py +49 -37
  195. foxes/utils/exec_python.py +5 -5
  196. foxes/utils/factory.py +3 -5
  197. foxes/utils/geom2d/__init__.py +7 -5
  198. foxes/utils/geopandas_utils.py +2 -2
  199. foxes/utils/pandas_utils.py +4 -3
  200. foxes/utils/tab_files.py +0 -1
  201. foxes/utils/weibull.py +28 -0
  202. foxes/utils/wrg_utils.py +3 -1
  203. foxes/utils/xarray_utils.py +9 -2
  204. foxes/variables.py +67 -9
  205. {foxes-1.3.dist-info → foxes-1.5.dist-info}/METADATA +34 -63
  206. foxes-1.5.dist-info/RECORD +328 -0
  207. {foxes-1.3.dist-info → foxes-1.5.dist-info}/WHEEL +1 -1
  208. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +2 -3
  209. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +1 -1
  210. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
  211. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +0 -1
  212. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +0 -2
  213. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +0 -1
  214. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +0 -1
  215. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +0 -1
  216. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +0 -1
  217. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +0 -1
  218. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
  219. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +0 -1
  220. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +0 -1
  221. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +0 -1
  222. foxes/input/states/field_data_nc.py +0 -847
  223. foxes/output/round.py +0 -10
  224. foxes/utils/pandas_helpers.py +0 -178
  225. foxes-1.3.dist-info/RECORD +0 -313
  226. {foxes-1.3.dist-info → foxes-1.5.dist-info}/entry_points.txt +0 -0
  227. {foxes-1.3.dist-info → foxes-1.5.dist-info/licenses}/LICENSE +0 -0
  228. {foxes-1.3.dist-info → foxes-1.5.dist-info}/top_level.txt +0 -0
@@ -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,25 +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 = {
144
- v: d[:, downwind_index, None] if d.shape[1] > 1 else d[:, 0, None]
145
- for v, d in amb_res.items()
146
- }
147
- else:
148
- ares = {}
149
- for v, d in amb_res.items():
150
- ares[v] = np.zeros(
151
- (n_states, 1, tdata.n_tpoints), dtype=config.dtype_double
152
- )
153
- ares[v][:] = np.einsum(
154
- "sp,p->s",
155
- d[:, downwind_index] if d.shape[1] > 1 else d[:, 0],
156
- rpoint_weights,
157
- )[:, None, None]
158
-
159
- 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)
160
137
 
161
138
  for v in wdel.keys():
162
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,7 @@
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
8
+ from .direct_infusion import DirectMDataInfusion as DirectMDataInfusion
@@ -199,4 +199,4 @@ class CentreRotor(RotorModel):
199
199
  self._set_res(fdata, v, res, downwind_index)
200
200
  del res
201
201
  if copy_to_ambient and v in FV.var2amb:
202
- fdata[FV.var2amb[v]] = fdata[v].copy()
202
+ fdata.add(FV.var2amb[v], fdata[v].copy(), fdata.dims[v])
@@ -0,0 +1,241 @@
1
+ import numpy as np
2
+
3
+ from foxes.core import TData
4
+ import foxes.variables as FV
5
+ import foxes.constants as FC
6
+
7
+ from .centre import CentreRotor
8
+
9
+
10
+ class DirectMDataInfusion(CentreRotor):
11
+ """
12
+ Direct data infusion of data stored under mdata.
13
+
14
+ Attributes
15
+ ----------
16
+ svars2mdvars: dict
17
+ A mapping of state variables to mdata variables.
18
+ mdata_vars: list of str
19
+ The mdata variables to be used for infusion. By default,
20
+ all mdata variables are searched.
21
+ turbine_coord: str, optional
22
+ The mdata coordinate that represents turbine names. By default,
23
+ the second coordinate is used as a candidate if the mdata variable
24
+ has three dimensions.
25
+
26
+ :group: models.turbine_types
27
+
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ svars2mdvars=None,
33
+ mdata_vars=None,
34
+ turbine_coord=None,
35
+ **kwargs,
36
+ ):
37
+ """
38
+ Constructor.
39
+
40
+ Parameters
41
+ ----------
42
+ svars2mdvars: dict, optional
43
+ A mapping of state variables to mdata variables.
44
+ mdata_vars: list of str, optional
45
+ The mdata variables to be used for infusion. By default,
46
+ all mdata variables are searched.
47
+ turbine_coord: str, optional
48
+ The mdata coordinate that represents turbine names. By default,
49
+ the second coordinate is used as a candidate if the mdata variable
50
+ has three dimensions.
51
+ kwargs: dict, optional
52
+ Additional parameters for RotorModel class
53
+
54
+ """
55
+ super().__init__(**kwargs)
56
+ self.svars2mdvars = svars2mdvars
57
+ self.mdata_vars = mdata_vars
58
+ self.turbine_coord = turbine_coord
59
+
60
+ def output_farm_vars(self, algo):
61
+ """
62
+ The variables which are being modified by the model.
63
+
64
+ Parameters
65
+ ----------
66
+ algo: foxes.core.Algorithm
67
+ The calculation algorithm
68
+
69
+ Returns
70
+ -------
71
+ output_vars: list of str
72
+ The output variable names
73
+
74
+ """
75
+ if self.svars2mdvars is None:
76
+ self.svars2mdvars = {v: v for v in algo.states.output_point_vars(algo)}
77
+
78
+ if self.calc_vars is None:
79
+ vrs = list(self.svars2mdvars.keys())
80
+ assert FV.WEIGHT not in vrs, (
81
+ f"Rotor '{self.name}': svars2mdvars keys {vrs} contain '{FV.WEIGHT}', please remove"
82
+ )
83
+
84
+ if FV.WS in vrs:
85
+ self.calc_vars = [FV.REWS] + [v for v in vrs if v != FV.WS]
86
+ else:
87
+ self.calc_vars = vrs
88
+
89
+ if algo.farm_controller.needs_rews2() and FV.REWS2 not in self.calc_vars:
90
+ self.calc_vars.append(FV.REWS2)
91
+ if algo.farm_controller.needs_rews3() and FV.REWS3 not in self.calc_vars:
92
+ self.calc_vars.append(FV.REWS3)
93
+
94
+ self.calc_vars = sorted(self.calc_vars)
95
+
96
+ if FV.WEIGHT not in self.calc_vars:
97
+ self.calc_vars.append(FV.WEIGHT)
98
+ if FV.WEIGHT not in self.svars2mdvars:
99
+ self.svars2mdvars[FV.WEIGHT] = FV.WEIGHT
100
+
101
+ return self.calc_vars
102
+
103
+ def calculate(
104
+ self,
105
+ algo,
106
+ mdata,
107
+ fdata,
108
+ rpoints=None,
109
+ rpoint_weights=None,
110
+ store=False,
111
+ downwind_index=None,
112
+ ):
113
+ """
114
+ Calculate ambient rotor effective results.
115
+
116
+ Parameters
117
+ ----------
118
+ algo: foxes.core.Algorithm
119
+ The calculation algorithm
120
+ mdata: foxes.core.MData
121
+ The model data
122
+ fdata: foxes.core.FData
123
+ The farm data
124
+ rpoints: numpy.ndarray, optional
125
+ The rotor points, or None for automatic for
126
+ this rotor. Shape: (n_states, n_turbines, n_rpoints, 3)
127
+ rpoint_weights: numpy.ndarray, optional
128
+ The rotor point weights, or None for automatic
129
+ for this rotor. Shape: (n_rpoints,)
130
+ store: bool, optional
131
+ Flag for storing ambient rotor point results
132
+ downwind_index: int, optional
133
+ Only compute for index in the downwind order
134
+
135
+ Returns
136
+ -------
137
+ results: dict
138
+ results dict. Keys: Variable name str. Values:
139
+ numpy.ndarray with results, shape: (n_states, n_turbines)
140
+
141
+ """
142
+ self.ensure_output_vars(algo, fdata)
143
+
144
+ if rpoints is None:
145
+ rpoints = mdata.get(
146
+ FC.ROTOR_POINTS, self.get_rotor_points(algo, mdata, fdata)
147
+ )
148
+ if downwind_index is not None:
149
+ rpoints = rpoints[:, downwind_index, None]
150
+ if rpoint_weights is None:
151
+ rpoint_weights = mdata.get_item(FC.TWEIGHTS, self.rotor_point_weights())
152
+
153
+ tdata = TData.from_tpoints(rpoints, rpoint_weights)
154
+ sres = {}
155
+ mdvs = self.mdata_vars if self.mdata_vars is not None else list(mdata.keys())
156
+ for v, w in self.svars2mdvars.items():
157
+ tdata.add(
158
+ v,
159
+ data=np.full_like(rpoints[..., 0], np.nan),
160
+ dims=(FC.STATE, FC.TARGET, FC.TPOINT),
161
+ )
162
+
163
+ # check fixed variables
164
+ if hasattr(algo.states, "fixed_vars") and v in algo.states.fixed_vars:
165
+ tdata[v][:] = algo.states.fixed_vars[v]
166
+ sres[v] = tdata[v]
167
+ continue
168
+
169
+ # search in mdata variables
170
+ tcoord = self.turbine_coord
171
+ for mdv in mdvs:
172
+ assert mdv in mdata and mdv in mdata.dims, (
173
+ f"Rotor '{self.name}': mdata variable '{mdv}' not found in mdata {list(mdata.keys())} with dims {list(mdata.dims.keys())}"
174
+ )
175
+ mdat = mdata[mdv]
176
+ dims = mdata.dims[mdv]
177
+
178
+ # skip coordinates in the search:
179
+ if dims == (mdv,):
180
+ continue
181
+
182
+ # find variable index in last data array dimension
183
+ vc = dims[-1]
184
+ assert vc in mdata and vc in mdata.dims and mdata.dims[vc] == (vc,), (
185
+ f"Rotor '{self.name}': mdata coordinate '{vc}' not in mdata or wrong dimensions {mdata.dims}, expected '{(vc,)}'"
186
+ )
187
+ vrs = list(mdata[vc])
188
+ if w in vrs:
189
+ i = vrs.index(w)
190
+ mdat = mdat[..., i]
191
+ dims = dims[:-1]
192
+
193
+ # pure state dependent variable
194
+ if dims == (FC.STATE,):
195
+ tdata[v][:] = mdat[:, None, None]
196
+
197
+ # state and turbine dependent variable
198
+ elif len(dims) == 2 and dims[0] == FC.STATE:
199
+ assert mdat.shape[1] == mdata.n_turbines, (
200
+ f"Rotor '{self.name}': mdata variable '{mdv}' has dimensions {dims} and unexpected shape {mdat.shape} for variable '{w}', expected ({mdata.n_states}, {mdata.n_turbines}) for this wind farm with {mdata.n_turbines} turbines"
201
+ )
202
+ if tcoord is None:
203
+ tcoord = dims[1]
204
+ assert dims[1] == tcoord, (
205
+ f"Rotor '{self.name}': mdata variable '{mdv}' has unexpected dimensions {dims} for variable '{w}', expected ({FC.STATE}, {tcoord})"
206
+ )
207
+
208
+ tdata[v][:] = mdat[:, :, None]
209
+
210
+ else:
211
+ if tcoord is None:
212
+ tcoord = "<turbine>"
213
+ raise ValueError(
214
+ f"Rotor '{self.name}': mdata variable '{mdv}' has unexpected dimensions {dims} for variable '{w}' at position {i}, expected ({FC.STATE},) or ({FC.STATE}, {tcoord})"
215
+ )
216
+
217
+ sres[v] = tdata[v]
218
+ break
219
+
220
+ if v not in sres:
221
+ raise ValueError(
222
+ f"Rotor '{self.name}': mdata variable '{w}' not found in any of the mdata variables {mdvs}"
223
+ )
224
+
225
+ if store:
226
+ algo.add_to_chunk_store(FC.ROTOR_POINTS, rpoints, mdata=mdata)
227
+ algo.add_to_chunk_store(FC.ROTOR_WEIGHTS, rpoint_weights, mdata=mdata)
228
+ algo.add_to_chunk_store(FC.AMB_ROTOR_RES, sres, mdata=mdata)
229
+ algo.add_to_chunk_store(FC.WEIGHT_RES, tdata[FV.WEIGHT], mdata=mdata)
230
+
231
+ self.eval_rpoint_results(
232
+ algo,
233
+ mdata,
234
+ fdata,
235
+ tdata,
236
+ rpoint_weights,
237
+ downwind_index,
238
+ copy_to_ambient=True,
239
+ )
240
+
241
+ return {v: fdata[v] for v in self.output_farm_vars(algo)}
@@ -2,14 +2,14 @@
2
2
  Turbine models.
3
3
  """
4
4
 
5
- from .kTI_model import kTI
6
- from .set_farm_vars import SetFarmVars
7
- from .thrust2ct import Thrust2Ct
8
- from .power_mask import PowerMask
9
- from .sector_management import SectorManagement
10
- from .table_factors import TableFactors
11
- from .yaw2yawm import YAW2YAWM
12
- from .yawm2yaw import YAWM2YAW
13
- from .calculator import Calculator
14
- from .rotor_centre_calc import RotorCentreCalc
15
- from .lookup_table import LookupTable
5
+ from .kTI_model import kTI as kTI
6
+ from .set_farm_vars import SetFarmVars as SetFarmVars
7
+ from .thrust2ct import Thrust2Ct as Thrust2Ct
8
+ from .power_mask import PowerMask as PowerMask
9
+ from .sector_management import SectorManagement as SectorManagement
10
+ from .table_factors import TableFactors as TableFactors
11
+ from .yaw2yawm import YAW2YAWM as YAW2YAWM
12
+ from .yawm2yaw import YAWM2YAW as YAWM2YAW
13
+ from .calculator import Calculator as Calculator
14
+ from .rotor_centre_calc import RotorCentreCalc as RotorCentreCalc
15
+ from .lookup_table import LookupTable as LookupTable
@@ -12,11 +12,17 @@ class Calculator(TurbineModel):
12
12
  out_vars: list of str
13
13
  The output variables
14
14
  func: Function
15
- The function: f(in0, in1, ..., stsel) -> (out0, out1, ...)
15
+ The function: f(in0, in1, ..., algo, mdata, fdata, st_sel) -> (out0, out1, ...)
16
16
  where inX and outY are numpy.ndarrays and
17
17
  st_sel is the state-turbine selection slice or array.
18
18
  All arrays have shape (n_states, n_turbines).
19
19
 
20
+ Beware that the turbine ordering in fdata is in downwind order,
21
+ hence external data X of shape (n_states, n_turbines) in farm order
22
+ needs to be reordered by X[ssel, order] with
23
+ ssel = fdata[FV.ORDER_SSEL], order = fdata[FV.ORDER]
24
+ before using it in combination with fdata variables.
25
+
20
26
  :group: models.turbine_models
21
27
 
22
28
  """
@@ -32,10 +38,16 @@ class Calculator(TurbineModel):
32
38
  out_vars: list of str
33
39
  The output variables
34
40
  func: Function
35
- The function: f(in0, in1, ..., stsel) -> (out0, out1, ...)
41
+ The function: f(in0, in1, ..., algo, mdata, fdata, st_sel) -> (out0, out1, ...)
36
42
  where inX and outY are numpy.ndarrays and
37
43
  st_sel is the state-turbine selection slice or array.
38
44
  All arrays have shape (n_states, n_turbines).
45
+
46
+ Beware that the turbine ordering in fdata is in downwind order,
47
+ hence external data X of shape (n_states, n_turbines) in farm order
48
+ needs to be reordered by X[ssel, order] with
49
+ ssel = fdata[FV.ORDER_SSEL], order = fdata[FV.ORDER]
50
+ before using it in combination with fdata variables.
39
51
  kwargs: dict, optional
40
52
  Additional arguments for TurbineModel
41
53
 
@@ -92,7 +104,8 @@ class Calculator(TurbineModel):
92
104
  Values: numpy.ndarray with shape (n_states, n_turbines)
93
105
 
94
106
  """
107
+ self.ensure_output_vars(algo, fdata)
95
108
  ins = [fdata[v] if v in fdata else mdata[v] for v in self.in_vars]
96
- outs = self.func(*ins, st_sel=st_sel)
109
+ outs = self.func(*ins, algo=algo, mdata=mdata, fdata=fdata, st_sel=st_sel)
97
110
 
98
111
  return {v: outs[vi] for vi, v in enumerate(self.out_vars)}
@@ -102,6 +102,7 @@ class kTI(TurbineModel):
102
102
  Values: numpy.ndarray with shape (n_states, n_turbines)
103
103
 
104
104
  """
105
+ self.ensure_output_vars(algo, fdata)
105
106
 
106
107
  kti = self.get_data(
107
108
  FV.KTI,
@@ -189,6 +189,8 @@ class LookupTable(TurbineModel):
189
189
  Values: numpy.ndarray with shape (n_states, n_turbines)
190
190
 
191
191
  """
192
+ self.ensure_output_vars(algo, fdata)
193
+
192
194
  data = {
193
195
  v: self.get_data(
194
196
  v,
@@ -151,6 +151,7 @@ class PowerMask(TurbineModel):
151
151
 
152
152
  """
153
153
  # prepare:
154
+ self.ensure_output_vars(algo, fdata)
154
155
  P = fdata[FV.P]
155
156
  max_P = fdata[FV.MAX_P]
156
157
  P_rated = self._P_rated[None, :]
@@ -107,6 +107,8 @@ class RotorCentreCalc(TurbineModel):
107
107
  Values: numpy.ndarray with shape (n_states, n_turbines)
108
108
 
109
109
  """
110
+ self.ensure_output_vars(algo, fdata)
111
+
110
112
  # prepare target point data:
111
113
  tdata = TData.from_points(
112
114
  fdata[FV.TXYH],
@@ -190,6 +190,7 @@ class SectorManagement(TurbineModel):
190
190
 
191
191
  """
192
192
  # prepare:
193
+ self.ensure_output_vars(algo, fdata)
193
194
  n_trbs = len(self._trbs)
194
195
  if n_trbs == fdata.n_turbines and np.all(
195
196
  self._trbs == np.arange(fdata.n_turbines)
@@ -268,21 +268,15 @@ class SetFarmVars(TurbineModel):
268
268
  Values: numpy.ndarray with shape (n_states, n_turbines)
269
269
 
270
270
  """
271
+ self.ensure_output_vars(algo, fdata)
272
+
271
273
  i0 = mdata.states_i0(counter=True)
272
274
  if not self.once or i0 not in self.__once_done:
273
-
274
- if self.pre_rotor:
275
- order = np.s_[:]
276
- ssel = np.s_[:]
277
- else:
278
- order = fdata[FV.ORDER]
279
- ssel = fdata[FV.ORDER_SSEL]
280
-
281
275
  bsel = np.zeros((fdata.n_states, fdata.n_turbines), dtype=bool)
282
276
  bsel[st_sel] = True
283
277
 
284
278
  for v in self.vars:
285
- data = mdata[self.var(v)][ssel, order]
279
+ data = mdata[self.var(v)]
286
280
  hsel = ~np.isnan(data)
287
281
  tsel = bsel & hsel
288
282
 
@@ -143,6 +143,8 @@ class TableFactors(TurbineModel):
143
143
  Values: numpy.ndarray with shape (n_states, n_turbines)
144
144
 
145
145
  """
146
+ self.ensure_output_vars(algo, fdata)
147
+
146
148
  n_sel = np.sum(st_sel)
147
149
  qts = np.zeros((n_sel, 2), dtype=config.dtype_double)
148
150
  qts[:, 0] = fdata[self.row_var][st_sel]
@@ -82,6 +82,7 @@ class Thrust2Ct(TurbineModel):
82
82
  Values: numpy.ndarray with shape (n_states, n_turbines)
83
83
 
84
84
  """
85
+ self.ensure_output_vars(algo, fdata)
85
86
  ct = fdata[FV.CT]
86
87
 
87
88
  T = fdata[self.thrust_var][st_sel]
@@ -55,6 +55,8 @@ class YAW2YAWM(TurbineModel):
55
55
  Values: numpy.ndarray with shape (n_states, n_turbines)
56
56
 
57
57
  """
58
+ self.ensure_output_vars(algo, fdata)
59
+
58
60
  yaw = fdata[FV.YAW][st_sel]
59
61
  wd = fdata[FV.WD][st_sel]
60
62
 
@@ -56,6 +56,8 @@ class YAWM2YAW(TurbineModel):
56
56
  Values: numpy.ndarray with shape (n_states, n_turbines)
57
57
 
58
58
  """
59
+ self.ensure_output_vars(algo, fdata)
60
+
59
61
  yawm = fdata[FV.YAWM][st_sel]
60
62
  wd = fdata[FV.WD][st_sel]
61
63
 
@@ -1,6 +1,5 @@
1
1
  import numpy as np
2
2
  import pandas as pd
3
- from pathlib import Path
4
3
 
5
4
  from foxes.core import TurbineType
6
5
  from foxes.utils import PandasFileHelper
@@ -232,7 +231,6 @@ class PCtFile(TurbineType):
232
231
 
233
232
  """
234
233
  if modify_ct or modify_P:
235
-
236
234
  ws = self.data_ws
237
235
  ct = self.data_ct
238
236
  P = self.data_P
@@ -302,6 +300,7 @@ class PCtFile(TurbineType):
302
300
  Values: numpy.ndarray with shape (n_states, n_turbines)
303
301
 
304
302
  """
303
+ self.ensure_output_vars(algo, fdata)
305
304
  rews2 = fdata[self.WSCT][st_sel]
306
305
  rews3 = fdata[self.WSP][st_sel]
307
306
 
@@ -332,10 +331,7 @@ class PCtFile(TurbineType):
332
331
  rews3 *= (cosm**self.p_P) ** (1.0 / 3.0)
333
332
  del yawm, cosm
334
333
 
335
- out = {
336
- FV.P: fdata[FV.P],
337
- FV.CT: fdata[FV.CT],
338
- }
334
+ out = {FV.P: fdata[FV.P], FV.CT: fdata[FV.CT]}
339
335
 
340
336
  out[FV.P][st_sel] = np.interp(
341
337
  rews3, self.data_ws, self.data_P, left=0.0, right=0.0
@@ -263,7 +263,6 @@ class PCtFromTwo(TurbineType):
263
263
 
264
264
  """
265
265
  if modify_ct:
266
-
267
266
  ws = self._data_ws_ct
268
267
  ct = self._data_ct
269
268
 
@@ -291,7 +290,6 @@ class PCtFromTwo(TurbineType):
291
290
  self._data_ct = np.concatenate([new_ct[:-1], ct], axis=0)
292
291
 
293
292
  if modify_P:
294
-
295
293
  ws = self._data_ws_P
296
294
  P = self._data_P
297
295
 
@@ -347,6 +345,7 @@ class PCtFromTwo(TurbineType):
347
345
  Values: numpy.ndarray with shape (n_states, n_turbines)
348
346
 
349
347
  """
348
+ self.ensure_output_vars(algo, fdata)
350
349
  rews2 = fdata[self.WSCT][st_sel]
351
350
  rews3 = fdata[self.WSP][st_sel]
352
351
 
@@ -2,12 +2,13 @@
2
2
  Turbine type models.
3
3
  """
4
4
 
5
- from .PCt_file import PCtFile
6
- from .PCt_from_two import PCtFromTwo
7
- from .CpCt_file import CpCtFile
8
- from .CpCt_from_two import CpCtFromTwo
9
- from .null_type import NullType
10
- from .wsrho2PCt_from_two import WsRho2PCtFromTwo
11
- from .wsti2PCt_from_two import WsTI2PCtFromTwo
12
- from .lookup import FromLookupTable
13
- from .TBL_file import TBLFile
5
+ from .PCt_file import PCtFile as PCtFile
6
+ from .PCt_from_two import PCtFromTwo as PCtFromTwo
7
+ from .CpCt_file import CpCtFile as CpCtFile
8
+ from .CpCt_from_two import CpCtFromTwo as CpCtFromTwo
9
+ from .null_type import NullType as NullType
10
+ from .wsrho2PCt_from_two import WsRho2PCtFromTwo as WsRho2PCtFromTwo
11
+ from .wsti2PCt_from_two import WsTI2PCtFromTwo as WsTI2PCtFromTwo
12
+ from .lookup import FromLookupTable as FromLookupTable
13
+ from .TBL_file import TBLFile as TBLFile
14
+ from .calculator_type import CalculatorType as CalculatorType