foxes 1.2.2__py3-none-any.whl → 1.2.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 (63) hide show
  1. examples/field_data_nc/run.py +11 -4
  2. examples/streamline_wakes/run.py +6 -3
  3. foxes/algorithms/downwind/downwind.py +1 -0
  4. foxes/config/__init__.py +1 -1
  5. foxes/config/config.py +80 -14
  6. foxes/constants.py +12 -1
  7. foxes/core/algorithm.py +13 -8
  8. foxes/core/engine.py +30 -0
  9. foxes/core/farm_controller.py +41 -24
  10. foxes/core/states.py +1 -1
  11. foxes/core/wind_farm.py +109 -0
  12. foxes/engines/dask.py +88 -4
  13. foxes/engines/default.py +45 -2
  14. foxes/engines/mpi.py +5 -16
  15. foxes/engines/multiprocess.py +1 -10
  16. foxes/engines/numpy.py +30 -0
  17. foxes/engines/pool.py +48 -0
  18. foxes/engines/ray.py +1 -1
  19. foxes/engines/single.py +30 -0
  20. foxes/input/farm_layout/from_csv.py +2 -2
  21. foxes/input/farm_layout/from_file.py +2 -2
  22. foxes/input/farm_layout/from_json.py +2 -2
  23. foxes/input/states/__init__.py +0 -1
  24. foxes/input/states/create/random_abl_states.py +2 -2
  25. foxes/input/states/field_data_nc.py +286 -141
  26. foxes/input/states/multi_height.py +3 -3
  27. foxes/input/states/states_table.py +3 -3
  28. foxes/input/yaml/dict.py +83 -46
  29. foxes/input/yaml/windio/__init__.py +2 -1
  30. foxes/input/yaml/windio/read_attributes.py +17 -34
  31. foxes/input/yaml/windio/read_farm.py +57 -3
  32. foxes/input/yaml/windio/read_outputs.py +116 -56
  33. foxes/input/yaml/windio/{get_states.py → read_site.py} +69 -0
  34. foxes/input/yaml/windio/windio.py +42 -119
  35. foxes/input/yaml/yaml.py +3 -3
  36. foxes/models/model_book.py +1 -0
  37. foxes/models/point_models/__init__.py +1 -0
  38. foxes/models/point_models/ustar2ti.py +84 -0
  39. foxes/models/turbine_models/lookup_table.py +2 -2
  40. foxes/models/turbine_models/sector_management.py +2 -2
  41. foxes/models/turbine_models/table_factors.py +2 -2
  42. foxes/models/turbine_types/CpCt_file.py +2 -2
  43. foxes/models/turbine_types/CpCt_from_two.py +3 -3
  44. foxes/models/turbine_types/PCt_file.py +2 -2
  45. foxes/models/turbine_types/PCt_from_two.py +3 -3
  46. foxes/models/turbine_types/TBL_file.py +2 -2
  47. foxes/models/turbine_types/wsrho2PCt_from_two.py +3 -3
  48. foxes/models/turbine_types/wsti2PCt_from_two.py +3 -3
  49. foxes/output/__init__.py +1 -0
  50. foxes/output/output.py +5 -3
  51. foxes/output/slice_data.py +1 -1
  52. foxes/output/slices_data.py +323 -0
  53. foxes/output/state_turbine_table.py +11 -0
  54. foxes/utils/load.py +12 -4
  55. foxes/utils/xarray_utils.py +14 -3
  56. foxes/variables.py +5 -0
  57. {foxes-1.2.2.dist-info → foxes-1.2.3.dist-info}/METADATA +6 -2
  58. {foxes-1.2.2.dist-info → foxes-1.2.3.dist-info}/RECORD +62 -61
  59. foxes/input/states/slice_data_nc.py +0 -687
  60. {foxes-1.2.2.dist-info → foxes-1.2.3.dist-info}/LICENSE +0 -0
  61. {foxes-1.2.2.dist-info → foxes-1.2.3.dist-info}/WHEEL +0 -0
  62. {foxes-1.2.2.dist-info → foxes-1.2.3.dist-info}/entry_points.txt +0 -0
  63. {foxes-1.2.2.dist-info → foxes-1.2.3.dist-info}/top_level.txt +0 -0
