foxes 0.7.1__py3-none-any.whl → 0.7.3__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 (66) hide show
  1. foxes/VERSION +1 -1
  2. foxes/algorithms/downwind/downwind.py +60 -46
  3. foxes/algorithms/downwind/models/farm_wakes_calc.py +17 -6
  4. foxes/algorithms/downwind/models/point_wakes_calc.py +13 -45
  5. foxes/algorithms/iterative/iterative.py +1 -1
  6. foxes/algorithms/iterative/models/farm_wakes_calc.py +18 -4
  7. foxes/constants.py +5 -0
  8. foxes/core/__init__.py +2 -1
  9. foxes/core/ground_model.py +254 -0
  10. foxes/core/model.py +3 -2
  11. foxes/core/partial_wakes_model.py +19 -3
  12. foxes/core/states.py +33 -0
  13. foxes/core/wake_model.py +138 -2
  14. foxes/data/__init__.py +1 -1
  15. foxes/data/states/WRF-Timeseries-3000.nc +0 -0
  16. foxes/data/states/windio_timeseries_5000.nc +0 -0
  17. foxes/data/static_data.py +7 -0
  18. foxes/data/windio/DTU_10MW_turbine.yaml +10 -0
  19. foxes/data/windio/__init__.py +0 -0
  20. foxes/data/windio/windio_5turbines_timeseries.yaml +63 -0
  21. foxes/input/states/__init__.py +1 -0
  22. foxes/input/states/multi_height.py +225 -6
  23. foxes/input/windio/__init__.py +6 -1
  24. foxes/input/windio/get_states.py +115 -0
  25. foxes/input/windio/read_attributes.py +321 -0
  26. foxes/input/windio/read_farm.py +163 -0
  27. foxes/input/windio/read_fields.py +164 -0
  28. foxes/input/windio/runner.py +105 -0
  29. foxes/input/windio/windio.py +136 -254
  30. foxes/models/__init__.py +2 -1
  31. foxes/models/ground_models/__init__.py +2 -0
  32. foxes/models/ground_models/no_ground.py +12 -0
  33. foxes/models/ground_models/wake_mirror.py +161 -0
  34. foxes/models/model_book.py +114 -164
  35. foxes/models/partial_wakes/axiwake.py +27 -4
  36. foxes/models/partial_wakes/top_hat.py +26 -4
  37. foxes/models/turbine_types/PCt_file.py +1 -0
  38. foxes/models/turbine_types/PCt_from_two.py +92 -0
  39. foxes/models/wake_frames/yawed_wakes.py +41 -38
  40. foxes/models/wake_models/__init__.py +0 -1
  41. foxes/models/wake_models/induction/__init__.py +1 -0
  42. foxes/models/wake_models/induction/rankine_half_body.py +1 -1
  43. foxes/models/wake_models/induction/vortex_sheet.py +227 -0
  44. foxes/models/wake_models/ti/crespo_hernandez.py +26 -24
  45. foxes/models/wake_models/ti/iec_ti.py +33 -26
  46. foxes/models/wake_models/wind/bastankhah14.py +11 -32
  47. foxes/models/wake_models/wind/bastankhah16.py +30 -34
  48. foxes/models/wake_models/wind/jensen.py +13 -29
  49. foxes/models/wake_models/wind/turbopark.py +31 -61
  50. foxes/models/wake_superpositions/ws_max.py +1 -0
  51. foxes/models/wake_superpositions/ws_pow.py +1 -0
  52. foxes/models/wake_superpositions/ws_quadratic.py +1 -0
  53. foxes/output/grids.py +6 -6
  54. foxes/output/output.py +6 -6
  55. foxes/utils/__init__.py +1 -1
  56. foxes/utils/factory.py +284 -76
  57. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/METADATA +8 -6
  58. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/RECORD +65 -51
  59. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/WHEEL +1 -1
  60. foxes/models/wake_models/wake_mirror.py +0 -196
  61. /foxes/models/{axial_induction_models → axial_induction}/__init__.py +0 -0
  62. /foxes/models/{axial_induction_models → axial_induction}/betz.py +0 -0
  63. /foxes/models/{axial_induction_models → axial_induction}/madsen.py +0 -0
  64. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/LICENSE +0 -0
  65. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/top_level.txt +0 -0
  66. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/zip-safe +0 -0
