foxes 1.2.1__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 (64) 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/__init__.py +1 -1
  29. foxes/input/yaml/dict.py +268 -135
  30. foxes/input/yaml/windio/__init__.py +2 -1
  31. foxes/input/yaml/windio/read_attributes.py +17 -34
  32. foxes/input/yaml/windio/read_farm.py +57 -3
  33. foxes/input/yaml/windio/read_outputs.py +116 -56
  34. foxes/input/yaml/windio/{get_states.py → read_site.py} +69 -0
  35. foxes/input/yaml/windio/windio.py +42 -119
  36. foxes/input/yaml/yaml.py +3 -3
  37. foxes/models/model_book.py +1 -0
  38. foxes/models/point_models/__init__.py +1 -0
  39. foxes/models/point_models/ustar2ti.py +84 -0
  40. foxes/models/turbine_models/lookup_table.py +2 -2
  41. foxes/models/turbine_models/sector_management.py +2 -2
  42. foxes/models/turbine_models/table_factors.py +2 -2
  43. foxes/models/turbine_types/CpCt_file.py +2 -2
  44. foxes/models/turbine_types/CpCt_from_two.py +3 -3
  45. foxes/models/turbine_types/PCt_file.py +2 -2
  46. foxes/models/turbine_types/PCt_from_two.py +3 -3
  47. foxes/models/turbine_types/TBL_file.py +2 -2
  48. foxes/models/turbine_types/wsrho2PCt_from_two.py +3 -3
  49. foxes/models/turbine_types/wsti2PCt_from_two.py +3 -3
  50. foxes/output/__init__.py +1 -0
  51. foxes/output/output.py +5 -3
  52. foxes/output/slice_data.py +1 -1
  53. foxes/output/slices_data.py +323 -0
  54. foxes/output/state_turbine_table.py +11 -0
  55. foxes/utils/load.py +12 -4
  56. foxes/utils/xarray_utils.py +14 -3
  57. foxes/variables.py +5 -0
  58. {foxes-1.2.1.dist-info → foxes-1.2.3.dist-info}/METADATA +6 -2
  59. {foxes-1.2.1.dist-info → foxes-1.2.3.dist-info}/RECORD +63 -62
  60. foxes/input/states/slice_data_nc.py +0 -687
  61. {foxes-1.2.1.dist-info → foxes-1.2.3.dist-info}/LICENSE +0 -0
  62. {foxes-1.2.1.dist-info → foxes-1.2.3.dist-info}/WHEEL +0 -0
  63. {foxes-1.2.1.dist-info → foxes-1.2.3.dist-info}/entry_points.txt +0 -0
  64. {foxes-1.2.1.dist-info → foxes-1.2.3.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,55 @@
1
1
  import numpy as np
2
2
  import pandas as pd
3
3
  import xarray as xr
4
+ from copy import copy
4
5
  from scipy.interpolate import interpn
5
6
 
6
- from foxes.core import States
7
+ from foxes.core import States, get_engine
7
8
  from foxes.utils import wd2uv, uv2wd, import_module
8
9
  from foxes.data import STATES, StaticData
9
10
  import foxes.variables as FV
10
11
  import foxes.constants as FC
11
- from foxes.config import config, get_path
12
+ from foxes.config import config, get_input_path
13
+
14
+
15
+ def _read_nc_file(
16
+ fpath,
17
+ coords,
18
+ vars,
19
+ weight_var,
20
+ nc_engine,
21
+ sel,
22
+ isel,
23
+ minimal,
24
+ ):
25
+ """Helper function for nc file reading"""
26
+ data = xr.open_dataset(fpath, engine=nc_engine)
27
+ for c in coords:
28
+ if c is not None and c not in data:
29
+ raise KeyError(
30
+ f"Missing coordinate '{c}' in file {fpath}, got: {list(data.coords.keys())}"
31
+ )
32
+ if minimal:
33
+ weights = None
34
+ if weight_var is not None:
35
+ if weight_var not in data.data_vars:
36
+ raise KeyError(
37
+ f"Missing weight var '{weight_var}' in file {fpath}, found: {list(data.data_vars.keys())}"
38
+ )
39
+ if data[weight_var].dims != (coords[0],):
40
+ raise ValueError(
41
+ f"Wrong dimensions for variable '{weight_var}' in file {fpath}. Expecting {(coords[0],)}, got {data[weight_var].dims}"
42
+ )
43
+ weights = data[weight_var].to_numpy()
44
+ return data[coords[0]].to_numpy(), weights
45
+ else:
46
+ data = data[vars]
47
+ data.attrs = {}
48
+ if isel is not None and len(isel):
49
+ data = data.isel(**isel)
50
+ if sel is not None and len(sel):
51
+ data = data.sel(**sel)
52
+ return data
12
53
 
13
54
 
14
55
  class FieldDataNC(States):
@@ -37,9 +78,13 @@ class FieldDataNC(States):
37
78
  The y coordinate name in the data
38
79
  h_coord: str
39
80
  The height coordinate name in the data
40
- pre_load: bool
41
- Flag for loading all data into memory during
42
- initialization
81
+ load_mode: str
82
+ The load mode, choices: preload, lazy, fly.
83
+ preload loads all data during initialization,
84
+ lazy lazy-loads the data using dask, and fly
85
+ reads only states index and weights during initialization
86
+ and then opens the relevant files again within
87
+ the chunk calculation
43
88
  weight_ncvar: str
44
89
  Name of the weight data variable in the nc file(s)
45
90
  bounds_error: bool
@@ -52,6 +97,9 @@ class FieldDataNC(States):
52
97
  Linearly interpolate nan values
53
98
  interpn_pars: dict, optional
54
99
  Additional parameters for scipy.interpolate.interpn
100
+ bounds_extra_space: float or str
101
+ The extra space, either float in m,
102
+ or str for units of D, e.g. '2.5D'
55
103
 
56
104
  :group: input.states
57
105
 
@@ -67,13 +115,13 @@ class FieldDataNC(States):
67
115
  x_coord="UTMX",
68
116
  y_coord="UTMY",
69
117
  h_coord="height",
70
- pre_load=True,
118
+ load_mode="preload",
71
119
  weight_ncvar=None,
72
120
  time_format="%Y-%m-%d_%H:%M:%S",
73
121
  sel=None,
74
122
  isel=None,
75
123
  interp_nans=False,
76
- verbosity=1,
124
+ bounds_extra_space=1000,
77
125
  **interpn_pars,
78
126
  ):
79
127
  """
@@ -98,11 +146,15 @@ class FieldDataNC(States):
98
146
  The x coordinate name in the data
99
147
  y_coord: str
100
148
  The y coordinate name in the data
101
- h_coord: str
149
+ h_coord: str, optional
102
150
  The height coordinate name in the data
103
- pre_load: bool
104
- Flag for loading all data into memory during
105
- initialization
151
+ load_mode: str
152
+ The load mode, choices: preload, lazy, fly.
153
+ preload loads all data during initialization,
154
+ lazy lazy-loads the data using dask, and fly
155
+ reads only states index and weights during initialization
156
+ and then opens the relevant files again within
157
+ the chunk calculation
106
158
  weight_ncvar: str, optional
107
159
  Name of the weight data variable in the nc file(s)
108
160
  time_format: str
@@ -113,8 +165,9 @@ class FieldDataNC(States):
113
165
  Subset selection via xr.Dataset.isel()
114
166
  interp_nans: bool
115
167
  Linearly interpolate nan values
116
- verbosity: int
117
- Verbosity level for pre_load file reading
168
+ bounds_extra_space: float or str, optional
169
+ The extra space, either float in m,
170
+ or str for units of D, e.g. '2.5D'
118
171
  interpn_pars: dict, optional
119
172
  Additional parameters for scipy.interpolate.interpn
120
173
 
@@ -128,12 +181,13 @@ class FieldDataNC(States):
128
181
  self.y_coord = y_coord
129
182
  self.h_coord = h_coord
130
183
  self.weight_ncvar = weight_ncvar
131
- self.pre_load = pre_load
184
+ self.load_mode = load_mode
132
185
  self.time_format = time_format
133
- self.sel = sel
134
- self.isel = isel
186
+ self.sel = sel if sel is not None else {}
187
+ self.isel = isel if isel is not None else {}
135
188
  self.interpn_pars = interpn_pars
136
189
  self.interp_nans = interp_nans
190
+ self.bounds_extra_space = bounds_extra_space
137
191
 
138
192
  self.var2ncvar = {
139
193
  v: var2ncvar.get(v, v) for v in output_vars if v not in fixed_vars
@@ -145,57 +199,6 @@ class FieldDataNC(States):
145
199
  self.__weights = None
146
200
  self.__inds = None
147
201
 
148
- # pre-load file reading:
149
- if not isinstance(self.data_source, xr.Dataset):
150
- if "*" in str(self.data_source):
151
- pass
152
- else:
153
- self.__data_source = get_path(self.data_source)
154
- if not self.data_source.is_file():
155
- self.__data_source = StaticData().get_file_path(
156
- STATES, self.data_source.name, check_raw=False
157
- )
158
- if verbosity:
159
- if pre_load:
160
- print(
161
- f"States '{self.name}': Reading data from '{self.data_source}'"
162
- )
163
- else:
164
- print(
165
- f"States '{self.name}': Reading index from '{self.data_source}'"
166
- )
167
-
168
- def _read_ds():
169
- fpath = get_path(self.data_source)
170
- if fpath.is_file():
171
- return xr.open_dataset(fpath)
172
- else:
173
- # try to read multiple files, needs dask:
174
- try:
175
- return xr.open_mfdataset(
176
- str(fpath),
177
- parallel=False,
178
- concat_dim=self.states_coord,
179
- combine="nested",
180
- data_vars="minimal",
181
- coords="minimal",
182
- compat="override",
183
- )
184
- except ValueError as e:
185
- import_module("dask", hint="pip install dask")
186
- raise e
187
-
188
- self.__data_source = _read_ds()
189
-
190
- if sel is not None:
191
- self.__data_source = self.data_source.sel(self.sel)
192
- if isel is not None:
193
- self.__data_source = self.data_source.isel(self.isel)
194
- if pre_load:
195
- self.__data_source.load()
196
-
197
- self._get_inds(self.data_source)
198
-
199
202
  @property
200
203
  def data_source(self):
201
204
  """
