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,577 @@
1
+ import numpy as np
2
+ from scipy.interpolate import interpn
3
+
4
+ from foxes.core import States
5
+ from foxes.utils import uv2wd
6
+ from foxes.models.wake_frames.timelines import Timelines
7
+ import foxes.variables as FV
8
+ import foxes.constants as FC
9
+
10
+
11
+ class OnePointFlowStates(States):
12
+ """
13
+ Time-evolving states based on horizontally
14
+ homogeneous timeseries data
15
+
16
+ Attributes
17
+ ----------
18
+ ref_xy: list of float
19
+ The [x, y] or [x, y, z] coordinates of the base states.
20
+ If [x, y, z] then z will serve as height
21
+ tl_heights: list of float
22
+ The heights at which timelines will be calculated
23
+ dt_min: float
24
+ The delta t value in minutes,
25
+ if not from timeseries data
26
+ intp_pars: dict
27
+ Parameters for height interpolation with
28
+ scipy.interpolate.interpn
29
+
30
+ :group: input.states
31
+
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ ref_xy,
37
+ *base_states_args,
38
+ base_states=None,
39
+ tl_heights=None,
40
+ dt_min=None,
41
+ **base_states_kwargs,
42
+ ):
43
+ """
44
+ Constructor.
45
+
46
+ Parameters
47
+ ----------
48
+ ref_xy: list of float
49
+ The [x, y] or [x, y, z] coordinates of the base states.
50
+ If [x, y, z] then z will serve as height
51
+ base_states_args: tuple, optional
52
+ Arguments for creating the base states from
53
+ States.new(), if not given as base_states
54
+ base_states: foxes.core.States, optional
55
+ The base states, representing horizontally
56
+ homogeneous inflow
57
+ tl_heights: list of float, optional
58
+ The heights at which timelines will be calculated
59
+ dt_min: float, optional
60
+ The delta t value in minutes,
61
+ if not from timeseries data
62
+ base_states_kwargs: dict, optional
63
+ Arguments for creating the base states from
64
+ States.new(), if not given as base_states
65
+
66
+ """
67
+ super().__init__()
68
+ self.ref_xy = np.array(ref_xy, dtype=FC.DTYPE)
69
+ self.heights = tl_heights
70
+ self.base_states = base_states
71
+ self.dt_min = dt_min
72
+
73
+ self.intp_pars = {"fill_value": None}
74
+ if "bounds_error" in base_states_kwargs:
75
+ self.intp_pars["bounds_error"] = base_states_kwargs["bounds_error"]
76
+
77
+ if base_states is not None and len(base_states_kwargs):
78
+ raise KeyError(
79
+ f"Base states of type '{type(base_states).__name__}' were given, cannot handle base_state_pars {list(base_states_kwargs.keys())}"
80
+ )
81
+ elif base_states is not None and len(base_states_args):
82
+ raise KeyError(
83
+ f"Base states of type '{type(base_states).__name__}' were given, cannot handle base_states_args of types {[type(a).__name__ for a in base_states_args]}"
84
+ )
85
+ elif base_states is None:
86
+ self.base_states = States.new(*base_states_args, **base_states_kwargs)
87
+
88
+ def __repr__(self):
89
+ return f"{type(self).__name__}(base={type(self.base_states).__name__}, heights={self.heights}, dt_min={self.dt_min})"
90
+
91
+ def sub_models(self):
92
+ """
93
+ List of all sub-models
94
+
95
+ Returns
96
+ -------
97
+ smdls: list of foxes.core.Model
98
+ All sub models
99
+
100
+ """
101
+ return [self.base_states]
102
+
103
+ def initialize(self, algo, verbosity=0):
104
+ """
105
+ Initializes the model.
106
+
107
+ Parameters
108
+ ----------
109
+ algo: foxes.core.Algorithm
110
+ The calculation algorithm
111
+ verbosity: int
112
+ The verbosity level, 0 = silent
113
+
114
+ """
115
+ super().initialize(algo, verbosity)
116
+
117
+ # find heights:
118
+ if self.heights is None:
119
+ if hasattr(self.base_states, "heights"):
120
+ self.heights = self.base_states.heights
121
+ elif len(self.ref_xy) > 2:
122
+ self.heights = [self.ref_xy[2]]
123
+ else:
124
+ raise KeyError(
125
+ f"Cannot find 'heights' in base states of type '{type(self.base_states).__name__}', missing either `ref_xy` of type [x, y, z], or explicit value list via parameter 'tl_heights'"
126
+ )
127
+
128
+ # pre-calc data:
129
+ Timelines._precalc_data(
130
+ self, algo, self.base_states, self.heights, verbosity, needs_res=True
131
+ )
132
+
133
+ def size(self):
134
+ """
135
+ The total number of states.
136
+
137
+ Returns
138
+ -------
139
+ int:
140
+ The total number of states
141
+
142
+ """
143
+ return self.base_states.size()
144
+
145
+ def index(self):
146
+ """
147
+ The index list
148
+
149
+ Returns
150
+ -------
151
+ indices: array_like
152
+ The index labels of states, or None for default integers
153
+
154
+ """
155
+ return self.base_states.index()
156
+
157
+ def output_point_vars(self, algo):
158
+ """
159
+ The variables which are being modified by the model.
160
+
161
+ Parameters
162
+ ----------
163
+ algo: foxes.core.Algorithm
164
+ The calculation algorithm
165
+
166
+ Returns
167
+ -------
168
+ output_vars: list of str
169
+ The output variable names
170
+
171
+ """
172
+ return self.base_states.output_point_vars(algo)
173
+
174
+ def weights(self, algo):
175
+ """
176
+ The statistical weights of all states.
177
+
178
+ Parameters
179
+ ----------
180
+ algo: foxes.core.Algorithm
181
+ The calculation algorithm
182
+
183
+ Returns
184
+ -------
185
+ weights: numpy.ndarray
186
+ The weights, shape: (n_states, n_turbines)
187
+
188
+ """
189
+ return self.base_states.weights(algo)
190
+
191
+ def set_running(
192
+ self,
193
+ algo,
194
+ data_stash,
195
+ sel=None,
196
+ isel=None,
197
+ verbosity=0,
198
+ ):
199
+ """
200
+ Sets this model status to running, and moves
201
+ all large data to stash.
202
+
203
+ The stashed data will be returned by the
204
+ unset_running() function after running calculations.
205
+
206
+ Parameters
207
+ ----------
208
+ algo: foxes.core.Algorithm
209
+ The calculation algorithm
210
+ data_stash: dict
211
+ Large data stash, this function adds data here.
212
+ Key: model name. Value: dict, large model data
213
+ sel: dict, optional
214
+ The subset selection dictionary
215
+ isel: dict, optional
216
+ The index subset selection dictionary
217
+ verbosity: int
218
+ The verbosity level, 0 = silent
219
+
220
+ """
221
+ super().set_running(algo, data_stash, sel, isel, verbosity)
222
+
223
+ if sel is not None or isel is not None:
224
+ data_stash[self.name]["data"] = self.timelines_data
225
+
226
+ if isel is not None:
227
+ self.timelines_data = self.timelines_data.isel(isel)
228
+ if sel is not None:
229
+ self.timelines_data = self.timelines_data.sel(sel)
230
+
231
+ def unset_running(
232
+ self,
233
+ algo,
234
+ data_stash,
235
+ sel=None,
236
+ isel=None,
237
+ verbosity=0,
238
+ ):
239
+ """
240
+ Sets this model status to not running, recovering large data
241
+ from stash
242
+
243
+ Parameters
244
+ ----------
245
+ algo: foxes.core.Algorithm
246
+ The calculation algorithm
247
+ data_stash: dict
248
+ Large data stash, this function adds data here.
249
+ Key: model name. Value: dict, large model data
250
+ sel: dict, optional
251
+ The subset selection dictionary
252
+ isel: dict, optional
253
+ The index subset selection dictionary
254
+ verbosity: int
255
+ The verbosity level, 0 = silent
256
+
257
+ """
258
+ super().unset_running(algo, data_stash, sel, isel, verbosity)
259
+
260
+ data = data_stash[self.name]
261
+ if "data" in data:
262
+ self.timelines_data = data.pop("data")
263
+
264
+ def calc_states_indices(self, algo, mdata, points, hi, ref_xy):
265
+
266
+ n_states, n_points = points.shape[:2]
267
+ dxy = self.timelines_data["dxy"].to_numpy()[hi]
268
+
269
+ i0 = mdata.states_i0(counter=True)
270
+ trace_p = points[:, :, :2] - ref_xy[:, :, :2]
271
+ trace_si = np.zeros((n_states, n_points), dtype=FC.ITYPE)
272
+ trace_si[:] = i0 + np.arange(n_states)[:, None]
273
+ coeffs = np.full((n_states, n_points), np.nan, dtype=FC.DTYPE)
274
+
275
+ # flake8: noqa: F821
276
+ def _eval_trace(sel, hdxy=None, hdxy0=None, trs=None):
277
+ """Helper function that updates trace_done"""
278
+ nonlocal coeffs
279
+
280
+ # project onto local x direction:
281
+ hdxy0 = dxy[trace_si[sel]] if hdxy0 is None else hdxy0
282
+ nx = hdxy0 / np.linalg.norm(hdxy0, axis=-1)[..., None]
283
+ projx = np.einsum("...d,...d->...", trace_p[sel], nx)
284
+
285
+ # check for local points:
286
+ if hdxy is None:
287
+ seld = (projx >= 1e-10) & (projx <= 1e-10)
288
+ if np.any(seld):
289
+ coeffs[sel] = np.where(seld, -1, coeffs[sel])
290
+
291
+ # check for vicinity to reference plane:
292
+ else:
293
+ lx = np.einsum("...d,...d->...", hdxy, nx)
294
+ seld = ((lx < 0) & (projx >= lx) & (projx <= 0)) | (
295
+ (lx > 0) & (projx >= 0) & (projx <= lx)
296
+ )
297
+ if np.any(seld):
298
+ w = projx / np.abs(lx)
299
+ coeffs[sel] = np.where(seld, w, coeffs[sel])
300
+
301
+ # step backwards in time, until projection onto axis is negative:
302
+ _eval_trace(sel=np.s_[:])
303
+ sel = np.isnan(coeffs)
304
+ tshift = 0
305
+ while np.any(sel):
306
+
307
+ trs = trace_si[sel]
308
+ hdxy = -dxy[trs]
309
+ trace_p[sel] += hdxy
310
+
311
+ _eval_trace(sel, hdxy=hdxy, hdxy0=dxy[trs - tshift])
312
+
313
+ tshift -= 1
314
+ sel0 = np.isnan(coeffs)
315
+ trace_si[sel0 & sel] -= 1
316
+ sel = sel0 & (trace_si >= 0)
317
+
318
+ del trs, sel0, hdxy
319
+
320
+ # step forwards in time, until projection onto axis is positive:
321
+ sel = np.isnan(coeffs)
322
+ if np.any(sel):
323
+ trace_p = np.where(
324
+ sel[:, :, None], points[:, :, :2] - ref_xy[:, :, :2], trace_p
325
+ )
326
+ trace_si = np.where(sel, i0 + np.arange(n_states)[:, None] + 1, trace_si)
327
+
328
+ sel &= trace_si < algo.n_states
329
+ tshift = 1
330
+ while np.any(sel):
331
+
332
+ trs = trace_si[sel]
333
+ hdxy = dxy[trs]
334
+ trace_p[sel] += hdxy
335
+
336
+ _eval_trace(sel, hdxy=hdxy, hdxy0=dxy[trs - tshift])
337
+
338
+ tshift += 1
339
+ sel0 = np.isnan(coeffs)
340
+ trace_si[sel0 & sel] += 1
341
+ sel = sel0 & (trace_si < algo.n_states)
342
+
343
+ del trs, sel0, hdxy
344
+
345
+ return trace_si, coeffs
346
+
347
+ def calculate(self, algo, mdata, fdata, tdata):
348
+ """
349
+ The main model calculation.
350
+
351
+ This function is executed on a single chunk of data,
352
+ all computations should be based on numpy arrays.
353
+
354
+ Parameters
355
+ ----------
356
+ algo: foxes.core.Algorithm
357
+ The calculation algorithm
358
+ mdata: foxes.core.MData
359
+ The model data
360
+ fdata: foxes.core.FData
361
+ The farm data
362
+ tdata: foxes.core.TData
363
+ The target point data
364
+
365
+ Returns
366
+ -------
367
+ results: dict
368
+ The resulting data, keys: output variable str.
369
+ Values: numpy.ndarray with shape
370
+ (n_states, n_targets, n_tpoints)
371
+
372
+ """
373
+ # prepare:
374
+ targets = tdata[FC.TARGETS]
375
+ n_states, n_targets, n_tpoints = targets.shape[:3]
376
+ n_points = n_targets * n_tpoints
377
+ points = targets.reshape(n_states, n_points, 3)
378
+ heights = self.timelines_data["height"].to_numpy()
379
+ n_heights = len(heights)
380
+
381
+ # compute states indices for all requested points:
382
+ trace_si = []
383
+ coeffs = []
384
+ for hi in range(n_heights):
385
+ s, c = self.calc_states_indices(
386
+ algo, mdata, points, hi, self.ref_xy[None, None, :]
387
+ )
388
+ trace_si.append(s)
389
+ coeffs.append(c)
390
+ del s, c
391
+
392
+ # flake8: noqa: F821
393
+ def _interp_time(hi, v):
394
+ """Helper function for interpolation bewteen states"""
395
+
396
+ sts = trace_si[hi]
397
+ cfs = coeffs[hi]
398
+ data = self.timelines_data[v].to_numpy()[hi]
399
+ out = np.zeros(sts.shape, dtype=FC.DTYPE)
400
+
401
+ sel_low = sts < 0
402
+ if np.any(sel_low):
403
+ out[sel_low] = data[0]
404
+
405
+ sel_hi = sts >= algo.n_states
406
+ if np.any(sel_hi):
407
+ out[sel_hi] = data[algo.n_states - 1]
408
+
409
+ sel = (~sel_low) & (~sel_hi) & (cfs <= 0)
410
+ if np.any(sel):
411
+ s = sts[sel]
412
+ c = -cfs[sel]
413
+ out[sel] = c * data[s] + (1 - c) * data[s - 1]
414
+
415
+ sel = (~sel_low) & (~sel_hi) & (cfs > 0)
416
+ if np.any(sel):
417
+ s = sts[sel]
418
+ c = cfs[sel]
419
+ out[sel] = c * data[s - 1] + (1 - c) * data[s]
420
+
421
+ return out
422
+
423
+ # interpolate to heights:
424
+ if n_heights > 1:
425
+
426
+ ar_states = np.arange(n_states)
427
+ ar_points = np.arange(n_points)
428
+
429
+ crds = (heights, ar_states, ar_points)
430
+
431
+ data = {
432
+ v: np.stack([_interp_time(hi, v) for hi in range(n_heights)], axis=0)
433
+ for v in self.timelines_data.data_vars.keys()
434
+ if v != "dxy"
435
+ }
436
+ vres = list(data.keys())
437
+ data = np.stack(list(data.values()), axis=-1)
438
+
439
+ eval = np.zeros((n_states, n_points, 3), dtype=FC.DTYPE)
440
+ eval[:, :, 0] = points[:, :, 2]
441
+ eval[:, :, 1] = ar_states[:, None]
442
+ eval[:, :, 2] = ar_points[None, :]
443
+
444
+ try:
445
+ ires = interpn(crds, data, eval, **self.intp_pars)
446
+ except ValueError as e:
447
+ print(f"\nStates '{self.name}': Interpolation error")
448
+ print("INPUT VARS: (heights, states, points)")
449
+ print(
450
+ "DATA BOUNDS:",
451
+ [float(np.min(d)) for d in crds],
452
+ [float(np.max(d)) for d in crds],
453
+ )
454
+ print(
455
+ "EVAL BOUNDS:",
456
+ [float(np.min(p)) for p in eval.T],
457
+ [float(np.max(p)) for p in eval.T],
458
+ )
459
+ print(
460
+ "\nMaybe you want to try the option 'bounds_error=False'? This will extrapolate the data.\n"
461
+ )
462
+ raise e
463
+ del crds, eval, data, ar_points, ar_states
464
+
465
+ results = {}
466
+ for v in self.output_point_vars(algo):
467
+ if v not in [FV.WS, FV.WD]:
468
+ results[v] = ires[:, :, vres.index(v)]
469
+ elif v not in results:
470
+ uv = np.stack(
471
+ [ires[:, :, vres.index("U")], ires[:, :, vres.index("V")]],
472
+ axis=-1,
473
+ )
474
+ results = {FV.WD: uv2wd(uv), FV.WS: np.linalg.norm(uv, axis=-1)}
475
+ del uv
476
+
477
+ # no dependence on height:
478
+ else:
479
+ results = {}
480
+ for v in self.output_point_vars(algo):
481
+ if v not in [FV.WS, FV.WD]:
482
+ results[v] = _interp_time(hi, v)
483
+ elif v not in results:
484
+ uv = np.stack(
485
+ [_interp_time(hi, "U"), _interp_time(hi, "V")], axis=-1
486
+ )
487
+ results = {FV.WD: uv2wd(uv), FV.WS: np.linalg.norm(uv, axis=-1)}
488
+ del uv
489
+
490
+ return {
491
+ v: d.reshape(n_states, n_targets, n_tpoints) for v, d in results.items()
492
+ }
493
+
494
+
495
+ class OnePointFlowTimeseries(OnePointFlowStates):
496
+ """
497
+ Inhomogeneous inflow from homogeneous timeseries data
498
+ at one point
499
+
500
+ :group: input.states
501
+
502
+ """
503
+
504
+ def __init__(self, ref_xy, *args, tl_heights=None, **kwargs):
505
+ """
506
+ Constructor.
507
+
508
+ Parameters
509
+ ----------
510
+ ref_xy: list of float
511
+ The [x, y] or [x, y, z] coordinates of the base states.
512
+ If [x, y, z] then z will serve as height
513
+ args: tuple, optional
514
+ Parameters for the base class
515
+ tl_heights: list of float, optional
516
+ The heights at which timelines will be calculated
517
+ kwargs: dict, optional
518
+ Parameters for the base class
519
+
520
+ """
521
+ if tl_heights is None and len(ref_xy) < 3:
522
+ tl_heights = [100.0]
523
+ super().__init__(
524
+ ref_xy,
525
+ *args,
526
+ tl_heights=tl_heights,
527
+ states_type="Timeseries",
528
+ **kwargs,
529
+ )
530
+
531
+
532
+ class OnePointFlowMultiHeightTimeseries(OnePointFlowStates):
533
+ """
534
+ Inhomogeneous inflow from height dependent homogeneous
535
+ timeseries data at one point
536
+
537
+ :group: input.states
538
+
539
+ """
540
+
541
+ def __init__(self, *args, **kwargs):
542
+ """
543
+ Constructor.
544
+
545
+ Parameters
546
+ ----------
547
+ args: tuple, optional
548
+ Parameters for the base class
549
+ kwargs: dict, optional
550
+ Parameters for the base class
551
+
552
+ """
553
+ super().__init__(*args, states_type="MultiHeightTimeseries", **kwargs)
554
+
555
+
556
+ class OnePointFlowMultiHeightNCTimeseries(OnePointFlowStates):
557
+ """
558
+ Inhomogeneous inflow from height dependent homogeneous
559
+ timeseries data at one point based on NetCDF input
560
+
561
+ :group: input.states
562
+
563
+ """
564
+
565
+ def __init__(self, *args, **kwargs):
566
+ """
567
+ Constructor.
568
+
569
+ Parameters
570
+ ----------
571
+ args: tuple, optional
572
+ Parameters for the base class
573
+ kwargs: dict, optional
574
+ Parameters for the base class
575
+
576
+ """
577
+ super().__init__(*args, states_type="MultiHeightNCTimeseries", **kwargs)
@@ -40,7 +40,7 @@ class ScanWS(States):
40
40
  """
