foxes 0.6.1__py3-none-any.whl → 0.7__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 (129) hide show
  1. foxes/VERSION +1 -1
  2. foxes/algorithms/downwind/downwind.py +131 -65
  3. foxes/algorithms/downwind/models/__init__.py +2 -1
  4. foxes/algorithms/downwind/models/farm_wakes_calc.py +87 -55
  5. foxes/algorithms/downwind/models/init_farm_data.py +134 -0
  6. foxes/algorithms/downwind/models/point_wakes_calc.py +54 -65
  7. foxes/algorithms/downwind/models/{calc_order.py → reorder_farm_output.py} +28 -26
  8. foxes/algorithms/iterative/iterative.py +100 -51
  9. foxes/algorithms/iterative/models/convergence.py +3 -3
  10. foxes/algorithms/iterative/models/farm_wakes_calc.py +55 -48
  11. foxes/algorithms/sequential/models/seq_state.py +7 -6
  12. foxes/algorithms/sequential/sequential.py +81 -44
  13. foxes/constants.py +33 -18
  14. foxes/core/__init__.py +2 -2
  15. foxes/core/algorithm.py +31 -12
  16. foxes/core/data.py +335 -41
  17. foxes/core/data_calc_model.py +27 -23
  18. foxes/core/farm_controller.py +27 -28
  19. foxes/core/farm_data_model.py +26 -4
  20. foxes/core/model.py +186 -129
  21. foxes/core/partial_wakes_model.py +84 -81
  22. foxes/core/point_data_model.py +51 -18
  23. foxes/core/rotor_model.py +59 -77
  24. foxes/core/states.py +6 -6
  25. foxes/core/turbine_model.py +4 -4
  26. foxes/core/turbine_type.py +24 -0
  27. foxes/core/vertical_profile.py +3 -3
  28. foxes/core/wake_frame.py +91 -50
  29. foxes/core/wake_model.py +74 -43
  30. foxes/core/wake_superposition.py +29 -26
  31. foxes/input/farm_layout/__init__.py +1 -0
  32. foxes/input/farm_layout/from_random.py +49 -0
  33. foxes/input/states/__init__.py +1 -1
  34. foxes/input/states/create/__init__.py +1 -0
  35. foxes/input/states/create/random_abl_states.py +6 -2
  36. foxes/input/states/create/random_timeseries.py +56 -0
  37. foxes/input/states/field_data_nc.py +12 -8
  38. foxes/input/states/multi_height.py +24 -14
  39. foxes/input/states/scan_ws.py +13 -17
  40. foxes/input/states/single.py +28 -20
  41. foxes/input/states/states_table.py +22 -18
  42. foxes/models/axial_induction_models/betz.py +1 -1
  43. foxes/models/farm_models/turbine2farm.py +2 -2
  44. foxes/models/model_book.py +40 -14
  45. foxes/models/partial_wakes/__init__.py +2 -2
  46. foxes/models/partial_wakes/axiwake.py +73 -200
  47. foxes/models/partial_wakes/centre.py +40 -0
  48. foxes/models/partial_wakes/grid.py +7 -63
  49. foxes/models/partial_wakes/rotor_points.py +53 -147
  50. foxes/models/partial_wakes/segregated.py +158 -0
  51. foxes/models/partial_wakes/top_hat.py +88 -196
  52. foxes/models/point_models/set_uniform_data.py +4 -4
  53. foxes/models/point_models/tke2ti.py +4 -4
  54. foxes/models/point_models/wake_deltas.py +4 -4
  55. foxes/models/rotor_models/centre.py +15 -19
  56. foxes/models/rotor_models/grid.py +2 -1
  57. foxes/models/rotor_models/levels.py +2 -1
  58. foxes/models/turbine_models/__init__.py +0 -1
  59. foxes/models/turbine_models/calculator.py +11 -7
  60. foxes/models/turbine_models/kTI_model.py +13 -11
  61. foxes/models/turbine_models/lookup_table.py +22 -9
  62. foxes/models/turbine_models/power_mask.py +81 -51
  63. foxes/models/turbine_models/rotor_centre_calc.py +17 -20
  64. foxes/models/turbine_models/sector_management.py +5 -6
  65. foxes/models/turbine_models/set_farm_vars.py +49 -20
  66. foxes/models/turbine_models/table_factors.py +5 -5
  67. foxes/models/turbine_models/thrust2ct.py +9 -5
  68. foxes/models/turbine_models/yaw2yawm.py +7 -13
  69. foxes/models/turbine_models/yawm2yaw.py +7 -11
  70. foxes/models/turbine_types/PCt_file.py +84 -3
  71. foxes/models/turbine_types/PCt_from_two.py +7 -3
  72. foxes/models/turbine_types/null_type.py +2 -2
  73. foxes/models/turbine_types/wsrho2PCt_from_two.py +2 -2
  74. foxes/models/turbine_types/wsti2PCt_from_two.py +6 -2
  75. foxes/models/wake_frames/farm_order.py +26 -22
  76. foxes/models/wake_frames/rotor_wd.py +32 -31
  77. foxes/models/wake_frames/seq_dynamic_wakes.py +112 -64
  78. foxes/models/wake_frames/streamlines.py +51 -47
  79. foxes/models/wake_frames/timelines.py +59 -47
  80. foxes/models/wake_frames/yawed_wakes.py +63 -40
  81. foxes/models/wake_models/axisymmetric.py +31 -35
  82. foxes/models/wake_models/dist_sliced.py +50 -56
  83. foxes/models/wake_models/gaussian.py +33 -35
  84. foxes/models/wake_models/induction/rankine_half_body.py +79 -87
  85. foxes/models/wake_models/induction/rathmann.py +56 -63
  86. foxes/models/wake_models/induction/self_similar.py +59 -62
  87. foxes/models/wake_models/ti/crespo_hernandez.py +83 -74
  88. foxes/models/wake_models/ti/iec_ti.py +65 -75
  89. foxes/models/wake_models/top_hat.py +60 -69
  90. foxes/models/wake_models/wake_mirror.py +49 -54
  91. foxes/models/wake_models/wind/bastankhah14.py +44 -66
  92. foxes/models/wake_models/wind/bastankhah16.py +84 -111
  93. foxes/models/wake_models/wind/jensen.py +67 -89
  94. foxes/models/wake_models/wind/turbopark.py +93 -133
  95. foxes/models/wake_superpositions/ti_linear.py +33 -27
  96. foxes/models/wake_superpositions/ti_max.py +33 -27
  97. foxes/models/wake_superpositions/ti_pow.py +35 -27
  98. foxes/models/wake_superpositions/ti_quadratic.py +33 -27
  99. foxes/models/wake_superpositions/ws_linear.py +39 -32
  100. foxes/models/wake_superpositions/ws_max.py +40 -33
  101. foxes/models/wake_superpositions/ws_pow.py +39 -32
  102. foxes/models/wake_superpositions/ws_product.py +35 -28
  103. foxes/models/wake_superpositions/ws_quadratic.py +39 -32
  104. foxes/opt/constraints/min_dist.py +1 -1
  105. foxes/opt/objectives/farm_vars.py +1 -1
  106. foxes/opt/problems/layout/farm_layout.py +38 -97
  107. foxes/output/__init__.py +1 -0
  108. foxes/output/farm_results_eval.py +1 -1
  109. foxes/output/flow_plots_2d/flow_plots.py +2 -0
  110. foxes/output/flow_plots_2d/get_fig.py +2 -0
  111. foxes/output/grids.py +1 -1
  112. foxes/output/rose_plot.py +3 -3
  113. foxes/output/rotor_point_plots.py +117 -0
  114. foxes/output/turbine_type_curves.py +2 -2
  115. foxes/utils/__init__.py +2 -1
  116. foxes/utils/load.py +29 -0
  117. foxes/utils/random_xy.py +56 -0
  118. foxes/utils/runners/runners.py +13 -1
  119. foxes/utils/windrose_plot.py +1 -1
  120. foxes/variables.py +10 -0
  121. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/METADATA +13 -7
  122. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/RECORD +126 -122
  123. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/WHEEL +1 -1
  124. foxes/models/partial_wakes/distsliced.py +0 -322
  125. foxes/models/partial_wakes/mapped.py +0 -252
  126. foxes/models/turbine_models/set_XYHD.py +0 -130
  127. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/LICENSE +0 -0
  128. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/top_level.txt +0 -0
  129. {foxes-0.6.1.dist-info → foxes-0.7.dist-info}/zip-safe +0 -0
