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
@@ -51,7 +51,7 @@ class StatesTable(States):
51
51
  var2col={},
52
52
  fixed_vars={},
53
53
  profiles={},
54
- pd_read_pars={},
54
+ read_pars={},
55
55
  states_sel=None,
56
56
  states_loc=None,
57
57
  ):
@@ -72,7 +72,7 @@ class StatesTable(States):
72
72
  profiles: dict
73
73
  Key: output variable name str, Value: str or dict
74
74
  or `foxes.core.VerticalProfile`
75
- pd_read_pars: dict
75
+ read_pars: dict
76
76
  pandas file reading parameters
77
77
  states_sel: slice or range or list of int, optional
78
78
  States subset selection
@@ -83,7 +83,7 @@ class StatesTable(States):
83
83
  super().__init__()
84
84
 
85
85
  self.ovars = list(output_vars)
86
- self.rpars = pd_read_pars
86
+ self.rpars = read_pars
87
87
  self.var2col = var2col
88
88
  self.fixed_vars = fixed_vars
89
89
  self.profdicts = profiles
@@ -98,7 +98,7 @@ class StatesTable(States):
98
98
  self._N = None
99
99
  self._tvars = None
100
100
  self._profiles = None
101
- self._data_source = data_source
101
+ self._data = data_source
102
102
 
103
103
  @property
104
104
  def data_source(self):
@@ -115,7 +115,7 @@ class StatesTable(States):
115
115
  raise ValueError(
116
116
  f"States '{self.name}': Cannot access data_source while running"
117
117
  )
118
- return self._data_source
118
+ return self._data
119
119
 
120
120
  def reset(self, algo=None, states_sel=None, states_loc=None, verbosity=0):
121
121
  """
@@ -153,7 +153,6 @@ class StatesTable(States):
153
153
 
154
154
  """
155
155
  self._profiles = {}
156
- self._tvars = set(self.ovars)
157
156
  for v, d in self.profdicts.items():
158
157
  if isinstance(d, str):
159
158
  self._profiles[v] = VerticalProfile.new(d)
@@ -166,9 +165,7 @@ class StatesTable(States):
166
165
  raise TypeError(
167
166
  f"States '{self.name}': Wrong profile type '{type(d).__name__}' for variable '{v}'. Expecting VerticalProfile, str or dict"
168
167
  )
169
- self._tvars.update(self._profiles[v].input_vars())
170
- self._tvars -= set(self.fixed_vars.keys())
171
- self._tvars = list(self._tvars)
168
+
172
169
  super().initialize(algo, verbosity)
173
170
 
174
171
  def sub_models(self):
@@ -213,13 +210,13 @@ class StatesTable(States):
213
210
  if isinstance(self.data_source, pd.DataFrame):
214
211
  data = self.data_source
215
212
  else:
216
- self._data_source = get_input_path(self.data_source)
213
+ self._data = get_input_path(self.data_source)
217
214
  if not self.data_source.is_file():
218
215
  if verbosity > 0:
219
216
  print(
220
217
  f"States '{self.name}': Reading static data '{self.data_source}' from context '{STATES}'"
221
218
  )
222
- self._data_source = algo.dbook.get_file_path(
219
+ self._data = algo.dbook.get_file_path(
223
220
  STATES, self.data_source.name, check_raw=False
224
221
  )
225
222
  if verbosity > 0:
@@ -245,6 +242,12 @@ class StatesTable(States):
245
242
  f"Weight variable '{col_w}' defined in var2col, but not found in states table columns {data.columns}"
246
243
  )
247
244
 
245
+ self._tvars = set(self.ovars)
246
+ for v in self.profdicts.keys():
247
+ self._tvars.update(self._profiles[v].input_vars())
248
+ self._tvars -= set(self.fixed_vars.keys())
249
+ self._tvars = list(self._tvars)
250
+
248
251
  tcols = []
249
252
  for v in self._tvars:
250
253
  c = self.var2col.get(v, v)
@@ -340,10 +343,10 @@ class StatesTable(States):
340
343
  super().set_running(algo, data_stash, sel, isel, verbosity)
341
344
 
342
345
  data_stash[self.name] = dict(
343
- data_source=self._data_source,
346
+ data_source=self._data,
344
347
  inds=self.__inds,
345
348
  )
346
- del self._data_source, self.__inds
349
+ del self._data, self.__inds
347
350
 
