foxes 0.6.1__py3-none-any.whl → 0.7__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 (129) hide show
  1. foxes/VERSION +1 -1
  2. foxes/algorithms/downwind/downwind.py +131 -65
  3. foxes/algorithms/downwind/models/__init__.py +2 -1
  4. foxes/algorithms/downwind/models/farm_wakes_calc.py +87 -55
  5. foxes/algorithms/downwind/models/init_farm_data.py +134 -0
  6. foxes/algorithms/downwind/models/point_wakes_calc.py +54 -65
  7. foxes/algorithms/downwind/models/{calc_order.py → reorder_farm_output.py} +28 -26
  8. foxes/algorithms/iterative/iterative.py +100 -51
  9. foxes/algorithms/iterative/models/convergence.py +3 -3
  10. foxes/algorithms/iterative/models/farm_wakes_calc.py +55 -48
  11. foxes/algorithms/sequential/models/seq_state.py +7 -6
  12. foxes/algorithms/sequential/sequential.py +81 -44
  13. foxes/constants.py +33 -18
  14. foxes/core/__init__.py +2 -2
  15. foxes/core/algorithm.py +31 -12
  16. foxes/core/data.py +335 -41
  17. foxes/core/data_calc_model.py +27 -23
  18. foxes/core/farm_controller.py +27 -28
  19. foxes/core/farm_data_model.py +26 -4
  20. foxes/core/model.py +186 -129
  21. foxes/core/partial_wakes_model.py +84 -81
  22. foxes/core/point_data_model.py +51 -18
  23. foxes/core/rotor_model.py +59 -77
  24. foxes/core/states.py +6 -6
  25. foxes/core/turbine_model.py +4 -4
  26. foxes/core/turbine_type.py +24 -0
  27. foxes/core/vertical_profile.py +3 -3
  28. foxes/core/wake_frame.py +91 -50
  29. foxes/core/wake_model.py +74 -43
  30. foxes/core/wake_superposition.py +29 -26
  31. foxes/input/farm_layout/__init__.py +1 -0
  32. foxes/input/farm_layout/from_random.py +49 -0
  33. foxes/input/states/__init__.py +1 -1
  34. foxes/input/states/create/__init__.py +1 -0
  35. foxes/input/states/create/random_abl_states.py +6 -2
  36. foxes/input/states/create/random_timeseries.py +56 -0
  37. foxes/input/states/field_data_nc.py +12 -8
  38. foxes/input/states/multi_height.py +24 -14
  39. foxes/input/states/scan_ws.py +13 -17
  40. foxes/input/states/single.py +28 -20
  41. foxes/input/states/states_table.py +22 -18
  42. foxes/models/axial_induction_models/betz.py +1 -1
  43. foxes/models/farm_models/turbine2farm.py +2 -2
  44. foxes/models/model_book.py +40 -14
  45. foxes/models/partial_wakes/__init__.py +2 -2
  46. foxes/models/partial_wakes/axiwake.py +73 -200
  47. foxes/models/partial_wakes/centre.py +40 -0
  48. foxes/models/partial_wakes/grid.py +7 -63
  49. foxes/models/partial_wakes/rotor_points.py +53 -147
  50. foxes/models/partial_wakes/segregated.py +158 -0
  51. foxes/models/partial_wakes/top_hat.py +88 -196
  52. foxes/models/point_models/set_uniform_data.py +4 -4
  53. foxes/models/point_models/tke2ti.py +4 -4
  54. foxes/models/point_models/wake_deltas.py +4 -4
  55. foxes/models/rotor_models/centre.py +15 -19
  56. foxes/models/rotor_models/grid.py +2 -1
  57. foxes/models/rotor_models/levels.py +2 -1
  58. foxes/models/turbine_models/__init__.py +0 -1
  59. foxes/models/turbine_models/calculator.py +11 -7
  60. foxes/models/turbine_models/kTI_model.py +13 -11
  61. foxes/models/turbine_models/lookup_table.py +22 -9
  62. foxes/models/turbine_models/power_mask.py +81 -51
  63. foxes/models/turbine_models/rotor_centre_calc.py +17 -20
  64. foxes/models/turbine_models/sector_management.py +5 -6
  65. foxes/models/turbine_models/set_farm_vars.py +49 -20
  66. foxes/models/turbine_models/table_factors.py +5 -5
  67. foxes/models/turbine_models/thrust2ct.py +9 -5
  68. foxes/models/turbine_models/yaw2yawm.py +7 -13
  69. foxes/models/turbine_models/yawm2yaw.py +7 -11
  70. foxes/models/turbine_types/PCt_file.py +84 -3
  71. foxes/models/turbine_types/PCt_from_two.py +7 -3
  72. foxes/models/turbine_types/null_type.py +2 -2
  73. foxes/models/turbine_types/wsrho2PCt_from_two.py +2 -2
  74. foxes/models/turbine_types/wsti2PCt_from_two.py +6 -2
  75. foxes/models/wake_frames/farm_order.py +26 -22
  76. foxes/models/wake_frames/rotor_wd.py +32 -31
  77. foxes/models/wake_frames/seq_dynamic_wakes.py +112 -64
  78. foxes/models/wake_frames/streamlines.py +51 -47
  79. foxes/models/wake_frames/timelines.py +59 -47
  80. foxes/models/wake_frames/yawed_wakes.py +63 -40
  81. foxes/models/wake_models/axisymmetric.py +31 -35
  82. foxes/models/wake_models/dist_sliced.py +50 -56
  83. foxes/models/wake_models/gaussian.py +33 -35
  84. foxes/models/wake_models/induction/rankine_half_body.py +79 -87
  85. foxes/models/wake_models/induction/rathmann.py +56 -63
  86. foxes/models/wake_models/induction/self_similar.py +59 -62
  87. foxes/models/wake_models/ti/crespo_hernandez.py +83 -74
  88. foxes/models/wake_models/ti/iec_ti.py +65 -75
  89. foxes/models/wake_models/top_hat.py +60 -69
  90. foxes/models/wake_models/wake_mirror.py +49 -54
  91. foxes/models/wake_models/wind/bastankhah14.py +44 -66
  92. foxes/models/wake_models/wind/bastankhah16.py +84 -111
  93. foxes/models/wake_models/wind/jensen.py +67 -89
  94. foxes/models/wake_models/wind/turbopark.py +93 -133
  95. foxes/models/wake_superpositions/ti_linear.py +33 -27
  96. foxes/models/wake_superpositions/ti_max.py +33 -27
  97. foxes/models/wake_superpositions/ti_pow.py +35 -27
  98. foxes/models/wake_superpositions/ti_quadratic.py +33 -27
  99. foxes/models/wake_superpositions/ws_linear.py +39 -32
  100. foxes/models/wake_superpositions/ws_max.py +40 -33
  101. foxes/models/wake_superpositions/ws_pow.py +39 -32
  102. foxes/models/wake_superpositions/ws_product.py +35 -28
  103. foxes/models/wake_superpositions/ws_quadratic.py +39 -32
  104. foxes/opt/constraints/min_dist.py +1 -1
  105. foxes/opt/objectives/farm_vars.py +1 -1
  106. foxes/opt/problems/layout/farm_layout.py +38 -97
  107. foxes/output/__init__.py +1 -0
  108. foxes/output/farm_results_eval.py +1 -1
  109. foxes/output/flow_plots_2d/flow_plots.py +2 -0
  110. foxes/output/flow_plots_2d/get_fig.py +2 -0
  111. foxes/output/grids.py +1 -1
  112. foxes/output/rose_plot.py +3 -3
  113. foxes/output/rotor_point_plots.py +117 -0
  114. foxes/output/turbine_type_curves.py +2 -2
  115. foxes/utils/__init__.py +2 -1
  116. foxes/utils/load.py +29 -0
  117. foxes/utils/random_xy.py +56 -0
  118. foxes/utils/runners/runners.py +13 -1
  119. foxes/utils/windrose_plot.py +1 -1
  120. foxes/variables.py +10 -0
  121. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/METADATA +13 -7
  122. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/RECORD +126 -122
  123. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/WHEEL +1 -1
  124. foxes/models/partial_wakes/distsliced.py +0 -322
  125. foxes/models/partial_wakes/mapped.py +0 -252
  126. foxes/models/turbine_models/set_XYHD.py +0 -130
  127. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/LICENSE +0 -0
  128. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/top_level.txt +0 -0
  129. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/zip-safe +0 -0
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
+ from copy import deepcopy
2
3
 