@@ -207,56 +210,34 @@ class FieldDataNC(States):
207
210
  The data source
208
211
 
209
212
  """
210
- if self.pre_load and self.running:
213
+ if self.load_mode in ["preload", "fly"] and self.running:
211
214
  raise ValueError(
212
- f"States '{self.name}': Cannot access data_source while running"
215
+ f"States '{self.name}': Cannot access data_source while running for load mode '{self.load_mode}'"
213
216
  )
214
217
  return self.__data_source
215
218
 
216
- def _get_inds(self, ds):
217
- """
218
- Helper function for index and weights
219
- reading
220
- """
221
- for c in [self.states_coord, self.x_coord, self.y_coord, self.h_coord]:
222
- if not c in ds:
223
- raise KeyError(
224
- f"States '{self.name}': Missing coordinate '{c}' in data"
225
- )
226
-
227
- self.__inds = ds[self.states_coord].to_numpy()
228
- if self.time_format is not None:
229
- self.__inds = pd.to_datetime(
230
- self.__inds, format=self.time_format
231
- ).to_numpy()
232
- self._N = len(self.__inds)
233
-
234
- if self.weight_ncvar is not None:
235
- self.__weights = ds[self.weight_ncvar].to_numpy()
236
-
237
- for v in self.ovars:
238
- if v in self.var2ncvar:
239
- ncv = self.var2ncvar[v]
240
- if not ncv in ds:
241
- raise KeyError(
242
- f"States '{self.name}': nc variable '{ncv}' not found in data, found: {sorted(list(ds.keys()))}"
243
- )
244
- elif v not in self.fixed_vars:
245
- raise ValueError(
246
- f"States '{self.name}': Variable '{v}' neither found in var2ncvar not in fixed_vars"
247
- )
248
-
249
- def _get_data(self, ds, verbosity):
219
+ def _get_data(self, ds, coords, verbosity):
250
220
  """
251
221
  Helper function for data extraction