@@ -23,6 +23,25 @@ class PartialWakesModel(Model):
23
23
 
24
24
  """
25
25
 
26
+ def check_wmodel(self, wmodel, error=True):
27
+ """
28
+ Checks the wake model type
29
+
30
+ Parameters
31
+ ----------
32
+ wmodel: foxes.core.WakeModel
33
+ The wake model to be tested
34
+ error: bool
35
+ Flag for raising TypeError
36
+
37
+ Returns
38
+ -------
39
+ chk: bool
40
+ True if wake model is compatible
41
+
42
+ """
43
+ return True
44
+
26
45
  @abstractmethod
27
46
  def get_wake_points(self, algo, mdata, fdata):
28
47
  """
@@ -66,9 +85,6 @@ class PartialWakesModel(Model):
66
85
  The target point data
67
86
  wmodel: foxes.core.WakeModel
68
87
  The wake model
69
- wpoints: numpy.ndarray
70
- The wake evaluation points,
71
- shape: (n_states, n_turbines, n_tpoints, 3)
72
88
 
73
89
  Returns
74
90
  -------
foxes/core/states.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from abc import abstractmethod
2
2
 
3
3
  from .point_data_model import PointDataModel, PointDataModelList
4
+ from foxes.utils import all_subclasses
4
5
  import foxes.variables as FV
5
6
  import foxes.constants as FC
6
7
 
@@ -131,6 +132,38 @@ class States(PointDataModel):
131
132
  else:
132
133
  return ExtendedStates(self, [s])
133
134
 
135
+ @classmethod
136
+ def new(cls, states_type, *args, **kwargs):
137
+ """
138
+ Run-time states factory.
139
+
140
+ Parameters
141
+ ----------
142
+ states_type: str
143
+ The selected derived class name
144
+ args: tuple, optional
145
+ Additional parameters for constructor
146
+ kwargs: dict, optional
147
+ Additional parameters for constructor
148
+
149
+ """
150
+
151
+ if states_type is None:
152
+ return None
153
+
154
+ allc = all_subclasses(cls)
155
+ found = states_type in [scls.__name__ for scls in allc]
156
+
157
+ if found:
158
+ for scls in allc:
159
+ if scls.__name__ == states_type:
160
+ return scls(*args, **kwargs)
161
+ else:
162
+ estr = "States type '{}' is not defined, available types are \n {}".format(
163
+ states_type, sorted([i.__name__ for i in allc])
164
+ )
165
+ raise KeyError(estr)
166
+
134
167
 
135
168
  class ExtendedStates(States):
136
169
  """
foxes/core/wake_model.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from abc import abstractmethod
2
2
  import numpy as np
3
3
 
4
- from foxes.utils import all_subclasses
4
+ from foxes.utils import all_subclasses, Factory
5
5
  import foxes.variables as FV
6
6
  import foxes.constants as FC
7
7
 
@@ -49,7 +49,7 @@ class WakeModel(Model):
49
49
  -------
50
50
  wake_deltas: dict
51
51
  Key: variable name, value: The zero filled
52
- wake deltas, shape: (n_states, n_turbines, n_rpoints, ...)
52
+ wake deltas, shape: (n_states, n_targets, n_tpoints, ...)
53
53
 
54
54
  """
55
55
  return {FV.WS: np.zeros_like(tdata[FC.TARGETS][..., 0])}
@@ -183,3 +183,139 @@ class TurbineInductionModel(WakeModel):
183
183
 
