foxes 1.2.5__py3-none-any.whl → 1.4__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (201) hide show
  1. docs/source/conf.py +3 -3
  2. examples/abl_states/run.py +2 -2
  3. examples/compare_rotors_pwakes/run.py +1 -1
  4. examples/compare_wakes/run.py +1 -2
  5. examples/dyn_wakes/run.py +29 -6
  6. examples/induction/run.py +3 -3
  7. examples/multi_height/run.py +1 -1
  8. examples/power_mask/run.py +2 -2
  9. examples/quickstart/run.py +16 -0
  10. examples/random_timeseries/run.py +3 -4
  11. examples/scan_row/run.py +3 -3
  12. examples/sequential/run.py +33 -10
  13. examples/single_state/run.py +3 -4
  14. examples/states_lookup_table/run.py +3 -3
  15. examples/streamline_wakes/run.py +27 -4
  16. examples/tab_file/run.py +3 -3
  17. examples/timelines/run.py +29 -5
  18. examples/timeseries/run.py +3 -3
  19. examples/timeseries_slurm/run.py +3 -3
  20. examples/wind_rose/run.py +3 -3
  21. examples/yawed_wake/run.py +16 -8
  22. foxes/__init__.py +22 -18
  23. foxes/algorithms/__init__.py +6 -6
  24. foxes/algorithms/downwind/__init__.py +2 -2
  25. foxes/algorithms/downwind/downwind.py +53 -27
  26. foxes/algorithms/downwind/models/__init__.py +6 -6
  27. foxes/algorithms/downwind/models/farm_wakes_calc.py +22 -14
  28. foxes/algorithms/downwind/models/init_farm_data.py +4 -5
  29. foxes/algorithms/downwind/models/point_wakes_calc.py +7 -13
  30. foxes/algorithms/downwind/models/reorder_farm_output.py +5 -1
  31. foxes/algorithms/downwind/models/set_amb_point_results.py +7 -7
  32. foxes/algorithms/iterative/__init__.py +7 -3
  33. foxes/algorithms/iterative/iterative.py +1 -2
  34. foxes/algorithms/iterative/models/__init__.py +7 -3
  35. foxes/algorithms/iterative/models/farm_wakes_calc.py +15 -8
  36. foxes/algorithms/sequential/__init__.py +3 -3
  37. foxes/algorithms/sequential/models/__init__.py +2 -2
  38. foxes/algorithms/sequential/models/seq_state.py +0 -18
  39. foxes/algorithms/sequential/sequential.py +8 -22
  40. foxes/config/__init__.py +5 -1
  41. foxes/constants.py +22 -0
  42. foxes/core/__init__.py +45 -22
  43. foxes/core/algorithm.py +0 -1
  44. foxes/core/data.py +56 -29
  45. foxes/core/engine.py +28 -14
  46. foxes/core/farm_controller.py +2 -2
  47. foxes/core/farm_data_model.py +1 -0
  48. foxes/core/ground_model.py +4 -13
  49. foxes/core/model.py +5 -5
  50. foxes/core/partial_wakes_model.py +147 -10
  51. foxes/core/point_data_model.py +2 -3
  52. foxes/core/rotor_model.py +42 -38
  53. foxes/core/states.py +4 -50
  54. foxes/core/turbine.py +2 -1
  55. foxes/core/wake_deflection.py +130 -0
  56. foxes/core/wake_model.py +222 -9
  57. foxes/core/wake_superposition.py +122 -4
  58. foxes/core/wind_farm.py +6 -6
  59. foxes/data/__init__.py +7 -2
  60. foxes/data/states/weibull_sectors_12.csv +13 -0
  61. foxes/data/states/weibull_sectors_12.nc +0 -0
  62. foxes/engines/__init__.py +14 -15
  63. foxes/engines/dask.py +39 -14
  64. foxes/engines/numpy.py +0 -3
  65. foxes/input/__init__.py +3 -3
  66. foxes/input/farm_layout/__init__.py +8 -8
  67. foxes/input/farm_layout/from_csv.py +1 -1
  68. foxes/input/farm_layout/ring.py +0 -1
  69. foxes/input/states/__init__.py +22 -11
  70. foxes/input/states/create/__init__.py +3 -2
  71. foxes/input/states/field_data_nc.py +48 -84
  72. foxes/input/states/multi_height.py +40 -60
  73. foxes/input/states/one_point_flow.py +22 -25
  74. foxes/input/states/scan.py +6 -19
  75. foxes/input/states/single.py +6 -18
  76. foxes/input/states/states_table.py +25 -44
  77. foxes/input/states/weibull_sectors.py +225 -0
  78. foxes/input/states/wrg_states.py +151 -37
  79. foxes/input/yaml/__init__.py +9 -3
  80. foxes/input/yaml/dict.py +19 -19
  81. foxes/input/yaml/windio/__init__.py +10 -5
  82. foxes/input/yaml/windio/read_attributes.py +2 -2
  83. foxes/input/yaml/windio/read_farm.py +5 -5
  84. foxes/input/yaml/windio/read_fields.py +4 -2
  85. foxes/input/yaml/windio/read_site.py +52 -0
  86. foxes/input/yaml/windio/windio.py +1 -1
  87. foxes/models/__init__.py +15 -14
  88. foxes/models/axial_induction/__init__.py +2 -2
  89. foxes/models/farm_controllers/__init__.py +1 -1
  90. foxes/models/farm_models/__init__.py +1 -1
  91. foxes/models/ground_models/__init__.py +3 -2
  92. foxes/models/ground_models/wake_mirror.py +3 -3
  93. foxes/models/model_book.py +175 -49
  94. foxes/models/partial_wakes/__init__.py +6 -6
  95. foxes/models/partial_wakes/axiwake.py +30 -5
  96. foxes/models/partial_wakes/centre.py +47 -0
  97. foxes/models/partial_wakes/rotor_points.py +45 -9
  98. foxes/models/partial_wakes/segregated.py +2 -20
  99. foxes/models/partial_wakes/top_hat.py +27 -2
  100. foxes/models/point_models/__init__.py +4 -4
  101. foxes/models/rotor_models/__init__.py +3 -3
  102. foxes/models/rotor_models/centre.py +6 -4
  103. foxes/models/turbine_models/__init__.py +11 -11
  104. foxes/models/turbine_models/set_farm_vars.py +0 -1
  105. foxes/models/turbine_types/PCt_file.py +0 -2
  106. foxes/models/turbine_types/PCt_from_two.py +0 -2
  107. foxes/models/turbine_types/__init__.py +9 -9
  108. foxes/models/vertical_profiles/__init__.py +7 -7
  109. foxes/models/wake_deflections/__init__.py +3 -0
  110. foxes/models/{wake_frames/yawed_wakes.py → wake_deflections/bastankhah2016.py} +32 -111
  111. foxes/models/wake_deflections/jimenez.py +277 -0
  112. foxes/models/wake_deflections/no_deflection.py +94 -0
  113. foxes/models/wake_frames/__init__.py +6 -7
  114. foxes/models/wake_frames/dynamic_wakes.py +12 -3
  115. foxes/models/wake_frames/rotor_wd.py +3 -1
  116. foxes/models/wake_frames/seq_dynamic_wakes.py +45 -8
  117. foxes/models/wake_frames/streamlines.py +8 -6
  118. foxes/models/wake_frames/timelines.py +19 -3
  119. foxes/models/wake_models/__init__.py +7 -7
  120. foxes/models/wake_models/dist_sliced.py +50 -84
  121. foxes/models/wake_models/gaussian.py +20 -0
  122. foxes/models/wake_models/induction/__init__.py +5 -5
  123. foxes/models/wake_models/induction/rankine_half_body.py +30 -71
  124. foxes/models/wake_models/induction/rathmann.py +65 -64
  125. foxes/models/wake_models/induction/self_similar.py +65 -68
  126. foxes/models/wake_models/induction/self_similar2020.py +0 -3
  127. foxes/models/wake_models/induction/vortex_sheet.py +71 -75
  128. foxes/models/wake_models/ti/__init__.py +2 -2
  129. foxes/models/wake_models/ti/crespo_hernandez.py +5 -3
  130. foxes/models/wake_models/ti/iec_ti.py +6 -4
  131. foxes/models/wake_models/top_hat.py +58 -7
  132. foxes/models/wake_models/wind/__init__.py +6 -4
  133. foxes/models/wake_models/wind/bastankhah14.py +25 -7
  134. foxes/models/wake_models/wind/bastankhah16.py +35 -3
  135. foxes/models/wake_models/wind/jensen.py +15 -2
  136. foxes/models/wake_models/wind/turbopark.py +28 -2
  137. foxes/models/wake_superpositions/__init__.py +18 -9
  138. foxes/models/wake_superpositions/ti_linear.py +4 -4
  139. foxes/models/wake_superpositions/ti_max.py +4 -4
  140. foxes/models/wake_superpositions/ti_pow.py +4 -4
  141. foxes/models/wake_superpositions/ti_quadratic.py +4 -4
  142. foxes/models/wake_superpositions/wind_vector.py +257 -0
  143. foxes/models/wake_superpositions/ws_linear.py +9 -10
  144. foxes/models/wake_superpositions/ws_max.py +8 -8
  145. foxes/models/wake_superpositions/ws_pow.py +8 -8
  146. foxes/models/wake_superpositions/ws_product.py +4 -4
  147. foxes/models/wake_superpositions/ws_quadratic.py +8 -8
  148. foxes/output/__init__.py +21 -19
  149. foxes/output/farm_layout.py +14 -6
  150. foxes/output/farm_results_eval.py +51 -27
  151. foxes/output/flow_plots_2d/__init__.py +2 -2
  152. foxes/output/flow_plots_2d/get_fig.py +4 -2
  153. foxes/output/rose_plot.py +23 -5
  154. foxes/output/seq_plugins/__init__.py +2 -2
  155. foxes/output/seq_plugins/seq_flow_ani_plugin.py +0 -3
  156. foxes/output/seq_plugins/seq_wake_debug_plugin.py +0 -1
  157. foxes/output/slice_data.py +16 -19
  158. foxes/output/turbine_type_curves.py +7 -8
  159. foxes/utils/__init__.py +37 -19
  160. foxes/utils/abl/__init__.py +4 -4
  161. foxes/utils/cubic_roots.py +1 -1
  162. foxes/utils/data_book.py +4 -3
  163. foxes/utils/dict.py +3 -3
  164. foxes/utils/exec_python.py +5 -5
  165. foxes/utils/factory.py +1 -3
  166. foxes/utils/geom2d/__init__.py +7 -5
  167. foxes/utils/geopandas_utils.py +2 -2
  168. foxes/utils/pandas_utils.py +4 -3
  169. foxes/utils/tab_files.py +0 -1
  170. foxes/utils/weibull.py +28 -0
  171. foxes/utils/wrg_utils.py +3 -1
  172. foxes/utils/xarray_utils.py +9 -2
  173. foxes/variables.py +67 -9
  174. {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/METADATA +14 -21
  175. foxes-1.4.dist-info/RECORD +320 -0
  176. {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/WHEEL +1 -1
  177. tests/0_consistency/iterative/test_iterative.py +2 -3
  178. tests/0_consistency/partial_wakes/test_partial_wakes.py +2 -2
  179. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +2 -3
  180. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +48 -56
  181. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +0 -1
  182. tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +33 -36
  183. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +0 -1
  184. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +0 -2
  185. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +3 -3
  186. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +3 -4
  187. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +3 -4
  188. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +3 -4
  189. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +3 -4
  190. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +0 -2
  191. tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +3 -3
  192. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +3 -3
  193. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +0 -1
  194. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +3 -4
  195. tests/3_examples/test_examples.py +3 -2
  196. foxes/output/round.py +0 -10
  197. foxes/utils/pandas_helpers.py +0 -178
  198. foxes-1.2.5.dist-info/RECORD +0 -312
  199. {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/entry_points.txt +0 -0
  200. {foxes-1.2.5.dist-info → foxes-1.4.dist-info/licenses}/LICENSE +0 -0
  201. {foxes-1.2.5.dist-info → foxes-1.4.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,9 @@
1
1
  import numpy as np
2
+ from scipy.interpolate import interpn
2
3
 
3
4
  from foxes.core.states import States
4
5
  from foxes.config import config, get_input_path
5
- from foxes.utils.wrg_utils import ReaderWRG
6
+ from foxes.utils import ReaderWRG, weibull_weights
6
7
  from foxes.data import STATES
7
8
  import foxes.variables as FV
8
9
  import foxes.constants as FC
@@ -25,6 +26,8 @@ class WRGStates(States):
25
26
  bounds_extra_space: float or str
26
27
  The extra space, either float in m,
27
28
  or str for units of D, e.g. '2.5D'
29
+ interpn_pars: dict
30
+ Additional parameters for scipy.interpolate.interpn
28
31
 
29
32
  :group: input.states
30
33
 
@@ -36,7 +39,7 @@ class WRGStates(States):
36
39
  ws_bins,
37
40
  fixed_vars={},
38
41
  bounds_extra_space="1D",
39
- **kwargs,
42
+ **interpn_pars,
40
43
  ):
41
44
  """
42
45
  Constructor
@@ -54,15 +57,16 @@ class WRGStates(States):
54
57
  bounds_extra_space: float or str, optional
55
58
  The extra space, either float in m,
56
59
  or str for units of D, e.g. '2.5D'
57
- kwargs: dict, optional
58
- Parameters for the base class
60
+ interpn_pars: dict, optional
61
+ Additional parameters for scipy.interpolate.interpn
59
62
 
60
63
  """
61
- super().__init__(**kwargs)
64
+ super().__init__()
62
65
  self.wrg_fname = wrg_fname
63
66
  self.ws_bins = np.asarray(ws_bins)
64
67
  self.fixed_vars = fixed_vars
65
68
  self.bounds_extra_space = bounds_extra_space
69
+ self.interpn_pars = interpn_pars
66
70
 
67
71
  def load_data(self, algo, verbosity=0):
68
72
  """
@@ -100,11 +104,14 @@ class WRGStates(States):
100
104
  elif verbosity:
101
105
  print(f"States '{self.name}': Reading file {fpath}")
102
106
  wrg = ReaderWRG(fpath)
103
- self._p0 = np.array([wrg.x0, wrg.y0], dtype=config.dtype_double)
104
- self._nx = wrg.nx
105
- self._ny = wrg.ny
106
- self._ns = wrg.n_sectors
107
- self._res = wrg.resolution
107
+ p0 = np.array([wrg.x0, wrg.y0], dtype=config.dtype_double)
108
+ nx = wrg.nx
109
+ ny = wrg.ny
110
+ ns = wrg.n_sectors
111
+ res = wrg.resolution
112
+ p1 = p0 + np.array([nx * res, ny * res])
113
+ if verbosity > 0:
114
+ print(f"States '{self.name}': Data bounds {p0} - {p1}")
108
115
 
109
116
  # find bounds:
110
117
  if self.bounds_extra_space is not None:
@@ -112,48 +119,56 @@ class WRGStates(States):
112
119
  extra_space=self.bounds_extra_space, algo=algo
113
120
  )
114
121
  if verbosity > 0:
115
- print(
116
- f"States '{self.name}': Restricting to bounds {xy_min} - {xy_max}"
117
- )
118
- xy_min -= self._p0
119
- xy_max -= self._p0
120
- ij_min = np.asarray(xy_min / self._res, dtype=config.dtype_int)
121
- ij_max = np.asarray(xy_max / self._res, dtype=config.dtype_int) + 1
122
+ print(f"States '{self.name}': Farm bounds {xy_min} - {xy_max}")
123
+ ij_min = np.asarray((xy_min - p0) / res, dtype=config.dtype_int)
124
+ ij_max = np.asarray((xy_max - p0) / res, dtype=config.dtype_int) + 1
122
125
  sx = slice(ij_min[0], ij_max[0])
123
126
  sy = slice(ij_min[1], ij_max[1])
124
127
  else:
125
128
  sx = np.s_[:]
126
129
  sy = np.s_[:]
130
+ self._x = p0[0] + np.arange(nx) * res
131
+ self._x = self._x[sx]
132
+ self._y = p0[1] + np.arange(ny) * res
133
+ self._y = self._y[sy]
134
+ if len(self._x) < 2 or len(self._y) < 2:
135
+ raise ValueError("No overlap between data bounds and farm bounds")
136
+ p0[0] = np.min(self._x)
137
+ p0[1] = np.min(self._y)
138
+ p1[0] = np.max(self._x)
139
+ p1[1] = np.max(self._y)
140
+ if verbosity > 0:
141
+ print(f"States '{self.name}': New bounds {p0} - {p1}")
127
142
 
128
143
  # store data:
129
144
  A = []
130
145
  k = []
131
- fs = []
132
- for s in range(self._ns):
133
- A.append(wrg.data[f"A_{s}"].to_numpy().reshape(self._ny, self._nx)[sy, sx])
134
- k.append(wrg.data[f"k_{s}"].to_numpy().reshape(self._ny, self._nx)[sy, sx])
135
- fs.append(
136
- wrg.data[f"fs_{s}"].to_numpy().reshape(self._ny, self._nx)[sy, sx]
137
- )
146
+ f = []
147
+ for s in range(ns):
148
+ A.append(wrg.data[f"As_{s}"].to_numpy().reshape(ny, nx)[sy, sx])
149
+ k.append(wrg.data[f"Ks_{s}"].to_numpy().reshape(ny, nx)[sy, sx])
150
+ f.append(wrg.data[f"fs_{s}"].to_numpy().reshape(ny, nx)[sy, sx])
138
151
  del wrg
139
152
  A = np.stack(A, axis=0).T
140
153
  k = np.stack(k, axis=0).T
141
- fs = np.stack(fs, axis=0).T
142
- self._data = np.stack([A, k, fs], axis=-1) # (x, y, wd, AKfs)
154
+ f = np.stack(f, axis=0).T
155
+ self._data = np.stack([A, k, f], axis=-1) # (x, y, wd, AKfs)
143
156
 
144
157
  # store ws and wd:
145
- self.WSWD = self.var("WSWD")
146
- self._wds = np.arange(0.0, 360.0, 360 / self._ns)
158
+ self.VARS = self.var("VARS")
159
+ self.DATA = self.var("DATA")
160
+ self._wds = np.arange(0.0, 360.0, 360 / ns)
147
161
  self._wsd = self.ws_bins[1:] - self.ws_bins[:-1]
148
162
  self._wss = 0.5 * (self.ws_bins[:-1] + self.ws_bins[1:])
149
- self._N = len(self._wss) * self._ns
150
- wswd = np.zeros((len(self._wss), self._ns, 2), dtype=config.dtype_double)
151
- wswd[..., 0] = self._wss[:, None]
152
- wswd[..., 1] = self._wds[None, :]
153
- wswd = wswd.reshape(self._N, 2)
163
+ self._N = len(self._wss) * ns
164
+ data = np.zeros((len(self._wss), ns, 3), dtype=config.dtype_double)
165
+ data[..., 0] = self._wss[:, None]
166
+ data[..., 1] = self._wds[None, :]
167
+ data[..., 2] = self._wsd[:, None]
168
+ data = data.reshape(self._N, 3)
154
169
  idata = super().load_data(algo, verbosity)
155
- idata.coords[self.WSWD] = [self.var(FV.WS), self.var(FV.WD)]
156
- idata.data_vars[self.WSWD] = ((FC.STATE, self.WSWD), wswd)
170
+ idata["coords"][self.VARS] = ["ws", "wd", "dws"]
171
+ idata["data_vars"][self.DATA] = ((FC.STATE, self.VARS), data)
157
172
 
158
173
  return idata
159
174
 
@@ -184,6 +199,105 @@ class WRGStates(States):
184
199
  The output variable names
185
200
 
186
201
  """
187
- ovars = set([FV.WS, FV.WD, FV.WEIGHT])
188
- ovars.update(self.fixed_vars.values())
202
+ ovars = set([FV.WS, FV.WD])
203
+ ovars.update(self.fixed_vars.keys())
189
204
  return list(ovars)
205
+
206
+ def calculate(self, algo, mdata, fdata, tdata):
207
+ """
208
+ The main model calculation.
209
+
210
+ This function is executed on a single chunk of data,
211
+ all computations should be based on numpy arrays.
212
+
213
+ Parameters
214
+ ----------
215
+ algo: foxes.core.Algorithm
216
+ The calculation algorithm
217
+ mdata: foxes.core.MData
218
+ The model data
219
+ fdata: foxes.core.FData
220
+ The farm data
221
+ tdata: foxes.core.TData
222
+ The target point data
223
+
224
+ Returns
225
+ -------
226
+ results: dict
227
+ The resulting data, keys: output variable str.
228
+ Values: numpy.ndarray with shape
229
+ (n_states, n_targets, n_tpoints)
230
+
231
+ """
232
+
233
+ # prepare:
234
+ n_states = tdata.n_states
235
+ n_targets = tdata.n_targets
236
+ n_tpoints = tdata.n_tpoints
237
+ n_pts = n_states * n_targets * n_tpoints
238
+ points = tdata[FC.TARGETS]
239
+ ws = mdata[self.DATA][:, 0]
240
+ wd = mdata[self.DATA][:, 1]
241
+ wsd = mdata[self.DATA][:, 2]
242
+
243
+ out = {}
244
+
245
+ out[FV.WS] = tdata[FV.WS]
246
+ out[FV.WS][:] = ws[:, None, None]
247
+
248
+ out[FV.WD] = tdata[FV.WD]
249
+ out[FV.WD][:] = wd[:, None, None]
250
+
251
+ for v, d in self.fixed_vars.items():
252
+ out[v] = tdata[v]
253
+ out[v][:] = d
254
+
255
+ # interpolate A, k, f from x, y, wd
256
+ z = points[..., 2].copy()
257
+ points[..., 2] = wd[:, None, None]
258
+ pts = points.reshape(n_pts, 3)
259
+ gvars = (self._x, self._y, self._wds)
260
+ try:
261
+ ipars = dict(bounds_error=True, fill_value=None)
262
+ ipars.update(self.interpn_pars)
263
+ data = interpn(gvars, self._data, pts, **ipars).reshape(
264
+ n_states, n_targets, n_tpoints, 3
265
+ )
266
+ except ValueError as e:
267
+ print(f"\nStates '{self.name}': Interpolation error")
268
+ print("INPUT VARS: (x, y, wd)")
269
+ print(
270
+ "DATA BOUNDS:",
271
+ [float(np.min(d)) for d in gvars],
272
+ [float(np.max(d)) for d in gvars],
273
+ )
274
+ print(
275
+ "EVAL BOUNDS:",
276
+ [float(np.min(p)) for p in pts.T],
277
+ [float(np.max(p)) for p in pts.T],
278
+ )
279
+ print(
280
+ "\nMaybe you want to try the option 'bounds_error=False'? This will extrapolate the data.\n"
281
+ )
282
+ raise e
283
+
284
+ A = data[..., 0]
285
+ k = data[..., 1]
286
+ f = data[..., 2]
287
+ points[..., 2] = z
288
+ del data, gvars, pts, z, wd
289
+
290
+ tdata.add(
291
+ FV.WEIGHT,
292
+ f,
293
+ dims=(FC.STATE, FC.TARGET, FC.TPOINT),
294
+ )
295
+
296
+ tdata[FV.WEIGHT] *= weibull_weights(
297
+ ws=out[FV.WS],
298
+ ws_deltas=wsd[:, None, None],
299
+ A=A,
300
+ k=k,
301
+ )
302
+
303
+ return out
@@ -1,3 +1,9 @@
1
- from .dict import read_dict, run_dict, run_outputs, run_obj_function, get_output_obj
2
- from .yaml import foxes_yaml
3
- from .windio.windio import read_windio, foxes_windio
1
+ from .dict import read_dict as read_dict
2
+ from .dict import run_dict as run_dict
3
+ from .dict import run_outputs as run_outputs
4
+ from .dict import run_obj_function as run_obj_function
5
+ from .dict import get_output_obj as get_output_obj
6
+
7
+ from .yaml import foxes_yaml as foxes_yaml
8
+ from .windio.windio import read_windio as read_windio
9
+ from .windio.windio import foxes_windio as foxes_windio
foxes/input/yaml/dict.py CHANGED
@@ -75,13 +75,13 @@ def read_dict(
75
75
  print(*args, **kwargs)
76
76
 
77
77
  # set working directory:
78
- l = 0
78
+ ld = 0
79
79
  for c, d in zip(
80
80
  [FC.WORK_DIR, FC.INPUT_DIR, FC.OUTPUT_DIR], [work_dir, input_dir, output_dir]
81
81
  ):
82
82
  if d is not None:
83
83
  config[c] = d
84
- l = max(l, len(str(d)))
84
+ ld = max(ld, len(str(d)))
85
85
  _print("\n--------------------- Reading foxes parameter dict ---------------------")
86
86
  _print("Working directory :", config.work_dir)
87
87
  _print("Input directory :", config.input_dir)
@@ -95,9 +95,9 @@ def read_dict(
95
95
  else:
96
96
  states = algo.states
97
97
  else:
98
- assert (
99
- algo is None
100
- ), f"Cannot handle both the algo and the states argument, please drop one"
98
+ assert algo is None, (
99
+ "Cannot handle both the algo and the states argument, please drop one"
100
+ )
101
101
 
102
102
  # create model book:
103
103
  if mbook is None:
@@ -119,9 +119,9 @@ def read_dict(
119
119
  else:
120
120
  mbook = algo.mbook
121
121
  else:
122
- assert (
123
- algo is None
124
- ), f"Cannot handle both the algo and the mbook argument, please drop one"
122
+ assert algo is None, (
123
+ "Cannot handle both the algo and the mbook argument, please drop one"
124
+ )
125
125
 
126
126
  # create farm:
127
127
  if farm is None:
@@ -129,8 +129,8 @@ def read_dict(
129
129
  _print("Creating wind farm")
130
130
  fdict = idict.get_item("wind_farm")
131
131
  lyts = [
132
- Dict(l, name=f"{fdict.name}.layout{i}")
133
- for i, l in enumerate(fdict.pop_item("layouts"))
132
+ Dict(lo, name=f"{fdict.name}.layout{i}")
133
+ for i, lo in enumerate(fdict.pop_item("layouts"))
134
134
  ]
135
135
  farm = WindFarm(**fdict)
136
136
  for lyt in lyts:
@@ -141,9 +141,9 @@ def read_dict(
141
141
  else:
142
142
  farm = algo.farm
143
143
  else:
144
- assert (
145
- algo is None
146
- ), f"Cannot handle both the algo and the farm argument, please drop one"
144
+ assert algo is None, (
145
+ "Cannot handle both the algo and the farm argument, please drop one"
146
+ )
147
147
 
148
148
  # create engine:
149
149
  engine = None
@@ -321,12 +321,12 @@ def run_obj_function(
321
321
 
322
322
  def _set_label(rlabels, k, r):
323
323
  if k not in ["", "none", "None", "_", "__"]:
324
- assert (
325
- k[0] == "$"
326
- ), f"Output {i} of type '{ocls}', function '{fname}': result labels must start with '$', got '{k}'"
327
- assert (
328
- "[" not in k and "]" not in k and "," not in k
329
- ), f"Output {i} of type '{ocls}', function '{fname}': result labels cannot contain '[' or ']' or comma, got '{k}'"
324
+ assert k[0] == "$", (
325
+ f"Output {i} of type '{ocls}', function '{fname}': result labels must start with '$', got '{k}'"
326
+ )
327
+ assert "[" not in k and "]" not in k and "," not in k, (
328
+ f"Output {i} of type '{ocls}', function '{fname}': result labels cannot contain '[' or ']' or comma, got '{k}'"
329
+ )
330
330
  _print(f" result label {k}: {type(r).__name__}")
331
331
  rlabels[k] = r
332
332
 
@@ -1,5 +1,10 @@
1
- from .windio import foxes_windio, read_windio
2
- from .read_site import read_site
3
- from .read_fields import read_wind_resource_field
4
- from .read_farm import read_layout, read_turbine_types, read_farm
5
- from .read_attributes import read_attributes
1
+ from .windio import foxes_windio as foxes_windio
2
+ from .windio import read_windio as read_windio
3
+
4
+ from .read_farm import read_layout as read_layout
5
+ from .read_farm import read_turbine_types as read_turbine_types
6
+ from .read_farm import read_farm as read_farm
7
+
8
+ from .read_site import read_site as read_site
9
+ from .read_fields import read_wind_resource_field as read_wind_resource_field
10
+ from .read_attributes import read_attributes as read_attributes
@@ -202,7 +202,7 @@ def _read_rotor_averaging(rotor_averaging, algo_dict, verbosity):
202
202
  elif background_averaging in ["none", "None", None]:
203
203
  algo_dict["rotor_model"] = None
204
204
  elif background_averaging == "grid":
205
- algo_dict["rotor_model"] = f"grid{nx*ny}"
205
+ algo_dict["rotor_model"] = f"grid{nx * ny}"
206
206
  else:
207
207
  algo_dict["rotor_model"] = background_averaging
208
208
 
@@ -215,7 +215,7 @@ def _read_rotor_averaging(rotor_averaging, algo_dict, verbosity):
215
215
  algo_dict["partial_wakes"] = "rotor_points"
216
216
  else:
217
217
  if grid == "grid":
218
- algo_dict["partial_wakes"] = f"grid{nx*ny}"
218
+ algo_dict["partial_wakes"] = f"grid{nx * ny}"
219
219
  else:
220
220
  algo_dict["partial_wakes"] = grid
221
221
  else:
@@ -77,7 +77,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
77
77
  return FV.REWS if wse == 1 else (FV.REWS2 if wse == 2 else FV.REWS3)
78
78
 
79
79
  _print(f" Creating model '{tname}'", level=3)
80
- _print(f" Turbine type class: PCtFomTwo", level=3)
80
+ _print(" Turbine type class: PCtFomTwo", level=3)
81
81
  mbook.turbine_types[tname] = TurbineType.new(
82
82
  ttype_type="PCtFromTwo",
83
83
  data_source_P=data_P,
@@ -111,7 +111,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
111
111
  data_ct = pd.DataFrame(data={"ws": ws_ct, "ct": ct})
112
112
 
113
113
  _print(f" Creating model '{tname}'", level=3)
114
- _print(f" Turbine type class: CpCtFromTwo", level=3)
114
+ _print(" Turbine type class: CpCtFromTwo", level=3)
115
115
  mbook.turbine_types[tname] = TurbineType.new(
116
116
  ttype_type="CpCtFromTwo",
117
117
  data_source_cp=data_cp,
@@ -126,7 +126,7 @@ def read_turbine_types(wio_farm, mbook, ws_exp_P, ws_exp_ct, verbosity):
126
126
  _print(" ", mbook.turbine_types[tname], level=3)
127
127
 
128
128
  else:
129
- raise KeyError(f"Expecting either 'power_curve' or 'Cp_curve'")
129
+ raise KeyError("Expecting either 'power_curve' or 'Cp_curve'")
130
130
 
131
131
  return ttypes
132
132
 
@@ -157,7 +157,7 @@ def read_layout(lname, ldict, farm, ttypes, verbosity=1):
157
157
  cdict = Dict(ldict["coordinates"], name="coordinates")
158
158
  tmap = ldict.get_item("turbine_types", None)
159
159
  if verbosity > 2:
160
- print(f" Turbine type map:", tmap)
160
+ print(" Turbine type map:", tmap)
161
161
  for i, xy in enumerate(zip(cdict["x"], cdict["y"])):
162
162
  tt = ttypes[tmap[i] if tmap is not None else 0]
163
163
  farm.add_turbine(
@@ -211,7 +211,7 @@ def read_farm(wio_dict, mbook, verbosity):
211
211
  if isinstance(wfarm, dict):
212
212
  layouts = Dict(wfarm, name=wio_farm.name + ".layouts")
213
213
  else:
214
- layouts = {str(i): l for i, l in enumerate(wfarm)}
214
+ layouts = {str(i): lf for i, lf in enumerate(wfarm)}
215
215
  layouts = Dict(layouts, name=wio_farm.name + ".layouts")
216
216
  if verbosity > 2:
217
217
  print(" Reading layouts")
@@ -22,6 +22,8 @@ wio2foxes = {
22
22
  "LMO": FV.MOL,
23
23
  "z0": FV.Z0,
24
24
  "reference_height": FV.H,
25
+ "weibull_a": FV.WEIBULL_A,
26
+ "weibull_k": FV.WEIBULL_k,
25
27
  }
26
28
 
27
29
  """ Mapping from foxes to windio variables
@@ -134,8 +136,6 @@ def read_wind_resource_field(
134
136
 
135
137
  """
136
138
  if name in [
137
- "weibull_a",
138
- "weibull_k",
139
139
  "potential_temperature",
140
140
  "friction_velocity",
141
141
  "k",
@@ -178,6 +178,8 @@ def read_wind_resource_field(
178
178
  "LMO",
179
179
  "z0",
180
180
  "reference_height",
181
+ "weibull_a",
182
+ "weibull_k",
181
183
  ] and _read_multi_dimensional_data(name, wio_data, fields, dims):
182
184
  return True
183
185
 
@@ -159,6 +159,55 @@ def _get_MultiHeightNCTimeseries(
159
159
  return False
160
160
 
161
161
 
162
+ def _get_WeibullSectors(
163
+ coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
164
+ ):
165
+ """Try to generate Weibull sector parameters
166
+ :group: input.yaml.windio
167
+ """
168
+ if (
169
+ FV.WEIBULL_A in fields
170
+ and FV.WEIBULL_k in fields
171
+ and "sector_probability" in fields
172
+ and len(dims[FV.WEIBULL_A]) == 1
173
+ and len(dims[FV.WEIBULL_k]) == 1
174
+ and len(dims["sector_probability"]) == 1
175
+ ):
176
+ if verbosity > 2:
177
+ print(" selecting class 'WeibullSectors'")
178
+
179
+ data = {}
180
+ fix = {}
181
+ c = dims[FV.WEIBULL_A][0]
182
+ for v, d in fields.items():
183
+ if dims[v] == (c,):
184
+ data[v] = d
185
+ elif len(dims[v]) == 0:
186
+ fix[v] = d
187
+ elif verbosity > 2:
188
+ print(f" ignoring field '{v}' with dims {dims[v]}")
189
+ fix.update({v: d for v, d in fixval.items() if v not in data})
190
+
191
+ if FV.WD in coords:
192
+ data[FV.WD] = coords[FV.WD]
193
+
194
+ sdata = pd.DataFrame(index=range(len(fields[FV.WEIBULL_A])), data=data)
195
+ sdata.index.name = "sector"
196
+ states_dict.update(
197
+ dict(
198
+ states_type="WeibullSectors",
199
+ data_source=sdata,
200
+ ws_bins=coords.get(FV.WS, np.arange(30)),
201
+ output_vars=ovars,
202
+ var2ncvar={FV.WEIGHT: "sector_probability"},
203
+ fixed_vars=fix,
204
+ profiles=profiles,
205
+ )
206
+ )
207
+ return True
208
+ return False
209
+
210
+
162
211
  def get_states(coords, fields, dims, verbosity=1):
163
212
  """
164
213
  Reads states parameters from windio input
@@ -200,6 +249,9 @@ def get_states(coords, fields, dims, verbosity=1):
200
249
  or _get_MultiHeightNCTimeseries(
201
250
  coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
202
251
  )
252
+ or _get_WeibullSectors(
253
+ coords, fields, dims, states_dict, ovars, fixval, profiles, verbosity
254
+ )
203
255
  ):
204
256
  return States.new(**states_dict)
205
257
  else:
@@ -43,7 +43,7 @@ def read_windio(wio_dict, verbosity=1):
43
43
  if not isinstance(wio_dict, Dict):
44
44
  wio_dict = Dict(wio_dict, name="windio")
45
45
 
46
- _print(f"Reading windio data")
46
+ _print("Reading windio data")
47
47
  _print(" Name:", wio_dict.pop_item("name", None))
48
48
  _print(" Contents:", [k for k in wio_dict.keys()])
49
49
 
foxes/models/__init__.py CHANGED
@@ -2,18 +2,19 @@
2
2
  Model collection.
3
3
  """
4
4
 
5
- from . import turbine_types
6
- from . import rotor_models
7
- from . import turbine_models
8
- from . import farm_models
9
- from . import partial_wakes
10
- from . import wake_frames
11
- from . import wake_models
12
- from . import wake_superpositions
13
- from . import farm_controllers
14
- from . import vertical_profiles
15
- from . import point_models
16
- from . import axial_induction
17
- from . import ground_models
5
+ from . import turbine_types as turbine_types
6
+ from . import rotor_models as rotor_models
7
+ from . import turbine_models as turbine_models
8
+ from . import farm_models as farm_models
9
+ from . import partial_wakes as partial_wakes
10
+ from . import wake_frames as wake_frames
11
+ from . import wake_models as wake_models
12
+ from . import wake_deflections as wake_deflections
13
+ from . import wake_superpositions as wake_superpositions
14
+ from . import farm_controllers as farm_controllers
15
+ from . import vertical_profiles as vertical_profiles
16
+ from . import point_models as point_models
17
+ from . import axial_induction as axial_induction
18
+ from . import ground_models as ground_models
18
19
 
19
- from .model_book import ModelBook
20
+ from .model_book import ModelBook as ModelBook
@@ -1,2 +1,2 @@
1
- from .betz import BetzAxialInduction
2
- from .madsen import MadsenAxialInduction
1
+ from .betz import BetzAxialInduction as BetzAxialInduction
2
+ from .madsen import MadsenAxialInduction as MadsenAxialInduction
@@ -2,4 +2,4 @@
2
2
  Farm controller models.
3
3
  """
4
4
 
5
- from .basic import BasicFarmController
5
+ from .basic import BasicFarmController as BasicFarmController
@@ -2,4 +2,4 @@
2
2
  Farm models.
3
3
  """
4
4
 
5
- from .turbine2farm import Turbine2FarmModel
5
+ from .turbine2farm import Turbine2FarmModel as Turbine2FarmModel
@@ -1,2 +1,3 @@
1
- from .no_ground import NoGround
2
- from .wake_mirror import WakeMirror, GroundMirror
1
+ from .no_ground import NoGround as NoGround
2
+ from .wake_mirror import WakeMirror as WakeMirror
3
+ from .wake_mirror import GroundMirror as GroundMirror
@@ -75,13 +75,14 @@ class WakeMirror(GroundModel):
75
75
  # assert(np.all(fdata[FV.H]==fdata[FV.TXYH[..., 2]]))
76
76
 
77
77
  # contribution from main wake:
78
- wcoos = algo.wake_frame.get_wake_coos(algo, mdata, fdata, tdata, downwind_index)
78
+ wcoos = algo.wake_frame.get_wake_coos(
79
+ algo, mdata, fdata, tdata, downwind_index, wmodel
80
+ )
79
81
  wmodel.contribute(algo, mdata, fdata, tdata, downwind_index, wcoos, wake_deltas)
80
82
 
81
83
  # contribution from mirrors:
82
84
  tdata[FC.TARGETS] = tdata[FC.TARGETS].copy() # making sure this is no ref
83
85
  for h in self.heights:
84
-
85
86
  fdata[FV.TXYH][:, downwind_index, 2] = hh + 2 * (h - hh)
86
87
 
87
88
  pwake.contribute(
@@ -136,7 +137,6 @@ class WakeMirror(GroundModel):
136
137
  # contribution from mirrors:
137
138
  tdata[FC.TARGETS] = tdata[FC.TARGETS].copy() # making sure this is no ref
138
139
  for h in self.heights:
139
-
140
140
  fdata[FV.TXYH][:, downwind_index, 2] = hh + 2 * (h - hh)
141
141
 
142
142
  wcoos = algo.wake_frame.get_wake_coos(