3
- import foxes.variables as FV
4
- from foxes.core import FarmDataModel
4
+ from foxes.core import FarmDataModel, TData
5
5
 
6
6
 
7
7
  class FarmWakesCalculation(FarmDataModel):
@@ -60,22 +60,7 @@ class FarmWakesCalculation(FarmDataModel):
60
60
  All sub models
61
61
 
62
62
  """
63
- return [self.pwakes] if self.urelax is None else [self.urelax, self.pwakes]
64
-
65
- def initialize(self, algo, verbosity=0):
66
- """
67
- Initializes the model.
68
-
69
- Parameters
70
- ----------
71
- algo: foxes.core.Algorithm
72
- The calculation algorithm
73
- verbosity: int
74
- The verbosity level, 0 = silent
75
-
76
- """
77
- self.pwakes = algo.partial_wakes_model
78
- super().initialize(algo, verbosity)
63
+ return [] if self.urelax is None else [self.urelax]
79
64
 
80
65
  def calculate(self, algo, mdata, fdata):
81
66
  """ "
@@ -100,35 +85,57 @@ class FarmWakesCalculation(FarmDataModel):
100
85
  Values: numpy.ndarray with shape (n_states, n_turbines)
101
86
 
102
87
  """
103
-
104
- torder = fdata[FV.ORDER]
105
- n_order = torder.shape[1]
106
- n_states = mdata.n_states
107
-
108
- def _evaluate(algo, mdata, fdata, pdata, wdeltas, o):
109
- self.pwakes.evaluate_results(
110
- algo, mdata, fdata, pdata, wdeltas, states_turbine=o
111
- )
112
-
113
- trbs = np.zeros((n_states, algo.n_turbines), dtype=bool)
114
- np.put_along_axis(trbs, o[:, None], True, axis=1)
115
-
116
- res = algo.farm_controller.calculate(
117
- algo, mdata, fdata, pre_rotor=False, st_sel=trbs
118
- )
119
- fdata.update(res)
120
-
121
- if self.urelax is not None:
122
- res = self.urelax.calculate(algo, mdata, fdata)
123
- for v, d in res.items():
124
- fdata[v][trbs] = d[trbs]
125
-
126
- wdeltas, pdata = self.pwakes.new_wake_deltas(algo, mdata, fdata)
127
- for oi in range(n_order):
128
- o = torder[:, oi]
129
- self.pwakes.contribute_to_wake_deltas(algo, mdata, fdata, pdata, o, wdeltas)
130
-
131
- for oi in range(n_order):
132
- _evaluate(algo, mdata, fdata, pdata, wdeltas, torder[:, oi])
88
+ # collect ambient rotor results and weights:
89
+ rotor = algo.rotor_model
90
+ weights = rotor.from_data_or_store(rotor.RWEIGHTS, algo, mdata)
91
+ amb_res = rotor.from_data_or_store(rotor.AMBRES, algo, mdata)
92
+
93
+ # generate all wake evaluation points
94
+ # (n_states, n_order, n_rpoints)
95
+ pwake2tdata = {}
96
+ for wname, wmodel in algo.wake_models.items():
97
+ pwake = algo.partial_wakes[wname]
98
+ if pwake.name not in pwake2tdata:
99
+ tpoints, tweights = pwake.get_wake_points(algo, mdata, fdata)
100
+ pwake2tdata[pwake.name] = TData.from_tpoints(tpoints, tweights)
101
+
102
+ def _get_wdata(tdatap, wdeltas, s):
103
+ """Helper function for wake data extraction"""
104
+ tdata = tdatap.get_slice(s, keep=True)
105
+ wdelta = {v: d[s] for v, d in wdeltas.items()}
106
+ return tdata, wdelta
107
+
108
+ wake_res = deepcopy(amb_res)
109
+ n_turbines = mdata.n_turbines
110
+ for wname, wmodel in algo.wake_models.items():
111
+ pwake = algo.partial_wakes[wname]
112
+ tdatap = pwake2tdata[pwake.name]
113
+ wdeltas = pwake.new_wake_deltas(algo, mdata, fdata, tdatap, wmodel)
114
+
115
+ for oi in range(n_turbines):
116
+
117
+ if oi > 0:
118
+ tdata, wdelta = _get_wdata(tdatap, wdeltas, np.s_[:, :oi])
119
+ pwake.contribute(algo, mdata, fdata, tdata, oi, wdelta, wmodel)
120
+
121
+ if oi < n_turbines - 1:
122
+ tdata, wdelta = _get_wdata(tdatap, wdeltas, np.s_[:, oi + 1 :])
123
+ pwake.contribute(algo, mdata, fdata, tdata, oi, wdelta, wmodel)
124
+
125
+ for oi in range(n_turbines):
126
+ wres = pwake.finalize_wakes(
127
+ algo, mdata, fdata, tdatap, amb_res, weights, wdeltas, wmodel, oi
128
+ )
129
+ for v, d in wres.items():
130
+ if v in wake_res:
131
+ wake_res[v][:, oi] += d
132
+
133
+ del pwake, tdatap, wdeltas
134
+
135
+ rotor.eval_rpoint_results(algo, mdata, fdata, wake_res, weights)
136
+ res = algo.farm_controller.calculate(algo, mdata, fdata, pre_rotor=False)
137
+ if self.urelax is not None:
138
+ res = self.urelax.calculate(algo, mdata, fdata, res)
139
+ fdata.update(res)
133
140
 
134
141
  return {v: fdata[v] for v in self.output_farm_vars(algo)}
@@ -134,8 +134,8 @@ class SeqState(States):
134
134
  """