184
184
  """
185
185
  return False
186
+
187
+
188
+ class WakeK(Model):
189
+ """
190
+ Handler for the wake growth parameter k
191
+
192
+ Attributes
193
+ ----------
194
+ k_var: str
195
+ The name of the k variable
196
+ ti_var: str
197
+ The name of the TI variable
198
+
199
+ :group: core
200
+
201
+ """
202
+
203
+ def __init__(
204
+ self,
205
+ k=None,
206
+ ka=None,
207
+ kb=None,
208
+ k_var=FV.K,
209
+ ti_var=FV.TI,
210
+ ):
211
+ """
212
+ Constructor.
213
+
214
+ Parameters
215
+ ----------
216
+ k: float, optional
217
+ The k value
218
+ ka: float, optional
219
+ The ka value in k = ka * TI + kb
220
+ kb: float, optional
221
+ The kb value in k = ka * TI + kb
222
+ k_var: str
223
+ The name of the k variable
224
+ ti_var: str
225
+ The name of the TI variable
226
+
227
+ """
228
+ super().__init__()
229
+ self._k = k
230
+ self._ka = ka
231
+ self._kb = kb
232
+ self.k_var = k_var
233
+ self.ti_var = ti_var
234
+
235
+ if k is not None and (ka is not None or kb is not None):
236
+ raise ValueError("Got 'k' and also ('ka' or 'kb') as non-None parameters")
237
+ elif k is None and kb is not None and (ka is None or ka == 0):
238
+ raise ValueError(f"Got k={k}, ka={ka}, kb={kb}, use k={kb} instead")
239
+
240
+ setattr(self, self.k_var, None)
241
+
242
+ def repr(self):
243
+ """
244
+ Provides the representative string
245
+
246
+ Returns
247
+ -------
248
+ s: str
249
+ The representative string
250
+
251
+ """
252
+ if self._k is not None:
253
+ s = f"{self.k_var}={self._k}"
254
+ elif self._ka is not None or self._kb is not None:
255
+ s = f"{self.k_var}={self._ka}*{self.ti_var}"
256
+ if self._kb is not None and self._kb > 0:
257
+ s += f"+{self._kb}"
258
+ else:
259
+ s = f"k_var={self.k_var}"
260
+ return s
261
+
262
+ @property
263
+ def all_none(self):
264
+ """Flag for k=ka=kb=None"""
265
+ return self._k is None and self._ka is None and self._kb is None
266
+
267
+ @property
268
+ def use_amb_ti(self):
269
+ """Flag for using ambient ti"""
270
+ return self.ti_var in FV.amb2var
271
+
272
+ def __call__(
273
+ self,
274
+ *args,
275
+ lookup_ti="w",
276
+ lookup_k="sw",
277
+ ti=None,
278
+ amb_ti=None,
279
+ **kwargs,
280
+ ):
281
+ """
282
+ Gets the k value
283
+
284
+ Parameters
285
+ ----------
286
+ args: tuple, optional
287
+ Arguments for get_data
288
+ lookup_ti: str
289
+ The ti lookup order for get_data
290
+ lookup_k: str
291
+ The k lookup order for get_data
292
+ ti: numpy.ndarray, optional
293
+ ti data in the requested target shape,
294
+ if known
295
+ amb_ti: numpy.ndarray, optional
296
+ Ambient ti data in the requested target shape,
297
+ if known
298
+ kwargs: dict, optional
299
+ Arguments for get_data
300
+
301
+ Returns
302
+ -------
303
+ k: numpy.ndarray
304
+ The k array as returned by get_data
305
+
306
+ """
307
+ if self._k is not None:
308
+ setattr(self, self.k_var, self._k)
309
+ elif self._ka is not None or self._kb is not None:
310
+ if self.ti_var == FV.TI and ti is not None:
311
+ pass
312
+ elif self.ti_var == FV.AMB_TI and amb_ti is not None:
313
+ ti = amb_ti
314
+ else:
315
+ ti = self.get_data(self.ti_var, *args, lookup=lookup_ti, **kwargs)
316
+ kb = 0 if self._kb is None else self._kb
317
+ setattr(self, self.k_var, self._ka * ti + kb)
318
+
319
+ k = self.get_data(self.k_var, *args, lookup=lookup_k, **kwargs)
320
+ setattr(self, self.k_var, None)
321
+ return k
foxes/data/__init__.py CHANGED
@@ -3,4 +3,4 @@ Static data collection.
3
3
  """
4
4
 
5
5
  from .parse import parse_Pct_file_name, parse_Pct_two_files
