foxes 0.8.1__py3-none-any.whl → 1.0__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 (175) hide show
  1. docs/source/conf.py +353 -0
  2. examples/abl_states/run.py +160 -0
  3. examples/compare_rotors_pwakes/run.py +217 -0
  4. examples/compare_wakes/run.py +241 -0
  5. examples/dyn_wakes/run.py +311 -0
  6. examples/field_data_nc/run.py +121 -0
  7. examples/induction_RHB/run.py +201 -0
  8. examples/multi_height/run.py +113 -0
  9. examples/power_mask/run.py +249 -0
  10. examples/random_timeseries/run.py +210 -0
  11. examples/scan_row/run.py +193 -0
  12. examples/sector_management/run.py +162 -0
  13. examples/sequential/run.py +209 -0
  14. examples/single_state/run.py +201 -0
  15. examples/states_lookup_table/run.py +137 -0
  16. examples/streamline_wakes/run.py +138 -0
  17. examples/tab_file/run.py +142 -0
  18. examples/timelines/run.py +267 -0
  19. examples/timeseries/run.py +183 -0
  20. examples/timeseries_slurm/run.py +185 -0
  21. examples/wind_rose/run.py +141 -0
  22. examples/windio/run.py +29 -0
  23. examples/yawed_wake/run.py +196 -0
  24. foxes/__init__.py +4 -8
  25. foxes/algorithms/__init__.py +1 -1
  26. foxes/algorithms/downwind/downwind.py +232 -101
  27. foxes/algorithms/downwind/models/farm_wakes_calc.py +11 -6
  28. foxes/algorithms/downwind/models/init_farm_data.py +1 -1
  29. foxes/algorithms/downwind/models/point_wakes_calc.py +5 -6
  30. foxes/algorithms/downwind/models/reorder_farm_output.py +0 -1
  31. foxes/algorithms/downwind/models/set_amb_point_results.py +4 -2
  32. foxes/algorithms/iterative/iterative.py +73 -33
  33. foxes/algorithms/iterative/models/farm_wakes_calc.py +11 -6
  34. foxes/algorithms/sequential/models/plugin.py +1 -1
  35. foxes/algorithms/sequential/sequential.py +126 -255
  36. foxes/constants.py +17 -2
  37. foxes/core/__init__.py +1 -0
  38. foxes/core/algorithm.py +631 -146
  39. foxes/core/data.py +252 -20
  40. foxes/core/data_calc_model.py +13 -289
  41. foxes/core/engine.py +630 -0
  42. foxes/core/farm_controller.py +37 -9
  43. foxes/core/farm_data_model.py +15 -0
  44. foxes/core/model.py +133 -80
  45. foxes/core/point_data_model.py +15 -0
  46. foxes/core/rotor_model.py +27 -21
  47. foxes/core/states.py +16 -0
  48. foxes/core/turbine_type.py +28 -0
  49. foxes/core/wake_frame.py +22 -4
  50. foxes/core/wake_model.py +2 -3
  51. foxes/data/windio/windio_5turbines_timeseries.yaml +23 -1
  52. foxes/engines/__init__.py +16 -0
  53. foxes/engines/dask.py +975 -0
  54. foxes/engines/default.py +75 -0
  55. foxes/engines/futures.py +72 -0
  56. foxes/engines/mpi.py +38 -0
  57. foxes/engines/multiprocess.py +74 -0
  58. foxes/engines/numpy.py +185 -0
  59. foxes/engines/pool.py +263 -0
  60. foxes/engines/single.py +139 -0
  61. foxes/input/farm_layout/__init__.py +1 -0
  62. foxes/input/farm_layout/from_csv.py +4 -0
  63. foxes/input/farm_layout/from_json.py +1 -1
  64. foxes/input/farm_layout/grid.py +2 -2
  65. foxes/input/farm_layout/ring.py +65 -0
  66. foxes/input/farm_layout/row.py +2 -2
  67. foxes/input/states/__init__.py +6 -0
  68. foxes/input/states/create/random_abl_states.py +1 -1
  69. foxes/input/states/field_data_nc.py +157 -32
  70. foxes/input/states/multi_height.py +127 -13
  71. foxes/input/states/one_point_flow.py +577 -0
  72. foxes/input/states/scan_ws.py +73 -2
  73. foxes/input/states/states_table.py +204 -35
  74. foxes/input/windio/__init__.py +1 -1
  75. foxes/input/windio/get_states.py +44 -23
  76. foxes/input/windio/read_attributes.py +41 -16
  77. foxes/input/windio/read_farm.py +116 -102
  78. foxes/input/windio/read_fields.py +13 -6
  79. foxes/input/windio/read_outputs.py +63 -22
  80. foxes/input/windio/runner.py +31 -17
  81. foxes/input/windio/windio.py +36 -22
  82. foxes/models/ground_models/wake_mirror.py +8 -4
  83. foxes/models/model_book.py +29 -18
  84. foxes/models/partial_wakes/rotor_points.py +3 -3
  85. foxes/models/rotor_models/centre.py +4 -0
  86. foxes/models/rotor_models/grid.py +22 -23
  87. foxes/models/rotor_models/levels.py +4 -5
  88. foxes/models/turbine_models/calculator.py +0 -2
  89. foxes/models/turbine_models/lookup_table.py +27 -2
  90. foxes/models/turbine_models/rotor_centre_calc.py +4 -3
  91. foxes/models/turbine_models/set_farm_vars.py +103 -34
  92. foxes/models/turbine_types/PCt_file.py +24 -0
  93. foxes/models/turbine_types/PCt_from_two.py +24 -0
  94. foxes/models/turbine_types/__init__.py +1 -0
  95. foxes/models/turbine_types/lookup.py +316 -0
  96. foxes/models/turbine_types/null_type.py +50 -0
  97. foxes/models/turbine_types/wsrho2PCt_from_two.py +24 -0
  98. foxes/models/turbine_types/wsti2PCt_from_two.py +24 -0
  99. foxes/models/vertical_profiles/data_profile.py +1 -1
  100. foxes/models/wake_frames/__init__.py +1 -0
  101. foxes/models/wake_frames/dynamic_wakes.py +424 -0
  102. foxes/models/wake_frames/farm_order.py +23 -3
  103. foxes/models/wake_frames/rotor_wd.py +4 -2
  104. foxes/models/wake_frames/seq_dynamic_wakes.py +56 -63
  105. foxes/models/wake_frames/streamlines.py +19 -20
  106. foxes/models/wake_frames/timelines.py +328 -127
  107. foxes/models/wake_frames/yawed_wakes.py +4 -1
  108. foxes/models/wake_models/dist_sliced.py +1 -3
  109. foxes/models/wake_models/induction/rankine_half_body.py +4 -4
  110. foxes/models/wake_models/induction/rathmann.py +2 -2
  111. foxes/models/wake_models/induction/self_similar.py +2 -2
  112. foxes/models/wake_models/induction/vortex_sheet.py +2 -2
  113. foxes/models/wake_models/ti/iec_ti.py +34 -17
  114. foxes/models/wake_models/top_hat.py +1 -1
  115. foxes/models/wake_models/wind/bastankhah14.py +2 -2
  116. foxes/models/wake_models/wind/bastankhah16.py +8 -7
  117. foxes/models/wake_models/wind/jensen.py +1 -1
  118. foxes/models/wake_models/wind/turbopark.py +2 -2
  119. foxes/output/__init__.py +4 -1
  120. foxes/output/farm_layout.py +2 -2
  121. foxes/output/flow_plots_2d/__init__.py +0 -1
  122. foxes/output/flow_plots_2d/flow_plots.py +70 -30
  123. foxes/output/grids.py +91 -21
  124. foxes/output/seq_plugins/__init__.py +2 -0
  125. foxes/output/{flow_plots_2d → seq_plugins}/seq_flow_ani_plugin.py +62 -20
  126. foxes/output/seq_plugins/seq_wake_debug_plugin.py +145 -0
  127. foxes/output/slice_data.py +131 -111
  128. foxes/output/state_turbine_map.py +18 -13
  129. foxes/output/state_turbine_table.py +19 -19
  130. foxes/utils/__init__.py +1 -1
  131. foxes/utils/dev_utils.py +42 -0
  132. foxes/utils/dict.py +1 -1
  133. foxes/utils/factory.py +147 -52
  134. foxes/utils/pandas_helpers.py +4 -3
  135. foxes/utils/wind_dir.py +0 -2
  136. foxes/utils/xarray_utils.py +25 -13
  137. foxes/variables.py +37 -0
  138. {foxes-0.8.1.dist-info → foxes-1.0.dist-info}/METADATA +72 -34
  139. foxes-1.0.dist-info/RECORD +307 -0
  140. {foxes-0.8.1.dist-info → foxes-1.0.dist-info}/WHEEL +1 -1
  141. foxes-1.0.dist-info/top_level.txt +4 -0
  142. tests/0_consistency/iterative/test_iterative.py +92 -0
  143. tests/0_consistency/partial_wakes/test_partial_wakes.py +90 -0
  144. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +85 -0
  145. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +103 -0
  146. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +85 -0
  147. tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +87 -0
  148. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +82 -0
  149. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +82 -0
  150. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/flappy/run.py +92 -0
  151. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +93 -0
  152. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/flappy/run.py +92 -0
  153. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +96 -0
  154. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/flappy/run.py +94 -0
  155. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +122 -0
  156. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/flappy/run.py +94 -0
  157. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +122 -0
  158. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/flappy/run.py +92 -0
  159. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +93 -0
  160. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +85 -0
  161. tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +130 -0
  162. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/flappy/run.py +96 -0
  163. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +116 -0
  164. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +93 -0
  165. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +99 -0
  166. tests/3_examples/test_examples.py +34 -0
  167. foxes/VERSION +0 -1
  168. foxes/output/flow_plots_2d.py +0 -0
  169. foxes/utils/plotly_helpers.py +0 -19
  170. foxes/utils/runners/__init__.py +0 -1
  171. foxes/utils/runners/runners.py +0 -280
  172. foxes-0.8.1.dist-info/RECORD +0 -248
  173. foxes-0.8.1.dist-info/top_level.txt +0 -1
  174. foxes-0.8.1.dist-info/zip-safe +0 -1
  175. {foxes-0.8.1.dist-info → foxes-1.0.dist-info}/LICENSE +0 -0
