Dynamic wakes 2

Sequential wake propagation

For some applications it may be useful to run foxes in state-by-state mode, i.e., without the benefits of state-wise chunking. For example, when coupling it to another simulation tool that runs based on an overall outer loop over the individual states.

For such cases the Sequential algorithm has been added, which basically is an iterator over the states. Obviously this algorithm is much slower than the Downwind algorithm. On the plus side, for timeseries ambient states, it has the advantage that it can be combined with local propagation of wake parcels between two subsequent time stamps. This functionality is provided by the wake frame class SeqDynamicWakes and does not require spatial homogeneity.

In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["animation.html"] = "jshtml"

import foxes
import foxes.variables as FV
import foxes.constants as FC

We create a case with a regular 3 x 3 wind farm layout:

In [2]:
mbook = foxes.models.ModelBook()

states = foxes.input.states.Timeseries(
    data_source="timeseries_3000.csv.gz",
    output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],
    var2col={FV.WS: "WS", FV.WD: "WD", FV.TI: "TI", FV.RHO: "RHO"},
    states_sel=range(230, 280),
)

farm = foxes.WindFarm()
foxes.input.farm_layout.add_grid(
    farm,
    xy_base=np.array([0.0, 0.0]),
    step_vectors=np.array([[1000.0, 0], [0, 800.0]]),
    steps=(3, 3),
    turbine_models=["DTU10MW"],
    verbosity=0
)

algo = foxes.algorithms.Sequential(
    mbook,
    farm,
    states=states,
    rotor_model="centre",
    wake_models=["Bastankhah025_linear_lim_k004"],
    wake_frame="seq_dyn_wakes_1min",
    partial_wakes_model="auto",
    chunks={FC.STATE: None, FC.POINT: 5000},
    verbosity=1
)

Notice that for the sake of drama, we artificially reset the time step to 1min in the wake frame choice, more realistic would have been the choice seq_dyn_wakes which is based on the true time delta of the time series.

Our goal in this example is the creation of an animation that shows the dynamics of the flow in a horizontal plane at hub height. With the Sequential algorithm we can achieve this by treating the algo object as a Python iterator, for example within a for-loop.

Before doing so, we add a plugin that is evaluated whenever the iterator moves to the next state. In this case the plugin creates an image that is later on used for the animation:

In [3]:
with foxes.utils.runners.DaskRunner() as runner:

    # Add a plugin to the algorithm that updates
    # an image for an animation with every time step:
    fig, ax = plt.subplots()
    anigen = foxes.output.SeqFlowAnimationPlugin(
        runner=runner,
        orientation="xy",
        var=FV.WS,
        resolution=20,
        levels=None,
        quiver_pars=dict(scale=0.008, alpha=0.5),
        quiver_n=111,
        xmin=-5000,
        ymin=-5000,
        xmax=7000,
        ymax=7000,
        fig=fig,
        ax=ax,
        vmin=0,
        vmax=10,
        ret_im=True,
        title=lambda si, s: f"t = {si:02d} min",
        animated=True,
    )
    algo.plugins.append(anigen)

    # Now run all states sequentially:
    for r in algo:
        print(algo.index)

    print("\nFarm results:\n")
    print(algo.farm_results)

    # Create the animation:

    anim = foxes.output.Animator(fig)
    anim.add_generator(anigen.gen_images())
    ani = anim.animate(interval=600)

    lo = foxes.output.FarmLayoutOutput(farm)
    lo.get_figure(
        fig=fig,
        ax=ax,
        title="",
        annotate=1,
        anno_delx=-120,
        anno_dely=-60,
        alpha=0,
        s=10,
    )

    plt.close()
    print("done.")

print("Creating animation")
ani

Initializing algorithm 'Sequential'
Initializing model 'Sequential'
Initializing model 'Timeseries'
States 'Timeseries': Reading static data 'timeseries_3000.csv.gz' from context 'states'
Path: /home/jonas/gits/wakes/foxes/foxes/data/states/timeseries_3000.csv.gz
Initializing model 'SeqState'
Initializing model 'centre'
Initializing model 'basic_ctrl_prer'
Initializing model 'DTU10MW'
Turbine type 'DTU10MW': Reading static data from context 'power_ct_curve'
Path: /home/jonas/gits/wakes/foxes/foxes/data/power_ct_curves/DTU-10MW-D178d3-H119.csv
Initializing model 'basic_ctrl_postr'
Initializing model 'basic_ctrl'
Initializing model 'seq_dyn_wakes_1min'
Partial wakes 'auto': Applying PartialAxiwake to ['Bastankhah025_linear_lim_k004']
Initializing model 'Madsen'
Initializing model 'Bastankhah025_linear_lim_k004'
Initializing model 'PartialAxiwake10'
Initializing model 'auto'

--------------------------------------------------
  Running Sequential: calc_farm
--------------------------------------------------
  n_states : 50
  n_turbines: 9
--------------------------------------------------
  states   : SeqState (SeqState)
  rotor    : centre (CentreRotor)
  controller: basic_ctrl (BasicFarmController)
  partialwks: auto (Mapped)
  wake frame: seq_dyn_wakes_1min (SeqDynamicWakes)
--------------------------------------------------
  wakes:
    0) Bastankhah025_linear_lim_k004 (Bastankhah2014)(k=0.04, sp=ws_linear_lim)
--------------------------------------------------
  turbine models:
    0) DTU10MW (PCtFile)