6
- from .static_data import FARM, STATES, PCTCURVE, StaticData
6
+ from .static_data import FARM, STATES, PCTCURVE, WINDIO, StaticData
Binary file
foxes/data/static_data.py CHANGED
@@ -3,6 +3,7 @@ from foxes.utils import DataBook
3
3
  from . import farms
4
4
  from . import states
5
5
  from . import power_ct_curves
6
+ from . import windio
6
7
 
7
8
  FARM = "farm"
8
9
  """ Static wind farm data identifier
@@ -19,6 +20,11 @@ PCTCURVE = "power_ct_curve"
19
20
  :group: data
20
21
  """
21
22
 
23
+ WINDIO = "windio"
24
+ """ Static windio data identifier
25
+ :group: data
26
+ """
27
+
22
28
 
23
29
  class StaticData(DataBook):
24
30
  """
@@ -35,3 +41,4 @@ class StaticData(DataBook):
35
41
  self.add_data_package(FARM, farms, ".csv")
36
42
  self.add_data_package(STATES, states, [".csv", ".csv.gz", ".nc", ".tab"])
37
43
  self.add_data_package(PCTCURVE, power_ct_curves, ".csv")
44
+ self.add_data_package(WINDIO, windio, ".yaml")
@@ -0,0 +1,10 @@
1
+ name: DTU 10MW Offshore Reference Turbine
2
+ performance:
3
+ power_curve:
4
+ power_values: [263388., 751154., 1440738., 2355734., 3506858., 4993092., 6849310., 9116402., 10000754., 10009590., 10000942., 10042678., 10003480., 10001600., 10001506., 10013632., 10007428., 10005360., 10002728., 10001130., 10004984., 9997558.]
5
+ power_wind_speeds: [4.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.,17.,18.,19.,20.,21.,22.,23.,24.,25.]
6
+ Ct_curve:
7
+ Ct_values: [0.923,0.919,0.904,0.858,0.814,0.814,0.814,0.814,0.577,0.419,0.323,0.259,0.211,0.175,0.148,0.126,0.109,0.095,0.084,0.074,0.066,0.059]
8
+ Ct_wind_speeds: [4.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.,17.,18.,19.,20.,21.,22.,23.,24.,25.]
9
+ hub_height: 119.0
10
+ rotor_diameter: 178.3
File without changes
@@ -0,0 +1,63 @@
1
+ name: 5 turbines and a wind timeseries
2
+
3
+ site:
4
+ name: Site with wind timeseries data
5
+ boundaries:
6
+ polygons: [
7
+ x: [-10, -10, 1610, 1610],
8
+ y: [-10, 10, 10, -10]
9
+ ]
10
+ energy_resource:
11
+ name: NetCDF timeseries with 5000 states
12
+ wind_resource: !include ../states/windio_timeseries_5000.nc
13
+
14
+ wind_farm:
15
+ name: One row with 5 turbines
16
+ layouts:
17
+ initial_layout:
18
+ coordinates:
19
+ x: [0, 0, 0, 0, 0]
20
+ y: [0, 600, 1150, 1730, 2400]
21
+ turbines: !include DTU_10MW_turbine.yaml
22
+
23
+ attributes:
24
+ flow_model:
25
+ name: foxes
26
+
27
+ analysis:
28
+
29
+ wind_deficit_model:
30
+ name: Bastankhah2014
31
+ wake_expansion_coefficient: # k = ka*ti + kb
32
+ k_a: 0.04
33
+ k_b: 0.0
34
+ free_stream_ti: false
35
+ ceps: 0.2
36
+ use_effective_ws: true
37
+
38
+ axial_induction_model: Madsen
39
+
40
+ deflection_model:
41
+ name: None
42
+
43
+ turbulence_model:
44
+ name: CrespoHernandez
45
+
46
+ superposition_model:
47
+ ws_superposition: Linear
48
+ ti_superposition: Quadratic
49
+
50
+ rotor_averaging:
51
+ grid: grid
52
+ n_x_grid_points: 4
53
+ n_y_grid_points: 4
54
+ background_averaging: grid
55
+ wake_averaging: axiwake9
56
+ wind_speed_exponent_for_power: 3
57
+ wind_speed_exponent_for_ct: 2
58
+
59
+ blockage_model:
60
+ name: None
61
+
62
+
63
+
@@ -7,5 +7,6 @@ from .scan_ws import ScanWS
7
7
  from .states_table import StatesTable, Timeseries, TabStates
8
8
  from .field_data_nc import FieldDataNC
9
9
  from .multi_height import MultiHeightStates, MultiHeightTimeseries
10
+ from .multi_height import MultiHeightNCStates, MultiHeightNCTimeseries
10
11
 
11
12
  from . import create
@@ -1,5 +1,6 @@
1
1
  import numpy as np
2
2
  import pandas as pd
3
+ from xarray import Dataset, open_dataset
3
4
  from pathlib import Path
4
5
  from scipy.interpolate import interp1d
5
6
 
@@ -100,7 +101,6 @@ class MultiHeightStates(States):
100
101
  self.states_sel = states_sel
101
102
  self.states_loc = states_loc
102
103
 
103
- self._cmap = None
104
104
  self._solo = None
105
105
  self._weights = None
106
106
  self._N = None
@@ -217,14 +217,14 @@ class MultiHeightStates(States):
217
217
  data[col_w] = self._weights[:, 0]
218
218
 
219
219
  cols = []
220
- self._cmap = {}
220
+ cmap = {}
221
221
  self._solo = {}
222
222
  for v in self.ovars:
223
223
  vcols = self._find_cols(v, data.columns)
224
224
  if len(vcols) == 1:
225
225
  self._solo[v] = data[vcols[0]].to_numpy()
226
226
  elif len(vcols) > 1:
227
- self._cmap[v] = (len(cols), len(cols) + len(vcols))
227
+ cmap[v] = (len(cols), len(cols) + len(vcols))
228
228
  cols += vcols
229
229
  data = data[cols]
230
230
 
@@ -235,7 +235,7 @@ class MultiHeightStates(States):
235
235
  idata = super().load_data(algo, verbosity)
236
236
 
237
237
  idata["coords"][self.H] = self.heights
238
- idata["coords"][self.VARS] = list(self._cmap.keys())
238
+ idata["coords"][self.VARS] = list(cmap.keys())
239
239
 
240
240
  n_hts = len(self.heights)
241
241
  n_vrs = int(len(data.columns) / n_hts)
@@ -247,6 +247,7 @@ class MultiHeightStates(States):
247
247
 
248
248
  for v, d in self._solo.items():
249
249
  idata["data_vars"][self.var(v)] = ((FC.STATE,), d)
250
+ self._solo = list(self._solo.keys())
250
251
 
251
252
  return idata
252
253
 
@@ -387,7 +388,7 @@ class MultiHeightStates(States):
387
388
  elif v in self.fixed_vars:
388
389
  results[v] = np.zeros((n_states, n_targets, n_tpoints), dtype=FC.DTYPE)
389
390
  results[v][:] = self.fixed_vars[v]
390
- elif v in self._solo.keys():
391
+ elif v in self._solo:
391
392
  results[v] = np.zeros((n_states, n_targets, n_tpoints), dtype=FC.DTYPE)
392
393
  results[v][:] = mdata[self.var(v)][:, None, None]
393
394
  else:
@@ -408,12 +409,200 @@ class MultiHeightStates(States):
408
409
 
409
410
  """
