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
@@ -101,9 +101,13 @@ class OnePointFlowStates(States):
101
101
  """
102
102
  return [self.base_states]
103
103
 
104
- def initialize(self, algo, verbosity=0):
104
+ def load_data(self, algo, verbosity=0):
105
105
  """
106
- Initializes the model.
106
+ Load and/or create all model data that is subject to chunking.
107
+
108
+ Such data should not be stored under self, for memory reasons. The
109
+ data returned here will automatically be chunked and then provided
110
+ as part of the mdata object during calculations.
107
111
 
108
112
  Parameters
109
113
  ----------
@@ -112,8 +116,14 @@ class OnePointFlowStates(States):
112
116
  verbosity: int
113
117
  The verbosity level, 0 = silent
114
118
 
119
+ Returns
120
+ -------
121
+ idata: dict
122
+ The dict has exactly two entries: `data_vars`,
123
+ a dict with entries `name_str -> (dim_tuple, data_ndarray)`;
124
+ and `coords`, a dict with entries `dim_name_str -> dim_array`
125
+
115
126
  """
116
- super().initialize(algo, verbosity)
117
127
 
118
128
  # find heights:
119
129
  if self.heights is None:
@@ -127,10 +137,14 @@ class OnePointFlowStates(States):
127
137
  )
128
138
 
129
139
  # pre-calc data:
130
- Timelines._precalc_data(
140
+ self.WEIGHT = self.var(FV.WEIGHT)
141
+ idata = super().load_data(algo, verbosity)
142
+ idata["data_vars"][self.WEIGHT] = Timelines._precalc_data(
131
143
  self, algo, self.base_states, self.heights, verbosity, needs_res=True
132
144
  )
133
145
 
146
+ return idata
147
+
134
148
  def size(self):
135
149
  """
136
150
  The total number of states.
@@ -172,23 +186,6 @@ class OnePointFlowStates(States):
172
186
  """
173
187
  return self.base_states.output_point_vars(algo)
174
188
 
175
- def weights(self, algo):
176
- """
177
- The statistical weights of all states.
178
-
179
- Parameters
180
- ----------
181
- algo: foxes.core.Algorithm
182
- The calculation algorithm
183
-
184
- Returns
185
- -------
186
- weights: numpy.ndarray
187
- The weights, shape: (n_states, n_turbines)
188
-
189
- """
190
- return self.base_states.weights(algo)
191
-
192
189
  def set_running(
193
190
  self,
194
191
  algo,
@@ -263,7 +260,6 @@ class OnePointFlowStates(States):
263
260
  self.timelines_data = data.pop("data")
264
261
 
265
262
  def calc_states_indices(self, algo, mdata, points, hi, ref_xy):
266
-
267
263
  n_states, n_points = points.shape[:2]
268
264
  dxy = self.timelines_data["dxy"].to_numpy()[hi]
269
265
 
@@ -304,7 +300,6 @@ class OnePointFlowStates(States):
304
300
  sel = np.isnan(coeffs)
305
301
  tshift = 0
306
302
  while np.any(sel):
307
-
308
303
  trs = trace_si[sel]
309
304
  hdxy = -dxy[trs]
310
305
  trace_p[sel] += hdxy
@@ -329,7 +324,6 @@ class OnePointFlowStates(States):
329
324
  sel &= trace_si < algo.n_states
330
325
  tshift = 1
331
326
  while np.any(sel):
332
-
333
327
  trs = trace_si[sel]
334
328
  hdxy = dxy[trs]
335
329
  trace_p[sel] += hdxy
@@ -423,7 +417,6 @@ class OnePointFlowStates(States):
423
417
 
424
418
  # interpolate to heights:
425
419
  if n_heights > 1:
426
-
427
420
  ar_states = np.arange(n_states)
428
421
  ar_points = np.arange(n_points)
429
422
 
@@ -488,6 +481,10 @@ class OnePointFlowStates(States):
488
481
  results = {FV.WD: uv2wd(uv), FV.WS: np.linalg.norm(uv, axis=-1)}
489
482
  del uv
490
483
 
484
+ # set weights:
485
+ tdata[FV.WEIGHT] = mdata[self.WEIGHT][:, None, None]
486
+ tdata.dims[FV.WEIGHT] = (FC.STATE, FC.TARGET, FC.TPOINT)
487
+
491
488
  return {
492
489
  v: d.reshape(n_states, n_targets, n_tpoints) for v, d in results.items()
493
490
  }
@@ -176,25 +176,6 @@ class ScanStates(States):
176
176
  """