--------------------------------------------------

Initializing model 'SetXYHD'
Initializing model 'SetXYHD_t2f'
Initializing model 'calc_yaw_CentreRotor1'
Initializing model 'CalcOrder'
Initializing model 'SetAmbFarmResults'
Initializing model 'FarmWakesCalculation'
Initializing model 'Sequential_calc'

--------------------------------------------------
  Model oder
--------------------------------------------------
00) SetXYHD_t2f
01) basic_ctrl
02) calc_yaw_CentreRotor1
03) centre
04) CalcOrder
05) basic_ctrl
  05.0) Post-rotor: DTU10MW
06) SetAmbFarmResults
07) FarmWakesCalculation
--------------------------------------------------


Input data:

  weight: ('state', 'turbine') float64, shape (50, 9)
  Timeseries_data: ('state', 'Timeseries_vars') float64, shape (50, 4)
  tmodel_sels: ('state', 'turbine', 'tmodels') bool, shape (50, 9, 1)

Output farm variables: AMB_CT, AMB_P, AMB_REWS, AMB_REWS2, AMB_REWS3, AMB_RHO, AMB_TI, AMB_WD, AMB_YAW, CT, D, H, P, REWS, REWS2, REWS3, RHO, TI, WD, X, Y, YAW, order, weight

2018-10-10T05:00:00.000000000
2018-10-10T06:00:00.000000000
2018-10-10T07:00:00.000000000
2018-10-10T08:00:00.000000000
2018-10-10T09:00:00.000000000
2018-10-10T10:00:00.000000000
2018-10-10T11:00:00.000000000
2018-10-10T12:00:00.000000000
2018-10-10T13:00:00.000000000
2018-10-10T14:00:00.000000000
2018-10-10T15:00:00.000000000
2018-10-10T16:00:00.000000000
2018-10-10T17:00:00.000000000
2018-10-10T18:00:00.000000000
2018-10-10T19:00:00.000000000
2018-10-10T20:00:00.000000000
2018-10-10T21:00:00.000000000
2018-10-10T22:00:00.000000000
2018-10-10T23:00:00.000000000
2018-10-11T00:00:00.000000000
2018-10-11T01:00:00.000000000
2018-10-11T02:00:00.000000000
2018-10-11T03:00:00.000000000
2018-10-11T04:00:00.000000000
2018-10-11T05:00:00.000000000
2018-10-11T06:00:00.000000000
2018-10-11T07:00:00.000000000
2018-10-11T08:00:00.000000000
2018-10-11T09:00:00.000000000
2018-10-11T10:00:00.000000000
2018-10-11T11:00:00.000000000
2018-10-11T12:00:00.000000000
2018-10-11T13:00:00.000000000
2018-10-11T14:00:00.000000000
2018-10-11T15:00:00.000000000
2018-10-11T16:00:00.000000000
2018-10-11T17:00:00.000000000
2018-10-11T18:00:00.000000000
2018-10-11T19:00:00.000000000
2018-10-11T20:00:00.000000000
2018-10-11T21:00:00.000000000
2018-10-11T22:00:00.000000000
2018-10-11T23:00:00.000000000
2018-10-12T00:00:00.000000000
2018-10-12T01:00:00.000000000
2018-10-12T02:00:00.000000000
2018-10-12T03:00:00.000000000
2018-10-12T04:00:00.000000000
2018-10-12T05:00:00.000000000
2018-10-12T06:00:00.000000000

Farm results:

<xarray.Dataset>
Dimensions:    (state: 50, turbine: 9, xyh: 3)
Coordinates:
  * state      (state) datetime64[ns] 2018-10-10T05:00:00 ... 2018-10-12T06:0...
  * turbine    (turbine) int64 0 1 2 3 4 5 6 7 8
Dimensions without coordinates: xyh
Data variables: (12/26)
    AMB_CT     (state, turbine) float64 0.0 0.0 0.0 0.0 ... 0.814 0.814 0.814
    AMB_P      (state, turbine) float64 0.0 0.0 0.0 ... 6.477e+03 6.477e+03
    AMB_REWS   (state, turbine) float64 3.51 3.51 3.51 3.51 ... 9.59 9.59 9.59
    AMB_REWS2  (state, turbine) float64 3.51 3.51 3.51 3.51 ... 9.59 9.59 9.59
    AMB_REWS3  (state, turbine) float64 3.51 3.51 3.51 3.51 ... 9.59 9.59 9.59
    AMB_RHO    (state, turbine) float64 1.27 1.27 1.27 1.27 ... 1.23 1.23 1.23
    ...         ...
    Y          (state, turbine) float64 0.0 800.0 1.6e+03 ... 0.0 800.0 1.6e+03
    YAW        (state, turbine) float64 81.3 81.3 81.3 81.3 ... 38.4 38.4 38.4
    order      (state, turbine) int64 8 7 6 5 4 3 2 1 0 8 ... 8 5 7 2 4 6 1 3 0
    weight     (state, turbine) float64 0.02 0.02 0.02 0.02 ... 0.02 0.02 0.02
    txyh       (state, turbine, xyh) float64 0.0 0.0 119.0 ... 1.6e+03 119.0
    tname      (turbine) <U2 'T0' 'T1' 'T2' 'T3' 'T4' 'T5' 'T6' 'T7' 'T8'
Creating animation data
done.
Creating animation
Out[3]: