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
@@ -66,28 +66,29 @@ class FarmDataModel(DataCalcModel):
66
66
  """
67
67
  return (FC.STATE, FC.TURBINE)
68
68
 
69
- def ensure_variables(self, algo, mdata, fdata):
69
+ def ensure_output_vars(self, algo, fdata):
70
70
  """
71
- Add variables to fdata, initialized with NaN
71
+ Ensures that the output variables are present in the farm data.
72
72
 
73
73
  Parameters
74
74
  ----------
75
75
  algo: foxes.core.Algorithm
76
76
  The calculation algorithm
77
- mdata: foxes.core.Data
78
- The model data
79
- fdata: foxes.core.Data
77
+ fdata: foxes.core.FData
80
78
  The farm data
81
79
 
82
80
  """
83
- n_states = fdata.n_states
84
- n_turbines = fdata.n_turbines
85
- for v in self.output_farm_vars(algo):
86
- if v not in fdata:
87
- fdata[v] = np.full(
88
- (n_states, n_turbines), np.nan, dtype=config.dtype_double
81
+ for var in self.output_farm_vars(algo):
82
+ if var not in fdata:
83
+ fdata.add(
84
+ var,
85
+ np.full(
86
+ (fdata.n_states, fdata.n_turbines),
87
+ np.nan,
88
+ dtype=config.dtype_double,
89
+ ),
90
+ (FC.STATE, FC.TURBINE),
89
91
  )
90
- fdata.dims[v] = (FC.STATE, FC.TURBINE)
91
92
 
92
93
  @abstractmethod
93
94
  def calculate(self, algo, mdata, fdata):
@@ -264,7 +265,7 @@ class FarmDataModelList(FarmDataModel):
264
265
  The model data
265
266
  fdata: foxes.core.FData
266
267
  The farm data
267
- parameters: list of dict, optional
268
+ parameters: list of dict
268
269
  A list of parameter dicts, one for each model
269
270
 
270
271
  Returns
@@ -274,6 +275,8 @@ class FarmDataModelList(FarmDataModel):
274
275
  Values: numpy.ndarray with shape (n_states, n_turbines)
275
276
 
276
277
  """
278
+ self.ensure_output_vars(algo, fdata)
279
+
277
280
  if parameters is None:
278
281
  parameters = [{}] * len(self.models)
279
282
  elif not isinstance(parameters, list):
@@ -91,7 +91,6 @@ class GroundModel(Model):
91
91
  mdata,
92
92
  fdata,
93
93
  tdata,
94
- amb_res,
95
94
  rpoint_weights,
96
95
  wake_deltas,
97
96
  wmodel,
@@ -114,11 +113,6 @@ class GroundModel(Model):
114
113
  The farm data
115
114
  tdata: foxes.core.Data
116
115
  The target point data
117
- amb_res: dict
118
- The ambient results at the target points
119
- of all rotors. Key: variable name, value
120
- np.ndarray of shape:
121
- (n_states, n_turbines, n_rotor_points)
122
116
  rpoint_weights: numpy.ndarray
123
117
  The rotor point weights, shape: (n_rotor_points,)
124
118
  wake_deltas: dict
@@ -143,7 +137,6 @@ class GroundModel(Model):
143
137
  mdata,
144
138
  fdata,
145
139
  tdata,
146
- amb_res,
147
140
  rpoint_weights,
148
141
  wake_deltas,
149
142
  wmodel,
@@ -226,7 +219,7 @@ class GroundModel(Model):
226
219
  algo,
227
220
  mdata,
228
221
  fdata,
229
- amb_results,
222
+ tdata,
230
223
  wake_deltas,
231
224
  wmodel,
232
225
  ):
@@ -243,17 +236,15 @@ class GroundModel(Model):
243
236
  The model data
244
237
  fdata: foxes.core.FData
245
238
  The farm data
