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
foxes/input/yaml/dict.py CHANGED
@@ -5,13 +5,13 @@ from inspect import signature
5
5
  import foxes.input.farm_layout as farm_layout
6
6
  from foxes.core import States, Engine, WindFarm, Algorithm
7
7
  from foxes.models import ModelBook
8
- from foxes import output
9
- from foxes.utils import Dict
8
+ from foxes.output import Output
9
+ from foxes.utils import Dict, new_cls
10
10
  from foxes.config import config
11
11
  import foxes.constants as FC
12
12
 
13
13
 
14
- def run_dict(
14
+ def read_dict(
15
15
  idict,
16
16
  farm=None,
17
17
  states=None,
@@ -25,7 +25,7 @@ def run_dict(
25
25
  **algo_pars,
26
26
  ):
27
27
  """
28
- Runs foxes from dictionary input
28
+ Read dictionary input into foxes objects
29
29
 
30
30
  Parameters
31
31
  ----------
@@ -58,12 +58,10 @@ def run_dict(
58
58
 
59
59
  Returns
60
60
  -------
61
- farm_results: xarray.Dataset, optional
62
- The farm results
63
- point_results: xarray.Dataset, optional
64
- The point results
65
- output_i: object
66
- For each output either None or the output result
61
+ algo: foxes.core.Algorithm
62
+ The algorithm
63
+ engine: foxes.core.Engine
64
+ The engine, or None if not set
67
65
 
68
66
  :group: input.yaml
69
67
 
@@ -137,12 +135,220 @@ def run_dict(
137
135
  if verbosity is not None:
138
136
  adict["verbosity"] = verbosity - 1
139
137
  if algo_pars is not None:
140
- adict.update(algo_pars)
138
+ adict.update({v: d for v, d in algo_pars.items() if d is not None})
141
139
  algo = Algorithm.new(**adict)
142
140
 
141
+ return algo, engine
142
+
143
+
144
+ def run_outputs(
145
+ idict,
146
+ algo=None,
147
+ farm_results=None,
148
+ point_results=None,
149
+ verbosity=None,
150
+ ):
151
+ """
152
+ Run outputs from dict.
153
+
154
+ Parameters
155
+ ----------
156
+ idict: foxes.utils.Dict
157
+ The input parameter dictionary
158
+ algo: foxes.core.Algorithm, optional
159
+ The algorithm
160
+ farm_results: xarray.Dataset, optional
161
+ The farm results
162
+ point_results: xarray.Dataset, optional
163
+ The point results
164
+ verbosity: int, optional
165
+ Force a verbosity level, 0 = silent, overrules
166
+ settings from idict
167
+
168
+ Returns
169
+ -------
170
+ outputs: list of tuple
171
+ For each output enty, a tuple (dict, results),
172
+ where results is a tuple that represents one
173
+ entry per function call
174
+
175
+ :group: input.yaml
176
+
177
+ """
178
+
179
+ def _print(*args, level=1, **kwargs):
180
+ if verbosity is None or verbosity >= level:
181
+ print(*args, **kwargs)
182
+
183
+ out = []
184
+ if "outputs" in idict:
185
+ _print("Running outputs")
186
+ odicts = [
187
+ Dict(odict, name=f"{idict.name}.output{i}")
188
+ for i, odict in enumerate(idict["outputs"])
189
+ ]
190
+
191
+ rlabels = Dict(name="result_labels")
192
+
193
+ def _get_object(rlabels, d):
194
+ d = d.replace("]", "")
195
+ i0 = d.find("[")
196
+ if i0 > 0:
197
+ inds = tuple([int(x) for x in d[i0 + 1 :].split(",")])
198
+ return rlabels[d[:i0]][inds]
199
+ else:
200
+ return rlabels[d]
201
+
202
+ for i, d in enumerate(odicts):
203
+
204
+ if "output_type" in d:
205
+ ocls = d.pop_item("output_type")
206
+ _print(f" Output {i}: {ocls}")
207
+ d0 = dict(output_type=ocls)
208
+ d0.update(d)
209
+ flist = [
210
+ Dict(f, name=f"{d.name}.function{i}")
211
+ for i, f in enumerate(d.pop_item("functions"))
212
+ ]
213
+ cls = new_cls(Output, ocls)
214
+ prs = list(signature(cls.__init__).parameters.keys())
215
+ if "algo" in prs:
216
+ assert (
217
+ algo is not None
218
+ ), f"Output {i} of type '{ocls}' requires algo"
219
+ d["algo"] = algo
220
+ if "farm" in prs:
221
+ d["farm"] = algo.farm
222
+ if "farm_results" in prs:
223
+ if farm_results is None:
224
+ print(f"No farm results; skipping output {ocls}")
225
+ for fdict in flist:
226
+ out += (d0, None)
227
+ continue
228
+ d["farm_results"] = farm_results
229
+ if "point_results" in prs:
230
+ d["point_results"] = point_results
231
+ o = cls(**d)
232
+
233
+ elif "object" in d:
234
+ ocls = d.pop("object")
235
+ o = _get_object(rlabels, ocls)
236
+ d0 = dict(object=ocls)
237
+ d0.update(d)
238
+ flist = [
239
+ Dict(f, name=f"{d.name}.function{i}")
240
+ for i, f in enumerate(d.pop_item("functions"))
241
+ ]
242
+
243
+ else:
244
+ raise KeyError(
245
+ f"Output {i} of type '{ocls}': Please specify either 'output_type' or 'object'"
246
+ )
247
+
248
+ fres = []
249
+ for fdict in flist:
250
+ fname = fdict.pop_item("function")
251
+ _print(f" - {fname}")
252
+ plt_show = fdict.pop("plt_show", False)
253
+ plt_close = fdict.pop("plt_close", False)
254
+ rlbs = fdict.pop("result_labels", None)
255
+
256
+ # grab function:
257
+ assert hasattr(
258
+ o, fname
259
+ ), f"Output {i} of type '{ocls}': Function '{fname}' not found"
260
+ f = getattr(o, fname)
261
+
262
+ # add required input data objects:
263
+ prs = list(signature(f).parameters.keys())
264
+ if "algo" in prs:
265
+ fdict["algo"] = algo
266
+ if "farm" in prs:
267
+ fdict["farm"] = algo.farm
268
+
269
+ # replace result labels by objects:
270
+ for k, d in fdict.items():
271
+ if isinstance(d, str) and d[0] == "$":
272
+ fdict[k] = _get_object(rlabels, d)
273
+
274
+ # run function:
275
+ args = fdict.pop("args", tuple())
276
+ results = f(*args, **fdict)
277
+
278
+ # pyplot shortcuts:
279
+ if plt_show:
280
+ plt.show()
281
+ if plt_close:
282
+ results = None
283
+ plt.close()
284
+
285
+ # store results under result labels:
286
+ if rlbs is not None:
287
+
288
+ def _set_label(rlabels, k, r):
289
+ if k not in ["", "none", "None", "_", "__"]:
290
+ assert (
291
+ k[0] == "$"
292
+ ), f"Output {i} of type '{ocls}', function '{fname}': result labels must start with '$', got '{k}'"
293
+ assert (
294
+ "[" not in k and "]" not in k and "," not in k
295
+ ), f"Output {i} of type '{ocls}', function '{fname}': result labels cannot contain '[' or ']' or comma, got '{k}'"
296
+ _print(f" result label {k}: {type(r).__name__}")
297
+ rlabels[k] = r
298
+
299
+ if isinstance(rlbs, (list, tuple)):
300
+ for i, k in enumerate(rlbs):
301
+ _set_label(rlabels, k, results[i])
302
+ else:
303
+ _set_label(rlabels, rlbs, results)
304
+
305
+ fres.append(results)
306
+ out.append((d0, fres))
307
+
308
+ return out
309
+
310
+
311
+ def run_dict(idict, *args, verbosity=None, **kwargs):
312
+ """
313
+ Runs foxes from dictionary input
314
+
315
+ Parameters
316
+ ----------
317
+ idict: foxes.utils.Dict
318
+ The input parameter dictionary
319
+ args: tuple, optional
320
+ Additional parameters for read_dict
321
+ verbosity: int, optional
322
+ Force a verbosity level, 0 = silent, overrules
323
+ settings from idict
324
+ kwargs: dict, optional
325
+ Additional parameters for read_dict
326
+
327
+ Returns
328
+ -------
329
+ farm_results: xarray.Dataset, optional
330
+ The farm results
331
+ point_results: xarray.Dataset, optional
332
+ The point results
333
+ outputs: list of tuple
334
+ For each output enty, a tuple (dict, results),
335
+ where results is a tuple that represents one
336
+ entry per function call
337
+
338
+ :group: input.yaml
339
+
340
+ """
341
+
342
+ def _print(*args, level=1, **kwargs):
343
+ if verbosity is None or verbosity >= level:
344
+ print(*args, **kwargs)
345
+
346
+ # read components:
347
+ algo, engine = read_dict(idict, *args, verbosity=verbosity, **kwargs)
348
+
143
349
  # run farm calculation:
144
- rdict = idict.get_item("calc_farm")
145
- if rdict.pop_item("run"):
350
+ rdict = idict.get_item("calc_farm", Dict(name=idict.name + ".calc_farm"))
351
+ if rdict.pop_item("run", True):
146
352
  _print("Running calc_farm")
147
353
  farm_results = algo.calc_farm(**rdict)
148
354
  else:
@@ -150,6 +356,7 @@ def run_dict(
150
356
  out = (farm_results,)
151
357
 
152
358
  # run points calculation:
359
+ point_results = None
153
360
  if "calc_points" in idict:
154
361
  rdict = idict.get_item("calc_points")
155
362
  if rdict.pop_item("run"):
@@ -164,43 +371,7 @@ def run_dict(
164
371
  out += (point_results,)
165
372
 
166
373
  # run outputs:
167
- if "outputs" in idict:
168
- _print("Running outputs")
169
- odict = idict["outputs"]
170
- for ocls, d in odict.items():
171
- _print(f" Output {ocls}")
172
- flist = [
173
- Dict(f, name=f"{d.name}.function{i}")
174
- for i, f in enumerate(d.pop_item("functions"))
175
- ]
176
- try:
177
- cls = getattr(output, ocls)
178
- except AttributeError as e:
179
- print(f"\nClass '{ocls}' not found in outputs. Found:")
180
- prs = list(signature(cls.__init__).parameters.keys())
181
- if "algo" in prs:
182
- d["algo"] = algo
183
- if "farm_results" in prs:
184
- if farm_results is None:
185
- print(f"No farm results; skipping output {ocls}")
186
- for fdict in flist:
187
- out += (None,)
188
- continue
189
- d["farm_results"] = farm_results
190
- o = cls(**d)
191
- for fdict in flist:
192
- fname = fdict.pop_item("name")
193
- _print(f" - {fname}")
194
- plt_show = fdict.pop("plt_show", False)
195
- f = getattr(o, fname)
196
- prs = list(signature(f).parameters.keys())
197
- if "algo" in prs:
198
- fdict["algo"] = algo
199
- res = f(**fdict)
200
- out += (res,) if not isinstance(res, tuple) else res
201
- if plt_show:
202
- plt.show()
203
- plt.close()
374
+ out += (run_outputs(idict, algo, farm_results, point_results, verbosity),)
204
375
 
205
376
  # shutdown engine, if created above:
206
377
  if engine is not None:
foxes/input/yaml/yaml.py CHANGED
@@ -24,18 +24,18 @@ def foxes_yaml():
24
24
  help="The input yaml file",
25
25
  )
26
26
  parser.add_argument("-o", "--out_dir", help="The output directory", default=".")
27
- parser.add_argument("-r", "--rotor", help="The rotor model", default="centre")
27
+ parser.add_argument("-r", "--rotor", help="The rotor model", default=None)
28
28
  parser.add_argument(
29
- "-p", "--pwakes", help="The partial wakes models", default="centre", nargs="+"
29
+ "-p", "--pwakes", help="The partial wakes models", default=None, nargs="+"
30
30
  )
31
31
  parser.add_argument(
32
32
  "-w",
33
33
  "--wakes",
34
34
  help="The wake models",
35
- default=["Jensen_linear_k007"],
35
+ default=None,
36
36
  nargs="+",
37
37
  )
38
- parser.add_argument("-f", "--frame", help="The wake frame", default="rotor_wd")
38
+ parser.add_argument("-f", "--frame", help="The wake frame", default=None)
39
39
  parser.add_argument("-e", "--engine", help="The engine", default=None)
40
40
  parser.add_argument(
41
41
  "-n", "--n_procs", help="The number of processes", default=None, type=int
@@ -51,7 +51,7 @@ def foxes_yaml():
51
51
  "-C",
52
52
  "--chunksize_points",
53
53
  help="The chunk size for points",
54
- default=5000,
54
+ default=None,
55
55
  type=int,
56
56
  )
57
57
  parser.add_argument(
foxes/output/__init__.py CHANGED
@@ -6,7 +6,7 @@ from .round import round_defaults
6
6
  from .output import Output
7
7
  from .farm_layout import FarmLayoutOutput
8
8
  from .farm_results_eval import FarmResultsEval
9
- from .rose_plot import RosePlotOutput, StatesRosePlotOutput
9
+ from .rose_plot import RosePlotOutput, StatesRosePlotOutput, WindRoseBinPlot
10
10
  from .results_writer import ResultsWriter
11
11
  from .state_turbine_map import StateTurbineMap
12
12
  from .turbine_type_curves import TurbineTypeCurves
@@ -15,6 +15,7 @@ from .calc_points import PointCalculator
15
15
  from .slice_data import SliceData
16
16
  from .rotor_point_plots import RotorPointPlot
17
17
  from .state_turbine_table import StateTurbineTable
18
+ from .plt import plt
18
19
 
19
20
  from .flow_plots_2d import FlowPlots2D
20
21
  from .seq_plugins import SeqFlowAnimationPlugin, SeqWakeDebugPlugin
@@ -64,7 +64,12 @@ class FarmResultsEval(Output):
64
64
  fields = []
65
65
  for v in vars:
66
66
  if isinstance(v, str):
67
- fields.append(self.results[v].to_numpy())
67
+ vdata = self.results[v].to_numpy()
68
+ nns = np.sum(np.isnan(vdata))
69
+ assert (
70
+ nns == 0
71
+ ), f"Found {nns} nan values for variable '{v}' of shape {vdata.shape}"
72
+ fields.append(vdata)
68
73
  else:
69
74
  fields.append(v)
70
75
  if nas is None:
@@ -99,7 +104,7 @@ class FarmResultsEval(Output):
99
104
  vars_op: dict
100
105
  The operation per variable. Key: str, the variable
101
106
  name. Value: str, the operation, choices
102
- are: sum, mean, min, max.
107
+ are: weights, mean_no_weights, sum, min, max.
103
108
 
104
109
  Returns
105
110
  -------
@@ -111,23 +116,27 @@ class FarmResultsEval(Output):
111
116
 
112
117
  rdata = {}
113
118
  for v, op in vars_op.items():
114
- if op == "mean":
115
- rdata[v] = self.weinsum("t", v)
119
+ vdata = self.results[v].to_numpy()
120
+ nns = np.sum(np.isnan(vdata))
121
+ assert (
122
+ nns == 0
123
+ ), f"Found {nns} nan values for variable '{v}' of shape {vdata.shape}"
124
+
125
+ if op == "weights":
126
+ rdata[v] = self.weinsum("t", vdata)
127
+ elif op == "mean_no_weights":
128
+ rdata[v] = np.mean(vdata, axis=0)
116
129
  elif op == "sum":
117
- vdata = self.results[v].to_numpy()
118
130
  rdata[v] = np.sum(vdata, axis=0)
119
131
  elif op == "min":
120
- vdata = self.results[v].to_numpy()
121
132
  rdata[v] = np.min(vdata, axis=0)
122
133
  elif op == "max":
123
- vdata = self.results[v].to_numpy()
124
134
  rdata[v] = np.max(vdata, axis=0)
125
135
  elif op == "std":
126
- vdata = self.results[v].to_numpy()
127
136
  rdata[v] = np.std(vdata, axis=0)
128
137
  else:
129
138
  raise KeyError(
130
- f"Unknown operation '{op}' for variable '{v}'. Please choose: sum, mean, min, max"
139
+ f"Unknown operation '{op}' for variable '{v}'. Please choose: weights, mean_no_weights, sum, min, max"
131
140
  )
132
141
 
133
142
  data = pd.DataFrame(index=range(n_turbines), data=rdata)
@@ -144,7 +153,7 @@ class FarmResultsEval(Output):
144
153
  vars_op: dict
145
154
  The operation per variable. Key: str, the variable
146
155
  name. Value: str, the operation, choices
147
- are: sum, mean, min, max.
156
+ are: weights, mean_no_weights, sum, min, max.
148
157
 
149
158
  Returns
150
159
  -------
@@ -156,20 +165,25 @@ class FarmResultsEval(Output):
156
165
 
157
166
  rdata = {}
158
167
  for v, op in vars_op.items():
159
- if op == "mean":
160
- rdata[v] = self.weinsum("s", v)
168
+ vdata = self.results[v].to_numpy()
169
+ nns = np.sum(np.isnan(vdata))
170
+ assert (
171
+ nns == 0
172
+ ), f"Found {nns} nan values for variable '{v}' of shape {vdata.shape}"
173
+
174
+ if op == "weights":
175
+ rdata[v] = self.weinsum("s", vdata)
176
+ elif op == "mean_no_weights":
177
+ rdata[v] = np.mean(vdata, axis=1)
161
178
  elif op == "sum":
162
- vdata = self.results[v].to_numpy()
163
179
  rdata[v] = np.sum(vdata, axis=1)
164
180
  elif op == "min":
165
- vdata = self.results[v].to_numpy()
166
181
  rdata[v] = np.min(vdata, axis=1)
167
182
  elif op == "max":
168
- vdata = self.results[v].to_numpy()
169
183
  rdata[v] = np.max(vdata, axis=1)
170
184
  else:
171
185
  raise KeyError(
172
- f"Unknown operation '{op}' for variable '{v}'. Please choose: sum, mean, min, max"
186
+ f"Unknown operation '{op}' for variable '{v}'. Please choose: weights, mean_no_weights, sum, min, max"
173
187
  )
174
188
 
175
189
  data = pd.DataFrame(index=states, data=rdata)
@@ -205,29 +219,32 @@ class FarmResultsEval(Output):
205
219
  rdata = {}
206
220
  for v, op in turbines_op.items():
207
221
  vdata = sdata[v].to_numpy()
208
- if op == "mean":
209
- if states_op[v] == "mean":
222
+ nns = np.sum(np.isnan(vdata))
223
+ assert (
224
+ nns == 0
225
+ ), f"Found {nns} nan values for variable '{v}' of shape {vdata.shape}"
226
+
227
+ if op == "weights":
228
+ if states_op[v] == "weights":
210
229
  rdata[v] = self.weinsum("", v)
211
230
  else:
212
- vdata = sdata[v].to_numpy()
213
231
  rdata[v] = self.weinsum("", vdata[None, :])
232
+ elif op == "mean_no_weights":
233
+ rdata[v] = np.sum(vdata)
214
234
  elif op == "sum":
215
- vdata = sdata[v].to_numpy()
216
235
  rdata[v] = np.sum(vdata)
217
236
  elif op == "min":
218
- vdata = sdata[v].to_numpy()
219
237
  rdata[v] = np.min(vdata)
220
238
  elif op == "max":
221
- vdata = sdata[v].to_numpy()
222
239
  rdata[v] = np.max(vdata)
223
240
  else:
224
241
  raise KeyError(
225
- f"Unknown operation '{op}' for variable '{v}'. Please choose: sum, mean, min, max"
242
+ f"Unknown operation '{op}' for variable '{v}'. Please choose: sum, mean, min, max, weights"
226
243
  )
227
244
 
228
245
  return rdata
229
246
 
230
- def calc_states_mean(self, vars):
247
+ def calc_states_mean(self, vars, use_weights=True):
231
248
  """
232
249
  Calculates the mean wrt states.
233
250
 
@@ -235,6 +252,8 @@ class FarmResultsEval(Output):
235
252
  ----------
236
253
  vars: list of str
237
254
  The variables
255
+ use_weights: bool
256
+ Flag for using states weights for the mean
238
257
 
239
258
  Returns
240
259
  -------
@@ -242,9 +261,10 @@ class FarmResultsEval(Output):
242
261
  The results per turbine
243
262
 
244
263
  """
264
+ r = "weights" if use_weights else "mean_no_weights"
245
265
  if isinstance(vars, str):
246
- return self.reduce_states({vars: "mean"})
247
- return self.reduce_states({v: "mean" for v in vars})
266
+ return self.reduce_states({vars: r})
267
+ return self.reduce_states({v: r for v in vars})
248
268
 
249
269
  def calc_states_sum(self, vars):
250
270
  """
@@ -291,7 +311,7 @@ class FarmResultsEval(Output):
291
311
  The results per state
292
312
 
293
313
  """
294
- return self.reduce_turbines({v: "mean" for v in vars})
314
+ return self.reduce_turbines({v: "mean_no_weights" for v in vars})
295
315
 
296
316
  def calc_turbine_sum(self, vars):
297
317
  """
@@ -325,7 +345,7 @@ class FarmResultsEval(Output):
325
345
  The fully contracted results
326
346
 
327
347
  """
328
- op = {v: "mean" for v in vars}
348
+ op = {v: "weights" for v in vars}
329
349
  return self.reduce_all(states_op=op, turbines_op=op)
330
350
 
331
351
  def calc_farm_sum(self, vars):
@@ -362,7 +382,7 @@ class FarmResultsEval(Output):
362
382
 
363
383
  """
364
384
  v = FV.P if not ambient else FV.AMB_P
365
- cdata = self.reduce_all(states_op={v: "mean"}, turbines_op={v: "sum"})
385
+ cdata = self.reduce_all(states_op={v: "weights"}, turbines_op={v: "sum"})
366
386
  return cdata[v]
367
387
 
368
388
  def calc_turbine_yield(
@@ -536,10 +556,12 @@ class FarmResultsEval(Output):
536
556
  The verbosity level, 0 = silent
537
557
 
538
558
  """
539
- P = self.results[FV.P]
540
- P0 = self.results[FV.AMB_P] + 1e-14
541
- self.results[FV.EFF] = P / P0 # add to farm results
542
- if verbosity:
559
+ P = self.results[FV.P].to_numpy()
560
+ P0 = np.maximum(self.results[FV.AMB_P].to_numpy(), 1e-12)
561
+ eff = np.minimum(P / P0, 1)
562
+ eff[P < 1e-10] = 0
563
+ self.results[FV.EFF] = (self.results[FV.AMB_P].dims, eff)
564
+ if verbosity > 0:
543
565
  print("Efficiency added to farm results")
544
566
 
545
567
  def calc_farm_efficiency(self):
@@ -553,8 +575,8 @@ class FarmResultsEval(Output):
553
575
 
554
576
  """
555
577
  P = self.calc_mean_farm_power()
556
- P0 = self.calc_mean_farm_power(ambient=True) + 1e-14
557
- return P / P0
578
+ P0 = np.maximum(self.calc_mean_farm_power(ambient=True), 1e-14)
579
+ return np.minimum(P / P0, 1)
558
580
 
559
581
  def gen_stdata(
560
582
  self,
foxes/output/output.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from pathlib import Path
2
2
 
3
3
  from foxes.config import config, get_path
4
- from foxes.utils import PandasFileHelper, all_subclasses
4
+ from foxes.utils import PandasFileHelper, new_instance, all_subclasses
5
5
 
6
6
 
7
7
  class Output:
@@ -116,20 +116,4 @@ class Output:
116
116
  Additional parameters for the constructor
117
117
 
118
118
  """
119
-
120
- if output_type is None:
121
- return None
122
-
123
- allc = all_subclasses(cls)
124
- found = output_type in [scls.__name__ for scls in allc]
125
-
126
- if found:
127
- for scls in allc:
128
- if scls.__name__ == output_type:
129
- return scls(*args, **kwargs)
130
-
131
- else:
132
- estr = "Output type '{}' is not defined, available types are \n {}".format(
133
- output_type, sorted([i.__name__ for i in allc])
134
- )
135
- raise KeyError(estr)
119
+ return new_instance(cls, output_type, *args, **kwargs)
foxes/output/plt.py ADDED
@@ -0,0 +1,19 @@
1
+ from matplotlib import pyplot
2
+
3
+ from .output import Output
4
+
5
+
6
+ class plt(Output):
7
+ """
8
+ Class that runs plt commands
9
+
10
+ :group: output
11
+
12
+ """
13
+
14
+ def __getattr__(self, name):
15
+ return getattr(pyplot, name)
16
+
17
+ def savefig(self, fname, *args, **kwargs):
18
+ fpath = super().get_fpath(fname)
19
+ pyplot.savefig(fpath, *args, **kwargs)