foxes 0.8.2__py3-none-any.whl → 1.1.0.2__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 (215) 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/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 +190 -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 +247 -111
  27. foxes/algorithms/downwind/models/farm_wakes_calc.py +12 -7
  28. foxes/algorithms/downwind/models/init_farm_data.py +2 -2
  29. foxes/algorithms/downwind/models/point_wakes_calc.py +6 -7
  30. foxes/algorithms/downwind/models/reorder_farm_output.py +1 -2
  31. foxes/algorithms/downwind/models/set_amb_farm_results.py +1 -1
  32. foxes/algorithms/downwind/models/set_amb_point_results.py +5 -3
  33. foxes/algorithms/iterative/iterative.py +74 -34
  34. foxes/algorithms/iterative/models/farm_wakes_calc.py +12 -7
  35. foxes/algorithms/iterative/models/urelax.py +3 -3
  36. foxes/algorithms/sequential/models/plugin.py +5 -5
  37. foxes/algorithms/sequential/models/seq_state.py +1 -1
  38. foxes/algorithms/sequential/sequential.py +126 -255
  39. foxes/constants.py +22 -7
  40. foxes/core/__init__.py +1 -0
  41. foxes/core/algorithm.py +632 -147
  42. foxes/core/data.py +252 -20
  43. foxes/core/data_calc_model.py +15 -291
  44. foxes/core/engine.py +640 -0
  45. foxes/core/farm_controller.py +38 -10
  46. foxes/core/farm_data_model.py +16 -1
  47. foxes/core/ground_model.py +2 -2
  48. foxes/core/model.py +249 -182
  49. foxes/core/partial_wakes_model.py +1 -1
  50. foxes/core/point_data_model.py +17 -2
  51. foxes/core/rotor_model.py +27 -21
  52. foxes/core/states.py +17 -1
  53. foxes/core/turbine_type.py +28 -0
  54. foxes/core/wake_frame.py +30 -34
  55. foxes/core/wake_model.py +5 -5
  56. foxes/core/wake_superposition.py +1 -1
  57. foxes/data/windio/windio_5turbines_timeseries.yaml +31 -15
  58. foxes/engines/__init__.py +17 -0
  59. foxes/engines/dask.py +982 -0
  60. foxes/engines/default.py +75 -0
  61. foxes/engines/futures.py +72 -0
  62. foxes/engines/mpi.py +38 -0
  63. foxes/engines/multiprocess.py +71 -0
  64. foxes/engines/numpy.py +167 -0
  65. foxes/engines/pool.py +249 -0
  66. foxes/engines/ray.py +79 -0
  67. foxes/engines/single.py +141 -0
  68. foxes/input/farm_layout/__init__.py +1 -0
  69. foxes/input/farm_layout/from_csv.py +4 -0
  70. foxes/input/farm_layout/from_json.py +2 -2
  71. foxes/input/farm_layout/grid.py +2 -2
  72. foxes/input/farm_layout/ring.py +65 -0
  73. foxes/input/farm_layout/row.py +2 -2
  74. foxes/input/states/__init__.py +7 -0
  75. foxes/input/states/create/random_abl_states.py +1 -1
  76. foxes/input/states/field_data_nc.py +158 -33
  77. foxes/input/states/multi_height.py +128 -14
  78. foxes/input/states/one_point_flow.py +577 -0
  79. foxes/input/states/scan_ws.py +74 -3
  80. foxes/input/states/single.py +1 -1
  81. foxes/input/states/slice_data_nc.py +681 -0
  82. foxes/input/states/states_table.py +204 -35
  83. foxes/input/windio/__init__.py +2 -2
  84. foxes/input/windio/get_states.py +44 -23
  85. foxes/input/windio/read_attributes.py +48 -17
  86. foxes/input/windio/read_farm.py +116 -102
  87. foxes/input/windio/read_fields.py +16 -6
  88. foxes/input/windio/read_outputs.py +71 -24
  89. foxes/input/windio/runner.py +31 -17
  90. foxes/input/windio/windio.py +41 -23
  91. foxes/models/farm_models/turbine2farm.py +1 -1
  92. foxes/models/ground_models/wake_mirror.py +10 -6
  93. foxes/models/model_book.py +58 -20
  94. foxes/models/partial_wakes/axiwake.py +3 -3
  95. foxes/models/partial_wakes/rotor_points.py +3 -3
  96. foxes/models/partial_wakes/top_hat.py +2 -2
  97. foxes/models/point_models/set_uniform_data.py +1 -1
  98. foxes/models/point_models/tke2ti.py +1 -1
  99. foxes/models/point_models/wake_deltas.py +1 -1
  100. foxes/models/rotor_models/centre.py +4 -0
  101. foxes/models/rotor_models/grid.py +24 -25
  102. foxes/models/rotor_models/levels.py +4 -5
  103. foxes/models/turbine_models/calculator.py +4 -6
  104. foxes/models/turbine_models/kTI_model.py +22 -6
  105. foxes/models/turbine_models/lookup_table.py +30 -4
  106. foxes/models/turbine_models/rotor_centre_calc.py +4 -3
  107. foxes/models/turbine_models/set_farm_vars.py +103 -34
  108. foxes/models/turbine_types/PCt_file.py +27 -3
  109. foxes/models/turbine_types/PCt_from_two.py +27 -3
  110. foxes/models/turbine_types/TBL_file.py +80 -0
  111. foxes/models/turbine_types/__init__.py +2 -0
  112. foxes/models/turbine_types/lookup.py +316 -0
  113. foxes/models/turbine_types/null_type.py +51 -1
  114. foxes/models/turbine_types/wsrho2PCt_from_two.py +29 -5
  115. foxes/models/turbine_types/wsti2PCt_from_two.py +31 -7
  116. foxes/models/vertical_profiles/__init__.py +1 -1
  117. foxes/models/vertical_profiles/data_profile.py +1 -1
  118. foxes/models/wake_frames/__init__.py +1 -0
  119. foxes/models/wake_frames/dynamic_wakes.py +424 -0
  120. foxes/models/wake_frames/farm_order.py +25 -5
  121. foxes/models/wake_frames/rotor_wd.py +6 -4
  122. foxes/models/wake_frames/seq_dynamic_wakes.py +61 -74
  123. foxes/models/wake_frames/streamlines.py +21 -22
  124. foxes/models/wake_frames/timelines.py +330 -129
  125. foxes/models/wake_frames/yawed_wakes.py +7 -4
  126. foxes/models/wake_models/dist_sliced.py +2 -4
  127. foxes/models/wake_models/induction/rankine_half_body.py +5 -5
  128. foxes/models/wake_models/induction/rathmann.py +78 -24
  129. foxes/models/wake_models/induction/self_similar.py +78 -28
  130. foxes/models/wake_models/induction/vortex_sheet.py +86 -48
  131. foxes/models/wake_models/ti/crespo_hernandez.py +6 -4
  132. foxes/models/wake_models/ti/iec_ti.py +40 -21
  133. foxes/models/wake_models/top_hat.py +1 -1
  134. foxes/models/wake_models/wind/bastankhah14.py +8 -6
  135. foxes/models/wake_models/wind/bastankhah16.py +17 -16
  136. foxes/models/wake_models/wind/jensen.py +4 -3
  137. foxes/models/wake_models/wind/turbopark.py +16 -13
  138. foxes/models/wake_superpositions/ti_linear.py +1 -1
  139. foxes/models/wake_superpositions/ti_max.py +1 -1
  140. foxes/models/wake_superpositions/ti_pow.py +1 -1
  141. foxes/models/wake_superpositions/ti_quadratic.py +1 -1
  142. foxes/models/wake_superpositions/ws_linear.py +8 -7
  143. foxes/models/wake_superpositions/ws_max.py +8 -7
  144. foxes/models/wake_superpositions/ws_pow.py +8 -7
  145. foxes/models/wake_superpositions/ws_product.py +5 -5
  146. foxes/models/wake_superpositions/ws_quadratic.py +8 -7
  147. foxes/output/__init__.py +4 -1
  148. foxes/output/farm_layout.py +16 -12
  149. foxes/output/farm_results_eval.py +1 -1
  150. foxes/output/flow_plots_2d/__init__.py +0 -1
  151. foxes/output/flow_plots_2d/flow_plots.py +70 -30
  152. foxes/output/grids.py +92 -22
  153. foxes/output/results_writer.py +2 -2
  154. foxes/output/rose_plot.py +3 -3
  155. foxes/output/seq_plugins/__init__.py +2 -0
  156. foxes/output/{flow_plots_2d → seq_plugins}/seq_flow_ani_plugin.py +64 -22
  157. foxes/output/seq_plugins/seq_wake_debug_plugin.py +145 -0
  158. foxes/output/slice_data.py +131 -111
  159. foxes/output/state_turbine_map.py +19 -14
  160. foxes/output/state_turbine_table.py +19 -19
  161. foxes/utils/__init__.py +1 -1
  162. foxes/utils/abl/neutral.py +2 -2
  163. foxes/utils/abl/stable.py +2 -2
  164. foxes/utils/abl/unstable.py +2 -2
  165. foxes/utils/data_book.py +1 -1
  166. foxes/utils/dev_utils.py +42 -0
  167. foxes/utils/dict.py +24 -1
  168. foxes/utils/exec_python.py +1 -1
  169. foxes/utils/factory.py +176 -53
  170. foxes/utils/geom2d/circle.py +1 -1
  171. foxes/utils/geom2d/polygon.py +1 -1
  172. foxes/utils/geopandas_utils.py +2 -2
  173. foxes/utils/load.py +2 -2
  174. foxes/utils/pandas_helpers.py +3 -2
  175. foxes/utils/wind_dir.py +0 -2
  176. foxes/utils/xarray_utils.py +24 -14
  177. foxes/variables.py +39 -2
  178. {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/METADATA +75 -33
  179. foxes-1.1.0.2.dist-info/RECORD +309 -0
  180. {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/WHEEL +1 -1
  181. foxes-1.1.0.2.dist-info/top_level.txt +4 -0
  182. tests/0_consistency/iterative/test_iterative.py +92 -0
  183. tests/0_consistency/partial_wakes/test_partial_wakes.py +90 -0
  184. tests/1_verification/flappy_0_6/PCt_files/flappy/run.py +85 -0
  185. tests/1_verification/flappy_0_6/PCt_files/test_PCt_files.py +103 -0
  186. tests/1_verification/flappy_0_6/abl_states/flappy/run.py +85 -0
  187. tests/1_verification/flappy_0_6/abl_states/test_abl_states.py +87 -0
  188. tests/1_verification/flappy_0_6/partial_top_hat/flappy/run.py +82 -0
  189. tests/1_verification/flappy_0_6/partial_top_hat/test_partial_top_hat.py +82 -0
  190. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/flappy/run.py +92 -0
  191. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +93 -0
  192. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/flappy/run.py +92 -0
  193. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +96 -0
  194. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/flappy/run.py +94 -0
  195. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +122 -0
  196. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/flappy/run.py +94 -0
  197. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +122 -0
  198. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/flappy/run.py +92 -0
  199. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +93 -0
  200. tests/1_verification/flappy_0_6_2/grid_rotors/flappy/run.py +85 -0
  201. tests/1_verification/flappy_0_6_2/grid_rotors/test_grid_rotors.py +130 -0
  202. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/flappy/run.py +96 -0
  203. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +116 -0
  204. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/flappy/run.py +93 -0
  205. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +99 -0
  206. tests/3_examples/test_examples.py +34 -0
  207. foxes/VERSION +0 -1
  208. foxes/output/flow_plots_2d.py +0 -0
  209. foxes/utils/geopandas_helpers.py +0 -294
  210. foxes/utils/runners/__init__.py +0 -1
  211. foxes/utils/runners/runners.py +0 -280
  212. foxes-0.8.2.dist-info/RECORD +0 -247
  213. foxes-0.8.2.dist-info/top_level.txt +0 -1
  214. foxes-0.8.2.dist-info/zip-safe +0 -1
  215. {foxes-0.8.2.dist-info → foxes-1.1.0.2.dist-info}/LICENSE +0 -0
foxes/core/engine.py ADDED
@@ -0,0 +1,640 @@
1
+ import os
2
+ import numpy as np
3
+ from abc import ABC, abstractmethod
4
+ from tqdm import tqdm
5
+ from xarray import Dataset
6
+
7
+ from foxes.core import MData, FData, TData
8
+ from foxes.utils import all_subclasses
9
+ import foxes.constants as FC
10
+
11
+ __global_engine_data__ = dict(
12
+ engine=None,
13
+ )
14
+
15
+
16
+ class Engine(ABC):
17
+ """
18
+ Abstract base clas for foxes calculation engines
19
+
20
+ Attributes
21
+ ----------
22
+ chunk_size_states: int
23
+ The size of a states chunk
24
+ chunk_size_points: int
25
+ The size of a points chunk
26
+ n_procs: int, optional
27
+ The number of processes to be used,
28
+ or None for automatic
29
+ verbosity: int
30
+ The verbosity level, 0 = silent
31
+
32
+ :group: core
33
+
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ chunk_size_states=None,
39
+ chunk_size_points=None,
40
+ n_procs=None,
41
+ verbosity=1,
42
+ ):
43
+ """
44
+ Constructor.
45
+
46
+ Parameters
47
+ ----------
48
+ chunk_size_states: int, optional
49
+ The size of a states chunk
50
+ chunk_size_points: int, optional
51
+ The size of a points chunk
52
+ n_procs: int, optional
53
+ The number of processes to be used,
54
+ or None for automatic
55
+ verbosity: int
56
+ The verbosity level, 0 = silent
57
+
58
+ """
59
+ self.chunk_size_states = chunk_size_states
60
+ self.chunk_size_points = chunk_size_points
61
+ try:
62
+ self.n_procs = n_procs if n_procs is not None else os.process_cpu_count()
63
+ except AttributeError:
64
+ self.n_procs = os.cpu_count()
65
+ self.verbosity = verbosity
66
+ self.__initialized = False
67
+ self.__entered = False
68
+
69
+ def __repr__(self):
70
+ s = f"n_procs={self.n_procs}, chunk_size_states={self.chunk_size_states}, chunk_size_points={self.chunk_size_points}"
71
+ return f"{type(self).__name__}({s})"
72
+
73
+ def __enter__(self):
74
+ if self.__entered:
75
+ raise ValueError(f"Enter called for already entered engine")
76
+ self.__entered = True
77
+ if not self.initialized:
78
+ self.initialize()
79
+ return self
80
+
81
+ def __exit__(self, *exit_args):
82
+ if not self.__entered:
83
+ raise ValueError(f"Exit called for not entered engine")
84
+ self.__entered = False
85
+ if self.initialized:
86
+ self.finalize(*exit_args)
87
+
88
+ def __del__(self):
89
+ if self.initialized:
90
+ self.finalize()
91
+
92
+ @property
93
+ def entered(self):
94
+ """
95
+ Flag that this model has been entered.
96
+
97
+ Returns
98
+ -------
99
+ flag: bool
100
+ True if the model has been entered.
101
+
102
+ """
103
+ return self.__entered
104
+
105
+ @property
106
+ def initialized(self):
107
+ """
108
+ Initialization flag.
109
+
110
+ Returns
111
+ -------
112
+ flag: bool
113
+ True if the model has been initialized.
114
+
115
+ """
116
+ return self.__initialized
117
+
118
+ def initialize(self):
119
+ """
120
+ Initializes the engine.
121
+ """
122
+ if not self.entered:
123
+ self.__enter__()
124
+ elif not self.initialized:
125
+ if get_engine(error=False, default=False) is not None:
126
+ raise ValueError(
127
+ f"Cannot initialize engine '{type(self).__name__}', since engine already set to '{type(get_engine()).__name__}'"
128
+ )
129
+ global __global_engine_data__
130
+ __global_engine_data__["engine"] = self
131
+ self.__initialized = True
132
+
133
+ def finalize(self, type=None, value=None, traceback=None):
134
+ """
135
+ Finalizes the engine.
136
+
137
+ Parameters
138
+ ----------
139
+ type: object, optional
140
+ Dummy argument for the exit function
141
+ value: object, optional
142
+ Dummy argument for the exit function
143
+ traceback: object, optional
144
+ Dummy argument for the exit function
145
+
146
+ """
147
+ if self.entered:
148
+ self.__exit__(type, value, traceback)
149
+ elif self.initialized:
150
+ global __global_engine_data__
151
+ __global_engine_data__["engine"] = None
152
+ self.__initialized = False
153
+
154
+ def print(self, *args, level=1, **kwargs):
155
+ """Prints based on verbosity"""
156
+ if self.verbosity >= level:
157
+ print(*args, **kwargs)
158
+
159
+ @property
160
+ def loop_dims(self):
161
+ """
162
+ Gets the loop dimensions (possibly chunked)
163
+
164
+ Returns
165
+ -------
166
+ dims: list of str
167
+ The loop dimensions (possibly chunked)
168
+
169
+ """
170
+ if self.chunk_size_states is None and self.chunk_size_states is None:
171
+ return []
172
+ elif self.chunk_size_states is None:
173
+ return [FC.TARGET]
174
+ elif self.chunk_size_points is None:
175
+ return [FC.STATE]
176
+ else:
177
+ return [FC.STATE, FC.TARGET]
178
+
179
+ def select_subsets(self, *datasets, sel=None, isel=None):
180
+ """
181
+ Takes subsets of datasets
182
+
183
+ Parameters
184
+ ----------
185
+ datasets: tuple
186
+ The xarray.Dataset or xarray.Dataarray objects
187
+ sel: dict, optional
188
+ The selection dictionary
189
+ isel: dict, optional
190
+ The index selection dictionary
191
+
192
+ Returns
193
+ -------
194
+ subsets: list
195
+ The subsets of the input data
196
+
197
+ """
198
+ if sel is not None:
199
+ new_datasets = []
200
+ for data in datasets:
201
+ if data is not None:
202
+ s = {c: u for c, u in sel.items() if c in data.coords}
203
+ new_datasets.append(data.sel(s) if len(s) else data)
204
+ else:
205
+ new_datasets.append(data)
206
+ datasets = new_datasets
207
+
208
+ if isel is not None:
209
+ new_datasets = []
210
+ for data in datasets:
211
+ if data is not None:
212
+ s = {c: u for c, u in isel.items() if c in data.coords}
213
+ new_datasets.append(data.isel(s) if len(s) else data)
214
+ else:
215
+ new_datasets.append(data)
216
+ datasets = new_datasets
217
+
218
+ return datasets
219
+
220
+ def calc_chunk_sizes(self, n_states, n_targets=1):
221
+ """
222
+ Computes the sizes of states and points chunks
223
+
224
+ Parameters
225
+ ----------
226
+ n_states: int
227
+ The number of states
228
+ n_targets: int
229
+ The number of point targets
230
+
231
+ Returns
232
+ -------
233
+ chunk_sizes_states: numpy.ndarray
234
+ The sizes of all states chunks, shape: (n_chunks_states,)
235
+ chunk_sizes_targets: numpy.ndarray
236
+ The sizes of all targets chunks, shape: (n_chunks_targets,)
237
+
238
+ """
239
+ # determine states chunks:
240
+ if self.chunk_size_states is None:
241
+ n_chunks_states = min(self.n_procs, n_states)
242
+ chunk_size_states = max(int(n_states / self.n_procs), 1)
243
+ else:
244
+ chunk_size_states = min(n_states, self.chunk_size_states)
245
+ n_chunks_states = max(int(n_states / chunk_size_states), 1)
246
+ if int(n_states / n_chunks_states) > chunk_size_states:
247
+ n_chunks_states += 1
248
+ chunk_size_states = int(n_states / n_chunks_states)
249
+ chunk_sizes_states = np.full(n_chunks_states, chunk_size_states)
250
+ extra = n_states - n_chunks_states * chunk_size_states
251
+ if extra > 0:
252
+ chunk_sizes_states[-extra:] += 1
253
+
254
+ s = np.sum(chunk_sizes_states)
255
+ assert (
256
+ s == n_states
257
+ ), f"States count mismatch: Expecting {n_states}, chunks sum is {s}. Chunks: {[int(c) for c in chunk_sizes_states]}"
258
+
259
+ # determine points chunks:
260
+ chunk_sizes_targets = [n_targets]
261
+ if n_targets > 1:
262
+ if self.chunk_size_points is None:
263
+ if n_chunks_states == 1:
264
+ n_chunks_targets = min(self.n_procs, n_targets)
265
+ chunk_size_targets = max(int(n_targets / self.n_procs), 1)
266
+ else:
267
+ chunk_size_targets = n_targets
268
+ n_chunks_targets = 1
269
+ else:
270
+ chunk_size_targets = min(n_targets, self.chunk_size_points)
271
+ n_chunks_targets = max(int(n_targets / chunk_size_targets), 1)
272
+ if int(n_targets / n_chunks_targets) > chunk_size_targets:
273
+ n_chunks_targets += 1
274
+ chunk_size_targets = int(n_targets / n_chunks_targets)
275
+ chunk_sizes_targets = np.full(n_chunks_targets, chunk_size_targets)
276
+ extra = n_targets - n_chunks_targets * chunk_size_targets
277
+ if extra > 0:
278
+ chunk_sizes_targets[-extra:] += 1
279
+
280
+ s = np.sum(chunk_sizes_targets)
281
+ assert (
282
+ s == n_targets
283
+ ), f"Targets count mismatch: Expecting {n_targets}, chunks sum is {s}. Chunks: {[int(c) for c in chunk_sizes_targets]}"
284
+
285
+ return chunk_sizes_states, chunk_sizes_targets
286
+
287
+ def get_chunk_input_data(
288
+ self,
289
+ algo,
290
+ model_data,
291
+ farm_data,
292
+ point_data,
293
+ states_i0_i1,
294
+ targets_i0_i1,
295
+ out_vars,
296
+ ):
297
+ """
298
+ Extracts the data for a single chunk calculation
299
+
300
+ Parameters
301
+ ----------
302
+ algo: foxes.core.Algorithm
303
+ The algorithm object
304
+ model_data: xarray.Dataset
305
+ The initial model data
306
+ farm_data: xarray.Dataset
307
+ The initial farm data
308
+ point_data: xarray.Dataset
309
+ The initial point data
310
+ states_i0_i1: tuple
311
+ The (start, end) values of the states
312
+ targets_i0_i1: tuple
313
+ The (start, end) values of the targets
314
+ out_vars: list of str
315
+ Names of the output variables
316
+
317
+ Returns
318
+ -------
319
+ data: list of foxes.core.Data
320
+ Either [mdata, fdata] or [mdata, fdata, tdata]
321
+
322
+ """
323
+ # prepare:
324
+ i0_states, i1_states = states_i0_i1
325
+ i0_targets, i1_targets = targets_i0_i1
326
+ s_states = np.s_[i0_states:i1_states]
327
+ s_targets = np.s_[i0_targets:i1_targets]
328
+
329
+ # create mdata:
330
+ mdata = MData.from_dataset(
331
+ model_data,
332
+ s_states=s_states,
333
+ loop_dims=[FC.STATE],
334
+ states_i0=i0_states,
335
+ copy=True,
336
+ )
337
+
338
+ # create fdata:
339
+ if point_data is None:
340
+
341
+ def cb(data, dims):
342
+ n_states = i1_states - i0_states
343
+ for o in set(out_vars).difference(data.keys()):
344
+ data[o] = np.full(
345
+ (n_states, algo.n_turbines), np.nan, dtype=FC.DTYPE
346
+ )
347
+ dims[o] = (FC.STATE, FC.TURBINE)
348
+
349
+ else:
350
+ cb = None
351
+ fdata = FData.from_dataset(
352
+ farm_data,
353
+ mdata=mdata,
354
+ s_states=s_states,
355
+ callback=cb,
356
+ loop_dims=[FC.STATE],
357
+ states_i0=i0_states,
358
+ copy=True,
359
+ )
360
+
361
+ # create tdata:
362
+ tdata = None
363
+ if point_data is not None:
364
+
365
+ def cb(data, dims):
366
+ n_states = i1_states - i0_states
367
+ n_targets = i1_targets - i0_targets
368
+ for o in set(out_vars).difference(data.keys()):
369
+ data[o] = np.full((n_states, n_targets, 1), np.nan, dtype=FC.DTYPE)
370
+ dims[o] = (FC.STATE, FC.TARGET, FC.TPOINT)
371
+
372
+ tdata = TData.from_dataset(
373
+ point_data,
374
+ mdata=mdata,
375
+ s_states=s_states,
376
+ s_targets=s_targets,
377
+ callback=cb,
378
+ loop_dims=[FC.STATE, FC.TARGET],
379
+ states_i0=i0_states,
380
+ copy=True,
381
+ )
382
+
383
+ return [d for d in [mdata, fdata, tdata] if d is not None]
384
+
385
+ def combine_results(
386
+ self,
387
+ algo,
388
+ results,
389
+ model_data,
390
+ out_vars,
391
+ out_coords,
392
+ n_chunks_states,
393
+ n_chunks_targets,
394
+ goal_data,
395
+ iterative,
396
+ ):
397
+ """
398
+ Combines chunk results into final Dataset
399
+
400
+ Parameters
401
+ ----------
402
+ algo: foxes.core.Algorithm
403
+ The algorithm object
404
+ results: dict
405
+ The results from the chunk calculations,
406
+ key: (chunki_states, chunki_targets),
407
+ value: dict with numpy.ndarray values
408
+ model_data: xarray.Dataset
409
+ The initial model data
410
+ out_vars: list of str
411
+ Names of the output variables
412
+ out_coords: list of str
413
+ Names of the output coordinates
414
+ n_chunks_states: int
415
+ The number of states chunks
416
+ n_chunks_targets: int
417
+ The number of targets chunks
418
+ goal_data: foxes.core.Data
419
+ Either fdata or tdata
420
+ iterative: bool
421
+ Flag for use within the iterative algorithm
422
+
423
+ Returns
424
+ -------
425
+ ds: xarray.Dataset
426
+ The final results dataset
427
+
428
+ """
429
+ self.print(f"{type(self).__name__}: Combining results", level=2)
430
+ pbar = tqdm(total=len(out_vars)) if self.verbosity > 1 else None
431
+ data_vars = {}
432
+ for v in out_vars:
433
+ if v in results[(0, 0)][0]:
434
+ data_vars[v] = [out_coords, []]
435
+
436
+ if n_chunks_targets == 1:
437
+ alls = 0
438
+ for chunki_states in range(n_chunks_states):
439
+ r, cstore = results[(chunki_states, 0)]
440
+ data_vars[v][1].append(r[v])
441
+ alls += data_vars[v][1][-1].shape[0]
442
+ if iterative:
443
+ for k, c in cstore.items():
444
+ if k in algo.chunk_store:
445
+ algo.chunk_store[k].update(c)
446
+ else:
447
+ algo.chunk_store[k] = c
448
+ else:
449
+ for chunki_states in range(n_chunks_states):
450
+ tres = []
451
+ for chunki_points in range(n_chunks_targets):
452
+ r, cstore = results[(chunki_states, chunki_points)]
453
+ tres.append(r[v])
454
+ if iterative:
455
+ for k, c in cstore.items():
456
+ if k in algo.chunk_store:
457
+ algo.chunk_store[k].update(c)
458
+ else:
459
+ algo.chunk_store[k] = c
460
+ data_vars[v][1].append(np.concatenate(tres, axis=1))
461
+ del tres
462
+ del r, cstore
463
+ data_vars[v][1] = np.concatenate(data_vars[v][1], axis=0)
464
+ else:
465
+ data_vars[v] = (goal_data[v].dims, goal_data[v].to_numpy())
466
+
467
+ if pbar is not None:
468
+ pbar.update()
469
+ del results
470
+ if pbar is not None:
471
+ pbar.close()
472
+
473
+ # if not iterative or algo.final_iteration:
474
+ # algo.reset_chunk_store()
475
+
476
+ coords = {}
477
+ if FC.STATE in out_coords and FC.STATE in model_data.coords:
478
+ coords[FC.STATE] = model_data[FC.STATE].to_numpy()
479
+
480
+ return Dataset(
481
+ coords=coords,
482
+ data_vars={v: tuple(d) for v, d in data_vars.items()},
483
+ )
484
+
485
+ @abstractmethod
486
+ def run_calculation(self, algo, model, model_data, farm_data, point_data=None):
487
+ """
488
+ Runs the model calculation
489
+
490
+ Parameters
491
+ ----------
492
+ algo: foxes.core.Algorithm
493
+ The algorithm object
494
+ model: foxes.core.DataCalcModel
495
+ The model that whose calculate function
496
+ should be run
497
+ model_data: xarray.Dataset
498
+ The initial model data
499
+ farm_data: xarray.Dataset
500
+ The initial farm data
501
+ point_data: xarray.Dataset, optional
502
+ The initial point data
503
+
504
+ Returns
505
+ -------
506
+ results: xarray.Dataset
507
+ The model results
508
+
509
+ """
510
+ n_states = model_data.sizes[FC.STATE]
511
+ if point_data is None:
512
+ self.print(
513
+ f"{type(self).__name__}: Calculating {n_states} states for {algo.n_turbines} turbines"
514
+ )
515
+ else:
516
+ self.print(
517
+ f"{type(self).__name__}: Calculating data at {point_data.sizes[FC.TARGET]} points for {n_states} states"
518
+ )
519
+ if not self.initialized:
520
+ raise ValueError(f"Engine '{type(self).__name__}' not initialized")
521
+ if not model.initialized:
522
+ raise ValueError(f"Model '{model.name}' not initialized")
523
+
524
+ @classmethod
525
+ def new(cls, engine_type, *args, **kwargs):
526
+ """
527
+ Run-time engine factory.
528
+
529
+ Parameters
530
+ ----------
531
+ engine_type: str
532
+ The selected derived class name
533
+ args: tuple, optional
534
+ Additional parameters for constructor
535
+ kwargs: dict, optional
536
+ Additional parameters for constructor
537
+
538
+ """
539
+
540
+ if engine_type is None:
541
+ return None
542
+ else:
543
+ engine_type = dict(
544
+ default="DefaultEngine",
545
+ threads="ThreadsEngine",
546
+ process="ProcessEngine",
547
+ xarray="XArrayEngine",
548
+ dask="DaskEngine",
549
+ multiprocess="MultiprocessEngine",
550
+ local_cluster="LocalClusterEngine",
551
+ slurm_cluster="SlurmClusterEngine",
552
+ mpi="MPIEngine",
553
+ ray="RayEngine",
554
+ numpy="NumpyEngine",
555
+ single="SingleChunkEngine",
556
+ ).get(engine_type, engine_type)
557
+
558
+ allc = all_subclasses(cls)
559
+ found = engine_type in [scls.__name__ for scls in allc]
560
+
561
+ if found:
562
+ for scls in allc:
563
+ if scls.__name__ == engine_type:
564
+ return scls(*args, **kwargs)
565
+
566
+ else:
567
+ estr = "engine type '{}' is not defined, available types are \n {}".format(
568
+ engine_type, sorted([i.__name__ for i in allc])
569
+ )
570
+ raise KeyError(estr)
571
+
572
+
573
+ def get_engine(error=True, default=True):
574
+ """
575
+ Gets the global calculation engine
576
+
577
+ Parameters
578
+ ----------
579
+ error: bool
580
+ Flag for raising ValueError if no
581
+ engine is found
582
+ default: bool or dict or Engine
583
+ Set default engine if not set yet
584
+
585
+ Returns
586
+ -------
587
+ engine: foxes.core.Engine
588
+ The foxes calculation engine
589
+
590
+ :group: core
591
+
592
+ """
593
+ engine = __global_engine_data__["engine"]
594
+ if engine is None:
595
+ if isinstance(default, dict):
596
+ engine = Engine.new(**default)
597
+ print(f"Selecting default engine '{engine}'")
598
+ engine.initialize()
599
+ return engine
600
+ elif isinstance(default, Engine):
601
+ print(f"Selecting default engine '{default}'")
602
+ default.initialize()
603
+ return default
604
+ elif isinstance(default, bool) and default:
605
+ engine = Engine.new(
606
+ engine_type="DefaultEngine", chunk_size_points=20000, verbosity=0
607
+ )
608
+ print(f"Selecting '{engine}'")
609
+ engine.initialize()
610
+ return engine
611
+ elif error:
612
+ raise ValueError("Engine not found.")
613
+ return engine
614
+
615
+
616
+ def has_engine():
617
+ """
618
+ Flag that checks if engine has been set
619
+
620
+ Returns
621
+ -------
622
+ flag: bool
623
+ True if engine has been set
624
+
625
+ :group: core
626
+
627
+ """
628
+ return __global_engine_data__["engine"] is not None
629
+
630
+
631
+ def reset_engine():
632
+ """
633
+ Resets the global calculation engine
634
+
635
+ :group: core
636
+
637
+ """
638
+ engine = get_engine(error=False, default=False)
639
+ if engine is not None:
640
+ engine.finalize(type=None, value=None, traceback=None)