@@ -1,4 +1,5 @@
1
1
  from abc import abstractmethod
2
+ import numpy as np
2
3
 
3
4
  from .data_calc_model import DataCalcModel
4
5
  import foxes.constants as FC
@@ -51,6 +52,27 @@ class FarmDataModel(DataCalcModel):
51
52
  """
52
53
  return []
53
54
 
55
+ def ensure_variables(self, algo, mdata, fdata):
56
+ """
57
+ Add variables to fdata, initialized with NaN
58
+
59
+ Parameters
60
+ ----------
61
+ algo: foxes.core.Algorithm
62
+ The calculation algorithm
63
+ mdata: foxes.core.Data
64
+ The model data
65
+ fdata: foxes.core.Data
66
+ The farm data
67
+
68
+ """
69
+ n_states = fdata.n_states
70
+ n_turbines = fdata.n_turbines
71
+ for v in self.output_farm_vars(algo):
72
+ if v not in fdata:
73
+ fdata[v] = np.full((n_states, n_turbines), np.nan, dtype=FC.DTYPE)
74
+ fdata.dims[v] = (FC.STATE, FC.TURBINE)
75
+
54
76
  @abstractmethod
55
77
  def calculate(self, algo, mdata, fdata):
56
78
  """ "
@@ -63,9 +85,9 @@ class FarmDataModel(DataCalcModel):
63
85
  ----------