246
- amb_results: dict
247
- The ambient results, key: variable name str,
248
- values: numpy.ndarray with shape
249
- (n_states, n_targets, n_tpoints)
239
+ tdata: foxes.core.TData
240
+ The target point data
250
241
  wake_deltas: dict
251
242
  The wake deltas object at the selected target
252
243
  turbines. Key: variable str, value: numpy.ndarray
253
244
  with shape (n_states, n_targets, n_tpoints)
254
245
 
255
246
  """
256
- wmodel.finalize_wake_deltas(algo, mdata, fdata, amb_results, wake_deltas)
247
+ wmodel.finalize_wake_deltas(algo, mdata, fdata, tdata, wake_deltas)
257
248
 
258
249
  @classmethod
259
250
  def new(cls, ground_type, *args, **kwargs):
foxes/core/model.py CHANGED
@@ -64,12 +64,30 @@ class Model(ABC):
64
64
 
65
65
  Returns
66
66
  -------
67
- str
67
+ vnm: str
68
68
  Model specific variable name
69
69
 
70
70
  """
71
71
  return f"{self.name}_{v}"
72
72
 
73
+ def unvar(self, vnm):
74
+ """
75
+ Translates model specific variable name to origninal variable name.
76
+
77
+ Parameters
78
+ ----------
79
+ vnm: str
80
+ The vamodel specific variable name
81
+
82
+ Returns
83
+ -------
84
+ v: str
85
+ Original variable name
86
+
87
+ """
88
+ lng = len(f"{self.name}_")
89
+ return vnm[lng:] if vnm.startswith(f"{self.name}_") else None
90
+
73
91
  @property
74
92
  def initialized(self):
75
93
  """
@@ -77,7 +95,7 @@ class Model(ABC):
77
95
 
78
96
  Returns
79
97
  -------
80
- bool :
98
+ bool:
81
99
  True if the model has been initialized.
82
100
 
83
101
  """
@@ -424,8 +442,8 @@ class Model(ABC):
424
442
  # lookup mdata:
425
443
  elif s == "m" and mdata is not None and variable in mdata:
426
444
  a, d = _filter_dims(mdata)
427
- l = len(d)
428
- if l <= len(dims) and d == dims[:l]:
445
+ ld = len(d)
446
+ if ld <= len(dims) and d == dims[:ld]:
429
447
  out = _match_shape(mdata[variable])
430
448
 
431
449
  # lookup fdata:
@@ -448,8 +466,8 @@ class Model(ABC):
448
466
  and variable in tdata
449
467
  ):
450
468
  a, d = _filter_dims(tdata)
451
- l = len(d)
452
- if l <= len(dims) and d == dims[:l]:
469
+ ld = len(ld)
470
+ if ld <= len(dims) and d == dims[:ld]:
453
471
  out = _match_shape(tdata[variable])
454
472
 
455
473
  # lookup wake modelling data:
@@ -1,8 +1,13 @@
1
1
  from abc import abstractmethod
2
+ import numpy as np
2
3
 
3
- from foxes.utils import new_instance
4
+ from foxes.utils import new_instance, wd2uv, uv2wd
5
+ from foxes.config import config
6
+ import foxes.variables as FV
7
+ import foxes.constants as FC
4
8
 
5
9
  from .model import Model
10
+ from .data import TData
6
11
 
7
12
 
8
13
  class PartialWakesModel(Model):
