foxes 0.7.1__py3-none-any.whl → 0.7.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of foxes might be problematic. Click here for more details.

Files changed (66) hide show
  1. foxes/VERSION +1 -1
  2. foxes/algorithms/downwind/downwind.py +60 -46
  3. foxes/algorithms/downwind/models/farm_wakes_calc.py +17 -6
  4. foxes/algorithms/downwind/models/point_wakes_calc.py +13 -45
  5. foxes/algorithms/iterative/iterative.py +1 -1
  6. foxes/algorithms/iterative/models/farm_wakes_calc.py +18 -4
  7. foxes/constants.py +5 -0
  8. foxes/core/__init__.py +2 -1
  9. foxes/core/ground_model.py +254 -0
  10. foxes/core/model.py +3 -2
  11. foxes/core/partial_wakes_model.py +19 -3
  12. foxes/core/states.py +33 -0
  13. foxes/core/wake_model.py +138 -2
  14. foxes/data/__init__.py +1 -1
  15. foxes/data/states/WRF-Timeseries-3000.nc +0 -0
  16. foxes/data/states/windio_timeseries_5000.nc +0 -0
  17. foxes/data/static_data.py +7 -0
  18. foxes/data/windio/DTU_10MW_turbine.yaml +10 -0
  19. foxes/data/windio/__init__.py +0 -0
  20. foxes/data/windio/windio_5turbines_timeseries.yaml +63 -0
  21. foxes/input/states/__init__.py +1 -0
  22. foxes/input/states/multi_height.py +225 -6
  23. foxes/input/windio/__init__.py +6 -1
  24. foxes/input/windio/get_states.py +115 -0
  25. foxes/input/windio/read_attributes.py +321 -0
  26. foxes/input/windio/read_farm.py +163 -0
  27. foxes/input/windio/read_fields.py +164 -0
  28. foxes/input/windio/runner.py +105 -0
  29. foxes/input/windio/windio.py +136 -254
  30. foxes/models/__init__.py +2 -1
  31. foxes/models/ground_models/__init__.py +2 -0
  32. foxes/models/ground_models/no_ground.py +12 -0
  33. foxes/models/ground_models/wake_mirror.py +161 -0
  34. foxes/models/model_book.py +114 -164
  35. foxes/models/partial_wakes/axiwake.py +27 -4
  36. foxes/models/partial_wakes/top_hat.py +26 -4
  37. foxes/models/turbine_types/PCt_file.py +1 -0
  38. foxes/models/turbine_types/PCt_from_two.py +92 -0
  39. foxes/models/wake_frames/yawed_wakes.py +41 -38
  40. foxes/models/wake_models/__init__.py +0 -1
  41. foxes/models/wake_models/induction/__init__.py +1 -0
  42. foxes/models/wake_models/induction/rankine_half_body.py +1 -1
  43. foxes/models/wake_models/induction/vortex_sheet.py +227 -0
  44. foxes/models/wake_models/ti/crespo_hernandez.py +26 -24
  45. foxes/models/wake_models/ti/iec_ti.py +33 -26
  46. foxes/models/wake_models/wind/bastankhah14.py +11 -32
  47. foxes/models/wake_models/wind/bastankhah16.py +30 -34
  48. foxes/models/wake_models/wind/jensen.py +13 -29
  49. foxes/models/wake_models/wind/turbopark.py +31 -61
  50. foxes/models/wake_superpositions/ws_max.py +1 -0
  51. foxes/models/wake_superpositions/ws_pow.py +1 -0
  52. foxes/models/wake_superpositions/ws_quadratic.py +1 -0
  53. foxes/output/grids.py +6 -6
  54. foxes/output/output.py +6 -6
  55. foxes/utils/__init__.py +1 -1
  56. foxes/utils/factory.py +284 -76
  57. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/METADATA +8 -6
  58. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/RECORD +65 -51
  59. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/WHEEL +1 -1
  60. foxes/models/wake_models/wake_mirror.py +0 -196
  61. /foxes/models/{axial_induction_models → axial_induction}/__init__.py +0 -0
  62. /foxes/models/{axial_induction_models → axial_induction}/betz.py +0 -0
  63. /foxes/models/{axial_induction_models → axial_induction}/madsen.py +0 -0
  64. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/LICENSE +0 -0
  65. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/top_level.txt +0 -0
  66. {foxes-0.7.1.dist-info → foxes-0.7.3.dist-info}/zip-safe +0 -0
