foxes 1.2__py3-none-any.whl → 1.2.1__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 (57) hide show
  1. examples/abl_states/run.py +5 -5
  2. examples/induction/run.py +5 -5
  3. examples/random_timeseries/run.py +13 -13
  4. examples/scan_row/run.py +12 -7
  5. examples/sector_management/run.py +11 -7
  6. examples/single_state/run.py +5 -5
  7. examples/tab_file/run.py +1 -1
  8. examples/timeseries/run.py +5 -5
  9. examples/timeseries_slurm/run.py +5 -5
  10. examples/wind_rose/run.py +1 -1
  11. examples/yawed_wake/run.py +5 -5
  12. foxes/algorithms/downwind/downwind.py +15 -5
  13. foxes/algorithms/sequential/sequential.py +1 -1
  14. foxes/core/algorithm.py +24 -20
  15. foxes/core/axial_induction_model.py +18 -0
  16. foxes/core/engine.py +2 -14
  17. foxes/core/farm_controller.py +18 -0
  18. foxes/core/ground_model.py +19 -0
  19. foxes/core/partial_wakes_model.py +9 -21
  20. foxes/core/point_data_model.py +18 -0
  21. foxes/core/rotor_model.py +2 -18
  22. foxes/core/states.py +2 -17
  23. foxes/core/turbine_model.py +2 -18
  24. foxes/core/turbine_type.py +2 -18
  25. foxes/core/vertical_profile.py +8 -20
  26. foxes/core/wake_frame.py +2 -20
  27. foxes/core/wake_model.py +19 -20
  28. foxes/core/wake_superposition.py +19 -0
  29. foxes/input/states/__init__.py +1 -1
  30. foxes/input/states/field_data_nc.py +14 -1
  31. foxes/input/states/{scan_ws.py → scan.py} +39 -52
  32. foxes/input/yaml/__init__.py +1 -1
  33. foxes/input/yaml/dict.py +221 -50
  34. foxes/input/yaml/yaml.py +5 -5
  35. foxes/output/__init__.py +2 -1
  36. foxes/output/farm_results_eval.py +57 -35
  37. foxes/output/output.py +2 -18
  38. foxes/output/plt.py +19 -0
  39. foxes/output/rose_plot.py +413 -207
  40. foxes/utils/__init__.py +1 -2
  41. foxes/utils/subclasses.py +69 -0
  42. {foxes-1.2.dist-info → foxes-1.2.1.dist-info}/METADATA +1 -2
  43. {foxes-1.2.dist-info → foxes-1.2.1.dist-info}/RECORD +56 -56
  44. tests/0_consistency/iterative/test_iterative.py +1 -1
  45. tests/0_consistency/partial_wakes/test_partial_wakes.py +1 -1
  46. tests/1_verification/flappy_0_6/row_Jensen_linear_centre/test_row_Jensen_linear_centre.py +7 -2
  47. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat/test_row_Jensen_linear_tophat.py +7 -2
  48. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2005/test_row_Jensen_linear_tophat_IECTI_2005.py +7 -2
  49. tests/1_verification/flappy_0_6/row_Jensen_linear_tophat_IECTI2019/test_row_Jensen_linear_tophat_IECTI_2019.py +7 -2
  50. tests/1_verification/flappy_0_6/row_Jensen_quadratic_centre/test_row_Jensen_quadratic_centre.py +7 -2
  51. tests/1_verification/flappy_0_6_2/row_Bastankhah_Crespo/test_row_Bastankhah_Crespo.py +7 -3
  52. tests/1_verification/flappy_0_6_2/row_Bastankhah_linear_centre/test_row_Bastankhah_linear_centre.py +7 -2
  53. foxes/utils/windrose_plot.py +0 -152
  54. {foxes-1.2.dist-info → foxes-1.2.1.dist-info}/LICENSE +0 -0
  55. {foxes-1.2.dist-info → foxes-1.2.1.dist-info}/WHEEL +0 -0
  56. {foxes-1.2.dist-info → foxes-1.2.1.dist-info}/entry_points.txt +0 -0
  57. {foxes-1.2.dist-info → foxes-1.2.1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  from abc import abstractmethod
2
2
 
3
- from foxes.utils import all_subclasses
3
+ from foxes.utils import new_instance
4
4
 
5
5
  from .farm_data_model import FarmDataModel
6
6
 
@@ -60,20 +60,4 @@ class TurbineModel(FarmDataModel):
60
60
  Additional parameters for constructor
61
61
 
62
62
  """
63
-
64
- if tmodel_type is None:
65
- return None
66
-
67
- allc = all_subclasses(cls)
68
- found = tmodel_type in [scls.__name__ for scls in allc]
69
-
70
- if found:
71
- for scls in allc:
72
- if scls.__name__ == tmodel_type:
73
- return scls(*args, **kwargs)
74
-
75
- else:
76
- estr = "Turbine model type '{}' is not defined, available types are \n {}".format(
77
- tmodel_type, sorted([i.__name__ for i in allc])
78
- )
79
- raise KeyError(estr)
63
+ return new_instance(cls, tmodel_type, *args, **kwargs)
@@ -1,6 +1,6 @@
1
1
  from abc import abstractmethod
2
2
 
3
- from foxes.utils import all_subclasses
3
+ from foxes.utils import new_instance
4
4
  import foxes.constants as FC
5
5
 
6
6
  from .turbine_model import TurbineModel
@@ -127,20 +127,4 @@ class TurbineType(TurbineModel):
127
127
  Additional parameters for constructor
128
128
 
129
129
  """
130
-
131
- if ttype_type is None:
132
- return None
133
-
134
- allc = all_subclasses(cls)
135
- found = ttype_type in [scls.__name__ for scls in allc]
136
-
137
- if found:
138
- for scls in allc:
139
- if scls.__name__ == ttype_type:
140
- return scls(*args, **kwargs)
141
-
142
- else:
143
- estr = "Turbine type class '{}' is not defined, available types are \n {}".format(
144
- ttype_type, sorted([i.__name__ for i in allc])
145
- )
146
- raise KeyError(estr)
130
+ return new_instance(cls, ttype_type, *args, **kwargs)
@@ -1,7 +1,7 @@
1
1
  from abc import abstractmethod
2
2
 
3
3
  from .model import Model
4
- from foxes.utils import all_subclasses
4
+ from foxes.utils import new_instance
5
5
 
6
6
 
7
7
  class VerticalProfile(Model):
@@ -48,30 +48,18 @@ class VerticalProfile(Model):
48
48
  pass
49
49
 
50
50
  @classmethod
51
- def new(cls, profile_type, **kwargs):
51
+ def new(cls, profile_type, *args, **kwargs):
52
52
  """
53
- Run-time profile factory.
53
+ Run-time vertical profile factory.
54
54
 
55
55
  Parameters
56
56
  ----------
57
57
  profile_type: str
58
58
  The selected derived class name
59
+ args: tuple, optional
60
+ Additional parameters for the constructor
61
+ kwargs: dict, optional
62
+ Additional parameters for the constructor
59
63
 
60
64
  """
61
-
62
- if profile_type is None:
63
- return None
64
-
65
- allc = all_subclasses(cls)
66
- found = profile_type in [scls.__name__ for scls in allc]
67
-
68
- if found:
69
- for scls in allc:
70
- if scls.__name__ == profile_type:
71
- return scls(**kwargs)
72
-
73
- else:
74
- estr = "Vertical profile type '{}' is not defined, available types are \n {}".format(
75
- profile_type, sorted([i.__name__ for i in allc])
76
- )
77
- raise KeyError(estr)
65
+ return new_instance(cls, profile_type, *args, **kwargs)
foxes/core/wake_frame.py CHANGED
@@ -2,7 +2,7 @@ from abc import abstractmethod
2
2
  import numpy as np
3
3
  from scipy.interpolate import interpn
4
4
 
5
- from foxes.utils import all_subclasses
5
+ from foxes.utils import new_instance
6
6
  from foxes.config import config
7
7
  import foxes.variables as FV
8
8
  import foxes.constants as FC
@@ -318,22 +318,4 @@ class WakeFrame(Model):
318
318
  Additional parameters for constructor
319
319
 
320
320
  """
321
-
322
- if wframe_type is None:
323
- return None
324
-
325
- allc = all_subclasses(cls)
326
- found = wframe_type in [scls.__name__ for scls in allc]
327
-
328
- if found:
329
- for scls in allc:
330
- if scls.__name__ == wframe_type:
331
- return scls(*args, **kwargs)
332
-
333
- else:
334
- estr = (
335
- "Wake frame type '{}' is not defined, available types are \n {}".format(
336
- wframe_type, sorted([i.__name__ for i in allc])
337
- )
338
- )
339
- raise KeyError(estr)
321
+ return new_instance(cls, wframe_type, *args, **kwargs)
foxes/core/wake_model.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from abc import abstractmethod
2
2
  import numpy as np
3
3
 
4
- from foxes.utils import all_subclasses
4
+ from foxes.utils import new_instance
5
5
  import foxes.variables as FV
6
6
  import foxes.constants as FC
7
7
 
@@ -141,25 +141,7 @@ class WakeModel(Model):
141
141
  Additional parameters for constructor
142
142
 
143
143
  """
144
-
145
- if wmodel_type is None:
146
- return None
147
-
148
- allc = all_subclasses(cls)
149
- found = wmodel_type in [scls.__name__ for scls in allc]
150
-
151
- if found:
152
- for scls in allc:
153
- if scls.__name__ == wmodel_type:
154
- return scls(*args, **kwargs)
155
-
156
- else:
157
- estr = (
158
- "Wake model type '{}' is not defined, available types are \n {}".format(
159
- wmodel_type, sorted([i.__name__ for i in allc])
160
- )
161
- )
162
- raise KeyError(estr)
144
+ return new_instance(cls, wmodel_type, *args, **kwargs)
163
145
 
164
146
 
165
147
  class TurbineInductionModel(WakeModel):
@@ -184,6 +166,23 @@ class TurbineInductionModel(WakeModel):
184
166
  """
185
167
  return False
186
168
 
169
+ @classmethod
170
+ def new(cls, induction_type, *args, **kwargs):
171
+ """
172
+ Run-time turbine induction model factory.
173
+
174
+ Parameters
175
+ ----------
176
+ induction_type: str
177
+ The selected derived class name
178
+ args: tuple, optional
179
+ Additional parameters for constructor
180
+ kwargs: dict, optional
181
+ Additional parameters for constructor
182
+
183
+ """
184
+ return new_instance(cls, induction_type, *args, **kwargs)
185
+
187
186
 
188
187
  class WakeK(Model):
189
188
  """
@@ -1,5 +1,7 @@
1
1
  from abc import abstractmethod
2
2
 
3
+ from foxes.utils import new_instance
4
+
3
5
  from .model import Model
4
6
 
5
7
 
@@ -106,3 +108,20 @@ class WakeSuperposition(Model):
106
108
 
107
109
  """
108
110
  pass
111
+
112
+ @classmethod
113
+ def new(cls, superp_type, *args, **kwargs):
114
+ """
115
+ Run-time wake superposition model factory.
116
+
117
+ Parameters
118
+ ----------
119
+ superp_type: str
120
+ The selected derived class name
121
+ args: tuple, optional
122
+ Additional parameters for constructor
123
+ kwargs: dict, optional
124
+ Additional parameters for constructor
125
+
126
+ """
127
+ return new_instance(cls, superp_type, *args, **kwargs)
@@ -3,7 +3,7 @@ Atmospheric input states.
3
3
  """
4
4
 
5
5
  from .single import SingleStateStates
6
- from .scan_ws import ScanWS
6
+ from .scan import ScanStates
7
7
  from .states_table import StatesTable, Timeseries, TabStates
8
8
  from .field_data_nc import FieldDataNC
9
9
  from .slice_data_nc import SliceDataNC
@@ -260,21 +260,26 @@ class FieldDataNC(States):
260
260
 
261
261
  cor_shxy = (self.states_coord, self.h_coord, self.x_coord, self.y_coord)
262
262
  cor_shyx = (self.states_coord, self.h_coord, self.y_coord, self.x_coord)
263
+ cor_sxy = (self.states_coord, self.x_coord, self.y_coord)
264
+ cor_syx = (self.states_coord, self.y_coord, self.x_coord)
263
265
  cor_sh = (self.states_coord, self.h_coord)
264
266
  cor_s = (self.states_coord,)
265
267
  vars_shyx = []
268
+ vars_syx = []
266
269
  vars_sh = []
267
270
  vars_s = []
268
271
  for v, ncv in self.var2ncvar.items():
269
272
  if ds[ncv].dims == cor_shyx or ds[ncv].dims == cor_shxy:
270
273
  vars_shyx.append(v)
274
+ elif ds[ncv].dims == cor_syx or ds[ncv].dims == cor_sxy:
275
+ vars_syx.append(v)
271
276
  elif ds[ncv].dims == cor_sh:
272
277
  vars_sh.append(v)
273
278
  elif ds[ncv].dims == cor_s:
274
279
  vars_s.append(v)
275
280
  else:
276
281
  raise ValueError(
277
- f"States '{self.name}': Wrong coordinate order for variable '{ncv}': Found {ds[ncv].dims}, expecting {cor_shxy}, {cor_shyx}, {cor_sh} or {cor_s}"
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}"
278
283
  )