177
177
  return self._vars
178
178
 
179
- def weights(self, algo):
180
- """
181
- The statistical weights of all states.
182
-
183
- Parameters
184
- ----------
185
- algo: foxes.core.Algorithm
186
- The calculation algorithm
187
-
188
- Returns
189
- -------
190
- weights: numpy.ndarray
191
- The weights, shape: (n_states, n_turbines)
192
-
193
- """
194
- return np.full(
195
- (self._N, algo.n_turbines), 1.0 / self._N, dtype=config.dtype_double
196
- )
197
-
198
179
  def calculate(self, algo, mdata, fdata, tdata):
199
180
  """
200
181
  The main model calculation.
@@ -226,4 +207,10 @@ class ScanStates(States):
226
207
  tdata[v] = np.zeros_like(tdata[FC.TARGETS][..., 0])
227
208
  tdata[v][:] = mdata[self.DATA][:, None, None, i]
228
209
 
210
+ # add weights:
211
+ tdata[FV.WEIGHT] = np.full(
212
+ (mdata.n_states, 1, 1), 1 / self._N, dtype=config.dtype_double
213
+ )
214
+ tdata.dims[FV.WEIGHT] = (FC.STATE, FC.TARGET, FC.TPOINT)
215
+
229
216
  return {v: tdata[v] for v in self.output_point_vars(algo)}
@@ -75,7 +75,7 @@ class SingleStateStates(States):
75
75
  and not len(profiles)
76
76
  ):
77
77
  raise KeyError(
78
- f"Expecting at least one parameter: ws, wd, ti, rho, profiles"
78
+ "Expecting at least one parameter: ws, wd, ti, rho, profiles"
79
79
  )
80
80
 
81
81
  def sub_models(self):
@@ -157,23 +157,6 @@ class SingleStateStates(States):
157
157
 
158
158
  return list(out)
159
159
 
160
- def weights(self, algo):
161
- """
162
- The statistical weights of all states.
163
-
164
- Parameters
165
- ----------
166
- algo: foxes.core.Algorithm
167
- The calculation algorithm
168
-
169
- Returns
170
- -------
171
- weights: numpy.ndarray
172
- The weights, shape: (n_states, n_turbines)
173
-
174
- """
175
- return np.ones((1, algo.n_turbines), dtype=config.dtype_double)
176
-
177
160
  def calculate(self, algo, mdata, fdata, tdata):
178
161
  """
179
162
  The main model calculation.
@@ -233,4 +216,9 @@ class SingleStateStates(States):
233
216
  pres = p.calculate(tdata, z)
234
217
  tdata[v] = pres
235
218
 
219
+ tdata[FV.WEIGHT] = np.full(
220
+ (mdata.n_states, 1, 1), 1.0, dtype=config.dtype_double
221
+ )
222
+ tdata.dims[FV.WEIGHT] = (FC.STATE, FC.TARGET, FC.TPOINT)
223
+
236
224
  return {v: tdata[v] for v in self.output_point_vars(algo)}
@@ -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
@@ -82,8 +82,8 @@ class StatesTable(States):
82
82
  """
83
83
  super().__init__()
84
84
 
85
- self.ovars = output_vars
86
- self.rpars = pd_read_pars
85
+ self.ovars = list(output_vars)
86
+ self.rpars = read_pars
87
87
  self.var2col = var2col
88
88
  self.fixed_vars = fixed_vars
89
89
  self.profdicts = profiles
@@ -98,9 +98,7 @@ class StatesTable(States):
98
98
  self._N = None
99
99
  self._tvars = None
100
100
  self._profiles = None
101
-
102
101
  self._data_source = data_source
103
- self.__weights = None
104
102
 
105
103
  @property
106
104
  def data_source(self):
@@ -155,7 +153,6 @@ class StatesTable(States):
155
153
 
156
154
  """
157
155
  self._profiles = {}
158
- self._tvars = set(self.ovars)
159
156
  for v, d in self.profdicts.items():
160
157
  if isinstance(d, str):
161
158
  self._profiles[v] = VerticalProfile.new(d)
@@ -168,9 +165,7 @@ class StatesTable(States):
168
165
  raise TypeError(
169
166
  f"States '{self.name}': Wrong profile type '{type(d).__name__}' for variable '{v}'. Expecting VerticalProfile, str or dict"
170
167
  )