41
41
  super().__init__()
42
42
 
43
- self._wsl = np.array(ws_list)
43
+ self.__wsl = np.array(ws_list)
44
44
  self.N = len(ws_list)
45
45
  self.wd = wd
46
46
  self.ti = ti
@@ -72,10 +72,81 @@ class ScanWS(States):
72
72
  self.WS = self.var(FV.WS)
73
73
 
74
74
  idata = super().load_data(algo, verbosity)
75
- idata["data_vars"][self.WS] = ((FC.STATE,), self._wsl)
75
+ idata["data_vars"][self.WS] = ((FC.STATE,), self.__wsl)
76
76
 
77
77
  return idata
78
78
 
79
+ def set_running(
80
+ self,
81
+ algo,
82
+ data_stash,
83
+ sel=None,
84
+ isel=None,
85
+ verbosity=0,
86
+ ):
87
+ """
88
+ Sets this model status to running, and moves
89
+ all large data to stash.
90
+
91
+ The stashed data will be returned by the
92
+ unset_running() function after running calculations.
93
+
94
+ Parameters
95
+ ----------
96
+ algo: foxes.core.Algorithm
97
+ The calculation algorithm
98
+ data_stash: dict
99
+ Large data stash, this function adds data here.
100
+ Key: model name. Value: dict, large model data
101
+ sel: dict, optional
102
+ The subset selection dictionary
103
+ isel: dict, optional
104
+ The index subset selection dictionary
105
+ verbosity: int
106
+ The verbosity level, 0 = silent
107
+
108
+ """
109
+ super().set_running(algo, data_stash, sel, isel, verbosity)
110
+
111
+ data_stash[self.name].update(
112
+ dict(
113
+ wsl=self.__wsl,
114
+ )
115
+ )
116
+ del self.__wsl
117
+
118
+ def unset_running(
119
+ self,
120
+ algo,
121
+ data_stash,
122
+ sel=None,
123
+ isel=None,
124
+ verbosity=0,
125
+ ):
126
+ """
127
+ Sets this model status to not running, recovering large data
128
+ from stash
129
+
130
+ Parameters
131
+ ----------
132
+ algo: foxes.core.Algorithm
133
+ The calculation algorithm
134
+ data_stash: dict
135
+ Large data stash, this function adds data here.
136
+ Key: model name. Value: dict, large model data
137
+ sel: dict, optional
138
+ The subset selection dictionary
139
+ isel: dict, optional
140
+ The index subset selection dictionary
141
+ verbosity: int
142
+ The verbosity level, 0 = silent
143
+
144
+ """
145
+ super().unset_running(algo, data_stash, sel, isel, verbosity)
146
+
147
+ data = data_stash[self.name]
148
+ self.__wsl = data.pop("wsl")
149
+
79
150
  def size(self):
80
151
  """
81
152
  The total number of states.