64
86
  algo: foxes.core.Algorithm
65
87
  The calculation algorithm
66
- mdata: foxes.core.Data
88
+ mdata: foxes.core.MData
67
89
  The model data
68
- fdata: foxes.core.Data
90
+ fdata: foxes.core.FData
69
91
  The farm data
70
92
 
71
93
  Returns
@@ -218,9 +240,9 @@ class FarmDataModelList(FarmDataModel):
218
240
  ----------
219
241
  algo: foxes.core.Algorithm
220
242
  The calculation algorithm
221
- mdata: foxes.core.Data
243
+ mdata: foxes.core.MData
222
244
  The model data
223
- fdata: foxes.core.Data
245
+ fdata: foxes.core.FData
224
246
  The farm data
225
247
  parameters: list of dict, optional
226
248
  A list of parameter dicts, one for each model
foxes/core/model.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import numpy as np
2
2
  from abc import ABC
3
3
  from itertools import count
4
+ from copy import deepcopy
4
5
 
5
6
  import foxes.constants as FC
6
7
  from .data import Data
@@ -37,8 +38,7 @@ class Model(ABC):
37
38
  self.__initialized = False
38
39
 
39
40
  def __repr__(self):
40
- t = type(self).__name__
41
- return f"{self.name} ({t})"
41
+ return f"{type(self).__name__}()"
42
42
 
43
43
  @property
44
44
  def model_id(self):
@@ -187,12 +187,12 @@ class Model(ABC):
187
187
  lookup="smfp",
188
188
  mdata=None,
189
189
  fdata=None,
190
- pdata=None,
191
- states_source_turbine=None,
192
- upcast=False,
190
+ tdata=None,
191
+ downwind_index=None,
193
192
  accept_none=False,
194
193
  accept_nan=True,
195
194
  algo=None,
195
+ upcast=False,
196
196
  ):