@@ -1,687 +0,0 @@
1
- import numpy as np
2
- import pandas as pd
3
- import xarray as xr
4
- from scipy.interpolate import interpn
5
-
6
- from foxes.core import States
7
- from foxes.utils import wd2uv, uv2wd, import_module
8
- from foxes.data import STATES, StaticData
9
- from foxes.config import config, get_path
10
- import foxes.variables as FV
11
- import foxes.constants as FC
12
-
13
-
14
- class SliceDataNC(States):
15
- """
16
- Heterogeneous ambient states on a regular
17
- horizontal grid in NetCDF format, independent
18
- of height.
19
-
20
- Attributes
21
- ----------
22
- data_source: str or xarray.Dataset
23
- The data or the file search pattern, should end with
24
- suffix '.nc'. One or many files.
25
- ovars: list of str
26
- The output variables
27
- var2ncvar: dict
28
- Mapping from variable names to variable names
29
- in the nc file
30
- fixed_vars: dict
31
- Uniform values for output variables, instead
32
- of reading from data
33
- states_coord: str
34
- The states coordinate name in the data
35
- x_coord: str
36
- The x coordinate name in the data
37
- y_coord: str
38
- The y coordinate name in the data
39
- pre_load: bool
40
- Flag for loading all data into memory during
41
- initialization
42
- weight_ncvar: str
43
- Name of the weight data variable in the nc file(s)
44
- bounds_error: bool
45
- Flag for raising errors if bounds are exceeded
46
- fill_value: number
47
- Fill value in case of exceeding bounds, if no bounds error
48
- time_format: str
49
- The datetime parsing format string
50
- interp_nans: bool
51
- Linearly interpolate nan values
52
- interpn_pars: dict, optional
53
- Additional parameters for scipy.interpolate.interpn
54
-
55
- :group: input.states
56
-
57
- """
58
-
59
- def __init__(
60
- self,
61
- data_source,
62
- output_vars,
63
- var2ncvar={},
64
- fixed_vars={},
65
- states_coord="Time",
66
- x_coord="UTMX",
67
- y_coord="UTMY",
68
- pre_load=True,
69
- weight_ncvar=None,
70
- time_format="%Y-%m-%d_%H:%M:%S",
71
- sel=None,
72
- isel=None,
73
- interp_nans=False,
74
- verbosity=1,
75
- **interpn_pars,
76
- ):
77
- """
78
- Constructor.
79
-
80
- Parameters
81
- ----------
82
- data_source: str or xarray.Dataset
83
- The data or the file search pattern, should end with
84
- suffix '.nc'. One or many files.
85
- output_vars: list of str
86
- The output variables
87
- var2ncvar: dict, optional
88
- Mapping from variable names to variable names
89
- in the nc file
90
- fixed_vars: dict, optional
91
- Uniform values for output variables, instead
92
- of reading from data
93
- states_coord: str
94
- The states coordinate name in the data
95
- x_coord: str
96
- The x coordinate name in the data
97
- y_coord: str
98
- The y coordinate name in the data
99
- pre_load: bool
100
- Flag for loading all data into memory during
101
- initialization
102
- weight_ncvar: str, optional
103
- Name of the weight data variable in the nc file(s)
104
- time_format: str
105
- The datetime parsing format string
106
- sel: dict, optional
107
- Subset selection via xr.Dataset.sel()
108
- isel: dict, optional
109
- Subset selection via xr.Dataset.isel()
110
- interp_nans: bool
111
- Linearly interpolate nan values
112
- verbosity: int
113
- Verbosity level for pre_load file reading
114
- interpn_pars: dict, optional
115
- Additional parameters for scipy.interpolate.interpn
116
-
117
- """
118
- super().__init__()
119
-
120
- self.states_coord = states_coord
121
- self.ovars = output_vars
122
- self.fixed_vars = fixed_vars
123
- self.x_coord = x_coord
124
- self.y_coord = y_coord
125
- self.weight_ncvar = weight_ncvar
126
- self.pre_load = pre_load
127
- self.time_format = time_format
128
- self.sel = sel
129
- self.isel = isel
130
- self.interpn_pars = interpn_pars
131
- self.interp_nans = interp_nans
132
-
133
- self.var2ncvar = {
134
- v: var2ncvar.get(v, v) for v in output_vars if v not in fixed_vars
135
- }
136
-
137
- self._N = None
138
-
139
- self.__data_source = data_source
140
- self.__weights = None
141
- self.__inds = None
142
-
143
- # pre-load file reading:
144
- if not isinstance(self.data_source, xr.Dataset):
145
- if "*" in str(self.data_source):
146
- pass
147
- else:
148
- self.__data_source = get_path(self.data_source)
149
- if not self.data_source.is_file():
150
- self.__data_source = StaticData().get_file_path(
151
- STATES, self.data_source.name, check_raw=False
152
- )
153
- if verbosity:
154
- if pre_load:
155
- print(
156
- f"States '{self.name}': Reading data from '{self.data_source}'"
157
- )
158
- else:
159
- print(
160
- f"States '{self.name}': Reading index from '{self.data_source}'"
161
- )
162
-
163
- def _read_ds():
164
- self._data_source = get_path(self.data_source)
165
- if self.data_source.is_file():
166
- return xr.open_dataset(self.data_source)
167
- else:
168
- # try to read multiple files, needs dask:
169
- try:
170
- return xr.open_mfdataset(
171
- str(self.data_source),
172
- parallel=False,
173
- concat_dim=self.states_coord,
174
- combine="nested",
175
- data_vars="minimal",
176
- coords="minimal",
177
- compat="override",
178
- )
179
- except ValueError as e:
180
- import_module("dask", hint="pip install dask")
181
- raise e
182
-
183
- self.__data_source = _read_ds()
184
-
185
- if sel is not None:
186
- self.__data_source = self.data_source.sel(self.sel)
187
- if isel is not None:
188
- self.__data_source = self.data_source.isel(self.isel)
189
- if pre_load:
190
- self.__data_source.load()
191
-
192
- self._get_inds(self.data_source)
193
-
194
- @property
195
- def data_source(self):
196
- """
197
- The data source
198
-
199
- Returns
200
- -------
201
- s: object
202
- The data source
203
-
204
- """
205
- if self.pre_load and self.running:
206
- raise ValueError(
207
- f"States '{self.name}': Cannot acces data_source while running"
208
- )
209
- return self.__data_source
210
-
211
- def _get_inds(self, ds):
212
- """
213
- Helper function for index and weights
214
- reading
215
- """
216
- for c in [self.states_coord, self.x_coord, self.y_coord]:
217
- if not c in ds:
218
- raise KeyError(
219
- f"States '{self.name}': Missing coordinate '{c}' in data"
220
- )
221
-
222
- self.__inds = ds[self.states_coord].to_numpy()
223
- if self.time_format is not None:
224
- self.__inds = pd.to_datetime(
225
- self.__inds, format=self.time_format
226
- ).to_numpy()
227
- self._N = len(self.__inds)
228
-
229
- if self.weight_ncvar is not None:
230
- self.__weights = ds[self.weight_ncvar].to_numpy()
231
-
232
- for v in self.ovars:
233
- if v in self.var2ncvar:
234
- ncv = self.var2ncvar[v]
235
- if not ncv in ds:
236
- raise KeyError(
237
- f"States '{self.name}': nc variable '{ncv}' not found in data, found: {sorted(list(ds.keys()))}"
238
- )
239
- elif v not in self.fixed_vars:
240
- raise ValueError(
241
- f"States '{self.name}': Variable '{v}' neither found in var2ncvar not in fixed_vars"
242
- )
243
-
244
- def _get_data(self, ds, verbosity):
245
- """
246
- Helper function for data extraction
247
- """
248
- x = ds[self.x_coord].to_numpy()
249
- y = ds[self.y_coord].to_numpy()
250
- n_x = len(x)
251
- n_y = len(y)
252
- n_sts = ds.sizes[self.states_coord]
253
-
254
- cor_sxy = (self.states_coord, self.x_coord, self.y_coord)
255
- cor_syx = (self.states_coord, self.y_coord, self.x_coord)
256
- cor_s = (self.states_coord,)
257
- vars_syx = []
258
- vars_s = []
259
- for v, ncv in self.var2ncvar.items():
260
- if ds[ncv].dims == cor_syx or ds[ncv].dims == cor_sxy:
261
- vars_syx.append(v)
262
- elif ds[ncv].dims == cor_s:
263
- vars_s.append(v)
264
- else:
265
- raise ValueError(
266
- f"States '{self.name}': Wrong coordinate order for variable '{ncv}': Found {ds[ncv].dims}, expecting {cor_sxy}, {cor_syx}, or {cor_s}"
267
- )
268
-
269
- data = np.zeros(
270
- (n_sts, n_y, n_x, len(self.var2ncvar)), dtype=config.dtype_double
271
- )
272
- for v in vars_syx:
273
- ncv = self.var2ncvar[v]
274
- if ds[ncv].dims == cor_syx:
275
- data[..., self._dkys[v]] = ds[ncv][:]
276
- else:
277
- data[..., self._dkys[v]] = np.swapaxes(ds[ncv].to_numpy(), 1, 2)
278
- for v in vars_s:
279
- ncv = self.var2ncvar[v]
280
- data[..., self._dkys[v]] = ds[ncv].to_numpy()[:, None, None]
281
- if FV.WD in self.fixed_vars:
282
- data[..., self._dkys[FV.WD]] = np.full(
283
- (n_sts, n_y, n_x), self.fixed_vars[FV.WD], dtype=config.dtype_double
284
- )
285
-
286
- if verbosity > 1:
287
- print(f"\n{self.name}: Data ranges")
288
- for v, i in self._dkys.items():
289
- d = data[..., i]
290
- nn = np.sum(np.isnan(d))
291
- print(
292
- f" {v}: {np.nanmin(d)} --> {np.nanmax(d)}, nans: {nn} ({100*nn/len(d.flat):.2f}%)"
293
- )
294
-
295
- return data
296
-
297
- def output_point_vars(self, algo):
298
- """
299
- The variables which are being modified by the model.
300
-
301
- Parameters
302
- ----------
303
- algo: foxes.core.Algorithm
304
- The calculation algorithm
305
-
306
- Returns
307
- -------
308
- output_vars: list of str
309
- The output variable names
310
-
311
- """
312
- return self.ovars
313
-
314
- def load_data(self, algo, verbosity=0):
315
- """
316
- Load and/or create all model data that is subject to chunking.
317
-
318
- Such data should not be stored under self, for memory reasons. The
319
- data returned here will automatically be chunked and then provided
320
- as part of the mdata object during calculations.
321
-
322
- Parameters
323
- ----------
324
- algo: foxes.core.Algorithm
325
- The calculation algorithm
326
- verbosity: int
327
- The verbosity level, 0 = silent
328
-
329
- Returns
330
- -------
331
- idata: dict
332
- The dict has exactly two entries: `data_vars`,
333
- a dict with entries `name_str -> (dim_tuple, data_ndarray)`;
334
- and `coords`, a dict with entries `dim_name_str -> dim_array`
335
-
336
- """
337
-
338
- if (FV.WS in self.ovars and FV.WD not in self.ovars) or (
339
- FV.WS not in self.ovars and FV.WD in self.ovars
340
- ):
341
- raise KeyError(
342
- f"States '{self.name}': Missing '{FV.WS}' or '{FV.WD}' in output variables {self.ovars}"
343
- )
344
-
345
- # ensure WD and WS get the first two slots of data:
346
- self._dkys = {}
347
- if FV.WS in self.ovars:
348
- self._dkys[FV.WD] = 0
349
- if FV.WS in self.var2ncvar:
350
- self._dkys[FV.WS] = 1
351
- for v in self.var2ncvar:
352
- if v not in self._dkys:
353
- self._dkys[v] = len(self._dkys)
354
- self._n_dvars = len(self._dkys)
355
-
356
- if self.__weights is None:
357
- self.__weights = np.full(
358
- (self._N, algo.n_turbines), 1.0 / self._N, dtype=config.dtype_double
359
- )
360
-
361
- idata = super().load_data(algo, verbosity)
362
-
363
- if self.pre_load:
364
- self.X = self.var(FV.X)
365
- self.Y = self.var(FV.Y)
366
- self.VARS = self.var("vars")
367
- self.DATA = self.var("data")
368
-
369
- ds = self.data_source
370
-
371
- y = ds[self.y_coord].to_numpy()
372
- x = ds[self.x_coord].to_numpy()
373
- v = list(self._dkys.keys())
374
- coos = (FC.STATE, self.Y, self.X, self.VARS)
375
- data = self._get_data(ds, verbosity)
376
- data = (coos, data)
377
-
378
- idata["coords"][self.Y] = y
379
- idata["coords"][self.X] = x
380
- idata["coords"][self.VARS] = v
381
- idata["data_vars"][self.DATA] = data
382
-
383
- return idata
384
-
385
- def set_running(
386
- self,
387
- algo,
388
- data_stash,
389
- sel=None,
390
- isel=None,
391
- verbosity=0,
392
- ):
393
- """
394
- Sets this model status to running, and moves
395
- all large data to stash.
396
-
397
- The stashed data will be returned by the
398
- unset_running() function after running calculations.
399
-
400
- Parameters
401
- ----------
402
- algo: foxes.core.Algorithm
403
- The calculation algorithm
404
- data_stash: dict
405
- Large data stash, this function adds data here.
406
- Key: model name. Value: dict, large model data
407
- sel: dict, optional
408
- The subset selection dictionary
409
- isel: dict, optional
410
- The index subset selection dictionary
411
- verbosity: int
412
- The verbosity level, 0 = silent
413
-
414
- """
415
- super().set_running(algo, data_stash, sel, isel, verbosity)
416
-
417
- data_stash[self.name] = dict(
418
- weights=self.__weights,
419
- inds=self.__inds,
420
- )
421
- del self.__weights, self.__inds
422
-
423
- if self.pre_load:
424
- data_stash[self.name]["data_source"] = self.__data_source
425
- del self.__data_source
426
-
427
- def unset_running(
428
- self,
429
- algo,
430
- data_stash,
431
- sel=None,
432
- isel=None,
433
- verbosity=0,
434
- ):
435
- """
436
- Sets this model status to not running, recovering large data
437
- from stash
438
-
439
- Parameters
440
- ----------
441
- algo: foxes.core.Algorithm
442
- The calculation algorithm
443
- data_stash: dict
444
- Large data stash, this function adds data here.
445
- Key: model name. Value: dict, large model data
446
- sel: dict, optional
447
- The subset selection dictionary
448
- isel: dict, optional
449
- The index subset selection dictionary
450
- verbosity: int
451
- The verbosity level, 0 = silent
452
-
453
- """
454
- super().unset_running(algo, data_stash, sel, isel, verbosity)
455
-
456
- data = data_stash[self.name]
457
- self.__weights = data.pop("weights")
458
- self.__inds = data.pop("inds")
459
-
460
- if self.pre_load:
461
- self.__data_source = data.pop("data_source")
462
-
463
- def size(self):
464
- """
465
- The total number of states.
466
-
467
- Returns
468
- -------
469
- int:
470
- The total number of states
471
-
472
- """
473
- return self._N
474
-
475
- def index(self):
476
- """
477
- The index list
478
-
479
- Returns
480
- -------
481
- indices: array_like
482
- The index labels of states, or None for default integers
483
-
484
- """
485
- if self.running:
486
- raise ValueError(f"States '{self.name}': Cannot acces index while running")
487
- return self.__inds
488
-
489
- def output_point_vars(self, algo):
490
- """
491
- The variables which are being modified by the model.
492
-
493
- Parameters
494
- ----------
495
- algo: foxes.core.Algorithm
496
- The calculation algorithm
497
-
498
- Returns
499
- -------
500
- output_vars: list of str
501
- The output variable names
502
-
503
- """
504
- return self.ovars
505
-
506
- def weights(self, algo):
507
- """
508
- The statistical weights of all states.
509
-
510
- Parameters
511
- ----------
512
- algo: foxes.core.Algorithm
513
- The calculation algorithm
514
-
515
- Returns
516
- -------
517
- weights: numpy.ndarray
518
- The weights, shape: (n_states, n_turbines)
519
-
520
- """
521
- if self.running:
522
- raise ValueError(
523
- f"States '{self.name}': Cannot acces weights while running"
524
- )
525
- return self.__weights
526
-
527
- def calculate(self, algo, mdata, fdata, tdata):
528
- """ "
529
- The main model calculation.
530
-
531
- This function is executed on a single chunk of data,
532
- all computations should be based on numpy arrays.
533
-
534
- Parameters
535
- ----------
536
- algo: foxes.core.Algorithm
537
- The calculation algorithm
538
- mdata: foxes.core.MData
539
- The model data
540
- fdata: foxes.core.FData
541
- The farm data
542
- tdata: foxes.core.TData
543
- The target point data
544
-
545
- Returns
546
- -------
547
- results: dict
548
- The resulting data, keys: output variable str.
549
- Values: numpy.ndarray with shape
550
- (n_states, n_targets, n_tpoints)
551
-
552
- """
553
- # prepare:
554
- n_states = tdata.n_states
555
- n_targets = tdata.n_targets
556
- n_tpoints = tdata.n_tpoints
557
- points = tdata[FC.TARGETS].reshape(n_states, n_targets * n_tpoints, 3)
558
- n_pts = points.shape[1]
559
- n_states = fdata.n_states
560
-
561
- # pick pre-loaded data:
562
- if self.pre_load:
563
- x = mdata[self.X]
564
- y = mdata[self.Y]
565
- data = mdata[self.DATA].copy()
566
-
567
- # read data for this chunk:
568
- else:
569
- i0 = mdata.states_i0(counter=True)
570
- s = slice(i0, i0 + n_states)
571
- ds = self.data_source.isel({self.states_coord: s}).load()
572
-
573
- x = ds[self.x_coord].to_numpy()
574
- y = ds[self.y_coord].to_numpy()
575
- data = self._get_data(ds, verbosity=0)
576
-
577
- del ds
578
- n_y = len(y)
579
- n_x = len(x)
580
-
581
- # translate WS, WD into U, V:
582
- if FV.WD in self.ovars and FV.WS in self.ovars:
583
- wd = data[..., self._dkys[FV.WD]]
584
- ws = (
585
- data[..., self._dkys[FV.WS]]
586
- if FV.WS in self._dkys
587
- else self.fixed_vars[FV.WS]
588
- )
589
- wdwsi = [self._dkys[FV.WD], self._dkys[FV.WS]]
590
- data[..., wdwsi] = wd2uv(wd, ws, axis=-1)
591
- del ws, wd
592
-
593
- # prepare points:
594
- sts = np.arange(n_states)
595
- pts = np.append(
596
- points, np.zeros((n_states, n_pts, 1), dtype=config.dtype_double), axis=2
597
- )
598
- pts[:, :, 3] = sts[:, None]
599
- pts = pts.reshape(n_states * n_pts, 3)
600
- pts = np.flip(pts, axis=1)
601
- gvars = (sts, y, x)
602
-
603
- # interpolate nan values:
604
- if self.interp_nans and np.any(np.isnan(data)):
605
- df = pd.DataFrame(
606
- index=pd.MultiIndex.from_product(gvars, names=["state", "y", "x"]),
607
- data={
608
- v: data[..., vi].reshape(n_states * n_y * n_x)
609
- for v, vi in self._dkys.items()
610
- },
611
- )
612
- df.interpolate(
613
- axis=0, method="linear", limit_direction="forward", inplace=True
614
- )
615
- df.interpolate(
616
- axis=0, method="linear", limit_direction="backward", inplace=True
617
- )
618
- data = df.to_numpy().reshape(n_states, n_y, n_x, self._n_dvars)
619
- del df
620
-
621
- # interpolate:
622
- try:
623
- ipars = dict(bounds_error=True, fill_value=None)
624
- ipars.update(self.interpn_pars)
625
- data = interpn(gvars, data, pts, **ipars).reshape(
626
- n_states, n_pts, self._n_dvars
627
- )
628
- except ValueError as e:
629
- print(f"\nStates '{self.name}': Interpolation error")
630
- print("INPUT VARS: (state, y, x)")
631
- print(
632
- "DATA BOUNDS:",
633
- [float(np.min(d)) for d in gvars],
634
- [float(np.max(d)) for d in gvars],
635
- )
636
- print(
637
- "EVAL BOUNDS:",
638
- [float(np.min(p)) for p in pts.T],
639
- [float(np.max(p)) for p in pts.T],
640
- )
641
- print(
642
- "\nMaybe you want to try the option 'bounds_error=False'? This will extrapolate the data.\n"
643
- )
644
- raise e
645
- del pts, x, y, gvars
646
-
647
- # interpolate nan values:
648
- if self.interp_nans and np.any(np.isnan(data)):
649
- df = pd.DataFrame(
650
- index=pd.MultiIndex.from_product(
651
- (sts, range(n_pts)), names=["state", "point"]
652
- ),
653
- data={
654
- v: data[:, :, vi].reshape(n_states * n_pts)
655
- for v, vi in self._dkys.items()
656
- },
657
- )
658
- df["x"] = points[:, :, 0].reshape(n_states * n_pts)
659
- df["y"] = points[:, :, 1].reshape(n_states * n_pts)
660
- df = df.reset_index().set_index(["state", "x", "y"])
661
- df.interpolate(
662
- axis=0, method="linear", limit_direction="forward", inplace=True
663
- )
664
- df.interpolate(
665
- axis=0, method="linear", limit_direction="backward", inplace=True
666
- )
667
- df = df.reset_index().drop(["x", "y"], axis=1).set_index(["state", "point"])
668
- data = df.to_numpy().reshape(n_states, n_pts, self._n_dvars)
669
- del df
670
-
671
- # set output:
672
- out = {}
673
- if FV.WD in self.ovars and FV.WS in self.ovars:
674
- uv = data[..., wdwsi]
675
- out[FV.WS] = np.linalg.norm(uv, axis=-1)
676
- out[FV.WD] = uv2wd(uv, axis=-1)
677
- del uv
678
- for v in self.ovars:
679
- if v not in out:
680
- if v in self._dkys:
681
- out[v] = data[..., self._dkys[v]]
682
- else:
683
- out[v] = np.full(
684
- (n_states, n_pts), self.fixed_vars[v], dtype=config.dtype_double
685
- )
686
-
687
- return {v: d.reshape(n_states, n_targets, n_tpoints) for v, d in out.items()}
File without changes
File without changes