Advanced Concepts

Advanced Concepts#

More information regarding the numerical and computational formulation in FLORIS are detailed here. See Introductory Concepts for a guide on the basics.

# Create a basic FLORIS model for use later
import numpy as np
import matplotlib.pyplot as plt
from floris import FlorisModel
fmodel = FlorisModel("gch.yaml")

Data structures#

FLORIS adopts a structures of arrays data modeling paradigm (SoA, relative to array of structures {AoS}) for nearly all of the data in the floris.core package. This data model enables vectorization (SIMD operations) through Numpy array broadcasting for many operations. In general, there are two types of array shapes:

  • Field quantities have points throughout the computational domain but in context-specific locations and have the shape (n findex, n turbines, n grid, n grid).

  • Scalar quantities have a single value for each turbine and typically have the shape (n findex, n turbines, 1, 1). For scalar quanities, the arrays may be created with the shape (n findex, n turbines) and then expanded to the 4-dimensional shape prior to running the wake calculation.

Grids#

FLORIS includes a number of grid-types that create sampling points within the computational domain for different contexts. In the typical use case, AEP or some other metric of wind farm energy yield is the end result. Since the mathematical models in FLORIS are all analytical, we only need to create points on the turbines themselves in order to calculate the incoming wind speeds given all of the upstream conditions. In this case, we use the floris.core.grid.TurbineGrid() or floris.core.grid.TurbineCubatureGrid(). Each of these grid-types put points only on the turbine swept area, so all other field-quantities in FLORIS have the same shape.

# Plot the grid point locations for TurbineGrid and TurbineCubatureGrid

fmodel.set(layout_x=[0.0], layout_y=[0.0])
rotor_radius = fmodel.core.farm.rotor_diameters[0] / 2.0
hub_height = fmodel.core.farm.hub_heights[0]
theta = np.linspace(0, 2*np.pi, 100)
circlex = rotor_radius * np.cos(theta)
circley = rotor_radius * np.sin(theta) + hub_height

# TurbineGrid is the default
fig, ax = plt.subplots()
ax.scatter(0, hub_height, marker="+", color="r")
ax.scatter(fmodel.core.grid.y_sorted[0,0], fmodel.core.grid.z_sorted[0,0], marker="+", color="r")
ax.plot(circlex, circley)
ax.set_aspect('equal', 'box')
plt.show()
_images/4f941b269447a1c088a82b28ea552fa94254f73e7dee019f3936ab6174915bad.png

FLORIS as a library#

FLORIS is commonly used as a library in other software packages. In cases where the calling-code will create inputs for FLORIS rather than require them from the user, it can be helpful to initialize the FLORIS model with default inputs and then change them in code. In this case, the following workflow is recommended.

import floris

# Initialize FLORIS with defaults
fmodel = floris.FlorisModel("defaults")

# Within the calling-code's setup step, update FLORIS as needed
fmodel.set(
    wind_directions=[i for i in range(10)],
    wind_speeds=[5 + i for i in range(10)],
    turbulence_intensities=[i for i in range(10)],
    # turbine_library_path="path/to/turbine_library",   # Shown here for reference
    # turbine_type=["my_turbine"]
)

# Within the calling code's computation, run FLORIS
fmodel.run()

Alternatively, the calling-code can import the FLORIS default inputs as a Python dictionary and modify them directly before initializing the FLORIS model. This is especially helpful when the calling-code will modify a parameter that isn't supported by the FlorisModel.set(...) command. In particular, the wake model parameters are not directly accessible, so these can be updated externally, as shown below. Note that the FlorisModel.get_defaults() function returns a deep copy of the default inputs, so these can be modified directly without side effects.

import floris

# Retrieve the default parameters
fdefaults = floris.FlorisModel.get_defaults()

# Update wake model parameters
fdefaults["wake"]["model_strings"]["velocity_model"] = "jensen"
fdefaults["wake"]["wake_velocity_parameters"]["jensen"]["we"] = 0.05

# Initialize FLORIS with modified parameters
fmodel = floris.FlorisModel(configuration=fdefaults)

# Within the calling-code's setup step, update FLORIS as needed
fmodel.set(
    wind_directions=[i for i in range(10)],
    wind_speeds=[5 + i for i in range(10)],
    turbulence_intensities=[i for i in range(10)],
    # turbine_library_path="path/to/turbine_library",   # Shown here for reference
    # turbine_type=["my_turbine"]
)

# Verify settings are correct
fmodel.show_config()  # Shows truncated set of inputs; show all with fmodel.show_config(full=True)

# Within the calling code's computation, run FLORIS
fmodel.run()
solver
    type
        turbine_grid
    turbine_grid_points
        3
wake
    model_strings
        combination_model
            sosfs
        deflection_model
            gauss
        turbulence_model
            crespo_hernandez
        velocity_model
            jensen
farm
    layout_x
        [0.0]
    layout_y
        [0.0]
    turbine_type
        ['nrel_5MW']
    turbine_library_path
        /home/runner/work/floris/floris/floris/turbine_library
flow_field
    wind_speeds
        [5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0]
    wind_directions
        [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    wind_veer
        0.0
    wind_shear
        0.12
    air_density
        1.225
    turbulence_intensities
        [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    reference_wind_height
        90.0
name
    GCH
description
    Default initialization: Gauss-Curl hybrid model (GCH)
floris_version
    v4