@@ -0,0 +1,139 @@
1
+ from xarray import Dataset
2
+
3
+ import foxes.constants as FC
4
+ from foxes.core import Engine
5
+
6
+
7
+ class SingleChunkEngine(Engine):
8
+ """
9
+ Runs computations in a single chunk.
10
+
11
+ :group: engines
12
+
13
+ """
14
+
15
+ def __init__(self, *args, **kwargs):
16
+ """
17
+ Constructor.
18
+
19
+ Parameters
20
+ ----------
21
+ args: tuple, optional
22
+ Additional parameters for the base class
23
+ kwargs: dict, optional
24
+ Additional parameters for the base class
25
+
26
+ """
27
+ ignr = ["chunk_size_states", "chunk_size_points", "n_procs"]
28
+ for k in ignr:
29
+ if kwargs.pop(k, None) is not None and kwargs.get("verbosity", 1) > 1:
30
+ print(f"{type(self).__name__}: Ignoring {k}")
31
+ super().__init__(
32
+ *args,
33
+ chunk_size_states=None,
34
+ chunk_size_points=None,
35
+ n_procs=1,
36
+ **kwargs,
37
+ )
38
+
39
+ def __repr__(self):
40
+ return f"{type(self).__name__}()"
41
+
42
+ def run_calculation(
43
+ self,
44
+ algo,
45
+ model,
46
+ model_data=None,
47
+ farm_data=None,
48
+ point_data=None,
49
+ out_vars=[],
50
+ chunk_store={},
51
+ sel=None,
52
+ isel=None,
53
+ iterative=False,
54
+ **calc_pars,
55
+ ):
56
+ """
57
+ Runs the model calculation
58
+
59
+ Parameters
60
+ ----------
61
+ algo: foxes.core.Algorithm
62
+ The algorithm object
63
+ model: foxes.core.DataCalcModel
64
+ The model that whose calculate function
65
+ should be run
66
+ model_data: xarray.Dataset
67
+ The initial model data
68
+ farm_data: xarray.Dataset
69
+ The initial farm data
70
+ point_data: xarray.Dataset
71
+ The initial point data
72
+ out_vars: list of str, optional
73
+ Names of the output variables
74
+ chunk_store: foxes.utils.Dict
75
+ The chunk store
76
+ sel: dict, optional
77
+ Selection of coordinate subsets
78
+ isel: dict, optional
79
+ Selection of coordinate subsets index values
80
+ iterative: bool
81
+ Flag for use within the iterative algorithm
82
+ calc_pars: dict, optional
83
+ Additional parameters for the model.calculate()
84
+
85
+ Returns
86
+ -------
87
+ results: xarray.Dataset
88
+ The model results
89
+
90
+ """
91
+ # subset selection:
92
+ model_data, farm_data, point_data = self.select_subsets(
93
+ model_data, farm_data, point_data, sel=sel, isel=isel
94
+ )
95
+
96
+ # basic checks:
97
+ super().run_calculation(algo, model, model_data, farm_data, point_data)
98
+
99
+ # prepare:
100
+ n_states = model_data.sizes[FC.STATE]
101
+ n_targets = point_data.sizes[FC.TARGET] if point_data is not None else 0
102
+ out_coords = model.output_coords()
103
+ coords = {}
104
+ if FC.STATE in out_coords and FC.STATE in model_data.coords:
105
+ coords[FC.STATE] = model_data[FC.STATE].to_numpy()
106
+ if farm_data is None:
107
+ farm_data = Dataset()
108
+ goal_data = farm_data if point_data is None else point_data
109
+ algo.reset_chunk_store(chunk_store)
110
+
111
+ # calculate:
112
+
113
+ if n_states > 1:
114
+ self.print(f"Running single chunk calculation for {n_states} states")
115
+
116
+ data = self.get_chunk_input_data(
117
+ algo=algo,
118
+ model_data=model_data,
119
+ farm_data=farm_data,
120
+ point_data=point_data,
121
+ states_i0_i1=(0, n_states),
122
+ targets_i0_i1=(0, n_targets),
123
+ out_vars=out_vars,
124
+ )
125
+
126
+ results = {}
127
+ results[(0, 0)] = (model.calculate(algo, *data, **calc_pars), algo.chunk_store)
128
+
129
+ return self.combine_results(
130
+ algo=algo,
131
+ results=results,
132
+ model_data=model_data,
133
+ out_vars=out_vars,
134
+ out_coords=out_coords,
135
+ n_chunks_states=1,
136
+ n_chunks_targets=1,
137
+ goal_data=goal_data,
138
+ iterative=iterative,
139
+ )
@@ -9,3 +9,4 @@ from .from_csv import add_from_csv
9
9
  from .from_file import add_from_file