197
197
  """
198
198
  Getter for a data entry in the model object
@@ -203,26 +203,24 @@ class Model(ABC):
203
203
  variable: str
204
204
  The variable, serves as data key
205
205
  target: str, optional
206
- The dimensions identifier for the output, e.g
207
- FC.STATE_TURBINE, FC.STATE_POINT
206
+ The dimensions identifier for the output,
207
+ FC.STATE_TURBINE, FC.STATE_TARGET or
208
+ FC.STATE_TARGET_TPOINT
208
209
  lookup: str
209
210
  The order of data sources. Combination of:
210
211
  's' for self,
211
212
  'm' for mdata,
212
213
  'f' for fdata,
213
- 'p' for pdata,
214
+ 't' for tdata,
214
215
  'w' for wake modelling data
215
216
  mdata: foxes.core.Data, optional
216
217
  The model data
217
218
  fdata: foxes.core.Data, optional
218
219
  The farm data
219
- pdata: foxes.core.Data, optional
220
- The evaluation point data
221
- states_source_turbine: numpy.ndarray, optional
222
- For each state, one turbine index for the
223
- wake causing turbine. Shape: (n_states,)
224
- upcast: bool, optional
225
- Upcast array to dims if data is scalar
220
+ tdata: foxes.core.Data, optional
221
+ The target point data
222
+ downwind_index: int, optional
223
+ The index in the downwind order
226
224
  data_prio: bool
227
225
  First search the data source, then the object
228
226
  accept_none: bool
@@ -231,146 +229,235 @@ class Model(ABC):
231
229
  Do not throw an error if data entry is np.nan
232
230
  algo: foxes.core.Algorithm, optional
233
231
  The algorithm, needed for data from previous iteration
232
+ upcast: bool
233
+ Flag for ensuring targets dimension,
234
+ otherwise dimension 1 is entered
234
235
 
235
236
  """
236
237
 
237
238
  def _geta(a):
238
- sources = [s for s in [mdata, fdata, pdata, algo, self] if s is not None]
239
+ sources = [s for s in [mdata, fdata, tdata, algo, self] if s is not None]
239
240
  for s in sources:
240
- if a == "states_i0":
241
- out = s.states_i0(counter=True, algo=algo)
242
- if out is not None:
243
- return out
244
- else:
245
- try:
241
+ try:
242
+ if a == "states_i0":
243
+ out = s.states_i0(counter=True, algo=algo)
244
+ if out is not None:
245
+ return out
246
+ else:
246
247
  out = getattr(s, a)
247
248
  if out is not None:
248
249
  return out
249
- except AttributeError:
250
- pass
250
+ except AttributeError:
251
+ pass
251
252
  raise KeyError(
252
- f"Model '{self.name}': Failed to determine '{a}'. Maybe add to arguments of get_data: mdata, fdata, pdata, algo?"
253
+ f"Model '{self.name}': Failed to determine '{a}'. Maybe add to arguments of get_data: mdata, fdata, tdata, algo?"
253
254
  )
254
255
 
255
256
  n_states = _geta("n_states")
256
257
  if target == FC.STATE_TURBINE:
257
258
  n_turbines = _geta("n_turbines")
258
259
  dims = (FC.STATE, FC.TURBINE)
259
- elif target == FC.STATE_POINT:
260
- n_points = _geta("n_points")
261
- dims = (FC.STATE, FC.POINT)
260
+ shp = (n_states, n_turbines)
261
+ elif target == FC.STATE_TARGET:
262
+ n_targets = _geta("n_targets")
263
+ dims = (FC.STATE, FC.TARGET)
264
+ shp = (n_states, n_targets)
265
+ elif target == FC.STATE_TARGET_TPOINT:
266
+ n_targets = _geta("n_targets")
267
+ n_tpoints = _geta("n_tpoints")
268
+ dims = (FC.STATE, FC.TARGET, FC.TPOINT)
269
+ shp = (n_states, n_targets, n_tpoints)
262
270
  else:
263
271
  raise KeyError(
264
- f"Model '{self.name}': Wrong parameter 'target = {target}'. Choices: {FC.STATE_TURBINE}, {FC.STATE_POINT}"
272
+ f"Model '{self.name}': Wrong parameter 'target = {target}'. Choices: {FC.STATE_TURBINE}, {FC.STATE_TARGET}, {FC.STATE_TARGET_TPOINT}"
265
273
  )
266
274
 
267
275
  out = None
276
+ out_dims = None
268
277
  for s in lookup:
269
278
  # lookup self:
270
279
  if s == "s" and hasattr(self, variable):