410
411
  super().finalize(algo, verbosity)
411
- self._cmap = None
412
412
  self._solo = None
413
413
  self._weights = None
414
414
  self._N = None
415
415
 
416
416
 
417
+ class MultiHeightNCStates(MultiHeightStates):
418
+ """
419
+ Multi-height states from xarray Dataset.
420
+
421
+ Attributes
422
+ ----------
423
+ data_source: str or xarray.Dataset
424
+ Either path to a file or data
425
+ state_coord: str
426
+ Name of the state coordinate
427
+ h_coord: str
428
+ Name of the height coordinate
429
+ xr_read_pars: dict
430
+ Parameters for xarray.open_dataset
431
+
432
+ :group: input.states
433
+
434
+ """
435
+
436
+ def __init__(
437
+ self,
438
+ data_source,
439
+ *args,
440
+ state_coord=FC.STATE,
441
+ h_coord=FV.H,
442
+ heights=None,
443
+ format_times_func="default",
444
+ xr_read_pars={},
445
+ **kwargs,
446
+ ):
447
+ """
448
+ Constructor.
449
+
450
+ Parameters
451
+ ----------
452
+ data_source: str or pandas.DataFrame
453
+ Either path to a file or data
454
+ args: tuple, optional
455
+ Parameters for the base class
456
+ state_coord: str
457
+ Name of the state coordinate
458
+ h_coord: str
459
+ Name of the height coordinate
460
+ output_vars: list of str
461
+ The output variables
462
+ heights: list of float, optional
463
+ The heights at which to search data
464
+ format_times_func: Function or 'default', optional
465
+ The function that maps state_coord values
466
+ to datetime dtype format
467
+ xr_read_pars: dict, optional
468
+ Parameters for xarray.open_dataset
469
+ kwargs: dict, optional
470
+ Parameters for the base class
471
+
472
+ """
473
+ super().__init__(
474
+ data_source,
475
+ *args,
476
+ heights=[],
477
+ pd_read_pars=None,
478
+ **kwargs,
479
+ )
480
+ self.state_coord = state_coord
481
+ self.heights = heights
482
+ self.h_coord = h_coord
483
+ self.xr_read_pars = xr_read_pars
484
+
485
+ if format_times_func == "default":
486
+ self.format_times_func = lambda t: t.astype("datetime64[ns]")
487
+ else:
488
+ self.format_times_func = format_times_func
489
+
490
+ def load_data(self, algo, verbosity=0):
491
+ """
492
+ Load and/or create all model data that is subject to chunking.
493
+
494
+ Such data should not be stored under self, for memory reasons. The
495
+ data returned here will automatically be chunked and then provided
496
+ as part of the mdata object during calculations.
497
+
498
+ Parameters
499
+ ----------
500
+ algo: foxes.core.Algorithm
501
+ The calculation algorithm
502
+ verbosity: int
503
+ The verbosity level, 0 = silent
504
+
505
+ Returns
506
+ -------
507
+ idata: dict
508
+ The dict has exactly two entries: `data_vars`,
509
+ a dict with entries `name_str -> (dim_tuple, data_ndarray)`;
510
+ and `coords`, a dict with entries `dim_name_str -> dim_array`
511
+
512
+ """
513
+ if not isinstance(self.data_source, Dataset):
514
+ if not Path(self.data_source).is_file():
515
+ if verbosity:
516
+ print(
517
+ f"States '{self.name}': Reading static data '{self.data_source}' from context '{STATES}'"
518
+ )
519
+ self.data_source = algo.dbook.get_file_path(
520
+ STATES, self.data_source, check_raw=False
521
+ )
522
+ if verbosity:
523
+ print(f"Path: {self.data_source}")
524
+ elif verbosity:
525
+ print(f"States '{self.name}': Reading file {self.data_source}")
526
+ data = open_dataset(self.data_source, **self.xr_read_pars)
527
+ else:
528
+ data = self.data_source
529
+
530
+ if self.states_sel is not None:
531
+ data = data.isel({self.state_coord: self.states_sel})
532
+ if self.states_loc is not None:
533
+ data = data.sel({self.state_coord: self.states_loc})
534
+
535
+ self._N = data.sizes[self.state_coord]
536
+ self._inds = data.coords[self.state_coord].to_numpy()
537
+ if self.format_times_func is not None:
538
+ self._inds = self.format_times_func(self._inds)
539
+
540
+ w_name = self.var2col.get(FV.WEIGHT, FV.WEIGHT)
541
+ self._weights = np.zeros((self._N, algo.n_turbines), dtype=FC.DTYPE)
542
+ if w_name in data.data_vars:
543
+ if data[w_name].dims != (self.state_coord,):
544
+ raise ValueError(
545
+ f"Weights data '{w_name}': Expecting dims ({self.state_coord},), got {data[w_name]}"
546
+ )
547
+ self._weights[:] = data.data_vars[w_name].to_numpy()[:, None]
548
+ elif FV.WEIGHT in self.var2col:
549
+ raise KeyError(
550
+ f"Weight variable '{w_name}' defined in var2col, but not found in data_vars {list(data.data_vars.keys())}"
551
+ )
552
+ else:
553
+ self._weights = np.zeros((self._N, algo.n_turbines), dtype=FC.DTYPE)
554
+ self._weights[:] = 1.0 / self._N
555
+
556
+ cols = {}
557
+ self._solo = {}
558
+ for v in self.ovars:
559
+ c = self.var2col.get(v, v)
560
+ if c in self.fixed_vars:
561
+ pass
562
+ elif c in data.attrs:
563
+ self._solo[v] = np.full(self._N, data.attrs)
564
+ elif c in data.data_vars:
565
+ if data[c].dims == (self.state_coord,):
566
+ self._solo[v] = data.data_vars[c].to_numpy()
567
+ elif data[c].dims == (self.state_coord, self.h_coord):
568
+ cols[v] = c
569
+ else:
570
+ raise ValueError(
571
+ f"Variable '{c}': Expecting dims {(self.state_coord, self.h_coord)}, got {data[c].dims}"
572
+ )
573
+ else:
574
+ raise KeyError(
575
+ f"Missing variable '{c}', found data_vars {sorted(list(data.data_vars.keys()))} and attrs {sorted(list(data.attrs.keys()))}"
576
+ )
577
+
578
+ if self.heights is not None:
579
+ data = data.sel({self.h_coord: self.heights})
580
+ else:
581
+ self.heights = data[self.h_coord].to_numpy()
582
+
583
+ self.H = self.var(FV.H)
584
+ self.VARS = self.var("vars")
585
+ self.DATA = self.var("data")
586
+
587
+ idata = States.load_data(self, algo, verbosity)
588
+ idata["coords"][self.H] = self.heights
589
+ idata["coords"][self.VARS] = list(cols.keys())
590
+
591
+ dims = (FC.STATE, self.VARS, self.H)
592
+ idata["data_vars"][self.DATA] = (
593
+ dims,
594
+ np.stack(
595
+ [data.data_vars[c].to_numpy() for c in cols.values()], axis=1
596
+ ).astype(FC.DTYPE),
597
+ )
598
+
599
+ for v, d in self._solo.items():
600
+ idata["data_vars"][self.var(v)] = ((FC.STATE,), d.astype(FC.DTYPE))
601
+ self._solo = list(self._solo.keys())
602
+
603
+ return idata
604
+
605
+
417
606
  class MultiHeightTimeseries(MultiHeightStates):