@@ -68,6 +73,144 @@ class PartialWakesModel(Model):
68
73
  """
69
74
  pass
70
75
 
76
+ def get_initial_tdata(
77
+ self,
78
+ algo,
79
+ mdata,
80
+ fdata,
81
+ amb_rotor_res,
82
+ rotor_weights,
83
+ wmodels,
84
+ ):
85
+ """
86
+ Creates the initial target data object
87
+
88
+ Parameters
89
+ ----------
90
+ algo: foxes.core.Algorithm
91
+ The calculation algorithm
92
+ mdata: foxes.core.MData
93
+ The model data
94
+ fdata: foxes.core.FData
95
+ The farm data
96
+ amb_rotor_res: dict
97
+ The ambient results at rotor points,
98
+ key: variable name, value: numpy.ndarray
99
+ of shape: (n_states, n_turbines, n_rotor_points)
100
+ rotor_weights: numpy.ndarray
101
+ The rotor point weights, shape: (n_rotor_points,)
102
+ wmodels: list of foxes.core.WakeModel
103
+ The wake models for this partial wake model
104
+
105
+ Returns
106
+ -------
107
+ tdata: foxes.core.TData
108
+ The target point data for the wake points
109
+
110
+ """
111
+ tpoints, tweights = self.get_wake_points(algo, mdata, fdata)
112
+ tdata = TData.from_tpoints(tpoints, tweights)
113
+
114
+ # map wind data:
115
+ if FV.WD in amb_rotor_res or FV.WS in amb_rotor_res:
116
+ assert FV.WD in amb_rotor_res and FV.WS in amb_rotor_res, (
117
+ "Require both wind direction and speed in ambient rotor results."
118
+ )
119
+ uv = wd2uv(amb_rotor_res[FV.WD], amb_rotor_res[FV.WS])
120
+ uv = np.stack(
121
+ [
122
+ self.map_rotor_results(
123
+ algo, mdata, fdata, tdata, FV.U, uv[..., 0], rotor_weights
124
+ ),
125
+ self.map_rotor_results(
126
+ algo, mdata, fdata, tdata, FV.V, uv[..., 1], rotor_weights
127
+ ),
128
+ ],
129
+ axis=-1,
130
+ )
131
+ tdata.add(FV.AMB_WD, uv2wd(uv), dims=(FC.STATE, FC.TARGET, FC.TPOINT))
132
+ tdata.add(
133
+ FV.AMB_WS,
134
+ np.linalg.norm(uv, axis=-1),
135
+ dims=(FC.STATE, FC.TARGET, FC.TPOINT),
136
+ )
137
+ for wmodel in wmodels:
138
+ if wmodel.has_uv:
139
+ tdata.add(
140
+ FV.AMB_UV, uv, dims=(FC.STATE, FC.TARGET, FC.TPOINT, FC.XY)
141
+ )
142
+ break
143
+
144
+ # map rotor point results onto target points:
145
+ for v, d in amb_rotor_res.items():
146
+ if v not in [FV.WS, FV.WD, FV.U, FV.V, FV.UV]:
147
+ w = FV.var2amb.get(v, v)
148
+ tdata.add(
149
+ w,
150
+ self.map_rotor_results(
151
+ algo, mdata, fdata, tdata, v, d, rotor_weights
152
+ ),
153
+ dims=(FC.STATE, FC.TARGET, FC.TPOINT),
154
+ )
155
+
156
+ return tdata
157
+
158
+ def map_rotor_results(
159
+ self,
160
+ algo,
161
+ mdata,
162
+ fdata,
163
+ tdata,
164
+ variable,
165
+ rotor_res,
166
+ rotor_weights,
167
+ ):
168
+ """
169
+ Map ambient rotor point results onto target points.
170
+
171
+ Parameters
172
+ ----------
173
+ algo: foxes.core.Algorithm
174
+ The calculation algorithm
175
+ mdata: foxes.core.MData
176
+ The model data
177
+ fdata: foxes.core.FData
178
+ The farm data
179
+ tdata: foxes.core.TData
180
+ The target point data
181
+ variable: str
182
+ The variable name to map
183
+ rotor_res: numpy.ndarray
184
+ The results at rotor points, shape:
185
+ (n_states, n_turbines, n_rotor_points)
186
+ rotor_weights: numpy.ndarray
187
+ The rotor point weights, shape: (n_rotor_points,)
188
+
189
+ Returns
190
+ -------
191
+ res: numpy.ndarray
192
+ The mapped results at target points, shape:
193
+ (n_states, n_targets, n_tpoints)
194
+
195
+ """
196
+ if len(rotor_res.shape) > 2 and rotor_res.shape[:2] == (
197
+ tdata.n_states,
198
+ tdata.n_targets,
199
+ ):
200
+ q = np.zeros(
201
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
202
+ dtype=config.dtype_double,
203
+ )
204
+ if rotor_res.shape[2] == 1:
205
+ q[:] = rotor_res
206
+ else:
207
+ q[:] = np.einsum("str,r->st", rotor_res, rotor_weights)[:, :, None]
208
+ return q
209
+ else:
210
+ raise ValueError(
211
+ f"Partial wakes '{self.name}': Incompatible shape '{rotor_res.shape}' for variable '{variable}' in rotor results."
212
+ )
213
+
71
214
  def new_wake_deltas(self, algo, mdata, fdata, tdata, wmodel):
72
215
  """
