foxes 1.4__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 (85) hide show
  1. docs/source/conf.py +1 -1
  2. examples/field_data_nc/run.py +1 -1
  3. examples/streamline_wakes/run.py +2 -2
  4. examples/yawed_wake/run.py +3 -1
  5. foxes/algorithms/downwind/downwind.py +5 -5
  6. foxes/algorithms/downwind/models/init_farm_data.py +58 -28
  7. foxes/algorithms/downwind/models/set_amb_farm_results.py +1 -1
  8. foxes/core/algorithm.py +5 -5
  9. foxes/core/data.py +75 -4
  10. foxes/core/data_calc_model.py +4 -2
  11. foxes/core/engine.py +33 -40
  12. foxes/core/farm_data_model.py +16 -13
  13. foxes/core/model.py +19 -1
  14. foxes/core/point_data_model.py +19 -14
  15. foxes/core/rotor_model.py +1 -0
  16. foxes/core/wake_deflection.py +3 -3
  17. foxes/data/states/point_cloud_100.nc +0 -0
  18. foxes/data/states/weibull_cloud_4.nc +0 -0
  19. foxes/data/states/weibull_grid.nc +0 -0
  20. foxes/engines/dask.py +3 -6
  21. foxes/engines/default.py +2 -2
  22. foxes/engines/numpy.py +11 -10
  23. foxes/engines/pool.py +20 -11
  24. foxes/engines/single.py +8 -6
  25. foxes/input/farm_layout/__init__.py +1 -0
  26. foxes/input/farm_layout/from_arrays.py +68 -0
  27. foxes/input/states/__init__.py +7 -1
  28. foxes/input/states/dataset_states.py +710 -0
  29. foxes/input/states/field_data.py +531 -0
  30. foxes/input/states/multi_height.py +2 -0
  31. foxes/input/states/one_point_flow.py +1 -0
  32. foxes/input/states/point_cloud_data.py +618 -0
  33. foxes/input/states/scan.py +2 -0
  34. foxes/input/states/single.py +2 -0
  35. foxes/input/states/states_table.py +13 -23
  36. foxes/input/states/weibull_sectors.py +182 -77
  37. foxes/input/states/wrg_states.py +1 -1
  38. foxes/input/yaml/dict.py +25 -24
  39. foxes/input/yaml/windio/read_attributes.py +40 -27
  40. foxes/input/yaml/windio/read_farm.py +12 -10
  41. foxes/input/yaml/windio/read_outputs.py +25 -15
  42. foxes/input/yaml/windio/read_site.py +121 -12
  43. foxes/input/yaml/windio/windio.py +22 -10
  44. foxes/input/yaml/yaml.py +1 -0
  45. foxes/models/model_book.py +16 -15
  46. foxes/models/rotor_models/__init__.py +1 -0
  47. foxes/models/rotor_models/centre.py +1 -1
  48. foxes/models/rotor_models/direct_infusion.py +241 -0
  49. foxes/models/turbine_models/calculator.py +16 -3
  50. foxes/models/turbine_models/kTI_model.py +1 -0
  51. foxes/models/turbine_models/lookup_table.py +2 -0
  52. foxes/models/turbine_models/power_mask.py +1 -0
  53. foxes/models/turbine_models/rotor_centre_calc.py +2 -0
  54. foxes/models/turbine_models/sector_management.py +1 -0
  55. foxes/models/turbine_models/set_farm_vars.py +3 -8
  56. foxes/models/turbine_models/table_factors.py +2 -0
  57. foxes/models/turbine_models/thrust2ct.py +1 -0
  58. foxes/models/turbine_models/yaw2yawm.py +2 -0
  59. foxes/models/turbine_models/yawm2yaw.py +2 -0
  60. foxes/models/turbine_types/PCt_file.py +2 -4
  61. foxes/models/turbine_types/PCt_from_two.py +1 -0
  62. foxes/models/turbine_types/__init__.py +1 -0
  63. foxes/models/turbine_types/calculator_type.py +123 -0
  64. foxes/models/turbine_types/null_type.py +1 -0
  65. foxes/models/turbine_types/wsrho2PCt_from_two.py +2 -0
  66. foxes/models/turbine_types/wsti2PCt_from_two.py +3 -1
  67. foxes/output/farm_layout.py +2 -0
  68. foxes/output/farm_results_eval.py +4 -1
  69. foxes/output/flow_plots_2d/flow_plots.py +18 -0
  70. foxes/output/flow_plots_2d/get_fig.py +1 -0
  71. foxes/output/output.py +6 -1
  72. foxes/output/results_writer.py +1 -1
  73. foxes/output/rose_plot.py +10 -0
  74. foxes/output/rotor_point_plots.py +3 -0
  75. foxes/output/state_turbine_map.py +3 -0
  76. foxes/output/turbine_type_curves.py +3 -0
  77. foxes/utils/dict.py +46 -34
  78. foxes/utils/factory.py +2 -2
  79. {foxes-1.4.dist-info → foxes-1.5.dist-info}/METADATA +32 -52
  80. {foxes-1.4.dist-info → foxes-1.5.dist-info}/RECORD +84 -76
  81. foxes/input/states/field_data_nc.py +0 -833
  82. {foxes-1.4.dist-info → foxes-1.5.dist-info}/WHEEL +0 -0
  83. {foxes-1.4.dist-info → foxes-1.5.dist-info}/entry_points.txt +0 -0
  84. {foxes-1.4.dist-info → foxes-1.5.dist-info}/licenses/LICENSE +0 -0
  85. {foxes-1.4.dist-info → foxes-1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,531 @@
1
+ import numpy as np
2
+ from scipy.interpolate import interpn
3
+
4
+ from foxes.utils import wd2uv, uv2wd, weibull_weights
5
+ from foxes.config import config
6
+ import foxes.variables as FV
7
+ import foxes.constants as FC
8
+
9
+ from .dataset_states import DatasetStates
10
+
11
+
12
+ class FieldData(DatasetStates):
13
+ """
14
+ Heterogeneous ambient states on a regular
15
+ horizontal grid in NetCDF format.
16
+
17
+ Attributes
18
+ ----------
19
+ states_coord: str
20
+ The states coordinate name in the data
21
+ x_coord: str
22
+ The x coordinate name in the data
23
+ y_coord: str
24
+ The y coordinate name in the data
25
+ h_coord: str
26
+ The height coordinate name in the data
27
+ weight_ncvar: str
28
+ Name of the weight data variable in the nc file(s)
29
+ interpn_pars: dict, optional
30
+ Additional parameters for scipy.interpolate.interpn
31
+ bounds_extra_space: float or str
32
+ The extra space, either float in m,
33
+ or str for units of D, e.g. '2.5D'
34
+
35
+ :group: input.states
36
+
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ *args,
42
+ states_coord="Time",
43
+ x_coord="UTMX",
44
+ y_coord="UTMY",
45
+ h_coord="height",
46
+ weight_ncvar=None,
47
+ bounds_extra_space=1000,
48
+ interpn_pars={},
49
+ **kwargs,
50
+ ):
51
+ """
52
+ Constructor.
53
+
54
+ Parameters
55
+ ----------
56
+ args: tuple, optional
57
+ Arguments for the base class
58
+ states_coord: str
59
+ The states coordinate name in the data
60
+ x_coord: str
61
+ The x coordinate name in the data
62
+ y_coord: str
63
+ The y coordinate name in the data
64
+ h_coord: str, optional
65
+ The height coordinate name in the data
66
+ weight_ncvar: str, optional
67
+ Name of the weight data variable in the nc file(s)
68
+ bounds_extra_space: float or str, optional
69
+ The extra space, either float in m,
70
+ or str for units of D, e.g. '2.5D'
71
+ interpn_pars: dict
72
+ Parameters for scipy.interpolate.interpn
73
+ kwargs: dict, optional
74
+ Additional parameters for the base class
75
+
76
+ """
77
+ super().__init__(*args, **kwargs)
78
+
79
+ self.states_coord = states_coord
80
+ self.x_coord = x_coord
81
+ self.y_coord = y_coord
82
+ self.h_coord = h_coord
83
+ self.weight_ncvar = weight_ncvar
84
+ self.interpn_pars = interpn_pars
85
+ self.bounds_extra_space = bounds_extra_space
86
+
87
+ assert FV.WEIGHT not in self.ovars, (
88
+ f"States '{self.name}': Cannot have '{FV.WEIGHT}' as output variable, got {self.ovars}"
89
+ )
90
+ self.variables = [v for v in self.ovars if v not in self.fixed_vars]
91
+ if weight_ncvar is not None:
92
+ self.var2ncvar[FV.WEIGHT] = weight_ncvar
93
+ self.variables.append(FV.WEIGHT)
94
+ elif FV.WEIGHT in self.var2ncvar:
95
+ raise KeyError(
96
+ f"States '{self.name}': Cannot have '{FV.WEIGHT}' in var2ncvar, use weight_ncvar instead"
97
+ )
98
+
99
+ self._cmap = {
100
+ FC.STATE: self.states_coord,
101
+ FV.X: self.x_coord,
102
+ FV.Y: self.y_coord,
103
+ }
104
+ if self.h_coord is not None:
105
+ self._cmap[FV.H] = self.h_coord
106
+
107
+ def load_data(self, algo, verbosity=0):
108
+ """
109
+ Load and/or create all model data that is subject to chunking.
110
+
111
+ Such data should not be stored under self, for memory reasons. The
112
+ data returned here will automatically be chunked and then provided
113
+ as part of the mdata object during calculations.
114
+
115
+ Parameters
116
+ ----------
117
+ algo: foxes.core.Algorithm
118
+ The calculation algorithm
119
+ verbosity: int
120
+ The verbosity level, 0 = silent
121
+
122
+ Returns
123
+ -------
124
+ idata: dict
125
+ The dict has exactly two entries: `data_vars`,
126
+ a dict with entries `name_str -> (dim_tuple, data_ndarray)`;
127
+ and `coords`, a dict with entries `dim_name_str -> dim_array`
128
+
129
+ """
130
+ return super().load_data(
131
+ algo,
132
+ cmap=self._cmap,
133
+ variables=self.variables,
134
+ bounds_extra_space=self.bounds_extra_space,
135
+ verbosity=verbosity,
136
+ )
137
+
138
+ def calculate(self, algo, mdata, fdata, tdata):
139
+ """
140
+ The main model calculation.
141
+
142
+ This function is executed on a single chunk of data,
143
+ all computations should be based on numpy arrays.
144
+
145
+ Parameters
146
+ ----------
147
+ algo: foxes.core.Algorithm
148
+ The calculation algorithm
149
+ mdata: foxes.core.MData
150
+ The model data
151
+ fdata: foxes.core.FData
152
+ The farm data
153
+ tdata: foxes.core.TData
154
+ The target point data
155
+
156
+ Returns
157
+ -------
158
+ results: dict
159
+ The resulting data, keys: output variable str.
160
+ Values: numpy.ndarray with shape
161
+ (n_states, n_targets, n_tpoints)
162
+
163
+ """
164
+ # prepare
165
+ self.ensure_output_vars(algo, tdata)
166
+ n_states = tdata.n_states
167
+ n_targets = tdata.n_targets
168
+ n_tpoints = tdata.n_tpoints
169
+ points = tdata[FC.TARGETS].reshape(n_states, n_targets * n_tpoints, 3)
170
+ n_pts = points.shape[1]
171
+
172
+ # get data for calculation
173
+ coords, data, weights = self.get_calc_data(mdata, self._cmap, self.variables)
174
+ coords[FC.STATE] = np.arange(n_states, dtype=config.dtype_int)
175
+
176
+ # interpolate data to points:
177
+ out = {}
178
+ for dims, (vrs, d) in data.items():
179
+ # translate (WD, WS) to (U, V):
180
+ if FV.WD in vrs or FV.WS in vrs:
181
+ assert FV.WD in vrs and (FV.WS in vrs or FV.WS in self.fixed_vars), (
182
+ f"States '{self.name}': Missing '{FV.WD}' or '{FV.WS}' in data variables {vrs} for dimensions {dims}"
183
+ )
184
+ iwd = vrs.index(FV.WD)
185
+ iws = vrs.index(FV.WS)
186
+ ws = d[..., iws] if FV.WS in vrs else self.fixed_vars[FV.WS]
187
+ d[..., [iwd, iws]] = wd2uv(d[..., iwd], ws, axis=-1)
188
+ del ws
189
+
190
+ # prepare grid:
191
+ idims = dims[:-1]
192
+ gvars = tuple([coords[c] for c in idims])
193
+
194
+ # prepare points:
195
+ n_vrs = len(vrs)
196
+ tdims = [n_states, n_pts, n_vrs]
197
+ if idims == (FC.STATE, FV.X, FV.Y, FV.H):
198
+ pts = np.append(
199
+ np.zeros((n_states, n_pts, 1), dtype=config.dtype_double),
200
+ points,
201
+ axis=2,
202
+ )
203
+ pts[..., 0] = np.arange(n_states)[:, None]
204
+ pts = pts.reshape(n_states * n_pts, 4)
205
+ elif idims == (FC.STATE, FV.X, FV.Y):
206
+ pts = np.append(
207
+ np.zeros((n_states, n_pts, 1), dtype=config.dtype_double),
208
+ points[..., :2],
209
+ axis=2,
210
+ )
211
+ pts[..., 0] = np.arange(n_states)[:, None]
212
+ pts = pts.reshape(n_states * n_pts, 3)
213
+ elif idims == (FC.STATE, FV.H):
214
+ pts = np.append(
215
+ np.zeros((n_states, n_pts, 1), dtype=config.dtype_double),
216
+ points[..., 2, None],
217
+ axis=2,
218
+ )
219
+ pts[..., 0] = np.arange(n_states)[:, None]
220
+ pts = pts.reshape(n_states * n_pts, 2)
221
+ elif idims == (FC.STATE,):
222
+ if FV.WD in vrs:
223
+ uv = d[..., [iwd, iws]]
224
+ d[..., iwd] = uv2wd(uv)
225
+ d[..., iws] = np.linalg.norm(uv, axis=-1)
226
+ del uv
227
+ for i, v in enumerate(vrs):
228
+ if v in self.ovars:
229
+ out[v] = np.zeros((n_states, n_pts), dtype=config.dtype_double)
230
+ out[v][:] = d[:, None, i]
231
+ continue
232
+ elif idims == (FV.X, FV.Y, FV.H):
233
+ pts = points[0]
234
+ tdims = (1, n_pts, n_vrs)
235
+ elif idims == (FV.X, FV.Y):
236
+ pts = points[0][:, :2]
237
+ tdims = (1, n_pts, n_vrs)
238
+ elif idims == (FV.H,):
239
+ pts = points[0][:, 2]
240
+ tdims = (1, n_pts, n_vrs)
241
+ else:
242
+ raise ValueError(
243
+ f"States '{self.name}': Unsupported dimensions {dims} for variables {vrs}"
244
+ )
245
+
246
+ # interpolate:
247
+ try:
248
+ ipars = dict(bounds_error=True, fill_value=None)
249
+ ipars.update(self.interpn_pars)
250
+ d = interpn(gvars, d, pts, **ipars).reshape(tdims)
251
+ except ValueError as e:
252
+ print(f"\nStates '{self.name}': Interpolation error")
253
+ print("INPUT VARS: (state, x, y, height)")
254
+ print(
255
+ "DATA BOUNDS:",
256
+ [float(np.min(d)) for d in gvars],
257
+ [float(np.max(d)) for d in gvars],
258
+ )
259
+ print(
260
+ "EVAL BOUNDS:",
261
+ [float(np.min(p)) for p in pts.T],
262
+ [float(np.max(p)) for p in pts.T],
263
+ )
264
+ print(
265
+ "\nMaybe you want to try the option 'bounds_error=False' in 'interpn_pars'? This will extrapolate the data.\n"
266
+ )
267
+ raise e
268
+ del pts, gvars
269
+
270
+ # translate (U, V) into (WD, WS):
271
+ if FV.WD in vrs:
272
+ uv = d[..., [iwd, iws]]
273
+ d[..., iwd] = uv2wd(uv)
274
+ d[..., iws] = np.linalg.norm(uv, axis=-1)
275
+ del uv
276
+
277
+ # broadcast if needed:
278
+ if tdims != (n_states, n_pts, n_vrs):
279
+ tmp = d
280
+ d = np.zeros((n_states, n_pts, n_vrs), dtype=config.dtype_double)
281
+ d[:] = tmp
282
+ del tmp
283
+
284
+ # set output:
285
+ for i, v in enumerate(vrs):
286
+ if v in self.ovars:
287
+ out[v] = d[..., i]
288
+
289
+ # set fixed variables:
290
+ for v, d in self.fixed_vars.items():
291
+ out[v] = np.full((n_states, n_pts), d, dtype=config.dtype_double)
292
+
293
+ # add weights:
294
+ if weights is not None:
295
+ tdata[FV.WEIGHT] = weights[:, None, None]
296
+ elif FV.WEIGHT in out:
297
+ tdata[FV.WEIGHT] = out.pop(FV.WEIGHT).reshape(
298
+ n_states, n_targets, n_tpoints
299
+ )
300
+ else:
301
+ tdata[FV.WEIGHT] = np.full(
302
+ (mdata.n_states, 1, 1), 1 / self._N, dtype=config.dtype_double
303
+ )
304
+ tdata.dims[FV.WEIGHT] = (FC.STATE, FC.TARGET, FC.TPOINT)
305
+
306
+ return {v: d.reshape(n_states, n_targets, n_tpoints) for v, d in out.items()}
307
+
308
+
309
+ class WeibullField(FieldData):
310
+ """
311
+ Weibull sectors at regular grid points
312
+
313
+ Attributes
314
+ ----------
315
+ wd_coord: str
316
+ The wind direction coordinate name
317
+ ws_coord: str
318
+ The wind speed coordinate name, if wind speed bin
319
+ centres are in data, else None
320
+ ws_bins: numpy.ndarray
321
+ The wind speed bins, including
322
+ lower and upper bounds, shape: (n_ws_bins+1,)
323
+
324
+ :group: input.states
325
+
326
+ """
327
+
328
+ def __init__(
329
+ self,
330
+ *args,
331
+ wd_coord,
332
+ ws_coord=None,
333
+ ws_bins=None,
334
+ **kwargs,
335
+ ):
336
+ """
337
+ Constructor.
338
+
339
+ Parameters
340
+ ----------
341
+ args: tuple, optional
342
+ Positional arguments for the base class
343
+ wd_coord: str
344
+ The wind direction coordinate name
345
+ ws_coord: str, optional
346
+ The wind speed coordinate name, if wind speed bin
347
+ centres are in data
348
+ ws_bins: list of float, optional
349
+ The wind speed bins, including
350
+ lower and upper bounds
351
+ kwargs: dict, optional
352
+ Keyword arguments for the base class
353
+
354
+ """
355
+ super().__init__(
356
+ *args,
357
+ states_coord=wd_coord,
358
+ time_format=None,
359
+ load_mode="preload",
360
+ **kwargs,
361
+ )
362
+ self.wd_coord = wd_coord
363
+ self.ws_coord = ws_coord
364
+ self.ws_bins = None if ws_bins is None else np.sort(np.asarray(ws_bins))
365
+
366
+ assert ws_coord is None or ws_bins is None, (
367
+ f"States '{self.name}': Cannot have both ws_coord '{ws_coord}' and ws_bins {ws_bins}"
368
+ )
369
+ assert ws_coord is not None or ws_bins is not None, (
370
+ f"States '{self.name}': Expecting either ws_coord or ws_bins"
371
+ )
372
+ if ws_coord is not None:
373
+ self._cmap[FV.WS] = ws_coord
374
+
375
+ if FV.WD not in self.ovars:
376
+ raise ValueError(
377
+ f"States '{self.name}': Expecting output variable '{FV.WD}', got {self.ovars}"
378
+ )
379
+ for v in [FV.WEIBULL_A, FV.WEIBULL_k, FV.WEIGHT]:
380
+ if v in self.ovars:
381
+ raise ValueError(
382
+ f"States '{self.name}': Cannot have '{v}' as output variable"
383
+ )
384
+ if v not in self.variables:
385
+ self.variables.append(v)
386
+
387
+ for v in [FV.WS, FV.WD]:
388
+ if v in self.variables:
389
+ self.variables.remove(v)
390
+
391
+ self._n_wd = None
392
+ self._n_ws = None
393
+
394
+ def __repr__(self):
395
+ return f"{type(self).__name__}(n_wd={self._n_wd}, n_ws={self._n_ws})"
396
+
397
+ def _read_ds(self, ds, cmap, variables, verbosity=0):
398
+ """
399
+ Helper function for _get_data, extracts data from the original Dataset.
400
+
401
+ Parameters
402
+ ----------
403
+ ds: xarray.Dataset
404
+ The Dataset to read data from
405
+ cmap: dict
406
+ A mapping from foxes variable names to Dataset dimension names
407
+ variables: list of str
408
+ The variables to extract from the Dataset
409
+ verbosity: int
410
+ The verbosity level, 0 = silent
411
+
412
+ Returns
413
+ -------
414
+ coords: dict
415
+ keys: Foxes variable names, values: 1D coordinate value arrays
416
+ data: dict
417
+ The extracted data, keys are variable names,
418
+ values are tuples (dims, data_array)
419
+ where dims is a tuple of dimension names and
420
+ data_array is a numpy.ndarray with the data values
421
+
422
+ """
423
+ # read data, using wd_coord as state coordinate
424
+ coords, data0 = super()._read_ds(ds, cmap, variables, verbosity)
425
+ wd = coords.pop(FC.STATE)
426
+
427
+ # replace state by wd coordinate
428
+ data0 = {
429
+ v: (tuple({FC.STATE: FV.WD}.get(c, c) for c in dims), d)
430
+ for v, (dims, d) in data0.items()
431
+ }
432
+
433
+ # check weights
434
+ if FV.WEIGHT not in data0:
435
+ raise KeyError(
436
+ f"States '{self.name}': Missing weights variable '{FV.WEIGHT}' in data, found {sorted(list(data0.keys()))}"
437
+ )
438
+ else:
439
+ dims = data0[FV.WEIGHT][0]
440
+ if FV.WD not in dims:
441
+ raise KeyError(
442
+ f"States '{self.name}': Expecting weights variable '{FV.WEIGHT}' to contain dimension '{FV.WD}', got {dims}"
443
+ )
444
+ if FV.WS in dims:
445
+ raise KeyError(
446
+ f"States '{self.name}': Expecting weights variable '{FV.WEIGHT}' to not contain dimension '{FV.WS}', got {dims}"
447
+ )
448
+
449
+ # construct wind speed bins and bin deltas
450
+ assert FV.WS not in data0, (
451
+ f"States '{self.name}': Cannot have '{FV.WS}' in data, found variables {list(data0.keys())}"
452
+ )
453
+ if self.ws_bins is not None:
454
+ wsb = self.ws_bins
455
+ wss = 0.5 * (wsb[:-1] + wsb[1:])
456
+ elif self.ws_coord in ds.coords:
457
+ wss = ds[self.ws_coord].to_numpy()
458
+ wsb = np.zeros((len(wss) + 1,), dtype=config.dtype_double)
459
+ wsb[1:-1] = 0.5 * (wss[1:] + wss[:-1])
460
+ wsb[0] = wss[0] - 0.5 * wsb[1]
461
+ wsb[-1] = wss[-1] + 0.5 * wsb[-2]
462
+ self.ws_bins = wsb
463
+ else:
464
+ raise ValueError(
465
+ f"States '{self.name}': Expecting ws_bins argument, or '{self.ws_coord}' among data coordinates, got {list(ds.coords.keys())}"
466
+ )
467
+ wsd = wsb[1:] - wsb[:-1]
468
+ n_ws = len(wss)
469
+ n_wd = len(wd)
470
+ del wsb, ds
471
+
472
+ # calculate Weibull weights
473
+ dms = [FV.WS, FV.WD]
474
+ shp = [n_ws, n_wd]
475
+ for c in [FV.X, FV.Y, FV.H]:
476
+ for v in [FV.WEIBULL_A, FV.WEIBULL_k]:
477
+ if c in data0[v][0]:
478
+ dms.append(c)
479
+ shp.append(data0[v][1].shape[data0[v][0].index(c)])
480
+ break
481
+ dms = tuple(dms)
482
+ shp = tuple(shp)
483
+ if data0[FV.WEIGHT][0] == dms:
484
+ w = data0.pop(FV.WEIGHT)[1]
485
+ else:
486
+ s_w = tuple([np.s_[:] if c in data0[FV.WEIGHT][0] else None for c in dms])
487
+ w = np.zeros(shp, dtype=config.dtype_double)
488
+ w[:] = data0.pop(FV.WEIGHT)[1][s_w]
489
+ s_ws = tuple([np.s_[:], None] + [None] * (len(dms) - 2))
490
+ s_A = tuple([np.s_[:] if c in data0[FV.WEIBULL_A][0] else None for c in dms])
491
+ s_k = tuple([np.s_[:] if c in data0[FV.WEIBULL_A][0] else None for c in dms])
492
+ data0[FV.WEIGHT] = (
493
+ dms,
494
+ w
495
+ * weibull_weights(
496
+ ws=wss[s_ws],
497
+ ws_deltas=wsd[s_ws],
498
+ A=data0.pop(FV.WEIBULL_A)[1][s_A],
499
+ k=data0.pop(FV.WEIBULL_k)[1][s_k],
500
+ ),
501
+ )
502
+ del w, s_ws, s_A, s_k
503
+
504
+ # translate binned data to states
505
+ self._N = n_ws * n_wd
506
+ self._inds = None
507
+ data = {
508
+ FV.WS: np.zeros((n_ws, n_wd), dtype=config.dtype_double),
509
+ FV.WD: np.zeros((n_ws, n_wd), dtype=config.dtype_double),
510
+ }
511
+ data[FV.WS][:] = wss[:, None]
512
+ data[FV.WD][:] = wd[None, :]
513
+ data[FV.WS] = ((FC.STATE,), data[FV.WS].reshape(self._N))
514
+ data[FV.WD] = ((FC.STATE,), data[FV.WD].reshape(self._N))
515
+ for v in list(data0.keys()):
516
+ dims, d = data0.pop(v)
517
+ if dims[0] == FV.WD:
518
+ dms = tuple([FC.STATE] + list(dims[1:]))
519
+ shp = [n_ws] + list(d.shape)
520
+ data[v] = np.zeros(shp, dtype=config.dtype_double)
521
+ data[v][:] = d[None, ...]
522
+ data[v] = (dms, data[v].reshape([self._N] + shp[2:]))
523
+ elif len(dims) >= 2 and dims[:2] == (FV.WS, FV.WD):
524
+ dms = tuple([FC.STATE] + list(dims[2:]))
525
+ shp = [self._N] + list(d.shape[2:])
526
+ data[v] = (dms, d.reshape(shp))
527
+ else:
528
+ data[v] = (dims, d)
529
+ data0 = data
530
+
531
+ return coords, data
@@ -410,6 +410,8 @@ class MultiHeightStates(States):
410
410
  (n_states, n_targets, n_tpoints)
411
411
 
412
412
  """
413
+ self.ensure_output_vars(algo, tdata)
414
+
413
415
  n_states = tdata.n_states
414
416
  n_targets = tdata.n_targets
415
417
  n_tpoints = tdata.n_tpoints
@@ -366,6 +366,7 @@ class OnePointFlowStates(States):
366
366
 
367
367
  """
368
368
  # prepare:
369
+ self.ensure_output_vars(algo, tdata)
369
370
  targets = tdata[FC.TARGETS]
370
371
  n_states, n_targets, n_tpoints = targets.shape[:3]
371
372
  n_points = n_targets * n_tpoints