279
284
 
280
285
  data = np.zeros(
@@ -286,6 +291,14 @@ class FieldDataNC(States):
286
291
  data[..., self._dkys[v]] = ds[ncv][:]
287
292
  else:
288
293
  data[..., self._dkys[v]] = np.swapaxes(ds[ncv].to_numpy(), 2, 3)
294
+ for v in vars_syx:
295
+ ncv = self.var2ncvar[v]
296
+ if ds[ncv].dims == cor_syx:
297
+ data[..., self._dkys[v]] = ds[ncv].to_numpy()[:, None]
298
+ else:
299
+ data[..., self._dkys[v]] = np.swapaxes(ds[ncv].to_numpy(), 1, 2)[
300
+ :, None
301
+ ]
289
302
  for v in vars_sh:
290
303
  ncv = self.var2ncvar[v]
291
304
  data[..., self._dkys[v]] = ds[ncv].to_numpy()[:, :, None, None]
@@ -6,46 +6,35 @@ import foxes.variables as FV
6
6
  import foxes.constants as FC
7
7
 
8
8
 
9
- class ScanWS(States):
9
+ class ScanStates(States):
10
10
  """
11
- A given list of wind speeds, all other variables are fixed.
11
+ Scan over selected variables
12
12
 
13
13
  Parameters
14
14
  ----------
15
- wd: float
16
- The wind direction
17
- ti: float
18
- The TI value
19
- rho: float
20
- The air density
15
+ scans: dict
16
+ The scans, key: variable name,
17
+ value: scan values
21
18
 
22
19
  :group: input.states
23
20
 
24
21
  """
25
22
 
26
- def __init__(self, ws_list, wd, ti=None, rho=None):
23
+ def __init__(self, scans, **kwargs):
27
24
  """
28
25
  Constructor.
29
26
 
30
27
  Parameters
31
28
  ----------
32
- ws_list: array_like
33
- The wind speed values
34
- wd: float
35
- The wind direction
36
- ti: float, optional
37
- The TI value
38
- rho: float, optional
39
- The air density
29
+ scans: dict
30
+ The scans, key: variable name,
31
+ value: scan values
32
+ kwargs: dict, optional
33
+ Parameters for the base class
40
34
 
41
35
  """
42
- super().__init__()
43
-
44
- self.__wsl = np.array(ws_list)
45
- self.N = len(ws_list)
46
- self.wd = wd
47
- self.ti = ti
48
- self.rho = rho
36
+ super().__init__(**kwargs)
37
+ self.scans = {v: np.asarray(d) for v, d in scans.items()}
49
38
 
50
39
  def load_data(self, algo, verbosity=0):
51
40
  """
@@ -70,10 +59,24 @@ class ScanWS(States):
70
59
  and `coords`, a dict with entries `dim_name_str -> dim_array`
71
60
 
72
61
  """
73
- self.WS = self.var(FV.WS)
62
+ n_v = len(self.scans)
63
+ shp = [len(v) for v in self.scans.values()]
64
+ self._N = np.prod(shp)
65
+ self._vars = list(self.scans.keys())
66
+
67
+ data = np.zeros(shp + [n_v], dtype=config.dtype_double)
68
+ for i, d in enumerate(self.scans.values()):
69
+ s = [None] * n_v
70
+ s[i] = np.s_[:]
71
+ s = tuple(s)
72
+ data[..., i] = d[s]
73
+ data = data.reshape(self._N, n_v)
74
74
 
75
+ self.VARS = self.var("vars")
76
+ self.DATA = self.var("data")
75
77
  idata = super().load_data(algo, verbosity)
76
- idata["data_vars"][self.WS] = ((FC.STATE,), self.__wsl)
78
+ idata["coords"][self.VARS] = self._vars
79
+ idata["data_vars"][self.DATA] = ((FC.STATE, self.VARS), data)
77
80
 
78
81
  return idata
79
82
 
@@ -109,12 +112,8 @@ class ScanWS(States):
109
112
  """
110
113
  super().set_running(algo, data_stash, sel, isel, verbosity)
111
114
 
112
- data_stash[self.name].update(
113
- dict(
114
- wsl=self.__wsl,
115
- )
116
- )
117
- del self.__wsl
115
+ data_stash[self.name].update(dict(scans=self.scans))
116
+ del self.scans
118
117
 
119
118
  def unset_running(
120
119
  self,
@@ -146,7 +145,7 @@ class ScanWS(States):
146
145
  super().unset_running(algo, data_stash, sel, isel, verbosity)
147
146
 
148
147
  data = data_stash[self.name]
149
- self.__wsl = data.pop("wsl")
148
+ self.scans = data.pop("scans")
150
149
 
151
150
  def size(self):
152
151
  """
@@ -158,7 +157,7 @@ class ScanWS(States):
158
157
  The total number of states
159
158
 
160
159
  """
161
- return self.N
160
+ return self._N
162
161
 
163
162
  def output_point_vars(self, algo):
164
163
  """
@@ -175,14 +174,7 @@ class ScanWS(States):
175
174
  The output variable names
176
175
 
177
176
  """
178
- pvars = [FV.WS]
179
- if self.wd is not None:
180
- pvars.append(FV.WD)
181
- if self.ti is not None:
182
- pvars.append(FV.TI)
183
- if self.rho is not None:
184
- pvars.append(FV.RHO)
185
- return pvars
177
+ return self._vars
186
178
 
187
179
  def weights(self, algo):
188
180
  """
@@ -200,7 +192,7 @@ class ScanWS(States):
200
192
 
201
193
  """
202
194
  return np.full(
203
- (self.N, algo.n_turbines), 1.0 / self.N, dtype=config.dtype_double
195
+ (self._N, algo.n_turbines), 1.0 / self._N, dtype=config.dtype_double
204
196
  )
205
197
 
206
198
  def calculate(self, algo, mdata, fdata, tdata):
@@ -229,14 +221,9 @@ class ScanWS(States):
229
221
  (n_states, n_targets, n_tpoints)
230
222
 
231
223
  """
232
- tdata[FV.WS] = np.zeros_like(tdata[FC.TARGETS][..., 0])
233
- tdata[FV.WS][:] = mdata[self.WS][:, None, None]
234
-
235
- if self.wd is not None:
236
- tdata[FV.WD] = np.full_like(tdata[FV.WS], self.wd)
237
- if self.ti is not None:
238
- tdata[FV.TI] = np.full_like(tdata[FV.WS], self.ti)
239
- if self.rho is not None:
240
- tdata[FV.RHO] = np.full_like(tdata[FV.WS], self.rho)
224
+ for i, v in enumerate(self._vars):
225
+ if v not in tdata:
226
+ tdata[v] = np.zeros_like(tdata[FC.TARGETS][..., 0])
227
+ tdata[v][:] = mdata[self.DATA][:, None, None, i]
241
228
 
242
229
  return {v: tdata[v] for v in self.output_point_vars(algo)}
@@ -1,3 +1,3 @@
1
- from .dict import run_dict
1
+ from .dict import read_dict, run_dict, run_outputs
2
2
  from .yaml import foxes_yaml
3
3
  from .windio.windio import read_windio, foxes_windio