73
216
  Creates new initial wake deltas, filled
@@ -77,11 +220,11 @@ class PartialWakesModel(Model):
77
220
  ----------
78
221
  algo: foxes.core.Algorithm
79
222
  The calculation algorithm
80
- mdata: foxes.core.Data
223
+ mdata: foxes.core.MData
81
224
  The model data
82
- fdata: foxes.core.Data
225
+ fdata: foxes.core.FData
83
226
  The farm data
84
- tdata: foxes.core.Data
227
+ tdata: foxes.core.TData
85
228
  The target point data
86
229
  wmodel: foxes.core.WakeModel
87
230
  The wake model
@@ -140,7 +283,6 @@ class PartialWakesModel(Model):
140
283
  mdata,
141
284
  fdata,
142
285
  tdata,
143
- amb_res,
144
286
  rpoint_weights,
145
287
  wake_deltas,
146
288
  wmodel,
@@ -162,11 +304,6 @@ class PartialWakesModel(Model):
162
304
  The farm data
163
305
  tdata: foxes.core.Data
164
306
  The target point data
165
- amb_res: dict
166
- The ambient results at the target points
167
- of all rotors. Key: variable name, value
168
- np.ndarray of shape:
169
- (n_states, n_turbines, n_rotor_points)
170
307
  rpoint_weights: numpy.ndarray
171
308
  The rotor point weights, shape: (n_rotor_points,)
172
309
  wake_deltas: dict
@@ -42,30 +42,33 @@ class PointDataModel(DataCalcModel):
42
42
  """
43
43
  return (FC.STATE, FC.TARGET, FC.TPOINT)
44
44
 
45
- def ensure_variables(self, algo, mdata, fdata, tdata):
45
+ def ensure_output_vars(self, algo, tdata):
46
46
  """
47
- Add variables to tdata, initialized with NaN
47
+ Ensures that the output variables are present in the target data.
48
48
 
49
49
  Parameters
50
50
  ----------
51
51
  algo: foxes.core.Algorithm
52
52
  The calculation algorithm
53
- mdata: foxes.core.Data
54
- The model data
55
- fdata: foxes.core.Data
56
- The farm data
57
- tdata: foxes.core.Data
53
+ tdata: foxes.core.TData
58
54
  The target point data
59
55
 
60
56
  """
61
- for v in self.output_point_vars(algo):
62
- if v not in tdata:
63
- tdata[v] = np.full(
64
- (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
65
- np.nan,
66
- dtype=config.dtype_double,
57
+ vrs = set(self.output_point_vars(algo))
58
+ if hasattr(self, "fixed_vars"):
59
+ vrs.update(self.fixed_vars.keys())
60
+
61
+ for var in vrs:
62
+ if var not in tdata:
63
+ tdata.add(
64
+ var,
65
+ np.full(
66
+ (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
67
+ np.nan,
68
+ dtype=config.dtype_double,
69
+ ),
70
+ (FC.STATE, FC.TARGET, FC.TPOINT),
67
71
  )
68
- tdata.dims[v] = (FC.STATE, FC.TARGET, FC.TPOINT)
69
72
 
70
73
  @abstractmethod
71
74
  def calculate(self, algo, mdata, fdata, tdata):
@@ -98,8 +101,7 @@ class PointDataModel(DataCalcModel):
98
101
 
99
102
  def run_calculation(self, algo, *data, out_vars, **calc_pars):
100
103
  """