252
222
  """
253
- x = ds[self.x_coord].to_numpy()
254
- y = ds[self.y_coord].to_numpy()
255
- h = ds[self.h_coord].to_numpy()
256
- n_x = len(x)
257
- n_y = len(y)
258
- n_h = len(h)
259
- n_sts = ds.sizes[self.states_coord]
223
+ for ci, c in enumerate(coords):
224
+ found = False
225
+ if c is not None:
226
+ for v in ds.data_vars.values():
227
+ if c in v.dims:
228
+ found = True
229
+ break
230
+ if not found:
231
+ coords[ci] = None
232
+
233
+ dlst = []
234
+ for c in coords:
235
+ if c is not None:
236
+ dlst.append(np.atleast_1d(ds[c].to_numpy()))
237
+ else:
238
+ dlst.append(np.array([0], dtype=config.dtype_double))
239
+ sts, h, y, x = dlst
240
+ n_sts, n_h, n_y, n_x = [len(u) for u in dlst]
260
241
 
261
242
  cor_shxy = (self.states_coord, self.h_coord, self.x_coord, self.y_coord)
262
243
  cor_shyx = (self.states_coord, self.h_coord, self.y_coord, self.x_coord)
@@ -278,8 +259,13 @@ class FieldDataNC(States):
278
259
  elif ds[ncv].dims == cor_s:
279
260
  vars_s.append(v)
280
261
  else:
262
+ expc = [
263
+ c
264
+ for c in [cor_shxy, cor_shyx, cor_sxy, cor_syx, cor_sh, cor_s]
265
+ if None not in c
266
+ ]
281
267
  raise ValueError(
282
- f"States '{self.name}': Wrong coordinate order for variable '{ncv}': Found {ds[ncv].dims}, expecting {cor_shxy}, {cor_shyx}, {cor_sxy}, {cor_syx}, {cor_sh} or {cor_s}"
268
+ f"States '{self.name}': Wrong coordinates for variable '{ncv}': Found {ds[ncv].dims}, expecting one of {expc}"
283
269
  )
284
270
 
285
271
  data = np.zeros(
@@ -321,7 +307,7 @@ class FieldDataNC(States):
321
307
  f" {v}: {np.nanmin(d)} --> {np.nanmax(d)}, nans: {nn} ({100*nn/len(d.flat):.2f}%)"
322
308
  )
323
309
 
324
- return data
310
+ return sts, h, y, x, data
325
311
 
326
312
  def output_point_vars(self, algo):
327
313
  """
@@ -364,13 +350,129 @@ class FieldDataNC(States):
364
350
 