135
135
  return self.states.output_point_vars(algo)
136
136
 
137
- def calculate(self, algo, mdata, fdata, pdata):
138
- """
137
+ def calculate(self, algo, mdata, fdata, tdata):
138
+ """ "
139
139
  The main model calculation.
140
140
 
141
141
  This function is executed on a single chunk of data,
@@ -149,14 +149,15 @@ class SeqState(States):
149
149
  The model data
150
150
  fdata: foxes.core.Data
151
151
  The farm data
152
- pdata: foxes.core.Data
153
- The point data
152
+ tdata: foxes.core.Data
153
+ The target point data
154
154
 
155
155
  Returns
156
156
  -------
157
157
  results: dict
158
158
  The resulting data, keys: output variable str.
159
- Values: numpy.ndarray with shape (n_states, n_points)
159
+ Values: numpy.ndarray with shape
160
+ (n_states, n_targets, n_tpoints)
160
161
 
161
162
  """
162
- return self.states.calculate(algo, mdata, fdata, pdata)
163
+ return self.states.calculate(algo, mdata, fdata, tdata)
@@ -4,7 +4,7 @@ from xarray import Dataset
4
4
  from foxes.algorithms.downwind.downwind import Downwind
5
5
  import foxes.constants as FC
6
6
  import foxes.variables as FV
7
- from foxes.core.data import Data
7
+ from foxes.core.data import MData, FData, TData
8
8
 
9
9
  from . import models as mdls
10
10
 
@@ -30,7 +30,8 @@ class Sequential(Downwind):
30
30
  The points of interest, shape: (n_states, n_points, 3)
31
31
  plugins: list of foxes.algorithm.sequential.SequentialIterPlugin
32
32
  The plugins, updated with every iteration
33
-
33
+ outputs: list of str
34
+ The output variables
34
35
  :group: algorithms.sequential
35
36
 
36
37
  """