171
- self._tvars.update(self._profiles[v].input_vars())
172
- self._tvars -= set(self.fixed_vars.keys())
173
- self._tvars = list(self._tvars)
168
+
174
169
  super().initialize(algo, verbosity)
175
170
 
176
171
  def sub_models(self):
@@ -210,10 +205,10 @@ class StatesTable(States):
210
205
  """
211
206
  self.VARS = self.var("vars")
212
207
  self.DATA = self.var("data")
208
+ self.WEIGHT = self.var(FV.WEIGHT)
213
209
 
214
210
  if isinstance(self.data_source, pd.DataFrame):
215
211
  data = self.data_source
216
- isorg = True
217
212
  else:
218
213
  self._data_source = get_input_path(self.data_source)
219
214
  if not self.data_source.is_file():
@@ -230,7 +225,6 @@ class StatesTable(States):
230
225
  print(f"States '{self.name}': Reading file {self.data_source}")
231
226
  rpars = dict(self.RDICT, **self.rpars)
232
227
  data = PandasFileHelper().read_file(self.data_source, **rpars)
233
- isorg = False
234
228
 
235
229
  if self.states_sel is not None:
236
230
  data = data.iloc[self.states_sel]
@@ -240,18 +234,19 @@ class StatesTable(States):
240
234
  self.__inds = data.index.to_numpy()
241
235
 
242
236
  col_w = self.var2col.get(FV.WEIGHT, FV.WEIGHT)
243
- self.__weights = np.zeros((self._N, algo.n_turbines), dtype=config.dtype_double)
237
+ weights = None
244
238
  if col_w in data:
245
- self.__weights[:] = data[col_w].to_numpy()[:, None]
239
+ weights = data[col_w].to_numpy()
246
240
  elif FV.WEIGHT in self.var2col:
247
241
  raise KeyError(
248
242
  f"Weight variable '{col_w}' defined in var2col, but not found in states table columns {data.columns}"
249
243
  )
250
- else:
251
- self.__weights[:] = 1.0 / self._N
252
- if isorg:
253
- data = data.copy()
254
- data[col_w] = self.__weights[:, 0]
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)
255
250
 
256
251
  tcols = []
257
252
  for v in self._tvars:
@@ -267,6 +262,8 @@ class StatesTable(States):
267
262
  idata = super().load_data(algo, verbosity)
268
263
  idata["coords"][self.VARS] = self._tvars
269
264
  idata["data_vars"][self.DATA] = ((FC.STATE, self.VARS), data.to_numpy())
265
+ if weights is not None:
266
+ idata["data_vars"][self.WEIGHT] = (FC.STATE, weights)
270
267
 
271
268
  return idata
272
269
 
@@ -313,27 +310,6 @@ class StatesTable(States):
313
310
  """
314
311
  return self.ovars
315
312
 