101
- Starts the model calculation in parallel, via
102
- xarray's `apply_ufunc`.
104
+ Starts the model calculation in parallel.
103
105
 
104
106
  Typically this function is called by algorithms.
105
107
 
@@ -149,7 +151,7 @@ class PointDataModelList(PointDataModel):
149
151
 
150
152
  By using the PointDataModelList the models'
151
153
  `calculate` functions are called together
152
- under one common call of xarray's `apply_ufunc`.
154
+ under one common call by the engine.
153
155
 
154
156
  Attributes
155
157
  ----------
@@ -248,6 +250,8 @@ class PointDataModelList(PointDataModel):
248
250
  (n_states, n_targets, n_tpoints)
249
251
 
250
252
  """
253
+ self.ensure_output_vars(algo, tdata)
254
+
251
255
  if parameters is None:
252
256
  parameters = [{}] * len(self.models)
253
257
  elif not isinstance(parameters, list):
foxes/core/rotor_model.py CHANGED
@@ -60,9 +60,9 @@ class RotorModel(FarmDataModel):
60
60
  """
61
61
  if self.calc_vars is None:
62
62
  vrs = algo.states.output_point_vars(algo)
63
- assert (
64
- FV.WEIGHT not in vrs
65
- ), f"Rotor '{self.name}': States '{algo.states.name}' output_point_vars contain '{FV.WEIGHT}', please remove"
63
+ assert FV.WEIGHT not in vrs, (
64
+ f"Rotor '{self.name}': States '{algo.states.name}' output_point_vars contain '{FV.WEIGHT}', please remove"
65
+ )
66
66
 
67
67
  if FV.WS in vrs:
68
68
  self.calc_vars = [FV.REWS] + [v for v in vrs if v != FV.WS]
@@ -362,6 +362,7 @@ class RotorModel(FarmDataModel):
362
362
  numpy.ndarray with results, shape: (n_states, n_turbines)
363
363
 