418
607
  """
419
608
  Multi-height timeseries states data.
@@ -423,3 +612,33 @@ class MultiHeightTimeseries(MultiHeightStates):
423
612
  """
424
613
 
425
614
  RDICT = {"index_col": 0, "parse_dates": [0]}
615
+
616
+
617
+ class MultiHeightNCTimeseries(MultiHeightNCStates):
618
+ """
619
+ Multi-height timeseries from xarray Dataset.
620
+
621
+ :group: input.states
622
+
623
+ """
624
+
625
+ def __init__(
626
+ self,
627
+ *args,
628
+ time_coord=FC.TIME,
629
+ **kwargs,
630
+ ):
631
+ """
632
+ Constructor.
633
+
634
+ Parameters
635
+ ----------
636
+ args: tuple, optional
637
+ Parameters for the base class
638
+ time_coord: str
639
+ Name of the state coordinate
640
+ kwargs: dict, optional
641
+ Parameters for the base class
642
+
643
+ """
644
+ super().__init__(*args, state_coord=time_coord, **kwargs)
@@ -2,4 +2,9 @@
2
2
  Functions for usingin windIO yaml files as input.
3
3
  """
4
4
 
5
- from .windio import read_case
5
+ from .windio import read_windio
6
+ from .read_fields import wio2foxes, foxes2wio
7
+ from .get_states import get_states
8
+ from .read_farm import read_turbine_type, read_layout
9
+ from .read_attributes import read_attributes
10
+ from .runner import WindioRunner