10
10
  from .from_df import add_from_df
11
11
  from .from_random import add_random
12
+ from .ring import add_ring
@@ -14,6 +14,7 @@ def add_from_csv(
14
14
  col_D=None,
15
15
  col_id=None,
16
16
  cols_models_pre=None,
17
+ col_turbine_type=None,
17
18
  cols_models_post=None,
18
19
  turbine_base_name="T",
19
20
  turbine_ids=None,
@@ -47,6 +48,8 @@ def add_from_csv(
47
48
  cols_models_pre: list of str, optional
48
49
  The turbine model columns, entered before
49
50
  turbine_models
51
+ col_turbine_type: str, optional
52
+ The turbine type name
50
53
  cols_models_post: list of str, optional
51
54
  The turbine model columns, entered after
52
55
  turbine_models
@@ -95,6 +98,7 @@ def add_from_csv(
95
98
  hmodels = (
96
99
  [] if cols_models_pre is None else data.loc[i, cols_models_pre].tolist()
97
100
  )
101
+ hmodels += [] if col_turbine_type is None else [data.loc[i, col_turbine_type]]
98
102
  hmodels += tmodels
99
103
  hmodels += (
100
104
  [] if cols_models_post is None else data.loc[i, cols_models_post].tolist()
@@ -60,7 +60,7 @@ def add_from_json(
60
60
  xy=np.array([wdict["UTMX"], wdict["UTMY"]]),
61
61
  index=wdict.get("id", None),
62
62
  name=wt_name,
63
- **pars
63
+ **pars,
64
64
  ),
65
65
  verbosity=verbosity,
66
66
  )
@@ -12,7 +12,7 @@ def add_grid(
12
12
  indices=None,
13
13
  names=None,
14
14
  verbosity=1,
15
- **turbine_parameters
15
+ **turbine_parameters,
16
16
  ):
17
17
  """
18
18
  Add a regular grid of turbines.
@@ -54,7 +54,7 @@ def add_grid(
54
54
  xy=xy_base + xi * step_vectors[0] + yi * step_vectors[1],
55
55
  index=None if indices is None else indices[i],
56
56
  name=None if names is None else names[i],
57
- **turbine_parameters
57
+ **turbine_parameters,
58
58
  ),
59
59
  verbosity=verbosity,
60
60
  )
@@ -0,0 +1,65 @@
1
+ import numpy as np
2
+
3
+ from foxes.core import Turbine
4
+ from foxes.utils import wd2wdvec
5
+
6
+
7
+ def add_ring(
8
+ farm,
9
+ xy_base,
10
+ dist,
11
+ n_turbines,
12
+ offset_deg=0,
13
+ indices=None,
14
+ names=None,
15
+ verbosity=1,
16
+ **turbine_parameters,
17
+ ):
18
+ """
19
+ Add a ring of turbines.
20
+
21
+ Parameters
22
+ ----------
23
+ farm: foxes.WindFarm
24
+ The wind farm
25
+ xy_base: numpy.ndarray
26
+ The base point, shape: (2,)
27
+ dist: float
28
+ The distance between turbines
29
+ n_turbines: int
30
+ The number of turbines
31
+ offset_deg: float
32
+ The offset from north in degrees,
33
+ following wind direction conventions
34
+ indices: list of int, optional
35
+ The turbine indices
36
+ names: list of str, optional
37
+ The turbine names
38
+ verbosity: int
39
+ The verbosity level, 0 = silent
40
+ turbine_parameters: dict, optional
41
+ Parameters forwarded to `foxes.core.Turbine`
42
+
43
+ :group: input.farm_layout
44
+
45
+ """
46
+ p0 = np.array(xy_base)
47
+ R = n_turbines * dist / (2 * np.pi)
48
+ a = np.atleast_1d(offset_deg)
49
+ da = 360 / n_turbines
50
+
51
+ for i in range(n_turbines):
52
+
53
+ n = wd2wdvec(a)[0]
54
+
55
+ farm.add_turbine(
56
+ Turbine(
57
+ xy=p0 + R * n,
58
+ index=None if indices is None else indices[i],
59
+ name=None if names is None else names[i],
60
+ **turbine_parameters,
61
+ ),
62
+ verbosity=verbosity,
63
+ )
64
+
65
+ a[0] += da
@@ -11,7 +11,7 @@ def add_row(
11
11
  indices=None,
12
12
  names=None,
13
13
  verbosity=1,
14
- **turbine_parameters
14
+ **turbine_parameters,
15
15
  ):
16
16
  """
17
17
  Add a single row of turbines.
@@ -47,7 +47,7 @@ def add_row(
47
47
  xy=p0 + i * delta,
48
48
  index=None if indices is None else indices[i],
49
49
  name=None if names is None else names[i],
50
- **turbine_parameters
50
+ **turbine_parameters,
51
51
  ),
52
52
  verbosity=verbosity,
53
53
  )
@@ -8,5 +8,11 @@ from .states_table import StatesTable, Timeseries, TabStates
8
8
  from .field_data_nc import FieldDataNC
9
9
  from .multi_height import MultiHeightStates, MultiHeightTimeseries
10
10
  from .multi_height import MultiHeightNCStates, MultiHeightNCTimeseries
11
+ from .one_point_flow import (
12
+ OnePointFlowStates,
13
+ OnePointFlowTimeseries,
14
+ OnePointFlowMultiHeightTimeseries,
15
+ OnePointFlowMultiHeightNCTimeseries,
16
+ )
11
17
 
12
18
  from . import create
@@ -67,7 +67,7 @@ def write_random_abl_states(
67
67
  normalize=True,
68
68
  verbosity=1,
69
69
  digits="auto",
70
- **kwargs
70
+ **kwargs,
71
71
  ):
72
72
  """
73
73
  Writes random abl states to file
@@ -2,9 +2,10 @@ import numpy as np
2
2
  import pandas as pd
3
3
  import xarray as xr
4
4
  from scipy.interpolate import interpn
5
+ from pathlib import Path
5
6
 
6
7
  from foxes.core import States
7
- from foxes.utils import wd2uv, uv2wd
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
@@ -120,7 +121,6 @@ class FieldDataNC(States):
120
121
  """
121
122
  super().__init__()
122
123
 
123
- self.data_source = data_source
124
124
  self.states_coord = states_coord
125
125
  self.ovars = output_vars
126
126
  self.fixed_vars = fixed_vars
@@ -139,16 +139,18 @@ class FieldDataNC(States):
139
139
  v: var2ncvar.get(v, v) for v in output_vars if v not in fixed_vars
140
140
  }
141
141
 
142
- self._inds = None
143
142
  self._N = None
144
- self._weights = None
145
143
 
146
- # pre-load file reading, usually prior to DaskRunner:
144
+ self.__data_source = data_source
145
+ self.__weights = None
146
+ self.__inds = None
147
+
148
+ # pre-load file reading:
147
149
  if not isinstance(self.data_source, xr.Dataset):
148
150
  if "*" in str(self.data_source):
149
151
  pass
150
152
  else:
151
- self.data_source = StaticData().get_file_path(
153
+ self.__data_source = StaticData().get_file_path(
152
154
  STATES, self.data_source, check_raw=True
153
155
  )
154
156
  if verbosity:
@@ -161,26 +163,54 @@ class FieldDataNC(States):
161
163
  f"States '{self.name}': Reading index from '{self.data_source}'"
162
164
  )
163
165
 
164
- with xr.open_mfdataset(
165
- str(self.data_source),
166
- parallel=False,
167
- concat_dim=self.states_coord,
168
- combine="nested",
169
- data_vars="minimal",
170
- coords="minimal",
171
- compat="override",
172
- ) as ds:
173
- self.data_source = ds
166
+ def _read_ds():
167
+ if Path(self.data_source).is_file():
168
+ return xr.open_dataset(self.data_source)
169
+ else:
170
+ # try to read multiple files, needs dask:
171
+ try:
172
+ return xr.open_mfdataset(
173
+ str(self.data_source),
174
+ parallel=False,
175
+ concat_dim=self.states_coord,
176
+ combine="nested",
177
+ data_vars="minimal",
178
+ coords="minimal",
179
+ compat="override",
180
+ )
181
+ except ValueError as e:
182
+ import_module("dask", hint="pip install dask")
183
+ raise e
184
+
185
+ with _read_ds() as ds:
186
+ self.__data_source = ds
174
187
 
175
188
  if sel is not None:
176
- self.data_source = self.data_source.sel(self.sel)
189
+ self.__data_source = self.data_source.sel(self.sel)
177
190
  if isel is not None:
178
- self.data_source = self.data_source.isel(self.isel)
191
+ self.__data_source = self.data_source.isel(self.isel)
179
192
  if pre_load:
180
- self.data_source.load()
193
+ self.__data_source.load()
181
194
 
182
195
  self._get_inds(self.data_source)
183
196
 
197
+ @property
198
+ def data_source(self):
199
+ """
200
+ The data source
201
+
202
+ Returns
203
+ -------
204
+ s: object
205
+ The data source
206
+
207
+ """
208
+ if self.pre_load and self.running:
209
+ raise ValueError(
210
+ f"States '{self.name}': Cannot acces data_source while running"
211
+ )
212
+ return self.__data_source
213
+
184
214
  def _get_inds(self, ds):
185
215
  """
186
216
  Helper function for index and weights
@@ -192,13 +222,15 @@ class FieldDataNC(States):
192
222
  f"States '{self.name}': Missing coordinate '{c}' in data"
193
223
  )
194
224
 
195
- self._inds = ds[self.states_coord].to_numpy()
225
+ self.__inds = ds[self.states_coord].to_numpy()
196
226
  if self.time_format is not None:
197
- self._inds = pd.to_datetime(self._inds, format=self.time_format).to_numpy()
198
- self._N = len(self._inds)
227
+ self.__inds = pd.to_datetime(
228
+ self.__inds, format=self.time_format
229
+ ).to_numpy()
230
+ self._N = len(self.__inds)
199
231
 
200
232
  if self.weight_ncvar is not None:
201
- self._weights = ds[self.weight_ncvar].to_numpy()
233
+ self.__weights = ds[self.weight_ncvar].to_numpy()
202
234
 
203
235
  for v in self.ovars:
204
236
  if v in self.var2ncvar:
@@ -331,8 +363,8 @@ class FieldDataNC(States):
331
363
  self._dkys[v] = len(self._dkys)
332
364
  self._n_dvars = len(self._dkys)
333
365
 
334
- if self._weights is None:
335
- self._weights = np.full(
366
+ if self.__weights is None:
367
+ self.__weights = np.full(
336
368
  (self._N, algo.n_turbines), 1.0 / self._N, dtype=FC.DTYPE
337
369
  )
338
370
 
@@ -363,6 +395,84 @@ class FieldDataNC(States):
363
395
 
364
396
  return idata
365
397
 
398
+ def set_running(
399
+ self,
400
+ algo,
401
+ data_stash,
402
+ sel=None,
403
+ isel=None,
404
+ verbosity=0,
405
+ ):
406
+ """
407
+ Sets this model status to running, and moves
408
+ all large data to stash.
409
+
410
+ The stashed data will be returned by the
411
+ unset_running() function after running calculations.
412
+
413
+ Parameters
414
+ ----------
415
+ algo: foxes.core.Algorithm
416
+ The calculation algorithm
417
+ data_stash: dict
418
+ Large data stash, this function adds data here.
419
+ Key: model name. Value: dict, large model data
420
+ sel: dict, optional
421
+ The subset selection dictionary
422
+ isel: dict, optional
423
+ The index subset selection dictionary
424
+ verbosity: int
425
+ The verbosity level, 0 = silent
426
+
427
+ """
428
+ super().set_running(algo, data_stash, sel, isel, verbosity)
429
+
430
+ data_stash[self.name] = dict(
431
+ weights=self.__weights,
432
+ inds=self.__inds,
433
+ )
434
+ del self.__weights, self.__inds
435
+
436
+ if self.pre_load:
437
+ data_stash[self.name]["data_source"] = self.__data_source
438
+ del self.__data_source
439
+
440
+ def unset_running(
441
+ self,
442
+ algo,
443
+ data_stash,
444
+ sel=None,
445
+ isel=None,
446
+ verbosity=0,
447
+ ):
448
+ """
449
+ Sets this model status to not running, recovering large data
450
+ from stash
451
+
452
+ Parameters
453
+ ----------
454
+ algo: foxes.core.Algorithm
455
+ The calculation algorithm
456
+ data_stash: dict
457
+ Large data stash, this function adds data here.
458
+ Key: model name. Value: dict, large model data
459
+ sel: dict, optional
460
+ The subset selection dictionary
461
+ isel: dict, optional
462
+ The index subset selection dictionary
463
+ verbosity: int
464
+ The verbosity level, 0 = silent
465
+
466
+ """
467
+ super().unset_running(algo, data_stash, sel, isel, verbosity)
468
+
469
+ data = data_stash[self.name]
470
+ self.__weights = data.pop("weights")
471
+ self.__inds = data.pop("inds")
472
+
473
+ if self.pre_load:
474
+ self.__data_source = data.pop("data_source")
475
+
366
476
  def size(self):
367
477
  """
368
478
  The total number of states.
@@ -385,7 +495,9 @@ class FieldDataNC(States):
385
495
  The index labels of states, or None for default integers
386
496
 
387
497
  """
388
- return self._inds
498
+ if self.running:
499
+ raise ValueError(f"States '{self.name}': Cannot acces index while running")
500
+ return self.__inds
389
501
 
390
502
  def output_point_vars(self, algo):
391
503
  """
@@ -419,7 +531,11 @@ class FieldDataNC(States):
419
531
  The weights, shape: (n_states, n_turbines)
420
532
 
421
533
  """
422
- return self._weights
534
+ if self.running:
535
+ raise ValueError(
536
+ f"States '{self.name}': Cannot acces weights while running"
537
+ )
538
+ return self.__weights
423
539
 
424
540
  def calculate(self, algo, mdata, fdata, tdata):
425
541
  """ "
@@ -464,7 +580,7 @@ class FieldDataNC(States):
464
580
 
465
581
  # read data for this chunk:
466
582
  else:
467
- i0 = np.where(self._inds == mdata[FC.STATE][0])[0][0]
583
+ i0 = mdata.states_i0(counter=True)
468
584
  s = slice(i0, i0 + n_states)
469
585
  ds = self.data_source.isel({self.states_coord: s}).load()
470
586
 
@@ -520,17 +636,26 @@ class FieldDataNC(States):
520
636
 
521
637
  # interpolate:
522
638
  try:
523
- data = interpn(gvars, data, pts, **self.interpn_pars).reshape(
639
+ ipars = dict(bounds_error=True, fill_value=None)
640
+ ipars.update(self.interpn_pars)
641
+ data = interpn(gvars, data, pts, **ipars).reshape(
524
642
  n_states, n_pts, self._n_dvars
525
643
  )
526
644
  except ValueError as e:
527
- print(f"\n\nStates '{self.name}': Interpolation error")
645
+ print(f"\nStates '{self.name}': Interpolation error")
528
646
  print("INPUT VARS: (state, heights, y, x)")
529
647
  print(
530
- "DATA BOUNDS:", [np.min(d) for d in gvars], [np.max(d) for d in gvars]
648
+ "DATA BOUNDS:",
649
+ [float(np.min(d)) for d in gvars],
650
+ [float(np.max(d)) for d in gvars],
651
+ )
652
+ print(
653
+ "EVAL BOUNDS:",
654
+ [float(np.min(p)) for p in pts.T],
655
+ [float(np.max(p)) for p in pts.T],
531
656
  )
532
657
  print(
533
- "EVAL BOUNDS:", [np.min(p) for p in pts.T], [np.max(p) for p in pts.T]
658
+ "\nMaybe you want to try the option 'bounds_error=False'? This will extrapolate the data.\n"
534
659
  )
535
660
  raise e
536
661
  del pts, x, y, h, gvars