@@ -184,6 +184,7 @@ class WSPow(WakeSuperposition):
184
184
  w = np.minimum(w, self.lim_high - amb_results)
185
185
  return w
186
186
 
187
+
187
188
  class WSPowLocal(WakeSuperposition):
188
189
  """
189
190
  Local power supersposition of wind deficit results
@@ -179,6 +179,7 @@ class WSQuadratic(WakeSuperposition):
179
179
  w = np.minimum(w, self.lim_high - amb_results)
180
180
  return w
181
181
 
182
+
182
183
  class WSQuadraticLocal(WakeSuperposition):
183
184
  """
184
185
  Local quadratic supersposition of wind deficit results
foxes/output/grids.py CHANGED
@@ -108,12 +108,12 @@ def get_grid_xy(
108
108
  n_states = farm_results[FV.H].shape[0]
109
109
 
110
110
  # get base rectangle:
111
- x_min = xmin if xmin is not None else farm_results[FV.X].min() - xspace
112
- y_min = ymin if ymin is not None else farm_results[FV.Y].min() - yspace
113
- z_min = z if z is not None else farm_results[FV.H].min()
114
- x_max = xmax if xmax is not None else farm_results[FV.X].max() + xspace
115
- y_max = ymax if ymax is not None else farm_results[FV.Y].max() + yspace
116
- z_max = z if z is not None else farm_results[FV.H].max()
111
+ x_min = xmin if xmin is not None else farm_results[FV.X].min().to_numpy() - xspace
112
+ y_min = ymin if ymin is not None else farm_results[FV.Y].min().to_numpy() - yspace
113
+ z_min = z if z is not None else farm_results[FV.H].min().to_numpy()
114
+ x_max = xmax if xmax is not None else farm_results[FV.X].max().to_numpy() + xspace
115
+ y_max = ymax if ymax is not None else farm_results[FV.Y].max().to_numpy() + yspace
116
+ z_max = z if z is not None else farm_results[FV.H].max().to_numpy()
117
117
 
118
118
  x_pos, x_res = np.linspace(
119
119
  x_min,
foxes/output/output.py CHANGED
@@ -97,13 +97,13 @@ class Output:
97
97
  print(n)
98
98
 
99
99
  @classmethod
100
- def new(cls, model_type, *args, **kwargs):
100
+ def new(cls, output_type, *args, **kwargs):
101
101
  """
102
102
  Run-time output model factory.
103
103
 
104
104
  Parameters
105
105
  ----------
106
- model_type: string
106
+ output_type: string
107
107
  The selected derived class name
108
108
  args: tuple, optional
109
109
  Additional parameters for the constructor
@@ -112,19 +112,19 @@ class Output:
112
112
 
113
113
  """
114
114
 
115
- if model_type is None:
115
+ if output_type is None:
116
116
  return None
117
117
 
118
118
  allc = all_subclasses(cls)
119
- found = model_type in [scls.__name__ for scls in allc]
119
+ found = output_type in [scls.__name__ for scls in allc]
120
120
 
121
121
  if found:
122
122
  for scls in allc:
123
- if scls.__name__ == model_type:
123
+ if scls.__name__ == output_type:
124
124
  return scls(*args, **kwargs)
125
125
 
126
126
  else:
127
127
  estr = "Output type '{}' is not defined, available types are \n {}".format(
128
- model_type, sorted([i.__name__ for i in allc])
128
+ output_type, sorted([i.__name__ for i in allc])
129
129
  )
130
130
  raise KeyError(estr)
foxes/utils/__init__.py CHANGED
@@ -7,7 +7,7 @@ from .pandas_utils import PandasFileHelper
7
7
  from .xarray_utils import write_nc
8
8
  from .subclasses import all_subclasses
9
9
  from .dict import Dict
10
- from .factory import Factory, FDict
10
+ from .factory import Factory, FDict, WakeKFactory
11
11
  from .data_book import DataBook
12
12
  from .cubic_roots import cubic_roots
13
13
  from .geopandas_utils import read_shp, shp2csv, read_shp_polygons, shp2geom2d
foxes/utils/factory.py CHANGED
@@ -1,10 +1,12 @@
1
1
  from .dict import Dict
2
+ import foxes.variables as FV
3
+
2
4
 
3
5
  class Factory:
4
6
  """
5
- Constructs objects from a choice of allowed
7
+ Constructs objects from a choice of allowed
6
8
  constructor parameters
7
-
9
+
8
10
  Attributes
9
11
  ----------
10
12
  base: class
@@ -22,25 +24,26 @@ class Factory:
22
24
  Hints for print_toc, only for variables for which the
23
25
  options are functions or missing
24
26
  options: dict
25
- For each variable, e.g. A, B or C, the list or dict
27
+ For each variable, e.g. A, B or C, the list or dict
26
28
  or function that maps a str to the actual value
27
29
 
28
30
  :group: utils
29
-
31
+
30
32
  """
33
+
31
34
  def __init__(
32
- self,
33
- base,
34
- name_template,
35
- args=(),
36
- kwargs={},
37
- var2arg={},
38
- hints={},
39
- **options,
40
- ):
35
+ self,
36
+ base,
37
+ name_template,
38
+ args=(),
39
+ kwargs={},
40
+ var2arg={},
41
+ hints={},
42
+ **options,
43
+ ):
41
44
  """
42
45
  Constructor.
43
-
46
+
44
47
  Parameters
45
48
  ----------
46
49
  base: class
@@ -58,9 +61,9 @@ class Factory:
58
61
  Hints for print_toc, only for variables for which the
59
62
  options are functions or missing
60
63
  options: dict
61
- For each variable, e.g. A, B or C, the list or dict
64
+ For each variable, e.g. A, B or C, the list or dict
62
65
  or function that maps a str to the actual value
63
-
66
+
64
67
  """
65
68
  self.base = base
66
69
  self.name_template = name_template
@@ -69,17 +72,21 @@ class Factory:
69
72
  self.var2arg = var2arg
70
73
  self.hints = hints
71
74
 
72
- parts = name_template.split(">")
73
- if len(parts) < 2:
74
- raise ValueError(f"Factory '{name_template}': Expecting at least one variable in template, pattern '<..>'")
75
-
76
75
  self._vars = []
77
76
  self._pre = []
77
+ parts = name_template.split(">")
78
+
78
79
  for i, p in enumerate(parts):
79
80
  if i < len(parts) - 1:
80
81
  parts2 = p.split("<")
81
82
  if len(parts2) != 2:
82
- raise ValueError(f"Factory '{name_template}': incomplete pattern brackets '<..>'")
83
+ raise ValueError(
84
+ f"Factory '{name_template}': incomplete pattern brackets '<..>' between variables, e.g. '_'"
85
+ )
86
+ if i > 0 and len(parts2[0]) == 0:
87
+ raise ValueError(
88
+ f"Factory '{name_template}': Missing seperator like '_' in template between variables '{self._vars[-1]}' and '{parts[1]}'"
89
+ )
83
90
  self._pre.append(parts2[0])
84
91
  self._vars.append(parts2[1])
85
92
  else:
@@ -89,34 +96,42 @@ class Factory:
89
96
  for vi, v in enumerate(self.variables):
90
97
  p = self._pre[vi]
91
98
  if vi < len(self.variables) - 1 and p == "":
92
- raise ValueError(f"Factory '{name_template}': Require indicator before variable '{v}' in template, e.g. '{v}<{v}>'")
93
-
99
+ raise ValueError(
100
+ f"Factory '{name_template}': Require indicator before variable '{v}' in template, e.g. '{v}<{v}>'"
101
+ )
102
+
94
103
  self.options = Dict(name=f"{self._pre[0]}_options")
95
- for v, o in options.items():
104
+ for v, o in options.items():
96
105
  if v not in self.variables:
97
- raise KeyError(f"Factory '{name_template}': Variable '{v}' found in options, but not in template")
106
+ raise KeyError(
107
+ f"Factory '{name_template}': Variable '{v}' found in options, but not in template"
108
+ )
98
109
  if isinstance(o, list) or isinstance(o, tuple):
99
110
  o = {str(k): k for k in o}
100
111
  if isinstance(o, dict):
101
112
  for k in o.keys():
102
113
  if not isinstance(k, str):
103
- raise TypeError(f"Factory '{name_template}': Found option for variable '{v}' that is not a str, {k}")
114
+ raise TypeError(
115
+ f"Factory '{name_template}': Found option for variable '{v}' that is not a str, {k}"
116
+ )
104
117
  self.options[v] = Dict(name=f"{self._pre[0]}_options_{v}", **o)
105
118
  elif hasattr(o, "__call__"):
106
119
  self.options[v] = o
107
120
  else:
108
- raise ValueError(f"Factory '{name_template}': Variable '{v}' has option of type '{type(v).__name__}'. Only list, tuple, dict or function are supported")
109
-
121
+ raise ValueError(
122
+ f"Factory '{name_template}': Variable '{v}' has option of type '{type(v).__name__}'. Only list, tuple, dict or function are supported"
123
+ )
124
+
110
125
  @property
111
126
  def name_prefix(self):
112
127
  """
113
128
  The beginning of the name template
114
-
129
+
115
130
  Returns
116
131
  -------
117
132
  nbase: str
118
133
  The beginning of the name template
119
-
134
+
120
135
  """
121
136
  return self._pre[0]
122
137
 
@@ -124,30 +139,30 @@ class Factory:
124
139
  def name_suffix(self):
125
140
  """
126
141
  The ending of the name template
127
-
142
+
128
143
  Returns
129
144
  -------
130
145
  nbase: str
131
146
  The ending of the name template
132
-
147
+
133
148
  """
134
149
  return self._pre[-1]
135
-
150
+
136
151
  @property
137
152
  def variables(self):
138
153
  """
139
154
  The list of variables
140
-
155
+
141
156
  Returns
142
157
  -------
143
158
  vrs: list of str
144
159
  The variables
145
-
160
+
146
161
  """
147
162
  return self._vars
148
-
163
+
149
164
  def __str__(self):
150
- """ String representation """
165
+ """String representation"""
151
166
  s = f"{self.name_template}: {self.base.__name__} with"
152
167
  for k, d in self.kwargs.items():
153
168
  s += f"\n {k}={d}"
@@ -157,11 +172,11 @@ class Factory:
157
172
  else:
158
173
  s += f"\n {v}={self.hints.get(v, '(value)')}"
159
174
  return s
160
-
175
+
161
176
  def check_match(self, name):
162
177
  """
163
178
  Tests if a name matches the template
164
-
179
+
165
180
  Parameters
166
181
  ----------
167
182
  name: str
@@ -171,7 +186,7 @@ class Factory:
171
186
  -------
172
187
  success: bool
173
188
  True if the template is matched
174
-
189
+
175
190
  """
176
191
  data_str = name
177
192
  for vi in range(len(self.variables)):
@@ -182,7 +197,7 @@ class Factory:
182
197
  return False
183
198
  data_str = data_str[j:]
184
199
 
185
- q = self._pre[vi+1]
200
+ q = self._pre[vi + 1]
186
201
  if q != "":
187
202
  i = data_str.find(q)
188
203
  j = i + len(q)
@@ -190,47 +205,42 @@ class Factory:
190
205
  return False
191
206
  else:
192
207
  data_str = ""
193
-
208
+
194
209
  return True
195
210
 
196
211
  def construct(self, name):
197
212
  """
198
213
  Create an object of the base class.
199
-
214
+
200
215
  Parameters
201
216
  ----------
202
217
  name: str
203
218
  The name, matching the template
204
-
219
+
205
220
  Returns
206
221
  -------
207
222
  obj: object
208
223
  The instance of the base class
209
-
224
+
210
225
  """
211
- data_str = name
226
+ j = 0
212
227
  wlist = []
213
- for vi, v in enumerate(self.variables):
214
- p = self._pre[vi]
215
- i = data_str.find(p)
216
- j = i + len(p)
217
- if i < 0 or len(data_str) <= j:
218
- raise ValueError(f"Factory '{self.name_template}': Name '{name}' not matching template")
219
- data_str = data_str[j:]
220
-
221
- q = self._pre[vi+1]
222
- if q != "":
223
- i = data_str.find(q)
224
- j = i + len(q)
225
- if i < 0 or len(data_str) <= j:
226
- raise ValueError(f"Factory '{self.name_template}': Name '{name}' not matching template")
227
- wlist.append(data_str[:i])
228
+ for pi, p in enumerate(self._pre):
229
+ if len(p) > 0:
230
+ i = name[j:].find(p)
231
+ if i < 0 or (pi == 0 and i > 0):
232
+ raise ValueError(
233
+ f"Factory '{self.name_template}': Name '{name}' not matching template"
234
+ )
235
+ w = name[j : j + i]
236
+ j += i + len(p)
228
237
  else:
229
- wlist.append(data_str)
230
- data_str = ""
238
+ w = name[j:]
239
+ if pi > 0:
240
+ wlist.append(w)
231
241
 
232
242
  kwargs = {}
233
- for vi, v in enumerate(self.variables):
243
+ for vi, v in enumerate(self.variables):
234
244
  w = self.var2arg.get(v, v)
235
245
  data = wlist[vi]
236
246
  if v in self.options:
@@ -246,24 +256,206 @@ class Factory:
246
256
 
247
257
  return self.base(*self.args, **kwargs)
248
258
 
259
+
260
+ class WakeKFactory:
261
+ """
262
+ A factory that automatically handles
263
+ wake_k parameters
264
+
265
+ Attributes
266
+ ----------
267
+ factories: list of Factory
268
+ The individual factories
269
+
270
+ :group: utils
271
+
272
+ """
273
+
274
+ def __init__(self, base, name_template, *args, hints={}, **kwargs):
275
+ """
276
+ Constructor.
277
+
278
+ Parameters
279
+ ----------
280
+ base: class
281
+ The class of which objects are to be created
282
+ name_template: str
283
+ The name template, e.g. 'name_<A>_<B>_<C>' for
284
+ variables A, B, C. Indicate wake_k part by '_[wake_k]'
285
+ args: tuple, optional
286
+ Additional arguments for Factory
287
+ hints: dict
288
+ Hints for print_toc, only for variables for which the
289
+ options are functions or missing
290
+ kwargs: dict
291
+ Additional arguments for Factory
292
+
293
+ """
294
+ self._base = base
295
+ self._kwargs = kwargs
296
+ self._template0 = name_template
297
+ self.factories = []
298
+
299
+ i0 = name_template.find("_[wake_k]")
300
+ i1 = i0 + len("_[wake_k]")
301
+ kw = kwargs.pop("kwargs", {})
302
+
303
+ if i0 < 0:
304
+ raise ValueError(
305
+ f"String '_[wake_k]' not found in name template '{name_template}'"
306
+ )
307
+
308
+ # add case ka, kb:
309
+ t = name_template[:i0] + "_ka<ka>_kb<kb>"
310
+ if len(name_template) > i1:
311
+ t += name_template[i1:]
312
+ h = hints.copy()
313
+ h["ka"] = "(Value, e.g. 04 for 0.4)"
314
+ h["kb"] = "(Value, e.g. 001 for 0.01)"
315
+ kw["ti_var"] = FV.TI
316
+ self.factories.append(
317
+ Factory(
318
+ base,
319
+ t,
320
+ *args,
321
+ hints=h,
322
+ kwargs=kw.copy(),
323
+ **kwargs,
324
+ ka=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
325
+ kb=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
326
+ )
327
+ )
328
+
329
+ # add case ambient ka, kb:
330
+ t = name_template[:i0] + "_ambka<ka>_kb<kb>"
331
+ if len(name_template) > i1:
332
+ t += name_template[i1:]
333
+ h = hints.copy()
334
+ h["ka"] = "(Value, e.g. 04 for 0.4)"
335
+ h["kb"] = "(Value, e.g. 001 for 0.01)"
336
+ kw["ti_var"] = FV.AMB_TI
337
+ self.factories.append(
338
+ Factory(
339
+ base,
340
+ t,
341
+ *args,
342
+ hints=h,
343
+ kwargs=kw.copy(),
344
+ **kwargs,
345
+ ka=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
346
+ kb=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
347
+ )
348
+ )
349
+
350
+ # add case ka:
351
+ t = name_template[:i0] + "_ka<ka>"
352
+ if len(name_template) > i1:
353
+ t += name_template[i1:]
354
+ h = hints.copy()
355
+ h["ka"] = "(Value, e.g. 04 for 0.4)"
356
+ kw["ti_var"] = FV.TI
357
+ self.factories.append(
358
+ Factory(
359
+ base,
360
+ t,
361
+ *args,
362
+ hints=h,
363
+ kwargs=kw.copy(),
364
+ **kwargs,
365
+ ka=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
366
+ )
367
+ )
368
+
369
+ # add case ka:
370
+ t = name_template[:i0] + "_ambka<ka>"
371
+ if len(name_template) > i1:
372
+ t += name_template[i1:]
373
+ h = hints.copy()
374
+ h["ka"] = "(Value, e.g. 04 for 0.4)"
375
+ kw["ti_var"] = FV.AMB_TI
376
+ self.factories.append(
377
+ Factory(
378
+ base,
379
+ t,
380
+ *args,
381
+ hints=h,
382
+ kwargs=kw.copy(),
383
+ **kwargs,
384
+ ka=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
385
+ )
386
+ )
387
+
388
+ # add case k:
389
+ t = name_template[:i0] + "_k<k>"
390
+ if len(name_template) > i1:
391
+ t += name_template[i1:]
392
+ h = hints.copy()
393
+ h["k"] = "(Value, e.g. 004 for 0.04)"
394
+ kw["ti_var"] = FV.TI
395
+ self.factories.append(
396
+ Factory(
397
+ base,
398
+ t,
399
+ *args,
400
+ hints=h,
401
+ kwargs=kw.copy(),
402
+ **kwargs,
403
+ k=lambda x: float(f"0.{x[1:]}" if x[0] == "0" else float(x)),
404
+ )
405
+ )
406
+
407
+ # add case without k:
408
+ t = name_template[:i0]
409
+ if len(name_template) > i1:
410
+ t += name_template[i1:]
411
+ kw["ti_var"] = FV.TI
412
+ self.factories.append(
413
+ Factory(
414
+ base,
415
+ t,
416
+ *args,
417
+ hints=hints,
418
+ kwargs=kw.copy(),
419
+ **kwargs,
420
+ )
421
+ )
422
+
423
+ def __str__(self):
424
+ """String representation"""
425
+ s = f"{self._template0}: {self._base.__name__} with"
426
+ for k, d in self._kwargs.items():
427
+ s += f"\n {k}={d}"
428
+ f0 = self.factories[-1]
429
+ for v in f0.variables:
430
+ if v in f0.options and isinstance(f0.options[v], dict):
431
+ s += f"\n {v} from {list(f0.options[v])}"
432
+ else:
433
+ s += f"\n {v}={f0.hints.get(v, '(value)')}"
434
+ s += (
435
+ f"\n [wake_k]=(None or k<k> or ka<ka> or ka<ka>_kb<kb>, e.g. 004 for 0.04)"
436
+ )
437
+ return s
438
+
439
+
249
440
  class FDict(Dict):
250
441
  """
251
442
  A dictionary with factory support
252
-
443
+
253
444
  Attributes
254
445
  ----------
255
446
  store_created: bool
256
447
  Flag for storing created objects
257
448
  factories: list of foxes.utils.Factory
258
449
  The factories
259
-
450
+
260
451
  :group: utils
261
-
452
+
262
453
  """
454
+
263
455
  def __init__(self, *args, store_created=True, **kwargs):
264
456
  """
265
457
  Constructor.
266
-
458
+
267
459
  Parameters
268
460
  ----------
269
461
  args: tuple, optional
@@ -272,30 +464,32 @@ class FDict(Dict):
272
464
  Flag for storing created objects
273
465
  kwargs: dict, optional
274
466
  Parameters for the base class
275
-
467
+
276
468
  """
277
469
  super().__init__(*args, **kwargs)
278
470
  self.store_created = store_created
279
471
  self.factories = []
280
472
 
281
- def add_factory(self, *args, **kwargs):
473
+ def add_factory(self, *args, factory=None, **kwargs):
282
474
  """
283
- Constructor.
284
-
475
+ Adds a Factory object.
476
+
285
477
  Parameters
286
478
  ----------
287
479
  args: tuple, optional
288
480
  Parameters for the Factory constructor
481
+ factory: Factory, optional
482
+ The factory object
289
483
  kwargs: dict, optional
290
484
  Parameters for the Factory constructor
291
-
485
+
292
486
  """
293
- f = Factory(*args, **kwargs)
487
+ f = Factory(*args, **kwargs) if factory is None else factory
294
488
  i = len(self.factories)
295
489
  for gi in range(len(self.factories) - 1, -1, -1):
296
490
  g = self.factories[gi]
297
491
  if (
298
- g.name_prefix == f.name_prefix
492
+ g.name_prefix == f.name_prefix
299
493
  and g.name_suffix == f.name_suffix
300
494
  and len(f.variables) > len(g.variables)
301
495
  ):
@@ -306,6 +500,21 @@ class FDict(Dict):
306
500
  else:
307
501
  self.factories.insert(i, f)
308
502
 
503
+ def add_k_factory(self, *args, **kwargs):
504
+ """
505
+ Adds a WakeKFactory.
506
+
507
+ Parameters
508
+ ----------
509
+ args: tuple, optional
510
+ Parameters for the Factory constructor
511
+ kwargs: dict, optional
512
+ Parameters for the Factory constructor
513
+
514
+ """
515
+ for f in WakeKFactory(*args, **kwargs).factories:
516
+ self.add_factory(factory=f)
517
+
309
518
  def __contains__(self, key):
310
519
  found = super().__contains__(key)
311
520
  if not found:
@@ -330,4 +539,3 @@ class FDict(Dict):
330
539
  k = ", ".join(sorted(list(self.keys())))
331
540
  e = f"{self.name}: Cannot find key '{key}', also no factory matches. Known keys: {k}. Known factories: {[f.name_template for f in self.factories]}"
332
541
  raise KeyError(e)
333
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foxes
3
- Version: 0.7.1
3
+ Version: 0.7.3
4
4
  Summary: Farm Optimization and eXtended yield Evaluation Software
5
5
  Author: Fraunhofer IWES
6
6
  Author-email: jonas.schmidt@iwes.fraunhofer.de
@@ -17,16 +17,18 @@ Requires-Python: >=3.8
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: matplotlib
20
- Requires-Dist: numpy
20
+ Requires-Dist: numpy <2
21
+ Requires-Dist: pandas
21
22
  Requires-Dist: xarray
22
- Requires-Dist: dask[distributed]
23
+ Requires-Dist: dask
24
+ Requires-Dist: distributed
23
25
  Requires-Dist: scipy
24
26
  Requires-Dist: netcdf4
25
27
  Requires-Dist: windrose
26
28
  Requires-Dist: iwopy >=0.1.4
27
29
  Requires-Dist: pyarrow
28
30
  Provides-Extra: all
29
- Requires-Dist: windio >=1.0 ; extra == 'all'
31
+ Requires-Dist: windio >=1 ; extra == 'all'
30
32
  Requires-Dist: flake8 ; extra == 'all'
31
33
  Requires-Dist: pytest ; extra == 'all'
32
34
  Requires-Dist: pymoo >=0.6 ; extra == 'all'
@@ -45,7 +47,7 @@ Requires-Dist: ipywidgets ; extra == 'doc'
45
47
  Requires-Dist: m2r2 ; extra == 'doc'
46
48
  Requires-Dist: lxml-html-clean ; extra == 'doc'
47
49
  Provides-Extra: io
48
- Requires-Dist: windio >=1.0 ; extra == 'io'
50
+ Requires-Dist: windio >=1 ; extra == 'io'
49
51
  Provides-Extra: scripts
50
52
  Provides-Extra: test
51
53
  Requires-Dist: flake8 ; extra == 'test'
@@ -170,7 +172,7 @@ The supported Python versions are:
170
172
  - `Python 3.11`
171
173
  - `Python 3.12`
172
174
 
173
- ### Preparation
175
+ ### Preparation (optional)
174
176
 
175
177
  It is strongly recommend to use the `libmamba` dependency solver instead of the default solver. Install it once by
176
178