365
351
  """
366
352
 
367
- if (FV.WS in self.ovars and FV.WD not in self.ovars) or (
368
- FV.WS not in self.ovars and FV.WD in self.ovars
369
- ):
370
- raise KeyError(
371
- f"States '{self.name}': Missing '{FV.WS}' or '{FV.WD}' in output variables {self.ovars}"
353
+ # pre-load file reading:
354
+ coords = [self.states_coord, self.h_coord, self.y_coord, self.x_coord]
355
+ if not isinstance(self.data_source, xr.Dataset):
356
+
357
+ # check variables:
358
+ for v in self.ovars:
359
+ if v not in self.var2ncvar and v not in self.fixed_vars:
360
+ raise ValueError(
361
+ f"States '{self.name}': Variable '{v}' neither found in var2ncvar not in fixed_vars"
362
+ )
363
+ if (FV.WS in self.ovars and FV.WD not in self.ovars) or (
364
+ FV.WS not in self.ovars and FV.WD in self.ovars
365
+ ):
366
+ raise KeyError(
367
+ f"States '{self.name}': Missing '{FV.WS}' or '{FV.WD}' in output variables {self.ovars}"
368
+ )
369
+
370
+ # check static data:
371
+ fpath = get_input_path(self.data_source)
372
+ if "*" not in str(self.data_source):
373
+ if not fpath.is_file():
374
+ fpath = StaticData().get_file_path(
375
+ STATES, fpath.name, check_raw=False
376
+ )
377
+
378
+ # find bounds:
379
+ if self.x_coord is not None and self.x_coord not in self.sel:
380
+ xy_min, xy_max = algo.farm.get_xy_bounds(
381
+ extra_space=self.bounds_extra_space, algo=algo
382
+ )
383
+ if verbosity > 0:
384
+ print(
385
+ f"States '{self.name}': Restricting to bounds {xy_min} - {xy_max}"
386
+ )
387
+ self.sel.update(
388
+ {
389
+ self.x_coord: slice(xy_min[0], xy_max[1]),
390
+ self.y_coord: slice(xy_min[1], xy_max[1]),
391
+ }
392
+ )
393
+
394
+ # read files:
395
+ if verbosity > 0:
396
+ if self.load_mode == "preload":
397
+ print(
398
+ f"States '{self.name}': Reading data from '{self.data_source}'"
399
+ )
400
+ elif self.load_mode == "lazy":
401
+ print(
402
+ f"States '{self.name}': Reading header from '{self.data_source}'"
403
+ )
404
+ else:
405
+ print(
406
+ f"States '{self.name}': Reading states from '{self.data_source}'"
407
+ )
408
+ files = sorted(list(fpath.resolve().parent.glob(fpath.name)))
409
+ vars = list(self.var2ncvar.values())
410
+ if self.weight_ncvar is not None:
411
+ vars += [self.weight_ncvar]
412
+ self.__data_source = get_engine().map(
413
+ _read_nc_file,
414
+ files,
415
+ coords=coords,
416
+ weight_var=self.weight_ncvar,
417
+ vars=vars,
418
+ nc_engine=config.nc_engine,
419
+ isel=self.isel,
420
+ sel=self.sel,
421
+ minimal=self.load_mode == "fly",
372
422
  )
373
423
 
424
+ if self.load_mode in ["preload", "lazy"]:
425
+
426
+ if self.load_mode == "lazy":
427
+ try:
428
+ self.__data_source = [ds.chunk() for ds in self.__data_source]
429
+ except (ModuleNotFoundError, ValueError) as e:
430
+ import_module("dask")
431
+ raise e
432
+ self.__data_source = xr.concat(
433
+ self.__data_source,
434
+ dim=self.states_coord,
435
+ coords="minimal",
436
+ data_vars="minimal",
437
+ compat="equals",
438
+ join="exact",
439
+ combine_attrs="drop",
440
+ )
441
+ if self.load_mode == "preload":
442
+ self.__data_source.load()
443
+ if self.weight_ncvar is not None:
444
+ self.__weights = self.__data_source[self.weight_ncvar].to_numpy()
445
+ self.__inds = self.__data_source[self.states_coord].to_numpy()
446
+ self._N = len(self.__inds)
447
+
448
+ elif self.load_mode == "fly":
449
+ self.__inds, weights = zip(*self.__data_source)
450
+ self.__data_source = fpath
451
+ self._files_maxi = {f: len(inds) for f, inds in zip(files, self.__inds)}
452
+ self.__inds = np.concatenate(self.__inds, axis=0)
453
+ self._N = len(self.__inds)
454
+ if weights[0] is not None:
455
+ self.__weights = np.zeros(
456
+ (self._N, algo.n_turbines), dtype=config.dtype_double
457
+ )
458
+ self.__weights[:] = np.concatenate(weights, axis=0)[:, None]
459
+ del weights
460
+
461
+ else:
462
+ raise KeyError(
463
+ f"States '{self.name}': Unknown load_mode '{self.load_mode}', choices: preload, lazy, fly"
464
+ )
465
+
466
+ if self.time_format is not None:
467
+ self.__inds = pd.to_datetime(
468
+ self.__inds, format=self.time_format
469
+ ).to_numpy()
470
+
471
+ if self.__weights is None:
472
+ self.__weights = np.full(
473
+ (self._N, algo.n_turbines), 1.0 / self._N, dtype=config.dtype_double
474
+ )
475
+
374
476
  # ensure WD and WS get the first two slots of data:
375
477
  self._dkys = {}
376
478
  if FV.WS in self.ovars:
@@ -382,34 +484,25 @@ class FieldDataNC(States):
382
484
  self._dkys[v] = len(self._dkys)
383
485
  self._n_dvars = len(self._dkys)
384
486
 
385
- if self.__weights is None:
386
- self.__weights = np.full(
387
- (self._N, algo.n_turbines), 1.0 / self._N, dtype=config.dtype_double
388
- )
389
-
390
487
  idata = super().load_data(algo, verbosity)
391
488
 
392
- if self.pre_load:
489
+ if self.load_mode == "preload":
393
490
  self.X = self.var(FV.X)
394
491
  self.Y = self.var(FV.Y)
395
492
  self.H = self.var(FV.H)
396
493
  self.VARS = self.var("vars")
397
494
  self.DATA = self.var("data")
398
495
 
399
- ds = self.data_source
496
+ __, h, y, x, data = self._get_data(self.data_source, coords, verbosity)
497
+ self._prl_coords = coords
400
498
 
401
- h = ds[self.h_coord].to_numpy()
402
- y = ds[self.y_coord].to_numpy()
403
- x = ds[self.x_coord].to_numpy()
404
- v = list(self._dkys.keys())
405
499
  coos = (FC.STATE, self.H, self.Y, self.X, self.VARS)
406
- data = self._get_data(ds, verbosity)
407
500
  data = (coos, data)
408
501
 
409
502
  idata["coords"][self.H] = h
410
503
  idata["coords"][self.Y] = y
411
504
  idata["coords"][self.X] = x
412
- idata["coords"][self.VARS] = v
505
+ idata["coords"][self.VARS] = list(self._dkys.keys())
413
506
  idata["data_vars"][self.DATA] = data
414
507
 
415
508
  return idata
@@ -452,7 +545,7 @@ class FieldDataNC(States):
452
545
  )
453
546
  del self.__weights, self.__inds
454
547
 
455
- if self.pre_load:
548
+ if self.load_mode == "preload":
456
549
  data_stash[self.name]["data_source"] = self.__data_source
457
550
  del self.__data_source
458
551
 
@@ -489,7 +582,7 @@ class FieldDataNC(States):
489
582
  self.__weights = data.pop("weights")
490
583
  self.__inds = data.pop("inds")
491
584
 
492
- if self.pre_load:
585
+ if self.load_mode == "preload":
493
586
  self.__data_source = data.pop("data_source")
494
587
 
495
588
  def size(self):
@@ -589,26 +682,73 @@ class FieldDataNC(States):
589
682
  points = tdata[FC.TARGETS].reshape(n_states, n_targets * n_tpoints, 3)
590
683
  n_pts = points.shape[1]
591
684
  n_states = fdata.n_states
685
+ coords = [self.states_coord, self.h_coord, self.y_coord, self.x_coord]
592
686
 
593
- # pick pre-loaded data:
594
- if self.pre_load:
687
+ # case preload:
688
+ if self.load_mode == "preload":
595
689
  x = mdata[self.X]
596
690
  y = mdata[self.Y]
597
691
  h = mdata[self.H]
598
692
  data = mdata[self.DATA].copy()
693
+ coords = self._prl_coords
599
694
 
600
- # read data for this chunk:
601
- else:
695
+ # case lazy:
696
+ elif self.load_mode == "lazy":
602
697
  i0 = mdata.states_i0(counter=True)
603
698
  s = slice(i0, i0 + n_states)
604
699
  ds = self.data_source.isel({self.states_coord: s}).load()
700
+ __, h, y, x, data = self._get_data(ds, coords, verbosity=0)
701
+ del ds
605
702
 
606
- x = ds[self.x_coord].to_numpy()
607
- y = ds[self.y_coord].to_numpy()
608
- h = ds[self.h_coord].to_numpy()
609
- data = self._get_data(ds, verbosity=0)
703
+ # case fly:
704
+ elif self.load_mode == "fly":
705
+ vars = list(self.var2ncvar.values())
706
+ if self.weight_ncvar is not None:
707
+ vars += [self.weight_ncvar]
708
+
709
+ i0 = mdata.states_i0(counter=True)
710
+ i1 = i0 + n_states
711
+ j0 = 0
712
+ data = []
713
+ for fpath, n in self._files_maxi.items():
714
+ if i0 < j0:
715
+ break
716
+ else:
717
+ j1 = j0 + n
718
+ if i0 < j1:
719
+ a = i0 - j0
720
+ b = min(i1, j1) - j0
721
+ isel = copy(self.isel)
722
+ isel[self.states_coord] = slice(a, b)
723
+
724
+ data.append(
725
+ _read_nc_file(
726
+ fpath,
727
+ coords=coords,
728
+ weight_var=self.weight_ncvar,
729
+ vars=vars,
730
+ nc_engine=config.nc_engine,
731
+ isel=isel,
732
+ sel=self.sel,
733
+ minimal=False,
734
+ )
735
+ )
736
+
737
+ i0 += b - a
738
+ j0 = j1
739
+
740
+ assert (
741
+ i0 == i1
742
+ ), f"States '{self.name}': Missing states for load_mode '{self.load_mode}': (i0, i1) = {(i0, i1)}"
743
+
744
+ data = xr.concat(data, dim=self.states_coord)
745
+ __, h, y, x, data = self._get_data(data, coords, verbosity=0)
746
+
747
+ else:
748
+ raise KeyError(
749
+ f"States '{self.name}': Unknown load_mode '{self.load_mode}', choices: preload, lazy, fly"
750
+ )
610
751
 
611
- del ds
612
752
  n_h = len(h)
613
753
  n_y = len(y)
614
754
  n_x = len(x)
@@ -635,6 +775,11 @@ class FieldDataNC(States):
635
775
  pts = np.flip(pts, axis=1)
636
776
  gvars = (sts, h, y, x)
637
777
 
778
+ # reset None coordinate data, since that should not be interpolated:
779
+ for i, (c, g) in enumerate(zip(coords, gvars)):
780
+ if c is None:
781
+ pts[..., i] = g[0]
782
+
638
783
  # interpolate nan values:
639
784
  if self.interp_nans and np.any(np.isnan(data)):
640
785
  df = pd.DataFrame(
@@ -6,7 +6,7 @@ from scipy.interpolate import interp1d
6
6
  from foxes.core import States
7
7
  from foxes.utils import PandasFileHelper
8
8
  from foxes.data import STATES
9
- from foxes.config import config, get_path
9
+ from foxes.config import config, get_input_path
10
10
  from foxes.utils import wd2uv, uv2wd
11
11
  import foxes.variables as FV
12
12
  import foxes.constants as FC
@@ -198,7 +198,7 @@ class MultiHeightStates(States):
198
198
 
199
199
  """