348
351
  def unset_running(
349
352
  self,
@@ -375,7 +378,7 @@ class StatesTable(States):
375
378
  super().unset_running(algo, data_stash, sel, isel, verbosity)
376
379
 
377
380
  data = data_stash[self.name]
378
- self._data_source = data.pop("data_source")
381
+ self._data = data.pop("data_source")
379
382
  self.__inds = data.pop("inds")
380
383
 
381
384
  def calculate(self, algo, mdata, fdata, tdata):
@@ -404,23 +407,13 @@ class StatesTable(States):
404
407
  (n_states, n_targets, n_tpoints)
405
408
 
406
409
  """
410
+ self.ensure_output_vars(algo, tdata)
411
+
407
412
  for i, v in enumerate(self._tvars):
408
- if v in tdata:
409
- tdata[v][:] = mdata[self.DATA][:, i, None, None]
410
- else:
411
- tdata[v] = np.zeros(
412
- (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
413
- dtype=config.dtype_double,
414
- )
415
- tdata[v][:] = mdata[self.DATA][:, i, None, None]
416
- tdata.dims[v] = (FC.STATE, FC.TARGET, FC.TPOINT)
413
+ tdata[v][:] = mdata[self.DATA][:, i, None, None]
417
414
 
418
415
  for v, f in self.fixed_vars.items():
419
- tdata[v] = np.full(
420
- (tdata.n_states, tdata.n_targets, tdata.n_tpoints),
421
- f,
422
- dtype=config.dtype_double,
423
- )
416
+ tdata[v][:] = f
424
417
 
425
418
  z = tdata[FC.TARGETS][..., 2]
426
419
  for v, p in self._profiles.items():
@@ -569,7 +562,7 @@ class TabStates(StatesTable):
569
562
 
570
563
  sel = weights > 0
571
564
 
572
- self._data_source = pd.DataFrame(
565
+ self._data = pd.DataFrame(
573
566
  index=np.arange(np.sum(sel)),
574
567
  data={
575
568
  FV.WS: ws[sel],
@@ -577,7 +570,7 @@ class TabStates(StatesTable):
577
570
  FV.WEIGHT: weights[sel],
578
571
  },
579
572
  )
580
- self._data_source.index.name = FC.STATE
573
+ self._data.index.name = FC.STATE
581
574
 
582
575
  return super().load_data(algo, verbosity)
583
576
 
@@ -0,0 +1,330 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ from os import PathLike
4
+ from xarray import Dataset, open_dataset
5
+
6
+ from foxes.data import STATES
7
+ from foxes.utils import PandasFileHelper, weibull_weights
8
+ from foxes.config import config, get_input_path
9
+ import foxes.variables as FV
10
+ import foxes.constants as FC
11
+
12
+ from .states_table import StatesTable
13
+
14
+
15
+ class WeibullSectors(StatesTable):
16
+ """
17
+ States with wind speed from Weibull parameters
18
+ from a NetCDF file
19
+
20
+ Attributes
21
+ ----------
22
+ ws_bins: numpy.ndarray
23
+ The wind speed bins, including
24
+ lower and upper bounds, shape: (n_ws_bins+1,)
25
+ var2ncvar: dict
26
+ Mapping from variable names to variable names
27
+ in the nc file
28
+ sel: dict
29
+ Subset selection via xr.Dataset.sel()
30
+ isel: dict
31
+ Subset selection via xr.Dataset.isel()
32
+ rpars: dict
33
+ Additional parameters for reading the file
34
+ RDICT: dict
35
+ Default xarray file reading parameters
36
+
37
+ :group: input.states
38
+
39
+ """
40
+
41
+ RDICT = {}
42
+
43
+ def __init__(
44
+ self,
45
+ data_source,
46
+ output_vars,
47
+ ws_bins=None,
48
+ var2ncvar={},
49
+ sel=None,
50
+ isel=None,
51
+ read_pars={},
52
+ **kwargs,
53
+ ):
54
+ """
55
+ Constructor.
56
+
57
+ Parameters
58
+ ----------
59
+ data_source: str or xarray.Dataset or pandas.DataFrame
60
+ Either path to NetCDF or csv file or data
61
+ output_vars: list of str
62
+ The output variables
63
+ ws_bins: list of float, optional
64
+ The wind speed bins, including
65
+ lower and upper bounds
66
+ var2ncvar: dict
67
+ Mapping from variable names to variable names
68
+ in the nc file
69
+ sel: dict, optional
70
+ Subset selection via xr.Dataset.sel()
71
+ isel: dict, optional
72
+ Subset selection via xr.Dataset.isel()
73
+ read_pars: dict
74
+ Additional parameters for reading the file
75
+ kwargs: dict, optional
76
+ Additional arguments for the base class
77
+
78
+ """
79
+ super().__init__(data_source, output_vars, var2col={}, **kwargs)
80
+ self.ws_bins = None if ws_bins is None else np.asarray(ws_bins)
81
+ self.var2ncvar = var2ncvar
82
+ self.sel = sel if sel is not None else {}
83
+ self.isel = isel if isel is not None else {}
84
+ self.rpars = read_pars
85
+
86
+ if FV.WS not in self.ovars:
87
+ raise ValueError(
88
+ f"States '{self.name}': Expecting output variable '{FV.WS}', got {self.ovars}"
89
+ )
90
+ for v in [FV.WEIBULL_A, FV.WEIBULL_k, FV.WEIGHT]:
91
+ if v in self.ovars:
92
+ raise ValueError(
93
+ f"States '{self.name}': Cannot have '{v}' as output variable"
94
+ )
95
+
96
+ self._original_data = None
97
+
98
+ def __repr__(self):
99
+ return f"{type(self).__name__}(n_wd={self._n_wd}, n_ws={self._n_ws})"
100
+
101
+ def _read_data(self, algo, point_coord=None, verbosity=0):
102
+ """
103
+ Extracts data from file or Dataset.
104
+
105
+ Parameters
106
+ ----------
107
+ algo: foxes.core.Algorithm
108
+ The calculation algorithm
109
+ point_coord: str, optional
110
+ The coordinate name representing the point index
111
+ verbosity: int
112
+ The verbosity level, 0 = silent
113
+
114
+ Returns
115
+ -------
116
+ data: xarray.Dataset
117
+ The input data
118
+
119
+ """
120
+ # store original data
121
+ if self._original_data is not None:
122
+ self._data = self._original_data
123
+ self._original_data = None
124
+
125
+ # read file or grab data
126
+ cwd = self.var2ncvar.get(FV.WD, FV.WD)
127
+ if isinstance(self.data_source, (str, PathLike)):
128
+ fpath = get_input_path(self.data_source)
129
+ if not fpath.is_file():
130
+ if verbosity > 0:
131
+ print(
132
+ f"States '{self.name}': Reading static data '{fpath}' from context '{STATES}'"
133
+ )
134
+ fpath = algo.dbook.get_file_path(STATES, fpath.name, check_raw=False)
135
+ if verbosity > 0:
136
+ print(f"Path: {fpath}")
137
+ elif verbosity > 0:
138
+ print(f"States '{self.name}': Reading file {fpath}")
139
+ rpars = dict(self.RDICT, **self.rpars)
140
+ if fpath.suffix == ".nc":
141
+ data = open_dataset(fpath, engine=config.nc_engine, **rpars)
142
+ else:
143
+ data = PandasFileHelper().read_file(fpath, **rpars)
144
+ data.index.name = cwd
145
+ data = data.to_xarray()
146
+ self._original_data = data
147
+
148
+ elif isinstance(self.data_source, Dataset):
149
+ data = self.data_source
150
+
151
+ elif isinstance(self.data_source, pd.DataFrame):
152
+ data = self.data_source
153
+ data.index.name = cwd
154
+ data = data.to_xarray()
155
+
156
+ # optionally select a subset
157
+ if self.isel is not None and len(self.isel):
158
+ data = data.isel(**self.isel)
159
+ if self.sel is not None and len(self.sel):
160
+ data = data.sel(**self.sel)
161
+
162
+ # remove wd 360 from the end, if wd 0 is given:
163
+ wd = data[cwd].to_numpy()
164
+ if wd[0] == 0.0 and wd[-1] == 360.0:
165
+ data = data.isel({cwd: np.s_[:-1]})
166
+
167
+ # construct wind speed bins and bin deltas
168
+ cws = self.var2ncvar.get(FV.WS, FV.WS)
169
+ if self.ws_bins is not None:
170
+ wsb = self.ws_bins
171
+ wss = 0.5 * (wsb[:-1] + wsb[1:])
172
+ elif cws in data:
173
+ wss = data[cws].to_numpy()
174
+ wsb = np.zeros((len(wss) + 1,), dtype=config.dtype_double)
175
+ wsb[1:-1] = 0.5 * (wss[1:] + wss[:-1])
176
+ wsb[0] = wss[0] - 0.5 * wsb[1]
177
+ wsb[-1] = wss[-1] + 0.5 * wsb[-2]
178
+ self.ws_bins = wsb
179
+ else:
180
+ raise ValueError(
181
+ f"States '{self.name}': Expecting ws_bins argument, since '{cws}' not found in data"
182
+ )
183
+ wsd = wsb[1:] - wsb[:-1]
184
+ n_ws = len(wss)
185
+ del wsb
186
+
187
+ # prepare data binning
188
+ if self._original_data is None:
189
+ self._original_data = self.data_source
190
+ cpt = (
191
+ self.var2ncvar.get(FC.POINT, FC.POINT) if point_coord is not None else None
192
+ )
193
+ n_wd = data.sizes[cwd]
194
+ self.BIN_WD = self.var("bin_wd")
195
+ self.BIN_WS = self.var("bin_ws")
196
+ self.POINT = self.var("point")
197
+ if cpt is None:
198
+ n_pt = 0
199
+ shp = (n_wd, n_ws)
200
+ dms = (self.BIN_WD, self.BIN_WS)
201
+ else:
202
+ n_pt = data.sizes[cpt]
203
+ shp = (n_wd, n_ws, n_pt)
204
+ dms = (self.BIN_WD, self.BIN_WS, self.POINT)
205
+
206
+ # create binned data
207
+ self._data = {
208
+ FV.WD: np.zeros(shp, dtype=config.dtype_double),
209
+ FV.WS: np.zeros(shp, dtype=config.dtype_double),
210
+ }
211
+ if cpt is None:
212
+ self._data[FV.WD][:] = data[cwd].to_numpy()[:, None]
213
+ self._data[FV.WS][:] = wss[None, :]
214
+ else:
215
+ self._data[FV.WD][:] = data[cwd].to_numpy()[:, None, None]
216
+ self._data[FV.WS][:] = wss[None, :, None]
217
+ for v in [FV.WEIBULL_A, FV.WEIBULL_k, FV.WEIGHT] + self.ovars:
218
+ if v not in [FV.WS, FV.WD] and v not in self.fixed_vars:
219
+ w = self.var2ncvar.get(v, v)
220
+ if w not in data:
221
+ raise KeyError(
222
+ f"States '{self.name}': Missing variable '{w}' in data, found {list(data.data_vars.keys())}"
223
+ )
224
+ d = data[w]
225
+ iws = d.dims.index(cws) if cws in d.dims else -1
226
+ iwd = d.dims.index(cwd) if cwd in d.dims else -1
227
+ ipt = d.dims.index(cpt) if cpt is not None and cpt in d.dims else -1
228
+ d = d.to_numpy()
229
+ if v in [FV.WEIBULL_A, FV.WEIBULL_k, FV.WEIGHT]:
230
+ if cws in data[w].dims:
231
+ raise ValueError(
232
+ f"States '{self.name}': Cannot have '{cws}' as dimension in variable '{v}', got {data[w].dims}"
233
+ )
234
+ if cwd not in data[w].dims:
235
+ raise ValueError(
236
+ f"States '{self.name}': Expecting '{cwd}' as dimension in variable '{v}', got {data[w].dims}"
237
+ )
238
+ if iws < 0 and iwd < 0 and ipt < 0:
239
+ self.fixed_vars[v] = d.to_numpy()
240
+ elif iws >= 0 and iwd >= 0 and ipt >= 0:
241
+ self._data[v] = np.moveaxis(d, [iwd, iws, ipt], [0, 1, 2])
242
+ elif iws >= 0 and iwd >= 0 and ipt < 0:
243
+ if cpt is None:
244
+ self._data[v] = np.moveaxis(d, [iwd, iws], [0, 1])
245
+ else:
246
+ self._data[v] = np.zeros(shp, dtype=config.dtype_double)
247
+ self._data[v][:] = np.moveaxis(d, [iwd, iws], [0, 1])[
248
+ :, :, None
249
+ ]
250
+ elif iws >= 0 and iwd < 0 and ipt >= 0:
251
+ self._data[v] = np.zeros(shp, dtype=config.dtype_double)
252
+ self._data[v][:] = np.moveaxis(d, [iws, ipt], [0, 1])[None, :, :]
253
+ elif iws < 0 and iwd >= 0 and ipt >= 0:
254
+ self._data[v] = np.zeros(shp, dtype=config.dtype_double)
255
+ self._data[v][:] = np.moveaxis(d, [iwd, ipt], [0, 1])[:, None, :]
256
+ elif iws >= 0 and iwd < 0 and ipt < 0:
257
+ self._data[v] = np.zeros(shp, dtype=config.dtype_double)
258
+ if cpt is None:
259
+ self._data[v][:] = d[None, :]
260
+ else:
261
+ self._data[v][:] = d[None, :, None]
262
+ elif iws < 0 and iwd >= 0 and ipt < 0:
263
+ self._data[v] = np.zeros(shp, dtype=config.dtype_double)
264
+ if cpt is None:
265
+ self._data[v][:] = d[:, None]
266
+ else:
267
+ self._data[v][:] = d[:, None, None]
268
+ elif iws < 0 and iwd < 0 and ipt >= 0:
269
+ self._data[v] = np.zeros(shp, dtype=config.dtype_double)
270
+ self._data[v][:] = d[None, None, :]
271
+
272
+ # compute Weibull weights
273
+ self._data[FV.WEIGHT] *= weibull_weights(
274
+ ws=wss[None, :, None] if cpt is not None else wss[None, :],
275
+ ws_deltas=wsd[None, :, None] if cpt is not None else wsd[None, :],
276
+ A=self._data.pop(FV.WEIBULL_A),
277
+ k=self._data.pop(FV.WEIBULL_k),
278
+ )
279
+
280
+ # translate binned data to states
281
+ self._n_pt = n_pt
282
+ self._n_wd = n_wd
283
+ self._n_ws = n_ws
284
+ self._N = n_wd * n_ws
285
+ self._data = Dataset(
286
+ data_vars={v: (dms, d) for v, d in self._data.items()},
287
+ )
288
+
289
+ return data
290
+
291
+ def load_data(self, algo, verbosity=0):
292
+ """
293
+ Load and/or create all model data that is subject to chunking.
294
+
295
+ Such data should not be stored under self, for memory reasons. The
296
+ data returned here will automatically be chunked and then provided
297
+ as part of the mdata object during calculations.
298
+
299
+ Parameters
300
+ ----------
301
+ algo: foxes.core.Algorithm
302
+ The calculation algorithm
303
+ verbosity: int
304
+ The verbosity level, 0 = silent
305
+
306
+ Returns
307
+ -------
308
+ idata: dict
309
+ The dict has exactly two entries: `data_vars`,
310
+ a dict with entries `name_str -> (dim_tuple, data_ndarray)`;
311
+ and `coords`, a dict with entries `dim_name_str -> dim_array`
312
+
313
+ """
314
+ self._read_data(algo, verbosity=0)
315
+
316
+ tmp = {}
317
+ for v, d in self._data.data_vars.items():
318
+ if self.POINT in d.dims:
319
+ raise TypeError(
320
+ f"States '{self.name}': Variable '{v}' has unsupported dimension '{self.POINT}', dims = {d.dims}"
321
+ )
322
+ else:
323
+ tmp[v] = d.to_numpy().reshape(self._N)
324
+ self._data = tmp
325
+ del tmp
326
+
327
+ self._data = pd.DataFrame(data=self._data, index=np.arange(self._N))
328
+ self._data.index.name = FC.STATE
329
+
330
+ return super().load_data(algo, verbosity)
@@ -3,7 +3,7 @@ from scipy.interpolate import interpn
3
3
 
4
4
  from foxes.core.states import States
5
5
  from foxes.config import config, get_input_path
6
- from foxes.utils.wrg_utils import ReaderWRG
6
+ from foxes.utils import ReaderWRG, weibull_weights
7
7
  from foxes.data import STATES
8
8
  import foxes.variables as FV
9
9
  import foxes.constants as FC
@@ -132,7 +132,7 @@ class WRGStates(States):
132
132
  self._y = p0[1] + np.arange(ny) * res
133
133
  self._y = self._y[sy]
134
134
  if len(self._x) < 2 or len(self._y) < 2:
135
- raise ValueError(f"No overlap between data bounds and farm bounds")
135
+ raise ValueError("No overlap between data bounds and farm bounds")
136
136
  p0[0] = np.min(self._x)
137
137
  p0[1] = np.min(self._y)
138
138
  p1[0] = np.max(self._x)
@@ -229,8 +229,8 @@ class WRGStates(States):
229
229
  (n_states, n_targets, n_tpoints)
230
230
 
231
231
  """
232
-
233
232
  # prepare:
233
+ self.ensure_output_vars(algo, tdata)
234
234
  n_states = tdata.n_states
235
235
  n_targets = tdata.n_targets
236
236
  n_tpoints = tdata.n_tpoints
@@ -293,9 +293,11 @@ class WRGStates(States):
293
293
  dims=(FC.STATE, FC.TARGET, FC.TPOINT),
294
294
  )
295
295
 
296
- wsA = out[FV.WS] / A
297
- tdata[FV.WEIGHT] *= wsd[:, None, None] * (
298
- k / A * wsA ** (k - 1) * np.exp(-(wsA**k))
296
+ tdata[FV.WEIGHT] *= weibull_weights(
297
+ ws=out[FV.WS],
298
+ ws_deltas=wsd[:, None, None],
299
+ A=A,
300
+ k=k,
299
301
  )
300
302
 
301
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