@@ -58,15 +59,15 @@ class Sequential(Downwind):
58
59
 
59
60
  def __init__(
60
61
  self,
61
- mbook,
62
62
  farm,
63
63
  states,
64
64
  *args,
65
65
  points=None,
66
66
  ambient=False,
67
67
  calc_pars={},
68
- chunks={FC.STATE: None, FC.POINT: 10000},
68
+ chunks={FC.STATE: None, FC.POINT: 4000},
69
69
  plugins=[],
70
+ outputs=None,
70
71
  **kwargs,
71
72
  ):
72
73
  """
@@ -74,8 +75,6 @@ class Sequential(Downwind):
74
75
 
75
76
  Parameters
76
77
  ----------
77
- mbook: foxes.ModelBook
78
- The model book
79
78
  farm: foxes.WindFarm
80
79
  The wind farm
81
80
  states: foxes.core.States
@@ -93,18 +92,19 @@ class Sequential(Downwind):
93
92
  The xarray.Dataset chunk parameters
94
93
  plugins: list of foxes.algorithm.sequential.SequentialIterPlugin
95
94
  The plugins, updated with every iteration
95
+ outputs: list of str, optional
96
+ The output variables
96
97
  kwargs: dict, optional
97
98
  Additional arguments for Downwind
98
99
 
99
100
  """
100
- super().__init__(
101
- mbook, farm, mdls.SeqState(states), *args, chunks=chunks, **kwargs
102
- )
101
+ super().__init__(farm, mdls.SeqState(states), *args, chunks=chunks, **kwargs)
103
102
  self.ambient = ambient
104
103
  self.calc_pars = calc_pars
105
104
  self.states0 = self.states.states
106
105
  self.points = points
107
106
  self.plugins = plugins
107
+ self.outputs = outputs if outputs is not None else self.DEFAULT_FARM_OUTPUTS
108
108
 
109
109
  self._i = None
110
110
 
@@ -135,7 +135,7 @@ class Sequential(Downwind):
135
135
  self._counter = 0
136
136
 
137
137
  self._mlist, self._calc_pars = self._collect_farm_models(
138
- self.calc_pars, self.ambient
138
+ self.outputs, self.calc_pars, self.ambient
139
139
  )
140
140
  if not self._mlist.initialized:
141
141
  self._mlist.initialize(self, self.verbosity)
@@ -155,14 +155,14 @@ class Sequential(Downwind):
155
155
  print(f"Output farm variables:", ", ".join(self.farm_vars))
156
156
  print()
157
157
 
158
- self._mdata = Data(
158
+ self._mdata = MData(
159
159
  data={v: d[1] for v, d in self._mdata["data_vars"].items()},
160
160
  dims={v: d[0] for v, d in self._mdata["data_vars"].items()},
161
161
  loop_dims=[FC.STATE],
162
162
  name="mdata",
163
163
  )
164
164
 
165
- self._fdata = Data(
165
+ self._fdata = FData(
166
166
  data={
167
167
  v: np.zeros((self.n_states, self.n_turbines), dtype=FC.DTYPE)
168
168
  for v in self.farm_vars
@@ -182,14 +182,13 @@ class Sequential(Downwind):
182
182
  self.print(f"\nOutput point variables:", ", ".join(self._pvars), "\n")
183
183
 
184
184
  n_points = self.points.shape[1]
185
- self._pdata = Data.from_points(
185
+ self._tdata = TData.from_points(
186
186
  self.points,
187
187
  data={
188
- v: np.zeros((self.n_states, n_points), dtype=FC.DTYPE)
188
+ v: np.zeros((self.n_states, n_points, 1), dtype=FC.DTYPE)
189
189
  for v in self._pvars
190
190
  },
191
- dims={v: (FC.STATE, FC.POINT) for v in self._pvars},
192
- name="pdata",
191
+ dims={v: (FC.STATE, FC.TARGET, FC.TPOINT) for v in self._pvars},
193
192
  )
194
193
 
195
194
  for p in self.plugins:
@@ -207,7 +206,7 @@ class Sequential(Downwind):
207
206
  self.states._indx = self._inds[self._i]
208
207
  self.states._weight = self._weights[self._i]
209
208
 
210
- mdata = Data(
209
+ mdata = MData(
211
210
  data={
212
211
  v: d[self._i, None] if self._mdata.dims[v][0] == FC.STATE else d
213
212
  for v, d in self._mdata.items()
@@ -217,7 +216,7 @@ class Sequential(Downwind):
217
216
  name="mdata",
218
217
  )
219
218
 
220
- fdata = Data(
219
+ fdata = FData(
221
220
  data={
222
221
  v: np.zeros((1, self.n_turbines), dtype=FC.DTYPE)
223
222
  for v in self.farm_vars
@@ -234,7 +233,7 @@ class Sequential(Downwind):
234
233
  self._fdata[v][self._i] = d[0]
235
234
 
236
235
  fres = Dataset(
237
- coords={FC.STATE: [self.index], FC.TURBINE: np.arange(self.n_turbines)},
236
+ coords={FC.STATE: [self.index]},
238
237
  data_vars={v: ((FC.STATE, FC.TURBINE), d) for v, d in fres.items()},
239
238
  )
240
239
  fres[FC.TNAME] = ((FC.TURBINE,), self.farm.turbine_names)
@@ -250,25 +249,28 @@ class Sequential(Downwind):
250
249
 
251
250
  else:
252
251
  n_points = self.points.shape[1]
253
- pdata = Data.from_points(
252
+ tdata = TData.from_points(
254
253
  self.points[self.counter, None],
255
254
  data={
256
- v: np.zeros((1, n_points), dtype=FC.DTYPE) for v in self._pvars
255
+ v: np.zeros((1, n_points, 1), dtype=FC.DTYPE)
256
+ for v in self._pvars
257
257
  },
258
- dims={v: (FC.STATE, FC.POINT) for v in self._pvars},
259
- name="pdata",
258
+ dims={v: (FC.STATE, FC.TARGET, FC.TPOINT) for v in self._pvars},
260
259
  )
261
260
 
262
261
  pres = self._plist.calculate(
263
- self, mdata, fdata, pdata, parameters=self._calc_pars_p
262
+ self, mdata, fdata, tdata, parameters=self._calc_pars_p
264
263
  )
265
264
 
266
265
  for v, d in pres.items():
267
- self._pdata[v][self._i] = d[0]
266
+ self._tdata[v][self._i] = d[0]
268
267
 
269
268
  pres = Dataset(
270
- coords={FC.STATE: [self.index], FC.POINT: np.arange(n_points)},
271
- data_vars={v: ((FC.STATE, FC.POINT), d) for v, d in pres.items()},
269
+ coords={FC.STATE: [self.index]},
270
+ data_vars={
271
+ v: ((FC.STATE, FC.TARGET, FC.TPOINT), d)
272
+ for v, d in pres.items()
273
+ },
272
274
  )
273
275
 
274
276
  for p in self.plugins:
@@ -330,6 +332,26 @@ class Sequential(Downwind):
330
332
  """
331
333
  return self.states._indx if self.iterating else None
332
334
 
335
+ def states_i0(self, counter, algo=None):
336
+ """
337
+ Returns counter or index
338
+
339
+ Parameters
340
+ ----------
341
+ counter: bool
342
+ Flag for counter
343
+ algo: object, optional
344
+ Dummy argument, due to consistency with
345
+ foxes.core.Data.states_i0
346
+
347
+ Returns
348
+ -------
349
+ i0: int
350
+ The counter or index
351
+
352
+ """
353
+ return self.counter if counter else self.index
354
+
333
355
  @property
334
356
  def weight(self):
335
357
  """
@@ -350,7 +372,7 @@ class Sequential(Downwind):
350
372
 
351
373
  Returns
352
374
  -------
353
- d: foxes.core.Data
375
+ d: foxes.core.MData
354
376
  The current model data
355
377
 
356
378
  """
@@ -363,24 +385,24 @@ class Sequential(Downwind):
363
385
 
364
386
  Returns
365
387
  -------
366
- d: foxes.core.Data
388
+ d: foxes.core.FData
367
389
  The current farm data
368
390
 
369
391
  """
370
392
  return self._fdata
371
393
 
372
394
  @property
373
- def pdata(self):
395
+ def tdata(self):
374
396
  """
375
397
  Get the current point data
376
398
 
377
399
  Returns
378
400
  -------
379
- d: foxes.core.Data
401
+ d: foxes.core.TData
380
402
  The current point data
381
403
 
382
404
  """
383
- return self._pdata if self.points is not None and self.iterating else None
405
+ return self._tdata if self.points is not None and self.iterating else None
384
406
 
385
407
  @property
386
408
  def farm_results(self):
@@ -404,6 +426,19 @@ class Sequential(Downwind):
404
426
 
405
427
  return results
406
428
 
429
+ @property
430
+ def prev_farm_results(self):
431
+ """
432
+ Alias for farm_results
433
+
434
+ Returns
435
+ -------
436
+ results: xarray.Dataset
437
+ The overall farm results
438
+
439
+ """
440
+ return self.farm_results
441
+
407
442
  @property
408
443
  def cur_farm_results(self):
409
444
  """
@@ -449,7 +484,7 @@ class Sequential(Downwind):
449
484
  FC.POINT: np.arange(n_points),
450
485
  FC.XYH: np.arange(3),
451
486
  },
452
- data_vars={v: (self._pdata.dims[v], d) for v, d in self._pdata.items()},
487
+ data_vars={v: (self._tdata.dims[v], d) for v, d in self._tdata.items()},
453
488
  )
454
489
 
455
490
  return results
@@ -477,7 +512,7 @@ class Sequential(Downwind):
477
512
  FC.XYH: np.arange(3),
478
513
  },
479
514
  data_vars={
480
- v: (self._pdata.dims[v], d[i, None]) for v, d in self._pdata.items()
515
+ v: (self._tdata.dims[v], d[i, None]) for v, d in self._tdata.items()
481
516
  },
482
517
  )
483
518
 
@@ -501,13 +536,13 @@ class Sequential(Downwind):
501
536
  pvars = plist.output_point_vars(self)
502
537
 
503
538
  mdata = self.get_models_idata()
504
- mdata = Data(
539
+ mdata = MData(
505
540
  data={v: d[1] for v, d in mdata["data_vars"].items()},
506
541
  dims={v: d[0] for v, d in mdata["data_vars"].items()},
507
542
  loop_dims=[FC.STATE],
508
543
  name="mdata",
509
544
  )
510
- mdata = Data(
545
+ mdata = MData(
511
546
  data={
512
547
  v: d[self.states.counter, None] if mdata.dims[v][0] == FC.STATE else d
513
548
  for v, d in mdata.items()
@@ -517,24 +552,26 @@ class Sequential(Downwind):
517
552
  name="mdata",
518
553
  )
519
554
 
520
- fdata = Data(
555
+ fdata = FData(
521
556
  data={v: farm_results[v].to_numpy() for v in self.farm_vars},
522
557
  dims={v: (FC.STATE, FC.TURBINE) for v in self.farm_vars},
523
558
  loop_dims=[FC.STATE],
524
559
  name="fdata",
525
560
  )
526
561
 
527
- pdata = Data.from_points(
562
+ tdata = TData.from_points(
528
563
  points[0, None],
529
- data={v: np.zeros((1, n_points), dtype=FC.DTYPE) for v in pvars},
530
- dims={v: (FC.STATE, FC.POINT) for v in pvars},
531
- name="pdata",
564
+ data={v: np.zeros((1, n_points, 1), dtype=FC.DTYPE) for v in pvars},
565
+ dims={v: (FC.STATE, FC.TARGET, FC.TPOINT) for v in pvars},
566
+ name="tdata",
532
567
  )
533
568
 
534
- pres = plist.calculate(self, mdata, fdata, pdata, parameters=calc_pars)
569
+ pres = plist.calculate(self, mdata, fdata, tdata, parameters=calc_pars)
535
570
  pres = Dataset(
536
- coords={FC.STATE: self.states.index(), FC.POINT: np.arange(n_points)},
537
- data_vars={v: ((FC.STATE, FC.POINT), d) for v, d in pres.items()},
571
+ coords={FC.STATE: self.states.index()},
572
+ data_vars={
573
+ v: ((FC.STATE, FC.TARGET, FC.TPOINT), d) for v, d in pres.items()
574
+ },
538
575
  )
539
576
 
540
577
  # plist.finalize(self, self.verbosity)
foxes/constants.py CHANGED
@@ -25,33 +25,43 @@ TNAME = "tname"
25
25
  :group: foxes.constants
26
26
  """
27
27
 
28
- POINT = "point"
29
- """ Point identifier
28
+ TARGET = "target"
29
+ """ Target identifier
30
30
  :group: foxes.constants
31
31
  """
32
32
 
33
- POINTS = "points"
34
- """ Points identifier
33
+ TARGETS = "targets"
34
+ """ Targets identifier
35
35
  :group: foxes.constants
36
36
  """
37
37
 
38
- RPOINT = "rotor_point"
39
- """ Rotor point identifier
38
+ TPOINT = "target_point"
39
+ """ Target point identifier
40
40
  :group: foxes.constants
41
41
  """
42
42
 
43
- RPOINTS = "rotor_points"
44
- """ Rotor points identifier
43
+ TPOINTS = "target_points"
44
+ """ Points per target identifier
45
45
  :group: foxes.constants
46
46
  """
47
47
 
48
- RWEIGHTS = "rotor_weights"
49
- """ Rotor point weights identifier
48
+ TWEIGHTS = "tpoint_weights"
49
+ """ Target point weights identifier
50
50
  :group: foxes.constants
51
51
  """
52
52
 
53
- AMB_RPOINT_RESULTS = "amb_rpoint_res"
54
- """ Identified for ambient rotor point results
53
+ POINT = "point"
54
+ """ Point identifier
55
+ :group: foxes.constants
56
+ """
57
+
58
+ POINTS = "points"
59
+ """ Points identifier
60
+ :group: foxes.constants
61
+ """
62
+
63
+ AMB_TARGET_RESULTS = "amb_target_res"
64
+ """ Identified for ambient target results
55
65
  :group: foxes.constants
56
66
  """
57
67
 
@@ -81,18 +91,23 @@ STATES_SEL = "states_sel"
81
91
  :group: foxes.constants
82
92
  """
83
93
 
84
- STATE_SOURCE_TURBINE = "state_source_turbine"
85
- """Identifier for the source turbines per state
94
+ STATE_TURBINE = "state-turbine"
95
+ """Identifier for state-turbine dimensions
96
+ :group: foxes.constants
97
+ """
98
+
99
+ STATE_TARGET = "state-target"
100
+ """Identifier for state-target dimensions
86
101
  :group: foxes.constants
87
102
  """
88
103
 
89
- STATE_TURBINE = "state-turbine"
90
- """Identifier for state-turbine dimensions
104
+ STATE_TARGET_TPOINT = "state-target-tpoint"
105
+ """Identifier for state-target-tpoints dimensions
91
106
  :group: foxes.constants
92
107
  """
93
108
 
94
- STATE_POINT = "state-point"
95
- """Identifier for state-point dimensions
109
+ STATE_SOURCE_ORDERI = "state-source-orderi"
110
+ """Identifier for order index of wake causing turbines
96
111
  :group: foxes.constants
97
112
  """
98
113
 
foxes/core/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  Abstract classes and core functionality.
3
3
  """
4
4
 
5
- from .data import Data
5
+ from .data import Data, MData, FData, TData
6
6
  from .model import Model
7
7
  from .data_calc_model import DataCalcModel
8
8
  from .states import States, ExtendedStates
@@ -18,7 +18,7 @@ from .farm_controller import FarmController
18
18
  from .turbine import Turbine
19
19
  from .partial_wakes_model import PartialWakesModel
20
20
  from .wake_frame import WakeFrame
21
- from .wake_model import WakeModel
21
+ from .wake_model import WakeModel, TurbineInductionModel
22
22
  from .wake_superposition import WakeSuperposition
23
23
  from .vertical_profile import VerticalProfile
24
24
  from .axial_induction_model import AxialInductionModel
foxes/core/algorithm.py CHANGED
@@ -63,6 +63,9 @@ class Algorithm(Model):
63
63
  self.n_turbines = farm.n_turbines
64
64
  self.dbook = StaticData() if dbook is None else dbook
65
65
 
66
+ if chunks is not None and FC.TARGET not in chunks:
67
+ self.chunks[FC.TARGET] = chunks.get(FC.POINT, None)
68
+
66
69
  self._idata_mem = Dict()
67
70
 
68
71
  def print(self, *args, vlim=1, **kwargs):
@@ -115,15 +118,24 @@ class Algorithm(Model):
115
118
  raise ValueError(
116
119
  f"Input {mtype} data entry '{v}': Dimension '{FC.STATE}' not at first position, got {t[0]}"
117
120
  )
118
- if FC.POINT in t[0] and t[0][1] != FC.POINT:
119
- raise ValueError(
120
- f"Input {mtype} data entry '{v}': Dimension '{FC.POINT}' not at second position, got {t[0]}"
121
- )
122
- elif FC.POINT in t[0]:
123
- if t[0][0] != FC.POINT:
124
- raise ValueError(
125
- f"Input {mtype} data entry '{v}': Dimension '{FC.POINT}' not at first position, got {t[0]}"
126
- )
121
+ if FC.TURBINE in t[0]:
122
+ if t[0][1] != FC.TURBINE:
123
+ raise ValueError(
124
+ f"Input {mtype} data entry '{v}': Dimension '{FC.TURBINE}' not at second position, got {t[0]}"
125
+ )
126
+ if FC.TARGET in t[0]:
127
+ if t[0][1] != FC.TARGET:
128
+ raise ValueError(
129
+ f"Input {mtype} data entry '{v}': Dimension '{FC.TARGET}' not at second position, got {t[0]}"
130
+ )
131
+ if len(t[0]) < 3 or t[0][2] != FC.TPOINT:
132
+ raise KeyError(
133
+ f"Input {mtype} data entry '{v}': Expecting dimension '{FC.TPOINT}' as third entry. Got {t[0]}"
134
+ )
135
+ elif FC.TURBINE in t[0]:
136
+ raise ValueError(
137
+ f"Input {mtype} data entry '{v}': Dimension '{FC.TURBINE}' requires combination with dimension '{FC.STATE}'"
138
+ )
127
139
  for d, s in zip(t[0], t[1].shape):
128
140
  if d not in sizes:
129
141
  sizes[d] = s
@@ -153,9 +165,9 @@ class Algorithm(Model):
153
165
  raise ValueError(
154
166
  f"Dimension '{FC.TURBINE}' cannot be chunked, got chunks {self.chunks}"
155
167
  )
156
- if FC.RPOINT in self.chunks.keys():
168
+ if FC.TPOINT in self.chunks.keys():
157
169
  raise ValueError(
158
- f"Dimension '{FC.RPOINT}' cannot be chunked, got chunks {self.chunks}"
170
+ f"Dimension '{FC.TPOINT}' cannot be chunked, got chunks {self.chunks}"
159
171
  )
160
172
  xrdata = xrdata.chunk(
161
173
  chunks={c: v for c, v in self.chunks.items() if c in sizes}
@@ -376,7 +388,14 @@ class Algorithm(Model):
376
388
  raise ValueError(
377
389
  f"points have wrong dimensions, expecting ({self.n_states}, {points.shape[1]}, 3), got {points.shape}"
378
390
  )
379
- idata["data_vars"][FC.POINTS] = ((FC.STATE, FC.POINT, FC.XYH), points)
391
+ idata["data_vars"][FC.TARGETS] = (
392
+ (FC.STATE, FC.TARGET, FC.TPOINT, FC.XYH),
393
+ points[:, :, None, :],
394
+ )
395
+ idata["data_vars"][FC.TWEIGHTS] = (
396
+ (FC.TPOINT,),
397
+ np.array([1.0], dtype=FC.DTYPE),
398
+ )
380
399
 
381
400
  sizes = self.__get_sizes(idata, "point")
382
401
  return self.__get_xrdata(idata, sizes)