200
200
  if not isinstance(self.data_source, pd.DataFrame):
201
- self._data_source = get_path(self.data_source)
201
+ self._data_source = get_input_path(self.data_source)
202
202
  if not self.data_source.is_file():
203
203
  if verbosity:
204
204
  print(
@@ -625,7 +625,7 @@ class MultiHeightNCStates(MultiHeightStates):
625
625
 
626
626
  """
627
627
  if not isinstance(self.data_source, Dataset):
628
- self._data_source = get_path(self.data_source)
628
+ self._data_source = get_input_path(self.data_source)
629
629
  if not self.data_source.is_file():
630
630
  if verbosity:
631
631
  print(
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  from foxes.core import States, VerticalProfile
7
7
  from foxes.utils import PandasFileHelper, read_tab_file
8
8
  from foxes.data import STATES
9
- from foxes.config import config, get_path
9
+ from foxes.config import config, get_input_path
10
10
  import foxes.variables as FV
11
11
  import foxes.constants as FC
12
12
 
@@ -215,7 +215,7 @@ class StatesTable(States):
215
215
  data = self.data_source
216
216
  isorg = True
217
217
  else:
218
- self._data_source = get_path(self.data_source)
218
+ self._data_source = get_input_path(self.data_source)
219
219
  if not self.data_source.is_file():
220
220
  if verbosity:
221
221
  print(
@@ -550,7 +550,7 @@ class TabStates(StatesTable):
550
550
  """
551
551
  if self.data_source is None:
552
552
  if self.__tab_data is None:
553
- self.__tab_source = get_path(self.__tab_source)
553
+ self.__tab_source = get_input_path(self.__tab_source)
554
554
  if not self.__tab_source.is_file():
555
555
  if verbosity:
556
556
  print(
@@ -1,3 +1,3 @@
1
- from .dict import read_dict, run_dict, run_outputs
1
+ from .dict import read_dict, run_dict, run_outputs, run_obj_function, get_output_obj
2
2
  from .yaml import foxes_yaml
3
3
  from .windio.windio import read_windio, foxes_windio