316
- def weights(self, algo):
317
- """
318
- The statistical weights of all states.
319
-
320
- Parameters
321
- ----------
322
- algo: foxes.core.Algorithm
323
- The calculation algorithm
324
-
325
- Returns
326
- -------
327
- weights: numpy.ndarray
328
- The weights, shape: (n_states, n_turbines)
329
-
330
- """
331
- if self.running:
332
- raise ValueError(
333
- f"States '{self.name}': Cannot access weights while running"
334
- )
335
- return self.__weights
336
-
337
313
  def set_running(
338
314
  self,
339
315
  algo,
@@ -368,10 +344,9 @@ class StatesTable(States):
368
344
 
369
345
  data_stash[self.name] = dict(
370
346
  data_source=self._data_source,
371
- weights=self.__weights,
372
347
  inds=self.__inds,
373
348
  )
374
- del self._data_source, self.__weights, self.__inds
349
+ del self._data_source, self.__inds
375
350
 
376
351
  def unset_running(
377
352
  self,
@@ -404,7 +379,6 @@ class StatesTable(States):
404
379
 
405
380
  data = data_stash[self.name]
406
381
  self._data_source = data.pop("data_source")
407
- self.__weights = data.pop("weights")
408
382
  self.__inds = data.pop("inds")
409
383
 
410
384
  def calculate(self, algo, mdata, fdata, tdata):
@@ -455,6 +429,14 @@ class StatesTable(States):
455
429
  for v, p in self._profiles.items():
456
430
  tdata[v] = p.calculate(tdata, z)
457
431
 
432
+ if self.WEIGHT in mdata:
433
+ tdata[FV.WEIGHT] = mdata[self.WEIGHT][:, None, None]
434
+ else:
435
+ tdata[FV.WEIGHT] = np.full(
436
+ (mdata.n_states, 1, 1), 1 / self._N, dtype=config.dtype_double
437
+ )
438
+ tdata.dims[FV.WEIGHT] = (FC.STATE, FC.TARGET, FC.TPOINT)
439
+
458
440
  return {v: tdata[v] for v in self.output_point_vars(algo)}
459
441
 
460
442
  def finalize(self, algo, verbosity=0):
@@ -469,7 +451,6 @@ class StatesTable(States):
469
451
  The verbosity level
470
452
 
471
453
  """
472
- self.__weights = None
473
454
  self._N = None
474
455
  self._tvars = None
475
456
 
@@ -0,0 +1,225 @@
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
+ RDICT: dict
33
+ Default xarray file reading parameters
34
+
35
+ :group: input.states
36
+
37
+ """
38
+
39
+ RDICT = {}
40
+
41
+ def __init__(
42
+ self,
43
+ data_source,
44
+ output_vars,
45
+ ws_bins=None,
46
+ var2ncvar={},
47
+ sel=None,
48
+ isel=None,
49
+ **kwargs,
50
+ ):
51
+ """
52
+ Constructor.
53
+
54
+ Parameters
55
+ ----------
56
+ data_source: str or xarray.Dataset or pandas.DataFrame
57
+ Either path to NetCDF or csv file or data
58
+ output_vars: list of str
59
+ The output variables
60
+ ws_bins: list of float, optional
61
+ The wind speed bins, including
62
+ lower and upper bounds
63
+ var2ncvar: dict
64
+ Mapping from variable names to variable names
65
+ in the nc file
66
+ sel: dict, optional
67
+ Subset selection via xr.Dataset.sel()
68
+ isel: dict, optional
69
+ Subset selection via xr.Dataset.isel()
70
+ kwargs: dict, optional
71
+ Additional arguments for the base class
72
+
73
+ """
74
+ super().__init__(data_source, output_vars, var2col={}, **kwargs)
75
+ self.ws_bins = None if ws_bins is None else np.asarray(ws_bins)
76
+ self.var2ncvar = var2ncvar
77
+ self.sel = sel if sel is not None else {}
78
+ self.isel = isel if isel is not None else {}
79
+
80
+ if FV.WS not in self.ovars:
81
+ raise ValueError(
82
+ f"States '{self.name}': Expecting output variable '{FV.WS}', got {self.ovars}"
83
+ )
84
+ for v in [FV.WEIBULL_A, FV.WEIBULL_k, FV.WEIGHT]:
85
+ if v in self.ovars:
86
+ raise ValueError(
87
+ f"States '{self.name}': Cannot have '{v}' as output variable"
88
+ )
89
+
90
+ self._original_data_source = None
91
+
92
+ def __repr__(self):
93
+ return f"{type(self).__name__}(ws_bins={self._n_ws})"
94
+
95
+ def load_data(self, algo, verbosity=0):
96
+ """
97
+ Load and/or create all model data that is subject to chunking.
98
+
99
+ Such data should not be stored under self, for memory reasons. The
100
+ data returned here will automatically be chunked and then provided
101
+ as part of the mdata object during calculations.
102
+
103
+ Parameters
104
+ ----------
105
+ algo: foxes.core.Algorithm
106
+ The calculation algorithm
107
+ verbosity: int
108
+ The verbosity level, 0 = silent
109
+
110
+ Returns
111
+ -------
112
+ idata: dict
113
+ The dict has exactly two entries: `data_vars`,
114
+ a dict with entries `name_str -> (dim_tuple, data_ndarray)`;
115
+ and `coords`, a dict with entries `dim_name_str -> dim_array`
116
+
117
+ """
118
+
119
+ if self._original_data_source is not None:
120
+ self._data_source = self._original_data_source
121
+ self._original_data_source = None
122
+
123
+ if isinstance(self.data_source, (str, PathLike)):
124
+ fpath = get_input_path(self.data_source)
125
+ if not fpath.is_file():
126
+ if verbosity > 0:
127
+ print(
128
+ f"States '{self.name}': Reading static data '{fpath}' from context '{STATES}'"
129
+ )
130
+ fpath = algo.dbook.get_file_path(STATES, fpath.name, check_raw=False)
131
+ if verbosity > 0:
132
+ print(f"Path: {fpath}")
133
+ elif verbosity:
134
+ print(f"States '{self.name}': Reading file {fpath}")
135
+ rpars = dict(self.RDICT, **self.rpars)
136
+ if fpath.suffix == ".nc":
137
+ data = open_dataset(fpath, engine=config.nc_engine, **rpars)
138
+ else:
139
+ data = PandasFileHelper().read_file(fpath, **rpars).to_xarray()
140
+ self._original_data_source = data
141
+
142
+ elif isinstance(self.data_source, Dataset):
143
+ data = self.data_source
144
+
145
+ elif isinstance(self.data_source, pd.DataFrame):
146
+ data = self.data_source.to_xarray()
147
+
148
+ if self.isel is not None and len(self.isel):
149
+ data = data.isel(**self.isel)
150
+ if self.sel is not None and len(self.sel):
151
+ data = data.sel(**self.sel)
152
+
153
+ wsn = self.var2ncvar.get(FV.WS, FV.WS)
154
+ if self.ws_bins is not None:
155
+ wsb = self.ws_bins
156
+ elif wsn in data:
157
+ wsb = data[wsn].to_numpy()
158
+ else:
159
+ raise ValueError(
160
+ f"States '{self.name}': Expecting ws_bins argument, since '{wsn}' not found in data"
161
+ )
162
+ wss = 0.5 * (wsb[:-1] + wsb[1:])
163
+ wsd = wsb[1:] - wsb[:-1]
164
+ n_ws = len(wss)
165
+ self._n_ws = n_ws
166
+ del wsb
167
+
168
+ secn = None
169
+ n_secs = None
170
+ if self._original_data_source is None:
171
+ self._original_data_source = self.data_source
172
+ self._data_source = {}
173
+ for v in [FV.WEIBULL_A, FV.WEIBULL_k, FV.WEIGHT] + self.ovars:
174
+ if v != FV.WS and v not in self.fixed_vars:
175
+ c = self.var2ncvar.get(v, v)
176
+ if c not in data:
177
+ raise KeyError(
178
+ f"States '{self.name}': Missing variable '{c}' in data, found {list(data.data_vars.keys())}"
179
+ )
180
+ d = data[c]
181
+ if len(d.dims) == 0:
182
+ self.fixed_vars[v] = float(d.to_numpy())
183
+ continue
184
+ elif len(d.dims) != 1:
185
+ raise ValueError(
186
+ f"States '{self.name}': Expecting single dimension for variable '{c}', got {d.dims}"
187
+ )
188
+ elif secn is None:
189
+ secn = d.dims[0]
190
+ n_secs = data.sizes[secn]
191
+ elif d.dims[0] != secn:
192
+ raise ValueError(
193
+ f"States '{self.name}': Expecting dimension '{secn}' for variable '{c}', got {d.dims}"
194
+ )
195
+ self._data_source[v] = np.zeros(
196
+ (n_secs, n_ws), dtype=config.dtype_double
197
+ )
198
+ self._data_source[v][:] = d.to_numpy()[:, None]
199
+ self._data_source[FV.WS] = np.zeros((n_secs, n_ws), dtype=config.dtype_double)
200
+ self._data_source[FV.WS][:] = wss[None, :]
201
+ del wss
202
+
203
+ self._data_source[FV.WEIGHT] *= weibull_weights(
204
+ ws=self._data_source[FV.WS],
205
+ ws_deltas=wsd[None, :],
206
+ A=self._data_source.pop(FV.WEIBULL_A),
207
+ k=self._data_source.pop(FV.WEIBULL_k),
208
+ )
209
+
210
+ # remove wd 360 from the end, if wd 0 is given:
211
+ if FV.WD in self._data_source:
212
+ if np.all(self._data_source[FV.WD][0] == 0.0) and np.all(
213
+ self._data_source[FV.WD][-1] == 360.0
214
+ ):
215
+ for v in self._data_source.keys():
216
+ self._data_source[v] = self._data_source[v][:-1]
217
+ n_secs -= 1
218
+
219
+ N = n_secs * n_ws
220
+ for v in self._data_source.keys():
221
+ self._data_source[v] = self._data_source[v].reshape(N)
222
+ self._data_source = pd.DataFrame(data=self._data_source, index=np.arange(N))
223
+ self._data_source.index.name = FC.STATE
224
+
225
+ return super().load_data(algo, verbosity)