271
280
  a = getattr(self, variable)
272
-
273
- if a is not None and upcast:
274
- if target == FC.STATE_TURBINE:
281
+ if a is not None:
282
+ if not upcast:
283
+ out = a
284
+ out_dims = None
285
+ elif target == FC.STATE_TURBINE:
275
286
  out = np.full((n_states, n_turbines), np.nan, dtype=FC.DTYPE)
276
287
  out[:] = a
277
- elif target == FC.STATE_POINT:
278
- out = np.full((n_states, n_points), np.nan, dtype=FC.DTYPE)
288
+ out_dims = (FC.STATE, FC.TURBINE)
289
+ elif target == FC.STATE_TARGET:
290
+ out = np.full((n_states, n_targets), np.nan, dtype=FC.DTYPE)
279
291
  out[:] = a
280
- else:
281
- raise KeyError(
282
- f"Model '{self.name}': Wrong parameter 'target = {target}' for 'upcast = True' in get_data. Choose: FC.STATE_TURBINE, FC.STATE_POINT"
292
+ out_dims = (FC.STATE, FC.TARGET)
293
+ elif target == FC.STATE_TARGET_TPOINT:
294
+ out = np.full(
295
+ (n_states, n_targets, n_tpoints), np.nan, dtype=FC.DTYPE
283
296
  )
284
-
285
- else:
286
- out = a
297
+ out[:] = a
298
+ out_dims = (FC.STATE, FC.TARGET, FC.TPOINT)
299
+ else:
300
+ raise NotImplementedError
287
301
 
288
302
  # lookup mdata:
289
303
  elif (
290
304
  s == "m"
291
305
  and mdata is not None
292
306
  and variable in mdata
293
- and len(mdata.dims[variable]) > 1
294
- and tuple(mdata.dims[variable][:2]) == dims
307
+ and tuple(mdata.dims[variable]) == dims
295
308
  ):
296
309
  out = mdata[variable]
310
+ out_dims = dims
297
311
 
298
312
  # lookup fdata:
299
313
  elif (
300
314
  s == "f"
301
315
  and fdata is not None
302
316
  and variable in fdata
303
- and len(fdata.dims[variable]) > 1
304
- and tuple(fdata.dims[variable][:2]) == (FC.STATE, FC.TURBINE)
317
+ and tuple(fdata.dims[variable]) == (FC.STATE, FC.TURBINE)
305
318
  ):
306
- # direct fdata:
307
- if target == FC.STATE_TURBINE:
308
- out = fdata[variable]
309
-
310
- # translate state-turbine to state-point data:
311
- elif target == FC.STATE_POINT and states_source_turbine is not None:
312
- # from fdata, uniform for points:
313
- st_sel = (np.arange(n_states), states_source_turbine)
314
- out = np.zeros((n_states, n_points), dtype=FC.DTYPE)
315
- out[:] = fdata[variable][st_sel][:, None]
316
-
317
- # from previous iteration, if requested:
318
- if pdata is not None and FC.STATES_SEL in pdata:
319
- if not np.all(
320
- states_source_turbine == pdata[FC.STATE_SOURCE_TURBINE]
321
- ):
322
- raise ValueError(
323
- f"Model '{self.name}': Mismatch of 'states_source_turbine'. Expected {list(pdata[FC.STATE_SOURCE_TURBINE])}, got {list(states_source_turbine)}"
324
- )
325
-
326
- i0 = _geta("states_i0")
327
- sp = pdata[FC.STATES_SEL]
328
- sel = sp < i0
329
- if np.any(sel):
330
- if algo is None or not hasattr(algo, "prev_farm_results"):
331
- raise KeyError(
332
- f"Model '{self.name}': Argument algo is either not given, or not an iterative algorithm"
333
- )
334
-
335
- prev_fdata = getattr(algo, "prev_farm_results")
336
- if prev_fdata is None:
337
- out[sel] = 0
338
- else:
339
- st = np.zeros_like(sp)
340
- st[:] = states_source_turbine[:, None]
341
- out[sel] = prev_fdata[variable].to_numpy()[
342
- sp[sel], st[sel]
343
- ]
319
+ out = fdata[variable]
320
+ out_dims = (FC.STATE, FC.TURBINE)
344
321
 
345
322
  # lookup pdata:
346
323
  elif (
347
- s == "p"
348
- and pdata is not None
349
- and variable in pdata
350
- and len(pdata.dims[variable]) > 1
351
- and tuple(pdata.dims[variable][:2]) == dims
324
+ s == "t"
325
+ and tdata is not None
326
+ and variable in tdata
327
+ and tuple(tdata.dims[variable]) == (FC.STATE, FC.TARGET, FC.TPOINT)
352
328
  ):
353
- out = pdata[variable]
329
+ out = tdata[variable]
330
+ out_dims = (FC.STATE, FC.TARGET, FC.TPOINT)
354
331
 
355
332
  # lookup wake modelling data:
356
333
  elif (
357
334
  s == "w"
358
- and target == FC.STATE_POINT
359
335
  and fdata is not None
360
- and pdata is not None
336
+ and tdata is not None
361
337
  and variable in fdata
362
- and len(fdata.dims[variable]) > 1
363
- and tuple(fdata.dims[variable][:2]) == (FC.STATE, FC.TURBINE)
364
- and states_source_turbine is not None
338
+ and tuple(fdata.dims[variable]) == (FC.STATE, FC.TURBINE)
339
+ and downwind_index is not None
365
340
  and algo is not None
366
341
  ):
367
- out = algo.wake_frame.get_wake_modelling_data(
368
- algo, variable, states_source_turbine, fdata, pdata
342
+ out, out_dims = algo.wake_frame.get_wake_modelling_data(
343
+ algo,
344
+ variable,
345
+ downwind_index,
346
+ fdata,
347
+ tdata=tdata,
348
+ target=target,
349
+ upcast=upcast,
369
350
  )
370
351
 
371
352
  if out is not None:
372
353
  break
373
354
 
355
+ # cast dimensions:
356
+ if out_dims != dims:
357
+ if out_dims is None:
358
+ if upcast:
359
+ out0 = out
360
+ out = np.zeros(shp, dtype=FC.DTYPE)
361
+ out[:] = out0
362
+ out_dims = dims
363
+ del out0
364
+ else:
365
+ out_dims = tuple([1 for _ in dims])
366
+
367
+ elif out_dims == (FC.STATE, FC.TURBINE):
368
+ if downwind_index is None:
369
+ raise KeyError(
370
+ f"Require downwind_index for target {target} and out dims {out_dims}"
371
+ )
372
+ out0 = out[:, downwind_index, None]
373
+ if len(dims) == 3:
374
+ out0 = out0[:, :, None]
375
+ if upcast:
376
+ out = np.zeros(shp, dtype=FC.DTYPE)
377
+ out[:] = out0
378
+ out_dims = dims
379
+ else:
380
+ out = out0
381
+ out_dims = (FC.STATE, 1) if len(dims) == 2 else (FC.STATE, 1, 1)
382
+ del out0
383
+
384
+ elif out_dims == (FC.STATE, 1):
385
+ out0 = out
386
+ if len(dims) == 3:
387
+ out0 = out0[:, :, None]
388
+ out_dims = (FC.STATE, 1, 1)
389
+ if upcast:
390
+ out = np.zeros(shp, dtype=FC.DTYPE)
391
+ out[:] = out0
392
+ out_dims = dims
393
+ else:
394
+ out = out0
395
+ del out0
396
+
397
+ elif out_dims == (FC.STATE, 1, 1):
398
+ out0 = out
399
+ if len(dims) == 2:
400
+ out0 = out0[:, :, 0]
401
+ out_dims = (FC.STATE, 1)
402
+ if upcast:
403
+ out = np.zeros(shp, dtype=FC.DTYPE)
404
+ out[:] = out0
405
+ out_dims = dims
406
+ else:
407
+ out = out0
408
+ del out0
409
+
410
+ else:
411
+ raise NotImplementedError(
412
+ f"No casting implemented for target {target} and out dims {out_dims} fo upcast {upcast}"
413
+ )
414
+
415
+ # data from other chunks, only with iterations:
416
+ if (
417
+ target in [FC.STATE_TARGET, FC.STATE_TARGET_TPOINT]
418
+ and fdata is not None
419
+ and variable in fdata
420
+ and tdata is not None
421
+ and FC.STATES_SEL in tdata
422
+ ):
423
+ if out_dims != dims:
424
+ raise ValueError(
425
+ f"Model '{self.name}': Iteration data found for variable '{variable}', but missing upcast: out_dims = {out_dims}, expecting {dims}"
426
+ )
427
+ if downwind_index is None:
428
+ raise KeyError(
429
+ f"Model '{self.name}': Require downwind_index for obtaining results from previous iteration"
430
+ )
431
+ if tdata[FC.STATE_SOURCE_ORDERI] != downwind_index:
432
+ raise ValueError(
433
+ f"Model '{self.name}': Expecting downwind_index {tdata[FC.STATE_SOURCE_ORDERI]}, got {downwind_index}"
434
+ )
435
+ if algo is None:
436
+ raise ValueError(
437
+ f"Model '{self.name}': Iteration data found for variable '{variable}', requiring algo"
438
+ )
439
+
440
+ i0 = _geta("states_i0")
441
+ sts = tdata[FC.STATES_SEL]
442
+ if target == FC.STATE_TARGET and tdata.n_tpoints != 1:
443
+ # find the mean index and round it to nearest integer:
444
+ sts = tdata.tpoint_mean(FC.STATES_SEL)[:, :, None]
445
+ sts = (sts + 0.5).astype(FC.ITYPE)
446
+ sel = sts < i0
447
+ if np.any(sel):
448
+ if not hasattr(algo, "prev_farm_results"):
449
+ raise KeyError(
450
+ f"Model '{self.name}': Iteration data found for variable '{variable}', requiring iterative algorithm"
451
+ )
452
+ prev_fres = getattr(algo, "prev_farm_results")
453
+ if prev_fres is not None:
454
+ prev_data = prev_fres[variable].to_numpy()[sts[sel], downwind_index]
455
+ if target == FC.STATE_TARGET:
456
+ out[sel[:, :, 0]] = prev_data
457
+ else:
458
+ out[sel] = prev_data
459
+ del prev_fres, prev_data
460
+
374
461
  # check for None:
375
462
  if not accept_none and out is None:
376
463
  raise ValueError(
@@ -410,8 +497,10 @@ class Model(ABC):
410
497
  data={}, dims={}, loop_dims=data.loop_dims, name=f"{self.name}_{i0}"
411
498
  )
412
499
 
413
- self._store[i0][name] = data[name]
414
- self._store[i0].dims[name] = data.dims[name] if name in data.dims else None
500
+ self._store[i0][name] = deepcopy(data[name])
501
+ self._store[i0].dims[name] = (
502
+ deepcopy(data.dims[name]) if name in data.dims else None
503
+ )
415
504
 
416
505
  def from_data_or_store(self, name, algo, data, ret_dims=False, safe=False):
417
506
  """
@@ -450,35 +539,3 @@ class Model(ABC):
450
539
  return self._store[i0][name]
451
540
  else:
452
541
  return (None, None) if ret_dims else None
453
-
454
- '''
455
- @classmethod
456
- def reduce_states(cls, sel_states, objs):
457
- """
458
- Modifies the given objects by selecting a
459
- subset of states.
460
-
461
- Parameters
462
- ----------
463
- sel_states: list of int
464
- The states selection
465
- objs: list of foxes.core.Data
466
- The objects, e.g. [mdata, fdata, pdata]
467
-
468
- Returns
469
- -------
470
- mobjs: list of foxes.core.Data
471
- The modified objects with reduced
472
- states dimension
473
-
474
- """
475
- out = []
476
- for o in objs:
477
- data = {
478
- v: d[sel_states] if o.dims[v][0] == FC.STATE else d
479
- for v, d in o.items()
480
- }
481
- out.append(Data(data, o.dims, loop_dims=o.loop_dims, name=o.name))
482
-
483
- return out
484
- '''