364
364
  """
365
+ self.ensure_output_vars(algo, fdata)
365
366
 
366
367
  if rpoints is None:
367
368
  rpoints = mdata.get(
foxes/core/states.py CHANGED
@@ -2,7 +2,6 @@ from abc import abstractmethod
2
2
 
3
3
  from .point_data_model import PointDataModel, PointDataModelList
4
4
  from foxes.utils import new_instance
5
- import foxes.variables as FV
6
5
  import foxes.constants as FC
7
6
 
8
7
 
@@ -114,7 +113,7 @@ class States(PointDataModel):
114
113
  elif isinstance(s, ExtendedStates):
115
114
  if s.states is not self:
116
115
  raise ValueError(
117
- f"Cannot add extended states, since not based on same states"
116
+ "Cannot add extended states, since not based on same states"
118
117
  )
119
118
  return ExtendedStates(self, s.pmodels.models[1:])
120
119
  else:
@@ -268,7 +267,7 @@ class ExtendedStates(States):
268
267
  elif isinstance(m, ExtendedStates):
269
268
  if m.states is not self.states:
270
269
  raise ValueError(
271
- f"Cannot add extended states, since not based on same states"
270
+ "Cannot add extended states, since not based on same states"
272
271
  )
273
272
  return ExtendedStates(self.states, models + m.pmodels.models[1:])
274
273
  else:
foxes/core/turbine.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import numpy as np
2
+ from copy import deepcopy
2
3
 
3
4
 
4
5
  class Turbine:
@@ -71,7 +72,7 @@ class Turbine:
71
72
  self.index = index
72
73
  self.name = name
73
74
  self.xy = np.array(xy)
74
- self.models = turbine_models
75
+ self.models = deepcopy(turbine_models)
75
76
  self.D = D
76
77
  self.H = H
77
78
 
@@ -0,0 +1,130 @@
1
+ from abc import abstractmethod
2
+
3
+ from foxes.utils import new_instance
4
+ from .model import Model
5
+
6
+
7
+ class WakeDeflection(Model):
8
+ """
9
+ Abstract base class for wake deflection models.
10
+
11
+ :group: core
12
+
13
+ """
14
+
15
+ @property
16
+ def has_uv(self):
17
+ """
18
+ This model uses wind vector data
19
+
20
+ Returns
21
+ -------
22
+ hasuv: bool
23
+ Flag for wind vector data
24
+
25
+ """
26
+ return False
27
+
28
+ @abstractmethod
29
+ def calc_deflection(
30
+ self,
31
+ algo,
32
+ mdata,
33
+ fdata,
34
+ tdata,
35
+ downwind_index,
36
+ coos,
37
+ ):
38
+ """
39
+ Calculates the wake deflection.
40
+
41
+ This function optionally adds FC.WDEFL_ROT_ANGLE or
42
+ FC.WDEFL_DWS_FACTOR to the tdata.
43
+
44
+ Parameters
45
+ ----------
46
+ algo: foxes.core.Algorithm
47
+ The calculation algorithm
48
+ mdata: foxes.core.MData
49
+ The model data
50
+ fdata: foxes.core.FData
51
+ The farm data
52
+ tdata: foxes.core.TData
53
+ The target point data
54
+ downwind_index: int
55
+ The index of the wake causing turbine
56
+ in the downwind order
57
+ coos: numpy.ndarray
58
+ The wake frame coordinates of the evaluation
59
+ points, shape: (n_states, n_targets, n_tpoints, 3)
60
+
61
+ Returns
62
+ -------
63
+ coos: numpy.ndarray
64
+ The wake frame coordinates of the evaluation
65
+ points, shape: (n_states, n_targets, n_tpoints, 3)
66
+
67
+ """
68
+ pass
69
+
70
+ def get_yaw_alpha_seq(
71
+ self,
72
+ algo,
73
+ mdata,
74
+ fdata,
75
+ tdata,
76
+ downwind_index,
77
+ x,
78
+ ):
79
+ """
80
+ Computes sequential wind vector rotation angles.
81
+
82
+ Wind vector rotation angles are computed at the
83
+ current trace points due to a yawed rotor
84
+ for sequential runs.
85
+
86
+ Parameters
87
+ ----------
88
+ algo: foxes.core.Algorithm
89
+ The calculation algorithm
90
+ mdata: foxes.core.MData
91
+ The model data
92
+ fdata: foxes.core.FData
93
+ The farm data
94
+ tdata: foxes.core.TData
95
+ The target point data
96
+ downwind_index: int
97
+ The index of the wake causing turbine
98
+ in the downwind order
99
+ x: numpy.ndarray
100
+ The distance from the wake causing rotor
101
+ for the first n_times subsequent time steps,
102
+ shape: (n_times,)
103
+
104
+ Returns
105
+ -------
106
+ alpha: numpy.ndarray
107
+ The delta WD result at the x locations,
108
+ shape: (n_times,)
109
+
110
+ """
111
+ raise NotImplementedError(
112
+ f"Wake deflection '{self.name}' not implemented for sequential runs"
113
+ )
114
+
115
+ @classmethod
116
+ def new(cls, wdefl_type, *args, **kwargs):
117
+ """
118
+ Run-time wake deflection model factory.
119
+
120
+ Parameters
121
+ ----------
122
+ wdefl_type: str
123
+ The selected derived class name
124
+ args: tuple, optional
125
+ Additional parameters for constructor
126
+ kwargs: dict, optional
127
+ Additional parameters for constructor
128
+
129
+ """
130
+ return new_instance(cls, wdefl_type, *args, **kwargs)